Commit e5194406 authored by Han-Wen Nienhuys's avatar Han-Wen Nienhuys

nodefs: Rename2 support, kinda.

parent 89a1a0c7
...@@ -91,7 +91,7 @@ type Node interface { ...@@ -91,7 +91,7 @@ type Node interface {
Mknod(ctx context.Context, name string, mode uint32, dev uint32, out *fuse.EntryOut) (*Inode, fuse.Status) Mknod(ctx context.Context, name string, mode uint32, dev uint32, out *fuse.EntryOut) (*Inode, fuse.Status)
Rmdir(ctx context.Context, name string) fuse.Status Rmdir(ctx context.Context, name string) fuse.Status
Unlink(ctx context.Context, name string) fuse.Status Unlink(ctx context.Context, name string) fuse.Status
Rename(ctx context.Context, name string, newParent Node, newName string) fuse.Status Rename(ctx context.Context, name string, newParent Node, newName string, flags uint32) fuse.Status
Open(ctx context.Context, flags uint32) (fh File, fuseFlags uint32, code fuse.Status) Open(ctx context.Context, flags uint32) (fh File, fuseFlags uint32, code fuse.Status)
......
...@@ -11,6 +11,7 @@ import ( ...@@ -11,6 +11,7 @@ import (
"time" "time"
"github.com/hanwen/go-fuse/fuse" "github.com/hanwen/go-fuse/fuse"
"golang.org/x/sys/unix"
) )
type mapEntry struct { type mapEntry struct {
...@@ -142,7 +143,7 @@ func (b *rawBridge) Lookup(header *fuse.InHeader, name string, out *fuse.EntryOu ...@@ -142,7 +143,7 @@ func (b *rawBridge) Lookup(header *fuse.InHeader, name string, out *fuse.EntryOu
return code return code
} }
b.addNewChild(parent, name, child, out) b.addNewChild(parent, name, child, nil, out)
b.setEntryOutTimeout(out) b.setEntryOutTimeout(out)
return fuse.OK return fuse.OK
} }
...@@ -178,7 +179,7 @@ func (b *rawBridge) Mkdir(input *fuse.MkdirIn, name string, out *fuse.EntryOut) ...@@ -178,7 +179,7 @@ func (b *rawBridge) Mkdir(input *fuse.MkdirIn, name string, out *fuse.EntryOut)
log.Panicf("Mkdir: mode must be S_IFDIR (%o), got %o", fuse.S_IFDIR, out.Attr.Mode) log.Panicf("Mkdir: mode must be S_IFDIR (%o), got %o", fuse.S_IFDIR, out.Attr.Mode)
} }
b.addNewChild(parent, name, child, out) b.addNewChild(parent, name, child, nil, out)
b.setEntryOutTimeout(out) b.setEntryOutTimeout(out)
return fuse.OK return fuse.OK
} }
...@@ -191,18 +192,26 @@ func (b *rawBridge) Mknod(input *fuse.MknodIn, name string, out *fuse.EntryOut) ...@@ -191,18 +192,26 @@ func (b *rawBridge) Mknod(input *fuse.MknodIn, name string, out *fuse.EntryOut)
return code return code
} }
b.addNewChild(parent, name, child, out) b.addNewChild(parent, name, child, nil, out)
b.setEntryOutTimeout(out) b.setEntryOutTimeout(out)
return fuse.OK return fuse.OK
} }
func (b *rawBridge) addNewChild(parent *Inode, name string, child *Inode, out *fuse.EntryOut) { // addNewChild inserts the child into the tree. Returns file handle if file != nil.
func (b *rawBridge) addNewChild(parent *Inode, name string, child *Inode, file File, out *fuse.EntryOut) uint64 {
lockNodes(parent, child) lockNodes(parent, child)
parent.setEntry(name, child) parent.setEntry(name, child)
b.mu.Lock() b.mu.Lock()
child.lookupCount++
if child.nodeID == 0 { if child.nodeID == 0 {
b.registerInode(child) b.registerInode(child)
} }
var fh uint64
if file != nil {
fh = b.registerFile(file)
}
out.NodeId = child.nodeID out.NodeId = child.nodeID
// NOSUBMIT - or should let FS expose Attr.Ino? This makes // NOSUBMIT - or should let FS expose Attr.Ino? This makes
// testing semantics hard though, because os.Lstat doesn't // testing semantics hard though, because os.Lstat doesn't
...@@ -211,6 +220,7 @@ func (b *rawBridge) addNewChild(parent *Inode, name string, child *Inode, out *f ...@@ -211,6 +220,7 @@ func (b *rawBridge) addNewChild(parent *Inode, name string, child *Inode, out *f
out.Generation = b.nodes[out.NodeId].generation out.Generation = b.nodes[out.NodeId].generation
b.mu.Unlock() b.mu.Unlock()
unlockNodes(parent, child) unlockNodes(parent, child)
return fh
} }
func (b *rawBridge) setEntryOutTimeout(out *fuse.EntryOut) { func (b *rawBridge) setEntryOutTimeout(out *fuse.EntryOut) {
...@@ -252,19 +262,7 @@ func (b *rawBridge) Create(input *fuse.CreateIn, name string, out *fuse.CreateOu ...@@ -252,19 +262,7 @@ func (b *rawBridge) Create(input *fuse.CreateIn, name string, out *fuse.CreateOu
return code return code
} }
lockNode2(parent, child) out.Fh = b.addNewChild(parent, name, child, f, &out.EntryOut)
parent.setEntry(name, child)
b.mu.Lock()
if child.nodeID == 0 {
b.registerInode(child)
}
out.Fh = b.registerFile(f)
out.NodeId = child.nodeID
out.Ino = child.nodeID
out.Generation = b.nodes[child.nodeID].generation
b.mu.Unlock()
unlockNode2(parent, child)
b.setEntryOutTimeout(&out.EntryOut) b.setEntryOutTimeout(&out.EntryOut)
out.OpenFlags = flags out.OpenFlags = flags
...@@ -278,10 +276,7 @@ func (b *rawBridge) Create(input *fuse.CreateIn, name string, out *fuse.CreateOu ...@@ -278,10 +276,7 @@ func (b *rawBridge) Create(input *fuse.CreateIn, name string, out *fuse.CreateOu
} }
func (b *rawBridge) Forget(nodeid, nlookup uint64) { func (b *rawBridge) Forget(nodeid, nlookup uint64) {
b.mu.Lock() n, _ := b.inode(nodeid, 0)
n := b.nodes[nodeid].inode
b.mu.Unlock()
n.removeRef(nlookup, false) n.removeRef(nlookup, false)
} }
...@@ -308,14 +303,18 @@ func (b *rawBridge) GetAttr(input *fuse.GetAttrIn, out *fuse.AttrOut) (code fuse ...@@ -308,14 +303,18 @@ func (b *rawBridge) GetAttr(input *fuse.GetAttrIn, out *fuse.AttrOut) (code fuse
out.Nlink = 1 out.Nlink = 1
} }
if b.options.AttrTimeout != nil { b.setAttrTimeout(out)
out.SetTimeout(*b.options.AttrTimeout)
}
out.Ino = input.NodeId out.Ino = input.NodeId
return code return code
} }
func (b *rawBridge) setAttrTimeout(out *fuse.AttrOut) {
if b.options.AttrTimeout != nil {
out.SetTimeout(*b.options.AttrTimeout)
}
}
func (b *rawBridge) SetAttr(input *fuse.SetAttrIn, out *fuse.AttrOut) (code fuse.Status) { func (b *rawBridge) SetAttr(input *fuse.SetAttrIn, out *fuse.AttrOut) (code fuse.Status) {
ctx := context.TODO() ctx := context.TODO()
...@@ -379,19 +378,26 @@ func (b *rawBridge) SetAttr(input *fuse.SetAttrIn, out *fuse.AttrOut) (code fuse ...@@ -379,19 +378,26 @@ func (b *rawBridge) SetAttr(input *fuse.SetAttrIn, out *fuse.AttrOut) (code fuse
// Must call GetAttr(); the filesystem may override some of // Must call GetAttr(); the filesystem may override some of
// the changes we effect here. // the changes we effect here.
attr := &out.Attr attr := &out.Attr
code = n.node.GetAttr(ctx, f, attr)
// TODO - attr timout? // should take AttrOut
code = n.node.GetAttr(ctx, f, attr)
b.setAttrTimeout(out)
return code return code
} }
func (b *rawBridge) Rename(input *fuse.RenameIn, oldName string, newName string) (code fuse.Status) { func (b *rawBridge) Rename(input *fuse.RenameIn, oldName string, newName string) fuse.Status {
p1, _ := b.inode(input.NodeId, 0) p1, _ := b.inode(input.NodeId, 0)
p2, _ := b.inode(input.Newdir, 0) p2, _ := b.inode(input.Newdir, 0)
if code := p1.node.Rename(context.TODO(), oldName, p2.node, newName); code.Ok() { code := p1.node.Rename(context.TODO(), oldName, p2.node, newName, input.Flags)
if code.Ok() {
if input.Flags&unix.RENAME_EXCHANGE != 0 {
// XXX - test coverage.
p1.ExchangeChild(oldName, p2, newName)
} else {
p1.MvChild(oldName, p2, newName, true) p1.MvChild(oldName, p2, newName, true)
} }
}
return code return code
} }
...@@ -541,5 +547,6 @@ func (b *rawBridge) FsyncDir(input *fuse.FsyncIn) (code fuse.Status) { ...@@ -541,5 +547,6 @@ func (b *rawBridge) FsyncDir(input *fuse.FsyncIn) (code fuse.Status) {
func (b *rawBridge) StatFs(input *fuse.InHeader, out *fuse.StatfsOut) (code fuse.Status) { func (b *rawBridge) StatFs(input *fuse.InHeader, out *fuse.StatfsOut) (code fuse.Status) {
return return
} }
func (b *rawBridge) Init(*fuse.Server) { func (b *rawBridge) Init(*fuse.Server) {
} }
...@@ -279,6 +279,7 @@ func (n *Inode) removeRef(nlookup uint64, dropPersistence bool) (forgotten bool, ...@@ -279,6 +279,7 @@ func (n *Inode) removeRef(nlookup uint64, dropPersistence bool) (forgotten bool,
if nlookup > 0 && dropPersistence { if nlookup > 0 && dropPersistence {
log.Panic("only one allowed") log.Panic("only one allowed")
} else if nlookup > 0 { } else if nlookup > 0 {
n.lookupCount -= nlookup n.lookupCount -= nlookup
n.changeCounter++ n.changeCounter++
} else if dropPersistence && n.persistent { } else if dropPersistence && n.persistent {
...@@ -396,9 +397,8 @@ retry: ...@@ -396,9 +397,8 @@ retry:
return true, true return true, true
} }
// TODO - RENAME_NOREPLACE, RENAME_EXCHANGE MvChild executes a // MvChild executes a rename. If overwrite is set, a child at the
// rename. If overwrite is set, a child at the destination will be // destination will be overwritten.
// overwritten.
func (n *Inode) MvChild(old string, newParent *Inode, newName string, overwrite bool) bool { func (n *Inode) MvChild(old string, newParent *Inode, newName string, overwrite bool) bool {
retry: retry:
for { for {
...@@ -453,3 +453,64 @@ retry: ...@@ -453,3 +453,64 @@ retry:
return true return true
} }
} }
func (n *Inode) ExchangeChild(oldName string, newParent *Inode, newName string) {
oldParent := n
retry:
for {
lockNode2(oldParent, newParent)
counter1 := oldParent.changeCounter
counter2 := newParent.changeCounter
oldChild := oldParent.children[oldName]
destChild := newParent.children[newName]
unlockNode2(oldParent, newParent)
if destChild == nil && oldChild == nil {
return
}
if destChild == oldChild {
return
}
lockNodes(oldParent, newParent, oldChild, destChild)
if counter2 != newParent.changeCounter || counter1 != oldParent.changeCounter {
unlockNodes(oldParent, newParent, oldChild, destChild)
continue retry
}
// Detach
if oldChild != nil {
delete(oldParent.children, oldName)
delete(oldChild.parents, parentData{oldName, oldParent})
oldParent.changeCounter++
oldChild.changeCounter++
}
if destChild != nil {
delete(newParent.children, newName)
delete(destChild.parents, parentData{newName, newParent})
destChild.changeCounter++
newParent.changeCounter++
}
// Attach
if oldChild != nil {
newParent.children[newName] = oldChild
newParent.changeCounter++
oldChild.parents[parentData{newName, newParent}] = struct{}{}
oldChild.changeCounter++
}
if destChild != nil {
oldParent.children[oldName] = oldChild
oldParent.changeCounter++
destChild.parents[parentData{oldName, oldParent}] = struct{}{}
destChild.changeCounter++
}
unlockNodes(oldParent, newParent, oldChild, destChild)
return
}
}
...@@ -123,7 +123,12 @@ func (n *loopbackNode) Unlink(ctx context.Context, name string) fuse.Status { ...@@ -123,7 +123,12 @@ func (n *loopbackNode) Unlink(ctx context.Context, name string) fuse.Status {
return fuse.ToStatus(err) return fuse.ToStatus(err)
} }
func (n *loopbackNode) Rename(ctx context.Context, name string, newParent Node, newName string) fuse.Status { func (n *loopbackNode) Rename(ctx context.Context, name string, newParent Node, newName string, flags uint32) fuse.Status {
if flags != 0 {
return fuse.ENOSYS
}
p1 := filepath.Join(n.path(), name) p1 := filepath.Join(n.path(), name)
var newParentLoopback *loopbackNode var newParentLoopback *loopbackNode
if r, ok := newParent.(*loopbackRoot); ok { if r, ok := newParent.(*loopbackRoot); ok {
......
...@@ -14,6 +14,8 @@ import ( ...@@ -14,6 +14,8 @@ import (
"testing" "testing"
"time" "time"
"golang.org/x/sys/unix"
"github.com/hanwen/go-fuse/fuse" "github.com/hanwen/go-fuse/fuse"
"github.com/hanwen/go-fuse/internal/testutil" "github.com/hanwen/go-fuse/internal/testutil"
) )
...@@ -249,7 +251,7 @@ func TestMkdir(t *testing.T) { ...@@ -249,7 +251,7 @@ func TestMkdir(t *testing.T) {
} }
} }
func TestRename(t *testing.T) { func testRenameOverwrite(t *testing.T, destExists bool) {
tc := newTestCase(t) tc := newTestCase(t)
defer tc.Clean() defer tc.Clean()
...@@ -260,6 +262,12 @@ func TestRename(t *testing.T) { ...@@ -260,6 +262,12 @@ func TestRename(t *testing.T) {
t.Fatalf("WriteFile: %v", err) t.Fatalf("WriteFile: %v", err)
} }
if destExists {
if err := ioutil.WriteFile(tc.origDir+"/dir/renamed", []byte("xx"), 0644); err != nil {
t.Fatalf("WriteFile: %v", err)
}
}
st := syscall.Stat_t{} st := syscall.Stat_t{}
if err := syscall.Lstat(tc.mntDir+"/file", &st); err != nil { if err := syscall.Lstat(tc.mntDir+"/file", &st); err != nil {
t.Fatalf("Lstat before: %v", err) t.Fatalf("Lstat before: %v", err)
...@@ -281,3 +289,49 @@ func TestRename(t *testing.T) { ...@@ -281,3 +289,49 @@ func TestRename(t *testing.T) {
t.Errorf("got ino %d, want %d", got, beforeIno) t.Errorf("got ino %d, want %d", got, beforeIno)
} }
} }
func TestRenameDestExist(t *testing.T) {
testRenameOverwrite(t, true)
}
func TestRenameDestNoExist(t *testing.T) {
testRenameOverwrite(t, false)
}
func TestRenameNoOverwrite(t *testing.T) {
tc := newTestCase(t)
defer tc.Clean()
if err := os.Mkdir(tc.origDir+"/dir", 0755); err != nil {
t.Fatalf("Mkdir: %v", err)
}
if err := ioutil.WriteFile(tc.origDir+"/file", []byte("hello"), 0644); err != nil {
t.Fatalf("WriteFile: %v", err)
}
if err := ioutil.WriteFile(tc.origDir+"/dir/file", []byte("x"), 0644); err != nil {
t.Fatalf("WriteFile: %v", err)
}
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)
if err := unix.Renameat2(f1, "file", f2, "file", unix.RENAME_NOREPLACE); err == nil {
t.Errorf("rename NOREPLACE succeeded")
} else if err != syscall.EEXIST {
t.Errorf("got %v (%T) want EEXIST", err, err)
}
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)
}
}
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