Why this matters
Links explain a whole family of puzzles: why deleting a huge log file did not free any disk space, why /bin/ls and /usr/bin/ls are the same file, how servers switch to a new software release in one instant step, and what "file names" really are. They also finish the picture of the filesystem you started building in module 2.
Names and inodes, again
Recall the model: a directory is a table of name → inode number; the inode holds the file's metadata and points to its data. The name is not part of the file. That has a striking consequence: nothing stops two directory entries from naming the same inode.
data/ inode 16500
┌──────────────┬───────┐ ┌──────────────────────┐ ┌──────────────────────┐
│ original.txt │ 16500 ├──────┬────▶│ regular file, 0644 │────▶│ the original content │
│ twin.txt │ 16500 ├──────┘ │ links: 2 │ └──────────────────────┘
│ shortcut.txt │ 16501 ├──┐ └──────────────────────┘
└──────────────┴───────┘ │ inode 16501
└────────▶┌──────────────────────┐
│ symbolic link │ content: "original.txt"
└──────────────────────┘
Hard links
ln TARGET NAME creates a hard link: a new directory entry for an existing inode.
Open the shell for this lesson and try:
$ cd data
$ ls -li
16500 -rw-r--r--. 1 learner learner 21 Nov 1 15:00 original.txt
$ ln original.txt twin.txt
$ ls -li
16500 -rw-r--r--. 2 learner learner 21 Nov 1 15:00 original.txt
16500 -rw-r--r--. 2 learner learner 21 Nov 1 15:00 twin.txt
Same inode number, and the link count column went from 1 to 2. Both names are equal; there is no original and no copy. Change the content through one name and you see it through the other, because there is only one file:
$ echo more >> twin.txt
$ cat original.txt
the original content
more
Deleting is unlinking
This is why the system call behind rm is called unlink. It removes one name and decrements the link count. The kernel frees the inode and its data blocks only when both:
- the link count is 0, and
- no process still has the file open.
The second rule explains a classic puzzle: a disk is full because of a huge log file, you rm it, and df shows no space freed. The program writing the log still has it open, so the data lives on (nameless) until that program closes it or exits. Restarting the program frees the space.
Limits of hard links
- Same filesystem only. An inode number means something only inside its own filesystem.
ln /work/x /tmp/xfails withInvalid cross-device link. - No directories.
ln dir dir2giveshard link not allowed for directory. If directories could have several parents, the tree could contain loops, and tools that walk it would never finish. (The.and..entries are the only hard links to directories, managed by the kernel itself.)
Symbolic links
ln -s TARGET NAME creates a symbolic link (symlink, soft link): a small file of its own, with its own inode, whose content is just a path. Whenever a program uses the link, the kernel reads that path and continues resolving from there.
$ ln -s original.txt shortcut.txt
$ ls -li
16500 -rw-r--r--. 2 learner learner 26 Nov 1 15:00 original.txt
16501 lrwxrwxrwx. 1 learner learner 12 Nov 1 15:02 shortcut.txt -> original.txt
16500 -rw-r--r--. 2 learner learner 26 Nov 1 15:00 twin.txt
$ cat shortcut.txt
the original content
more
$ readlink shortcut.txt
original.txt
Note the l type, the arrow, and the size: 12 bytes, the length of the text original.txt. The permissions of a symlink are always shown as rwxrwxrwx and do not matter; the target's permissions are what count.
Symlinks can do what hard links cannot: point at directories and across filesystems. This system uses them itself:
$ ls -l /bin
lrwxrwxrwx. 1 root root 7 Jan 1 2025 /bin -> usr/bin
Relative targets are relative to the link
The target text is interpreted relative to the directory containing the link, not to your current directory when you created it. This is the most common symlink mistake:
$ cd /work
$ ln -s data/original.txt data/oops.txt
$ cat data/oops.txt
cat: data/oops.txt: No such file or directory
$ readlink data/oops.txt
data/original.txt
The link lives in data/ and points to data/original.txt relative to data/, which means /work/data/data/original.txt. Either cd into the link's directory first, or use an absolute target, or use ln -sr (GNU: compute the relative path for you).
Dangling links
Since a symlink is only a path, its target can disappear or never exist. The link stays, now dangling (broken). ls -l still shows it; using it fails:
$ ls -l app/current
lrwxrwxrwx. 1 learner learner 13 Nov 1 15:05 app/current -> releases/v1.2
$ cat app/current/VERSION
cat: app/current/VERSION: No such file or directory
With colors on, ls shows dangling links in red. test -e (later module) is false for a dangling link because it follows the link; test -L checks for the link itself.
Replacing a link: ln -sfn
ln -s refuses if the name exists (File exists). -f removes the old name first. But if the existing name is a symlink to a directory, ln treats it as that directory and creates the new link inside it; -n says "treat a symlink to a directory as a plain name". So the idiom to repoint a link is:
$ ln -sfn releases/v1.4 app/current
This is how many deployment tools switch releases: the new version is unpacked beside the old, then one link is repointed. (For a truly atomic switch, tools create the new link under a temporary name and mv -T it over the old one, because rename is atomic.)
Hard or symbolic?
| Hard link | Symbolic link | |
|---|---|---|
| What it is | another name for the same inode | a small file containing a path |
| Own inode | no | yes |
| Directories | no | yes |
| Across filesystems | no | yes |
| Target deleted | still works (it is the file) | dangling |
Shown by ls -l | link count above 1 | l type and -> |
In everyday work you will create symlinks far more often. Hard links appear in backup tools (which hard-link unchanged files between snapshots to save space) and in the link count of ls -l.
Common mistakes
- Relative target written from the wrong place. The target is resolved from the link's directory. Check with
readlinkandls -l. - Argument order. It is
ln -s TARGET LINKNAME, the same order ascp SOURCE DEST. If you swap them, ln may complainFile existsor create a link in an unexpected place. rm link/on a symlink to a directory. With the trailing slash, some commands act on the target directory. Remove the link itself withrm link(no slash).- Expecting
rmto free space for an open file. The data stays until the last process closes it. - Repointing a directory link without
-n. You get a new link inside the old target.
Recap
- A name is a directory entry pointing at an inode; an inode can have several names (hard links).
- The link count in
ls -lcounts names; data is freed at zero names and zero open handles. rmisunlink: it removes a name, never "the file" directly.- Symbolic links are tiny files holding a path; they work across filesystems and for directories, and can dangle.
- Relative symlink targets are relative to the link's own directory.
ln TARGET NAMEhard,ln -s TARGET NAMEsymbolic,ln -sfnto repoint,readlinkto inspect.
Going further: stat -c '%h %n' /usr/bin/* | sort -rn | head shows the programs with the most hard links. Then try ls -l /etc/alternatives | head: Debian's system of symlinks for choosing default programs (the editor, pager and awk commands are chosen this way).