Commit 612c80e9 authored by Han-Wen Nienhuys's avatar Han-Wen Nienhuys

fs: allow setting root Loopback root Inode

This allows mounting a loopback file system somewhere other than the
root of the FUSE mount.

Addresses https://github.com/hanwen/go-fuse/issues/526
and https://github.com/hanwen/go-fuse/issues/503.

Change-Id: I38295087d50bf120e5d279eb8b913c2eb308c918
parent 1a7d98b0
......@@ -30,6 +30,11 @@ type LoopbackRoot struct {
// to a LOOKUP/CREATE/MKDIR/MKNOD opcode. If not set, use a
// LoopbackNode.
NewNode func(rootData *LoopbackRoot, parent *Inode, name string, st *syscall.Stat_t) InodeEmbedder
// RootNode is the root of the Loopback. This must be set if
// the Loopback file system is not the root of the FUSE
// mount. It is set automatically by NewLoopbackRoot.
RootNode InodeEmbedder
}
func (r *LoopbackRoot) newNode(parent *Inode, name string, st *syscall.Stat_t) InodeEmbedder {
......@@ -85,8 +90,19 @@ func (n *LoopbackNode) Statfs(ctx context.Context, out *fuse.StatfsOut) syscall.
// path returns the full path to the file in the underlying file
// system.
func (n *LoopbackNode) root() *Inode {
var rootNode *Inode
if n.RootData.RootNode != nil {
rootNode = n.RootData.RootNode.EmbeddedInode()
} else {
rootNode = n.Root()
}
return rootNode
}
func (n *LoopbackNode) path() string {
path := n.Path(n.Root())
path := n.Path(n.root())
return filepath.Join(n.RootData.Path, path)
}
......@@ -239,16 +255,15 @@ func (n *LoopbackNode) renameExchange(name string, newparent InodeEmbedder, newN
}
// Double check that nodes didn't change from under us.
inode := &n.Inode
if inode.Root() != inode && inode.StableAttr().Ino != n.RootData.idFromStat(&st).Ino {
if n.root() != n.EmbeddedInode() && n.Inode.StableAttr().Ino != n.RootData.idFromStat(&st).Ino {
return syscall.EBUSY
}
if err := syscall.Fstat(fd2, &st); err != nil {
return ToErrno(err)
}
newinode := newparent.EmbeddedInode()
if newinode.Root() != newinode && newinode.StableAttr().Ino != n.RootData.idFromStat(&st).Ino {
newinode, ok := newparent.(*LoopbackNode)
if (!ok || newinode.root() != newparent.EmbeddedInode()) && newinode.StableAttr().Ino != n.RootData.idFromStat(&st).Ino {
return syscall.EBUSY
}
......@@ -500,5 +515,7 @@ func NewLoopbackRoot(rootPath string) (InodeEmbedder, error) {
Dev: uint64(st.Dev),
}
return root.newNode(nil, "", &st), nil
rootNode := root.newNode(nil, "", &st)
root.RootNode = rootNode
return rootNode, nil
}
......@@ -2,12 +2,14 @@ package fs
import (
"bytes"
"context"
"fmt"
"os"
"reflect"
"syscall"
"testing"
"github.com/hanwen/go-fuse/v2/fuse"
"github.com/hanwen/go-fuse/v2/internal/renameat"
"github.com/kylelemons/godebug/pretty"
"golang.org/x/sys/unix"
......@@ -147,3 +149,31 @@ func TestXAttr(t *testing.T) {
t.Fatalf("got %v want ENOATTR", err)
}
}
func TestLoopbackNonRoot(t *testing.T) {
backing := t.TempDir()
content := []byte("hello")
if err := os.WriteFile(backing+"/file.txt", content, 0666); err != nil {
t.Fatal(err)
}
root := &Inode{}
mnt, _ := testMount(t, root, &Options{
OnAdd: func(ctx context.Context) {
lnode, err := NewLoopbackRoot(backing)
if err != nil {
return
}
sub := root.NewPersistentInode(ctx, lnode, StableAttr{Mode: fuse.S_IFDIR})
root.AddChild("sub", sub, true)
},
})
fi, err := os.Lstat(mnt + "/sub/file.txt")
if err != nil {
t.Fatal(err)
}
if fi.Size() != int64(len(content)) {
t.Errorf("got %d bytes, want %d", fi.Size(), len(content))
}
}
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