Linux FilesystemLesson 7 of 8
Permissions and ownership
Read, set and reason about who can read, change and run each file, including umask and the setuid, setgid and sticky bits.
chmodchownchgrpumaskidstatfind
After this lesson you can
- Read a mode in symbolic or octal form and say exactly who can do what.
- Change modes with chmod in both notations, including recursively.
- Explain how directory permissions differ from file permissions.
- Control default modes with umask and group inheritance with setgid.
Who, and what they can do
Every file has one owner (a user), one group, and a mode that says what each of three classes may do.
| Class | Who |
|---|---|
| u (user) | The file’s owner |
| g (group) | Members of the file’s group |
| o (others) | Everyone else |
The kernel checks the classes in that order and stops at the first that applies. If you are the owner, only the owner bits count, even if the group bits are more generous.
Each class has three bits:
| Bit | On a file | On a directory |
|---|---|---|
| r read | Read the content | List the names inside |
| w write | Change the content | Create, rename and delete entries inside |
| x execute | Run it as a program | Enter it and reach what is inside |
ls -l shows the mode as ten characters:
ls -l run.sh
-rwxr-x--- 1 alex dev 8 Nov 3 10:12 run.sh
The first character is the type. The next nine are three groups of rwx: owner rwx, group r-x, others ---. Here alex can read, write and run it, members of dev can read and run it, and everyone else has no access.
Find out who you are and which groups you belong to:
id
uid=1000(alex) gid=1000(alex) groups=1000(alex),27(sudo),1002(dev)
Octal mode
Add the numbers for each class: r = 4, w = 2, x = 1. One digit per class gives the mode:
| Symbolic | Per class | Octal |
|---|---|---|
rwxr-xr-x | 7, 5, 5 | 755 |
rw-r--r-- | 6, 4, 4 | 644 |
rw------- | 6, 0, 0 | 600 |
rwxrwx--- | 7, 7, 0 | 770 |
stat prints both forms:
stat -c '%a %A %n' run.sh
750 -rwxr-x--- run.sh
Memorise four: 644 (normal file), 755 (program or directory), 600 (private file), 700 (private directory).
Change permissions: chmod
chmod accepts octal or a symbolic expression. Symbolic is WHO OPERATOR PERMS, where who is any of u g o a, the operator is + add, - remove or = set exactly, and perms are r w x.
A script you wrote cannot run until you allow it:
cd ~/lab
echo 'echo hi' > run.sh
./run.sh
bash: ./run.sh: Permission denied
The shell reports exit status 126 for a command that was found but not allowed to run. Add execute for yourself:
chmod u+x run.sh
./run.sh
hi
More forms:
chmod 600 run.sh
chmod u=rwx,g=rx,o= run.sh
chmod g+w,o+r run.sh
chmod a-x run.sh
| Command | Result |
|---|---|
chmod 600 f | rw------- |
chmod u=rwx,g=rx,o= f | rwxr-x--- |
chmod g+w,o+r f | Adds group write and other read |
chmod a-x f | Removes execute for everyone |
Octal sets everything at once. Symbolic changes only what you name, which is safer when you do not know the current mode.
Recursive changes
-R applies a mode to a whole tree, but files and directories need different modes: a directory needs x to be entered, a data file usually should not have it. Capital X solves most of that: it sets execute only on directories and on files that already have execute for someone.
chmod -R u=rwX,go=rX tree
When rules are more specific, use find to treat each type on its own:
find tree -type d -exec chmod 755 {} +
find tree -type f -exec chmod 644 {} +
Permissions on directories
Directory bits are easy to get wrong because the meanings are different. Make a directory with a file in it:
mkdir box
echo secret > box/f
chmod u-r box
ls -ld box
d-wxrwxr-x 2 alex alex 4096 Nov 3 10:20 box
You still have x but not r. You cannot list the names, but you can reach a file whose name you know:
ls box
cat box/f
ls: cannot open directory 'box': Permission denied
secret
Now give back r and take away x instead:
chmod u+r,u-x box
ls box
cat box/f
f
cat: box/f: Permission denied
The names are visible, but without x nothing about them can be read: ls -l box shows ? for every field. Restore it with chmod u+x box.
Deleting is controlled by the directory, not the file. To remove or rename box/f you need w and x on box; the permissions on f itself do not matter. That is why a read-only file can still be removed from a writable directory.
Ownership: chown and chgrp
stat -c '%U:%G' run.sh
alex:alex
Only root can change the owner of a file. A normal user cannot give files away:
chown root run.sh
chown: changing ownership of 'run.sh': Operation not permitted
With sudo:
sudo chown root:dev run.sh
sudo chown -R alex: project/
chown USER:GROUP sets both. chown USER: sets the user and the user’s login group. chgrp GROUP file changes only the group, and a normal user can use it for any group they belong to:
chgrp dev run.sh
The default mode: umask
New files do not start at 000. A program asks for a mode (usually 666 for files and 777 for directories) and the umask removes bits from it:
| umask | New file | New directory | Typical use |
|---|---|---|---|
022 | 644 | 755 | Others may read, only you write |
002 | 664 | 775 | Your group may write too |
027 | 640 | 750 | Others get nothing |
077 | 600 | 700 | Fully private |
Read yours:
umask
0002
Debian and Ubuntu use 002 because every user has a private group of the same name. Other systems default to 022. Change it for the current shell and see the effect:
umask 077
touch private.txt
mkdir pdir
stat -c '%a %n' private.txt pdir
600 private.txt
700 pdir
The umask applies to the shell and the commands it starts, and resets when the shell closes. To keep a value, put the umask line in ~/.bashrc for you, or in a system-wide profile for everyone.
Special bits
Three more bits live in a fourth, leading octal digit.
| Bit | Octal | On a file | On a directory |
|---|---|---|---|
| setuid | 4 | Runs as the file’s owner | No effect |
| setgid | 2 | Runs as the file’s group | New files inherit the directory’s group |
| sticky | 1 | No effect | Only the owner can delete or rename entries |
setuid is how passwd can edit /etc/shadow for an ordinary user. It shows as s in the owner’s execute position:
ls -l /etc/shadow /usr/bin/passwd
-rw-r----- 1 root shadow 1430 Nov 3 10:20 /etc/shadow
-rwsr-xr-x 1 root root 64152 May 30 2024 /usr/bin/passwd
A setuid program owned by root is a security boundary, so never set the bit on your own scripts, and treat unexpected setuid files as a finding (find / -xdev -perm -4000).
The two directory bits are for sharing. setgid makes everything created inside belong to the directory’s group, not the creator’s primary group, so a team’s files stay in the team’s group. sticky lets people create files in a shared directory without being able to delete each other’s. /tmp is the standard example:
ls -ld /tmp
drwxrwxrwt 11 root root 4096 Nov 3 10:20 /tmp
The trailing t is the sticky bit on a world-writable directory. You will combine all of this to build a shared area in the next lesson.
chmod g+s shared
chmod +t shared
chmod 2775 shared
Practice
0 of 6 done
Do these on your own system. They get harder. Check each result, then tick it off.
- Warm-up
Run a script
Create
~/lab/run.shcontainingecho hi. Try to run it as./run.shand read the error. Fix it with onechmodcommand and run it again.Check your result
bash: ./run.sh: Permission denied hiHint
A file needs the execute permission to be run directly.
Solution
cd ~/lab echo 'echo hi' > run.sh ./run.sh chmod u+x run.sh ./run.sh - Warm-up
Translate modes
Create three empty files and give them these modes using octal for the first, symbolic for the second and either for the third. Then print each mode as octal and symbolic.
a.key: owner can read and write, nobody else can do anything.b.sh:rwxr-xr-x.c.txt: owner read and write, group read and write, others read only.
Check your result
stat -c '%a %A %n' a.key b.sh c.txt600 -rw------- a.key 755 -rwxr-xr-x b.sh 664 -rw-rw-r-- c.txtHint
Read is 4, write is 2, execute is 1. Add them per class.
Solution
cd ~/lab && touch a.key b.sh c.txt chmod 600 a.key chmod u=rwx,g=rx,o=rx b.sh chmod 664 c.txt stat -c '%a %A %n' a.key b.sh c.txt - Practice
Read versus execute on a directory
Make a directory
boxholding a filefthat containssecret. First remove only the owner's read permission frombox. Testls boxandcat box/f. Restore it, then remove only the owner's execute permission and run both tests again. In one sentence each, say what read and execute mean on a directory.Check your result
Without read:
ls boxfails withPermission denied, butcat box/fprintssecret, because you can still reach a name you already know. Without execute:ls boxprintsfbutls -l boxshows?for every field andcat box/ffails. Read lets you list names. Execute lets you reach what is inside.Hint
chmod u-r box, test,chmod u+r box, thenchmod u-x box, test,chmod u+x box.Solution
cd ~/lab && mkdir box && echo secret > box/f chmod u-r box; ls box; cat box/f; chmod u+r box chmod u-x box; ls box; cat box/f; chmod u+x box - Practice
Change what new files get
In your current shell set a umask that gives new files
600and new directories700. Create one of each in~/laband confirm the modes. Then restore the umask you started with.Check your result
stat -c '%a %n' private.txt pdir600 private.txt 700 pdirHint
A new file starts at 666 and a directory at 777. The umask removes bits from them.
Solution
old=$(umask) umask 077 cd ~/lab && touch private.txt && mkdir pdir stat -c '%a %n' private.txt pdir umask $old - Practice
Change an owner
Create
~/lab/own.txt. Trychown root own.txtwithoutsudoand read the error. Repeat it withsudo. As yourself, try to append a line to the file, then usesudoto give it back to you.Check your result
The first
chownfails withOperation not permitted: only root can give a file away. After the second,ls -l own.txtshowsroot root, and appending fails withPermission deniedbecause the mode-rw-rw-r--allows others only to read.Hint
sudo chown USER:GROUP file.id -unandid -gnprint yours.Solution
cd ~/lab && touch own.txt chown root own.txt sudo chown root own.txt echo hi >> own.txt sudo chown "$(id -un):$(id -gn)" own.txt - Challenge
Set a precise permission map
Create this tree under
~/lab/secure, then set every mode so that the final listing matches the table. Use as few commands as you can, and usefindto touch files and directories differently where needed.| Path | Mode | | --- | --- | |
secure,secure/public|755| |secure/public/index.html|644| |secure/binandsecure/bin/deploy.sh|750| |secure/keys|700| |secure/keys/id_demo|600|mkdir -p ~/lab/secure/{keys,public,bin} touch ~/lab/secure/keys/id_demo ~/lab/secure/public/index.html ~/lab/secure/bin/deploy.shCheck your result
find ~/lab/secure -printf '%m %p\n' | sort -k2755 /home/alex/lab/secure 750 /home/alex/lab/secure/bin 750 /home/alex/lab/secure/bin/deploy.sh 700 /home/alex/lab/secure/keys 600 /home/alex/lab/secure/keys/id_demo 755 /home/alex/lab/secure/public 644 /home/alex/lab/secure/public/index.htmlHint
Set sane defaults for everything with two
find -exec chmodcommands (one per type), then adjust the three exceptions.Solution
cd ~/lab/secure find . -type d -exec chmod 755 {} + find . -type f -exec chmod 644 {} + chmod 750 bin bin/deploy.sh chmod 700 keys chmod 600 keys/id_demo find ~/lab/secure -printf '%m %p\n' | sort -k2