Filesystem core concepts: directory layout, inode metadata, permissions, links,
file descriptors, and common security pitfalls.
Standard directory layout for Unix-like systems.
| Directory | Purpose | Examples |
|---|
/ | Root filesystem | Must boot and recover from here |
/bin | Essential user binaries | ls, cp, cat |
/sbin | Essential system binaries | init, mount, fsck |
/etc | Host-specific configuration | passwd, fstab, hosts |
/var | Variable data (survives reboot) | /var/log, /var/tmp |
/tmp | Temporary (cleared on reboot) | Session data |
/home | User home directories | /home/alice |
/root | Root user’s home | |
/usr | Secondary hierarchy (shareable) | Distro-installed software |
/usr/local | Locally installed software | Admin-installed programs |
/opt | Add-on application packages | Third-party software |
/srv | Data for services | Web server files |
/run | Runtime variable data | PID files, sockets |
| Mount | Type | Purpose |
|---|
/dev | devtmpfs | Device nodes (udev managed) |
/proc | procfs | Process and kernel info |
/sys | sysfs | Device and driver info |
/tmp | tmpfs | RAM-backed temporary storage |
/run | tmpfs | Runtime data (cleared on reboot) |
/proc/PID/cmdline # Command line arguments
/proc/PID/cwd # Symlink to current working directory
/proc/PID/exe # Symlink to executable
/proc/PID/fd/ # Open file descriptors
/proc/PID/maps # Memory mappings
/proc/PID/status # Human-readable status
/proc/PID/environ # Environment variables
/proc/cpuinfo # CPU information
/proc/meminfo # Memory statistics
/proc/mounts # Mounted filesystems
/sys/block/ # Block devices
/sys/class/ # Device classes (net, tty, etc.)
/sys/devices/ # Device hierarchy
/sys/fs/ # Filesystem information
| Device | Purpose |
|---|
/dev/null | Discards writes, reads EOF |
/dev/zero | Reads return zeros |
/dev/random | Blocking random generator |
/dev/urandom | Non-blocking random |
/dev/stdin | Symlink to /proc/self/fd/0 |
/dev/stdout | Symlink to /proc/self/fd/1 |
/dev/stderr | Symlink to /proc/self/fd/2 |
/dev/tty | Controlling terminal |
An inode stores file metadata—everything except the filename:
| Field | Description | Accessed via |
|---|
| Device ID | Filesystem hosting the inode | stat.st_dev |
| Inode number | Unique ID within filesystem | stat.st_ino |
| File type | Regular, directory, symlink, etc | stat.st_mode |
| Permissions | rwx bits for user/group/other | stat.st_mode |
| Link count | Number of hard links | stat.st_nlink |
| Owner/Group | UID and GID | stat.st_uid/gid |
| File size | Bytes (regular files/symlinks) | stat.st_size |
| Timestamps | atime, mtime, ctime | stat.st_*time |
| Aspect | Hard Link | Symbolic Link |
|---|
| Points to | Same inode | Pathname string |
| Cross filesystem | No | Yes |
| Link to directory | No (prevents loops) | Yes |
| Target deleted | Data remains (link count > 0) | Dangling link |
| Storage overhead | None | Stores target path |
# Hard link: both names share same inode
stat file hardlink # Same inode number
# Symbolic link: stores path to target
readlink symlink # Returns "file"
# Find all hard links to a file
find / -inum $(stat -c %i file) 2>/dev/null
readlink -f symlink # Final target
namei -l symlink # Full chain with permissions
| Type | Mode | Description |
|---|
| Regular | - | Ordinary file |
| Directory | d | Contains entries |
| Symlink | l | Points to path |
| Block | b | Block device |
| Character | c | Character device |
| FIFO | p | Named pipe |
| Socket | s | Unix domain socket |
Integer handles for open files, pipes, sockets, devices.
| FD | Name | Default | Shell |
|---|
| 0 | stdin | Terminal input | < file |
| 1 | stdout | Terminal output | > file |
| 2 | stderr | Terminal error | 2> file |
Process FD Table → Open File Table → Inode Table
[0] ──────────→ [offset, flags] ──→ [file metadata]
[1] ──────────→ [offset, flags] ──→ [file metadata]
[2] ──────────→ [offset, flags] ──→ [file metadata]
cat /proc/sys/fs/file-max # System-wide limit
Three sets control access for user (owner), group, and other (everyone else):
| Permission | Files | Directories |
|---|
r (4) | Read contents | List contents |
w (2) | Modify contents | Create/delete/rename files |
x (1) | Execute file | Access directory (cd into it) |
| Octal | Permissions | Common use |
|---|
755 | rwxr-xr-x | Directories, executables |
644 | rw-r—r— | Regular files |
600 | rw------- | Private files (credentials) |
700 | rwx------ | Private directories |
| Bit | Octal | On Files | On Directories |
|---|
| setuid | 4xxx | Execute as file owner | No effect |
| setgid | 2xxx | Execute as group owner | New files inherit directory group |
| sticky | 1xxx | No modern effect | Only owner can delete files |
# Find setuid/setgid files
find / -perm /6000 -type f 2>/dev/null
chmod u-s /path/to/binary
# Set sticky bit on shared directory
When basic permissions aren’t enough:
# Set ACL for specific user
setfacl -m u:alice:rw file.txt
# Set ACL for specific group
setfacl -m g:developers:rx directory/
# Default ACL (inherited by new files)
setfacl -d -m g:developers:rwx directory/
Masks permission bits when creating new files:
| umask | Files (from 666) | Dirs (from 777) |
|---|
| 022 | 644 (rw-r—r—) | 755 (rwxr-xr-x) |
| 027 | 640 (rw-r-----) | 750 (rwxr-x---) |
| 077 | 600 (rw-------) | 700 (rwx------) |
umask 027 # Set restrictive default
# Remove write for group/other
# Recursive: read all, execute dirs only
# Show permissions along path
# Find world-writable files
find / -perm -002 -type f 2>/dev/null
# Find world-writable directories without sticky bit
find / -perm -002 -type d ! -perm -1000 2>/dev/null
# BAD: Predictable, race condition
# GOOD: Atomic creation with random suffix
tmpfile=$(mktemp /tmp/myapp.XXXXXX)
name = tempfile.mktemp() # Race condition
fd, name = tempfile.mkstemp() # Atomic
Attacker creates symlink pointing to sensitive file in predictable location.
Mitigations:
- Use
O_NOFOLLOW flag with open()
- Create temp files in protected directories
- Enable kernel protections:
sysctl fs.protected_symlinks=1
find / -user root -perm -4000 2>/dev/null
# Find world-readable credentials
find /etc -name "*.conf" -perm -004 2>/dev/null | xargs grep -l password
namei -l /path/to/sensitive/file