1

Sometimes on old Linux systems, I experience that find doesn't support -writable and -readable tests, which test whether the file or directory is writable/readable resp. for the current user.

Say, I want to express -writable; then -perm -0002 would not be equivalent, since it doesn't test whether the user has write permissions by means of owner/group.

How can I express find's -writable test by means of find's other tests (e.g. -perm)?

Shuzheng
  • 4,411

1 Answers1

1

There's no convenient way. That's why GNU find added -readable and friends.

You can build an expression that approximates the permission test by enumerating the groups that the user is in. Untested.

can_access="( -user $(id -u) -perm -0${oct}00 -o ("
for g in $(id -G); do
  can_access="$can_access -group $g -o"
done
can_access="${can_access% -o} ) -perm -00${oct}0 -o -perm -000${oct} )"
find … $can_access -print

This doesn't give the correct result in some cases, for example if there are access control lists, or in edge cases such as -rw----r-- to deny access to a group. You can check for the edge cases with the same technique as above, but the expression gets even more complex. For access control lists, you need to invoke a tool that supports them.

Languages such as Perl and Python provide easy access both to the access(2) function and to the functionality of find. In Perl, with File::Find and -r/-w/-x (which use the effective uid and gid of the Perl process — use -R/-W/-X to check with the real uid/gid like access(2), and use the filetest 'access' pragma if your Perl isn't too ancient to have it to support things like ACL):

use File::Find;
use filetest 'access';
find(sub { if (-r $_) { print "$_ is readable\n"; } }, '.');

In Python, with os.walk and os.access (which uses the real uid and gid of the Python process, like access(2)):

import os
for dirpath, dirnames, filenames in os.walk('.', ):
    for filename in filenames:
        filename = os.path.join(dirpath, filenames)
        if os.access(filename, os.R_OK):
            print(filename + ' is readable\n')

The only fully reliable way is to try to open the file. This requires an external utility, so it'll be slower. To test for readability of a regular file:

find … -exec sh -c 'exec 2>/dev/null; : <"$0"' {} \; …

To test for writability, use : >>"$0" (this opens the file for appending, so it'll fail if the file isn't writable, but it doesn't actually modify anything, and in particular won't update the modification time). To test a directory for readability, use ls -- "$0" >/dev/null. To test a directory for executability, use cd -- "$0". There's no passive test for executability of a regular file, for writability of a directory, or for access to most non-regular files.

  • @StéphaneChazelas ! was a typo. The edge case of ---rwxrwx is similar to the slightly less rare edge case of -rw----r-- which I discuss in my answer. I only propose : >> "$0" for regular files; as you note, for other file types such as dangling symbolic links (and pipes etc.), it doesn't work. – Gilles 'SO- stop being evil' Jun 01 '19 at 20:03
  • thank you very much for your detailed answer. So, I guess the short answer is that -readable and friends cannot completely be expressed using find's other tests. I don't understand, why your test doesn't work for your edge case -rw----r--? In this case, -perm -000${oct} would yield true? – Shuzheng Jun 02 '19 at 09:06
  • @Shuzheng A file with -rw----r-- permissions owned by someuser:somegroup is readable to everyone except for members of the group somegroup who are not someuser. The simplified logic in my answer doesn't work if the calling user is a member of somegroup other than someuser: -perm -0004 is true, because the file is readable to others, so my logic concludes that everyone can read the file, which is wrong. It's possible to express the correct logic, but it's pretty long, and it still doesn't take access control lists into account. – Gilles 'SO- stop being evil' Jun 02 '19 at 09:12
  • Ahh, I see. I always thought that a user could read a file, if just any of the permissions for u/g/o allowed that - this is not true? Also, how can I see if ACL is enabled for a file by ls? – Shuzheng Jun 02 '19 at 10:26
  • @Shuzheng The first match applies, see https://unix.stackexchange.com/questions/134332/precedence-of-user-and-group-owner-in-file-permissions/134376#134376 https://unix.stackexchange.com/questions/353598/precedence-of-acls-when-a-user-belongs-to-multiple-groups/353629#353629 . ls shows + at the end of the permissions if there's an ACL, or a . if there's an SELinux (or similar) context. – Gilles 'SO- stop being evil' Jun 02 '19 at 18:54
  • thank you once again. FYI, your first answer link has several copy-paste typos wrt. permission bits and ACLs. Also, which man page would explain how u/g/o permissions are used? – Shuzheng Jun 03 '19 at 11:18
  • @Shuzheng Can you please tell me where the typos are, or even better suggest an edit? For ugo permissions, I can't find that in a man page; here's how POSIX describes it (I wouldn't say “explains it”, because it's in standardese: precise but hard to understand). – Gilles 'SO- stop being evil' Jun 04 '19 at 11:12