Linux File Permissions¶
This post covers Linux file and directory permissions from the ground up — what read/write/execute actually mean for files vs. directories, how to use chmod and chown with real examples, how default permissions are calculated with umask, and how the three special permissions (SUID, SGID, sticky bit) work with practical scenarios.
1. The Three Basic Permissions¶
Every file and directory carries three permission types, each with a symbol and a numeric value:
| Permission | Symbol | Value |
|---|---|---|
| Read | r |
4 |
| Write | w |
2 |
| Execute | x |
1 |
These apply separately to three categories of users:
| Category | Symbol |
|---|---|
| User / Owner | u |
| Group | g |
| Other | o |
And they can be changed using three operators:
| Operator | Meaning |
|---|---|
+ |
Add a permission, keep existing ones |
- |
Remove a permission, keep the rest |
= |
Set the exact permission, discarding anything not listed |
2. What Permissions Mean on a Directory¶
| Permission | Meaning | Example |
|---|---|---|
Read (r) |
Lets you list the contents of the directory | ls /project works only if you have read on /project |
Write (w) |
Lets you create or delete files/sub-directories inside it | touch /project/newfile.txt needs write on /project |
Execute (x) |
Lets you enter the directory (cd into it) and access its contents |
cd /project needs execute on /project |
Key rule: without execute (x) on a directory, write (w) is useless — you can't get inside to create or remove anything. For example, a directory with permissions rw------- lets the owner list files but not enter or create anything in it, because execute is missing.
3. What Permissions Mean on a File¶
| Permission | Meaning | Example |
|---|---|---|
Read (r) |
Lets you view the file's contents | cat notes.txt needs read on notes.txt |
Write (w) |
Lets you modify the file's contents | nano notes.txt and saving needs write |
Execute (x) |
Lets you run the file as a program/script | ./backup.sh needs execute on backup.sh |
Files are created without execute by default. You add it manually depending on the file type — e.g., a Python script or shell script:
chmod u+x backup.sh
./backup.sh
4. Reading a Permission String¶
Permission strings like rwxr-xr-- are read as three groups of three, left to right — user, group, other — but when you're reasoning about which category "wins" or applying privilege changes with numeric shorthand, always work through them right to left: other, then group, then user.
Example: rwxr-xr--
| Segment | Category | Permissions |
|---|---|---|
rwx |
User | read, write, execute |
r-x |
Group | read, execute |
r-- |
Other | read only |
5. Modifying Permissions with chmod¶
Symbolic method¶
Example 1 — remove group's rwx, add other's rwx, remove user's write, and show what changed:
chmod g-rwx,o+rwx,u-w report.txt -c
Output (via -c, which prints only what actually changed):
mode of 'report.txt' changed from 0644 (rw-r--r--) to 0087 (----w-rwx)
Example 2 — set an exact permission, discarding whatever other had before:
chmod o=x script.sh
If script.sh was rwxrwxrwx, it becomes rwxrwx--x — other now has only execute, nothing else, regardless of what it had previously.
Numeric (octal) method¶
Add up the values (4 = read, 2 = write, 1 = execute) for each category:
chmod 754 script.sh
This means:
- User (7 = 4+2+1) → read, write, execute
- Group (5 = 4+1) → read, execute
- Other (4) → read only
A 0 removes all permissions for that category:
chmod 700 private.txt
Only the owner can read/write/execute; group and other get nothing.
Recursive changes¶
chmod -R 777 /var/www/project
This applies rwxrwxrwx to the project directory and every file and sub-directory inside it, recursively.
Changing permissions for everyone at once¶
chmod a+x deploy.sh
The a (all) flag applies the change to user, group, and other in one command — here, giving everyone execute permission on deploy.sh.
6. Changing Ownership: chown and chgrp¶
| Command | Effect |
|---|---|
chown ravi:devteam report.txt |
Sets owner to ravi and group to devteam in one step |
chown ravi report.txt |
Changes only the owner to ravi |
chgrp devteam report.txt |
Changes only the group to devteam |
Example: you have a file owned by apple:apple and want it owned by mango with group devteam:
chown mango:devteam report.txt
You only need chown for changing both owner and group together — chgrp is only needed when changing the group alone.
7. Default Permissions and umask¶
Linux's maximum default permissions are:
- Directories:
777 - Files:
666(files never get execute by default, even at maximum)
The actual permission applied at creation time is:
Default permission = Maximum permission − umask value
With the standard umask of 022:
| Type | Calculation | Result |
|---|---|---|
| Directory | 777 − 022 |
755 (rwxr-xr-x) |
| File | 666 − 022 |
644 (rw-r--r--) |
Example — check it yourself:
umask # shows current value, e.g. 0022
mkdir testdir
touch testfile
ls -ld testdir # drwxr-xr-x
ls -l testfile # -rw-r--r--
Example — a stricter umask:
umask 077
touch secret.txt
ls -l secret.txt # -rw------- (only the owner gets any access)
Temporary vs. permanent umask¶
- Running
umask 077directly in the terminal only lasts for that session — it resets to022once you close the terminal or log out. - To make it permanent, add the
umaskline to your.bashrc:
echo "umask 027" >> ~/.bashrc
source ~/.bashrc
8. Special Permissions¶
Beyond the standard rwx set, three special permissions handle privilege escalation and inheritance:
| Special Permission | Numeric Value | Symbolic Form |
|---|---|---|
| SUID (Set User ID) | 4 | u+s |
| SGID (Set Group ID) | 2 | g+s |
| Sticky Bit | 1 | o+t |
8.1 SUID — Set User ID¶
Normally, running a command uses your own privileges. SUID makes a command run with the file owner's privileges instead — so a normal user can execute it with elevated rights, without being given full sudo access.
Example: useradd normally requires sudo because it needs root privileges to modify /etc/passwd. If you set SUID on the useradd binary:
chmod u+s /usr/sbin/useradd
...then a normal user can run useradd newuser and it will execute as root for that one command — without granting that user broad sudo access to everything else. (In practice this is rarely done for useradd specifically since it's a security-sensitive shortcut, but it illustrates exactly what SUID does — the classic real-world example is /usr/bin/passwd, which already ships with SUID so any user can update their own password, which is stored in root-owned /etc/shadow.)
You can spot SUID in ls -l output as an s in place of the owner's execute bit:
ls -l /usr/bin/passwd
-rwsr-xr-x. 1 root root 27832 ... /usr/bin/passwd
8.2 SGID — Set Group ID¶
Without SGID: if directory /project is owned by user apple, group mango, and user apple creates a new file inside it, that file gets owner apple and group apple — the creator's own group, not the parent directory's group.
With SGID applied to the directory:
chmod g+s /project
Now, any new file or sub-directory created inside /project — by any user — automatically inherits the group of the parent directory (mango), instead of the creator's personal group.
Example scenario: a shared team directory where multiple users (apple, banana, cherry) each have their own primary group, but all need their files to belong to devteam so teammates can access them:
mkdir /shared/devteam-project
chown :devteam /shared/devteam-project
chmod g+s /shared/devteam-project
Now no matter who creates a file inside devteam-project, it's automatically group-owned by devteam — no manual chgrp needed afterward.
8.3 Sticky Bit¶
The problem it solves: in a shared directory (like /tmp) where many users have write access, any user could delete or rename any other user's files, even ones they don't own — because directory write permission alone controls the ability to remove files inside it, regardless of who owns the file itself. This risks permanent, accidental data loss for other users.
The fix: applying the sticky bit to a directory means users can still create files inside it, but they can only delete or rename their own files — not other users'. Only the file's owner, the directory's owner, or root can remove it.
sudo chmod +t /home/apple
or, using the numeric form (1 prefix for sticky bit, combined with full 777):
sudo chmod 1777 /home/apple
You can confirm it's set by checking for a t at the end of the permission string:
ls -ld /home/apple
drwxrwxrwt. 2 apple apple 4096 ... /home/apple
Real-world example: /tmp on almost every Linux system already has the sticky bit set for exactly this reason — many users and processes write temporary files there, and the sticky bit stops one user from deleting another's temp files.