Commit 41883b06 authored by Han-Wen Nienhuys's avatar Han-Wen Nienhuys

Deal gracefully with failures in accessing the writable overlay.

This may occur if we loose connection to the NFS server, for example.
parent 75b155e7
...@@ -7,19 +7,17 @@ import ( ...@@ -7,19 +7,17 @@ import (
"time" "time"
) )
// newDirnameMap reads the contents of the given directory. On error,
/* // returns a nil map. This forces reloads in the DirCache until we
On error, returns an empty map, since we have little options // succeed.
for outputting any other diagnostics.
*/
func newDirnameMap(fs fuse.FileSystem, dir string) map[string]bool { func newDirnameMap(fs fuse.FileSystem, dir string) map[string]bool {
result := make(map[string]bool)
stream, code := fs.OpenDir(dir) stream, code := fs.OpenDir(dir)
if !code.Ok() { if !code.Ok() {
log.Printf("newDirnameMap(): %v %v", dir, code) log.Printf("newDirnameMap(): %v %v", dir, code)
return result return nil
} }
result := make(map[string]bool)
for e := range stream { for e := range stream {
if e.Mode&fuse.S_IFREG != 0 { if e.Mode&fuse.S_IFREG != 0 {
result[e.Name] = true result[e.Name] = true
...@@ -28,12 +26,10 @@ func newDirnameMap(fs fuse.FileSystem, dir string) map[string]bool { ...@@ -28,12 +26,10 @@ func newDirnameMap(fs fuse.FileSystem, dir string) map[string]bool {
return result return result
} }
/* // DirCache caches names in a directory for some time.
Caches names in a directory for some time. //
// If called when the cache is expired, the filenames are read afresh in
If called when the cache is expired, the filenames are read afresh in // the background.
the background.
*/
type DirCache struct { type DirCache struct {
dir string dir string
ttlNs int64 ttlNs int64
...@@ -72,7 +68,8 @@ func (me *DirCache) maybeRefresh() { ...@@ -72,7 +68,8 @@ func (me *DirCache) maybeRefresh() {
} }
me.updateRunning = true me.updateRunning = true
go func() { go func() {
me.setMap(newDirnameMap(me.fs, me.dir)) newmap := newDirnameMap(me.fs, me.dir)
me.setMap(newmap)
}() }()
} }
......
...@@ -122,24 +122,24 @@ func NewUnionFs(name string, fileSystems []fuse.FileSystem, options UnionFsOptio ...@@ -122,24 +122,24 @@ 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) bool { func (me *UnionFs) isDeleted(name string) (deleted bool, accessError os.Error) {
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 return found, nil
} }
_, code := me.fileSystems[0].GetAttr(marker) _, code := me.fileSystems[0].GetAttr(marker)
if code == fuse.OK { if code == fuse.OK {
return true return true, nil
} }
if code == fuse.ENOENT { if code == fuse.ENOENT {
return false return false, nil
} }
panic(fmt.Sprintf("Unexpected GetAttr return code %v %v", code, marker)) log.Println("error accessing deletion marker:", marker)
return false return false, os.EROFS
} }
func (me *UnionFs) getBranch(name string) branchResult { func (me *UnionFs) getBranch(name string) branchResult {
...@@ -557,7 +557,12 @@ func (me *UnionFs) GetAttr(name string) (a *os.FileInfo, s fuse.Status) { ...@@ -557,7 +557,12 @@ 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
} }
if me.isDeleted(name) { isDel, err := me.isDeleted(name)
if err != nil {
return nil, fuse.OsErrorToErrno(err)
}
if isDel {
return nil, fuse.ENOENT return nil, fuse.ENOENT
} }
r := me.getBranch(name) r := me.getBranch(name)
...@@ -621,6 +626,9 @@ func (me *UnionFs) OpenDir(directory string) (stream chan fuse.DirEntry, status ...@@ -621,6 +626,9 @@ func (me *UnionFs) OpenDir(directory string) (stream chan fuse.DirEntry, status
} }
wg.Wait() wg.Wait()
if deletions == nil {
return nil, syscall.EROFS
}
results := entries[0] results := entries[0]
......
...@@ -539,3 +539,74 @@ func TestDropCache(t *testing.T) { ...@@ -539,3 +539,74 @@ func TestDropCache(t *testing.T) {
t.Fatal("mismatch 2", names2) t.Fatal("mismatch 2", names2)
} }
} }
func TestDisappearing(t *testing.T) {
// This init is like setupUfs, but we want access to the
// writable Fs.
wd := fuse.MakeTempDir()
defer os.RemoveAll(wd)
err := os.Mkdir(wd+"/mount", 0700)
fuse.CheckSuccess(err)
err = os.Mkdir(wd+"/rw", 0700)
fuse.CheckSuccess(err)
os.Mkdir(wd+"/ro", 0700)
fuse.CheckSuccess(err)
wrFs := fuse.NewLoopbackFileSystem(wd+"/rw")
var fses []fuse.FileSystem
fses = append(fses, wrFs)
fses = append(fses, fuse.NewLoopbackFileSystem(wd+"/ro"))
ufs := NewUnionFs("testFs", fses, testOpts)
opts := &fuse.FileSystemOptions{
EntryTimeout: entryTtl,
AttrTimeout: entryTtl,
NegativeTimeout: entryTtl,
}
state, _, err := fuse.MountFileSystem(wd + "/mount", ufs, opts)
CheckSuccess(err)
defer state.Unmount()
state.Debug = true
go state.Loop(true)
log.Println("TestDisappearing2")
err = ioutil.WriteFile(wd + "/ro/file", []byte("blabla"), 0644)
CheckSuccess(err)
err = os.Remove(wd+"/mount/file")
CheckSuccess(err)
oldRoot := wrFs.Root
wrFs.Root = "/dev/null"
time.Sleep(1.5*entryTtl*1e9)
_, 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")
}
log.Println("expected write failure:", err)
// 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)
}
}
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