7 of 8

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.

  • chmod
  • chown
  • chgrp
  • umask
  • id
  • stat
  • find

40 min6 exercises

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.

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

BitOn a fileOn a directory
r readRead the contentList the names inside
w writeChange the contentCreate, rename and delete entries inside
x executeRun it as a programEnter 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:

SymbolicPer classOctal
rwxr-xr-x7, 5, 5755
rw-r--r--6, 4, 4644
rw-------6, 0, 0600
rwxrwx---7, 7, 0770

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
CommandResult
chmod 600 frw-------
chmod u=rwx,g=rx,o= frwxr-x---
chmod g+w,o+r fAdds group write and other read
chmod a-x fRemoves 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:

umaskNew fileNew directoryTypical use
022644755Others may read, only you write
002664775Your group may write too
027640750Others get nothing
077600700Fully 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.

BitOctalOn a fileOn a directory
setuid4Runs as the file’s ownerNo effect
setgid2Runs as the file’s groupNew files inherit the directory’s group
sticky1No effectOnly 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.

  1. Warm-up

    Run a script

    Create ~/lab/run.sh containing echo hi. Try to run it as ./run.sh and read the error. Fix it with one chmod command and run it again.

    Check your result
    bash: ./run.sh: Permission denied
    hi
    Hint

    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
  2. 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.txt
    600 -rw------- a.key
    755 -rwxr-xr-x b.sh
    664 -rw-rw-r-- c.txt
    Hint

    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
  3. Practice

    Read versus execute on a directory

    Make a directory box holding a file f that contains secret. First remove only the owner's read permission from box. Test ls box and cat 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 box fails with Permission denied, but cat box/f prints secret, because you can still reach a name you already know. Without execute: ls box prints f but ls -l box shows ? for every field and cat box/f fails. Read lets you list names. Execute lets you reach what is inside.

    Hint

    chmod u-r box, test, chmod u+r box, then chmod 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
  4. Practice

    Change what new files get

    In your current shell set a umask that gives new files 600 and new directories 700. Create one of each in ~/lab and confirm the modes. Then restore the umask you started with.

    Check your result
    stat -c '%a %n' private.txt pdir
    600 private.txt
    700 pdir
    Hint

    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
  5. Practice

    Change an owner

    Create ~/lab/own.txt. Try chown root own.txt without sudo and read the error. Repeat it with sudo. As yourself, try to append a line to the file, then use sudo to give it back to you.

    Check your result

    The first chown fails with Operation not permitted: only root can give a file away. After the second, ls -l own.txt shows root root, and appending fails with Permission denied because the mode -rw-rw-r-- allows others only to read.

    Hint

    sudo chown USER:GROUP file. id -un and id -gn print 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
  6. 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 use find to touch files and directories differently where needed.

    | Path | Mode | | --- | --- | | secure, secure/public | 755 | | secure/public/index.html | 644 | | secure/bin and secure/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.sh
    Check your result
    find ~/lab/secure -printf '%m %p\n' | sort -k2
    755 /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.html
    Hint

    Set sane defaults for everything with two find -exec chmod commands (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