Find files that a user can not read?
There are far many more things to take into consideration to check whether a user has access to a file via a given path:
- The owner of the file
- the group of the file
- the ACLs in the file
- the uid, gid, and supplementary gids of the user
- search access to any path component leading to that file.
- whether the file is a symlink
- permissions apply differently for users of id 0.
- possibly more security features like SELinux...
Short of actually switching all the uids and gids to those of the user and check, it's very difficult to implement the same logic as what the system does.
With zsh, you could do (as root):
readable() (
USERNAME=$u
[ -r "$REPLY" ]
)
u=some-user
print -rl -- **/*(DoN^+readable)
Or with perl
:
find . -print0 | sudo -u some-user perl -Mfiletest=access -l -0ne '
print unless -r'
That is in both cases, descend the directory tree as root
but test for file access as the corresponding user.
Running find -readable
as some-user
won't in cases as it won't be able to go past the directories for which the user has no access or no read permission (but possibly access).
Even when only considering the permission and ownership of the file itself (and not ACLs or path components...), you need at least (here GNU syntax):
u=some-user; g=$(id -G "$u" | sed 's/ / -o -group /g'); IFS=" "
find . ! \( -user "$u" -perm -u=r -o \
! -user "$u" \( -group $g \) -perm -g=r -o \
! -user "$u" ! \( -group $g \) -perm -o=r \)
The idea being that if the file is owned by the user, all other permissions are irrelevant. If not, then if the file is group-owned by any of the user's groups, then the "other" permission is irrelevant.
The logic is wrong. You're thinking this file shouldn't have been listed because it's owned by user123
and has the user's r
bit set. However, it's listed because it matches the second criterion (it's owned by group user123
and has the group's r
bit unset).
Your second version works because of one of de Morgan's laws: negating the logical ORing of a group of statements is logically equivalent to ANDing the negation of the individual statements. In other words:
! ( A || B || C ) == ( !A && !B && !C )
So the working find
is looking for a file that
- Is not (owned by user
user123
and readable by said user) AND - Is not (owned by group
user123
and readable by said group) AND - Is not world-readable.
while the first find
is looking for a file that
- Is owned by user
user123
and not readable by said user OR - Is owned by group
user123
and not readable by said group OR (if you had completed it) - Is not world-readable
So a file matching ANY of the above 3 criteria (and not necessarily all) would be listed as you have seen.
Edit
Incidentally (after viewing your profile), I'm a big fan of your O'Reilly book :)