A while back I wrote about how in POSIX you could theoretically use inode (number) zero . Not all Unixes consider inode zero to be valid; prominently, OpenBSD's getdents(2) doesn't return valid entries with an inode number of 0, and by extension, OpenBSD's filesystems won't have anything that uses inode zero. However, Linux is a different beast.
Recently, I saw a Go commit message with the interesting description of:
os: allow direntries to have zero inodes on Linux
Some Linux filesystems have been known to return valid entries with zero inodes. This new behavior also puts Go in agreement with recent glibc.
This fixes issue #76428 , and the issue has a simple reproduction to create something with inode numbers of zero. According to the bug report:
[...] On a Linux system with libfuse 3.17.1 or later, you can do this easily with GVFS:
# Create many dir entries (cd big && printf '%04x ' {0..1023} | xargs mkdir -p) gio mount sftp://localhost/$PWD/big
The resulting filesystem mount is in /run/user/$UID/gvfs (see the issue for the exact long path) and can be experimentally verified to have entries with inode numbers of zero (well, as reported by reading the directory). On systems using glibc 2.37 and later, you can look at this directory with 'ls' and see the zero inode numbers.
(Interested parties can try their favorite non-C or non-glibc bindings to see if those environments correctly handle this case.)
That this requires glibc 2.37 is due to this glibc bug , first opened in 2010 (but rejected at the time for reasons you can read in the glibc bug) and then resurfaced in 2016 and eventually fixed in 2022 (and then again in 2024 for the thread safe version of readdir). The 2016 glibc issue has a bit of a discussion about the kernel side. As covered in the Go issue, libfuse returning a zero inode number may be a bug itself , but there are (many) versions of libfuse out in the wild that actually do this today.
Of course, libfuse (and gvfs) may not be the only Linux filesystems and filesystem environments that can create this effect. I believe there are alternate language bindings and APIs for the kernel FUSE ( also , also ) support, so they might have the same bug as libfuse does.
(Both Go and Rust have at least one native binding to the kernel FUSE driver. I haven't looked at either to see what they do about inode numbers.)
PS: My understanding of the Linux (kernel) situation is that if you have something inside the kernel that needs an inode number and you ask the kernel to give you one (through get_next_ino(), an internal function for this), the kernel will carefully avoid giving you inode number 0. A lot of things get inode numbers this way, so this makes life easier for everyone. However, a filesystem can decide on inode numbers itself, and when it does it can use inode number 0 (either explicitly or by zeroing out the d_ino field in the getdents(2) dirent structs that it returns, which I believe is what's happening in the libfuse situation).