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 { ...@@ -30,6 +30,11 @@ type LoopbackRoot struct {
// to a LOOKUP/CREATE/MKDIR/MKNOD opcode. If not set, use a // to a LOOKUP/CREATE/MKDIR/MKNOD opcode. If not set, use a
// LoopbackNode. // LoopbackNode.
NewNode func(rootData *LoopbackRoot, parent *Inode, name string, st *syscall.Stat_t) InodeEmbedder 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 { 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. ...@@ -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 // path returns the full path to the file in the underlying file
// system. // 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 { func (n *LoopbackNode) path() string {
path := n.Path(n.Root()) path := n.Path(n.root())
return filepath.Join(n.RootData.Path, path) return filepath.Join(n.RootData.Path, path)
} }
...@@ -239,16 +255,15 @@ func (n *LoopbackNode) renameExchange(name string, newparent InodeEmbedder, newN ...@@ -239,16 +255,15 @@ func (n *LoopbackNode) renameExchange(name string, newparent InodeEmbedder, newN
} }
// Double check that nodes didn't change from under us. // Double check that nodes didn't change from under us.
inode := &n.Inode if n.root() != n.EmbeddedInode() && n.Inode.StableAttr().Ino != n.RootData.idFromStat(&st).Ino {
if inode.Root() != inode && inode.StableAttr().Ino != n.RootData.idFromStat(&st).Ino {
return syscall.EBUSY return syscall.EBUSY
} }
if err := syscall.Fstat(fd2, &st); err != nil { if err := syscall.Fstat(fd2, &st); err != nil {
return ToErrno(err) return ToErrno(err)
} }
newinode := newparent.EmbeddedInode() newinode, ok := newparent.(*LoopbackNode)
if newinode.Root() != newinode && newinode.StableAttr().Ino != n.RootData.idFromStat(&st).Ino { if (!ok || newinode.root() != newparent.EmbeddedInode()) && newinode.StableAttr().Ino != n.RootData.idFromStat(&st).Ino {
return syscall.EBUSY return syscall.EBUSY
} }
...@@ -500,5 +515,7 @@ func NewLoopbackRoot(rootPath string) (InodeEmbedder, error) { ...@@ -500,5 +515,7 @@ func NewLoopbackRoot(rootPath string) (InodeEmbedder, error) {
Dev: uint64(st.Dev), 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 ...@@ -2,12 +2,14 @@ package fs
import ( import (
"bytes" "bytes"
"context"
"fmt" "fmt"
"os" "os"
"reflect" "reflect"
"syscall" "syscall"
"testing" "testing"
"github.com/hanwen/go-fuse/v2/fuse"
"github.com/hanwen/go-fuse/v2/internal/renameat" "github.com/hanwen/go-fuse/v2/internal/renameat"
"github.com/kylelemons/godebug/pretty" "github.com/kylelemons/godebug/pretty"
"golang.org/x/sys/unix" "golang.org/x/sys/unix"
...@@ -147,3 +149,31 @@ func TestXAttr(t *testing.T) { ...@@ -147,3 +149,31 @@ func TestXAttr(t *testing.T) {
t.Fatalf("got %v want ENOATTR", err) 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