Unix File Systems and Permissions

Permissions are one of those things you think you understand until you have to explain them to someone else. The nine-character string on ls -l output — -rw-r--r-- — looks simple, but there are enough edge cases hiding in there to keep things interesting. This covers the core concepts and the parts that actually trip people up.

The Directory Tree

Everything in Unix lives under /. There's no C: drive, no separate volumes by letter — it's one tree, and other disks get mounted into it at whatever point you choose. Key spots:

  • /bin, /usr/bin — system and user binaries
  • /etc — configuration files (plain text, almost always)
  • /home — user home directories
  • /var — logs, caches, mail, anything that grows over time
  • /tmp — temporary files, usually wiped on reboot
  • /proc, /sys — not real files; the kernel exposing itself as a filesystem

File Types

The first character of ls -l output tells you what something actually is:

-  regular file
d  directory
l  symbolic link
c  character device (terminals, serial ports)
b  block device (disks)
s  socket
p  named pipe (FIFO)

Most of the time it's - or d. The device files live in /dev and you generally don't touch them directly.

Reading the Permission Bits

Take this output:

-rw-r--r-- 1 jason users 4096 Feb  5 12:34 notes.txt

After the file type character, the next nine characters are three groups of three: owner, group, others. Each group is rwx — read, write, execute. A dash means the permission isn't set.

rw-   owner (jason) can read and write, not execute
r--   group (users) can read only
r--   everyone else can read only

For directories, x means you can enter it (traverse). Without execute on a directory, you can't cd into it even if you can read the listing.

Changing Permissions with chmod

Two ways to do it — symbolic and numeric. I use numeric when I know exactly what I want, symbolic when I'm making a targeted change.

Numeric

Read = 4, write = 2, execute = 1. Add them up for each group:

$ chmod 755 script.sh    # rwxr-xr-x  (owner full, others read+execute)
$ chmod 644 config.txt   # rw-r--r--  (owner read+write, others read)
$ chmod 600 id_rsa       # rw-------  (owner only, private key territory)

Symbolic

$ chmod u+x script.sh    # add execute for owner
$ chmod go-w file.txt    # remove write from group and others
$ chmod a+r readme.txt   # add read for all (a = all)

Ownership: chown and chgrp

$ chown jason file.txt           # change owner
$ chown jason:users file.txt     # change owner and group together
$ chgrp www-data /var/www/html   # change group only
$ chown -R jason:jason ~/project # recursive, useful after copying files as root

You need root (or sudo) to give a file to another user. You can change your own files to a group you belong to without sudo.

Special Permissions

Three extra bits that don't show up in the basic rwx display — they sit in a fourth position in the numeric notation.

Setuid (4xxx)

When set on an executable, it runs as the file's owner rather than whoever ran it. passwd is the classic example — it needs root access to modify /etc/shadow, but ordinary users need to be able to run it:

$ ls -l /usr/bin/passwd
-rwsr-xr-x 1 root root 59976 ... /usr/bin/passwd

The s in the owner execute slot is setuid. On a directory, setuid is ignored on Linux (though not on some BSDs).

Setgid (2xxx)

On an executable, same idea — runs with the file's group. On a directory, new files created inside inherit the directory's group instead of the creator's primary group. Useful for shared project directories:

$ chmod 2775 /srv/shared
$ ls -ld /srv/shared
drwxrwsr-x 2 root devteam 4096 ... /srv/shared

Sticky Bit (1xxx)

On a directory, only the file's owner (or root) can delete or rename files inside, even if others have write permission on the directory. /tmp uses this — everyone can write there, but you can't delete each other's files:

$ ls -ld /tmp
drwxrwxrwt 12 root root 4096 ... /tmp

The t at the end is the sticky bit.

Access Control Lists

Standard Unix permissions only give you three buckets — owner, group, everyone else. ACLs let you add specific per-user or per-group permissions on top of that. Not every filesystem supports them, but ext4, XFS, and Btrfs all do.

$ getfacl file.txt                        # view current ACL
$ setfacl -m u:sarah:rw file.txt          # give sarah read+write
$ setfacl -m g:contractors:r file.txt     # give contractors read
$ setfacl -x u:sarah file.txt             # remove sarah's entry
$ setfacl -b file.txt                     # remove all ACL entries

When a file has an ACL, ls -l shows a + at the end of the permission string.

Finding Files by Permission

Sometimes you need to audit what's out there. The find command handles this well:

$ find /usr/bin -perm -4000              # find all setuid binaries
$ find /home -perm 777                   # world-writable files (usually wrong)
$ find /var/www -not -user www-data      # files not owned by web user