How Do File Permissions Work in a Linux File System?

How do file permissions work in a Linux file system

Anyone who has spent more than an afternoon in a Linux terminal has run into Permission denied at some point — trying to execute a script, edit a config file owned by root, or write to a directory that isn’t theirs. Behind that error sits one of the oldest and most consistently effective security models in computing: the UNIX permission system, inherited almost unchanged by Linux from its UNIX ancestors in the 1970s, and still the primary access control mechanism on every Linux system running today, from Raspberry Pis to hyperscale cloud infrastructure.

This article explains that model in depth — from the basic owner/group/other structure everyone eventually memorizes, to special permission bits, ACLs, and how it all maps onto the underlying file system data structures.

The Core Model: Owner, Group, Other

Every file and directory on a Linux system has three categories of “who,” each of which can independently be granted three categories of “what”:

Who:

What:

Running ls -l on a file shows this encoded in a ten-character string:

-rwxr-xr--  1 alice  developers  4096 Aug 12 10:15 deploy.sh

Breaking that down:

-   rwx    r-x    r--
│    │      │      │
│    │      │      └── other: read only
│    │      └───────── group: read + execute
│    └──────────────── owner: read + write + execute
└───────────────────── file type (- = regular file, d = directory, l = symlink)

Numeric (Octal) Notation

Permissions are also commonly expressed as a three-digit octal number, where each digit represents a category (owner, group, other) and is the sum of read (4), write (2), and execute (1):

PermissionValue
read4
write2
execute1

So rwxr-xr-- becomes:

Giving chmod 754 deploy.sh as the equivalent command to set that exact permission set. This numeric shorthand is ubiquitous in scripts, documentation, and Dockerfiles because it’s compact and unambiguous.

The Core Commands

Only the file’s owner or a privileged user (root, or someone with CAP_CHOWN) can change ownership; a file’s owner can change its own permissions with chmod even without being root, which is a subtlety that trips people up — you don’t need root to loosen or tighten permissions on files you own.

Directories Are Special

Permission bits mean something slightly different on directories than on regular files, and this is one of the most common sources of confusion:

Special Permission Bits: SUID, SGID, and the Sticky Bit

Beyond the basic rwx triad, Linux supports three special bits that modify default behavior:

SUID (Set User ID) — octal 4000 When set on an executable, the program runs with the privileges of the file’s owner, not the user who launched it. The classic example is /usr/bin/passwd, owned by root with the SUID bit set, so that any regular user can run it to change their own password (which requires writing to the root-owned /etc/shadow) without being root themselves.

ls -l /usr/bin/passwd
-rwsr-xr-x 1 root root 68208 ... /usr/bin/passwd

The lowercase s in the owner execute position signals SUID is set (and execute is also set); an uppercase S would mean SUID is set but execute is not, which is unusual and often a misconfiguration.

SGID (Set Group ID) — octal 2000 On an executable, similarly runs with the group privileges of the file’s group. On a directory, SGID has a different and very useful effect: new files and subdirectories created inside inherit the directory’s group rather than the creating user’s primary group — commonly used for shared team directories so that everyone’s files automatically belong to the right collaborative group.

Sticky Bit — octal 1000 On a directory, restricts deletion so that only the file’s owner (or root, or the directory’s owner) can delete or rename files within it, even if other users have write access to the directory. The canonical example is /tmp, which is world-writable but uses the sticky bit to prevent users from deleting each other’s temporary files:

ls -ld /tmp
drwxrwxrwt 15 root root 4096 Aug 15 09:00 /tmp

The trailing t shows the sticky bit is active.

Setting these: chmod 4755 file (SUID), chmod 2775 dir (SGID), chmod 1777 dir (sticky), or symbolically chmod u+s, chmod g+s, chmod +t.

Under the Hood: Inodes and Permission Storage

Every file on a Linux (ext4, XFS, Btrfs, etc.) file system is represented by an inode — a data structure holding metadata about the file: owner UID, group GID, permission bits, timestamps, size, and pointers to the data blocks on disk. The filename itself lives separately, in a directory entry that maps a name to an inode number. Permission bits are stored directly in the inode’s mode field, alongside the file-type bits.

This separation explains some otherwise-surprising behavior: hard links to the same inode always share the exact same permissions, owner, and group, because they are the same inode viewed through different directory entries — changing permissions via one hard-linked path changes it for all of them, instantly, because there’s only one inode being modified.

Directory Entry          Inode Table
"report.txt" -----> inode #4521 { mode: 0644, uid: 1000, gid: 1000, ... }
"report_link" ----> inode #4521  (same inode — hard link)

Access Control Lists (ACLs): Beyond Owner/Group/Other

The classic owner/group/other model has a real limitation: what if you need to grant a fourth user read access without changing the group, or without exposing the file to everyone in that group? POSIX ACLs solve this by allowing arbitrary additional user and group entries beyond the basic three.

setfacl -m u:bob:rw file.txt      # give user bob read+write, independent of owner/group
setfacl -m g:auditors:r file.txt  # give group auditors read access
getfacl file.txt                  # view the full ACL

When a file has an ACL, ls -l shows a + after the permission string to signal there’s more going on than the basic bits show:

-rw-rwx---+ 1 alice developers 1024 Aug 15 09:00 file.txt

ACLs are the mechanism used, for example, when a company needs one specific external contractor to read a file inside a project directory without joining the project’s primary group.

Comparisons with Other Operating Systems

Real-World Troubleshooting Scenarios

“Permission denied” running a script you just wrote

chmod +x myscript.sh
./myscript.sh

Scripts aren’t executable by default when created with a text editor; you must explicitly grant execute permission.

A web server returning 403 Forbidden for static files Usually a directory-execute or file-read issue: the web server’s user (often www-data or nginx) needs execute permission on every directory in the path to the file, and read permission on the file itself.

chmod 755 /var/www/html
chmod 644 /var/www/html/index.html
chown -R www-data:www-data /var/www/html

Cannot delete a file you don’t own, in a directory you do own This works precisely because directory write permission governs deletion — even though you might expect the file’s own permissions to be the deciding factor.

SUID binaries as a security audit target Since SUID root binaries run with elevated privilege, they’re a classic privilege-escalation vector if misconfigured or vulnerable. A standard hardening/audit step:

find / -perm -4000 -type f 2>/dev/null

This lists every SUID binary on the system, which a security review should compare against an expected baseline.

Default Permissions: umask Explained

New files and directories don’t appear with arbitrary permissions — they’re governed by the umask (user file-creation mask), a value that subtracts permission bits from a theoretical maximum default whenever a new file or directory is created. The default maximum is 666 (rw-rw-rw-) for regular files and 777 (rwxrwxrwx) for directories; the umask value is subtracted from these to produce the actual default permissions.

umask
# 0022

# For a new file: 666 - 022 = 644 (rw-r--r--)
# For a new directory: 777 - 022 = 755 (rwxr-xr-x)

A umask of 022 (a very common default) removes write permission for group and other on newly created files, which is why freshly created files typically appear as 644 rather than the theoretical 666 maximum — nobody but the owner gets write access by default unless something explicitly changes that afterward. Administrators managing shared multi-user systems often tighten this further (a umask of 077, for instance, removes all group/other access entirely) for environments where user files should be private by default rather than merely non-writable by others.

Immutable and Append-Only Attributes

Beyond the standard permission model, Linux file systems support additional extended attributes that constrain what even the owner (including root, in some configurations) can do to a file — a layer of protection distinct from and stronger than ordinary permission bits:

chattr +i important-config.conf   # immutable: cannot be modified or deleted by anyone, including root, until the flag is removed
chattr +a audit.log               # append-only: data can be added but existing content cannot be altered or removed
lsattr important-config.conf      # view current attributes

The immutable attribute is particularly useful for hardening critical configuration files against tampering — even a fully compromised root account cannot simply overwrite an immutable file without first explicitly clearing the attribute (chattr -i), which adds a meaningful speed bump against certain classes of attack or accidental damage, though it’s not an absolute guarantee against a sufficiently privileged and determined attacker who understands the mechanism.

Permissions in Containerized Environments

Container platforms like Docker introduce an additional wrinkle to the traditional permission model worth understanding, since containers share the host’s kernel rather than running a fully separate OS instance. A process running as UID 0 (root) inside a container is, by default, the same root as UID 0 on the underlying host from the kernel’s perspective — a fact that has real security implications if a container is compromised, since a container-escape vulnerability could translate directly into host-level root access. This is why user namespaces (remapping container UIDs to unprivileged host UIDs) and running containers as non-root users internally (USER directive in a Dockerfile) are widely recommended container security practices — they ensure that even “root” inside a container corresponds to an unprivileged, low-value account on the actual host system, limiting the damage a container escape can do.

Best Practices

Summary

Linux file permissions are built on a straightforward but powerful owner/group/other model, each with independent read/write/execute bits, stored directly in a file’s inode. Special bits — SUID, SGID, and the sticky bit — extend this model to handle privileged execution and shared-directory scenarios, while POSIX ACLs fill in the gaps when the basic three-category model isn’t granular enough. Directories interpret these bits distinctly from regular files, which explains behaviors (like being able to delete a “read-only” file) that often surprise newcomers. Understanding this system is foundational not just for day-to-day Linux administration, but for reasoning about security on any UNIX-derived platform, including macOS and, at the kernel level, Android.

FAQs

What does chmod 644 mean in practice? Owner gets read+write (6 = 4+2), group gets read-only (4), and other gets read-only (4) — the standard permission set for most regular data or configuration files.

Why can I delete a file I don’t own? Because deletion is controlled by write permission on the containing directory, not the file’s own permission bits — if you own or have write access to the directory, you can typically remove files inside it.

What’s the difference between chmod and chown? chmod changes what actions (read/write/execute) are allowed; chown changes who owns the file (user and/or group), which is a completely separate axis from what permissions are granted.

Is SUID dangerous? It can be, if the SUID binary has an exploitable bug, since the bug would then execute with the file owner’s (often root’s) privileges. It’s not inherently dangerous, but it’s a common target in privilege escalation research and should be minimized and audited.

Do symbolic links have their own permissions? Symlinks themselves typically show rwxrwxrwx (Linux effectively ignores their own permission bits), but access is actually governed by the permissions of the target file they point to.

References

Exit mobile version