File permissions explained
Nine bits, and what they mean on a directory instead
A file's mode is nine bits: read, write and execute, for the owner, the group and everyone else.
chmod sets them and umask decides the defaults. On directories the same bits mean different
things, and chmod treats setuid and setgid differently again.
For the flags themselves rather than the model behind them, see chmod.
Owner, group, and everyone else
Every file and directory has one owner (a user) and one group, plus a category for
everyone else. chown sets those two; chmod below sets what each of
them may do. Each of those three gets its own set of three permissions: read,
write, and execute. ls -l shows all nine bits in a row:
ls -l script.sh
-rwxr-xr-x 1 user user 0 Jul 5 15:53 script.sh
Read that as three groups of three: rwx (owner can read, write, execute), r-x (group can
read and execute, not write), r-x (everyone else, same as group here). The leading - is the
file type (- for a regular file, d for a directory, l for a symlink).
For a directory, the three bits mean something slightly different: r lets you list its
contents, w lets you create or delete entries inside it, and x lets you cd into it or
access anything inside by name at all. A directory with r-- and no x shows you filenames
with ls, but you can't open, read, or even stat any file inside it, because reaching a file
requires traversing the directory, and traversal needs x.
Numeric mode: three digits, one per actor
chmod accepts a three-digit octal number: owner, group, other, in that order. Each digit sums
r=4, w=2, x=1:
chmod 755 script.sh # owner: rwx (7), group: r-x (5), other: r-x (5)
chmod 644 notes.txt # owner: rw- (6), group: r-- (4), other: r-- (4)
755 is the standard mode for a script or executable; 644 is the standard mode for a plain
data file. If a permission set seems unreadable at a glance, add up which bits are on: rwx is
4+2+1, r-x is 4+1, r-- is just 4.
Symbolic mode: change one thing without recomputing the whole number
Numeric mode replaces all nine bits at once. Symbolic mode adjusts specific bits and leaves the
rest alone, using u (owner/user), g (group), o (other), or a (all), followed by +,
-, or =, followed by which permission:
chmod u+x script.sh # add execute for the owner only
chmod go-w notes.txt # remove write for group and other
chmod u+x,g-w file.txt # combine multiple changes with a comma
Use symbolic mode when you want to flip one bit without having to work out the numeric mode for the other eight first.
umask: the permissions new files don't get
umask doesn't set permissions directly. It's a mask that's subtracted from the default mode
new files and directories would otherwise get. The Debian default is 0022:
umask
0022
A new file normally starts at 666 (rw-rw-rw-) before the mask is applied, and a new
directory at 777. With a umask of 022, the 022 bits (group-write, other-write) get
cleared, leaving new files at 644 and new directories at 755. This is why every plain file
you create lands at 644 without you ever running chmod: the umask is doing it for you, on
every process, for the lifetime of the shell session (or until something changes it).
setuid, setgid, and the sticky bit
Three special bits sit above the normal nine:
- setuid (
chmod u+s, numeric4000) on an executable makes it run with the file owner's privileges, not the caller's. This is howpasswdlets an ordinary user change their own password despite the password database being root-owned, and howsudoworks at all. It is also why a missing sudo cannot be worked around by copying the binary somewhere. - setgid (
chmod g+s, numeric2000) on a directory makes new files created inside inherit the directory's group, instead of the creating user's primary group. Useful for shared team directories where everything should stay group-owned consistently. - sticky (
chmod +t, numeric1000) on a directory restricts deleting or renaming a file inside it to the file's owner (or root), even if others have write access to the directory./tmpis the canonical example: everyone can create files there, but you can't delete someone else's.
ls -l shows these as a letter in the execute slot: rwsr-xr-x (setuid), rwxrwsr-x (setgid),
rwxr-xr-t (sticky, with the directory's own execute bit also set; a capital T or S means
the special bit is set but the underlying execute bit isn't).
chmod on a directory preserves setuid and setgid
A three-digit chmod on a directory does not clear a setuid or setgid bit that is already
there:
mkdir shared
chmod 2775 shared # set setgid: drwxrwsr-x
chmod 755 shared # "reset" to 755... or so it seems
stat -c "%a %A" shared
2755 drwxr-sr-x
The setgid bit survives, even though the same three-digit chmod on a regular file clears those
bits without hesitation. This is documented, deliberate GNU coreutils behaviour rather than a
bug: man chmod says so under "SETUID AND SETGID BITS": "For directories chmod
preserves set-user-ID and set-group-ID bits unless you explicitly specify otherwise." To clear
them on a directory, you need an explicit fourth digit (chmod 0755 shared), a leading minus
(chmod -6000 shared), a leading equals (chmod =755 shared), or symbolic mode
(chmod g-s shared). The sticky bit doesn't get this special treatment: chmod 1755 followed
by chmod 755 clears it as you'd expect.
So if you're writing a script that "resets" permissions across a tree that might contain setgid
directories (a shared project folder, a mail spool), a bare chmod -R 755 silently leaves
setgid in place. If you intend to strip it, say so explicitly.
Common misconceptions
- "
chmod -Ronly touches files, directories are safe." It touches everything matching, directories included, and removing the execute bit from a directory withchmod -R 644makes it (and everything inside) inaccessible, even to its owner. A directory reduced torw-r--r--refusesls,cd, or any access to its contents until the execute bit comes back. - "Numeric chmod always sets exactly what I typed." True for files. Not entirely true for directories and the setuid/setgid bits.
- "If I own the file, I can always read it." Not if a directory somewhere in the path lacks the execute bit for you. Permission checks happen at every level of the path, not just on the final file.
- "A permission-denied error and a missing-file error look the same to a script." They don't have to. See Exit codes and error handling for how to tell failure modes apart instead of treating every non-zero exit the same way.