Skip to content
ioob.dev
Go back

Linux Basics Part 2 — File Permissions and Users/Groups

· 8 min read
Linux Series (2/8)
  1. Linux Basics Part 1 — The Shell and Filesystem Structure
  2. Linux Basics Part 2 — File Permissions and Users/Groups
  3. Linux Basics Part 3 — Processes and Signals
  4. Linux Basics Part 4 — Text Processing and Pipes
  5. Linux Basics Part 5 — Network Tools
  6. Linux Basics Part 6 — Systemd and Service Management
  7. Linux Basics Part 7 — Package Management
  8. Linux Basics Part 8 — Bash Scripting Basics
Table of contents

Table of contents

Why Are Permissions So Strict?

You uploaded a deployment script to a server, but Permission denied blocks execution. You try to edit someone else’s file and get rejected with “that’s not yours.” Why does everything keep getting blocked, and why was the system designed this way? — Every Linux beginner has gone through this frustration at least once.

The answer is simple. Linux was designed from the start with the premise of multiple users sharing a single machine. In the 1970s, dozens of users simultaneously connected to and worked on the minicomputers running UNIX. File permissions are the safeguard preventing someone from breaking another person’s files and preventing regular users from touching system files. This structure has survived nearly unchanged for over 50 years.

This part starts by decoding those cryptic symbols on the left side of ls -l output.

User, Group, and Others

Every file in Linux has three types of “stakeholders.”

flowchart LR
    FILE["File or Directory"]
    FILE --> OWNER["Owner<br/>(owner / user)"]
    FILE --> GROUP["Group<br/>(group)"]
    FILE --> OTHER["Everyone else<br/>(others / world)"]

For each stakeholder, actions on the file are split into read (r), write (w), and execute (x), each allowed or denied. 3 x 3 = 9 bits complete the access permissions.

The command to check which groups you belong to is id.

id
# uid=1000(alice) gid=1000(alice) groups=1000(alice),27(sudo),999(docker)

groups
# alice sudo docker

alice belongs to her own default group (primary group), and is additionally a member of the sudo and docker groups. This structure is why you need to be in the docker group to access the Docker socket.

Decoding the 9 Characters of ls -l

Let’s pull up the beginning of ls -l output again.

-rwxr-xr--  1 alice  developers  1024  Apr 20 10:30  deploy.sh

As mentioned earlier, the first character (-) is the file type. The next 9 characters are permissions, split into three groups of three.

-   rwx   r-x   r--
│    │     │     │
│    │     │     └── others: read only
│    │     └──────── group: read and execute
│    └────────────── owner: read, write, and execute
└─────────────────── regular file

Without x on a directory, you can’t even cd into it. Conversely, a directory with only r but no x creates a peculiar state where ls can see the names but you can’t access the files inside. The subtle differences that emerge from combining these permissions can be confusing at first — it’s better to try creating them yourself.

chmod — Changing Permissions

Permission changes are done with chmod (CHange MODe). There are two syntaxes.

Symbolic Mode — Human-Readable

Uses the format user type + operator + permission.

# User types: u(user/owner) g(group) o(others) a(all)
# Operators:  + (add) - (remove) = (set exactly this)
# Permissions: r w x

chmod u+x deploy.sh          # Add execute permission for the owner
chmod g-w notes.txt          # Remove write permission from the group
chmod o=r secret.env         # Others get read only
chmod a+r public.md          # Add read permission for everyone
chmod u=rwx,g=rx,o=r file    # Multiple specifications, comma-separated

Numeric Mode — Much Faster Once You Learn It

This method views each digit as a 3-bit binary number. Add r=4, w=2, x=1.

Permission     Binary  Decimal
---            ---     ---
rwx            111      7
rw-            110      6
r-x            101      5
r--            100      4
-wx            011      3
-w-            010      2
--x            001      1
---            000      0

Lining up three digits (owner, group, other) gives you the numeric mode.

chmod 755 deploy.sh
# owner=rwx(7), group=r-x(5), other=r-x(5)

chmod 644 notes.txt
# owner=rw-(6), group=r--(4), other=r--(4)

chmod 600 ~/.ssh/id_rsa
# Only the owner can read and write. SSH private keys must be 600 to work

A few combinations come up frequently in practice.

For recursive application, use the -R option. However, applying the same permissions to both files and directories causes problems — directories need x, but files get unnecessary x. In such cases, combine with find.

# Directories get 755, files get 644
find /path/to/dir -type d -exec chmod 755 {} \;
find /path/to/dir -type f -exec chmod 644 {} \;

chown — Changing Ownership

The command to change the owner or owning group is chown (CHange OWNer). It usually requires root privileges.

# Change owner only
sudo chown alice file.txt

# Change owner and group at once (separated by colon)
sudo chown alice:developers file.txt

# Change group only — leave the part before the colon empty, or use chgrp
sudo chown :developers file.txt
sudo chgrp developers file.txt

# Recursively
sudo chown -R alice:developers /var/www/site

A common scenario when running web servers is “I upload files with my account, but the www-data user runs them.” In this case, you keep the owner as your account, change the group to www-data, and grant read/execute permission to the group.

sudo — Borrowing Privileges Only When Needed

The supreme administrator in Linux is the root user (UID 0). But staying logged in as root all the time is dangerous. A single typo can destroy the system, and there’s often no way to recover accidentally deleted files.

That’s why sudo (SuperUser DO) exists. You stay as a regular user normally, and borrow root’s power only for the one command that needs admin privileges.

# Trying as a regular user — denied
apt install nginx
# Permission denied

# Prefix with sudo for one-time root access
sudo apt install nginx

# Switch to a root shell entirely (use with caution)
sudo -i
# or
sudo su -

Users who can use sudo are defined in the /etc/sudoers file (or files in /etc/sudoers.d/). Depending on the distribution, users in the sudo or wheel group are configured to use sudo by default.

Never edit the sudoers file directly with vi. A syntax error can lock you out of the system. Always use visudo — it validates the syntax before saving.

sudo visudo

umask — Determining Default Permissions for New Files

When you create an empty file with touch new.txt, what are the default permissions? Usually 644 (rw-r--r--). What determines this default is umask (User MASK).

umask specifies the permissions to subtract. It works by subtracting the umask value from the “potential maximum permissions” of a new file.

Let’s say the typical umask is 022.

File:      666 - 022 = 644  (rw-r--r--)
Directory: 777 - 022 = 755  (rwxr-xr-x)

Here’s how to check and set the current umask value.

umask
# 0022

# Set a stricter value — completely block group/others write permission
umask 077

# Check a newly created file
touch test.txt
ls -l test.txt
# -rw-------  1 alice  alice  0  Apr 20 10:30  test.txt

With 077, new files get 600 and new directories get 700. If protecting personal data matters on a shared server, a common pattern is to hardcode umask 077 in ~/.bashrc or ~/.zshrc.

An important note: umask does not change permissions on existing files. It only affects the “default permissions of files created going forward.”

SUID, SGID, Sticky Bit — The Three Special Permission Bits

Occasionally you’ll see s or t mixed into ls -l output. These are special permission bits. In numeric mode, they’re represented by the leading digit (4xxx, 2xxx, 1xxx).

SUID (Set User ID) — Execute with the Owner’s Permissions

When SUID is set on an executable, it runs with the file owner’s permissions regardless of who executes it. The x in the owner position becomes s.

ls -l /usr/bin/passwd
# -rwsr-xr-x 1 root root  68208 Feb  5 11:35  /usr/bin/passwd

The passwd command lets regular users change their own password. But the actual password is stored in /etc/shadow, which only root can write to. SUID is the solution. Because passwd has SUID set, even a regular user temporarily gains root privileges when executing it, allowing them to modify /etc/shadow.

SUID is powerful, and accordingly the most dangerous bit from a security perspective. If a SUID binary has a vulnerability, it becomes a pathway for privilege escalation. It’s worth reviewing the list of SUID binaries on your server at least once.

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

SUID is set with chmod u+s or chmod 4xxx.

SGID (Set Group ID) — Group Inheritance

When SGID is set on a directory, new files created inside it automatically inherit the directory’s group. The x in the group position becomes s.

sudo mkdir /shared
sudo chown alice:developers /shared
sudo chmod 2775 /shared         # Leading 2 is SGID
# drwxrwsr-x  alice  developers  /shared

# Now regardless of who creates a file, its group will be developers
cd /shared && touch file.txt
ls -l file.txt
# -rw-r--r-- bob developers ... file.txt

This is useful when creating shared team folders. Regardless of which group each person logged in with, all files created in that folder share the same group.

Sticky Bit — You Can Only Delete Your Own Files

Look at the permissions on /tmp and you’ll see something unusual.

ls -ld /tmp
# drwxrwxrwt  root root  /tmp

The last character is t instead of x. This is the sticky bit. It’s a mechanism that restricts a world-writable directory so that only the creator of a file can delete it. In places like /tmp where every user writes temporary files, it prevents users from deleting each other’s files.

chmod +t mydir
chmod 1777 mydir

Permission Debugging — When You’re Stuck in Practice

A checklist to run through when someone says “it won’t execute.”

  1. Does the file itself have x? Check with ls -l script.sh. If not, chmod +x
  2. Does the directory containing the file have x? Without directory x, you can’t access files inside it
  3. Do all parent directories have x? To execute /home/alice/scripts/run.sh, /, /home, /home/alice, and /home/alice/scripts all need x
  4. Is SELinux or AppArmor blocking it? Especially on RHEL-based systems, if ls -l shows permissions look fine but access is still denied, suspect the SELinux context (ls -Z)
  5. Is the filesystem mounted with noexec? If /tmp is mounted with noexec, script execution is completely blocked (mount | grep /tmp)

Permission problems are usually caught at #1 and #3. Especially #3 — x on parent directories — is the easiest point for beginners to miss.

Recap of This Part

A brief summary.

In the next part, we move beyond the world of files and permissions into processes. From PIDs to signals to background execution — we’ll dig into what “running” really means in Linux.

-> Part 3: Processes and Signals


Related Posts

Share this post on:

Comments

Loading comments...


Previous Post
Linux Basics Part 1 — The Shell and Filesystem Structure
Next Post
Linux Basics Part 3 — Processes and Signals