Commit beb5f868 authored by Han-Wen Nienhuys's avatar Han-Wen Nienhuys

Add update method MemUnionFs.

parent 05a51a96
......@@ -7,6 +7,7 @@ import (
"os"
"path/filepath"
"strings"
"sort"
"sync"
"time"
)
......@@ -18,7 +19,7 @@ type MemUnionFs struct {
fuse.DefaultNodeFileSystem
backingStore string
root *memNode
connector *fuse.FileSystemConnector
mutex sync.RWMutex
cond *sync.Cond
nextFree int
......@@ -43,12 +44,16 @@ type memNode struct {
}
type Result struct {
Info *os.FileInfo
*os.FileInfo
Original string
Backing string
Link string
}
func (me *MemUnionFs) OnMount(conn *fuse.FileSystemConnector) {
me.connector = conn
}
func (me *MemUnionFs) release() {
me.mutex.Lock()
defer me.mutex.Unlock()
......@@ -74,6 +79,52 @@ func (me *MemUnionFs) Clear() {
me.root.Clear("")
}
func (me *MemUnionFs) Update(results map[string]*Result) {
del := []string{}
add := []string{}
for k, v := range results {
if v.FileInfo != nil {
add = append(add, k)
} else {
del = append(del, k)
}
}
sort.Strings(del)
for i := len(del)-1; i >= 0; i-- {
n := del[i]
dir, base := filepath.Split(n)
dir = strings.TrimRight(dir, "/")
dirNode, rest := me.connector.Node(me.root.Inode(), dir)
if len(rest) > 0 {
continue
}
dirNode.RmChild(base)
me.connector.EntryNotify(dirNode, base)
}
me.mutex.Lock()
defer me.mutex.Unlock()
sort.Strings(add)
for _, n := range add {
node, rest := me.connector.Node(me.root.Inode(), n)
if len(rest) > 0 {
me.connector.EntryNotify(node, rest[0])
continue
}
me.connector.FileNotify(node, 0, 0)
mn := node.FsNode().(*memNode)
mn.original = n
mn.changed = false
r := results[n]
mn.info = *r.FileInfo
mn.link = r.Link
}
}
func (me *MemUnionFs) getFilename() string {
id := me.nextFree
me.nextFree++
......@@ -439,7 +490,7 @@ func (me *memNode) Reap(path string, results map[string]*Result) {
if me.changed {
info := me.info
results[path] = &Result{
Info: &info,
FileInfo: &info,
Link: me.link,
Backing: me.backing,
Original: me.original,
......
......@@ -32,7 +32,7 @@ func setupMemUfs(t *testing.T) (workdir string, ufs *MemUnionFs, cleanup func())
os.Mkdir(wd+"/ro", 0700)
fuse.CheckSuccess(err)
roFs := NewCachingFileSystem(fuse.NewLoopbackFileSystem(wd+"/ro"), 0.0)
roFs := fuse.NewLoopbackFileSystem(wd+"/ro")
memFs := NewMemUnionFs(wd+"/backing", roFs)
// We configure timeouts are smaller, so we can check for
......@@ -167,7 +167,7 @@ func TestMemUnionFsDelete(t *testing.T) {
}
r := ufs.Reap()
if r["file"] == nil || r["file"].Info != nil {
if r["file"] == nil || r["file"].FileInfo != nil {
t.Errorf("expect 1 deletion reap result: %v", r)
}
}
......@@ -279,7 +279,7 @@ func TestMemUnionFsMkdirPromote(t *testing.T) {
CheckSuccess(err)
r := ufs.Reap()
if r["subdir/subdir2/dir3"] == nil || r["subdir/subdir2/dir3"].Info.Mode & fuse.S_IFDIR == 0 {
if r["subdir/subdir2/dir3"] == nil || r["subdir/subdir2/dir3"].FileInfo.Mode & fuse.S_IFDIR == 0 {
t.Errorf("expect 1 file reap result: %v", r)
}
}
......@@ -488,6 +488,76 @@ func TestMemUnionFsDoubleOpen(t *testing.T) {
}
}
func TestMemUnionFsUpdate(t *testing.T) {
wd, ufs, clean := setupMemUfs(t)
defer clean()
err := ioutil.WriteFile(wd+"/ro/file1", []byte("blablabla"), 0644)
CheckSuccess(err)
_, err = os.Lstat(wd + "/mount/file1")
CheckSuccess(err)
if fi, _ := os.Lstat(wd + "/mount/file2"); fi != nil {
t.Fatal("file2 should not exist", fi)
}
if fi, _ := os.Lstat(wd + "/mount/symlink"); fi != nil {
t.Fatal("symlink should not exist", fi)
}
err = os.Remove(wd+"/ro/file1")
CheckSuccess(err)
err = ioutil.WriteFile(wd+"/ro/file2", []byte("foobar"), 0644)
CheckSuccess(err)
err = os.Symlink("target", wd + "/ro/symlink")
CheckSuccess(err)
// Still have cached attributes.
fi, err := os.Lstat(wd + "/mount/file1")
CheckSuccess(err)
if fi, _ := os.Lstat(wd + "/mount/file2"); fi != nil {
t.Fatal("file2 should not exist")
}
if fi, _ := os.Lstat(wd + "/mount/symlink"); fi != nil {
t.Fatal("symlink should not exist", fi)
}
roF2, err := os.Lstat(wd + "/ro/file2")
CheckSuccess(err)
roSymlinkFi, err := os.Lstat(wd + "/ro/symlink")
CheckSuccess(err)
updates := map[string]*Result{
"file1": &Result{
nil, "", "", "",
},
"file2": &Result{
roF2, "", "", "",
},
"symlink": &Result{
roSymlinkFi, "", "", "target",
},
}
ufs.Update(updates)
// Cached attributes flushed.
if fi, _ := os.Lstat(wd + "/mount/file1"); fi != nil {
t.Fatal("file1 should have disappeared", fi)
}
fi, err = os.Lstat(wd + "/mount/file2")
CheckSuccess(err)
if roF2.Mtime_ns != fi.Mtime_ns {
t.Fatalf("file2 attribute mismatch: got %v want %v", fi, roF2)
}
val, err := os.Readlink(wd + "/mount/symlink")
CheckSuccess(err)
if val != "target" {
t.Error("symlink value got %q want %v", val, "target")
}
}
func TestMemUnionFsFdLeak(t *testing.T) {
beforeEntries, err := ioutil.ReadDir("/proc/self/fd")
CheckSuccess(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