Commit 96be8078 authored by Han-Wen Nienhuys's avatar Han-Wen Nienhuys

nodefs: mask out root device number

This makes the inodes in loopback reflect the underlying inodes if
they don't span mounts
parent 18e0e322
......@@ -37,9 +37,9 @@ type FileID struct {
Gen uint64
}
// Zero returns if the FileID is zeroed out
// Reserved returns if the FileID is using reserved Inode numbers.
func (i *FileID) Reserved() bool {
return i.Ino == 0 || i.Ino == 1 || i.Ino == ^uint64(0)
return i.Ino == 1 || i.Ino == ^uint64(0)
}
// Inode is a node in VFS tree. Inodes are one-to-one mapped to
......
......@@ -18,7 +18,8 @@ import (
type loopbackRoot struct {
loopbackNode
root string
root string
rootDev uint64
}
func (n *loopbackRoot) newLoopbackNode() *loopbackNode {
......@@ -70,7 +71,7 @@ func (n *loopbackNode) Lookup(ctx context.Context, name string, out *fuse.EntryO
out.Attr.FromStat(&st)
node := n.rootNode.newLoopbackNode()
ch := n.inode().NewInode(node, out.Attr.Mode, idFromStat(&st))
ch := n.inode().NewInode(node, out.Attr.Mode, n.rootNode.idFromStat(&st))
return ch, fuse.OK
}
......@@ -89,7 +90,7 @@ func (n *loopbackNode) Mknod(ctx context.Context, name string, mode, rdev uint32
out.Attr.FromStat(&st)
node := n.rootNode.newLoopbackNode()
ch := n.inode().NewInode(node, out.Attr.Mode, idFromStat(&st))
ch := n.inode().NewInode(node, out.Attr.Mode, n.rootNode.idFromStat(&st))
return ch, fuse.OK
}
......@@ -109,7 +110,7 @@ func (n *loopbackNode) Mkdir(ctx context.Context, name string, mode uint32, out
out.Attr.FromStat(&st)
node := n.rootNode.newLoopbackNode()
ch := n.inode().NewInode(node, out.Attr.Mode, idFromStat(&st))
ch := n.inode().NewInode(node, out.Attr.Mode, n.rootNode.idFromStat(&st))
return ch, fuse.OK
}
......@@ -146,12 +147,21 @@ func (n *loopbackNode) Rename(ctx context.Context, name string, newParent Operat
return fuse.ToStatus(err)
}
func idFromStat(st *syscall.Stat_t) FileID {
func (r *loopbackRoot) idFromStat(st *syscall.Stat_t) FileID {
// We compose an inode number by the underlying inode, and
// mixing in the device number. In traditional filesystems,
// the inode numbers are small. The device numbers are also
// small (typically 16 bit). Finally, we mask out the root
// device number of the root, so a loopback FS that does not
// encompass multiple mounts will reflect the inode numbers of
// the underlying filesystem
swapped := (st.Dev << 32) | (st.Dev >> 32)
swappedRootDev := (r.rootDev << 32) | (r.rootDev >> 32)
return FileID{
Gen: 1,
// This should work well for traditional backing FSes,
// not so much for other go-fuse FS-es
Ino: uint64(st.Dev)<<32 ^ st.Ino,
Ino: (swapped ^ swappedRootDev) ^ st.Ino,
}
}
......@@ -170,7 +180,7 @@ func (n *loopbackNode) Create(ctx context.Context, name string, flags uint32, mo
}
node := n.rootNode.newLoopbackNode()
ch := n.inode().NewInode(node, uint32(st.Mode), idFromStat(&st))
ch := n.inode().NewInode(node, uint32(st.Mode), n.rootNode.idFromStat(&st))
lf := NewLoopbackFile(fd)
return ch, lf, 0, fuse.OK
}
......@@ -187,7 +197,7 @@ func (n *loopbackNode) Symlink(ctx context.Context, target, name string, out *fu
return nil, fuse.ToStatus(err)
}
node := n.rootNode.newLoopbackNode()
ch := n.inode().NewInode(node, uint32(st.Mode), idFromStat(&st))
ch := n.inode().NewInode(node, uint32(st.Mode), n.rootNode.idFromStat(&st))
out.Attr.FromStat(&st)
return ch, fuse.OK
......@@ -207,7 +217,7 @@ func (n *loopbackNode) Link(ctx context.Context, target Operations, name string,
return nil, fuse.ToStatus(err)
}
node := n.rootNode.newLoopbackNode()
ch := n.inode().NewInode(node, uint32(st.Mode), idFromStat(&st))
ch := n.inode().NewInode(node, uint32(st.Mode), n.rootNode.idFromStat(&st))
out.Attr.FromStat(&st)
return ch, fuse.OK
......@@ -271,12 +281,19 @@ func (n *loopbackNode) FGetAttr(ctx context.Context, f FileHandle, out *fuse.Att
// NewLoopback returns a root node for a loopback file system whose
// root is at the given root.
func NewLoopback(root string) DirOperations {
func NewLoopback(root string) (DirOperations, error) {
var st syscall.Stat_t
err := syscall.Stat(root, &st)
if err != nil {
return nil, err
}
n := &loopbackRoot{
root: root,
root: root,
rootDev: st.Dev,
}
n.rootNode = n
return n
return n, nil
}
func (n *loopbackNode) Access(ctx context.Context, mask uint32) fuse.Status {
......
......@@ -48,13 +48,13 @@ func (n *loopbackNode) renameExchange(name string, newparent *loopbackNode, newN
if err := syscall.Fstat(fd1, &st); err != nil {
return fuse.ToStatus(err)
}
if !InodeOf(n).IsRoot() && InodeOf(n).FileID().Ino != idFromStat(&st).Ino {
if !InodeOf(n).IsRoot() && InodeOf(n).FileID().Ino != n.rootNode.idFromStat(&st).Ino {
return fuse.EBUSY
}
if err := syscall.Fstat(fd2, &st); err != nil {
return fuse.ToStatus(err)
}
if !InodeOf(newparent).IsRoot() && InodeOf(newparent).FileID().Ino != idFromStat(&st).Ino {
if !InodeOf(newparent).IsRoot() && InodeOf(newparent).FileID().Ino != n.rootNode.idFromStat(&st).Ino {
return fuse.EBUSY
}
......
......@@ -68,8 +68,11 @@ func newTestCase(t *testing.T, entryCache bool, attrCache bool) *testCase {
t.Fatal(err)
}
tc.loopback = NewLoopback(tc.origDir)
_ = time.Second
var err error
tc.loopback, err = NewLoopback(tc.origDir)
if err != nil {
t.Fatalf("NewLoopback: %v", err)
}
oneSec := time.Second
......@@ -87,7 +90,6 @@ func newTestCase(t *testing.T, entryCache bool, attrCache bool) *testCase {
AttrTimeout: attrDT,
})
var err error
tc.server, err = fuse.NewServer(tc.rawFS, tc.mntDir,
&fuse.MountOptions{
Debug: testutil.VerboseTest(),
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment