Commit 7373951b authored by Han-Wen Nienhuys's avatar Han-Wen Nienhuys

nodefs: rename2 exchange support in loopback

parent 5a5aec24
......@@ -85,6 +85,14 @@ type Inode struct {
parents map[parentData]struct{}
}
func (n *Inode) FileID() FileID {
return n.nodeID
}
func (n *Inode) IsRoot() bool {
return n.nodeID.Ino == fuse.FUSE_ROOT_ID
}
// debugString is used for debugging. Racy.
func (n *Inode) debugString() string {
var ss []string
......
......@@ -13,6 +13,7 @@ import (
"github.com/hanwen/go-fuse/fuse"
"github.com/hanwen/go-fuse/internal"
"golang.org/x/sys/unix"
)
type loopbackRoot struct {
......@@ -150,14 +151,42 @@ func toLoopbackNode(op Operations) *loopbackNode {
return op.(*loopbackNode)
}
func (n *loopbackNode) Rename(ctx context.Context, name string, newParent Operations, newName string, flags uint32) fuse.Status {
func (n *loopbackNode) renameExchange(name string, newparent *loopbackNode, newName string) fuse.Status {
fd1, err := syscall.Open(n.path(), syscall.O_DIRECTORY, 0)
if err != nil {
return fuse.ToStatus(err)
}
defer syscall.Close(fd1)
fd2, err := syscall.Open(newparent.path(), syscall.O_DIRECTORY, 0)
defer syscall.Close(fd2)
if err != nil {
return fuse.ToStatus(err)
}
var st syscall.Stat_t
if err := syscall.Fstat(fd1, &st); err != nil {
return fuse.ToStatus(err)
}
if !InodeOf(n).IsRoot() && InodeOf(n).FileID().Ino != 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 {
return fuse.EBUSY
}
if flags != 0 {
return fuse.ENOSYS
return fuse.ToStatus(unix.Renameat2(fd1, name, fd2, newName, unix.RENAME_EXCHANGE))
}
func (n *loopbackNode) Rename(ctx context.Context, name string, newParent Operations, newName string, flags uint32) fuse.Status {
newParentLoopback := toLoopbackNode(newParent)
if flags&unix.RENAME_EXCHANGE != 0 {
return n.renameExchange(name, newParentLoopback, newName)
}
p1 := filepath.Join(n.path(), name)
newParentLoopback := toLoopbackNode(newParent)
p2 := filepath.Join(newParentLoopback.path(), newName)
err := os.Rename(p1, p2)
......
......@@ -22,6 +22,7 @@ import (
"github.com/hanwen/go-fuse/fuse"
"github.com/hanwen/go-fuse/internal/testutil"
"github.com/kylelemons/godebug/pretty"
)
var _ = log.Println
......@@ -320,11 +321,62 @@ func TestRenameNoOverwrite(t *testing.T) {
} else if err != syscall.EEXIST {
t.Errorf("got %v (%T) want EEXIST", err, err)
}
}
func TestRenameExchange(t *testing.T) {
tc := newTestCase(t)
defer tc.Clean()
if err := os.Mkdir(tc.origDir+"/dir", 0755); err != nil {
t.Fatalf("Mkdir: %v", err)
}
tc.writeOrig("file", "hello", 0644)
tc.writeOrig("dir/file", "x", 0644)
if err := unix.Renameat2(f1, "file", f2, "file", unix.RENAME_EXCHANGE); err == nil {
t.Errorf("rename EXCHANGE succeeded")
} else if err != syscall.EINVAL {
t.Errorf("got %v (%T) want %v (%T)", err, err, syscall.EINVAL, syscall.EINVAL)
f1, err := syscall.Open(tc.mntDir+"/", syscall.O_DIRECTORY, 0)
if err != nil {
t.Fatalf("open 1: %v", err)
}
defer syscall.Close(f1)
f2, err := syscall.Open(tc.mntDir+"/dir", syscall.O_DIRECTORY, 0)
if err != nil {
t.Fatalf("open 2: %v", err)
}
defer syscall.Close(f2)
var before1, before2 unix.Stat_t
if err := unix.Fstatat(f1, "file", &before1, 0); err != nil {
t.Fatalf("Fstatat: %v", err)
}
if err := unix.Fstatat(f2, "file", &before2, 0); err != nil {
t.Fatalf("Fstatat: %v", err)
}
if err := unix.Renameat2(f1, "file", f2, "file", unix.RENAME_EXCHANGE); err != nil {
t.Errorf("rename EXCHANGE: %v", err)
}
var after1, after2 unix.Stat_t
if err := unix.Fstatat(f1, "file", &after1, 0); err != nil {
t.Fatalf("Fstatat: %v", err)
}
if err := unix.Fstatat(f2, "file", &after2, 0); err != nil {
t.Fatalf("Fstatat: %v", err)
}
clearCtime := func(s *unix.Stat_t) {
s.Ctim.Sec = 0
s.Ctim.Nsec = 0
}
clearCtime(&after1)
clearCtime(&after2)
clearCtime(&before2)
clearCtime(&before1)
if diff := pretty.Compare(after1, before2); diff != "" {
t.Errorf("after1, before2: %s", diff)
}
if !reflect.DeepEqual(after2, before1) {
t.Errorf("after2, before1: %#v, %#v", after2, before1)
}
}
......
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