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

MemUnionFs: centralize locking, implement Reap/Clear.

parent 9da364b3
......@@ -19,7 +19,7 @@ type MemUnionFs struct {
backingStore string
root *memNode
mutex sync.Mutex
mutex sync.RWMutex
nextFree int
readonly fuse.FileSystem
......@@ -29,20 +29,39 @@ type memNode struct {
fuse.DefaultFsNode
fs *MemUnionFs
// protects mutable data below.
mutex *sync.RWMutex
backing string
original string
changed bool
link string
info os.FileInfo
deleted map[string]bool
}
// protects mutable data below.
mutex sync.RWMutex
backing string
changed bool
link string
info os.FileInfo
deleted map[string]bool
type Result struct {
Info *os.FileInfo
Original string
Backing string
Link string
}
func (me *MemUnionFs) getFilename() string {
func (me *MemUnionFs) Reap() map[string]*Result {
me.mutex.RLock()
defer me.mutex.RUnlock()
m := map[string]*Result{}
me.root.Reap("", m)
return m
}
func (me *MemUnionFs) Clear() {
me.mutex.Lock()
defer me.mutex.Unlock()
me.root.Clear("")
}
func (me *MemUnionFs) getFilename() string {
id := me.nextFree
me.nextFree++
return fmt.Sprintf("%s/%d", me.backingStore, id)
......@@ -59,7 +78,8 @@ func (me *MemUnionFs) StatFs() *fuse.StatfsOut {
func (me *MemUnionFs) newNode(isdir bool) *memNode {
n := &memNode{
fs: me,
fs: me,
mutex: &me.mutex,
}
if isdir {
n.deleted = map[string]bool{}
......@@ -171,6 +191,7 @@ func (me *memNode) Symlink(name string, content string, context *fuse.Context) (
n := me.newNode(false)
n.info.Mode = fuse.S_IFLNK | 0777
n.link = content
n.changed = true
me.Inode().AddChild(name, n.Inode())
me.touch()
me.deleted[name] = false, false
......@@ -190,10 +211,11 @@ func (me *memNode) Rename(oldName string, newParent fuse.FsNode, newName string,
}
func (me *memNode) Link(name string, existing fuse.FsNode, context *fuse.Context) (fi *os.FileInfo, newNode fuse.FsNode, code fuse.Status) {
me.mutex.Lock()
defer me.mutex.Unlock()
me.Inode().AddChild(name, existing.Inode())
fi, code = existing.GetAttr(nil, context)
me.mutex.Lock()
defer me.mutex.Unlock()
me.touch()
me.deleted[name] = false, false
return fi, existing, code
......@@ -202,6 +224,7 @@ func (me *memNode) Link(name string, existing fuse.FsNode, context *fuse.Context
func (me *memNode) Create(name string, flags uint32, mode uint32, context *fuse.Context) (file fuse.File, fi *os.FileInfo, newNode fuse.FsNode, code fuse.Status) {
me.mutex.Lock()
defer me.mutex.Unlock()
n := me.newNode(false)
n.info.Mode = mode | fuse.S_IFREG
n.changed = true
......@@ -229,9 +252,10 @@ func (me *memNodeFile) InnerFile() fuse.File {
func (me *memNodeFile) Flush() fuse.Status {
code := me.File.Flush()
if me.writable {
fi, _ := me.File.GetAttr()
me.node.mutex.Lock()
defer me.node.mutex.Unlock()
fi, _ := me.File.GetAttr()
me.node.info.Size = fi.Size
me.node.info.Blocks = fi.Blocks
}
......@@ -253,7 +277,7 @@ func (me *memNode) promote() {
destfs := &fuse.LoopbackFileSystem{Root: "/"}
fuse.CopyFile(me.fs.readonly, destfs,
me.original, strings.TrimLeft(me.backing, "/"), nil)
me.original = ""
files := me.Inode().Files(0)
for _, f := range files {
mf := f.File.(*memNodeFile)
......@@ -272,12 +296,14 @@ func (me *memNode) promote() {
func (me *memNode) Open(flags uint32, context *fuse.Context) (file fuse.File, code fuse.Status) {
if flags&fuse.O_ANYWRITE != 0 {
me.mutex.Lock()
defer me.mutex.Unlock()
me.promote()
me.touch()
me.mutex.Unlock()
}
me.mutex.RLock()
defer me.mutex.RUnlock()
if me.backing != "" {
f, err := os.OpenFile(me.backing, int(flags), 0666)
if err != nil {
......@@ -383,3 +409,37 @@ func (me *memNode) OpenDir(context *fuse.Context) (stream chan fuse.DirEntry, co
close(stream)
return stream, fuse.OK
}
func (me *memNode) Reap(path string, results map[string]*Result) {
for name, _ := range me.deleted {
p := filepath.Join(path, name)
results[p] = &Result{}
}
if me.changed {
info := me.info
results[path] = &Result{
Info: &info,
Link: me.link,
Backing: me.backing,
Original: me.original,
}
}
for n, ch := range me.Inode().FsChildren() {
p := filepath.Join(path, n)
ch.FsNode().(*memNode).Reap(p, results)
}
}
func (me *memNode) Clear(path string) {
me.original = path
me.changed = false
me.backing = ""
me.deleted = make(map[string]bool)
for n, ch := range me.Inode().FsChildren() {
p := filepath.Join(path, n)
mn := ch.FsNode().(*memNode)
mn.Clear(p)
}
}
......@@ -18,7 +18,7 @@ var _ = log.Print
var CheckSuccess = fuse.CheckSuccess
func setupMemUfs(t *testing.T) (workdir string, cleanup func()) {
func setupMemUfs(t *testing.T) (workdir string, ufs *MemUnionFs, cleanup func()) {
// Make sure system setting does not affect test.
syscall.Umask(0)
......@@ -49,14 +49,14 @@ func setupMemUfs(t *testing.T) (workdir string, cleanup func()) {
state.Debug = fuse.VerboseTest()
go state.Loop()
return wd, func() {
return wd, memFs, func() {
state.Unmount()
os.RemoveAll(wd)
}
}
func TestMemUnionFsSymlink(t *testing.T) {
wd, clean := setupMemUfs(t)
wd, ufs, clean := setupMemUfs(t)
defer clean()
err := os.Symlink("/foobar", wd+"/mount/link")
......@@ -68,10 +68,15 @@ func TestMemUnionFsSymlink(t *testing.T) {
if val != "/foobar" {
t.Errorf("symlink mismatch: %v", val)
}
r := ufs.Reap()
if len(r) != 2 || r["link"] == nil || r["link"].Link != "/foobar" {
t.Errorf("expect 1 symlink reap result: %v", r)
}
}
func TestMemUnionFsSymlinkPromote(t *testing.T) {
wd, clean := setupMemUfs(t)
wd, ufs, clean := setupMemUfs(t)
defer clean()
err := os.Mkdir(wd+"/ro/subdir", 0755)
......@@ -79,10 +84,15 @@ func TestMemUnionFsSymlinkPromote(t *testing.T) {
err = os.Symlink("/foobar", wd+"/mount/subdir/link")
CheckSuccess(err)
r := ufs.Reap()
if len(r) != 2 || r["subdir"] == nil || r["subdir/link"] == nil || r["subdir/link"].Link != "/foobar" {
t.Errorf("expect 1 symlink reap result: %v", r)
}
}
func TestMemUnionFsChtimes(t *testing.T) {
wd, clean := setupMemUfs(t)
wd, ufs, clean := setupMemUfs(t)
defer clean()
writeToFile(wd+"/ro/file", "a")
......@@ -96,10 +106,15 @@ func TestMemUnionFsChtimes(t *testing.T) {
if fi.Atime_ns != 82e9 || fi.Mtime_ns != 83e9 {
t.Error("Incorrect timestamp", fi)
}
r := ufs.Reap()
if r["file"] == nil || r["file"].Original == "" {
t.Errorf("expect 1 file reap result: %v", r)
}
}
func TestMemUnionFsChmod(t *testing.T) {
wd, clean := setupMemUfs(t)
wd, ufs, clean := setupMemUfs(t)
defer clean()
ro_fn := wd + "/ro/file"
......@@ -113,10 +128,15 @@ func TestMemUnionFsChmod(t *testing.T) {
if fi.Mode&07777 != 07070 {
t.Errorf("Unexpected mode found: %o", fi.Mode)
}
r := ufs.Reap()
if r["file"] == nil || r["file"].Original == "" {
t.Errorf("expect 1 file reap result: %v", r)
}
}
func TestMemUnionFsChown(t *testing.T) {
wd, clean := setupMemUfs(t)
wd, _, clean := setupMemUfs(t)
defer clean()
ro_fn := wd + "/ro/file"
......@@ -131,7 +151,7 @@ func TestMemUnionFsChown(t *testing.T) {
}
func TestMemUnionFsDelete(t *testing.T) {
wd, clean := setupMemUfs(t)
wd, ufs, clean := setupMemUfs(t)
defer clean()
writeToFile(wd+"/ro/file", "a")
......@@ -145,10 +165,15 @@ func TestMemUnionFsDelete(t *testing.T) {
if err == nil {
t.Fatal("should have disappeared.")
}
r := ufs.Reap()
if r["file"] == nil || r["file"].Info != nil {
t.Errorf("expect 1 deletion reap result: %v", r)
}
}
func TestMemUnionFsBasic(t *testing.T) {
wd, clean := setupMemUfs(t)
wd, _, clean := setupMemUfs(t)
defer clean()
writeToFile(wd+"/mount/rw", "a")
......@@ -184,17 +209,22 @@ func TestMemUnionFsBasic(t *testing.T) {
}
func TestMemUnionFsPromote(t *testing.T) {
wd, clean := setupMemUfs(t)
wd, ufs, clean := setupMemUfs(t)
defer clean()
err := os.Mkdir(wd+"/ro/subdir", 0755)
CheckSuccess(err)
writeToFile(wd+"/ro/subdir/file", "content")
writeToFile(wd+"/mount/subdir/file", "other-content")
r := ufs.Reap()
if r["subdir/file"] == nil || r["subdir/file"].Backing == "" {
t.Errorf("expect 1 file reap result: %v", r)
}
}
func TestMemUnionFsCreate(t *testing.T) {
wd, clean := setupMemUfs(t)
wd, ufs, clean := setupMemUfs(t)
defer clean()
err := os.MkdirAll(wd+"/ro/subdir/sub2", 0755)
......@@ -202,10 +232,15 @@ func TestMemUnionFsCreate(t *testing.T) {
writeToFile(wd+"/mount/subdir/sub2/file", "other-content")
_, err = os.Lstat(wd + "/mount/subdir/sub2/file")
CheckSuccess(err)
r := ufs.Reap()
if r["subdir/sub2/file"] == nil || r["subdir/sub2/file"].Backing == "" {
t.Errorf("expect 1 file reap result: %v", r)
}
}
func TestMemUnionFsOpenUndeletes(t *testing.T) {
wd, clean := setupMemUfs(t)
wd, _, clean := setupMemUfs(t)
defer clean()
writeToFile(wd+"/ro/file", "X")
......@@ -213,11 +248,10 @@ func TestMemUnionFsOpenUndeletes(t *testing.T) {
CheckSuccess(err)
writeToFile(wd+"/mount/file", "X")
_, err = os.Lstat(wd + "/mount/file")
CheckSuccess(err)
}
func TestMemUnionFsMkdir(t *testing.T) {
wd, clean := setupMemUfs(t)
wd, ufs, clean := setupMemUfs(t)
defer clean()
dirname := wd + "/mount/subdir"
......@@ -226,10 +260,15 @@ func TestMemUnionFsMkdir(t *testing.T) {
err = os.Remove(dirname)
CheckSuccess(err)
r := ufs.Reap()
if len(r) > 2 || r[""] == nil || r["subdir"] == nil {
t.Errorf("expect 1 file reap result: %v", r)
}
}
func TestMemUnionFsMkdirPromote(t *testing.T) {
wd, clean := setupMemUfs(t)
wd, ufs, clean := setupMemUfs(t)
defer clean()
dirname := wd + "/ro/subdir/subdir2"
......@@ -238,10 +277,15 @@ func TestMemUnionFsMkdirPromote(t *testing.T) {
err = os.Mkdir(wd+"/mount/subdir/subdir2/dir3", 0755)
CheckSuccess(err)
r := ufs.Reap()
if r["subdir/subdir2/dir3"] == nil || r["subdir/subdir2/dir3"].Info.Mode & fuse.S_IFDIR == 0 {
t.Errorf("expect 1 file reap result: %v", r)
}
}
func TestMemUnionFsRmdirMkdir(t *testing.T) {
wd, clean := setupMemUfs(t)
wd, _, clean := setupMemUfs(t)
defer clean()
err := os.Mkdir(wd+"/ro/subdir", 0755)
......@@ -256,7 +300,7 @@ func TestMemUnionFsRmdirMkdir(t *testing.T) {
}
func TestMemUnionFsLink(t *testing.T) {
wd, clean := setupMemUfs(t)
wd, _, clean := setupMemUfs(t)
defer clean()
content := "blabla"
......@@ -282,7 +326,7 @@ func TestMemUnionFsLink(t *testing.T) {
}
func TestMemUnionFsTruncate(t *testing.T) {
wd, clean := setupMemUfs(t)
wd, _, clean := setupMemUfs(t)
defer clean()
writeToFile(wd+"/ro/file", "hello")
......@@ -295,7 +339,7 @@ func TestMemUnionFsTruncate(t *testing.T) {
func TestMemUnionFsCopyChmod(t *testing.T) {
t.Log("TestCopyChmod")
wd, clean := setupMemUfs(t)
wd, _, clean := setupMemUfs(t)
defer clean()
contents := "hello"
......@@ -321,7 +365,7 @@ func TestMemUnionFsCopyChmod(t *testing.T) {
func TestMemUnionFsTruncateTimestamp(t *testing.T) {
t.Log("TestTruncateTimestamp")
wd, clean := setupMemUfs(t)
wd, _, clean := setupMemUfs(t)
defer clean()
contents := "hello"
......@@ -344,7 +388,7 @@ func TestMemUnionFsTruncateTimestamp(t *testing.T) {
func TestMemUnionFsRemoveAll(t *testing.T) {
t.Log("TestRemoveAll")
wd, clean := setupMemUfs(t)
wd, _, clean := setupMemUfs(t)
defer clean()
err := os.MkdirAll(wd+"/ro/dir/subdir", 0755)
......@@ -368,7 +412,7 @@ func TestMemUnionFsRemoveAll(t *testing.T) {
}
func TestMemUnionFsRmRf(t *testing.T) {
wd, clean := setupMemUfs(t)
wd, _, clean := setupMemUfs(t)
defer clean()
err := os.MkdirAll(wd+"/ro/dir/subdir", 0755)
......@@ -394,7 +438,7 @@ func TestMemUnionFsRmRf(t *testing.T) {
}
func TestMemUnionFsDeletedGetAttr(t *testing.T) {
wd, clean := setupMemUfs(t)
wd, _, clean := setupMemUfs(t)
defer clean()
err := ioutil.WriteFile(wd+"/ro/file", []byte("blabla"), 0644)
......@@ -413,7 +457,7 @@ func TestMemUnionFsDeletedGetAttr(t *testing.T) {
}
func TestMemUnionFsDoubleOpen(t *testing.T) {
wd, clean := setupMemUfs(t)
wd, _, clean := setupMemUfs(t)
defer clean()
err := ioutil.WriteFile(wd+"/ro/file", []byte("blablabla"), 0644)
CheckSuccess(err)
......@@ -451,7 +495,7 @@ func TestMemUnionFsFdLeak(t *testing.T) {
beforeEntries, err := ioutil.ReadDir("/proc/self/fd")
CheckSuccess(err)
wd, clean := setupMemUfs(t)
wd, _, clean := setupMemUfs(t)
err = ioutil.WriteFile(wd+"/ro/file", []byte("blablabla"), 0644)
CheckSuccess(err)
......@@ -472,7 +516,7 @@ func TestMemUnionFsFdLeak(t *testing.T) {
}
func TestMemUnionFsStatFs(t *testing.T) {
wd, clean := setupMemUfs(t)
wd, _, clean := setupMemUfs(t)
defer clean()
s1 := syscall.Statfs_t{}
......@@ -486,7 +530,7 @@ func TestMemUnionFsStatFs(t *testing.T) {
}
func TestMemUnionFsFlushSize(t *testing.T) {
wd, clean := setupMemUfs(t)
wd, _, clean := setupMemUfs(t)
defer clean()
fn := wd + "/mount/file"
......@@ -507,7 +551,7 @@ func TestMemUnionFsFlushSize(t *testing.T) {
}
func TestMemUnionFsFlushRename(t *testing.T) {
wd, clean := setupMemUfs(t)
wd, _, clean := setupMemUfs(t)
defer clean()
err := ioutil.WriteFile(wd+"/mount/file", []byte("x"), 0644)
......@@ -534,7 +578,7 @@ func TestMemUnionFsFlushRename(t *testing.T) {
}
func TestMemUnionFsTruncGetAttr(t *testing.T) {
wd, clean := setupMemUfs(t)
wd, _, clean := setupMemUfs(t)
defer clean()
c := []byte("hello")
......
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