diff --git a/internal/fusefrontend/root_node.go b/internal/fusefrontend/root_node.go index 39cdef7d..ac814adf 100644 --- a/internal/fusefrontend/root_node.go +++ b/internal/fusefrontend/root_node.go @@ -59,13 +59,16 @@ type RootNode struct { // quirks is a bitmap that enables workaround for quirks in the filesystem // backing the cipherdir quirks uint64 + // rootIno is the inode number that we report for the root node on mount + rootIno uint64 } func NewRootNode(args Args, c *contentenc.ContentEnc, n *nametransform.NameTransform) *RootNode { var rootDev uint64 var st syscall.Stat_t - if err := syscall.Stat(args.Cipherdir, &st); err != nil { - tlog.Warn.Printf("Could not stat backing directory %q: %v", args.Cipherdir, err) + var statErr error + if statErr = syscall.Stat(args.Cipherdir, &st); statErr != nil { + tlog.Warn.Printf("Could not stat backing directory %q: %v", args.Cipherdir, statErr) } else { rootDev = uint64(st.Dev) } @@ -87,6 +90,10 @@ func NewRootNode(args Args, c *contentenc.ContentEnc, n *nametransform.NameTrans dirCache: dirCache{ivLen: ivLen}, quirks: syscallcompat.DetectQuirks(args.Cipherdir), } + if statErr == nil { + rn.inoMap.TranslateStat(&st) + rn.rootIno = st.Ino + } return rn } @@ -288,3 +295,7 @@ func (rn *RootNode) decryptXattrName(cAttr string) (attr string, err error) { } return attr, nil } + +func (rn *RootNode) RootIno() uint64 { + return rn.rootIno +} diff --git a/internal/fusefrontend_reverse/root_node.go b/internal/fusefrontend_reverse/root_node.go index 9de81b50..cb04151d 100644 --- a/internal/fusefrontend_reverse/root_node.go +++ b/internal/fusefrontend_reverse/root_node.go @@ -51,6 +51,8 @@ type RootNode struct { // bizarre problems when inode numbers are reused behind our back, // like this one: https://github.com/rfjakob/gocryptfs/issues/802 gen uint64 + // rootIno is the inode number that we report for the root node on mount + rootIno uint64 } // NewRootNode returns an encrypted FUSE overlay filesystem. @@ -59,9 +61,10 @@ type RootNode struct { func NewRootNode(args fusefrontend.Args, c *contentenc.ContentEnc, n *nametransform.NameTransform) *RootNode { var rootDev uint64 var st syscall.Stat_t + var statErr error var shortNameMax int - if err := syscall.Stat(args.Cipherdir, &st); err != nil { - tlog.Warn.Printf("Could not stat backing directory %q: %v", args.Cipherdir, err) + if statErr = syscall.Stat(args.Cipherdir, &st); statErr != nil { + tlog.Warn.Printf("Could not stat backing directory %q: %v", args.Cipherdir, statErr) if args.OneFileSystem { tlog.Fatal.Printf("This is a fatal error in combination with -one-file-system") os.Exit(exitcodes.CipherDir) @@ -81,6 +84,10 @@ func NewRootNode(args fusefrontend.Args, c *contentenc.ContentEnc, n *nametransf rootDev: rootDev, shortNameMax: shortNameMax, } + if statErr == nil { + rn.inoMap.TranslateStat(&st) + rn.rootIno = st.Ino + } if len(args.Exclude) > 0 || len(args.ExcludeWildcard) > 0 || len(args.ExcludeFrom) > 0 { rn.excluder = prepareExcluder(args) } @@ -171,3 +178,7 @@ func (rn *RootNode) uniqueStableAttr(mode uint32, ino uint64) fs.StableAttr { Gen: atomic.AddUint64(&rn.gen, 1), } } + +func (rn *RootNode) RootIno() uint64 { + return rn.rootIno +} diff --git a/mount.go b/mount.go index 61079a94..0eaa3dd9 100644 --- a/mount.go +++ b/mount.go @@ -351,6 +351,10 @@ func initFuseFrontend(args *argContainer) (rootNode fs.InodeEmbedder, wipeKeys f return rootNode, func() { cCore.Wipe() } } +type RootInoer interface { + RootIno() uint64 +} + // initGoFuse calls into go-fuse to mount `rootNode` on `args.mountpoint`. // The mountpoint is ready to use when the functions returns. // On error, it calls os.Exit and does not return. @@ -375,6 +379,9 @@ func initGoFuse(rootNode fs.InodeEmbedder, args *argContainer) *fuse.Server { } } fuseOpts.NullPermissions = true + // The inode number for the root node must be manually set on mount + // https://github.com/hanwen/go-fuse/issues/399 + fuseOpts.RootStableAttr = &fs.StableAttr{Ino: rootNode.(RootInoer).RootIno()} // Enable go-fuse warnings fuseOpts.Logger = log.New(os.Stderr, "go-fuse: ", log.Lmicroseconds) fuseOpts.MountOptions = fuse.MountOptions{ diff --git a/tests/matrix/matrix_test.go b/tests/matrix/matrix_test.go index 0d9c22cc..417e126c 100644 --- a/tests/matrix/matrix_test.go +++ b/tests/matrix/matrix_test.go @@ -972,3 +972,13 @@ func TestPwd(t *testing.T) { os.Mkdir(dir, 0700) } } + +// TestRootIno checks that inode number of the root dir is set +// https://github.com/hanwen/go-fuse/issues/399 +func TestRootIno(t *testing.T) { + var st syscall.Stat_t + syscall.Stat(test_helpers.DefaultPlainDir, &st) + if st.Ino == 0 { + t.Errorf("inode number of root dir is zero") + } +} diff --git a/tests/plaintextnames/plaintextnames_test.go b/tests/plaintextnames/plaintextnames_test.go index 8892c393..4de07307 100644 --- a/tests/plaintextnames/plaintextnames_test.go +++ b/tests/plaintextnames/plaintextnames_test.go @@ -125,3 +125,13 @@ func TestInoReuseEvil(t *testing.T) { t.Logf("file ino = %d", st.Ino) } } + +// TestRootIno checks that inode number of the root dir is set +// https://github.com/hanwen/go-fuse/issues/399 +func TestRootIno(t *testing.T) { + var st syscall.Stat_t + syscall.Stat(cDir, &st) + if st.Ino == 0 { + t.Errorf("inode number of root dir is zero") + } +}