<!--date=20250205 -->


<h1>Unix File Systems and Permissions</h1>
<p>Permissions are one of those things you think you understand until you have to explain them to someone else. The nine-character string from <code>ls -l</code> — <code>-rw-r--r--</code> — looks simple, but there are enough edge cases hiding in it to keep things interesting. This covers the core concepts and the parts that actually trip people up.</p>

<h2>The Directory Tree</h2>
<p>Everything in Unix lives under <code>/</code>. There's no C: drive, no volumes by letter — it's one tree, and other disks get mounted into it at whatever point you choose.</p>

<table>
  <thead><tr><th>Directory</th><th>Purpose</th></tr></thead>
  <tbody>
    <tr><td><code>/bin</code>, <code>/usr/bin</code></td><td>System and user binaries (commands)</td></tr>
    <tr><td><code>/sbin</code>, <code>/usr/sbin</code></td><td>System administration binaries (often root-only)</td></tr>
    <tr><td><code>/etc</code></td><td>Configuration files — plain text, almost always editable</td></tr>
    <tr><td><code>/home</code></td><td>User home directories</td></tr>
    <tr><td><code>/var</code></td><td>Logs, caches, mail, databases — anything that grows over time</td></tr>
    <tr><td><code>/tmp</code></td><td>Temporary files, usually cleared on reboot</td></tr>
    <tr><td><code>/proc</code>, <code>/sys</code></td><td>Not real files — the kernel exposing itself as a filesystem</td></tr>
    <tr><td><code>/dev</code></td><td>Device files — disks, terminals, serial ports</td></tr>
  </tbody>
</table>

<h2>File Types</h2>
<p>The first character of <code>ls -l</code> output tells you what something actually is:</p>
<pre>
-   regular file
d   directory
l   symbolic link
c   character device (terminals, serial ports)
b   block device (disks)
s   socket (used by network services and databases)
p   named pipe (FIFO — for inter-process communication)
</pre>

<h2>Reading the Permission Bits</h2>
<pre>
$ ls -l notes.txt
-rw-r--r-- 1 jason users 4096 Feb  5 12:34 notes.txt

Breakdown:
  -          file type: regular file
  rw-        owner (jason): read + write
  r--        group (users): read only
  r--        others (everyone else): read only
  1          number of hard links
  jason      owner
  users      group
  4096       size in bytes
  Feb  5     modification time
  notes.txt  name
</pre>

<p>For directories, <code>x</code> (execute) means you can enter it (<code>cd</code> into it) and access files inside. Without <code>x</code> on a directory, you can't traverse it even if you can read the directory listing.</p>

<h2>Changing Permissions: chmod</h2>

<h3>Numeric Mode</h3>
<p>Read = 4, Write = 2, Execute = 1. Add them for each of the three groups (owner, group, others):</p>

<table>
  <thead><tr><th>Command</th><th>Result</th><th>Typical use</th></tr></thead>
  <tbody>
    <tr><td><code>chmod 755 script.sh</code></td><td><code>rwxr-xr-x</code></td><td>Executable: owner full, others read+execute</td></tr>
    <tr><td><code>chmod 644 config.txt</code></td><td><code>rw-r--r--</code></td><td>Files: owner read+write, others read</td></tr>
    <tr><td><code>chmod 600 id_rsa</code></td><td><code>rw-------</code></td><td>Private keys and secrets: owner only</td></tr>
    <tr><td><code>chmod 700 ~/.ssh</code></td><td><code>rwx------</code></td><td>Private directories: owner only</td></tr>
    <tr><td><code>chmod 2775 /srv/shared</code></td><td><code>rwxrwsr-x</code></td><td>Setgid directory for shared team projects</td></tr>
  </tbody>
</table>

<h3>Symbolic Mode</h3>
<pre>
$ chmod u+x script.sh      # add execute for owner (u=user/owner)
$ chmod go-w file.txt      # remove write from group and others
$ chmod a+r readme.txt     # add read for all (a=all)
$ chmod o-rx /home/jason   # remove read+execute from others on home dir
</pre>

<h2>Ownership: chown and chgrp</h2>
<pre>
$ chown jason file.txt           # change owner
$ chown jason:users file.txt     # change owner and group
$ chgrp www-data /var/www/html   # change group only
$ chown -R jason:jason ~/project # recursive — useful after copying files as root

# You need root to give a file to another user.
# You can change your own files to a group you belong to without sudo.
</pre>

<h2>Special Permission Bits</h2>

<h3>Setuid (4xxx)</h3>
<p>When set on an executable, it runs as the file's owner rather than whoever ran it. The classic example is <code>passwd</code> — ordinary users need to be able to change their own passwords, but the password database (<code>/etc/shadow</code>) requires root access to modify:</p>
<pre>
$ ls -l /usr/bin/passwd
-rwsr-xr-x 1 root root 59976 ... /usr/bin/passwd
           ↑ 's' in owner execute position = setuid
</pre>

<h3>Setgid (2xxx)</h3>
<p>On an executable: same idea — runs with the file's group. On a directory: new files created inside inherit the directory's group instead of the creator's primary group. Useful for shared project directories:</p>
<pre>
$ chmod 2775 /srv/shared
$ ls -ld /srv/shared
drwxrwsr-x 2 root devteam 4096 ... /srv/shared
              ↑ 's' in group execute position = setgid

Files created inside /srv/shared automatically get group "devteam"
regardless of which team member creates them.
</pre>

<h3>Sticky Bit (1xxx)</h3>
<p>On a directory, only the file's owner (or root) can delete or rename files inside, even if others have write permission on the directory. <code>/tmp</code> uses this — everyone can write there, but you can't delete each other's files:</p>
<pre>
$ ls -ld /tmp
drwxrwxrwt 12 root root 4096 ... /tmp
              ↑ 't' at end = sticky bit
</pre>

<h2>Access Control Lists (ACLs)</h2>
<p>Standard Unix permissions give you three buckets — owner, group, everyone else. ACLs add per-user or per-group permissions on top of that without changing the base permissions. Supported by ext4, XFS, Btrfs:</p>
<pre>
$ getfacl file.txt                     # view current ACL
$ setfacl -m u:sarah:rw file.txt       # give sarah read+write
$ setfacl -m g:contractors:r file.txt  # give contractors read
$ setfacl -x u:sarah file.txt          # remove sarah's ACL entry
$ setfacl -b file.txt                  # remove all ACL entries

When a file has an ACL, ls -l shows a '+' at the end of the permissions:
  -rw-r--r--+ 1 jason users 4096 ... file.txt
</pre>

<h2>Finding Files by Permission</h2>
<pre>
$ find /usr/bin -perm -4000            # all setuid binaries (security audit)
$ find /home -perm 777                  # world-writable files (usually wrong)
$ find /var/www -not -user www-data    # files not owned by the web user
$ find /tmp -perm -002 -type f         # world-writable files in /tmp
</pre>

<h2>References</h2>
<ul>
  <li><a href="https://man.openbsd.org/chmod.1" target="_blank" rel="noopener">chmod(1) man page — OpenBSD ↗</a></li>
  <li><a href="https://www.gnu.org/software/acl/manual/acl.html" target="_blank" rel="noopener">GNU ACL Manual ↗</a></li>
  <li><a href="https://tldp.org/LDP/intro-linux/html/sect_03_04.html" target="_blank" rel="noopener">Introduction to Linux — File Permissions ↗</a></li>
</ul>
