diff options
Diffstat (limited to 'pkg/sentry/fsimpl/gofer/revalidate.go')
-rw-r--r-- | pkg/sentry/fsimpl/gofer/revalidate.go | 386 |
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] -} |