fix(mount): gate directory nlink counting behind -posix.dirNLink option (#9026)

The directory nlink counting (2 + subdirectory count) requires listing
cached directory entries on every stat, which has a performance cost.
Gate it behind the -posix.dirNLink flag (default: off).

When disabled, directories report nlink=2 (POSIX baseline).
When enabled, directories report nlink=2 + number of subdirectories
from cached entries.
This commit is contained in:
Chris Lu authored and GitHub committed 2026-04-10 16:18:29 -07:00
1 parent 39e76b8e94
commit 8aa5809824
5 files changed
+18 -3

No files matched your search

+6
View File
@@ -55,6 +55,9 @@ type MountOptions struct {
// Distributed lock for cross-mount write coordination
distributedLock *bool
// POSIX compliance options
posixDirNlink *bool
// FUSE performance options
writebackCache *bool
asyncDio *bool
@@ -131,6 +134,9 @@ func init() {
// Distributed lock for cross-mount write coordination
mountOptions.distributedLock = cmdMount.Flag.Bool("dlm", false, "enable distributed lock for cross-mount write coordination (only one mount can write a file at a time)")
// POSIX compliance options
mountOptions.posixDirNlink = cmdMount.Flag.Bool("posix.dirNLink", false, "report POSIX-compliant directory nlink (2 + subdirectory count); costs one directory listing per stat")
// FUSE performance options
mountOptions.writebackCache = cmdMount.Flag.Bool("writebackCache", false, "enable FUSE writeback cache for improved write performance (at risk of data loss on crash)")
mountOptions.asyncDio = cmdMount.Flag.Bool("asyncDio", false, "enable async direct I/O for better concurrency")
+1
View File
@@ -354,6 +354,7 @@ func RunMount(option *MountOptions, umask os.FileMode) bool {
DirIdleEvictSec: *option.dirIdleEvictSec,
EnableDistributedLock: option.distributedLock != nil && *option.distributedLock,
WritebackCache: option.writebackCache != nil && *option.writebackCache,
PosixDirNlink: option.posixDirNlink != nil && *option.posixDirNlink,
})
// create mount root
+6
View File
@@ -93,6 +93,12 @@ type Option struct {
// When true, Flush() returns immediately and data upload + metadata flush happen in background.
WritebackCache bool
// PosixDirNlink enables POSIX-compliant directory nlink counting
// (nlink = 2 + number_of_subdirectories). This requires listing
// cached directory entries on every stat, which has a performance cost.
// When false (default), directories report nlink=2.
PosixDirNlink bool
uniqueCacheDirForRead string
uniqueCacheDirForWrite string
}
+4 -2
View File
@@ -17,7 +17,9 @@ func (wfs *WFS) GetAttr(cancel <-chan struct{}, input *fuse.GetAttrIn, out *fuse
glog.V(4).Infof("GetAttr %v", input.NodeId)
if input.NodeId == 1 {
wfs.setRootAttr(out)
wfs.applyDirNlink(&out.Attr, util.FullPath(wfs.option.FilerMountRootPath))
if wfs.option.PosixDirNlink {
wfs.applyDirNlink(&out.Attr, util.FullPath(wfs.option.FilerMountRootPath))
}
return fuse.OK
}
@@ -27,7 +29,7 @@ func (wfs *WFS) GetAttr(cancel <-chan struct{}, input *fuse.GetAttrIn, out *fuse
out.AttrValid = 1
wfs.setAttrByPbEntry(&out.Attr, inode, entry, true)
wfs.applyInMemoryAtime(&out.Attr, inode)
if entry.IsDirectory {
if entry.IsDirectory && wfs.option.PosixDirNlink {
wfs.applyDirNlink(&out.Attr, path)
}
return status
+1 -1
View File
@@ -44,7 +44,7 @@ func (wfs *WFS) Lookup(cancel <-chan struct{}, header *fuse.InHeader, name strin
wfs.outputFilerEntry(out, inode, localEntry)
if localEntry.IsDirectory() {
if localEntry.IsDirectory() && wfs.option.PosixDirNlink {
wfs.applyDirNlink(&out.Attr, fullFilePath)
}