Commit 14552a22 authored by Han-Wen Nienhuys's avatar Han-Wen Nienhuys

Forget client inodes on user-initiated cache drop.

parent 7ce43a45
......@@ -99,6 +99,21 @@ func (me *Inode) Children() (out map[string]*Inode) {
return out
}
// FsChildren returns all the children from the same filesystem. It
// will skip mountpoints.
func (me *Inode) FsChildren() (out map[string]*Inode) {
me.treeLock.RLock()
defer me.treeLock.RUnlock()
out = map[string]*Inode{}
for k, v := range me.children {
if v.mount == me.mount {
out[k] = v
}
}
return out
}
func (me *Inode) FsNode() FsNode {
return me.fsInode
}
......
......@@ -300,6 +300,31 @@ func TestLinkExisting(t *testing.T) {
}
}
// Deal correctly with hard links implied by matching client inode
// numbers.
func TestLinkForget(t *testing.T) {
me := NewTestCase(t)
defer me.Cleanup()
c := "hello"
err := ioutil.WriteFile(me.orig+"/file1", []byte(c), 0644)
CheckSuccess(err)
err = os.Link(me.orig+"/file1", me.orig+"/file2")
CheckSuccess(err)
f1, err := os.Lstat(me.mnt + "/file1")
CheckSuccess(err)
me.pathFs.ForgetClientInodes()
f2, err := os.Lstat(me.mnt + "/file2")
CheckSuccess(err)
if f1.Ino == f2.Ino {
t.Error("After forget, we should not export links")
}
}
func TestSymlink(t *testing.T) {
me := NewTestCase(t)
defer me.Cleanup()
......
......@@ -59,6 +59,26 @@ func (me *PathNodeFs) Mount(path string, nodeFs NodeFileSystem, opts *FileSystem
return me.connector.Mount(parent, name, nodeFs, opts)
}
// Forgets all known information on client inodes.
func (me *PathNodeFs) ForgetClientInodes() {
if !me.options.ClientInodes {
return
}
me.pathLock.Lock()
defer me.pathLock.Unlock()
me.clientInodeMap = map[uint64][]*clientInodePath{}
me.root.forgetClientInodes()
}
// Rereads all inode numbers for all known files.
func (me *PathNodeFs) RereadClientInodes() {
if !me.options.ClientInodes {
return
}
me.ForgetClientInodes()
me.root.updateClientInodes()
}
func (me *PathNodeFs) UnmountNode(node *Inode) Status {
return me.connector.Unmount(node)
}
......@@ -71,6 +91,7 @@ func (me *PathNodeFs) Unmount(path string) Status {
return me.connector.Unmount(node)
}
func (me *PathNodeFs) OnUnmount() {
}
......@@ -187,6 +208,22 @@ type pathInode struct {
DefaultFsNode
}
// Drop all known client inodes. Must have the treeLock.
func (me *pathInode) forgetClientInodes() {
me.clientInode = 0
for _, ch := range me.Inode().FsChildren() {
ch.FsNode().(*pathInode).forgetClientInodes()
}
}
// Reread all client nodes below this node. Must run outside the treeLock.
func (me *pathInode) updateClientInodes() {
me.GetAttr(nil, nil)
for _, ch := range me.Inode().FsChildren() {
ch.FsNode().(*pathInode).updateClientInodes()
}
}
func (me *pathInode) LockTree() func() {
me.pathFs.pathLock.Lock()
return func() { me.pathFs.pathLock.Unlock() }
......
......@@ -917,6 +917,7 @@ func (me *UnionFs) Open(name string, flags uint32, context *fuse.Context) (fuseF
if name == _DROP_CACHE {
if flags&fuse.O_ANYWRITE != 0 {
log.Println("Forced cache drop on", me.Name())
me.nodeFs.RereadClientInodes()
me.DropBranchCache(nil)
me.DropDeletionCache()
me.DropSubFsCaches()
......
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