Back Original

Hard times with hard links

“Not every man who bears the mark of the castaway…”

I wanted to have the same file in multiple places in my Obsidian vault. A symlink is the natural way to do this, but the Obsidian desktop app does not recognize symlinks.1

So I turned to the other kind of filesystem link on Unix-like operating systems: hard links.

A symlink is a reference to a path, while a hard link is a reference to an inode, the filesystem data structure that stores a file's metadata and its location on disk. Symlinks and hard links behave the same in most respects, but not when the target file is deleted: the symlink will be left dangling, while the hard link will keep the file's contents alive. The filesystem keeps a count of links to an inode and will not delete the file contents while there is at least one link. In fact, a regular file is just a hard link with a link count of 1 (or, a hard link is just a regular file with a link count greater than 1), while symlinks are a distinct file type alongside regular files and directories.

Suppose I have a hard link journal.md that points to the same inode as archive/2026/2026-journal.md. Any edits that I make to journal.md will also apply to archive/2026/2026-journal.md. If I delete journal.md, the archive file will remain untouched. So far, so good.

Now, what happens if a program overwrites journal.md?

  • If the program opens it with O_TRUNC and writes out the new contents, then journal.md and archive/2026/2026-journal.md will continue to share the same inode.
  • If the program instead creates a temporary file, writes to it, and atomically replaces journal.md using rename(2), journal.md will now have a different inode from archive/2026/2026-journal.md, and the two files will be decoupled: any future changes I make to journal.md will not affect the archive file. The hard link is broken – the two files are still valid, but they are no longer linked to each other as was intended.

To avoid breaking the link, you could check the file's link count before overwriting and use the O_TRUNC method if it has multiple links, but in practice almost no one does this,2 and even if you do, your write is no longer atomic, and there is still an unavoidable time-of-check to time-of-use race condition between checking the link count and overwriting the file.

Now suppose that your Obsidian vault is also a Git repository. As far as Git is concerned, the hard-linked files are two independent files, and Git operations will not respect the linkage:

  • git checkout, git restore, and other commands that overwrite files may break the links.
  • If the Git repository is cloned elsewhere, the two files in the clone will not be links at all, and their content can diverge. If you then pull back the divergent changes to the original repo, confusion will surely ensue.

Hard links seem appealing – symlinks that can never dangle! – but it is difficult for software to work with them reliably.

Further reading