Why this matters
"Permission denied" stops scripts, web servers and deployments every day. Sometimes it is protecting you (a private SSH key must not be readable by others, or ssh refuses to use it); sometimes it is a mistake (a script you forgot to make executable, a log directory the service cannot write to). Reading the nine permission letters fluently and changing them precisely is a core skill, and the rules the kernel applies are short enough to learn exactly.
The nine bits
Every inode stores, next to its owner (UID) and group (GID), a set of permission bits. ls -l shows them as nine letters after the type character:
- rwx r-x r--
│ ─── ─── ───
│ │ │ └── other: everyone else
│ │ └─────── group: members of the file's group
│ └──────────── user: the file's owner
└──────────────── type (- file, d directory, l link)
Each triplet is read, write, execute; a letter means granted, - means not. For a regular file:
| Bit | Allows |
|---|---|
r | reading the content (cat, cp from it, opening it in an editor) |
w | changing the content (writing, appending, truncating) |
x | running it as a program (./file) |
Directories use the same bits with different meanings; that gets its own section in the next lesson, because it surprises everyone.
Note what w on a file does not allow: deleting or renaming it. Those change the directory (they remove or edit a name in it), so they depend on the directory's permissions.
How the kernel decides
When a process opens a file, the kernel picks exactly one triplet:
is the process's UID the file's owner? ── yes → use the user triplet
│ no
is the file's GID among the process's groups? ── yes → use the group triplet
│ no
use the other triplet
then checks whether the requested access (read, write or execute) is in it. If not: Permission denied (the kernel's error code EACCES). The first matching class wins and the others are ignored, which leads to the odd but logical case in the second quiz: an owner with --- is locked out even if the group or others have access. (Root, UID 0, skips the check, as the previous lesson explained.)
Seeing permissions
Open the shell for this lesson:
$ cd perms
$ ls -l
-rw-r--r--. 1 learner learner 14 Nov 1 15:30 frozen.txt
-rw-r--r--. 1 learner learner 51 Nov 1 15:30 run.sh
-rw-r--r--. 1 learner learner 26 Nov 1 15:30 secret.txt
-rw-r--r--. 1 learner learner 11 Nov 1 15:30 shared.txt
$ stat -c '%A %U:%G %n' secret.txt
-rw-r--r-- learner:learner secret.txt
Right now anyone on the system could read secret.txt, and run.sh cannot be run:
$ ./run.sh
bash: ./run.sh: Permission denied
$ echo $?
126
Status 126 means "found, but could not execute", as you saw in module 1.
chmod: change mode
chmod changes the permission bits. Only the file's owner (or root) may do it. The symbolic form reads almost like English:
chmod [who][op][perms] FILE...
│ │ └─ r, w, x (any combination)
│ └───── + add, - remove, = set exactly (others cleared)
└────────── u user/owner, g group, o other, a all (default: all)
$ chmod u+x run.sh # owner may execute
$ chmod go-rwx secret.txt # group and other lose everything
$ chmod g+w shared.txt # group may write
$ chmod a-w frozen.txt # nobody may write
$ chmod u=rw,go=r notes.txt # set exactly: rw-r--r--
$ ls -l
-r--r--r--. 1 learner learner 14 Nov 1 15:30 frozen.txt
-rwxr--r--. 1 learner learner 51 Nov 1 15:30 run.sh
-rw-------. 1 learner learner 26 Nov 1 15:30 secret.txt
-rw-rw-r--. 1 learner learner 11 Nov 1 15:30 shared.txt
Several changes can be joined with commas, and several files given at once. +x without a "who" applies to all classes (minus whatever your umask masks, next lesson). chmod -R applies the change recursively to a directory tree; be careful with it, because files and directories usually need different bits.
Write protection is not much protection from yourself
$ echo "change" >> frozen.txt
bash: frozen.txt: Permission denied
$ rm frozen.txt
rm: remove write-protected regular file 'frozen.txt'? n
The append fails, as it should. But rm only asks: deleting depends on the directory, which you own and may write. And as the owner you can always chmod u+w it back. Removing w from your own files protects against accidents, not against yourself.
The execute bit and scripts
To run a file as ./name, the kernel needs the x bit for your class. For a compiled program, the kernel loads the machine code. For a text file, the kernel looks at the first line: if it starts with #! (the shebang), the rest of the line names the interpreter to run it with:
./run.sh → kernel reads "#!/bin/bash" → runs /bin/bash ./run.sh
Module 8 covers this in detail. Note that bash run.sh works even without the x bit: then bash is the program being executed, and it merely reads run.sh, which needs only r. That is why the code exercises in this course run as bash script.sh.
Changing owner and group
chown user file changes the owner, chgrp group file the group, and chown user:group file both. Giving a file away to another user requires root (otherwise you could create files that count against someone else's disk quota, or fake authorship). An ordinary user may change a file's group, but only to a group they belong to. In this sandbox you are only in the group learner, so there is nothing to change to; on a real system you would use it like this:
$ chgrp developers shared.txt # you must be in "developers"
$ sudo chown www-data:www-data /srv/site/uploads
Common mistakes
- Forgetting
xon a script../script.sh: Permission denied, status 126.chmod u+x(or run it asbash script.sh). chmod 777"to make it work". That gives every user on the system write access. Find which class actually needs which bit instead.- Thinking
won a file controls deletion. Deletion is controlled by the directory. - Expecting permissions to add up. Owner bits apply to the owner even if group or other bits are more generous.
chmod -R a+xon a tree. Makes every data file executable. Next lesson showsX(capital), which adds execute only where it makes sense.- Private keys too open. ssh refuses keys readable by others (
UNPROTECTED PRIVATE KEY FILE); the fix ischmod 600 ~/.ssh/id_ed25519.
Recap
- Nine bits: rwx for user (owner), group and other; shown by
ls -landstat -c %A. - File bits: r read content, w change content, x execute.
- The kernel uses exactly one class: owner, else group, else other.
chmod [ugoa][+-=][rwx], comma-separated, several files,-Rfor trees; only the owner or root may chmod.chownneeds root;chgrpworks only to groups you are in.- Status 126 means "found but not executable".
Going further: run ls -l /usr/bin/passwd and notice an s where the owner's x should be. What could that mean, given that passwd must edit /etc/shadow, which only root can write? The next lesson answers it.