1. Introduction

Permissions are a defining feature of files. As one of the key parts of metadata, we can use the permission set just like any other identifying data point. In fact, knowing how to do that can be invaluable when trying to sort unstructured file datasets.

In this tutorial, we search for files based on permissions. First, we briefly refresh our knowledge of special permissions. After that, we show a sample dataset and its permissions. Finally, we go over different options that a standard tool offers for searches based on permission sets.

We tested the code in this tutorial on Debian 12 (Bookworm) with GNU Bash 5.2.15. It should work in most POSIX-compliant environments unless otherwise specified.

2. Special Permissions

Normally, Linux objects have three sets of three permission types, each with a three-digit octal representation:

|           | [u]ser | [g]roup | [o]thers |
| [r]ead    | 400    | 040     | 004      |
| [w]rite   | 200    | 020     | 002      |
| e[x]ecute | 100    | 010     | 100      |

To calculate the complete permission set for a given filesystem object, we sum all applicable modes:

uuugggooo = u+r u+w   g+r   o+r
rw-r--r-- = 400+200 + 040 + 004 = 600 + 40 + 4 = 644

However, Linux also provides so-called special permissions.

2.1. The Extra Bits

Prepending an octal number to the mode above provides support for three more permissions:

  • 4 = suid (setuid): force file to run with owner privileges regardless of execution context
  • 2 = sgid (setgid): force file to run with owner group privileges regardless of execution context or force new files in directory to have same owner group
  • 1 = sticky: prevent deletion of files by users other than the file owner, directory owner, or root

Since these special bits aren’t related to specific entities like owners or others, they have a separate table:

|          | [s]pecial |
| [s]uid   | 4000      |
| [s]gid   | 2000      |
| s[t]icky | 1000      |

So, suid sets the user ID, and sgid sets the group ID before running the file. However, setting sgid on a directory means all newly-created files would inherit the owner group from the directory object.

On the other hand, sticky ensures files that coexist in a public directory are not deletable by non-owner and non-root users.

The final permission set of an object is calculated the same way with special permissions, but their character representation differs:

uuugggooo = u+r u+S    g+r g+S    o+r o+T
r-sr-Sr-T = 400+4000 + 040+2000 + 004+1000 = 4400 + 2040 + 1004 = 7444

Although they use the same S letter, suid and sgid differ in position:

  • suid is S in the owner [u]ser section
  • sgid is S in the owner [g]roup section
  • sticky is T in the [o]thers section

Now, let’s see how to change these permissions.

2.2. Setting Special Permissions

As with the regular ones, we can use chmod with both modes and symbols to assign special permissions.

Using symbols, we can easily add or remove a permission:

$ chmod u+s file

In this case, we use set suid via the s character in the [u]ser section. In fact, the capital S with chmod sets suid exclusively.

Of course, we can also specify the complete permission set:

$ chmod u=rw,g=r,o=rt file

Yet, it’s usually easier, albeit perhaps less readable, to assign absolute permissions with modes:

$ chmod 1644 file

In both cases, we set read and write permissions for the owner user, and read permissions for everyone else, as well as the sticky bit.

Importantly, only the file owner or root can set special permissions.

3. Sample Dataset

Permissions are an inseparable part of files in Linux filesystems.

To begin with, let’s see our sample dataset with the tree command:

$ tree
├── file.ro
├── file.rw
├── file.sgid
├── file.suid
└── subdir
    ├── subfile.sgid
    └── subfile.suid

2 directories, 6 files

Although not immediately obvious from the snippet, tree even color-codes the special permissions:

  • sgid: yellow
  • suid: red
  • sticky: blue

For convenience, we used the general symbolic modes as extensions.

Let’s use the basic ls command with its [-l]ong list and [-R]ecursive flags to check the permissions for a sample set of files and directories under the current path:

$ ls -lR
total 24
-r--r--r-- 1 baeldung baeldung  444 Nov 11 06:56 file.ro
-rw-rw-rw- 1 baeldung baeldung  666 Nov 11 06:56 file.rw
-rw-r-Sr-- 1 baeldung baeldung 2644 Nov 11 06:56 file.sgid
-rwSr--r-- 1 baeldung baeldung 4644 Nov 11 06:56 file.suid
drw-r--r-T 2 baeldung baeldung 4096 Nov 11 06:56 subdir

total 12
-r--r-Sr-- 1 baeldung baeldung 2444 Nov 11 06:56 subfile.sgid
-r-Sr--r-- 1 baeldung baeldung 4444 Nov 11 06:56 subfile.suid

Same as tree, ls uses colors to highlight permission differences. For convenience, we’ve set the sizes to the permissions mode of each object.

4. Using find With Owners and Permissions

The find command is a versatile tool for locating filesystem objects based on different criteria. One possible metadata point we can use is the file permissions.

In most examples below, we use the -printf option to output more reference information for each result:

  • %M symbolic mode
  • %p filename
  • %m octal mode

So, let’s explore different cases with our sample dataset.

4.1. Owner and Owning Group

Files can have different owners and owning groups. To filter by those, we can use the -user and -group:

$ find -user baeldung -group baeldung

These switches reference the /etc/passwd and /etc/group files to ensure the supplied entities exist.

4.2. Exact Permissions

If we know the exact permission set of a file, we can search for it by supplying that to the -perm option of find:

$ find -perm 666 -printf "%M %p %m\n"
-rw-rw-rw- ./file.rw 666

In particular, we specified 666 and got the only file with these permissions.

Let’s try it with symbols:

$ find -perm u=rw,g=rw,o=rw -printf "%M %p %m\n"
-rw-rw-rw- ./file.rw 666

Despite being a bit more cumbersome due to the comma separators, it produces the same result.

Importantly, specifying only partial permissions wouldn’t work:

$ find -perm 444 -printf "%M %p %m\n"
-r--r--r-- ./file.ro 444

Although we have the file.ro file with the 444 permission set and subfile.sgid with 4444, we actually search for 0444, which matches only the former.

4.3. Any Permissions

By default, -perm takes absolute permissions:

$ find -perm g=r

This command may look as if it should return all files with owner group read permissions, but it actually searches for the octal mode 040.

Should we want to partially match the current permission set of a file, i.e., match any of the parts, we prefix that set with a / forward slash:

$ find -perm /g=r -printf "%M %p %m\n"
drwxr-xr-x . 755
-r--r--r-- ./file.ro 444
-rwSr--r-- ./file.sgid 4644
-rw-r--r-T ./test 1644
drw-r--r-T ./subdir 1644
-r-Sr--r-- ./subdir/subfile.suid 4444
-r--r-Sr-- ./subdir/subfile.sgid 2444
-rw-r-Sr-- ./file.suid 2644
-rw-rw-rw- ./file.rw 666

Thus, we see all files where the owner group has read permissions.

The same is valid when using modes:

$ find -perm /020 -printf "%M %p %m\n"
-rw-rw-rw- ./file.rw 666

Here, we find the only file with write rights for the owning group.

Importantly, the more permissions we specify, the more criteria we provide for a possible match, as any that match are returned:

$ find -perm /220 -printf "%M %p %m\n"
drwxr-xr-x . 755
-rwSr--r-- ./file.sgid 4644
-rw-r--r-T ./test 1644
drw-r--r-T ./subdir 1644
-rwSr--r-- ./file.suid 4644
-rw-rw-rw- ./file.rw 666

Here, we added write permissions for the user as a criterion. In other words, when using /, adding more to the permission set query can only increase, not decrease, the number of results.

4.4. Specified Permissions

To search for all specified permissions without matching unspecified (0) ones, we can employ the dash prefix.

For just a single pick from the owner [u]ser, owning [g]roup, and [o]thers, there’s no difference between the / and prefixes:

$ find -perm -020 -printf "%M %p %m\n"
-rw-rw-rw- ./file.rw 666

However, when we specify at least two of the permission parts, the results change:

$ find -perm -220 -printf "%M %p %m\n"
-rw-rw-rw- ./file.rw 666

Unlike the example with /, having a in front of 220 forces find to show the only file where both the user and group have write permissions.

4.5. Special Permissions

Naturally, we can work with special permissions as well by prepending the first octal mode or using s and t, respectively.

So, we can show a search for any objects with the sticky bit:

$ find -perm /1000 -printf "%M %p %m\n"
drw-r--r-T ./subdir 1644

Now, let’s use symbolic permissions to find setuid objects with write permissions for the user:

$ find -perm -u=ws -printf "%M %p %m\n"
-rwSr--r-- ./file.suid 4644

Lastly, we can list objects with exact permissions that include setgid:

$ find -perm 2444 -printf "%M %p %m\n"
-r--r-Sr-- ./subdir/subfile.sgid 2444

This way, we can include special permissions as the search criteria.

4.6. Negative Searches

Although a general feature, negation can be critical for searches based on owners and permissions:

$ find ! -user baeldung

In this case, we see that the owning users of the current directory isn’t baeldung.

We can do the same with permissions:

$ find ! -perm -644 -printf "%M %p %m\n"
-r--r--r-- ./file.ro 444
-r--r-Sr-- ./subdir/subfile.suid 2444
-r-Sr--r-- ./subdir/subfile.sgid 4444

Here, we find all files that don’t have all specified permissions in 644. While the read-only permission for the owning group and others isn’t rare, most files do permit writes for the user, so we only get the three that don’t.

Naturally, we can use -and, -or, and () parentheses to enhance our criteria further.

5. Summary

In this article, we talked about special permissions, as well as ways to use the POSIX standard find command to search for files based on different permissions-related criteria.

In conclusion, permissions are a vital part of system administration, and being able to locate objects based on them can be indispensable when working with filesystems.