diff --git a/pkg/sentry/fsimpl/gofer/lisafs_dentry.go b/pkg/sentry/fsimpl/gofer/lisafs_dentry.go index e210658f8..578f429b2 100644 --- a/pkg/sentry/fsimpl/gofer/lisafs_dentry.go +++ b/pkg/sentry/fsimpl/gofer/lisafs_dentry.go @@ -286,23 +286,24 @@ func (d *lisafsDentry) getRemoteChild(ctx context.Context, name string) (*dentry // - fs.renameMu must be locked. // - d.opMu must be locked. // - d.isDir(). -// - !rp.Done(). +// - !rp.Done() && rp.Component() is not "." or "..". // // Postcondition: The returned dentry is already cached appropriately. func (d *lisafsDentry) getRemoteChildAndWalkPathLocked(ctx context.Context, rp *vfs.ResolvingPath, ds **[]*dentry) (*dentry, error) { - // Walk as much of the path as possible in 1 RPC. - // Note that pit is a copy of the iterator that does not affect rp. - var names []string - for pit := rp.Pit(); pit.Ok(); pit = pit.Next() { - name := pit.String() + // Collect as many path components as possible to walk. + var namesArr [16]string // arbitrarily sized array to help avoid slice allocation. + names := namesArr[:0] + rp.GetComponents(func(name string) bool { if name == "." { - continue + return true } if name == ".." { - break + return false } names = append(names, name) - } + return true + }) + // Walk as much of the path as possible in 1 RPC. _, inodes, err := d.controlFD.WalkMultiple(ctx, names) if err != nil { return nil, err diff --git a/pkg/sentry/vfs/resolving_path.go b/pkg/sentry/vfs/resolving_path.go index 710e87a93..ad059a6f7 100644 --- a/pkg/sentry/vfs/resolving_path.go +++ b/pkg/sentry/vfs/resolving_path.go @@ -223,12 +223,6 @@ func (rp *ResolvingPath) Final() bool { return rp.curPart == 0 && !rp.pit.NextOk() } -// Pit returns a copy of rp's current path iterator. Modifying the iterator -// does not change rp. -func (rp *ResolvingPath) Pit() fspath.Iterator { - return rp.pit -} - // Component returns the current path component in the stream represented by // rp. // @@ -260,6 +254,24 @@ func (rp *ResolvingPath) Advance() { } } +// GetComponents emits all the remaining path components in rp. It does *not* +// update rp state. It halts if emit() returns false. +func (rp *ResolvingPath) GetComponents(emit func(string) bool) { + // Copy rp state. + cur := rp.pit + curPart := rp.curPart + for cur.Ok() { + if !emit(cur.String()) { + break + } + cur = cur.Next() + if !cur.Ok() && curPart > 0 { + curPart-- + cur = rp.parts[curPart] + } + } +} + // CheckRoot is called before resolving the parent of the Dentry d. If the // Dentry is contextually a VFS root, such that path resolution should treat // d's parent as itself, CheckRoot returns (true, nil). If the Dentry is the