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)?

Gilles 'SO- stop being evil'
  • 807,993
  • 194
  • 1,674
  • 2,175
Shuzheng
  • 4,023
  • 1
  • 31
  • 71
  • If you're talking about the deprecated -perm +mode option, cant you just check writable for user or group or others (0222) ? – jai_s Jun 01 '19 at 18:06
  • See also [Find files that a user can write, efficiently with minimal process creation](//unix.stackexchange.com/q/203628) and [Using find in Solaris 10 to search a single directory but not its subdirectories](//unix.stackexchange.com/q/256991) – Stéphane Chazelas Jun 01 '19 at 19:44
  • Also: [Find files that a user can not read?](//unix.stackexchange.com/q/88573) – Stéphane Chazelas Jun 01 '19 at 19:52

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.

Gilles 'SO- stop being evil'
  • 807,993
  • 194
  • 1,674
  • 2,175
  • @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](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_05)'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