fix: default nlink=2 for directories to fix macOS NFS ReadDir#147
Open
teleclawd wants to merge 1 commit intowillscott:masterfrom
Open
fix: default nlink=2 for directories to fix macOS NFS ReadDir#147teleclawd wants to merge 1 commit intowillscott:masterfrom
teleclawd wants to merge 1 commit intowillscott:masterfrom
Conversation
macOS NFS client silently skips ReadDir when a directory has nlink=1, interpreting it as an empty directory. This breaks any virtual filesystem where file.GetInfo() returns nil (i.e., no real inode data is available to populate nlink from syscall.Stat_t). The NFS spec (RFC 1813 §3.3.1) defines nlink as the number of hard links to the object. For directories, this should be at minimum 2 (the directory itself + its '..' entry). A value of 1 is semantically incorrect for directories and causes macOS's NFS client to optimize away the ReadDir call entirely. Fix: default nlink to 2 for directories (1 for files) before checking GetInfo. Real filesystems with actual inode data are unaffected since GetInfo overwrites the default. Affected projects: any go-nfs consumer using billy.Filesystem with virtual/in-memory FileInfo (e.g., TigerFS on macOS).
c6ec595 to
83cbf97
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
macOS NFS client silently skips
ReadDirwhen a directory reportsnlink=1, interpreting it as having no subdirectories. This breaks any go-nfs consumer that uses a virtual billy.Filesystem wherefile.GetInfo()returns nil (no real inode/syscall.Stat_t data).When
GetInforeturns nil,ToFileAttributedefaultsNlinkto 1 for everything — including directories. On Linux this works fine (the NFS client calls ReadDir regardless). On macOS, it silently produces empty directory listings or hangs.nfsstat -cconfirms: 255 Getattr calls, 321 Lookups, 0 Readdir, 0 ReaddirPlus — macOS never even attempts to list directory contents.Root Cause
ToFileAttribute()infile.gounconditionally setsf.Nlink = 1before checkingGetInfo. Virtual filesystems don't have real stat data, soGetInforeturns nil and the default of 1 persists for directories.POSIX convention is that directories always have nlink ≥ 2 (
.self-link + parent entry). macOS NFS client uses this as an optimization hint — nlink=1 on a directory signals "no subdirectories, skip ReadDir." While the NFS spec (RFC 1813) doesn't mandate a minimum nlink for directories, returning 1 violates the POSIX convention that macOS depends on.Fix
Default
nlinkto 2 for directories, 1 for files, before theGetInfocheck. Real filesystems with actual inode data are completely unaffected sinceGetInfooverwrites the default when it succeeds.One-line semantic change, no behavioral change for real filesystems.
Testing
go test ./...)nfsstat -cconfirms Readdir calls resume after fix