From 868dc2c475725683cde1c4bd0d16014c040a632c Mon Sep 17 00:00:00 2001 From: Han-Wen Nienhuys <hanwen@google.com> Date: Mon, 20 Jun 2011 20:10:07 -0300 Subject: [PATCH] Deal with deletion directories which are removed from the backing store. Add test. --- unionfs/unionfs.go | 67 +++++++++++++++++++++++++++-------------- unionfs/unionfs_test.go | 26 ++++++++++++---- 2 files changed, 65 insertions(+), 28 deletions(-) diff --git a/unionfs/unionfs.go b/unionfs/unionfs.go index aa19c95..077547d 100644 --- a/unionfs/unionfs.go +++ b/unionfs/unionfs.go @@ -101,14 +101,10 @@ func NewUnionFs(name string, fileSystems []fuse.FileSystem, options UnionFsOptio } writable := g.fileSystems[0] - fi, code := writable.GetAttr(options.DeletionDirName) - if code == fuse.ENOENT { - code = writable.Mkdir(options.DeletionDirName, 0755) - fi, code = writable.GetAttr(options.DeletionDirName) - } - if !code.Ok() || !fi.IsDirectory() { - panic(fmt.Sprintf("could not create deletion path %v: %v", - options.DeletionDirName, code)) + code := g.createDeletionStore() + if !code.Ok() { + log.Printf("could not create deletion path %v: %v", options.DeletionDirName, code) + return nil } g.deletionCache = NewDirCache(writable, options.DeletionDirName, int64(options.DeletionCacheTTLSecs*1e9)) @@ -122,24 +118,41 @@ func NewUnionFs(name string, fileSystems []fuse.FileSystem, options UnionFsOptio //////////////// // Deal with all the caches. -func (me *UnionFs) isDeleted(name string) (deleted bool, accessError os.Error) { +func (me *UnionFs) isDeleted(name string) (deleted bool, code fuse.Status) { marker := me.deletionPath(name) haveCache, found := me.deletionCache.HasEntry(filepath.Base(marker)) if haveCache { - return found, nil + return found, fuse.OK } - _, code := me.fileSystems[0].GetAttr(marker) + _, code = me.fileSystems[0].GetAttr(marker) if code == fuse.OK { - return true, nil + return true, code + } + if code == fuse.ENOENT { + return false, fuse.OK } + + log.Println("error accessing deletion marker:", marker) + return false, syscall.EROFS +} + +func (me *UnionFs) createDeletionStore() (code fuse.Status) { + writable := me.fileSystems[0] + fi, code := writable.GetAttr(me.options.DeletionDirName) if code == fuse.ENOENT { - return false, nil + code = writable.Mkdir(me.options.DeletionDirName, 0755) + if code.Ok() { + fi, code = writable.GetAttr(me.options.DeletionDirName) + } + } + + if !code.Ok() || !fi.IsDirectory() { + code = syscall.EROFS } - log.Println("error accessing deletion marker:", marker) - return false, os.EROFS + return code } func (me *UnionFs) getBranch(name string) branchResult { @@ -209,6 +222,11 @@ func (me *UnionFs) removeDeletion(name string) { } func (me *UnionFs) putDeletion(name string) (code fuse.Status) { + code = me.createDeletionStore() + if !code.Ok() { + return code + } + marker := me.deletionPath(name) me.deletionCache.AddEntry(path.Base(marker)) @@ -218,7 +236,7 @@ func (me *UnionFs) putDeletion(name string) (code fuse.Status) { if code.Ok() && fi.Size == int64(len(name)) { return fuse.OK } - + var f fuse.File if code == fuse.ENOENT { f, code = writable.Create(marker, uint32(os.O_TRUNC|os.O_WRONLY), 0644) @@ -248,7 +266,7 @@ func (me *UnionFs) Promote(name string, srcResult branchResult) fuse.Status { // TODO - implement rename for dirs, links, etc. return fuse.ENOSYS } - + writable := me.fileSystems[0] sourceFs := me.fileSystems[srcResult.branch] @@ -557,11 +575,11 @@ func (me *UnionFs) GetAttr(name string) (a *os.FileInfo, s fuse.Status) { if name == me.options.DeletionDirName { return nil, fuse.ENOENT } - isDel, err := me.isDeleted(name) - if err != nil { - return nil, fuse.OsErrorToErrno(err) + isDel, s := me.isDeleted(name) + if !s.Ok() { + return nil, s } - + if isDel { return nil, fuse.ENOENT } @@ -627,7 +645,12 @@ func (me *UnionFs) OpenDir(directory string) (stream chan fuse.DirEntry, status wg.Wait() if deletions == nil { - return nil, syscall.EROFS + _, code := me.fileSystems[0].GetAttr(me.options.DeletionDirName) + if code == fuse.ENOENT { + deletions = map[string]bool{} + } else { + return nil, syscall.EROFS + } } results := entries[0] diff --git a/unionfs/unionfs_test.go b/unionfs/unionfs_test.go index 63ded70..2fab605 100644 --- a/unionfs/unionfs_test.go +++ b/unionfs/unionfs_test.go @@ -119,6 +119,20 @@ func remove(path string) { fuse.CheckSuccess(err) } +func TestAutocreateDeletionDir(t *testing.T) { + wd, clean := setupUfs(t) + defer clean() + + err := os.Remove(wd+"/rw/DELETIONS") + CheckSuccess(err) + + err = os.Mkdir(wd+"/mount/dir", 0755) + CheckSuccess(err) + + _, err = ioutil.ReadDir(wd+"/mount/dir") + CheckSuccess(err) +} + func TestSymlink(t *testing.T) { wd, clean := setupUfs(t) defer clean() @@ -203,7 +217,7 @@ func TestDelete(t *testing.T) { c, err := ioutil.ReadFile(delPath + "/" + k) CheckSuccess(err) if string(c) != "file" { - t.Fatal("content mismatch", string(c)) + t.Fatal("content mismatch", string(c)) } } } @@ -587,9 +601,9 @@ func TestDisappearing(t *testing.T) { _, err = ioutil.ReadDir(wd+"/mount") if err == nil { t.Fatal("Readdir should have failed") - } + } log.Println("expected readdir failure:", err) - + err = ioutil.WriteFile(wd + "/mount/file2", []byte("blabla"), 0644) if err == nil { t.Fatal("write should have failed") @@ -599,14 +613,14 @@ func TestDisappearing(t *testing.T) { // Restore, and wait for caches to catch up. wrFs.Root = oldRoot time.Sleep(1.5*entryTtl*1e9) - + _, err = ioutil.ReadDir(wd+"/mount") if err != nil { t.Fatal("Readdir should succeed", err) - } + } err = ioutil.WriteFile(wd + "/mount/file2", []byte("blabla"), 0644) if err != nil { t.Fatal("write should succeed", err) - } + } } -- 2.30.9