Memra

Two names for one file, and one name for a name

◈ 13 cards

`ln` versus `ln -s` derived from the inode: same inode and an incremented count versus a separate file holding a pathname — and exactly when removing a link destroys the data.

Two names for one file

Lesson 3 established that a directory entry is a (inode number, name) pair and that the inode has no name field. Everything in this lesson is that fact, cashed out.

If a name is just an entry in a table, there is no reason a second table row cannot hold the same inode number under a different name. That is a hard link, and ln existing new creates one. No data is copied. One directory entry is added and the inode's link count is incremented by one. The two names are not an original and a copy — they are peers, equally real, and neither can be identified as "the first one" afterwards, because the inode does not record which entry came first.

A symbolic linkln -s target new — is a completely different animal. It creates a whole new file, with its own inode, of file type l, whose contents are the target's pathname as a string. Nothing about the target changes; its link count does not move. When anything opens the symbolic link, the kernel sees type l, reads the stored pathname out of it, and resolves that instead.

So: a hard link is a second name for an identity. A symbolic link is a name for a name.

Worked example — building both, one step at a time

Start with an ordinary file. ls -il prints the inode number first, then the usual nine fields:

$ ls -il Chapter3
4271 -rw-r--r--  1 ada staff 8192 Sep  1 10:00 Chapter3

Inode 4271, link count 1. Add a hard link:

$ ln Chapter3 Chapter3.hard
$ ls -il Chapter3 Chapter3.hard
4271 -rw-r--r--  2 ada staff 8192 Sep  1 10:00 Chapter3
4271 -rw-r--r--  2 ada staff 8192 Sep  1 10:00 Chapter3.hard

Read the three columns that changed and the four that did not. Same inode number on both lines — one file. Link count 2 on both, because the count lives in the shared inode and there are now two entries naming it. Identical permissions, owner, size and timestamp, for the same reason: those fields are in the inode too, and there is only one of it. chmod through either name changes both, because there is nothing separate to change.

If you added a third name — ln Chapter3.hard Chapter3.copy3 — all three lines would show a count of 3. The count is a property of the file, not of a name, so every name reports the same number.

Now the symbolic link:

$ ln -s Chapter3 Chapter3.soft
$ ls -il Chapter3 Chapter3.soft
4271 -rw-r--r--  2 ada staff 8192 Sep  1 10:00 Chapter3
5106 lrwxr-xr-x  1 ada staff    8 Sep  1 10:02 Chapter3.soft -> Chapter3

Four tells, and every one of them is diagnostic:

  1. A different inode number (5106): this is a different file, not another name for 4271.
  2. Type l in the first column, and ls helpfully prints -> Chapter3.
  3. Link count 1 on the link, and Chapter3's count is still 2 — creating a symbolic link does not touch the target at all.
  4. Size 8. Not 8192. The link's contents are the string Chapter3, which is eight characters long. Point at /tmp/Chapter3 instead and the size would be 13. This is the most convincing single detail in the topic: it is the claim "the contents are the pathname" made checkable in one command.

The deletion experiment

Now remove the original name:

$ rm Chapter3
$ cat Chapter3.hard
(the eight kilobytes of chapter three, intact)
$ cat Chapter3.soft
cat: Chapter3.soft: No such file or directory
$ ls -il Chapter3.hard Chapter3.soft
4271 -rw-r--r--  1 ada staff 8192 Sep  1 10:00 Chapter3.hard
5106 lrwxr-xr-x  1 ada staff    8 Sep  1 10:02 Chapter3.soft -> Chapter3

rm did not remove a file. It removed a directory entry and decremented the link count, 2 down to 1 — which is why the system call underneath it is literally named unlink(). The data was never in danger: Chapter3.hard still names inode 4271, and inode 4271 still owns its blocks.

The symbolic link, meanwhile, is now dangling. It is perfectly healthy as a file — ls lists it, ls -il reports its inode and size — but the pathname it stores no longer resolves, so every attempt to use it fails with a message about a file that you can plainly see. That confusing pairing is the characteristic symptom of a broken symlink, and file Chapter3.soft will say broken symbolic link to 'Chapter3' in as many words.

Remove the last hard link and then the data goes:

$ rm Chapter3.hard

The count reaches 0, and only now does the kernel release the inode and free the disk blocks. So the answer to "does deleting a link delete the file?" is exact: it deletes the file precisely when the link you removed was the last one.

One refinement that earns a mark and is genuinely useful: a count of zero is not quite sufficient. If some process still has the file open, the kernel keeps the inode and the blocks alive until the last descriptor is closed — the file has no name any more and cannot be reopened, but the running process reads and writes it happily. That is why deleting a large log file does not free disk space until you restart the daemon writing it.

What each kind cannot do

A hard link is bound to an inode number, and inode numbers are file-system-local, so:

  • A hard link cannot cross a file system. ln /etc/passwd ~/passwd fails with Invalid cross-device link. (Compare lesson 6: faced with the same boundary, mv copies; ln refuses.)
  • An ordinary user cannot hard-link a directory. ln /tmp ~/tmp fails — directory hard links would let you build cycles the tree-walking tools cannot escape, so the kernel forbids them.
  • If an editor writes a new file and renames it over the old one instead of rewriting in place, the other names are left pointing at the old, unchanged inode and the link is silently broken. (vi and emacs do not do this; plenty of other tools do.)

A symbolic link is bound to a pathname, so it inverts every one of those: it can cross file systems, it can point at a directory, it can even point at something that does not exist yet — and it dangles the moment the target is moved or removed. It also costs an extra inode, the disk space for the pathname, and an extra open-and-read on every single access.

Why every directory has at least two links

One more consequence, and it is the best question in the topic. A brand-new empty directory has a link count of 2, not 1:

  • its parent holds an entry naming it, and
  • it holds its own . entry, which names it as well.

Create two subdirectories inside it and the count becomes 4, because each subdirectory's .. is one more directory entry pointing at the parent's inode. In general a directory's link count is 2 + (number of subdirectories) — which means you can read a directory's subdirectory count straight off column three of ls -ld without listing anything.

resolved on useChapter3directory entryChapter3.harddirectory entryinode 4271type -, links 2Chapter3.softdirectory entryinode 5106type l, links 1data: "Chapter3"8 bytesrm Chapter3 removes one arrow into 4271 and drops its count to 1.
The top path is a hard link: two names, one inode, link count 2. The bottom path is a symbolic link: its own inode, its own data block, and the data is the string "Chapter3" — eight bytes, which is what ls reports as its size.
+1+1projlink count 4srcits .. names projdocsits .. names proj
A directory's link count is 2 + the number of subdirectories. Column three of ls -ld therefore tells you how many subdirectories a directory has without listing it.
NORMAL ~/memra/learn/comp-325/hard-links-symbolic-links-and-link-counts utf-8 LF