mirror of
https://github.com/hanzoai/visor.git
synced 2026-09-24 17:04:14 +00:00
- Add type parameter Filesystem to vfs/genericfstree, which is required to provide `ancestryMu sync.RWMutex`, and add such a RWMutex to all FSImpls that use genericfstree. - Modify genericfstree.PrependPath() and genericfstree.IsDescendant() to use ancestryMu to ensure atomicity. For callers of genericfstree.PrependPath(), this means that (broader) FSImpl locks no longer need to be held during the call. For callers of genericfstree.IsDescendant(), this means that we can remove documentation warnings about its non-atomicity. - Minor cleanup: Remove useless variable `start`, which is always 0, from MM.ReadMaps/SmapsDataInto(). PiperOrigin-RevId: 696713993
215 lines
5.8 KiB
Go
215 lines
5.8 KiB
Go
// Copyright 2019 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 vfs
|
|
|
|
import (
|
|
"gvisor.dev/gvisor/pkg/context"
|
|
"gvisor.dev/gvisor/pkg/errors/linuxerr"
|
|
"gvisor.dev/gvisor/pkg/fspath"
|
|
"gvisor.dev/gvisor/pkg/sync"
|
|
)
|
|
|
|
var fspathBuilderPool = sync.Pool{
|
|
New: func() any {
|
|
return &fspath.Builder{}
|
|
},
|
|
}
|
|
|
|
func getFSPathBuilder() *fspath.Builder {
|
|
return fspathBuilderPool.Get().(*fspath.Builder)
|
|
}
|
|
|
|
func putFSPathBuilder(b *fspath.Builder) {
|
|
// No methods can be called on b after b.String(), so reset it to its zero
|
|
// value (as returned by fspathBuilderPool.New) instead.
|
|
*b = fspath.Builder{}
|
|
fspathBuilderPool.Put(b)
|
|
}
|
|
|
|
// PathnameWithDeleted returns an absolute pathname to vd, consistent with
|
|
// Linux's d_path(). In particular, if vd.Dentry() has been disowned,
|
|
// PathnameWithDeleted appends " (deleted)" to the returned pathname.
|
|
func (vfs *VirtualFilesystem) PathnameWithDeleted(ctx context.Context, vfsroot, vd VirtualDentry) (string, error) {
|
|
b := getFSPathBuilder()
|
|
defer putFSPathBuilder(b)
|
|
haveRef := false
|
|
defer func() {
|
|
if haveRef {
|
|
vd.DecRef(ctx)
|
|
}
|
|
}()
|
|
|
|
origD := vd.dentry
|
|
loop:
|
|
for {
|
|
err := vd.mount.fs.impl.PrependPath(ctx, vfsroot, vd, b)
|
|
switch err.(type) {
|
|
case nil:
|
|
if vd.mount == vfsroot.mount && vd.mount.root == vfsroot.dentry {
|
|
// genericfstree.PrependPath() will have returned
|
|
// PrependPathAtVFSRootError in this case since it checks
|
|
// against vfsroot before mnt.root, but other implementations
|
|
// of FilesystemImpl.PrependPath() may return nil instead.
|
|
break loop
|
|
}
|
|
nextVD := vfs.getMountpointAt(ctx, vd.mount, vfsroot)
|
|
if !nextVD.Ok() {
|
|
break loop
|
|
}
|
|
if haveRef {
|
|
vd.DecRef(ctx)
|
|
}
|
|
vd = nextVD
|
|
haveRef = true
|
|
// continue loop
|
|
case PrependPathSyntheticError:
|
|
// Skip prepending "/" and appending " (deleted)".
|
|
return b.String(), nil
|
|
case PrependPathAtVFSRootError, PrependPathAtNonMountRootError:
|
|
break loop
|
|
default:
|
|
return "", err
|
|
}
|
|
}
|
|
b.PrependByte('/')
|
|
if origD.IsDead() {
|
|
b.AppendString(" (deleted)")
|
|
}
|
|
return b.String(), nil
|
|
}
|
|
|
|
// PathnameReachable returns an absolute pathname to vd, consistent with
|
|
// Linux's __d_path() (as used by seq_path_root()). If vfsroot.Ok() and vd is
|
|
// not reachable from vfsroot, such that seq_path_root() would return SEQ_SKIP
|
|
// (causing the entire containing entry to be skipped), PathnameReachable
|
|
// returns ("", nil).
|
|
func (vfs *VirtualFilesystem) PathnameReachable(ctx context.Context, vfsroot, vd VirtualDentry) (string, error) {
|
|
b := getFSPathBuilder()
|
|
defer putFSPathBuilder(b)
|
|
haveRef := false
|
|
defer func() {
|
|
if haveRef {
|
|
vd.DecRef(ctx)
|
|
}
|
|
}()
|
|
loop:
|
|
for {
|
|
err := vd.mount.fs.impl.PrependPath(ctx, vfsroot, vd, b)
|
|
switch err.(type) {
|
|
case nil:
|
|
if vd.mount == vfsroot.mount && vd.mount.root == vfsroot.dentry {
|
|
break loop
|
|
}
|
|
nextVD := vfs.getMountpointAt(ctx, vd.mount, vfsroot)
|
|
if !nextVD.Ok() {
|
|
return "", nil
|
|
}
|
|
if haveRef {
|
|
vd.DecRef(ctx)
|
|
}
|
|
vd = nextVD
|
|
haveRef = true
|
|
case PrependPathAtVFSRootError:
|
|
break loop
|
|
case PrependPathAtNonMountRootError, PrependPathSyntheticError:
|
|
return "", nil
|
|
default:
|
|
return "", err
|
|
}
|
|
}
|
|
b.PrependByte('/')
|
|
return b.String(), nil
|
|
}
|
|
|
|
// PathnameInFilesystem returns an absolute path to vd relative to vd's
|
|
// Filesystem root. It also appends //deleted to for disowned entries. It is
|
|
// equivalent to Linux's dentry_path().
|
|
func (vfs *VirtualFilesystem) PathnameInFilesystem(ctx context.Context, vd VirtualDentry) (string, error) {
|
|
b := getFSPathBuilder()
|
|
defer putFSPathBuilder(b)
|
|
if vd.dentry.IsDead() {
|
|
b.PrependString("//deleted")
|
|
}
|
|
if err := vd.mount.fs.impl.PrependPath(ctx, VirtualDentry{}, VirtualDentry{dentry: vd.dentry}, b); err != nil {
|
|
// PrependPath returns an error if it encounters a filesystem root before
|
|
// the provided vfsroot. We don't provide a vfsroot, so encountering this
|
|
// error is expected and can be ignored.
|
|
switch err.(type) {
|
|
case PrependPathAtNonMountRootError:
|
|
default:
|
|
return "", err
|
|
}
|
|
}
|
|
b.PrependByte('/')
|
|
return b.String(), nil
|
|
}
|
|
|
|
// PathnameForGetcwd returns an absolute pathname to vd, consistent with
|
|
// Linux's sys_getcwd().
|
|
func (vfs *VirtualFilesystem) PathnameForGetcwd(ctx context.Context, vfsroot, vd VirtualDentry) (string, error) {
|
|
if vd.dentry.IsDead() {
|
|
return "", linuxerr.ENOENT
|
|
}
|
|
|
|
b := getFSPathBuilder()
|
|
defer putFSPathBuilder(b)
|
|
haveRef := false
|
|
defer func() {
|
|
if haveRef {
|
|
vd.DecRef(ctx)
|
|
}
|
|
}()
|
|
unreachable := false
|
|
loop:
|
|
for {
|
|
err := vd.mount.fs.impl.PrependPath(ctx, vfsroot, vd, b)
|
|
switch err.(type) {
|
|
case nil:
|
|
if vd.mount == vfsroot.mount && vd.mount.root == vfsroot.dentry {
|
|
break loop
|
|
}
|
|
nextVD := vfs.getMountpointAt(ctx, vd.mount, vfsroot)
|
|
if !nextVD.Ok() {
|
|
unreachable = true
|
|
break loop
|
|
}
|
|
if haveRef {
|
|
vd.DecRef(ctx)
|
|
}
|
|
vd = nextVD
|
|
haveRef = true
|
|
case PrependPathAtVFSRootError:
|
|
break loop
|
|
case PrependPathAtNonMountRootError, PrependPathSyntheticError:
|
|
unreachable = true
|
|
break loop
|
|
default:
|
|
return "", err
|
|
}
|
|
}
|
|
b.PrependByte('/')
|
|
if unreachable {
|
|
b.PrependString("(unreachable)")
|
|
}
|
|
return b.String(), nil
|
|
}
|
|
|
|
// As of this writing, we do not have equivalents to:
|
|
//
|
|
// - d_absolute_path(), which returns EINVAL if (effectively) any call to
|
|
// FilesystemImpl.PrependPath() would return PrependPathAtNonMountRootError.
|
|
//
|
|
// These should be added as necessary.
|