Backend / Linux & bash / 11_file_permissions.md

File Permissions and Ownership

Updated 6 interview angles 8 min read source
On this page14
  1. ls -l output
  2. What r/w/x mean
  3. Octal mode
  4. chmod — change permissions
  5. chown — change ownership
  6. umask — default permissions for new files
  7. Special permission bits
  8. Why “permission denied” even after chmod +x
  9. Recursive chmod patterns
  10. ACLs (advanced)
  11. Capabilities (modern alternative to setuid)
  12. Common pitfalls
  13. Common interview confusions
  14. Interview angle

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

text
-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

bash
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
bash
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

bash
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:

text
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:

bash
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)
bash
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

text
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

bash
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

text
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:

  1. No x on a parent directory. Can’t cd into a dir without x on it. ls -ld /path/to/dir shows the dir’s perms.
  2. 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.
  3. Filesystem is mounted noexec. mount | grep <fs> to check; common on /tmp in hardened systems.
  4. SELinux / AppArmor. Different layer; getenforce, dmesg for denied messages.
  5. 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:

bash
# removes x from directories — site breaks
chmod -R 644 /var/www

Without x on directories, you can’t traverse them. Right way:

bash
find /var/www -type d -exec chmod 755 {} +
find /var/www -type f -exec chmod 644 {} +

Or with chmod -R u+rwX,go=rX:

bash
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:

bash
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:

text
-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:

bash
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 -R on a tree owned partly by other users changes their files too. Easy to break with sudo.
  • Removing x from a directory thinking it’s like removing read — kills traversal. Use capital X in recursive chmod.
  • umask 000 because 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 755 means read for everyone.” — 755 = rwxr-xr-x = owner all, group r+x, others r+x. Yes, includes read for everyone.
  • x on a directory means I can list it.”r lets you list; x lets 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 6

  • “What does chmod 755 mean?”rwxr-xr-x. Owner: read/write/execute. Group: read/execute. Others: read/execute. Standard for executables and directories.
  • “What’s the difference between r and x on a directory?”r lets you list filenames; x lets you traverse (cd) and access files by name. Both usually granted together; x without r (--x) is a “lookup-only” directory.
  • “What’s umask?” — bitmask subtracted from default permissions when files are created. Default 022 means group/others lose write. Set 077 for “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/passwd is 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 /tmp so users can write there but can’t delete each other’s files.
  • “How do you safely chmod -R on a tree of files and dirs?” — separate dirs and files: find . -type d -exec chmod 755 {} +; find . -type f -exec chmod 644 {} +. Or use capital X: chmod -R u=rwX,g=rX,o=rX ..