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 (
"time"
)
/*
On error, returns an empty map, since we have little options
for outputting any other diagnostics.
*/
// newDirnameMap reads the contents of the given directory. On error,
// returns a nil map. This forces reloads in the DirCache until we
// succeed.
func newDirnameMap(fs fuse.FileSystem, dir string) map[string]bool {
result := make(map[string]bool)
stream, code := fs.OpenDir(dir)
if !code.Ok() {
log.Printf("newDirnameMap(): %v %v", dir, code)
return result
return nil
}
result := make(map[string]bool)
for e := range stream {
if e.Mode&fuse.S_IFREG != 0 {
result[e.Name] = true
......@@ -28,12 +26,10 @@ func newDirnameMap(fs fuse.FileSystem, dir string) map[string]bool {
return result
}
/*
Caches names in a directory for some time.
If called when the cache is expired, the filenames are read afresh in
the background.
*/
// DirCache caches names in a directory for some time.
//
// If called when the cache is expired, the filenames are read afresh in
// the background.
type DirCache struct {
dir string
ttlNs int64
......@@ -72,7 +68,8 @@ func (me *DirCache) maybeRefresh() {
}
me.updateRunning = true
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
////////////////
// 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)
haveCache, found := me.deletionCache.HasEntry(filepath.Base(marker))
if haveCache {
return found
return found, nil
}
_, code := me.fileSystems[0].GetAttr(marker)
if code == fuse.OK {
return true
return true, nil
}
if code == fuse.ENOENT {
return false
return false, nil
}
panic(fmt.Sprintf("Unexpected GetAttr return code %v %v", code, marker))
return false
log.Println("error accessing deletion marker:", marker)
return false, os.EROFS
}
func (me *UnionFs) getBranch(name string) branchResult {
......@@ -557,7 +557,12 @@ func (me *UnionFs) GetAttr(name string) (a *os.FileInfo, s fuse.Status) {
if name == me.options.DeletionDirName {
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
}
r := me.getBranch(name)
......@@ -621,6 +626,9 @@ func (me *UnionFs) OpenDir(directory string) (stream chan fuse.DirEntry, status
}
wg.Wait()
if deletions == nil {
return nil, syscall.EROFS
}
results := entries[0]
......
......@@ -539,3 +539,74 @@ func TestDropCache(t *testing.T) {
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