Commit 04f7c3eb authored by Han-Wen Nienhuys's avatar Han-Wen Nienhuys

Implement and test MemUnionFs.Reset()

parent 0c055e17
......@@ -17,15 +17,14 @@ var _ = log.Println
// A unionfs that only uses on-disk backing store for file contents.
type MemUnionFs struct {
fuse.DefaultNodeFileSystem
readonly fuse.FileSystem
backingStore string
root *memNode
connector *fuse.FileSystemConnector
mutex sync.RWMutex
root *memNode
cond *sync.Cond
nextFree int
readonly fuse.FileSystem
openWritable int
// All paths that have been renamed or deleted will be marked
......@@ -65,6 +64,25 @@ func (me *MemUnionFs) release() {
me.cond.Broadcast()
}
// Reset drops the state of the filesystem back its original.
func (me *MemUnionFs) Reset() {
me.mutex.Lock()
defer me.mutex.Unlock()
me.root.reset("")
for path, _ := range me.deleted {
parent, base := filepath.Split(path)
parent = stripSlash(parent)
last, rest := me.connector.Node(me.root.Inode(), parent)
if len(rest) == 0 {
me.connector.EntryNotify(last, base)
}
}
me.deleted = make(map[string]bool, len(me.deleted))
me.clearBackingStore()
}
func (me *MemUnionFs) Reap() map[string]*Result {
me.mutex.Lock()
defer me.mutex.Unlock()
......@@ -110,7 +128,11 @@ func (me *MemUnionFs) Clear() {
me.mutex.Lock()
defer me.mutex.Unlock()
me.deleted = make(map[string]bool)
me.root.Clear("")
me.root.clear("")
me.clearBackingStore()
}
func (me *MemUnionFs) clearBackingStore() {
f, err := os.Open(me.backingStore)
if err != nil {
return
......@@ -263,7 +285,6 @@ func (me *memNode) lookup(name string, context *fuse.Context) (fi *os.FileInfo,
if _, del := me.fs.deleted[fn]; del {
return nil, nil, fuse.ENOENT
}
fi, code = me.fs.readonly.GetAttr(fn, context)
if !code.Ok() {
return nil, nil, code
......@@ -613,13 +634,43 @@ func (me *memNode) Reap(path string, results map[string]*Result) {
}
}
func (me *memNode) Clear(path string) {
func (me *memNode) clear(path string) {
me.original = path
me.changed = false
me.backing = ""
for n, ch := range me.Inode().FsChildren() {
p := filepath.Join(path, n)
mn := ch.FsNode().(*memNode)
mn.Clear(p)
mn.clear(p)
}
}
func (me *memNode) reset(path string) (entryNotify bool) {
for n, ch := range me.Inode().FsChildren() {
p := filepath.Join(path, n)
mn := ch.FsNode().(*memNode)
if mn.reset(p) {
me.Inode().RmChild(n)
me.fs.connector.EntryNotify(me.Inode(), n)
}
}
if me.backing != "" || me.original != path {
return true
}
if me.changed {
info, code := me.fs.readonly.GetAttr(me.original, nil)
if !code.Ok() {
return true
}
me.info = *info
me.fs.connector.FileNotify(me.Inode(), -1, 0)
if me.Inode().IsDir() {
me.fs.connector.FileNotify(me.Inode(), 0, 0)
}
}
return false
}
......@@ -41,6 +41,7 @@ func setupMemUfs(t *testing.T) (workdir string, ufs *MemUnionFs, cleanup func())
EntryTimeout: .5 * entryTtl,
AttrTimeout: .5 * entryTtl,
NegativeTimeout: .5 * entryTtl,
PortableInodes: true,
}
state, conn, err := fuse.MountNodeFileSystem(wd+"/mnt", memFs, opts)
......@@ -805,3 +806,102 @@ func TestMemUnionGc(t *testing.T) {
t.Fatalf("should have 1 file after backing store gc: %v", entries)
}
}
func testEq(t *testing.T, got interface{}, want interface{}, expectEq bool) {
gots := fmt.Sprintf("%v", got)
wants := fmt.Sprintf("%v", want)
if (gots == wants) != expectEq {
op := "must differ from"
if expectEq {
op = "want"
}
t.Fatalf("Got %s %s %s.", gots, op, wants)
}
}
func TestMemUnionResetAttr(t *testing.T) {
wd, ufs, clean := setupMemUfs(t)
defer clean()
ioutil.WriteFile(wd + "/ro/fileattr", []byte{42}, 0644)
before, _ := os.Lstat(wd + "/mnt/fileattr")
err := os.Chmod(wd + "/mnt/fileattr", 0606)
CheckSuccess(err)
after, _ := os.Lstat(wd + "/mnt/fileattr")
testEq(t, after, before, false)
ufs.Reset()
afterReset, _ := os.Lstat(wd + "/mnt/fileattr")
testEq(t, afterReset, before, true)
}
func TestMemUnionResetContent(t *testing.T) {
wd, ufs, clean := setupMemUfs(t)
defer clean()
ioutil.WriteFile(wd + "/ro/filecontents", []byte{42}, 0644)
before, _ := ioutil.ReadFile(wd + "/mnt/filecontents")
ioutil.WriteFile(wd + "/mnt/filecontents", []byte{43}, 0644)
after, _ := ioutil.ReadFile(wd + "/mnt/filecontents")
testEq(t, after, before, false)
ufs.Reset()
afterReset, err := ioutil.ReadFile(wd + "/mnt/filecontents")
CheckSuccess(err)
testEq(t, afterReset, before, true)
}
func TestMemUnionResetDelete(t *testing.T) {
wd, ufs, clean := setupMemUfs(t)
defer clean()
ioutil.WriteFile(wd + "/ro/todelete", []byte{42}, 0644)
before, _ := os.Lstat(wd + "/mnt/todelete")
before.Ino = 0
os.Remove(wd + "/mnt/todelete")
after, _ := os.Lstat(wd + "/mnt/todelete")
testEq(t, after, before, false)
ufs.Reset()
afterReset, _ := os.Lstat(wd + "/mnt/todelete")
afterReset.Ino = 0
testEq(t, afterReset, before, true)
}
func clearInodes(infos []*os.FileInfo) {
for _, i := range infos {
i.Ino = 0
}
}
func TestMemUnionResetDirEntry(t *testing.T) {
wd, ufs, clean := setupMemUfs(t)
defer clean()
os.Mkdir(wd + "/ro/dir", 0755)
ioutil.WriteFile(wd + "/ro/dir/todelete", []byte{42}, 0644)
before, _ := ioutil.ReadDir(wd + "/mnt/dir")
clearInodes(before)
ioutil.WriteFile(wd + "/mnt/dir/newfile", []byte{42}, 0644)
os.Remove(wd + "/mnt/dir/todelete")
after, _ := ioutil.ReadDir(wd + "/mnt/dir")
clearInodes(after)
testEq(t, fuse.OsFileInfos(after), fuse.OsFileInfos(before), false)
ufs.Reset()
log.Println("reseT")
reset, _ := ioutil.ReadDir(wd + "/mnt/dir")
clearInodes(reset)
testEq(t, fuse.OsFileInfos(reset), fuse.OsFileInfos(before), true)
}
func TestMemUnionResetRename(t *testing.T) {
wd, ufs, clean := setupMemUfs(t)
defer clean()
os.Mkdir(wd + "/ro/dir", 0755)
ioutil.WriteFile(wd + "/ro/dir/movesrc", []byte{42}, 0644)
before, _ := ioutil.ReadDir(wd + "/mnt/dir")
clearInodes(before)
os.Rename(wd + "/mnt/dir/movesrc", wd + "/mnt/dir/dest")
after, _ := ioutil.ReadDir(wd + "/mnt/dir")
clearInodes(after)
testEq(t, fuse.OsFileInfos(after), fuse.OsFileInfos(before), false)
ufs.Reset()
reset, _ := ioutil.ReadDir(wd + "/mnt/dir")
clearInodes(reset)
testEq(t, fuse.OsFileInfos(reset), fuse.OsFileInfos(before), true)
}
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