summaryrefslogtreecommitdiffhomepage
path: root/pkg/sentry/fsimpl/gofer/revalidate.go
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/sentry/fsimpl/gofer/revalidate.go')
-rw-r--r--pkg/sentry/fsimpl/gofer/revalidate.go386
1 files changed, 0 insertions, 386 deletions
diff --git a/pkg/sentry/fsimpl/gofer/revalidate.go b/pkg/sentry/fsimpl/gofer/revalidate.go
deleted file mode 100644
index 8f81f0822..000000000
--- a/pkg/sentry/fsimpl/gofer/revalidate.go
+++ /dev/null
@@ -1,386 +0,0 @@
-// Copyright 2021 The gVisor Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package gofer
-
-import (
- "gvisor.dev/gvisor/pkg/context"
- "gvisor.dev/gvisor/pkg/sentry/vfs"
- "gvisor.dev/gvisor/pkg/sync"
-)
-
-type errPartialRevalidation struct{}
-
-// Error implements error.Error.
-func (errPartialRevalidation) Error() string {
- return "partial revalidation"
-}
-
-type errRevalidationStepDone struct{}
-
-// Error implements error.Error.
-func (errRevalidationStepDone) Error() string {
- return "stop revalidation"
-}
-
-// revalidatePath checks cached dentries for external modification. File
-// attributes are refreshed and cache is invalidated in case the dentry has been
-// deleted, or a new file/directory created in its place.
-//
-// Revalidation stops at symlinks and mount points. The caller is responsible
-// for revalidating again after symlinks are resolved and after changing to
-// different mounts.
-//
-// Preconditions:
-// * fs.renameMu must be locked.
-func (fs *filesystem) revalidatePath(ctx context.Context, rpOrig *vfs.ResolvingPath, start *dentry, ds **[]*dentry) error {
- // Revalidation is done even if start is synthetic in case the path is
- // something like: ../non_synthetic_file.
- if fs.opts.interop != InteropModeShared {
- return nil
- }
-
- // Copy resolving path to walk the path for revalidation.
- rp := rpOrig.Copy()
- err := fs.revalidate(ctx, rp, start, rp.Done, ds)
- rp.Release(ctx)
- return err
-}
-
-// revalidateParentDir does the same as revalidatePath, but stops at the parent.
-//
-// Preconditions:
-// * fs.renameMu must be locked.
-func (fs *filesystem) revalidateParentDir(ctx context.Context, rpOrig *vfs.ResolvingPath, start *dentry, ds **[]*dentry) error {
- // Revalidation is done even if start is synthetic in case the path is
- // something like: ../non_synthetic_file and parent is non synthetic.
- if fs.opts.interop != InteropModeShared {
- return nil
- }
-
- // Copy resolving path to walk the path for revalidation.
- rp := rpOrig.Copy()
- err := fs.revalidate(ctx, rp, start, rp.Final, ds)
- rp.Release(ctx)
- return err
-}
-
-// revalidateOne does the same as revalidatePath, but checks a single dentry.
-//
-// Preconditions:
-// * fs.renameMu must be locked.
-func (fs *filesystem) revalidateOne(ctx context.Context, vfsObj *vfs.VirtualFilesystem, parent *dentry, name string, ds **[]*dentry) error {
- // Skip revalidation for interop mode different than InteropModeShared or
- // if the parent is synthetic (child must be synthetic too, but it cannot be
- // replaced without first replacing the parent).
- if parent.cachedMetadataAuthoritative() {
- return nil
- }
-
- parent.dirMu.Lock()
- child, ok := parent.children[name]
- parent.dirMu.Unlock()
- if !ok {
- return nil
- }
-
- state := makeRevalidateState(parent)
- defer state.release()
-
- state.add(name, child)
- return fs.revalidateHelper(ctx, vfsObj, state, ds)
-}
-
-// revalidate revalidates path components in rp until done returns true, or
-// until a mount point or symlink is reached. It may send multiple MultiGetAttr
-// calls to the gofer to handle ".." in the path.
-//
-// Preconditions:
-// * fs.renameMu must be locked.
-// * InteropModeShared is in effect.
-func (fs *filesystem) revalidate(ctx context.Context, rp *vfs.ResolvingPath, start *dentry, done func() bool, ds **[]*dentry) error {
- state := makeRevalidateState(start)
- defer state.release()
-
- // Skip synthetic dentries because the start dentry cannot be replaced in case
- // it has been created in the remote file system.
- if !start.isSynthetic() {
- state.add("", start)
- }
-
-done:
- for cur := start; !done(); {
- var err error
- cur, err = fs.revalidateStep(ctx, rp, cur, state)
- if err != nil {
- switch err.(type) {
- case errPartialRevalidation:
- if err := fs.revalidateHelper(ctx, rp.VirtualFilesystem(), state, ds); err != nil {
- return err
- }
-
- // Reset state to release any remaining locks and restart from where
- // stepping stopped.
- state.reset()
- state.start = cur
-
- // Skip synthetic dentries because the start dentry cannot be replaced in
- // case it has been created in the remote file system.
- if !cur.isSynthetic() {
- state.add("", cur)
- }
-
- case errRevalidationStepDone:
- break done
-
- default:
- return err
- }
- }
- }
- return fs.revalidateHelper(ctx, rp.VirtualFilesystem(), state, ds)
-}
-
-// revalidateStep walks one element of the path and updates revalidationState
-// with the dentry if needed. It may also stop the stepping or ask for a
-// partial revalidation. Partial revalidation requires the caller to revalidate
-// the current revalidationState, release all locks, and resume stepping.
-// In case a symlink is hit, revalidation stops and the caller is responsible
-// for calling revalidate again after the symlink is resolved. Revalidation may
-// also stop for other reasons, like hitting a child not in the cache.
-//
-// Returns:
-// * (dentry, nil): step worked, continue stepping.`
-// * (dentry, errPartialRevalidation): revalidation should be done with the
-// state gathered so far. Then continue stepping with the remainder of the
-// path, starting at `dentry`.
-// * (nil, errRevalidationStepDone): revalidation doesn't need to step any
-// further. It hit a symlink, a mount point, or an uncached dentry.
-//
-// Preconditions:
-// * fs.renameMu must be locked.
-// * !rp.Done().
-// * InteropModeShared is in effect (assumes no negative dentries).
-func (fs *filesystem) revalidateStep(ctx context.Context, rp *vfs.ResolvingPath, d *dentry, state *revalidateState) (*dentry, error) {
- switch name := rp.Component(); name {
- case ".":
- // Do nothing.
-
- case "..":
- // Partial revalidation is required when ".." is hit because metadata locks
- // can only be acquired from parent to child to avoid deadlocks.
- if isRoot, err := rp.CheckRoot(ctx, &d.vfsd); err != nil {
- return nil, errRevalidationStepDone{}
- } else if isRoot || d.parent == nil {
- rp.Advance()
- return d, errPartialRevalidation{}
- }
- // We must assume that d.parent is correct, because if d has been moved
- // elsewhere in the remote filesystem so that its parent has changed,
- // we have no way of determining its new parent's location in the
- // filesystem.
- //
- // Call rp.CheckMount() before updating d.parent's metadata, since if
- // we traverse to another mount then d.parent's metadata is irrelevant.
- if err := rp.CheckMount(ctx, &d.parent.vfsd); err != nil {
- return nil, errRevalidationStepDone{}
- }
- rp.Advance()
- return d.parent, errPartialRevalidation{}
-
- default:
- d.dirMu.Lock()
- child, ok := d.children[name]
- d.dirMu.Unlock()
- if !ok {
- // child is not cached, no need to validate any further.
- return nil, errRevalidationStepDone{}
- }
-
- state.add(name, child)
-
- // Symlink must be resolved before continuing with revalidation.
- if child.isSymlink() {
- return nil, errRevalidationStepDone{}
- }
-
- d = child
- }
-
- rp.Advance()
- return d, nil
-}
-
-// revalidateHelper calls the gofer to stat all dentries in `state`. It will
-// update or invalidate dentries in the cache based on the result.
-//
-// Preconditions:
-// * fs.renameMu must be locked.
-// * InteropModeShared is in effect.
-func (fs *filesystem) revalidateHelper(ctx context.Context, vfsObj *vfs.VirtualFilesystem, state *revalidateState, ds **[]*dentry) error {
- if len(state.names) == 0 {
- return nil
- }
- // Lock metadata on all dentries *before* getting attributes for them.
- state.lockAllMetadata()
- stats, err := state.start.file.multiGetAttr(ctx, state.names)
- if err != nil {
- return err
- }
-
- i := -1
- for d := state.popFront(); d != nil; d = state.popFront() {
- i++
- found := i < len(stats)
- if i == 0 && len(state.names[0]) == 0 {
- if found && !d.isSynthetic() {
- // First dentry is where the search is starting, just update attributes
- // since it cannot be replaced.
- d.updateFromP9AttrsLocked(stats[i].Valid, &stats[i].Attr)
- }
- d.metadataMu.Unlock()
- continue
- }
-
- // Note that synthetic dentries will always fails the comparison check
- // below.
- if !found || d.qidPath != stats[i].QID.Path {
- d.metadataMu.Unlock()
- if !found && d.isSynthetic() {
- // We have a synthetic file, and no remote file has arisen to replace
- // it.
- return nil
- }
- // The file at this path has changed or no longer exists. Mark the
- // dentry invalidated, and re-evaluate its caching status (i.e. if it
- // has 0 references, drop it). The dentry will be reloaded next time it's
- // accessed.
- vfsObj.InvalidateDentry(ctx, &d.vfsd)
-
- name := state.names[i]
- d.parent.dirMu.Lock()
-
- if d.isSynthetic() {
- // Normally we don't mark invalidated dentries as deleted since
- // they may still exist (but at a different path), and also for
- // consistency with Linux. However, synthetic files are guaranteed
- // to become unreachable if their dentries are invalidated, so
- // treat their invalidation as deletion.
- d.setDeleted()
- d.decRefNoCaching()
- *ds = appendDentry(*ds, d)
-
- d.parent.syntheticChildren--
- d.parent.dirents = nil
- }
-
- // Since the dirMu was released and reacquired, re-check that the
- // parent's child with this name is still the same. Do not touch it if
- // it has been replaced with a different one.
- if child := d.parent.children[name]; child == d {
- // Invalidate dentry so it gets reloaded next time it's accessed.
- delete(d.parent.children, name)
- }
- d.parent.dirMu.Unlock()
-
- return nil
- }
-
- // The file at this path hasn't changed. Just update cached metadata.
- d.updateFromP9AttrsLocked(stats[i].Valid, &stats[i].Attr)
- d.metadataMu.Unlock()
- }
-
- return nil
-}
-
-// revalidateStatePool caches revalidateState instances to save array
-// allocations for dentries and names.
-var revalidateStatePool = sync.Pool{
- New: func() interface{} {
- return &revalidateState{}
- },
-}
-
-// revalidateState keeps state related to a revalidation request. It keeps track
-// of {name, dentry} list being revalidated, as well as metadata locks on the
-// dentries. The list must be in ancestry order, in other words `n` must be
-// `n-1` child.
-type revalidateState struct {
- // start is the dentry where to start the attributes search.
- start *dentry
-
- // List of names of entries to refresh attributes. Names length must be the
- // same as detries length. They are kept in separate slices because names is
- // used to call File.MultiGetAttr().
- names []string
-
- // dentries is the list of dentries that correspond to the names above.
- // dentry.metadataMu is acquired as each dentry is added to this list.
- dentries []*dentry
-
- // locked indicates if metadata lock has been acquired on dentries.
- locked bool
-}
-
-func makeRevalidateState(start *dentry) *revalidateState {
- r := revalidateStatePool.Get().(*revalidateState)
- r.start = start
- return r
-}
-
-// release must be called after the caller is done with this object. It releases
-// all metadata locks and resources.
-func (r *revalidateState) release() {
- r.reset()
- revalidateStatePool.Put(r)
-}
-
-// Preconditions:
-// * d is a descendant of all dentries in r.dentries.
-func (r *revalidateState) add(name string, d *dentry) {
- r.names = append(r.names, name)
- r.dentries = append(r.dentries, d)
-}
-
-func (r *revalidateState) lockAllMetadata() {
- for _, d := range r.dentries {
- d.metadataMu.Lock()
- }
- r.locked = true
-}
-
-func (r *revalidateState) popFront() *dentry {
- if len(r.dentries) == 0 {
- return nil
- }
- d := r.dentries[0]
- r.dentries = r.dentries[1:]
- return d
-}
-
-// reset releases all metadata locks and resets all fields to allow this
-// instance to be reused.
-func (r *revalidateState) reset() {
- if r.locked {
- // Unlock any remaining dentries.
- for _, d := range r.dentries {
- d.metadataMu.Unlock()
- }
- r.locked = false
- }
- r.start = nil
- r.names = r.names[:0]
- r.dentries = r.dentries[:0]
-}