Commit 868dc2c4 authored by Han-Wen Nienhuys's avatar Han-Wen Nienhuys

Deal with deletion directories which are removed from the backing

store.

Add test.
parent c614b5b8
...@@ -101,14 +101,10 @@ func NewUnionFs(name string, fileSystems []fuse.FileSystem, options UnionFsOptio ...@@ -101,14 +101,10 @@ func NewUnionFs(name string, fileSystems []fuse.FileSystem, options UnionFsOptio
} }
writable := g.fileSystems[0] writable := g.fileSystems[0]
fi, code := writable.GetAttr(options.DeletionDirName) code := g.createDeletionStore()
if code == fuse.ENOENT { if !code.Ok() {
code = writable.Mkdir(options.DeletionDirName, 0755) log.Printf("could not create deletion path %v: %v", options.DeletionDirName, code)
fi, code = writable.GetAttr(options.DeletionDirName) return nil
}
if !code.Ok() || !fi.IsDirectory() {
panic(fmt.Sprintf("could not create deletion path %v: %v",
options.DeletionDirName, code))
} }
g.deletionCache = NewDirCache(writable, options.DeletionDirName, int64(options.DeletionCacheTTLSecs*1e9)) g.deletionCache = NewDirCache(writable, options.DeletionDirName, int64(options.DeletionCacheTTLSecs*1e9))
...@@ -122,24 +118,41 @@ func NewUnionFs(name string, fileSystems []fuse.FileSystem, options UnionFsOptio ...@@ -122,24 +118,41 @@ func NewUnionFs(name string, fileSystems []fuse.FileSystem, options UnionFsOptio
//////////////// ////////////////
// Deal with all the caches. // 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) marker := me.deletionPath(name)
haveCache, found := me.deletionCache.HasEntry(filepath.Base(marker)) haveCache, found := me.deletionCache.HasEntry(filepath.Base(marker))
if haveCache { 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 { 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 { 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 code
return false, os.EROFS
} }
func (me *UnionFs) getBranch(name string) branchResult { func (me *UnionFs) getBranch(name string) branchResult {
...@@ -209,6 +222,11 @@ func (me *UnionFs) removeDeletion(name string) { ...@@ -209,6 +222,11 @@ func (me *UnionFs) removeDeletion(name string) {
} }
func (me *UnionFs) putDeletion(name string) (code fuse.Status) { func (me *UnionFs) putDeletion(name string) (code fuse.Status) {
code = me.createDeletionStore()
if !code.Ok() {
return code
}
marker := me.deletionPath(name) marker := me.deletionPath(name)
me.deletionCache.AddEntry(path.Base(marker)) me.deletionCache.AddEntry(path.Base(marker))
...@@ -218,7 +236,7 @@ func (me *UnionFs) putDeletion(name string) (code fuse.Status) { ...@@ -218,7 +236,7 @@ func (me *UnionFs) putDeletion(name string) (code fuse.Status) {
if code.Ok() && fi.Size == int64(len(name)) { if code.Ok() && fi.Size == int64(len(name)) {
return fuse.OK return fuse.OK
} }
var f fuse.File var f fuse.File
if code == fuse.ENOENT { if code == fuse.ENOENT {
f, code = writable.Create(marker, uint32(os.O_TRUNC|os.O_WRONLY), 0644) 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 { ...@@ -248,7 +266,7 @@ func (me *UnionFs) Promote(name string, srcResult branchResult) fuse.Status {
// TODO - implement rename for dirs, links, etc. // TODO - implement rename for dirs, links, etc.
return fuse.ENOSYS return fuse.ENOSYS
} }
writable := me.fileSystems[0] writable := me.fileSystems[0]
sourceFs := me.fileSystems[srcResult.branch] sourceFs := me.fileSystems[srcResult.branch]
...@@ -557,11 +575,11 @@ func (me *UnionFs) GetAttr(name string) (a *os.FileInfo, s fuse.Status) { ...@@ -557,11 +575,11 @@ func (me *UnionFs) GetAttr(name string) (a *os.FileInfo, s fuse.Status) {
if name == me.options.DeletionDirName { if name == me.options.DeletionDirName {
return nil, fuse.ENOENT return nil, fuse.ENOENT
} }
isDel, err := me.isDeleted(name) isDel, s := me.isDeleted(name)
if err != nil { if !s.Ok() {
return nil, fuse.OsErrorToErrno(err) return nil, s
} }
if isDel { if isDel {
return nil, fuse.ENOENT return nil, fuse.ENOENT
} }
...@@ -627,7 +645,12 @@ func (me *UnionFs) OpenDir(directory string) (stream chan fuse.DirEntry, status ...@@ -627,7 +645,12 @@ func (me *UnionFs) OpenDir(directory string) (stream chan fuse.DirEntry, status
wg.Wait() wg.Wait()
if deletions == nil { 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] results := entries[0]
......
...@@ -119,6 +119,20 @@ func remove(path string) { ...@@ -119,6 +119,20 @@ func remove(path string) {
fuse.CheckSuccess(err) 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) { func TestSymlink(t *testing.T) {
wd, clean := setupUfs(t) wd, clean := setupUfs(t)
defer clean() defer clean()
...@@ -203,7 +217,7 @@ func TestDelete(t *testing.T) { ...@@ -203,7 +217,7 @@ func TestDelete(t *testing.T) {
c, err := ioutil.ReadFile(delPath + "/" + k) c, err := ioutil.ReadFile(delPath + "/" + k)
CheckSuccess(err) CheckSuccess(err)
if string(c) != "file" { 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) { ...@@ -587,9 +601,9 @@ func TestDisappearing(t *testing.T) {
_, err = ioutil.ReadDir(wd+"/mount") _, err = ioutil.ReadDir(wd+"/mount")
if err == nil { if err == nil {
t.Fatal("Readdir should have failed") t.Fatal("Readdir should have failed")
} }
log.Println("expected readdir failure:", err) log.Println("expected readdir failure:", err)
err = ioutil.WriteFile(wd + "/mount/file2", []byte("blabla"), 0644) err = ioutil.WriteFile(wd + "/mount/file2", []byte("blabla"), 0644)
if err == nil { if err == nil {
t.Fatal("write should have failed") t.Fatal("write should have failed")
...@@ -599,14 +613,14 @@ func TestDisappearing(t *testing.T) { ...@@ -599,14 +613,14 @@ func TestDisappearing(t *testing.T) {
// Restore, and wait for caches to catch up. // Restore, and wait for caches to catch up.
wrFs.Root = oldRoot wrFs.Root = oldRoot
time.Sleep(1.5*entryTtl*1e9) time.Sleep(1.5*entryTtl*1e9)
_, err = ioutil.ReadDir(wd+"/mount") _, err = ioutil.ReadDir(wd+"/mount")
if err != nil { if err != nil {
t.Fatal("Readdir should succeed", err) t.Fatal("Readdir should succeed", err)
} }
err = ioutil.WriteFile(wd + "/mount/file2", []byte("blabla"), 0644) err = ioutil.WriteFile(wd + "/mount/file2", []byte("blabla"), 0644)
if err != nil { if err != nil {
t.Fatal("write should succeed", err) t.Fatal("write should succeed", err)
} }
} }
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