Plumb VFS2 inside the Sentry

- Added fsbridge package with interface that can be used to open
  and read from VFS1 and VFS2 files.
- Converted ELF loader to use fsbridge
- Added VFS2 types to FSContext
- Added vfs.MountNamespace to ThreadGroup

Updates #1623

PiperOrigin-RevId: 295183950
This commit is contained in:
gVisor bot
2020-02-14 11:12:47 -08:00
parent b2e86906ea
commit 4075de11be
52 changed files with 1133 additions and 321 deletions
+5
View File
@@ -16,10 +16,13 @@ go_library(
],
deps = [
"//pkg/abi/linux",
"//pkg/context",
"//pkg/fd",
"//pkg/fspath",
"//pkg/log",
"//pkg/sentry/fs",
"//pkg/sentry/fs/host",
"//pkg/sentry/fsbridge",
"//pkg/sentry/kernel",
"//pkg/sentry/kernel/auth",
"//pkg/sentry/kernel/time",
@@ -27,8 +30,10 @@ go_library(
"//pkg/sentry/state",
"//pkg/sentry/strace",
"//pkg/sentry/usage",
"//pkg/sentry/vfs",
"//pkg/sentry/watchdog",
"//pkg/sync",
"//pkg/syserror",
"//pkg/tcpip/link/sniffer",
"//pkg/urpc",
],
+117 -10
View File
@@ -18,19 +18,26 @@ import (
"bytes"
"encoding/json"
"fmt"
"path"
"sort"
"strings"
"text/tabwriter"
"time"
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
"gvisor.dev/gvisor/pkg/fspath"
"gvisor.dev/gvisor/pkg/log"
"gvisor.dev/gvisor/pkg/sentry/fs"
"gvisor.dev/gvisor/pkg/sentry/fs/host"
"gvisor.dev/gvisor/pkg/sentry/fsbridge"
"gvisor.dev/gvisor/pkg/sentry/kernel"
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
ktime "gvisor.dev/gvisor/pkg/sentry/kernel/time"
"gvisor.dev/gvisor/pkg/sentry/limits"
"gvisor.dev/gvisor/pkg/sentry/usage"
"gvisor.dev/gvisor/pkg/sentry/vfs"
"gvisor.dev/gvisor/pkg/syserror"
"gvisor.dev/gvisor/pkg/urpc"
)
@@ -60,6 +67,12 @@ type ExecArgs struct {
// process's MountNamespace.
MountNamespace *fs.MountNamespace
// MountNamespaceVFS2 is the mount namespace to execute the new process in.
// A reference on MountNamespace must be held for the lifetime of the
// ExecArgs. If MountNamespace is nil, it will default to the init
// process's MountNamespace.
MountNamespaceVFS2 *vfs.MountNamespace
// WorkingDirectory defines the working directory for the new process.
WorkingDirectory string `json:"wd"`
@@ -150,6 +163,7 @@ func (proc *Proc) execAsync(args *ExecArgs) (*kernel.ThreadGroup, kernel.ThreadI
Envv: args.Envv,
WorkingDirectory: args.WorkingDirectory,
MountNamespace: args.MountNamespace,
MountNamespaceVFS2: args.MountNamespaceVFS2,
Credentials: creds,
FDTable: fdTable,
Umask: 0022,
@@ -166,24 +180,53 @@ func (proc *Proc) execAsync(args *ExecArgs) (*kernel.ThreadGroup, kernel.ThreadI
// be donated to the new process in CreateProcess.
initArgs.MountNamespace.IncRef()
}
if initArgs.MountNamespaceVFS2 != nil {
// initArgs must hold a reference on MountNamespaceVFS2, which will
// be donated to the new process in CreateProcess.
initArgs.MountNamespaceVFS2.IncRef()
}
ctx := initArgs.NewContext(proc.Kernel)
if initArgs.Filename == "" {
// Get the full path to the filename from the PATH env variable.
paths := fs.GetPath(initArgs.Envv)
mns := initArgs.MountNamespace
if mns == nil {
mns = proc.Kernel.GlobalInit().Leader().MountNamespace()
if kernel.VFS2Enabled {
// Get the full path to the filename from the PATH env variable.
if initArgs.MountNamespaceVFS2 == nil {
// Set initArgs so that 'ctx' returns the namespace.
//
// MountNamespaceVFS2 adds a reference to the namespace, which is
// transferred to the new process.
initArgs.MountNamespaceVFS2 = proc.Kernel.GlobalInit().Leader().MountNamespaceVFS2()
}
paths := fs.GetPath(initArgs.Envv)
vfsObj := proc.Kernel.VFS
file, err := ResolveExecutablePath(ctx, vfsObj, initArgs.WorkingDirectory, initArgs.Argv[0], paths)
if err != nil {
return nil, 0, nil, fmt.Errorf("error finding executable %q in PATH %v: %v", initArgs.Argv[0], paths, err)
}
initArgs.File = fsbridge.NewVFSFile(file)
} else {
// Get the full path to the filename from the PATH env variable.
paths := fs.GetPath(initArgs.Envv)
if initArgs.MountNamespace == nil {
// Set initArgs so that 'ctx' returns the namespace.
initArgs.MountNamespace = proc.Kernel.GlobalInit().Leader().MountNamespace()
// initArgs must hold a reference on MountNamespace, which will
// be donated to the new process in CreateProcess.
initArgs.MountNamespaceVFS2.IncRef()
}
f, err := initArgs.MountNamespace.ResolveExecutablePath(ctx, initArgs.WorkingDirectory, initArgs.Argv[0], paths)
if err != nil {
return nil, 0, nil, fmt.Errorf("error finding executable %q in PATH %v: %v", initArgs.Argv[0], paths, err)
}
initArgs.Filename = f
}
f, err := mns.ResolveExecutablePath(ctx, initArgs.WorkingDirectory, initArgs.Argv[0], paths)
if err != nil {
return nil, 0, nil, fmt.Errorf("error finding executable %q in PATH %v: %v", initArgs.Argv[0], paths, err)
}
initArgs.Filename = f
}
mounter := fs.FileOwnerFromContext(ctx)
// TODO(gvisor.dev/issue/1623): Use host FD when supported in VFS2.
var ttyFile *fs.File
for appFD, hostFile := range args.FilePayload.Files {
var appFile *fs.File
@@ -411,3 +454,67 @@ func ttyName(tty *kernel.TTY) string {
}
return fmt.Sprintf("pts/%d", tty.Index)
}
// ResolveExecutablePath resolves the given executable name given a set of
// paths that might contain it.
func ResolveExecutablePath(ctx context.Context, vfsObj *vfs.VirtualFilesystem, wd, name string, paths []string) (*vfs.FileDescription, error) {
root := vfs.RootFromContext(ctx)
defer root.DecRef()
creds := auth.CredentialsFromContext(ctx)
// Absolute paths can be used directly.
if path.IsAbs(name) {
return openExecutable(ctx, vfsObj, creds, root, name)
}
// Paths with '/' in them should be joined to the working directory, or
// to the root if working directory is not set.
if strings.IndexByte(name, '/') > 0 {
if len(wd) == 0 {
wd = "/"
}
if !path.IsAbs(wd) {
return nil, fmt.Errorf("working directory %q must be absolute", wd)
}
return openExecutable(ctx, vfsObj, creds, root, path.Join(wd, name))
}
// Otherwise, we must lookup the name in the paths, starting from the
// calling context's root directory.
for _, p := range paths {
if !path.IsAbs(p) {
// Relative paths aren't safe, no one should be using them.
log.Warningf("Skipping relative path %q in $PATH", p)
continue
}
binPath := path.Join(p, name)
f, err := openExecutable(ctx, vfsObj, creds, root, binPath)
if err != nil {
return nil, err
}
if f == nil {
continue // Not found/no access.
}
return f, nil
}
return nil, syserror.ENOENT
}
func openExecutable(ctx context.Context, vfsObj *vfs.VirtualFilesystem, creds *auth.Credentials, root vfs.VirtualDentry, path string) (*vfs.FileDescription, error) {
pop := vfs.PathOperation{
Root: root,
Start: root, // binPath is absolute, Start can be anything.
Path: fspath.Parse(path),
FollowFinalSymlink: true,
}
opts := &vfs.OpenOptions{
Flags: linux.O_RDONLY,
FileExec: true,
}
f, err := vfsObj.OpenAt(ctx, creds, &pop, opts)
if err == syserror.ENOENT || err == syserror.EACCES {
return nil, nil
}
return f, err
}
+1
View File
@@ -36,6 +36,7 @@ go_library(
"//pkg/sentry/fs/proc/device",
"//pkg/sentry/fs/proc/seqfile",
"//pkg/sentry/fs/ramfs",
"//pkg/sentry/fsbridge",
"//pkg/sentry/inet",
"//pkg/sentry/kernel",
"//pkg/sentry/kernel/auth",
+5 -12
View File
@@ -28,6 +28,7 @@ import (
"gvisor.dev/gvisor/pkg/sentry/fs/proc/device"
"gvisor.dev/gvisor/pkg/sentry/fs/proc/seqfile"
"gvisor.dev/gvisor/pkg/sentry/fs/ramfs"
"gvisor.dev/gvisor/pkg/sentry/fsbridge"
"gvisor.dev/gvisor/pkg/sentry/kernel"
"gvisor.dev/gvisor/pkg/sentry/limits"
"gvisor.dev/gvisor/pkg/sentry/mm"
@@ -249,7 +250,7 @@ func newExe(t *kernel.Task, msrc *fs.MountSource) *fs.Inode {
return newProcInode(t, exeSymlink, msrc, fs.Symlink, t)
}
func (e *exe) executable() (d *fs.Dirent, err error) {
func (e *exe) executable() (file fsbridge.File, err error) {
e.t.WithMuLocked(func(t *kernel.Task) {
mm := t.MemoryManager()
if mm == nil {
@@ -262,8 +263,8 @@ func (e *exe) executable() (d *fs.Dirent, err error) {
// The MemoryManager may be destroyed, in which case
// MemoryManager.destroy will simply set the executable to nil
// (with locks held).
d = mm.Executable()
if d == nil {
file = mm.Executable()
if file == nil {
err = syserror.ENOENT
}
})
@@ -283,15 +284,7 @@ func (e *exe) Readlink(ctx context.Context, inode *fs.Inode) (string, error) {
}
defer exec.DecRef()
root := fs.RootFromContext(ctx)
if root == nil {
// This doesn't correspond to anything in Linux because the vfs is
// global there.
return "", syserror.EINVAL
}
defer root.DecRef()
n, _ := exec.FullName(root)
return n, nil
return exec.PathnameWithDeleted(ctx), nil
}
// namespaceSymlink represents a symlink in the namespacefs, such as the files
+24
View File
@@ -0,0 +1,24 @@
load("//tools:defs.bzl", "go_library")
licenses(["notice"])
go_library(
name = "fsbridge",
srcs = [
"bridge.go",
"fs.go",
"vfs.go",
],
visibility = ["//pkg/sentry:internal"],
deps = [
"//pkg/abi/linux",
"//pkg/context",
"//pkg/fspath",
"//pkg/sentry/fs",
"//pkg/sentry/kernel/auth",
"//pkg/sentry/memmap",
"//pkg/sentry/vfs",
"//pkg/syserror",
"//pkg/usermem",
],
)
+54
View File
@@ -0,0 +1,54 @@
// Copyright 2020 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 fsbridge provides common interfaces to bridge between VFS1 and VFS2
// files.
package fsbridge
import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
"gvisor.dev/gvisor/pkg/sentry/memmap"
"gvisor.dev/gvisor/pkg/sentry/vfs"
"gvisor.dev/gvisor/pkg/usermem"
)
// File provides a common interface to bridge between VFS1 and VFS2 files.
type File interface {
// 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.
PathnameWithDeleted(ctx context.Context) string
// ReadFull read all contents from the file.
ReadFull(ctx context.Context, dst usermem.IOSequence, offset int64) (int64, error)
// ConfigureMMap mutates opts to implement mmap(2) for the file.
ConfigureMMap(context.Context, *memmap.MMapOpts) error
// Type returns the file type, e.g. linux.S_IFREG.
Type(context.Context) (linux.FileMode, error)
// IncRef increments reference.
IncRef()
// DecRef decrements reference.
DecRef()
}
// Lookup provides a common interface to open files.
type Lookup interface {
// OpenPath opens a file.
OpenPath(ctx context.Context, path string, opts vfs.OpenOptions, remainingTraversals *uint, resolveFinal bool) (File, error)
}
+181
View File
@@ -0,0 +1,181 @@
// Copyright 2020 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 fsbridge
import (
"io"
"strings"
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
"gvisor.dev/gvisor/pkg/sentry/fs"
"gvisor.dev/gvisor/pkg/sentry/memmap"
"gvisor.dev/gvisor/pkg/sentry/vfs"
"gvisor.dev/gvisor/pkg/syserror"
"gvisor.dev/gvisor/pkg/usermem"
)
// fsFile implements File interface over fs.File.
//
// +stateify savable
type fsFile struct {
file *fs.File
}
var _ File = (*fsFile)(nil)
// NewFSFile creates a new File over fs.File.
func NewFSFile(file *fs.File) File {
return &fsFile{file: file}
}
// PathnameWithDeleted implements File.
func (f *fsFile) PathnameWithDeleted(ctx context.Context) string {
root := fs.RootFromContext(ctx)
if root == nil {
// This doesn't correspond to anything in Linux because the vfs is
// global there.
return ""
}
defer root.DecRef()
name, _ := f.file.Dirent.FullName(root)
return name
}
// ReadFull implements File.
func (f *fsFile) ReadFull(ctx context.Context, dst usermem.IOSequence, offset int64) (int64, error) {
var total int64
for dst.NumBytes() > 0 {
n, err := f.file.Preadv(ctx, dst, offset+total)
total += n
if err == io.EOF && total != 0 {
return total, io.ErrUnexpectedEOF
} else if err != nil {
return total, err
}
dst = dst.DropFirst64(n)
}
return total, nil
}
// ConfigureMMap implements File.
func (f *fsFile) ConfigureMMap(ctx context.Context, opts *memmap.MMapOpts) error {
return f.file.ConfigureMMap(ctx, opts)
}
// Type implements File.
func (f *fsFile) Type(context.Context) (linux.FileMode, error) {
return linux.FileMode(f.file.Dirent.Inode.StableAttr.Type.LinuxType()), nil
}
// IncRef implements File.
func (f *fsFile) IncRef() {
f.file.IncRef()
}
// DecRef implements File.
func (f *fsFile) DecRef() {
f.file.DecRef()
}
// fsLookup implements Lookup interface using fs.File.
//
// +stateify savable
type fsLookup struct {
mntns *fs.MountNamespace
root *fs.Dirent
workingDir *fs.Dirent
}
var _ Lookup = (*fsLookup)(nil)
// NewFSLookup creates a new Lookup using VFS1.
func NewFSLookup(mntns *fs.MountNamespace, root, workingDir *fs.Dirent) Lookup {
return &fsLookup{
mntns: mntns,
root: root,
workingDir: workingDir,
}
}
// OpenPath implements Lookup.
func (l *fsLookup) OpenPath(ctx context.Context, path string, opts vfs.OpenOptions, remainingTraversals *uint, resolveFinal bool) (File, error) {
var d *fs.Dirent
var err error
if resolveFinal {
d, err = l.mntns.FindInode(ctx, l.root, l.workingDir, path, remainingTraversals)
} else {
d, err = l.mntns.FindLink(ctx, l.root, l.workingDir, path, remainingTraversals)
}
if err != nil {
return nil, err
}
defer d.DecRef()
if !resolveFinal && fs.IsSymlink(d.Inode.StableAttr) {
return nil, syserror.ELOOP
}
fsPerm := openOptionsToPermMask(&opts)
if err := d.Inode.CheckPermission(ctx, fsPerm); err != nil {
return nil, err
}
// If they claim it's a directory, then make sure.
if strings.HasSuffix(path, "/") {
if d.Inode.StableAttr.Type != fs.Directory {
return nil, syserror.ENOTDIR
}
}
if opts.FileExec && d.Inode.StableAttr.Type != fs.RegularFile {
ctx.Infof("%q is not a regular file: %v", path, d.Inode.StableAttr.Type)
return nil, syserror.EACCES
}
f, err := d.Inode.GetFile(ctx, d, flagsToFileFlags(opts.Flags))
if err != nil {
return nil, err
}
return &fsFile{file: f}, nil
}
func openOptionsToPermMask(opts *vfs.OpenOptions) fs.PermMask {
mode := opts.Flags & linux.O_ACCMODE
return fs.PermMask{
Read: mode == linux.O_RDONLY || mode == linux.O_RDWR,
Write: mode == linux.O_WRONLY || mode == linux.O_RDWR,
Execute: opts.FileExec,
}
}
func flagsToFileFlags(flags uint32) fs.FileFlags {
return fs.FileFlags{
Direct: flags&linux.O_DIRECT != 0,
DSync: flags&(linux.O_DSYNC|linux.O_SYNC) != 0,
Sync: flags&linux.O_SYNC != 0,
NonBlocking: flags&linux.O_NONBLOCK != 0,
Read: (flags & linux.O_ACCMODE) != linux.O_WRONLY,
Write: (flags & linux.O_ACCMODE) != linux.O_RDONLY,
Append: flags&linux.O_APPEND != 0,
Directory: flags&linux.O_DIRECTORY != 0,
Async: flags&linux.O_ASYNC != 0,
LargeFile: flags&linux.O_LARGEFILE != 0,
Truncate: flags&linux.O_TRUNC != 0,
}
}
+134
View File
@@ -0,0 +1,134 @@
// Copyright 2020 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 fsbridge
import (
"io"
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
"gvisor.dev/gvisor/pkg/fspath"
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
"gvisor.dev/gvisor/pkg/sentry/memmap"
"gvisor.dev/gvisor/pkg/sentry/vfs"
"gvisor.dev/gvisor/pkg/usermem"
)
// fsFile implements File interface over vfs.FileDescription.
//
// +stateify savable
type vfsFile struct {
file *vfs.FileDescription
}
var _ File = (*vfsFile)(nil)
// NewVFSFile creates a new File over fs.File.
func NewVFSFile(file *vfs.FileDescription) File {
return &vfsFile{file: file}
}
// PathnameWithDeleted implements File.
func (f *vfsFile) PathnameWithDeleted(ctx context.Context) string {
root := vfs.RootFromContext(ctx)
defer root.DecRef()
vfsObj := f.file.VirtualDentry().Mount().Filesystem().VirtualFilesystem()
name, _ := vfsObj.PathnameWithDeleted(ctx, root, f.file.VirtualDentry())
return name
}
// ReadFull implements File.
func (f *vfsFile) ReadFull(ctx context.Context, dst usermem.IOSequence, offset int64) (int64, error) {
var total int64
for dst.NumBytes() > 0 {
n, err := f.file.PRead(ctx, dst, offset+total, vfs.ReadOptions{})
total += n
if err == io.EOF && total != 0 {
return total, io.ErrUnexpectedEOF
} else if err != nil {
return total, err
}
dst = dst.DropFirst64(n)
}
return total, nil
}
// ConfigureMMap implements File.
func (f *vfsFile) ConfigureMMap(ctx context.Context, opts *memmap.MMapOpts) error {
return f.file.ConfigureMMap(ctx, opts)
}
// Type implements File.
func (f *vfsFile) Type(ctx context.Context) (linux.FileMode, error) {
stat, err := f.file.Stat(ctx, vfs.StatOptions{})
if err != nil {
return 0, err
}
return linux.FileMode(stat.Mode).FileType(), nil
}
// IncRef implements File.
func (f *vfsFile) IncRef() {
f.file.IncRef()
}
// DecRef implements File.
func (f *vfsFile) DecRef() {
f.file.DecRef()
}
// fsLookup implements Lookup interface using fs.File.
//
// +stateify savable
type vfsLookup struct {
mntns *vfs.MountNamespace
root vfs.VirtualDentry
workingDir vfs.VirtualDentry
}
var _ Lookup = (*vfsLookup)(nil)
// NewVFSLookup creates a new Lookup using VFS2.
func NewVFSLookup(mntns *vfs.MountNamespace, root, workingDir vfs.VirtualDentry) Lookup {
return &vfsLookup{
mntns: mntns,
root: root,
workingDir: workingDir,
}
}
// OpenPath implements Lookup.
//
// remainingTraversals is not configurable in VFS2, all callers are using the
// default anyways.
//
// TODO(gvisor.dev/issue/1623): Check mount has read and exec permission.
func (l *vfsLookup) OpenPath(ctx context.Context, path string, opts vfs.OpenOptions, _ *uint, resolveFinal bool) (File, error) {
vfsObj := l.mntns.Root().Mount().Filesystem().VirtualFilesystem()
creds := auth.CredentialsFromContext(ctx)
pop := &vfs.PathOperation{
Root: l.root,
Start: l.root,
Path: fspath.Parse(path),
FollowFinalSymlink: resolveFinal,
}
fd, err := vfsObj.OpenAt(ctx, creds, pop, &opts)
if err != nil {
return nil, err
}
return &vfsFile{file: fd}, nil
}
+4
View File
@@ -28,6 +28,9 @@ import (
"gvisor.dev/gvisor/pkg/sync"
)
// Name is the default filesystem name.
const Name = "devtmpfs"
// FilesystemType implements vfs.FilesystemType.
type FilesystemType struct {
initOnce sync.Once
@@ -107,6 +110,7 @@ func (a *Accessor) wrapContext(ctx context.Context) *accessorContext {
func (ac *accessorContext) Value(key interface{}) interface{} {
switch key {
case vfs.CtxMountNamespace:
ac.a.mntns.IncRef()
return ac.a.mntns
case vfs.CtxRoot:
ac.a.root.IncRef()
+4 -1
View File
@@ -400,6 +400,7 @@ func (fs *filesystem) unlinkAt(ctx context.Context, rp *vfs.ResolvingPath, dir b
}
vfsObj := rp.VirtualFilesystem()
mntns := vfs.MountNamespaceFromContext(ctx)
defer mntns.DecRef()
parent.dirMu.Lock()
defer parent.dirMu.Unlock()
childVFSD := parent.vfsd.Child(name)
@@ -934,7 +935,9 @@ func (fs *filesystem) RenameAt(ctx context.Context, rp *vfs.ResolvingPath, oldPa
if oldParent == newParent && oldName == newName {
return nil
}
if err := vfsObj.PrepareRenameDentry(vfs.MountNamespaceFromContext(ctx), &renamed.vfsd, replacedVFSD); err != nil {
mntns := vfs.MountNamespaceFromContext(ctx)
defer mntns.DecRef()
if err := vfsObj.PrepareRenameDentry(mntns, &renamed.vfsd, replacedVFSD); err != nil {
return err
}
if err := renamed.file.rename(ctx, newParent.file, newName); err != nil {
+3
View File
@@ -52,6 +52,9 @@ import (
"gvisor.dev/gvisor/pkg/usermem"
)
// Name is the default filesystem name.
const Name = "9p"
// FilesystemType implements vfs.FilesystemType.
type FilesystemType struct{}
+8 -2
View File
@@ -544,6 +544,7 @@ func (fs *Filesystem) RenameAt(ctx context.Context, rp *vfs.ResolvingPath, oldPa
}
mntns := vfs.MountNamespaceFromContext(ctx)
defer mntns.DecRef()
virtfs := rp.VirtualFilesystem()
srcDirDentry := srcDirVFSD.Impl().(*Dentry)
@@ -595,7 +596,10 @@ func (fs *Filesystem) RmdirAt(ctx context.Context, rp *vfs.ResolvingPath) error
parentDentry := vfsd.Parent().Impl().(*Dentry)
parentDentry.dirMu.Lock()
defer parentDentry.dirMu.Unlock()
if err := virtfs.PrepareDeleteDentry(vfs.MountNamespaceFromContext(ctx), vfsd); err != nil {
mntns := vfs.MountNamespaceFromContext(ctx)
defer mntns.DecRef()
if err := virtfs.PrepareDeleteDentry(mntns, vfsd); err != nil {
return err
}
if err := parentDentry.inode.RmDir(ctx, rp.Component(), vfsd); err != nil {
@@ -697,7 +701,9 @@ func (fs *Filesystem) UnlinkAt(ctx context.Context, rp *vfs.ResolvingPath) error
parentDentry := vfsd.Parent().Impl().(*Dentry)
parentDentry.dirMu.Lock()
defer parentDentry.dirMu.Unlock()
if err := virtfs.PrepareDeleteDentry(vfs.MountNamespaceFromContext(ctx), vfsd); err != nil {
mntns := vfs.MountNamespaceFromContext(ctx)
defer mntns.DecRef()
if err := virtfs.PrepareDeleteDentry(mntns, vfsd); err != nil {
return err
}
if err := parentDentry.inode.Unlink(ctx, rp.Component(), vfsd); err != nil {
+1
View File
@@ -14,6 +14,7 @@ go_library(
"tasks_net.go",
"tasks_sys.go",
],
visibility = ["//pkg/sentry:internal"],
deps = [
"//pkg/abi/linux",
"//pkg/context",
+11 -7
View File
@@ -26,15 +26,18 @@ import (
"gvisor.dev/gvisor/pkg/sentry/vfs"
)
// procFSType is the factory class for procfs.
// Name is the default filesystem name.
const Name = "proc"
// FilesystemType is the factory class for procfs.
//
// +stateify savable
type procFSType struct{}
type FilesystemType struct{}
var _ vfs.FilesystemType = (*procFSType)(nil)
var _ vfs.FilesystemType = (*FilesystemType)(nil)
// GetFilesystem implements vfs.FilesystemType.
func (ft *procFSType) GetFilesystem(ctx context.Context, vfsObj *vfs.VirtualFilesystem, creds *auth.Credentials, source string, opts vfs.GetFilesystemOptions) (*vfs.Filesystem, *vfs.Dentry, error) {
func (ft *FilesystemType) GetFilesystem(ctx context.Context, vfsObj *vfs.VirtualFilesystem, creds *auth.Credentials, source string, opts vfs.GetFilesystemOptions) (*vfs.Filesystem, *vfs.Dentry, error) {
k := kernel.KernelFromContext(ctx)
if k == nil {
return nil, nil, fmt.Errorf("procfs requires a kernel")
@@ -47,12 +50,13 @@ func (ft *procFSType) GetFilesystem(ctx context.Context, vfsObj *vfs.VirtualFile
procfs := &kernfs.Filesystem{}
procfs.VFSFilesystem().Init(vfsObj, procfs)
var data *InternalData
var cgroups map[string]string
if opts.InternalData != nil {
data = opts.InternalData.(*InternalData)
data := opts.InternalData.(*InternalData)
cgroups = data.Cgroups
}
_, dentry := newTasksInode(procfs, k, pidns, data.Cgroups)
_, dentry := newTasksInode(procfs, k, pidns, cgroups)
return procfs.VFSFilesystem(), dentry.VFSDentry(), nil
}
+8 -9
View File
@@ -90,8 +90,7 @@ func setup(t *testing.T) *testutil.System {
ctx := k.SupervisorContext()
creds := auth.CredentialsFromContext(ctx)
vfsObj := vfs.New()
vfsObj.MustRegisterFilesystemType("procfs", &procFSType{}, &vfs.RegisterFilesystemTypeOptions{
k.VFS.MustRegisterFilesystemType(Name, &FilesystemType{}, &vfs.RegisterFilesystemTypeOptions{
AllowUserMount: true,
})
fsOpts := vfs.GetFilesystemOptions{
@@ -102,11 +101,11 @@ func setup(t *testing.T) *testutil.System {
},
},
}
mntns, err := vfsObj.NewMountNamespace(ctx, creds, "", "procfs", &fsOpts)
mntns, err := k.VFS.NewMountNamespace(ctx, creds, "", Name, &fsOpts)
if err != nil {
t.Fatalf("NewMountNamespace(): %v", err)
}
return testutil.NewSystem(ctx, t, vfsObj, mntns)
return testutil.NewSystem(ctx, t, k.VFS, mntns)
}
func TestTasksEmpty(t *testing.T) {
@@ -131,7 +130,7 @@ func TestTasks(t *testing.T) {
var tasks []*kernel.Task
for i := 0; i < 5; i++ {
tc := k.NewThreadGroup(nil, k.RootPIDNamespace(), kernel.NewSignalHandlers(), linux.SIGCHLD, k.GlobalInit().Limits())
task, err := testutil.CreateTask(s.Ctx, fmt.Sprintf("name-%d", i), tc)
task, err := testutil.CreateTask(s.Ctx, fmt.Sprintf("name-%d", i), tc, s.MntNs, s.Root, s.Root)
if err != nil {
t.Fatalf("CreateTask(): %v", err)
}
@@ -213,7 +212,7 @@ func TestTasksOffset(t *testing.T) {
k := kernel.KernelFromContext(s.Ctx)
for i := 0; i < 3; i++ {
tc := k.NewThreadGroup(nil, k.RootPIDNamespace(), kernel.NewSignalHandlers(), linux.SIGCHLD, k.GlobalInit().Limits())
if _, err := testutil.CreateTask(s.Ctx, fmt.Sprintf("name-%d", i), tc); err != nil {
if _, err := testutil.CreateTask(s.Ctx, fmt.Sprintf("name-%d", i), tc, s.MntNs, s.Root, s.Root); err != nil {
t.Fatalf("CreateTask(): %v", err)
}
}
@@ -337,7 +336,7 @@ func TestTask(t *testing.T) {
k := kernel.KernelFromContext(s.Ctx)
tc := k.NewThreadGroup(nil, k.RootPIDNamespace(), kernel.NewSignalHandlers(), linux.SIGCHLD, k.GlobalInit().Limits())
_, err := testutil.CreateTask(s.Ctx, "name", tc)
_, err := testutil.CreateTask(s.Ctx, "name", tc, s.MntNs, s.Root, s.Root)
if err != nil {
t.Fatalf("CreateTask(): %v", err)
}
@@ -352,7 +351,7 @@ func TestProcSelf(t *testing.T) {
k := kernel.KernelFromContext(s.Ctx)
tc := k.NewThreadGroup(nil, k.RootPIDNamespace(), kernel.NewSignalHandlers(), linux.SIGCHLD, k.GlobalInit().Limits())
task, err := testutil.CreateTask(s.Ctx, "name", tc)
task, err := testutil.CreateTask(s.Ctx, "name", tc, s.MntNs, s.Root, s.Root)
if err != nil {
t.Fatalf("CreateTask(): %v", err)
}
@@ -433,7 +432,7 @@ func TestTree(t *testing.T) {
var tasks []*kernel.Task
for i := 0; i < 5; i++ {
tc := k.NewThreadGroup(nil, k.RootPIDNamespace(), kernel.NewSignalHandlers(), linux.SIGCHLD, k.GlobalInit().Limits())
task, err := testutil.CreateTask(s.Ctx, fmt.Sprintf("name-%d", i), tc)
task, err := testutil.CreateTask(s.Ctx, fmt.Sprintf("name-%d", i), tc, s.MntNs, s.Root, s.Root)
if err != nil {
t.Fatalf("CreateTask(): %v", err)
}
+1
View File
@@ -7,6 +7,7 @@ go_library(
srcs = [
"sys.go",
],
visibility = ["//pkg/sentry:internal"],
deps = [
"//pkg/abi/linux",
"//pkg/context",
+3
View File
@@ -28,6 +28,9 @@ import (
"gvisor.dev/gvisor/pkg/syserror"
)
// Name is the default filesystem name.
const Name = "sysfs"
// FilesystemType implements vfs.FilesystemType.
type FilesystemType struct{}
+3 -4
View File
@@ -34,16 +34,15 @@ func newTestSystem(t *testing.T) *testutil.System {
}
ctx := k.SupervisorContext()
creds := auth.CredentialsFromContext(ctx)
v := vfs.New()
v.MustRegisterFilesystemType("sysfs", sys.FilesystemType{}, &vfs.RegisterFilesystemTypeOptions{
k.VFS.MustRegisterFilesystemType(sys.Name, sys.FilesystemType{}, &vfs.RegisterFilesystemTypeOptions{
AllowUserMount: true,
})
mns, err := v.NewMountNamespace(ctx, creds, "", "sysfs", &vfs.GetFilesystemOptions{})
mns, err := k.VFS.NewMountNamespace(ctx, creds, "", sys.Name, &vfs.GetFilesystemOptions{})
if err != nil {
t.Fatalf("Failed to create new mount namespace: %v", err)
}
return testutil.NewSystem(ctx, t, v, mns)
return testutil.NewSystem(ctx, t, k.VFS, mns)
}
func TestReadCPUFile(t *testing.T) {
+1 -1
View File
@@ -16,7 +16,7 @@ go_library(
"//pkg/cpuid",
"//pkg/fspath",
"//pkg/memutil",
"//pkg/sentry/fs",
"//pkg/sentry/fsimpl/tmpfs",
"//pkg/sentry/kernel",
"//pkg/sentry/kernel/auth",
"//pkg/sentry/kernel/sched",
+14 -10
View File
@@ -24,7 +24,7 @@ import (
"gvisor.dev/gvisor/pkg/context"
"gvisor.dev/gvisor/pkg/cpuid"
"gvisor.dev/gvisor/pkg/memutil"
"gvisor.dev/gvisor/pkg/sentry/fs"
"gvisor.dev/gvisor/pkg/sentry/fsimpl/tmpfs"
"gvisor.dev/gvisor/pkg/sentry/kernel"
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
"gvisor.dev/gvisor/pkg/sentry/kernel/sched"
@@ -33,6 +33,7 @@ import (
"gvisor.dev/gvisor/pkg/sentry/pgalloc"
"gvisor.dev/gvisor/pkg/sentry/platform"
"gvisor.dev/gvisor/pkg/sentry/time"
"gvisor.dev/gvisor/pkg/sentry/vfs"
// Platforms are plugable.
_ "gvisor.dev/gvisor/pkg/sentry/platform/kvm"
@@ -99,26 +100,27 @@ func Boot() (*kernel.Kernel, error) {
return nil, fmt.Errorf("initializing kernel: %v", err)
}
ctx := k.SupervisorContext()
kernel.VFS2Enabled = true
vfsObj := vfs.New()
vfsObj.MustRegisterFilesystemType(tmpfs.Name, &tmpfs.FilesystemType{}, &vfs.RegisterFilesystemTypeOptions{
AllowUserMount: true,
AllowUserList: true,
})
k.VFS = vfsObj
// Create mount namespace without root as it's the minimum required to create
// the global thread group.
mntns, err := fs.NewMountNamespace(ctx, nil)
if err != nil {
return nil, err
}
ls, err := limits.NewLinuxLimitSet()
if err != nil {
return nil, err
}
tg := k.NewThreadGroup(mntns, k.RootPIDNamespace(), kernel.NewSignalHandlers(), linux.SIGCHLD, ls)
tg := k.NewThreadGroup(nil, k.RootPIDNamespace(), kernel.NewSignalHandlers(), linux.SIGCHLD, ls)
k.TestOnly_SetGlobalInit(tg)
return k, nil
}
// CreateTask creates a new bare bones task for tests.
func CreateTask(ctx context.Context, name string, tc *kernel.ThreadGroup) (*kernel.Task, error) {
func CreateTask(ctx context.Context, name string, tc *kernel.ThreadGroup, mntns *vfs.MountNamespace, root, cwd vfs.VirtualDentry) (*kernel.Task, error) {
k := kernel.KernelFromContext(ctx)
config := &kernel.TaskConfig{
Kernel: k,
@@ -129,6 +131,8 @@ func CreateTask(ctx context.Context, name string, tc *kernel.ThreadGroup) (*kern
UTSNamespace: kernel.UTSNamespaceFromContext(ctx),
IPCNamespace: kernel.IPCNamespaceFromContext(ctx),
AbstractSocketNamespace: kernel.NewAbstractSocketNamespace(),
MountNamespaceVFS2: mntns,
FSContext: kernel.NewFSContextVFS2(root, cwd, 0022),
}
return k.TaskSet().NewTask(config)
}

Some files were not shown because too many files have changed in this diff Show More