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

nodefs: add Rename.

Inode.MvChild

Fix opaqueID
parent 29229b42
...@@ -91,6 +91,7 @@ type Node interface { ...@@ -91,6 +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
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)
......
...@@ -152,6 +152,10 @@ func (b *rawBridge) addNewChild(parent *Inode, name string, child *Inode, out *f ...@@ -152,6 +152,10 @@ func (b *rawBridge) addNewChild(parent *Inode, name string, child *Inode, out *f
b.registerInode(child) b.registerInode(child)
} }
out.NodeId = child.nodeID out.NodeId = child.nodeID
// NOSUBMIT - or should let FS expose Attr.Ino? This makes
// testing semantics hard though, because os.Lstat doesn't
// reflect the FUSE FS
out.Attr.Ino = child.nodeID
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)
...@@ -204,6 +208,7 @@ func (b *rawBridge) Create(input *fuse.CreateIn, name string, out *fuse.CreateOu ...@@ -204,6 +208,7 @@ func (b *rawBridge) Create(input *fuse.CreateIn, name string, out *fuse.CreateOu
} }
out.Fh = b.registerFile(f) out.Fh = b.registerFile(f)
out.NodeId = child.nodeID out.NodeId = child.nodeID
out.Ino = child.nodeID
out.Generation = b.nodes[child.nodeID].generation out.Generation = b.nodes[child.nodeID].generation
b.mu.Unlock() b.mu.Unlock()
unlockNode2(parent, child) unlockNode2(parent, child)
...@@ -254,6 +259,8 @@ func (b *rawBridge) GetAttr(input *fuse.GetAttrIn, out *fuse.AttrOut) (code fuse ...@@ -254,6 +259,8 @@ func (b *rawBridge) GetAttr(input *fuse.GetAttrIn, out *fuse.AttrOut) (code fuse
if b.options.AttrTimeout != nil { if b.options.AttrTimeout != nil {
out.SetTimeout(*b.options.AttrTimeout) out.SetTimeout(*b.options.AttrTimeout)
} }
out.Ino = input.NodeId
return code return code
} }
...@@ -327,7 +334,16 @@ func (b *rawBridge) SetAttr(input *fuse.SetAttrIn, out *fuse.AttrOut) (code fuse ...@@ -327,7 +334,16 @@ func (b *rawBridge) SetAttr(input *fuse.SetAttrIn, out *fuse.AttrOut) (code fuse
} }
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) (code fuse.Status) {
return fuse.ENOSYS p1, _ := b.inode(input.NodeId, 0)
p2, _ := b.inode(input.Newdir, 0)
if code := p1.node.Rename(context.TODO(), oldName, p2.node, newName); code.Ok() {
// NOSUBMIT - is it better to have the user code do
// this? Maybe the user code wants a transaction over
// more nodes?
p1.MvChild(oldName, p2, newName)
}
return code
} }
func (b *rawBridge) Link(input *fuse.LinkIn, filename string, out *fuse.EntryOut) (code fuse.Status) { func (b *rawBridge) Link(input *fuse.LinkIn, filename string, out *fuse.EntryOut) (code fuse.Status) {
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
package nodefs package nodefs
import ( import (
"fmt"
"log" "log"
"sort" "sort"
"strings" "strings"
...@@ -66,6 +67,16 @@ type Inode struct { ...@@ -66,6 +67,16 @@ type Inode struct {
parents map[parentData]struct{} parents map[parentData]struct{}
} }
// debugString is used for debugging. Racy.
func (n *Inode) debugString() string {
var ss []string
for nm, ch := range n.children {
ss = append(ss, fmt.Sprintf("%q=%d(%d)", nm, ch.nodeID, ch.opaqueID))
}
return fmt.Sprintf("%d: %s", n.nodeID, strings.Join(ss, ","))
}
// newInode creates creates new inode pointing to node. // newInode creates creates new inode pointing to node.
// //
// node -> inode association is NOT set. // node -> inode association is NOT set.
...@@ -276,6 +287,7 @@ func (n *Inode) newInode(node Node, mode uint32, opaqueID uint64, persistent boo ...@@ -276,6 +287,7 @@ func (n *Inode) newInode(node Node, mode uint32, opaqueID uint64, persistent boo
ch := &Inode{ ch := &Inode{
mode: mode ^ 07777, mode: mode ^ 07777,
node: node, node: node,
opaqueID: opaqueID,
bridge: n.bridge, bridge: n.bridge,
persistent: persistent, persistent: persistent,
parents: make(map[parentData]struct{}), parents: make(map[parentData]struct{}),
...@@ -299,7 +311,7 @@ func (n *Inode) removeRef(nlookup uint64, dropPersistence bool) (forgotten bool, ...@@ -299,7 +311,7 @@ func (n *Inode) removeRef(nlookup uint64, dropPersistence bool) (forgotten bool,
n.mu.Lock() n.mu.Lock()
if nlookup > 0 && dropPersistence { if nlookup > 0 && dropPersistence {
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++
...@@ -414,3 +426,44 @@ retry: ...@@ -414,3 +426,44 @@ retry:
return true, true return true, true
} }
// TODO - RENAME_NOREPLACE, RENAME_EXCHANGE
func (n *Inode) MvChild(old string, newParent *Inode, newName string) {
retry:
for {
lockNode2(n, newParent)
counter1 := n.changeCounter
counter2 := newParent.changeCounter
oldChild := n.children[old]
destChild := newParent.children[newName]
unlockNode2(n, newParent)
lockNodes(n, newParent, oldChild, destChild)
if counter2 != newParent.changeCounter || counter1 != n.changeCounter {
unlockNodes(n, newParent, oldChild, destChild)
continue retry
}
if destChild != nil {
delete(newParent.children, newName)
delete(destChild.parents, parentData{newName, newParent})
destChild.changeCounter++
newParent.changeCounter++
}
if oldChild != nil {
newParent.children[newName] = oldChild
newParent.changeCounter++
delete(n.children, old)
delete(oldChild.parents, parentData{old, n})
oldChild.parents[parentData{newName, newParent}] = struct{}{}
oldChild.changeCounter++
}
unlockNodes(n, newParent, oldChild, destChild)
return
}
}
...@@ -115,6 +115,20 @@ func (n *loopbackNode) Unlink(ctx context.Context, name string) fuse.Status { ...@@ -115,6 +115,20 @@ 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 {
p1 := filepath.Join(n.path(), name)
var newParentLoopback *loopbackNode
if r, ok := newParent.(*loopbackRoot); ok {
newParentLoopback = &r.loopbackNode
} else {
newParentLoopback = newParent.(*loopbackNode)
}
p2 := filepath.Join(newParentLoopback.path(), newName)
err := os.Rename(p1, p2)
return fuse.ToStatus(err)
}
func (n *loopbackNode) Create(ctx context.Context, name string, flags uint32, mode uint32) (inode *Inode, fh File, fuseFlags uint32, code fuse.Status) { func (n *loopbackNode) Create(ctx context.Context, name string, flags uint32, mode uint32) (inode *Inode, fh File, fuseFlags uint32, code fuse.Status) {
p := filepath.Join(n.path(), name) p := filepath.Join(n.path(), name)
......
...@@ -10,6 +10,7 @@ import ( ...@@ -10,6 +10,7 @@ import (
"os" "os"
"path/filepath" "path/filepath"
"runtime" "runtime"
"syscall"
"testing" "testing"
"time" "time"
...@@ -59,9 +60,12 @@ func newTestCase(t *testing.T) *testCase { ...@@ -59,9 +60,12 @@ func newTestCase(t *testing.T) *testCase {
} }
loopback := NewLoopback(tc.origDir) loopback := NewLoopback(tc.origDir)
_ = time.Second
oneSec := time.Second oneSec := time.Second
tc.rawFS = NewNodeFS(loopback, &Options{ tc.rawFS = NewNodeFS(loopback, &Options{
Debug: testutil.VerboseTest(), Debug: testutil.VerboseTest(),
// NOSUBMIT - should run all tests without cache too
EntryTimeout: &oneSec, EntryTimeout: &oneSec,
AttrTimeout: &oneSec, AttrTimeout: &oneSec,
}) })
...@@ -244,3 +248,36 @@ func TestMkdir(t *testing.T) { ...@@ -244,3 +248,36 @@ func TestMkdir(t *testing.T) {
t.Fatalf("Remove: %v", err) t.Fatalf("Remove: %v", err)
} }
} }
func TestRename(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)
}
st := syscall.Stat_t{}
if err := syscall.Lstat(tc.mntDir+"/file", &st); err != nil {
t.Fatalf("Lstat before: %v", err)
}
beforeIno := st.Ino
if err := os.Rename(tc.mntDir+"/file", tc.mntDir+"/dir/renamed"); err != nil {
t.Errorf("Rename: %v", err)
}
if fi, err := os.Lstat(tc.mntDir + "/file"); err == nil {
t.Fatalf("Lstat old: %v", fi)
}
if err := syscall.Lstat(tc.mntDir+"/dir/renamed", &st); err != nil {
t.Fatalf("Lstat after: %v", err)
}
if got := st.Ino; got != beforeIno {
t.Errorf("got ino %d, want %d", got, beforeIno)
}
}
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