Women in Technology

Hear us Roar



Article:
  MacFUSE: New Frontiers in File Systems
Subject:   st_nlink is the number of hard links.
Date:   2007-03-14 09:47:49
From:   ralph@inputplus.co.uk
Hi Scott,


"For a directory, the st_nlink field should be set to the total number of entries within the directory."


I think this is incorrect. stat(2) should confirm that st_nlink is the path's number of hard links. For an empty directory, this is normally two; one for . in the empty directory, and one for the directory's entry in the parent.

$ mkdir foo
$ ls -ld foo
drwxr-xr-x 2 ralph ralph 4096 2007-03-14 16:17 foo
$ stat foo | g Links
Device: 301h/769d Inode: 3107002 Links: 2
$ ls -di foo foo/.
3107002 foo 3107002 foo/.
$


For the root directory it is at least three; /, /., and /... Make a directory /foo and /foo/.. will have the same inode number as /, incrementing st_nlink.


Cheers, Ralph.

Full Threads Oldest First

Showing messages 1 through 5 of 5.

  • st_nlink is the number of hard links.
    2007-03-14 23:30:00  osxbook [View]

    Ralph, what Scott wrote is correct.

    Directories don't have hard links on Mac OS X (well, not yet), or on any typical Unix system for that matter. The rationale is that with directory hard links, it would be all too easy to create cycles.

    st_nlink *would* be the number of hard links, but for files. Looks like the man page for stat(2) fails to mention that.

    On Mac OS X, for directories, you can either set st_nlink to the number of objects within the directory ("." and ".." + the rest), or if the volume is such that the number is hard to calculate, you can set st_nlink to 1.

    -A
    • st_nlink is the number of hard links.
      2007-03-15 07:33:36  ralph@inputplus.co.uk [View]

      Hi osxbook,

      "Directories don't have hard links on Mac OS X (well, not yet), or on any typical Unix system for that matter."

      Oh, but they do, at least on Unix as my post above shows. ls -ld foo is showing that foo's inode has a reference count of 2; the second word of output. And ls -di foo foo/. is showing that both paths have the same inode number, 3107002.

      It's true that originally directories could also be hard linked with a system call but this was stopped because confusion could result, including hierarchy cycles. However, the kernel never stopped using hard links on directories, for example to implement .., and if you are writing a filesystem you still set up those hard links when making a directory; I doubt FUSE alters this.

      As for Mac OS X altering this aspect, perhaps it does but I doubt it. Here's a test to do on the native, non-FUSE, shipped by Apple, filesystem.


      $ mkdir foo
      $ cd foo
      $ ls -ld .
      drwxr-xr-x 2 ralph ralph 4096 2007-03-15 14:29 .
      $ touch a b c d e f g h
      $ ls -ld .
      drwxr-xr-x 2 ralph ralph 4096 2007-03-15 14:29 .
      $ mkdir 1 2 3
      $ ls -ld .
      drwxr-xr-x 5 ralph ralph 4096 2007-03-15 14:29 .
      $


      Keep an eye on the second word of ls(1)'s output. On a newly created, empty, directory it should be 2. Creating files in the directory doesn't change this. But creating subdirectories does; the link count is incremented because 1/.., 2/.., and 3/.. are all hard links to foo.

      Cheers, Ralph.
      • st_nlink is the number of hard links.
        2007-03-16 01:35:52  osxbook [View]

        Ralph

        "Oh, but they do,"

        First off, when I say "directories don't have hard links", I mean you can't create a hard link to a directory (through link(2)). You are interpreting "hard link" to be synonymous with the link count of a file or directory.

        "As for Mac OS X altering this aspect, perhaps it does but I doubt it. Here's a test to do on the native, non-FUSE, shipped by Apple, filesystem."

        The native file system on OS X is HFS+ and not UFS. Looks like your example uses UFS, and you also seem to assume that an inode-based "traditional" Unix file system is the only way to do things.

        On UFS, which indeed uses "real" inodes, you see the behavior you mentioned. That's because UFS will cause st_nlink to be set to the inode's link count. A brand new directory's inode's link count is 2. The mkdir vnode operation for UFS increments the parent directory's inode's link count. This is why you see what you're seeing.

        Now, HFS+ uses a bunch of B-Trees: one for the file system's hierarchical structure (the Catalog), another for attributes, another for extents, and so on. It doesn't use inodes the way you're thinking. In fact, what's reported as the inode number isn't really an inode number, but the node number in the Catalog B-Tree. There are no inodes in HFS+. In particular, the "." and ".." entries are "fake" special cases.

        Try your example on HFS+:


        $ cd /tmp/foo
        $ ls -ld .
        drwxr-xr-x 2 user wheel 68 Mar 16 00:55 .
        $ touch a b c d e f g h
        $ ls -ld .
        drwxr-xr-x 10 user wheel 340 Mar 16 00:56 .
        $ mkdir 1 2 3
        $ ls -ld .
        drwxr-xr-x 13 user wheel 442 Mar 16 00:56 .
        $


        As you see, the second word of ls(1)'s output has a different meaning here. It is the number of file system objects within the directory.

        How file hard links are implemented on HFS+ is interesting too, but that's another story.

        Moreover, if a Mac OS X file system tells the kernel that it doesn't support the nlink field, the kernel will substitute a value of 1.

        "for example to implement .., and if you are writing a filesystem you still set up those hard links when making a directory; I doubt FUSE alters this."

        MacFUSE is a file system enabler--not a file system itself (well, technically, it is a file system, just a generic one). Specific file systems are free to report whatever they like for st_nlink.

        It's actually a bit more complicated on Mac OS X, since there is also the concept of file system attributes. In the man page of getattrlist(2), look for ATTR_DIR_LINKCOUNT and ATTR_FILE_LINKCOUNT.

        Hopes this clarifies things.
        • st_nlink is the number of hard links.
          2007-03-19 11:21:06  ralph@inputplus.co.uk [View]

          First off, when I say "directories don't have hard links", I mean you can't create a hard link to a directory (through link(2)). You are interpreting "hard link" to be synonymous with the link count of a file or directory.

          Indeed I am since where I come from, UNIX, they are the same thing, hard links being resposible for incrementing an inode's reference count. It's even in the struct member's name: st_nlink.

          Thanks for pointing out that HFS+ doesn't conform to this model. I wonder if this affects find(1)'s attempt at leaf optimisation, see -noleaf.

          Cheers, Ralph.
          • st_nlink is the number of hard links.
            2007-03-21 14:57:17  osxbook [View]

            "Indeed I am since where I come from, UNIX, they are the same thing" ... doesn't conform to this model.

            I can understand that you might be surprised by the difference in behavior, but you're assuming too much if you think that the UNIX file system style behavior is a "model" that must be conformed to.

            It's more of an implementation detail: incarnations of the UNIX file system and many inode-based file systems do things that way. In fact, it is common enough behavior amongst *nix file systems that POSIX/SUSv3 are subtly ambiguous about st_nlink for directories. Yet, if you read one of these standards carefully enough, it is not required for st_nlink for directories to be what you were expecting it to be.