mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Add vfs.Pathname{WithDeleted,ForGetcwd}.
The former is needed for vfs.FileDescription to implement memmap.MappingIdentity, and the latter is needed to implement getcwd(2). PiperOrigin-RevId: 285051855
This commit is contained in:
@@ -38,6 +38,7 @@ go_library(
|
||||
"//pkg/abi/linux",
|
||||
"//pkg/binary",
|
||||
"//pkg/fd",
|
||||
"//pkg/fspath",
|
||||
"//pkg/log",
|
||||
"//pkg/sentry/arch",
|
||||
"//pkg/sentry/context",
|
||||
|
||||
@@ -20,6 +20,7 @@ import (
|
||||
"sync"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/abi/linux"
|
||||
"gvisor.dev/gvisor/pkg/fspath"
|
||||
"gvisor.dev/gvisor/pkg/sentry/context"
|
||||
"gvisor.dev/gvisor/pkg/sentry/fsimpl/ext/disklayout"
|
||||
"gvisor.dev/gvisor/pkg/sentry/vfs"
|
||||
@@ -441,3 +442,10 @@ func (fs *filesystem) UnlinkAt(ctx context.Context, rp *vfs.ResolvingPath) error
|
||||
|
||||
return syserror.EROFS
|
||||
}
|
||||
|
||||
// PrependPath implements vfs.FilesystemImpl.PrependPath.
|
||||
func (fs *filesystem) PrependPath(ctx context.Context, vfsroot, vd vfs.VirtualDentry, b *fspath.Builder) error {
|
||||
fs.mu.RLock()
|
||||
defer fs.mu.RUnlock()
|
||||
return vfs.GenericPrependPath(vfsroot, vd, b)
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ go_library(
|
||||
deps = [
|
||||
"//pkg/abi/linux",
|
||||
"//pkg/amutex",
|
||||
"//pkg/fspath",
|
||||
"//pkg/sentry/arch",
|
||||
"//pkg/sentry/context",
|
||||
"//pkg/sentry/kernel/auth",
|
||||
|
||||
@@ -19,6 +19,7 @@ import (
|
||||
"sync/atomic"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/abi/linux"
|
||||
"gvisor.dev/gvisor/pkg/fspath"
|
||||
"gvisor.dev/gvisor/pkg/sentry/context"
|
||||
"gvisor.dev/gvisor/pkg/sentry/vfs"
|
||||
"gvisor.dev/gvisor/pkg/syserror"
|
||||
@@ -582,3 +583,10 @@ func (fs *filesystem) UnlinkAt(ctx context.Context, rp *vfs.ResolvingPath) error
|
||||
inode.decLinksLocked()
|
||||
return nil
|
||||
}
|
||||
|
||||
// PrependPath implements vfs.FilesystemImpl.PrependPath.
|
||||
func (fs *filesystem) PrependPath(ctx context.Context, vfsroot, vd vfs.VirtualDentry, b *fspath.Builder) error {
|
||||
fs.mu.RLock()
|
||||
defer fs.mu.RUnlock()
|
||||
return vfs.GenericPrependPath(vfsroot, vd, b)
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ go_library(
|
||||
"mount.go",
|
||||
"mount_unsafe.go",
|
||||
"options.go",
|
||||
"pathname.go",
|
||||
"permissions.go",
|
||||
"resolving_path.go",
|
||||
"testutil.go",
|
||||
|
||||
@@ -18,6 +18,7 @@ import (
|
||||
"sync/atomic"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/abi/linux"
|
||||
"gvisor.dev/gvisor/pkg/fspath"
|
||||
"gvisor.dev/gvisor/pkg/sentry/context"
|
||||
)
|
||||
|
||||
@@ -185,5 +186,56 @@ type FilesystemImpl interface {
|
||||
// UnlinkAt removes the non-directory file at rp.
|
||||
UnlinkAt(ctx context.Context, rp *ResolvingPath) error
|
||||
|
||||
// TODO: d_path(); extended attributes; inotify_add_watch(); bind()
|
||||
// PrependPath prepends a path from vd to vd.Mount().Root() to b.
|
||||
//
|
||||
// If vfsroot.Ok(), it is the contextual VFS root; if it is encountered
|
||||
// before vd.Mount().Root(), PrependPath should stop prepending path
|
||||
// components and return a PrependPathAtVFSRootError.
|
||||
//
|
||||
// If traversal of vd.Dentry()'s ancestors encounters an independent
|
||||
// ("root") Dentry that is not vd.Mount().Root() (i.e. vd.Dentry() is not a
|
||||
// descendant of vd.Mount().Root()), PrependPath should stop prepending
|
||||
// path components and return a PrependPathAtNonMountRootError.
|
||||
//
|
||||
// Filesystems for which Dentries do not have meaningful paths may prepend
|
||||
// an arbitrary descriptive string to b and then return a
|
||||
// PrependPathSyntheticError.
|
||||
//
|
||||
// Most implementations can acquire the appropriate locks to ensure that
|
||||
// Dentry.Name() and Dentry.Parent() are fixed for vd.Dentry() and all of
|
||||
// its ancestors, then call GenericPrependPath.
|
||||
//
|
||||
// Preconditions: vd.Mount().Filesystem().Impl() == this FilesystemImpl.
|
||||
PrependPath(ctx context.Context, vfsroot, vd VirtualDentry, b *fspath.Builder) error
|
||||
|
||||
// TODO: extended attributes; inotify_add_watch(); bind()
|
||||
}
|
||||
|
||||
// PrependPathAtVFSRootError is returned by implementations of
|
||||
// FilesystemImpl.PrependPath() when they encounter the contextual VFS root.
|
||||
type PrependPathAtVFSRootError struct{}
|
||||
|
||||
// Error implements error.Error.
|
||||
func (PrependPathAtVFSRootError) Error() string {
|
||||
return "vfs.FilesystemImpl.PrependPath() reached VFS root"
|
||||
}
|
||||
|
||||
// PrependPathAtNonMountRootError is returned by implementations of
|
||||
// FilesystemImpl.PrependPath() when they encounter an independent ancestor
|
||||
// Dentry that is not the Mount root.
|
||||
type PrependPathAtNonMountRootError struct{}
|
||||
|
||||
// Error implements error.Error.
|
||||
func (PrependPathAtNonMountRootError) Error() string {
|
||||
return "vfs.FilesystemImpl.PrependPath() reached root other than Mount root"
|
||||
}
|
||||
|
||||
// PrependPathSyntheticError is returned by implementations of
|
||||
// FilesystemImpl.PrependPath() for which prepended names do not represent real
|
||||
// paths.
|
||||
type PrependPathSyntheticError struct{}
|
||||
|
||||
// Error implements error.Error.
|
||||
func (PrependPathSyntheticError) Error() string {
|
||||
return "vfs.FilesystemImpl.PrependPath() prepended synthetic name"
|
||||
}
|
||||
|
||||
@@ -16,6 +16,8 @@ package vfs
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/fspath"
|
||||
)
|
||||
|
||||
// GenericParseMountOptions parses a comma-separated list of options of the
|
||||
@@ -41,3 +43,27 @@ func GenericParseMountOptions(str string) map[string]string {
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
// GenericPrependPath may be used by implementations of
|
||||
// FilesystemImpl.PrependPath() for which a single statically-determined lock
|
||||
// or set of locks is sufficient to ensure its preconditions (as opposed to
|
||||
// e.g. per-Dentry locks).
|
||||
//
|
||||
// Preconditions: Dentry.Name() and Dentry.Parent() must be held constant for
|
||||
// vd.Dentry() and all of its ancestors.
|
||||
func GenericPrependPath(vfsroot, vd VirtualDentry, b *fspath.Builder) error {
|
||||
mnt, d := vd.mount, vd.dentry
|
||||
for {
|
||||
if mnt == vfsroot.mount && d == vfsroot.dentry {
|
||||
return PrependPathAtVFSRootError{}
|
||||
}
|
||||
if d == mnt.root {
|
||||
return nil
|
||||
}
|
||||
if d.parent == nil {
|
||||
return PrependPathAtNonMountRootError{}
|
||||
}
|
||||
b.PrependComponent(d.name)
|
||||
d = d.parent
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,153 @@
|
||||
// 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 (
|
||||
"sync"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/fspath"
|
||||
"gvisor.dev/gvisor/pkg/sentry/context"
|
||||
"gvisor.dev/gvisor/pkg/syserror"
|
||||
)
|
||||
|
||||
var fspathBuilderPool = sync.Pool{
|
||||
New: func() interface{} {
|
||||
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()
|
||||
}
|
||||
}()
|
||||
|
||||
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 {
|
||||
// GenericPrependPath() 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(vd.mount, vfsroot)
|
||||
if !nextVD.Ok() {
|
||||
break loop
|
||||
}
|
||||
if haveRef {
|
||||
vd.DecRef()
|
||||
}
|
||||
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.IsDisowned() {
|
||||
b.AppendString(" (deleted)")
|
||||
}
|
||||
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.IsDisowned() {
|
||||
return "", syserror.ENOENT
|
||||
}
|
||||
|
||||
b := getFSPathBuilder()
|
||||
defer putFSPathBuilder(b)
|
||||
haveRef := false
|
||||
defer func() {
|
||||
if haveRef {
|
||||
vd.DecRef()
|
||||
}
|
||||
}()
|
||||
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(vd.mount, vfsroot)
|
||||
if !nextVD.Ok() {
|
||||
unreachable = true
|
||||
break loop
|
||||
}
|
||||
if haveRef {
|
||||
vd.DecRef()
|
||||
}
|
||||
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.
|
||||
//
|
||||
// - dentry_path(), which does not walk up mounts (and only returns the path
|
||||
// relative to Filesystem root), but also appends "//deleted" for disowned
|
||||
// Dentries.
|
||||
//
|
||||
// These should be added as necessary.
|
||||
@@ -15,7 +15,10 @@
|
||||
package vfs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/abi/linux"
|
||||
"gvisor.dev/gvisor/pkg/fspath"
|
||||
"gvisor.dev/gvisor/pkg/sentry/context"
|
||||
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
|
||||
"gvisor.dev/gvisor/pkg/syserror"
|
||||
@@ -114,6 +117,12 @@ func (fs *FDTestFilesystem) UnlinkAt(ctx context.Context, rp *ResolvingPath) err
|
||||
return syserror.EPERM
|
||||
}
|
||||
|
||||
// PrependPath implements FilesystemImpl.PrependPath.
|
||||
func (fs *FDTestFilesystem) PrependPath(ctx context.Context, vfsroot, vd VirtualDentry, b *fspath.Builder) error {
|
||||
b.PrependComponent(fmt.Sprintf("vfs.fdTestDentry:%p", vd.dentry.impl.(*fdTestDentry)))
|
||||
return PrependPathSyntheticError{}
|
||||
}
|
||||
|
||||
type fdTestDentry struct {
|
||||
vfsd Dentry
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user