File Permissions and Ownership
Every file has an owner (user), a group, and three sets of read/write/execute bits — for owner, group, others. Plus three special bits (setuid, setgid, sticky). The interview question is “what does chmod 755 mean?” and “why is my script ‘permission denied’ even though I chmod +x’d it?”
ls -l output
-rwxr-xr-x 1 alice staff 1234 Jan 15 10:30 script.sh
Decoded:
| Position | Means |
|---|---|
- |
type (- file, d dir, l symlink, c char dev, b block dev, s socket, p pipe) |
rwx |
owner permissions: read, write, execute |
r-x |
group permissions |
r-x |
others’ permissions |
1 |
hard link count |
alice |
owner |
staff |
group |
1234 |
size in bytes |
Jan 15 10:30 |
mtime |
script.sh |
name |
What r/w/x mean
For files:
| Bit | Allows |
|---|---|
| r | read the file content |
| w | modify the file content |
| x | execute as a program |
For directories:
| Bit | Allows |
|---|---|
| r | list filenames in the directory |
| w | create / delete / rename files in the directory |
| x | enter the directory (cd), access files inside by name |
The directory x bit is critical and confusing. Without x on a directory, you can’t cd into it or access any file by name — even if you have read on the file itself.
Octal mode
Each rwx triplet maps to a single octal digit:
| rwx | Binary | Octal |
|---|---|---|
| — | 000 | 0 |
| –x | 001 | 1 |
| -w- | 010 | 2 |
| -wx | 011 | 3 |
| r– | 100 | 4 |
| r-x | 101 | 5 |
| rw- | 110 | 6 |
| rwx | 111 | 7 |
Mnemonic: r=4, w=2, x=1. Sum the bits you want.
Common modes:
| Octal | Symbolic | Use |
|---|---|---|
| 644 | rw-r--r-- |
typical file (owner can edit; everyone can read) |
| 755 | rwxr-xr-x |
typical executable / dir (owner full; others read+execute) |
| 600 | rw------- |
private file (only owner can read/write — for SSH keys, secrets) |
| 700 | rwx------ |
private directory |
| 777 | rwxrwxrwx |
“world writable” — almost always wrong |
| 666 | rw-rw-rw- |
world-writable file — almost always wrong |
chmod — change permissions
chmod 755 script.sh # octal mode
chmod +x script.sh # add execute (for everyone)
chmod u+x script.sh # add execute for owner only
chmod g-w file # remove group write
chmod o= file # remove all others' permissions
chmod a+r file # add read for all (a = u+g+o)
chmod -R 755 dir # recursive
Symbolic syntax:
| Who | + - = | What |
|---|---|---|
| u (user/owner), g (group), o (others), a (all) | + add, - remove, = set exactly | r, w, x |
chmod u=rwx,g=rx,o= file # owner all, group rx, others none
chmod -R u+rwX,go=rX dir # capital X = execute only for dirs / already-executable files
Capital X is “set execute only on directories or files that already have any execute bit.” Useful for chmod -R on a tree of files + dirs (you want dirs traversable but most files don’t need x).
chown — change ownership
chown alice file # change owner
chown alice:staff file # change owner and group
chown :staff file # change group only
chown -R alice:staff /home/alice # recursive
chgrp staff file # change group only (alternative)
Only root can change owner. Owner can change group (to one they belong to).
umask — default permissions for new files
When you create a file, its mode is determined by the program’s request AND umask:
file_mode = requested_mode AND NOT umask
Default umask: 022. Means:
- Strip write for group and others.
- File requested as 666 → 666 AND NOT 022 = 644.
- Directory requested as 777 → 777 AND NOT 022 = 755.
To make new files private by default:
umask 077 # strip ALL group/others perms
touch newfile # 600
Set in ~/.bashrc for permanence.
Special permission bits
Three additional bits beyond rwx:
| Bit | Symbol | Octal (4-digit) | Effect |
|---|---|---|---|
| setuid | s in user x |
4xxx | run as the file’s owner, not the invoker |
| setgid | s in group x |
2xxx | run as the file’s group; on dirs: new files inherit dir’s group |
| sticky | t in others x |
1xxx | only the owner can delete/rename their files (used on /tmp) |
chmod 4755 file # setuid + 755
chmod u+s file # setuid via symbolic
chmod 2755 dir # setgid on dir
chmod 1777 /tmp # sticky on /tmp (default)
setuid example: passwd
ls -l /usr/bin/passwd
-rwsr-xr-x 1 root root ... /usr/bin/passwd
The s in user-execute = setuid. When any user runs passwd, it runs as root (so it can edit /etc/shadow). This is how non-root users can change their password.
setgid on directory
mkdir shared
chmod 2775 shared # setgid + 775
chgrp dev shared
Now any file created in shared inherits dev group (instead of the creator’s primary group). Used for shared workspaces.
Sticky bit on /tmp
ls -ld /tmp
drwxrwxrwt ... /tmp
The t at the end = sticky. Anyone can write to /tmp, but only the owner of a file can delete it. Without sticky, world-writable + everyone can delete = chaos.
Why “permission denied” even after chmod +x
Common causes:
- No
xon a parent directory. Can’tcdinto a dir withoutxon it.ls -ld /path/to/dirshows the dir’s perms. - Script’s shebang interpreter doesn’t exist or isn’t executable.
#!/bin/python(typo for#!/usr/bin/env python) → “no such file” disguised as permission error. - Filesystem is mounted noexec.
mount | grep <fs>to check; common on/tmpin hardened systems. - SELinux / AppArmor. Different layer;
getenforce,dmesgfor denied messages. - Script line endings are CRLF (Windows). Run
dos2unix script.sh.
The error “bad interpreter” is shebang-related; “permission denied” is unix permissions or noexec.
Recursive chmod patterns
The classic mistake:
chmod -R 644 /var/www # removes x from directories — site breaks
Without x on directories, you can’t traverse them. Right way:
find /var/www -type d -exec chmod 755 {} +
find /var/www -type f -exec chmod 644 {} +
Or with chmod -R u+rwX,go=rX:
chmod -R u=rwX,g=rX,o=rX /var/www
Capital X = “execute only on dirs or already-executable files.”
ACLs (advanced)
Standard rwx is owner/group/others — only one group. ACLs (Access Control Lists) let you grant per-user or per-group permissions:
setfacl -m u:bob:rwx file # give bob rwx
setfacl -m g:devs:rx dir # give devs group rx
getfacl file # show ACLs
Files with ACLs show a + after the rwx in ls -l:
-rw-r--r--+ 1 alice staff ...
Used in shared filesystems where one-group-per-file isn’t enough. Most apps don’t need ACLs.
Capabilities (modern alternative to setuid)
Setuid root = “run with full root powers” — overkill if you only need one privilege (e.g. bind to port < 1024).
Capabilities split root into ~40 distinct privileges:
sudo setcap 'cap_net_bind_service=+ep' /usr/bin/python3
# now python can bind to port 80 without being root
Cleaner and safer than setuid. Used by modern web servers (nginx, caddy) to bind low ports.
Common pitfalls
chmod 777 thing“to fix permissions” is the most common security antipattern. Means anyone can read/write/execute. Find the actual permission you need.chown -Ron a tree owned partly by other users changes their files too. Easy to break with sudo.- Removing
xfrom a directory thinking it’s like removing read — kills traversal. Use capitalXin recursive chmod. umask 000because something complained about permissions — now everything you create is world-readable/writable.- Setuid on a script — most kernels ignore setuid on scripts (security hole). Setuid only works on compiled binaries.
Common interview confusions
- “
chmod 755means read for everyone.” — 755 = rwxr-xr-x = owner all, group r+x, others r+x. Yes, includes read for everyone. - “
xon a directory means I can list it.” —rlets you list;xlets you traverse and access by name. They’re orthogonal. - “setuid lets you run as root.” — only if the file’s owner is root. setuid runs as the file’s owner, whoever that is.
- “Permission denied means file permissions.” — could be: directory traversal, noexec mount, SELinux, missing shebang interpreter, CRLF line endings. The error message is generic.
Interview angle
- “What does
chmod 755mean?” —rwxr-xr-x. Owner: read/write/execute. Group: read/execute. Others: read/execute. Standard for executables and directories. - “What’s the difference between
randxon a directory?” —rlets you list filenames;xlets you traverse (cd) and access files by name. Both usually granted together;xwithoutr(--x) is a “lookup-only” directory. - “What’s umask?” — bitmask subtracted from default permissions when files are created. Default
022means group/others lose write. Set077for “everything I create is private.” - “What’s setuid and when is it used?” — file runs as its owner instead of the invoker. Classic example:
/usr/bin/passwdis setuid root so users can change their passwords. Modern alternative: capabilities (more granular). - “What’s the sticky bit?” — on a directory, only the file owner can delete or rename their own files. Used on
/tmpso users can write there but can’t delete each other’s files. - “How do you safely
chmod -Ron a tree of files and dirs?” — separate dirs and files:find . -type d -exec chmod 755 {} +; find . -type f -exec chmod 644 {} +. Or use capitalX:chmod -R u=rwX,g=rX,o=rX ..