unix · level 4

Filesystem & Links

Inodes, hard links, symlinks, and where config lives.

150 XP

Filesystem & Links

The filesystem is more than a tree of files and folders. Underneath, everything is an inode — a data structure that stores metadata and points to the actual data blocks on disk. Filenames are just labels that directories use to reference inodes.

Analogy

Think of a warehouse with numbered storage bins and a front-desk logbook. The bin (the inode) is where the actual stuff lives — weight, size, contents, who owns it. The logbook is just a list of nicknames mapped to bin numbers: "Summer Inventory → bin 4471". A hard link is writing a second nickname for the same bin — two entries in the logbook, one bin; scratch out one nickname and the other still reaches the stuff. A symlink is a sticky note that says "go look under this other nickname" — if that nickname is later erased from the logbook, the sticky note points at nothing and you get "No such file or directory". Mount points are a second warehouse whose loading dock has been welded to a specific aisle of the first, so to the forklift driver the building looks continuous.

Inodes

Every file has an inode. The inode stores:

  • File size, permissions, owner, timestamps
  • Pointers to the actual data blocks

The filename you see in ls is stored in a directory entry, which maps a name to an inode number. Multiple names can point to the same inode.

Hard links

A hard link is a second directory entry pointing to the same inode:

ln /etc/hosts /tmp/hosts-backup

Now /tmp/hosts-backup and /etc/hosts are two names for one file. The inode's link count is 2. Deleting /tmp/hosts-backup decrements the count to 1. The data is only removed when the link count reaches 0 and no process has the file open.

Hard links have two restrictions: they can't cross filesystem boundaries, and you should not hard-link directories (the kernel usually forbids it — it would create loops in the directory tree).

Symlinks

A symlink stores a path string, not an inode number:

ln -s /usr/local/bin/node /usr/bin/node

If /usr/local/bin/node is deleted, the symlink becomes dangling. Following it produces "No such file or directory". Unlike hard links, symlinks can cross filesystem boundaries and can point to directories.

Hard link Symlink
Points to Inode Path string
Works across filesystems No Yes
Can link directories No (usually) Yes
Survives target deletion Yes (data stays) No (becomes dangling)
Shown by ls -l No special mark -> annotation

Mount points

A mount point is a directory where a separate filesystem is grafted onto the tree. When you plug in a USB drive, macOS mounts it at /Volumes/MyDrive. The directory tree looks continuous, but the underlying storage is separate.

df -h shows mounted filesystems and their sizes. mount lists them all.

Paths — absolute and relative

An absolute path starts from /. It means the same thing regardless of where you are:

/Users/alice/code/project/README.md

A relative path starts from the current directory:

../../config/settings.json

$HOME and ~ both expand to your home directory. $HOME is an environment variable; ~ is expanded by the shell.

Where config lives

Platform Standard location
Linux (XDG) ~/.config/appname/
macOS (apps) ~/Library/Application Support/appname/
macOS (CLI tools) ~/.config/appname/ (increasingly common)
Both ~/.appnamerc or ~/.appname/config (older tools)

On macOS, GUI apps use ~/Library/. CLI tools often follow the XDG spec and use ~/.config/. Some older tools use dotfiles directly in $HOME.

macOS case-insensitive filesystem

The default macOS filesystem (APFS) is case-insensitive but case-preserving. That means:

  • README.md and readme.md are the same file as far as the OS is concerned.
  • But ls will show you whichever capitalisation you used when creating the file.

This matters when you work with code that runs on Linux (case-sensitive). A file named Button.tsx won't be found by an import for button.tsx on Linux, even though macOS happily serves it.

find

find . -name "*.log"              # files by name pattern
find . -type f -newer config.ts   # files newer than a reference file
find . -type d -empty             # empty directories
find . -name "*.tmp" -delete      # find and delete in one pass

find respects symlinks cautiously: -L tells it to follow them.