cmd/mount2: fix ESTALE over NFS by reporting stable inode numbers

setAttr left attr.Ino unset (0) and the NewInode sites left StableAttr.Ino
unset, so the kernel saw inode 0 while the node identity differed, which
breaks NFS file-handle validation. Set both to the stable VFS inode. The
bazil cmd/mount backend does not hit this because its framework assigns
stable inodes automatically; go-fuse needs them set explicitly.

Before: chmod/chown/truncate on a just-written file through an
NFS-exported mount2 mount failed with ESTALE.
After: they succeed.

Exercising the NFS handle-validation path needs a kernel NFS server, so it
isn't covered by the local vfstest harness; validated against a real Linux
nfs-kernel-server export over NFSv3, NFSv4.0 and NFSv4.2.

#9547

(cherry picked from commit 92b78aefa4)
This commit is contained in:
Sandy Luppino
2026-06-26 12:04:12 -04:00
committed by Nick Craig-Wood
parent e4093ec952
commit b9fd40d182
2 changed files with 5 additions and 4 deletions

View File

@@ -75,6 +75,7 @@ func setAttr(node vfs.Node, attr *fuse.Attr) {
vfs := node.VFS()
attr.Owner.Gid = vfs.Opt.GID
attr.Owner.Uid = vfs.Opt.UID
attr.Ino = node.Inode()
attr.Mode = getMode(node)
attr.Size = Size
attr.Nlink = 1

View File

@@ -201,7 +201,7 @@ func (n *Node) Lookup(ctx context.Context, name string, out *fuse.EntryOut) (ino
// out.SetAttrTimeout(dt time.Duration)
n.fsys.setEntryOut(vfsNode, out)
return n.NewInode(ctx, newNode, fusefs.StableAttr{Mode: out.Attr.Mode}), 0
return n.NewInode(ctx, newNode, fusefs.StableAttr{Mode: out.Attr.Mode, Ino: newNode.node.Inode()}), 0
}
var _ = (fusefs.NodeLookuper)((*Node)(nil))
@@ -337,7 +337,7 @@ func (n *Node) Mkdir(ctx context.Context, name string, mode uint32, out *fuse.En
}
newNode := newNode(n.fsys, newDir)
n.fsys.setEntryOut(newNode.node, out)
newInode := n.NewInode(ctx, newNode, fusefs.StableAttr{Mode: out.Attr.Mode})
newInode := n.NewInode(ctx, newNode, fusefs.StableAttr{Mode: out.Attr.Mode, Ino: newNode.node.Inode()})
return newInode, 0
}
@@ -379,7 +379,7 @@ func (n *Node) Create(ctx context.Context, name string, flags uint32, mode uint3
n.fsys.setEntryOut(vfsNode, out)
newNode := newNode(n.fsys, vfsNode)
fs.Debugf(nil, "attr=%#v", out.Attr)
newInode := n.NewInode(ctx, newNode, fusefs.StableAttr{Mode: out.Attr.Mode})
newInode := n.NewInode(ctx, newNode, fusefs.StableAttr{Mode: out.Attr.Mode, Ino: newNode.node.Inode()})
return newInode, fh, 0, 0
}
@@ -528,7 +528,7 @@ func (n *Node) Symlink(ctx context.Context, target, name string, out *fuse.Entry
n.fsys.setEntryOut(vfsNode, out)
newNode := newNode(n.fsys, vfsNode)
newInode := n.NewInode(ctx, newNode, fusefs.StableAttr{Mode: out.Attr.Mode})
newInode := n.NewInode(ctx, newNode, fusefs.StableAttr{Mode: out.Attr.Mode, Ino: newNode.node.Inode()})
return newInode, 0
}