diff options
Diffstat (limited to 'pkg/sentry/fs/gofer/cache_policy.go')
-rw-r--r-- | pkg/sentry/fs/gofer/cache_policy.go | 64 |
1 files changed, 54 insertions, 10 deletions
diff --git a/pkg/sentry/fs/gofer/cache_policy.go b/pkg/sentry/fs/gofer/cache_policy.go index fa8abf51c..98f43c578 100644 --- a/pkg/sentry/fs/gofer/cache_policy.go +++ b/pkg/sentry/fs/gofer/cache_policy.go @@ -17,6 +17,7 @@ package gofer import ( "fmt" + "gvisor.googlesource.com/gvisor/pkg/sentry/context" "gvisor.googlesource.com/gvisor/pkg/sentry/fs" ) @@ -108,25 +109,68 @@ func (cp cachePolicy) writeThrough(inode *fs.Inode) bool { return cp == cacheNone || cp == cacheAllWritethrough } -// revalidateDirent indicates that a dirent should be revalidated after a -// lookup, because the looked up version may be stale. -func (cp cachePolicy) revalidateDirent() bool { +// revalidate revalidates the child Inode if the cache policy allows it. +// +// Depending on the cache policy, revalidate will walk from the parent to the +// child inode, and if any unstable attributes have changed, will update the +// cached attributes on the child inode. If the walk fails, or the returned +// inode id is different from the one being revalidated, then the entire Dirent +// must be reloaded. +func (cp cachePolicy) revalidate(ctx context.Context, name string, parent, child *fs.Inode) bool { if cp == cacheAll || cp == cacheAllWritethrough { return false } - // TODO: The cacheRemoteRevalidating policy should only - // return true if the remote file's attributes have changed. - return true + if cp == cacheNone { + return true + } + + childIops, ok := child.InodeOperations.(*inodeOperations) + if !ok { + panic(fmt.Sprintf("revalidating inode operations of unknown type %T", child.InodeOperations)) + } + parentIops, ok := parent.InodeOperations.(*inodeOperations) + if !ok { + panic(fmt.Sprintf("revalidating inode operations with parent of unknown type %T", parent.InodeOperations)) + } + + // Walk from parent to child again. + // + // TODO: If we have a directory FD in the parent + // inodeOperations, then we can use fstatat(2) to get the inode + // attributes instead of making this RPC. + qids, _, mask, attr, err := parentIops.fileState.file.walkGetAttr(ctx, []string{name}) + if err != nil { + // Can't look up the name. Trigger reload. + return true + } + + // If the Path has changed, then we are not looking at the file file. + // We must reload. + if qids[0].Path != childIops.fileState.key.Inode { + return true + } + + // If we are not caching unstable attrs, then there is nothing to + // update on this inode. + if !cp.cacheUAttrs(child) { + return false + } + + // Update the inode's cached unstable attrs. + s := childIops.session() + childIops.cachingInodeOps.UpdateUnstable(unstable(ctx, mask, attr, s.mounter, s.client)) + + return false } -// keepDirent indicates that dirents should be kept pinned in the dirent tree -// even if there are no application references on the file. -func (cp cachePolicy) keepDirent(inode *fs.Inode) bool { +// keep indicates that dirents should be kept pinned in the dirent tree even if +// there are no application references on the file. +func (cp cachePolicy) keep(d *fs.Dirent) bool { if cp == cacheNone { return false } - sattr := inode.StableAttr + sattr := d.Inode.StableAttr // NOTE: Only cache files, directories, and symlinks. return fs.IsFile(sattr) || fs.IsDir(sattr) || fs.IsSymlink(sattr) } |