DEV Community

Cover image for Linux Permissions, Explained by Breaking Them on Purpose" published: true
The Linux Camp
The Linux Camp

Posted on

Linux Permissions, Explained by Breaking Them on Purpose" published: true

Most permission tutorials start with the table. Nine bits, three triplets, r is 4, w is 2, x is 1. You read it, you nod, and a week later you are pasting chmod 777 from a forum answer like everyone else.

This one runs backwards. We break things on purpose, read the exact error each breakage produces, and work out what the system was checking. The table arrives at the end, where it stops being trivia and becomes the name for something you have already felt.

You need any Linux machine: a VM, a cheap cloud box, a Raspberry Pi, WSL on Windows. Everything happens inside one throwaway folder, and the worst outcome available to you is deleting that folder. Twenty minutes.

The three questions

Hold one model in your head the whole way. Every time you touch a file, the kernel, the core of Linux that referees every file access, asks three questions in this order:

  1. Are you the owner of this file?
  2. If not, are you a member of the file's group?
  3. If neither, you are everyone else.

It takes the first category that matches you, reads that category's three permissions (read, write, execute), and stops. It never falls through to the next category to see whether some other category would have been kinder. That "and stops" is where all the strangeness lives, and Break 1 makes it hurt.

The sandbox

zayden@lab:~$ mkdir playground
zayden@lab:~$ cd playground
zayden@lab:~/playground$
Enter fullscreen mode Exit fullscreen mode

Everything below stays in there. When you are finished, the whole crime scene comes off in two commands:

cd ~              # step out of the playground first
rm -r playground  # delete the directory and everything inside it
Enter fullscreen mode Exit fullscreen mode

Use a normal user account that can run sudo, not a root login. Root skips almost every permission check, which would quietly spoil each experiment before it starts. Why root gets that treatment is Break 4's job.

Break 1: Lock yourself out of your own file

Make a file and look at it:

zayden@lab:~/playground$ echo "secret plans" > notes.txt
zayden@lab:~/playground$ ls -l notes.txt
-rw-r--r-- 1 zayden zayden 13 Jul 30 10:02 notes.txt
Enter fullscreen mode Exit fullscreen mode

That string of dashes and letters at the front is the permission readout. Ignore it for a few more minutes. Now remove every permission from everyone, including yourself:

zayden@lab:~/playground$ chmod 000 notes.txt
zayden@lab:~/playground$ ls -l notes.txt
---------- 1 zayden zayden 13 Jul 30 10:02 notes.txt
zayden@lab:~/playground$ cat notes.txt
cat: notes.txt: Permission denied
Enter fullscreen mode Exit fullscreen mode

You own this file. Your name is on it twice. The kernel refused you anyway, and the reason is that "and stops" rule. Question one: are you the owner? Yes. So it read the owner's three permissions, found no read there, and stopped. Whether the group or everyone else could read the file was never considered. First match, then verdict, no appeal.

So are you locked out forever?

zayden@lab:~/playground$ chmod 644 notes.txt
zayden@lab:~/playground$ cat notes.txt
secret plans
Enter fullscreen mode Exit fullscreen mode

chmod worked while cat did not, because changing permissions is not governed by the permission bits at all. It is a right of ownership. The owner can always change a file's mode, even when the current mode grants the owner nothing. You can lock yourself out of a file's contents, but you cannot lock yourself out of the lock.

Break 2: The script that refuses to run

Write a two-line script and try to run it:

zayden@lab:~/playground$ printf '#!/bin/bash\necho "hello from the script"\n' > hello.sh
zayden@lab:~/playground$ ./hello.sh
bash: ./hello.sh: Permission denied
Enter fullscreen mode Exit fullscreen mode

Same error text as Break 1, completely different check underneath. ./hello.sh asks the kernel to execute this file as a program, and execute is its own separate permission. Reading was never the problem, which you can prove in one line:

zayden@lab:~/playground$ bash hello.sh
hello from the script
Enter fullscreen mode Exit fullscreen mode

bash hello.sh runs bash, which is already executable over in /usr/bin, and bash merely reads your file the way cat would. Read is granted, so it works. Running the file directly is the thing that needs the x bit on the file itself. One chmod flips it:

zayden@lab:~/playground$ chmod +x hello.sh
zayden@lab:~/playground$ ls -l hello.sh
-rwxr-xr-x 1 zayden zayden 41 Jul 30 10:05 hello.sh
zayden@lab:~/playground$ ./hello.sh
hello from the script
Enter fullscreen mode Exit fullscreen mode

+x with no letter in front of it means "add execute everywhere the umask allows", and a standard umask of 022 blocks only write bits, so all three x characters appeared at once. That asymmetry between ./hello.sh and bash hello.sh is worth remembering. When a script suddenly stops running after you copy it between machines or pull it out of a zip archive, the file is usually fine and the x bit is what went missing.

Break 3: Seal a directory shut

This is the one that bends brains. Directories carry the same nine bits, but x means something different on them: not "run this", but "pass through this". Watch what happens when it goes missing.

zayden@lab:~/playground$ mkdir vault
zayden@lab:~/playground$ echo "gold" > vault/treasure.txt
zayden@lab:~/playground$ chmod a-x vault
zayden@lab:~/playground$ ls -ld vault
drw-r--r-- 2 zayden zayden 4096 Jul 30 10:07 vault
zayden@lab:~/playground$ cd vault
bash: cd: vault: Permission denied
Enter fullscreen mode Exit fullscreen mode

cd is pure pass-through, so it dies instantly. Now the strange part:

zayden@lab:~/playground$ ls vault
ls: cannot access 'vault/treasure.txt': Permission denied
treasure.txt
zayden@lab:~/playground$ ls -l vault
ls: cannot access 'vault/treasure.txt': Permission denied
total 0
-????????? ? ? ? ?            ? treasure.txt
Enter fullscreen mode Exit fullscreen mode

An error and a result at the same time, then a row of question marks. Here is the unlock: a directory is a file whose contents are a list of names. The r bit lets you read that list, which is why treasure.txt still prints. But the file's size, owner, permissions and timestamps live with the file itself, on the far side of the door you just sealed, and reaching them requires x. ls could read the guest list and could not walk in, so every fact about the file came back as a question mark.

If you are wondering why plain ls needed those details at all, it did not. Most systems alias ls to ls --color=auto, and picking a color per file requires the same blocked lookup. That is the alias talking, not ls itself.

Reading the file is fully off the table:

zayden@lab:~/playground$ cat vault/treasure.txt
cat: vault/treasure.txt: Permission denied
Enter fullscreen mode Exit fullscreen mode

It is not only the last directory in the path, either. To reach vault/treasure.txt the kernel needs x on every directory along the way, starting at /. One sealed door anywhere above a file blocks everything below it, which is why a permission problem is so often three levels higher than the file you are shouting at.

Now flip the experiment and give the directory x without r:

chmod 711 vault  # 7 = rwx for you, 1 = execute only for group and everyone else
Enter fullscreen mode Exit fullscreen mode

That is a speakeasy instead of a sealed vault. As group or other, cd vault works and cat vault/treasure.txt works if you already know the name, but ls vault fails outright. A menu with no door, or a door with no menu. The two bits really are that independent, and web servers lean on the door-with-no-menu form constantly.

One more directory surprise: deleting a file is an edit to the directory, because you are erasing a name from its list. rm therefore cares about w and x on the directory, not about the file's own bits. That is why rm can delete a read-only file inside a writable directory (it asks first) and cannot delete anything inside a directory you cannot write.

Repair the vault before moving on:

zayden@lab:~/playground$ chmod 755 vault
zayden@lab:~/playground$ cat vault/treasure.txt
gold
Enter fullscreen mode Exit fullscreen mode

Break 4: Try to give your file away

zayden@lab:~/playground$ chown root notes.txt
chown: changing ownership of 'notes.txt': Operation not permitted
Enter fullscreen mode Exit fullscreen mode

New error text, and the difference matters more than any bit in this article. "Permission denied" means the kernel checked the bits and the bits said no. "Operation not permitted" means the bits were never consulted, because the operation itself is reserved. On Linux, giving a file away is root-only. If ordinary users could hand files to each other, you could dump junk onto someone else's disk quota, or plant a file that looks like somebody else wrote it. So there are no three questions here. There is one: are you root? You are not.

zayden@lab:~/playground$ sudo chown root notes.txt
[sudo] password for zayden:
zayden@lab:~/playground$ ls -l notes.txt
-rw-r--r-- 1 root zayden 13 Jul 30 10:02 notes.txt
Enter fullscreen mode Exit fullscreen mode

Same file, same folder, same you. Try to append a line:

zayden@lab:~/playground$ echo "one more line" >> notes.txt
bash: notes.txt: Permission denied
zayden@lab:~/playground$ cat notes.txt
secret plans
Enter fullscreen mode Exit fullscreen mode

Run the three questions yourself before reading on. Owner? No, that is root now. Group? Yes, the file's group is still zayden and you are in it, so the kernel reads the group triplet: read yes, write no, and stops, exactly like Break 1. Reading works, appending does not. The error names bash rather than echo because your shell opens the file for the >> redirect before echo ever runs, which is a useful tell: when the complaint comes from bash, the redirect failed and the command never started.

So what did sudo actually do? It ran chown as root, and root passes nearly every file permission check automatically. Sudo does not grant you permission. It removes the questions. That is why it fixes almost everything, and why reaching for it by reflex is a bad habit. You are not solving the puzzle, you are unplugging the referee. (Nearly every check: even root cannot execute a file that has no x bit anywhere. The kernel wants at least one signal that the thing was meant to be a program.)

Take your file back:

zayden@lab:~/playground$ sudo chown zayden notes.txt
Enter fullscreen mode Exit fullscreen mode

The notation you already felt

Now the table, which at this point is a summary rather than a mystery.

The ten characters at the front of ls -l

- rwx r-x r-x
^  ^   ^   ^
|  |   |   everyone else
|  |   group
|  owner
type: - file, d directory, l symlink
Enter fullscreen mode Exit fullscreen mode

ls prints those with no spaces, as -rwxr-xr-x. They are spaced out here to show the grouping. Three triplets, one per question, each answering read, write and execute with a letter for yes and a dash for no. Every error you caused above was the kernel landing on one triplet and finding a dash where it needed a letter.

The three digits in chmod 755

The numeric form compresses each triplet into a single digit: r is 4, w is 2, x is 1, add them up. So 7 is rwx, 6 is rw-, 5 is r-x, 4 is r--, and 0 is nothing at all.

Octal Triplets Where you felt it
000 --- --- --- Break 1, locked out of your own notes
644 rw- r-- r-- notes.txt as created, hello.sh before +x
755 rwx r-x r-x hello.sh after +x, the repaired vault
711 rwx --x --x the speakeasy vault, door with no menu
600 rw- --- --- private files, and ssh refuses keys looser than this

The chmods you have been pasting for years now decode themselves. chmod 644 is "owner reads and writes, everyone else reads". chmod 755 is the same plus execute for all, the standard for scripts and for directories people are meant to enter. chmod 777 hands all nine yeses to everyone, and usually marks the spot where somebody stopped debugging and started gambling.

The symbolic form

The letters address the same three lanes: u owner, g group, o everyone else, a all three, combined with + to add, - to remove, and = to set exactly.

chmod u+x deploy.sh     # add execute for the owner only
chmod go-w report.txt   # take write away from group and everyone else
chmod a-x vault         # remove execute from all three, which is what Break 3 did
chmod u=rw,go= key.pem  # set exactly: owner reads and writes, nobody else gets anything
Enter fullscreen mode Exit fullscreen mode

Symbolic form is the safer habit when you are adjusting one thing, because it changes only the bits you name. Octal replaces all nine at once, so a typo in a hurry rewrites permissions you never meant to touch.

The error decoder

Four messages cover most permission trouble, and you have now caused every one of them on purpose.

cat: notes.txt: Permission denied

The bits denied a read. The same shape comes from cp, grep, less and anything else that opens a file. Check first: run ls -l notes.txt, work out which single triplet applies to you, and look for r in it.

bash: ./hello.sh: Permission denied

An execute was refused. Check first: ls -l hello.sh and look for x in your triplet. If x is present and it still fails, check whether the filesystem is mounted noexec, which is common on /tmp and on USB drives.

bash: cd: vault: Permission denied

Pass-through was refused. Check first: ls -ld vault, where the d makes ls describe the directory itself instead of listing its contents. Remember that the check applies to every directory on the path, not only the last one.

chown: changing ownership of 'notes.txt': Operation not permitted

Not a bits problem. This is a root-only operation, so chmod will never fix it. Check first: whether the change genuinely needs sudo, and whether you are sure it does. Any time you see "Operation not permitted" where you expected "Permission denied", stop staring at the permission bits and go looking for the rule instead.

Where to practice safely

Not on machines you care about. Permission experiments escalate: today you chmod 000 a note, next month you fat-finger a recursive chmod in the wrong terminal window. Do your breaking where mistakes are free.

A local VM is the classic answer. VirtualBox with a Debian or Ubuntu image, take a snapshot before you start, roll back when things get strange. A cheap cloud box you can rebuild on demand works the same way. WSL handles everything in this article too, as long as you work inside your Linux home directory rather than under /mnt/c, where the permission bits behave oddly because they are being translated from Windows.

Full disclosure, I built the third option: The Linux Camp (thelinuxcamp.com), real Linux VMs in your browser that boot in about a second, made for exactly this break-and-inspect style of learning. Wreck the machine, reset it, go again, and your progress is verified from the commands you actually type. Sign-up is required to launch a VM, the first track is free, and everything past it is $14.99 a month. Every experiment above runs identically on any Linux box you already have, so start wherever you are.

However you practice, keep the habit. Next time something denies you, do not paste a fix from a forum. Ask which of the three questions matched you, and which single bit said no. The system is not being difficult. It is answering exactly the question you asked, and you now speak enough of its language to ask better ones.

Top comments (0)