54

I've seen many explanations for why the link count for an empty directory in Unix based OSes is 2 instead of 1. They all say that it's because of the '.' directory, which every directory has pointing back to itself. I understand why having some concept of '.' is useful for specifying relative paths, but what is gained by implementing it at the filesystem level? Why not just have shells or the system calls that take paths know how to interpret it?

That '..' is a real link makes much more sense to me -- the filesystem needs to store a pointer back to the parent directory in order to navigate to it. But I don't see why '.' being a real link is necessary. It also seems like it leads to an ugly special case in the implementation -- you would think you could only free the space used by inodes that have a link count less than 1, but if they're directories, you actually need to check for a link count less than 2. Why the inconsistency?

1
  • 1
    Once you have the .. hardlinks your tree walking software already needs to have a "don't following cycles on the parent directory link" exceptions, so it is little added complexity to also except the . link. Commented Oct 16, 2011 at 19:42

2 Answers 2

40

An interesting question, indeed. At first glance I see the following advantages:

First of all you state that interpreting "." as the current directory may be done by the Shell or by system calls. But having the dot-entry in the directory actually removes this necessity and forces consistency at even a lower level.

But I don't think that this was the basic idea behind this design decision.

When a file is being created or removed from a directory, the directory's modification time stamp has to be updated, too. This timestamp is stored in its inode. The inode number is stored in the corresponding directory entry.

IF the dot entry would not be there, the routines would have to search for the inode number at the entry for this directory in the parent directory, which would cause a directory search again.

BUT luckily there is the dot entry in the current directory. The routine that adds or removes a file in the current directory just has to jump back to the first entry (where the dot-entry usually resides) and immediately has found the inode number for the current directory.

There is a third nice thing about the dot entry:

When fsck checks a rotten filesystem and has to deal with non-connected blocks that are also not on the free list, it's easy for it to verify if a data block (when interpreted as a directory list) has a dot entry that's pointing to an inode which in turn points back to this data block. If so, this data block may be considered as a lost directory which has to be reconnected.

3
  • Very useful answer. Commented Oct 21, 2011 at 11:58
  • 8
    The comment about routines searching for the directory inode is bogus. Kernel routines have no need to look up . in the current directory. Unless you can find a kernel where it actually works this way (I doubt it...) Commented Nov 30, 2012 at 0:32
  • 2
    I agree with @DietrichEpp; for the system to be looking at the directory entries in the first place, it must already know about the inode - because that's how it gets to the data blocks containing the directory entries.
    – Lqueryvg
    Commented Nov 21, 2014 at 13:13
11

(Hmm: the following is now a bit of an epic...)

The design of the directory on unix filesystems (which, to be pedantic, are typically but not necessarily attached to unix OSs) represents a wonderful insight, which actually reduces the number of special cases required.

A 'directory' is really just a file in the filesystem. All the actual content of files in the filesystem is in inodes (from your question, I can see that you're already aware of some of this stuff). There's no structure to the inodes on the disk -- they're just a big bunch of numbered blobs of bytes, spread like peanut-butter over the disk. This is not useful, and indeed is repellent to anyone with a shred of tidy-mindedness.

The only special inode is inode number 2 (not 0 or 1, for reasons of Tradition); inode 2 is a directory file: the root directory. When the system mounts the filesystem, it 'knows' it has to readdir inode 2, to get itself started.

A directory file is just a file, with an internal structure which is intended to be read by opendir(3) and friends. You can see its internal structure documented in dir(5) (depending on your OS); if you look at that, you'll see that the directory file entry contains almost no information about the file -- that's all in the file inode. One of the few things that's special about this file is that the open(2) function will given an error if you try to open a directory file with a mode which permits writing. Various other commands (to pick just one example, hexdump) will refuse to act in the normal way with directory files, just because that's probably not what you want to do (but that's their special case, not the filesystem's).

A hard link is nothing more nor less than an entry in a directory file's map. You can have two (or more) entries in such a map which both map to the same inode number: that inode therefore has two (or more) hard links. This also explains why every file has at least one 'hard link'. The inode has a reference count, which records how many times that inode is mentioned in a directory file somewhere in the filesystem (this is the number which you see when you do ls -l).

OK: we're getting to the point now.

The directory file is a map of strings ('filenames') to numbers (inode numbers). Those inode numbers are the numbers of the inodes of the files which are 'in' that directory. The files which are 'in' that directory might include other directory files, so their inode numbers will be amongst those listed in the directory. Thus, if you have a file /tmp/foo/bar, then the directory file foo includes an entry for bar, mapping that string to the inode for that file. There's also an entry in the directory file /tmp, for the directory file foo which is 'in' the directory /tmp.

When you create a directory with mkdir(2), that function

  1. creates a directory file (with some inode number) with the correct internal structure,
  2. adds an entry to the parent directory, mapping the new directory's name to this new inode (that accounts for one of the links),
  3. adds an entry to the new directory, mapping the string '.' to the same inode (this accounts for the other link), and
  4. adds another entry to the new directory, mapping the string '..' to the inode of the directory file it modified in step (2) (this accounts for the larger number of hard links you'll see on on directory files which contain subdirectories).

The end result is that (almost) the only special cases are:

  • The open(2) function tries to make it harder to shoot yourself in the foot, by preventing you opening directory files for writing.
  • The mkdir(2) function makes things nice and easy by adding a couple of extra entries ('.' and '..') to the new directory file, purely to make it convenient to move around the filesystem. I suspect that the filesystem would work perfectly well without '.' and '..', but would be a pain to use.
  • The directory file is one of the few types of files which are flagged as 'special' -- this is really what tells things like open(2) to behave slightly differently. See st_mode in stat(2).

(copied over from the stackoverflow original question, 2011-10-20)

3
  • 1
    You're confusing blocks with inodes. As a special-case, for short files, file-content might be inside the inode, but it's false to assert that inodes are unstructured. They're highly structured, containing almost all the file metadata except the filenames by which the file might be found. The inode contains pointers (direct, indirect, doubly-indirect, etc) to the blocks on disk, where the file content is.
    – Phil P
    Commented Mar 10, 2012 at 2:00
  • 2
    No, I'm not confusing blocks with inodes. The inodes are an abstraction sitting above blocks, and the point of this posting was to describe the relationship between the files and directories, and their content: all the filesystem structure comes from the directory files. It was long enough already without getting bogged down in inode implementations! (that said, I could possibly write the first couple of paragraphs more clearly). Also, as you see, I do explicitly state that all of the information about the file (except its name) is in the inode, and not in the directory file. Commented Apr 5, 2012 at 11:32
  • @NormanGray: Even as you defend yourself, you shoot yourself in the foot.  You said, "All the actual content of files in the filesystem is in inodes ...."  That's wrong.  Properties / attributes of a file (e.g, owner, permissions, modification time, etc.) are stored in the inode.  The content of an ordinary file is stored in data blocks.  If you don't want to get bogged down in inode implementations, then don't, but please don't make misleading oversimplifications, either. Commented Jun 29, 2015 at 4:35

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .