File Permissions

The Permission String

Run ls -l and every line starts with a 10-character string:

-rwxr-xr--  1 alice devs  4096 Apr 28 09:12 script.sh

Each character has a fixed meaning:

-
r
w
x
r
-
x
r
-
-
type
owner (u)
group (g)
other (o)

File type character

Permission bits

Each of the three groups (owner, group, other) has three bits:

CharNameOn a fileOn a directory
rreadopen & read contentlist entries (ls)
wwritemodify contentcreate/delete/rename entries
xexecuterun as programenter directory (cd)
-(none)permission not granted
Note Write on a directory controls whether you can add or remove names inside it — regardless of the permissions on the files themselves. You can delete a file you cannot read if you have write+execute on its parent directory.

Octal Notation

Each group of three bits maps to a single octal digit (0–7). Read=4, Write=2, Execute=1. Add them together.

OctalBinarySymbolicMeaning
7111rwxfull access
6110rw-read & write
5101r-xread & execute
4100r--read only
3011-wxwrite & execute
2010-w-write only
1001--xexecute only
0000---no access

A full permission set is three octal digits: owner · group · other.

755  →  rwxr-xr-x   # typical executable / directory
644  →  rw-r--r--   # typical regular file
600  →  rw-------   # private file (SSH keys, etc.)
700  →  rwx------   # private directory

chmod

chmod changes permissions. Two syntaxes exist: octal and symbolic.

Octal syntax

chmod 644 file.txt
chmod 755 /usr/local/bin/myscript
chmod -R 750 /var/myapp     # recursive

Symbolic syntax

Format: [who][op][perms]

chmod u+x script.sh          # add execute for owner
chmod go-w file.txt          # remove write from group and other
chmod a=r readonly.txt       # set everyone to read-only
chmod u=rwx,g=rx,o= dir/    # owner full, group r+x, other nothing

chown & chgrp

Every file has an owner (a user) and an owning group.

chown alice file.txt             # change owner
chown alice:devs file.txt        # change owner and group
chown :devs file.txt             # change group only (chgrp equivalent)
chown -R www-data:www-data /var/www/html   # recursive
Requires root Only root can change file ownership. Regular users can change the group only to one they belong to.

umask

umask sets the default permissions mask applied when new files and directories are created. It works by subtracting bits from the maximum defaults (666 for files, 777 for directories).

umask          # show current mask
umask 022      # set mask

With umask 022:

Files:       666 - 022 = 644  (rw-r--r--)
Directories: 777 - 022 = 755  (rwxr-xr-x)

With umask 077 (restrictive — common for home dirs):

Files:       666 - 077 = 600  (rw-------)
Directories: 777 - 077 = 700  (rwx------)

Set permanently in ~/.bashrc, ~/.profile, or /etc/profile.

Special Permission Bits

A fourth octal digit sits in front of the three standard digits, controlling three special bits.

BitOctalOn fileOn directory
setuid (SUID)4000runs as file owner, not caller(no standard effect)
setgid (SGID)2000runs as owning groupnew files inherit directory's group
sticky bit1000(legacy, mostly ignored)only owner/root can delete entries

In ls output

-rwsr-xr-x  # SUID set — 's' replaces 'x' in owner field
-rwxr-sr-x  # SGID set — 's' replaces 'x' in group field
drwxrwxt   # sticky — 't' replaces 'x' in other field
-rwSr--r--  # capital S: bit set but execute NOT set

Setting special bits

chmod 4755 /usr/bin/mysetuid   # SUID + 755
chmod 2775 /shared/project     # SGID + 775
chmod 1777 /tmp                # sticky + 777

# Symbolic equivalents:
chmod u+s file
chmod g+s dir/
chmod +t /tmp
Security SUID/SGID on executables are high-value attack targets. Audit regularly: find / -perm /6000 -type f 2>/dev/null. Never set SUID on shell scripts — Linux ignores it, but the intent itself is a red flag.

Access Control Lists (ACLs)

Standard Unix permissions allow exactly one owner and one group. ACLs extend this to arbitrary users and groups without changing ownership.

# View ACL
getfacl file.txt

# Grant bob read+write
setfacl -m u:bob:rw file.txt

# Grant group contractors read-only
setfacl -m g:contractors:r file.txt

# Remove a specific ACL entry
setfacl -x u:bob file.txt

# Remove all ACL entries
setfacl -b file.txt

When an ACL is present, ls -l shows a + after the permission string: -rw-r--r--+. The effective mask (mask:: in getfacl output) acts as an upper bound on ACL permissions — it does not affect the owning user.

How Permission Checks Work

The kernel checks in order and stops at the first match:

  1. Is the process UID == file owner UID? → apply owner bits.
  2. Is the process GID (or any supplementary GID) == file group? → apply group bits.
  3. Neither → apply other bits.

Only one set of bits applies. If you own a file but the owner bits deny access, the group or other bits are not consulted — even if they would grant it.

root exception UID 0 (root) bypasses all read/write checks. Execute is still checked: root needs at least one x bit set somewhere on a file to execute it.

Quick Reference

# Common permission sets
chmod 600 ~/.ssh/id_rsa          # private key: owner read+write only
chmod 644 ~/.ssh/id_rsa.pub      # public key: world-readable
chmod 700 ~/.ssh                 # .ssh directory: owner only
chmod 755 /var/www/html          # web root: served by www-data
chmod 640 /etc/app/config.env    # app config: owner rw, group r

# Audit / inspection
stat file.txt                    # full file metadata incl. permissions
find . -perm 777                 # find world-writable files
find . -perm /o+w -type f        # same with bitwise match
find / -perm /4000 -type f       # find SUID files