mirror of https://github.com/perkeep/perkeep.git
fs: implement Rename. all tests pass now.
Change-Id: I3876aeb6dafd7e4cc5254a741e6938a579373a64
This commit is contained in:
parent
a277eb99e0
commit
f709fc930c
|
@ -339,6 +339,62 @@ func TestDifferentWriteTypes(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func statStr(name string) string {
|
||||
fi, err := os.Stat(name)
|
||||
if os.IsNotExist(err) {
|
||||
return "ENOENT"
|
||||
}
|
||||
if err != nil {
|
||||
return "err=" + err.Error()
|
||||
}
|
||||
return fmt.Sprintf("file %v, size %d", fi.Mode(), fi.Size())
|
||||
}
|
||||
|
||||
func TestRename(t *testing.T) {
|
||||
condSkip(t)
|
||||
inEmptyMutDir(t, func(env *mountEnv, rootDir string) {
|
||||
name1 := filepath.Join(rootDir, "1")
|
||||
name2 := filepath.Join(rootDir, "2")
|
||||
subdir := filepath.Join(rootDir, "dir")
|
||||
name3 := filepath.Join(subdir, "3")
|
||||
|
||||
contents := []byte("Some file contents")
|
||||
const gone = "ENOENT"
|
||||
const reg = "file -rw-------, size 18"
|
||||
|
||||
if err := ioutil.WriteFile(name1, contents, 0644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := os.Mkdir(subdir, 0755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if got, want := statStr(name1), reg; got != want {
|
||||
t.Errorf("name1 = %q; want %q", got, want)
|
||||
}
|
||||
if err := os.Rename(name1, name2); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if got, want := statStr(name1), gone; got != want {
|
||||
t.Errorf("name1 = %q; want %q", got, want)
|
||||
}
|
||||
if got, want := statStr(name2), reg; got != want {
|
||||
t.Errorf("name2 = %q; want %q", got, want)
|
||||
}
|
||||
|
||||
// Moving to a different directory.
|
||||
if err := os.Rename(name2, name3); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if got, want := statStr(name2), gone; got != want {
|
||||
t.Errorf("name2 = %q; want %q", got, want)
|
||||
}
|
||||
if got, want := statStr(name3), reg; got != want {
|
||||
t.Errorf("name3 = %q; want %q", got, want)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func brokenTest(t *testing.T) {
|
||||
if v, _ := strconv.ParseBool(os.Getenv("RUN_BROKEN_TESTS")); !v {
|
||||
t.Skipf("Skipping broken tests without RUN_BROKEN_TESTS=1")
|
||||
|
@ -397,11 +453,13 @@ end tell
|
|||
}
|
||||
|
||||
func TestTextEdit(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skipf("Skipping in short mode")
|
||||
}
|
||||
if runtime.GOOS != "darwin" {
|
||||
t.Skipf("Skipping Darwin-specific test.")
|
||||
}
|
||||
condSkip(t)
|
||||
brokenTest(t)
|
||||
inEmptyMutDir(t, func(env *mountEnv, testDir string) {
|
||||
var (
|
||||
testFile = filepath.Join(testDir, "some-text-file.txt")
|
||||
|
@ -435,8 +493,7 @@ end tell
|
|||
fi, err := os.Stat(testFile)
|
||||
if err != nil {
|
||||
t.Errorf("Stat = %v, %v", fi, err)
|
||||
}
|
||||
if fi.Size() != int64(len(content2)) {
|
||||
} else if fi.Size() != int64(len(content2)) {
|
||||
t.Errorf("Stat size = %d; want %d", fi.Size(), len(content2))
|
||||
}
|
||||
slurp, err := ioutil.ReadFile(testFile)
|
||||
|
|
|
@ -50,7 +50,7 @@ type mutDir struct {
|
|||
|
||||
mu sync.Mutex
|
||||
lastPop time.Time
|
||||
children map[string]fuse.Node
|
||||
children map[string]mutFileOrDir
|
||||
}
|
||||
|
||||
// for debugging
|
||||
|
@ -97,7 +97,7 @@ func (n *mutDir) populate() error {
|
|||
|
||||
// Find all child permanodes and stick them in n.children
|
||||
if n.children == nil {
|
||||
n.children = make(map[string]fuse.Node)
|
||||
n.children = make(map[string]mutFileOrDir)
|
||||
}
|
||||
for k, v := range db.Permanode.Attr {
|
||||
const p = "camliPath:"
|
||||
|
@ -244,7 +244,7 @@ func (n *mutDir) creat(name string, isDir bool) (fuse.Node, error) {
|
|||
}
|
||||
|
||||
// Add a child node to this node.
|
||||
var child fuse.Node
|
||||
var child mutFileOrDir
|
||||
if isDir {
|
||||
child = &mutDir{
|
||||
fs: n.fs,
|
||||
|
@ -262,7 +262,7 @@ func (n *mutDir) creat(name string, isDir bool) (fuse.Node, error) {
|
|||
}
|
||||
n.mu.Lock()
|
||||
if n.children == nil {
|
||||
n.children = make(map[string]fuse.Node)
|
||||
n.children = make(map[string]mutFileOrDir)
|
||||
}
|
||||
n.children[name] = child
|
||||
n.mu.Unlock()
|
||||
|
@ -287,9 +287,67 @@ func (n *mutDir) Remove(req *fuse.RemoveRequest, intr fuse.Intr) fuse.Error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// &RenameRequest{Header:fuse.Header{Conn:(*fuse.Conn)(0xc210048180), ID:0x2, Node:0x8, Uid:0xf0d4, Gid:0x1388, Pid:0x5edb}, NewDir:0x8, OldName:"1", NewName:"2"}
|
||||
func (n *mutDir) Rename(req *fuse.RenameRequest, newDir fuse.Node, intr fuse.Intr) fuse.Error {
|
||||
log.Printf("UNIMPLEMENTED %T.Rename %+v; newDir=%#v", req, newDir)
|
||||
return fuse.EIO
|
||||
n2, ok := newDir.(*mutDir)
|
||||
if !ok {
|
||||
log.Printf("*mutDir newDir node isn't a *mutDir; is a %T; can't handle. returning EIO.", newDir)
|
||||
return fuse.EIO
|
||||
}
|
||||
|
||||
// TODO: do these populates in parallel:
|
||||
if err := n.populate(); err != nil {
|
||||
log.Printf("*mutDir.Rename src dir populate = %v", err)
|
||||
return fuse.EIO
|
||||
}
|
||||
if err := n2.populate(); err != nil {
|
||||
log.Printf("*mutDir.Rename dst dir populate = %v", err)
|
||||
return fuse.EIO
|
||||
}
|
||||
|
||||
n.mu.Lock()
|
||||
target, ok := n.children[req.OldName]
|
||||
n.mu.Unlock()
|
||||
if !ok {
|
||||
log.Printf("*mutDir.Rename src name %q isn't known", req.OldName)
|
||||
return fuse.ENOENT
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
|
||||
// Add a camliPath:name attribute to the dest permanode before unlinking it from
|
||||
// the source.
|
||||
claim := schema.NewSetAttributeClaim(n2.permanode, "camliPath:"+req.NewName, target.permanodeString())
|
||||
claim.SetClaimDate(now)
|
||||
_, err := n.fs.client.UploadAndSignBlob(claim)
|
||||
if err != nil {
|
||||
log.Printf("Upload rename link error: %v", err)
|
||||
return fuse.EIO
|
||||
}
|
||||
|
||||
delClaim := schema.NewDelAttributeClaim(n.permanode, "camliPath:"+req.OldName)
|
||||
delClaim.SetClaimDate(now)
|
||||
_, err = n.fs.client.UploadAndSignBlob(delClaim)
|
||||
if err != nil {
|
||||
log.Printf("Upload rename src unlink error: %v", err)
|
||||
return fuse.EIO
|
||||
}
|
||||
|
||||
// TODO(bradfitz): this locking would be racy, if the kernel
|
||||
// doesn't do it properly. (It should) Let's just trust the
|
||||
// kernel for now. Later we can verify and remove this
|
||||
// comment.
|
||||
n.mu.Lock()
|
||||
if n.children[req.OldName] != target {
|
||||
panic("Race.")
|
||||
}
|
||||
delete(n.children, req.OldName)
|
||||
n.mu.Unlock()
|
||||
n2.mu.Lock()
|
||||
n2.children[req.NewName] = target
|
||||
n2.mu.Unlock()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// mutFile is a mutable file.
|
||||
|
@ -550,3 +608,17 @@ func (h *mutFileHandle) Truncate(size uint64, intr fuse.Intr) fuse.Error {
|
|||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// mutFileOrDir is a *mutFile or *mutDir
|
||||
type mutFileOrDir interface {
|
||||
fuse.Node
|
||||
permanodeString() string
|
||||
}
|
||||
|
||||
func (n *mutFile) permanodeString() string {
|
||||
return n.permanode.String()
|
||||
}
|
||||
|
||||
func (n *mutDir) permanodeString() string {
|
||||
return n.permanode.String()
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue