mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
committed by
gVisor bot
parent
829beebf0b
commit
45a8ae240d
@@ -554,3 +554,11 @@ func (s *StaticDirectory) Open(rp *vfs.ResolvingPath, vfsd *vfs.Dentry, opts vfs
|
||||
fd.Init(rp.Mount(), vfsd, &s.OrderedChildren, &opts)
|
||||
return fd.VFSFileDescription(), nil
|
||||
}
|
||||
|
||||
// AlwaysValid partially implements kernfs.inodeDynamicLookup.
|
||||
type AlwaysValid struct{}
|
||||
|
||||
// Valid implements kernfs.inodeDynamicLookup.
|
||||
func (*AlwaysValid) Valid(context.Context) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -319,7 +319,7 @@ type inodeMetadata interface {
|
||||
// CheckPermissions checks that creds may access this inode for the
|
||||
// requested access type, per the the rules of
|
||||
// fs/namei.c:generic_permission().
|
||||
CheckPermissions(ctx context.Context, creds *auth.Credentials, atx vfs.AccessTypes) error
|
||||
CheckPermissions(ctx context.Context, creds *auth.Credentials, ats vfs.AccessTypes) error
|
||||
|
||||
// Mode returns the (struct stat)::st_mode value for this inode. This is
|
||||
// separated from Stat for performance.
|
||||
|
||||
@@ -8,6 +8,7 @@ go_library(
|
||||
"filesystem.go",
|
||||
"subtasks.go",
|
||||
"task.go",
|
||||
"task_fds.go",
|
||||
"task_files.go",
|
||||
"task_net.go",
|
||||
"tasks.go",
|
||||
@@ -19,8 +20,10 @@ go_library(
|
||||
"//pkg/abi/linux",
|
||||
"//pkg/context",
|
||||
"//pkg/log",
|
||||
"//pkg/refs",
|
||||
"//pkg/safemem",
|
||||
"//pkg/sentry/fs",
|
||||
"//pkg/sentry/fsbridge",
|
||||
"//pkg/sentry/fsimpl/kernfs",
|
||||
"//pkg/sentry/inet",
|
||||
"//pkg/sentry/kernel",
|
||||
@@ -53,6 +56,7 @@ go_test(
|
||||
"//pkg/fspath",
|
||||
"//pkg/sentry/contexttest",
|
||||
"//pkg/sentry/fsimpl/testutil",
|
||||
"//pkg/sentry/fsimpl/tmpfs",
|
||||
"//pkg/sentry/inet",
|
||||
"//pkg/sentry/kernel",
|
||||
"//pkg/sentry/kernel/auth",
|
||||
|
||||
@@ -34,6 +34,7 @@ type subtasksInode struct {
|
||||
kernfs.InodeDirectoryNoNewChildren
|
||||
kernfs.InodeAttrs
|
||||
kernfs.OrderedChildren
|
||||
kernfs.AlwaysValid
|
||||
|
||||
task *kernel.Task
|
||||
pidns *kernel.PIDNamespace
|
||||
@@ -61,11 +62,6 @@ func newSubtasks(task *kernel.Task, pidns *kernel.PIDNamespace, inoGen InoGenera
|
||||
return dentry
|
||||
}
|
||||
|
||||
// Valid implements kernfs.inodeDynamicLookup.
|
||||
func (i *subtasksInode) Valid(ctx context.Context) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// Lookup implements kernfs.inodeDynamicLookup.
|
||||
func (i *subtasksInode) Lookup(ctx context.Context, name string) (*vfs.Dentry, error) {
|
||||
tid, err := strconv.ParseUint(name, 10, 32)
|
||||
|
||||
@@ -45,19 +45,19 @@ var _ kernfs.Inode = (*taskInode)(nil)
|
||||
|
||||
func newTaskInode(inoGen InoGenerator, task *kernel.Task, pidns *kernel.PIDNamespace, isThreadGroup bool, cgroupControllers map[string]string) *kernfs.Dentry {
|
||||
contents := map[string]*kernfs.Dentry{
|
||||
"auxv": newTaskOwnedFile(task, inoGen.NextIno(), 0444, &auxvData{task: task}),
|
||||
"cmdline": newTaskOwnedFile(task, inoGen.NextIno(), 0444, &cmdlineData{task: task, arg: cmdlineDataArg}),
|
||||
"comm": newComm(task, inoGen.NextIno(), 0444),
|
||||
"environ": newTaskOwnedFile(task, inoGen.NextIno(), 0444, &cmdlineData{task: task, arg: environDataArg}),
|
||||
//"exe": newExe(t, msrc),
|
||||
//"fd": newFdDir(t, msrc),
|
||||
//"fdinfo": newFdInfoDir(t, msrc),
|
||||
"gid_map": newTaskOwnedFile(task, inoGen.NextIno(), 0644, &idMapData{task: task, gids: true}),
|
||||
"io": newTaskOwnedFile(task, inoGen.NextIno(), 0400, newIO(task, isThreadGroup)),
|
||||
"maps": newTaskOwnedFile(task, inoGen.NextIno(), 0444, &mapsData{task: task}),
|
||||
//"mountinfo": seqfile.NewSeqFileInode(t, &mountInfoFile{t: t}, msrc),
|
||||
//"mounts": seqfile.NewSeqFileInode(t, &mountsFile{t: t}, msrc),
|
||||
"net": newTaskNetDir(task, inoGen),
|
||||
"auxv": newTaskOwnedFile(task, inoGen.NextIno(), 0444, &auxvData{task: task}),
|
||||
"cmdline": newTaskOwnedFile(task, inoGen.NextIno(), 0444, &cmdlineData{task: task, arg: cmdlineDataArg}),
|
||||
"comm": newComm(task, inoGen.NextIno(), 0444),
|
||||
"environ": newTaskOwnedFile(task, inoGen.NextIno(), 0444, &cmdlineData{task: task, arg: environDataArg}),
|
||||
"exe": newExeSymlink(task, inoGen.NextIno()),
|
||||
"fd": newFDDirInode(task, inoGen),
|
||||
"fdinfo": newFDInfoDirInode(task, inoGen),
|
||||
"gid_map": newTaskOwnedFile(task, inoGen.NextIno(), 0644, &idMapData{task: task, gids: true}),
|
||||
"io": newTaskOwnedFile(task, inoGen.NextIno(), 0400, newIO(task, isThreadGroup)),
|
||||
"maps": newTaskOwnedFile(task, inoGen.NextIno(), 0444, &mapsData{task: task}),
|
||||
"mountinfo": newTaskOwnedFile(task, inoGen.NextIno(), 0444, &mountInfoData{task: task}),
|
||||
"mounts": newTaskOwnedFile(task, inoGen.NextIno(), 0444, &mountsData{task: task}),
|
||||
"net": newTaskNetDir(task, inoGen),
|
||||
"ns": newTaskOwnedDir(task, inoGen.NextIno(), 0511, map[string]*kernfs.Dentry{
|
||||
"net": newNamespaceSymlink(task, inoGen.NextIno(), "net"),
|
||||
"pid": newNamespaceSymlink(task, inoGen.NextIno(), "pid"),
|
||||
|
||||
@@ -0,0 +1,287 @@
|
||||
// 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 proc
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strconv"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/abi/linux"
|
||||
"gvisor.dev/gvisor/pkg/context"
|
||||
"gvisor.dev/gvisor/pkg/refs"
|
||||
"gvisor.dev/gvisor/pkg/sentry/fsimpl/kernfs"
|
||||
"gvisor.dev/gvisor/pkg/sentry/kernel"
|
||||
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
|
||||
"gvisor.dev/gvisor/pkg/sentry/vfs"
|
||||
"gvisor.dev/gvisor/pkg/syserror"
|
||||
)
|
||||
|
||||
type fdDir struct {
|
||||
inoGen InoGenerator
|
||||
task *kernel.Task
|
||||
|
||||
// When produceSymlinks is set, dirents produces for the FDs are reported
|
||||
// as symlink. Otherwise, they are reported as regular files.
|
||||
produceSymlink bool
|
||||
}
|
||||
|
||||
func (i *fdDir) lookup(name string) (*vfs.FileDescription, kernel.FDFlags, error) {
|
||||
fd, err := strconv.ParseUint(name, 10, 64)
|
||||
if err != nil {
|
||||
return nil, kernel.FDFlags{}, syserror.ENOENT
|
||||
}
|
||||
|
||||
var (
|
||||
file *vfs.FileDescription
|
||||
flags kernel.FDFlags
|
||||
)
|
||||
i.task.WithMuLocked(func(t *kernel.Task) {
|
||||
if fdTable := t.FDTable(); fdTable != nil {
|
||||
file, flags = fdTable.GetVFS2(int32(fd))
|
||||
}
|
||||
})
|
||||
if file == nil {
|
||||
return nil, kernel.FDFlags{}, syserror.ENOENT
|
||||
}
|
||||
return file, flags, nil
|
||||
}
|
||||
|
||||
// IterDirents implements kernfs.inodeDynamicLookup.
|
||||
func (i *fdDir) IterDirents(ctx context.Context, cb vfs.IterDirentsCallback, absOffset, relOffset int64) (int64, error) {
|
||||
var fds []int32
|
||||
i.task.WithMuLocked(func(t *kernel.Task) {
|
||||
if fdTable := t.FDTable(); fdTable != nil {
|
||||
fds = fdTable.GetFDs()
|
||||
}
|
||||
})
|
||||
|
||||
offset := absOffset + relOffset
|
||||
typ := uint8(linux.DT_REG)
|
||||
if i.produceSymlink {
|
||||
typ = linux.DT_LNK
|
||||
}
|
||||
|
||||
// Find the appropriate starting point.
|
||||
idx := sort.Search(len(fds), func(i int) bool { return fds[i] >= int32(relOffset) })
|
||||
if idx >= len(fds) {
|
||||
return offset, nil
|
||||
}
|
||||
for _, fd := range fds[idx:] {
|
||||
dirent := vfs.Dirent{
|
||||
Name: strconv.FormatUint(uint64(fd), 10),
|
||||
Type: typ,
|
||||
Ino: i.inoGen.NextIno(),
|
||||
NextOff: offset + 1,
|
||||
}
|
||||
if err := cb.Handle(dirent); err != nil {
|
||||
return offset, err
|
||||
}
|
||||
offset++
|
||||
}
|
||||
return offset, nil
|
||||
}
|
||||
|
||||
// fdDirInode represents the inode for /proc/[pid]/fd directory.
|
||||
//
|
||||
// +stateify savable
|
||||
type fdDirInode struct {
|
||||
kernfs.InodeNotSymlink
|
||||
kernfs.InodeDirectoryNoNewChildren
|
||||
kernfs.InodeAttrs
|
||||
kernfs.OrderedChildren
|
||||
kernfs.AlwaysValid
|
||||
fdDir
|
||||
}
|
||||
|
||||
var _ kernfs.Inode = (*fdDirInode)(nil)
|
||||
|
||||
func newFDDirInode(task *kernel.Task, inoGen InoGenerator) *kernfs.Dentry {
|
||||
inode := &fdDirInode{
|
||||
fdDir: fdDir{
|
||||
inoGen: inoGen,
|
||||
task: task,
|
||||
produceSymlink: true,
|
||||
},
|
||||
}
|
||||
inode.InodeAttrs.Init(task.Credentials(), inoGen.NextIno(), linux.ModeDirectory|0555)
|
||||
|
||||
dentry := &kernfs.Dentry{}
|
||||
dentry.Init(inode)
|
||||
inode.OrderedChildren.Init(kernfs.OrderedChildrenOptions{})
|
||||
|
||||
return dentry
|
||||
}
|
||||
|
||||
// Lookup implements kernfs.inodeDynamicLookup.
|
||||
func (i *fdDirInode) Lookup(ctx context.Context, name string) (*vfs.Dentry, error) {
|
||||
file, _, err := i.lookup(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
taskDentry := newFDSymlink(i.task.Credentials(), file, i.inoGen.NextIno())
|
||||
return taskDentry.VFSDentry(), nil
|
||||
}
|
||||
|
||||
// Open implements kernfs.Inode.
|
||||
func (i *fdDirInode) Open(rp *vfs.ResolvingPath, vfsd *vfs.Dentry, opts vfs.OpenOptions) (*vfs.FileDescription, error) {
|
||||
fd := &kernfs.GenericDirectoryFD{}
|
||||
fd.Init(rp.Mount(), vfsd, &i.OrderedChildren, &opts)
|
||||
return fd.VFSFileDescription(), nil
|
||||
}
|
||||
|
||||
// CheckPermissions implements kernfs.Inode.
|
||||
//
|
||||
// This is to match Linux, which uses a special permission handler to guarantee
|
||||
// that a process can still access /proc/self/fd after it has executed
|
||||
// setuid. See fs/proc/fd.c:proc_fd_permission.
|
||||
func (i *fdDirInode) CheckPermissions(ctx context.Context, creds *auth.Credentials, ats vfs.AccessTypes) error {
|
||||
err := i.InodeAttrs.CheckPermissions(ctx, creds, ats)
|
||||
if err == nil {
|
||||
// Access granted, no extra check needed.
|
||||
return nil
|
||||
}
|
||||
if t := kernel.TaskFromContext(ctx); t != nil {
|
||||
// Allow access if the task trying to access it is in the thread group
|
||||
// corresponding to this directory.
|
||||
if i.task.ThreadGroup() == t.ThreadGroup() {
|
||||
// Access granted (overridden).
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// fdSymlink is an symlink for the /proc/[pid]/fd/[fd] file.
|
||||
//
|
||||
// +stateify savable
|
||||
type fdSymlink struct {
|
||||
refs.AtomicRefCount
|
||||
kernfs.InodeAttrs
|
||||
kernfs.InodeSymlink
|
||||
|
||||
file *vfs.FileDescription
|
||||
}
|
||||
|
||||
var _ kernfs.Inode = (*fdSymlink)(nil)
|
||||
|
||||
func newFDSymlink(creds *auth.Credentials, file *vfs.FileDescription, ino uint64) *kernfs.Dentry {
|
||||
file.IncRef()
|
||||
inode := &fdSymlink{file: file}
|
||||
inode.Init(creds, ino, linux.ModeSymlink|0777)
|
||||
|
||||
d := &kernfs.Dentry{}
|
||||
d.Init(inode)
|
||||
return d
|
||||
}
|
||||
|
||||
func (s *fdSymlink) Readlink(ctx context.Context) (string, error) {
|
||||
root := vfs.RootFromContext(ctx)
|
||||
defer root.DecRef()
|
||||
|
||||
vfsObj := s.file.VirtualDentry().Mount().Filesystem().VirtualFilesystem()
|
||||
return vfsObj.PathnameWithDeleted(ctx, root, s.file.VirtualDentry())
|
||||
}
|
||||
|
||||
func (s *fdSymlink) DecRef() {
|
||||
s.AtomicRefCount.DecRefWithDestructor(func() {
|
||||
s.Destroy()
|
||||
})
|
||||
}
|
||||
|
||||
func (s *fdSymlink) Destroy() {
|
||||
s.file.DecRef()
|
||||
}
|
||||
|
||||
// fdInfoDirInode represents the inode for /proc/[pid]/fdinfo directory.
|
||||
//
|
||||
// +stateify savable
|
||||
type fdInfoDirInode struct {
|
||||
kernfs.InodeNotSymlink
|
||||
kernfs.InodeDirectoryNoNewChildren
|
||||
kernfs.InodeAttrs
|
||||
kernfs.OrderedChildren
|
||||
kernfs.AlwaysValid
|
||||
fdDir
|
||||
}
|
||||
|
||||
var _ kernfs.Inode = (*fdInfoDirInode)(nil)
|
||||
|
||||
func newFDInfoDirInode(task *kernel.Task, inoGen InoGenerator) *kernfs.Dentry {
|
||||
inode := &fdInfoDirInode{
|
||||
fdDir: fdDir{
|
||||
inoGen: inoGen,
|
||||
task: task,
|
||||
},
|
||||
}
|
||||
inode.InodeAttrs.Init(task.Credentials(), inoGen.NextIno(), linux.ModeDirectory|0555)
|
||||
|
||||
dentry := &kernfs.Dentry{}
|
||||
dentry.Init(inode)
|
||||
inode.OrderedChildren.Init(kernfs.OrderedChildrenOptions{})
|
||||
|
||||
return dentry
|
||||
}
|
||||
|
||||
// Lookup implements kernfs.inodeDynamicLookup.
|
||||
func (i *fdInfoDirInode) Lookup(ctx context.Context, name string) (*vfs.Dentry, error) {
|
||||
file, flags, err := i.lookup(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
data := &fdInfoData{file: file, flags: flags}
|
||||
dentry := newTaskOwnedFile(i.task, i.inoGen.NextIno(), 0444, data)
|
||||
return dentry.VFSDentry(), nil
|
||||
}
|
||||
|
||||
// Open implements kernfs.Inode.
|
||||
func (i *fdInfoDirInode) Open(rp *vfs.ResolvingPath, vfsd *vfs.Dentry, opts vfs.OpenOptions) (*vfs.FileDescription, error) {
|
||||
fd := &kernfs.GenericDirectoryFD{}
|
||||
fd.Init(rp.Mount(), vfsd, &i.OrderedChildren, &opts)
|
||||
return fd.VFSFileDescription(), nil
|
||||
}
|
||||
|
||||
// fdInfoData implements vfs.DynamicBytesSource for /proc/[pid]/fdinfo/[fd].
|
||||
//
|
||||
// +stateify savable
|
||||
type fdInfoData struct {
|
||||
kernfs.DynamicBytesFile
|
||||
refs.AtomicRefCount
|
||||
|
||||
file *vfs.FileDescription
|
||||
flags kernel.FDFlags
|
||||
}
|
||||
|
||||
var _ dynamicInode = (*fdInfoData)(nil)
|
||||
|
||||
func (d *fdInfoData) DecRef() {
|
||||
d.AtomicRefCount.DecRefWithDestructor(d.destroy)
|
||||
}
|
||||
|
||||
func (d *fdInfoData) destroy() {
|
||||
d.file.DecRef()
|
||||
}
|
||||
|
||||
// Generate implements vfs.DynamicBytesSource.Generate.
|
||||
func (d *fdInfoData) Generate(ctx context.Context, buf *bytes.Buffer) error {
|
||||
// TODO(b/121266871): Include pos, locks, and other data. For now we only
|
||||
// have flags.
|
||||
// See https://www.kernel.org/doc/Documentation/filesystems/proc.txt
|
||||
flags := uint(d.file.StatusFlags()) | d.flags.ToLinuxFileFlags()
|
||||
fmt.Fprintf(buf, "flags:\t0%o\n", flags)
|
||||
return nil
|
||||
}
|
||||
@@ -18,10 +18,14 @@ import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/abi/linux"
|
||||
"gvisor.dev/gvisor/pkg/context"
|
||||
"gvisor.dev/gvisor/pkg/safemem"
|
||||
"gvisor.dev/gvisor/pkg/sentry/fs"
|
||||
"gvisor.dev/gvisor/pkg/sentry/fsbridge"
|
||||
"gvisor.dev/gvisor/pkg/sentry/fsimpl/kernfs"
|
||||
"gvisor.dev/gvisor/pkg/sentry/kernel"
|
||||
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
|
||||
@@ -496,7 +500,7 @@ func (s *statusData) Generate(ctx context.Context, buf *bytes.Buffer) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// ioUsage is the /proc/<pid>/io and /proc/<pid>/task/<tid>/io data provider.
|
||||
// ioUsage is the /proc/[pid]/io and /proc/[pid]/task/[tid]/io data provider.
|
||||
type ioUsage interface {
|
||||
// IOUsage returns the io usage data.
|
||||
IOUsage() *usage.IO
|
||||
@@ -570,3 +574,248 @@ func (o *oomScoreAdj) Write(ctx context.Context, src usermem.IOSequence, offset
|
||||
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// exeSymlink is an symlink for the /proc/[pid]/exe file.
|
||||
//
|
||||
// +stateify savable
|
||||
type exeSymlink struct {
|
||||
kernfs.InodeAttrs
|
||||
kernfs.InodeNoopRefCount
|
||||
kernfs.InodeSymlink
|
||||
|
||||
task *kernel.Task
|
||||
}
|
||||
|
||||
var _ kernfs.Inode = (*exeSymlink)(nil)
|
||||
|
||||
func newExeSymlink(task *kernel.Task, ino uint64) *kernfs.Dentry {
|
||||
inode := &exeSymlink{task: task}
|
||||
inode.Init(task.Credentials(), ino, linux.ModeSymlink|0777)
|
||||
|
||||
d := &kernfs.Dentry{}
|
||||
d.Init(inode)
|
||||
return d
|
||||
}
|
||||
|
||||
// Readlink implements kernfs.Inode.
|
||||
func (s *exeSymlink) Readlink(ctx context.Context) (string, error) {
|
||||
if !kernel.ContextCanTrace(ctx, s.task, false) {
|
||||
return "", syserror.EACCES
|
||||
}
|
||||
|
||||
// Pull out the executable for /proc/[pid]/exe.
|
||||
exec, err := s.executable()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer exec.DecRef()
|
||||
|
||||
return exec.PathnameWithDeleted(ctx), nil
|
||||
}
|
||||
|
||||
func (s *exeSymlink) executable() (file fsbridge.File, err error) {
|
||||
s.task.WithMuLocked(func(t *kernel.Task) {
|
||||
mm := t.MemoryManager()
|
||||
if mm == nil {
|
||||
// TODO(b/34851096): Check shouldn't allow Readlink once the
|
||||
// Task is zombied.
|
||||
err = syserror.EACCES
|
||||
return
|
||||
}
|
||||
|
||||
// The MemoryManager may be destroyed, in which case
|
||||
// MemoryManager.destroy will simply set the executable to nil
|
||||
// (with locks held).
|
||||
file = mm.Executable()
|
||||
if file == nil {
|
||||
err = syserror.ENOENT
|
||||
}
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// forEachMountSource runs f for the process root mount and each mount that is
|
||||
// a descendant of the root.
|
||||
func forEachMount(t *kernel.Task, fn func(string, *fs.Mount)) {
|
||||
var fsctx *kernel.FSContext
|
||||
t.WithMuLocked(func(t *kernel.Task) {
|
||||
fsctx = t.FSContext()
|
||||
})
|
||||
if fsctx == nil {
|
||||
// The task has been destroyed. Nothing to show here.
|
||||
return
|
||||
}
|
||||
|
||||
// All mount points must be relative to the rootDir, and mounts outside
|
||||
// will be excluded.
|
||||
rootDir := fsctx.RootDirectory()
|
||||
if rootDir == nil {
|
||||
// The task has been destroyed. Nothing to show here.
|
||||
return
|
||||
}
|
||||
defer rootDir.DecRef()
|
||||
|
||||
mnt := t.MountNamespace().FindMount(rootDir)
|
||||
if mnt == nil {
|
||||
// Has it just been unmounted?
|
||||
return
|
||||
}
|
||||
ms := t.MountNamespace().AllMountsUnder(mnt)
|
||||
sort.Slice(ms, func(i, j int) bool {
|
||||
return ms[i].ID < ms[j].ID
|
||||
})
|
||||
for _, m := range ms {
|
||||
mroot := m.Root()
|
||||
if mroot == nil {
|
||||
continue // No longer valid.
|
||||
}
|
||||
mountPath, desc := mroot.FullName(rootDir)
|
||||
mroot.DecRef()
|
||||
if !desc {
|
||||
// MountSources that are not descendants of the chroot jail are ignored.
|
||||
continue
|
||||
}
|
||||
fn(mountPath, m)
|
||||
}
|
||||
}
|
||||
|
||||
// mountInfoData is used to implement /proc/[pid]/mountinfo.
|
||||
//
|
||||
// +stateify savable
|
||||
type mountInfoData struct {
|
||||
kernfs.DynamicBytesFile
|
||||
|
||||
task *kernel.Task
|
||||
}
|
||||
|
||||
var _ dynamicInode = (*mountInfoData)(nil)
|
||||
|
||||
// Generate implements vfs.DynamicBytesSource.Generate.
|
||||
func (i *mountInfoData) Generate(ctx context.Context, buf *bytes.Buffer) error {
|
||||
forEachMount(i.task, func(mountPath string, m *fs.Mount) {
|
||||
mroot := m.Root()
|
||||
if mroot == nil {
|
||||
return // No longer valid.
|
||||
}
|
||||
defer mroot.DecRef()
|
||||
|
||||
// Format:
|
||||
// 36 35 98:0 /mnt1 /mnt2 rw,noatime master:1 - ext3 /dev/root rw,errors=continue
|
||||
// (1)(2)(3) (4) (5) (6) (7) (8) (9) (10) (11)
|
||||
|
||||
// (1) MountSource ID.
|
||||
fmt.Fprintf(buf, "%d ", m.ID)
|
||||
|
||||
// (2) Parent ID (or this ID if there is no parent).
|
||||
pID := m.ID
|
||||
if !m.IsRoot() && !m.IsUndo() {
|
||||
pID = m.ParentID
|
||||
}
|
||||
fmt.Fprintf(buf, "%d ", pID)
|
||||
|
||||
// (3) Major:Minor device ID. We don't have a superblock, so we
|
||||
// just use the root inode device number.
|
||||
sa := mroot.Inode.StableAttr
|
||||
fmt.Fprintf(buf, "%d:%d ", sa.DeviceFileMajor, sa.DeviceFileMinor)
|
||||
|
||||
// (4) Root: the pathname of the directory in the filesystem
|
||||
// which forms the root of this mount.
|
||||
//
|
||||
// NOTE(b/78135857): This will always be "/" until we implement
|
||||
// bind mounts.
|
||||
fmt.Fprintf(buf, "/ ")
|
||||
|
||||
// (5) Mount point (relative to process root).
|
||||
fmt.Fprintf(buf, "%s ", mountPath)
|
||||
|
||||
// (6) Mount options.
|
||||
flags := mroot.Inode.MountSource.Flags
|
||||
opts := "rw"
|
||||
if flags.ReadOnly {
|
||||
opts = "ro"
|
||||
}
|
||||
if flags.NoAtime {
|
||||
opts += ",noatime"
|
||||
}
|
||||
if flags.NoExec {
|
||||
opts += ",noexec"
|
||||
}
|
||||
fmt.Fprintf(buf, "%s ", opts)
|
||||
|
||||
// (7) Optional fields: zero or more fields of the form "tag[:value]".
|
||||
// (8) Separator: the end of the optional fields is marked by a single hyphen.
|
||||
fmt.Fprintf(buf, "- ")
|
||||
|
||||
// (9) Filesystem type.
|
||||
fmt.Fprintf(buf, "%s ", mroot.Inode.MountSource.FilesystemType)
|
||||
|
||||
// (10) Mount source: filesystem-specific information or "none".
|
||||
fmt.Fprintf(buf, "none ")
|
||||
|
||||
// (11) Superblock options, and final newline.
|
||||
fmt.Fprintf(buf, "%s\n", superBlockOpts(mountPath, mroot.Inode.MountSource))
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
func superBlockOpts(mountPath string, msrc *fs.MountSource) string {
|
||||
// gVisor doesn't (yet) have a concept of super block options, so we
|
||||
// use the ro/rw bit from the mount flag.
|
||||
opts := "rw"
|
||||
if msrc.Flags.ReadOnly {
|
||||
opts = "ro"
|
||||
}
|
||||
|
||||
// NOTE(b/147673608): If the mount is a cgroup, we also need to include
|
||||
// the cgroup name in the options. For now we just read that from the
|
||||
// path.
|
||||
// TODO(gvisor.dev/issues/190): Once gVisor has full cgroup support, we
|
||||
// should get this value from the cgroup itself, and not rely on the
|
||||
// path.
|
||||
if msrc.FilesystemType == "cgroup" {
|
||||
splitPath := strings.Split(mountPath, "/")
|
||||
cgroupType := splitPath[len(splitPath)-1]
|
||||
opts += "," + cgroupType
|
||||
}
|
||||
return opts
|
||||
}
|
||||
|
||||
// mountsData is used to implement /proc/[pid]/mounts.
|
||||
//
|
||||
// +stateify savable
|
||||
type mountsData struct {
|
||||
kernfs.DynamicBytesFile
|
||||
|
||||
task *kernel.Task
|
||||
}
|
||||
|
||||
var _ dynamicInode = (*mountInfoData)(nil)
|
||||
|
||||
// Generate implements vfs.DynamicBytesSource.Generate.
|
||||
func (i *mountsData) Generate(ctx context.Context, buf *bytes.Buffer) error {
|
||||
forEachMount(i.task, func(mountPath string, m *fs.Mount) {
|
||||
// Format:
|
||||
// <special device or remote filesystem> <mount point> <filesystem type> <mount options> <needs dump> <fsck order>
|
||||
//
|
||||
// We use the filesystem name as the first field, since there
|
||||
// is no real block device we can point to, and we also should
|
||||
// not expose anything about the remote filesystem.
|
||||
//
|
||||
// Only ro/rw option is supported for now.
|
||||
//
|
||||
// The "needs dump"and fsck flags are always 0, which is allowed.
|
||||
root := m.Root()
|
||||
if root == nil {
|
||||
return // No longer valid.
|
||||
}
|
||||
defer root.DecRef()
|
||||
|
||||
flags := root.Inode.MountSource.Flags
|
||||
opts := "rw"
|
||||
if flags.ReadOnly {
|
||||
opts = "ro"
|
||||
}
|
||||
fmt.Fprintf(buf, "%s %s %s %s %d %d\n", "none", mountPath, root.Inode.MountSource.FilesystemType, opts, 0, 0)
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -46,6 +46,7 @@ type tasksInode struct {
|
||||
kernfs.InodeDirectoryNoNewChildren
|
||||
kernfs.InodeAttrs
|
||||
kernfs.OrderedChildren
|
||||
kernfs.AlwaysValid
|
||||
|
||||
inoGen InoGenerator
|
||||
pidns *kernel.PIDNamespace
|
||||
@@ -66,23 +67,23 @@ var _ kernfs.Inode = (*tasksInode)(nil)
|
||||
func newTasksInode(inoGen InoGenerator, k *kernel.Kernel, pidns *kernel.PIDNamespace, cgroupControllers map[string]string) (*tasksInode, *kernfs.Dentry) {
|
||||
root := auth.NewRootCredentials(pidns.UserNamespace())
|
||||
contents := map[string]*kernfs.Dentry{
|
||||
"cpuinfo": newDentry(root, inoGen.NextIno(), 0444, newStaticFile(cpuInfoData(k))),
|
||||
//"filesystems": newDentry(root, inoGen.NextIno(), 0444, &filesystemsData{}),
|
||||
"loadavg": newDentry(root, inoGen.NextIno(), 0444, &loadavgData{}),
|
||||
"sys": newSysDir(root, inoGen, k),
|
||||
"meminfo": newDentry(root, inoGen.NextIno(), 0444, &meminfoData{}),
|
||||
"mounts": kernfs.NewStaticSymlink(root, inoGen.NextIno(), "self/mounts"),
|
||||
"net": kernfs.NewStaticSymlink(root, inoGen.NextIno(), "self/net"),
|
||||
"stat": newDentry(root, inoGen.NextIno(), 0444, &statData{k: k}),
|
||||
"uptime": newDentry(root, inoGen.NextIno(), 0444, &uptimeData{}),
|
||||
"version": newDentry(root, inoGen.NextIno(), 0444, &versionData{k: k}),
|
||||
"cpuinfo": newDentry(root, inoGen.NextIno(), 0444, newStaticFile(cpuInfoData(k))),
|
||||
"filesystems": newDentry(root, inoGen.NextIno(), 0444, &filesystemsData{}),
|
||||
"loadavg": newDentry(root, inoGen.NextIno(), 0444, &loadavgData{}),
|
||||
"sys": newSysDir(root, inoGen, k),
|
||||
"meminfo": newDentry(root, inoGen.NextIno(), 0444, &meminfoData{}),
|
||||
"mounts": kernfs.NewStaticSymlink(root, inoGen.NextIno(), "self/mounts"),
|
||||
"net": kernfs.NewStaticSymlink(root, inoGen.NextIno(), "self/net"),
|
||||
"stat": newDentry(root, inoGen.NextIno(), 0444, &statData{}),
|
||||
"uptime": newDentry(root, inoGen.NextIno(), 0444, &uptimeData{}),
|
||||
"version": newDentry(root, inoGen.NextIno(), 0444, &versionData{}),
|
||||
}
|
||||
|
||||
inode := &tasksInode{
|
||||
pidns: pidns,
|
||||
inoGen: inoGen,
|
||||
selfSymlink: newSelfSymlink(root, inoGen.NextIno(), 0444, pidns).VFSDentry(),
|
||||
threadSelfSymlink: newThreadSelfSymlink(root, inoGen.NextIno(), 0444, pidns).VFSDentry(),
|
||||
selfSymlink: newSelfSymlink(root, inoGen.NextIno(), pidns).VFSDentry(),
|
||||
threadSelfSymlink: newThreadSelfSymlink(root, inoGen.NextIno(), pidns).VFSDentry(),
|
||||
cgroupControllers: cgroupControllers,
|
||||
}
|
||||
inode.InodeAttrs.Init(root, inoGen.NextIno(), linux.ModeDirectory|0555)
|
||||
@@ -121,11 +122,6 @@ func (i *tasksInode) Lookup(ctx context.Context, name string) (*vfs.Dentry, erro
|
||||
return taskDentry.VFSDentry(), nil
|
||||
}
|
||||
|
||||
// Valid implements kernfs.inodeDynamicLookup.
|
||||
func (i *tasksInode) Valid(ctx context.Context) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// IterDirents implements kernfs.inodeDynamicLookup.
|
||||
func (i *tasksInode) IterDirents(ctx context.Context, cb vfs.IterDirentsCallback, offset, _ int64) (int64, error) {
|
||||
// fs/proc/internal.h: #define FIRST_PROCESS_ENTRY 256
|
||||
|
||||
@@ -40,9 +40,9 @@ type selfSymlink struct {
|
||||
|
||||
var _ kernfs.Inode = (*selfSymlink)(nil)
|
||||
|
||||
func newSelfSymlink(creds *auth.Credentials, ino uint64, perm linux.FileMode, pidns *kernel.PIDNamespace) *kernfs.Dentry {
|
||||
func newSelfSymlink(creds *auth.Credentials, ino uint64, pidns *kernel.PIDNamespace) *kernfs.Dentry {
|
||||
inode := &selfSymlink{pidns: pidns}
|
||||
inode.Init(creds, ino, linux.ModeSymlink|perm)
|
||||
inode.Init(creds, ino, linux.ModeSymlink|0777)
|
||||
|
||||
d := &kernfs.Dentry{}
|
||||
d.Init(inode)
|
||||
@@ -72,9 +72,9 @@ type threadSelfSymlink struct {
|
||||
|
||||
var _ kernfs.Inode = (*threadSelfSymlink)(nil)
|
||||
|
||||
func newThreadSelfSymlink(creds *auth.Credentials, ino uint64, perm linux.FileMode, pidns *kernel.PIDNamespace) *kernfs.Dentry {
|
||||
func newThreadSelfSymlink(creds *auth.Credentials, ino uint64, pidns *kernel.PIDNamespace) *kernfs.Dentry {
|
||||
inode := &threadSelfSymlink{pidns: pidns}
|
||||
inode.Init(creds, ino, linux.ModeSymlink|perm)
|
||||
inode.Init(creds, ino, linux.ModeSymlink|0777)
|
||||
|
||||
d := &kernfs.Dentry{}
|
||||
d.Init(inode)
|
||||
@@ -138,21 +138,19 @@ func (c cpuStats) String() string {
|
||||
// +stateify savable
|
||||
type statData struct {
|
||||
kernfs.DynamicBytesFile
|
||||
|
||||
// k is the owning Kernel.
|
||||
k *kernel.Kernel
|
||||
}
|
||||
|
||||
var _ dynamicInode = (*statData)(nil)
|
||||
|
||||
// Generate implements vfs.DynamicBytesSource.Generate.
|
||||
func (s *statData) Generate(ctx context.Context, buf *bytes.Buffer) error {
|
||||
func (*statData) Generate(ctx context.Context, buf *bytes.Buffer) error {
|
||||
// TODO(b/37226836): We currently export only zero CPU stats. We could
|
||||
// at least provide some aggregate stats.
|
||||
var cpu cpuStats
|
||||
fmt.Fprintf(buf, "cpu %s\n", cpu)
|
||||
|
||||
for c, max := uint(0), s.k.ApplicationCores(); c < max; c++ {
|
||||
k := kernel.KernelFromContext(ctx)
|
||||
for c, max := uint(0), k.ApplicationCores(); c < max; c++ {
|
||||
fmt.Fprintf(buf, "cpu%d %s\n", c, cpu)
|
||||
}
|
||||
|
||||
@@ -176,7 +174,7 @@ func (s *statData) Generate(ctx context.Context, buf *bytes.Buffer) error {
|
||||
fmt.Fprintf(buf, "ctxt 0\n")
|
||||
|
||||
// CLOCK_REALTIME timestamp from boot, in seconds.
|
||||
fmt.Fprintf(buf, "btime %d\n", s.k.Timekeeper().BootTime().Seconds())
|
||||
fmt.Fprintf(buf, "btime %d\n", k.Timekeeper().BootTime().Seconds())
|
||||
|
||||
// Total number of clones.
|
||||
// TODO(b/37226836): Count this.
|
||||
@@ -209,7 +207,7 @@ type loadavgData struct {
|
||||
var _ dynamicInode = (*loadavgData)(nil)
|
||||
|
||||
// Generate implements vfs.DynamicBytesSource.Generate.
|
||||
func (d *loadavgData) Generate(ctx context.Context, buf *bytes.Buffer) error {
|
||||
func (*loadavgData) Generate(ctx context.Context, buf *bytes.Buffer) error {
|
||||
// TODO(b/62345059): Include real data in fields.
|
||||
// Column 1-3: CPU and IO utilization of the last 1, 5, and 10 minute periods.
|
||||
// Column 4-5: currently running processes and the total number of processes.
|
||||
@@ -223,16 +221,14 @@ func (d *loadavgData) Generate(ctx context.Context, buf *bytes.Buffer) error {
|
||||
// +stateify savable
|
||||
type meminfoData struct {
|
||||
kernfs.DynamicBytesFile
|
||||
|
||||
// k is the owning Kernel.
|
||||
k *kernel.Kernel
|
||||
}
|
||||
|
||||
var _ dynamicInode = (*meminfoData)(nil)
|
||||
|
||||
// Generate implements vfs.DynamicBytesSource.Generate.
|
||||
func (d *meminfoData) Generate(ctx context.Context, buf *bytes.Buffer) error {
|
||||
mf := d.k.MemoryFile()
|
||||
func (*meminfoData) Generate(ctx context.Context, buf *bytes.Buffer) error {
|
||||
k := kernel.KernelFromContext(ctx)
|
||||
mf := k.MemoryFile()
|
||||
mf.UpdateUsage()
|
||||
snapshot, totalUsage := usage.MemoryAccounting.Copy()
|
||||
totalSize := usage.TotalMemory(mf.TotalSize(), totalUsage)
|
||||
@@ -295,16 +291,14 @@ func (*uptimeData) Generate(ctx context.Context, buf *bytes.Buffer) error {
|
||||
// +stateify savable
|
||||
type versionData struct {
|
||||
kernfs.DynamicBytesFile
|
||||
|
||||
// k is the owning Kernel.
|
||||
k *kernel.Kernel
|
||||
}
|
||||
|
||||
var _ dynamicInode = (*versionData)(nil)
|
||||
|
||||
// Generate implements vfs.DynamicBytesSource.Generate.
|
||||
func (v *versionData) Generate(ctx context.Context, buf *bytes.Buffer) error {
|
||||
init := v.k.GlobalInit()
|
||||
func (*versionData) Generate(ctx context.Context, buf *bytes.Buffer) error {
|
||||
k := kernel.KernelFromContext(ctx)
|
||||
init := k.GlobalInit()
|
||||
if init == nil {
|
||||
// Attempted to read before the init Task is created. This can
|
||||
// only occur during startup, which should never need to read
|
||||
@@ -335,3 +329,19 @@ func (v *versionData) Generate(ctx context.Context, buf *bytes.Buffer) error {
|
||||
fmt.Fprintf(buf, "%s version %s %s\n", ver.Sysname, ver.Release, ver.Version)
|
||||
return nil
|
||||
}
|
||||
|
||||
// filesystemsData backs /proc/filesystems.
|
||||
//
|
||||
// +stateify savable
|
||||
type filesystemsData struct {
|
||||
kernfs.DynamicBytesFile
|
||||
}
|
||||
|
||||
var _ dynamicInode = (*filesystemsData)(nil)
|
||||
|
||||
// Generate implements vfs.DynamicBytesSource.Generate.
|
||||
func (d *filesystemsData) Generate(ctx context.Context, buf *bytes.Buffer) error {
|
||||
k := kernel.KernelFromContext(ctx)
|
||||
k.VFS().GenerateProcFilesystems(buf)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ import (
|
||||
"gvisor.dev/gvisor/pkg/context"
|
||||
"gvisor.dev/gvisor/pkg/fspath"
|
||||
"gvisor.dev/gvisor/pkg/sentry/fsimpl/testutil"
|
||||
"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/vfs"
|
||||
@@ -47,6 +48,7 @@ var (
|
||||
var (
|
||||
tasksStaticFiles = map[string]testutil.DirentType{
|
||||
"cpuinfo": linux.DT_REG,
|
||||
"filesystems": linux.DT_REG,
|
||||
"loadavg": linux.DT_REG,
|
||||
"meminfo": linux.DT_REG,
|
||||
"mounts": linux.DT_LNK,
|
||||
@@ -68,9 +70,14 @@ var (
|
||||
"cmdline": linux.DT_REG,
|
||||
"comm": linux.DT_REG,
|
||||
"environ": linux.DT_REG,
|
||||
"exe": linux.DT_LNK,
|
||||
"fd": linux.DT_DIR,
|
||||
"fdinfo": linux.DT_DIR,
|
||||
"gid_map": linux.DT_REG,
|
||||
"io": linux.DT_REG,
|
||||
"maps": linux.DT_REG,
|
||||
"mountinfo": linux.DT_REG,
|
||||
"mounts": linux.DT_REG,
|
||||
"net": linux.DT_DIR,
|
||||
"ns": linux.DT_DIR,
|
||||
"oom_score": linux.DT_REG,
|
||||
@@ -96,17 +103,37 @@ func setup(t *testing.T) *testutil.System {
|
||||
k.VFS().MustRegisterFilesystemType(Name, &FilesystemType{}, &vfs.RegisterFilesystemTypeOptions{
|
||||
AllowUserMount: true,
|
||||
})
|
||||
fsOpts := vfs.GetFilesystemOptions{
|
||||
InternalData: &InternalData{
|
||||
Cgroups: map[string]string{
|
||||
"cpuset": "/foo/cpuset",
|
||||
"memory": "/foo/memory",
|
||||
|
||||
mntns, err := k.VFS().NewMountNamespace(ctx, creds, "", tmpfs.Name, &vfs.GetFilesystemOptions{})
|
||||
if err != nil {
|
||||
t.Fatalf("NewMountNamespace(): %v", err)
|
||||
}
|
||||
pop := &vfs.PathOperation{
|
||||
Root: mntns.Root(),
|
||||
Start: mntns.Root(),
|
||||
Path: fspath.Parse("/proc"),
|
||||
}
|
||||
if err := k.VFS().MkdirAt(ctx, creds, pop, &vfs.MkdirOptions{Mode: 0777}); err != nil {
|
||||
t.Fatalf("MkDir(/proc): %v", err)
|
||||
}
|
||||
|
||||
pop = &vfs.PathOperation{
|
||||
Root: mntns.Root(),
|
||||
Start: mntns.Root(),
|
||||
Path: fspath.Parse("/proc"),
|
||||
}
|
||||
mntOpts := &vfs.MountOptions{
|
||||
GetFilesystemOptions: vfs.GetFilesystemOptions{
|
||||
InternalData: &InternalData{
|
||||
Cgroups: map[string]string{
|
||||
"cpuset": "/foo/cpuset",
|
||||
"memory": "/foo/memory",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
mntns, err := k.VFS().NewMountNamespace(ctx, creds, "", Name, &fsOpts)
|
||||
if err != nil {
|
||||
t.Fatalf("NewMountNamespace(): %v", err)
|
||||
if err := k.VFS().MountAt(ctx, creds, "", pop, Name, mntOpts); err != nil {
|
||||
t.Fatalf("MountAt(/proc): %v", err)
|
||||
}
|
||||
return testutil.NewSystem(ctx, t, k.VFS(), mntns)
|
||||
}
|
||||
@@ -115,7 +142,7 @@ func TestTasksEmpty(t *testing.T) {
|
||||
s := setup(t)
|
||||
defer s.Destroy()
|
||||
|
||||
collector := s.ListDirents(s.PathOpAtRoot("/"))
|
||||
collector := s.ListDirents(s.PathOpAtRoot("/proc"))
|
||||
s.AssertAllDirentTypes(collector, tasksStaticFiles)
|
||||
s.AssertDirentOffsets(collector, tasksStaticFilesNextOffs)
|
||||
}
|
||||
@@ -141,7 +168,7 @@ func TestTasks(t *testing.T) {
|
||||
expectedDirents[fmt.Sprintf("%d", i+1)] = linux.DT_DIR
|
||||
}
|
||||
|
||||
collector := s.ListDirents(s.PathOpAtRoot("/"))
|
||||
collector := s.ListDirents(s.PathOpAtRoot("/proc"))
|
||||
s.AssertAllDirentTypes(collector, expectedDirents)
|
||||
s.AssertDirentOffsets(collector, tasksStaticFilesNextOffs)
|
||||
|
||||
@@ -181,7 +208,7 @@ func TestTasks(t *testing.T) {
|
||||
}
|
||||
|
||||
// Test lookup.
|
||||
for _, path := range []string{"/1", "/2"} {
|
||||
for _, path := range []string{"/proc/1", "/proc/2"} {
|
||||
fd, err := s.VFS.OpenAt(
|
||||
s.Ctx,
|
||||
s.Creds,
|
||||
@@ -191,6 +218,7 @@ func TestTasks(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("vfsfs.OpenAt(%q) failed: %v", path, err)
|
||||
}
|
||||
defer fd.DecRef()
|
||||
buf := make([]byte, 1)
|
||||
bufIOSeq := usermem.BytesIOSequence(buf)
|
||||
if _, err := fd.Read(s.Ctx, bufIOSeq, vfs.ReadOptions{}); err != syserror.EISDIR {
|
||||
@@ -201,10 +229,10 @@ func TestTasks(t *testing.T) {
|
||||
if _, err := s.VFS.OpenAt(
|
||||
s.Ctx,
|
||||
s.Creds,
|
||||
s.PathOpAtRoot("/9999"),
|
||||
s.PathOpAtRoot("/proc/9999"),
|
||||
&vfs.OpenOptions{},
|
||||
); err != syserror.ENOENT {
|
||||
t.Fatalf("wrong error from vfsfs.OpenAt(/9999): %v", err)
|
||||
t.Fatalf("wrong error from vfsfs.OpenAt(/proc/9999): %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -302,12 +330,13 @@ func TestTasksOffset(t *testing.T) {
|
||||
fd, err := s.VFS.OpenAt(
|
||||
s.Ctx,
|
||||
s.Creds,
|
||||
s.PathOpAtRoot("/"),
|
||||
s.PathOpAtRoot("/proc"),
|
||||
&vfs.OpenOptions{},
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("vfsfs.OpenAt(/) failed: %v", err)
|
||||
}
|
||||
defer fd.DecRef()
|
||||
if _, err := fd.Seek(s.Ctx, tc.offset, linux.SEEK_SET); err != nil {
|
||||
t.Fatalf("Seek(%d, SEEK_SET): %v", tc.offset, err)
|
||||
}
|
||||
@@ -344,7 +373,7 @@ func TestTask(t *testing.T) {
|
||||
t.Fatalf("CreateTask(): %v", err)
|
||||
}
|
||||
|
||||
collector := s.ListDirents(s.PathOpAtRoot("/1"))
|
||||
collector := s.ListDirents(s.PathOpAtRoot("/proc/1"))
|
||||
s.AssertAllDirentTypes(collector, taskStaticFiles)
|
||||
}
|
||||
|
||||
@@ -362,14 +391,14 @@ func TestProcSelf(t *testing.T) {
|
||||
collector := s.WithTemporaryContext(task).ListDirents(&vfs.PathOperation{
|
||||
Root: s.Root,
|
||||
Start: s.Root,
|
||||
Path: fspath.Parse("/self/"),
|
||||
Path: fspath.Parse("/proc/self/"),
|
||||
FollowFinalSymlink: true,
|
||||
})
|
||||
s.AssertAllDirentTypes(collector, taskStaticFiles)
|
||||
}
|
||||
|
||||
func iterateDir(ctx context.Context, t *testing.T, s *testutil.System, fd *vfs.FileDescription) {
|
||||
t.Logf("Iterating: /proc%s", fd.MappedName(ctx))
|
||||
t.Logf("Iterating: %s", fd.MappedName(ctx))
|
||||
|
||||
var collector testutil.DirentCollector
|
||||
if err := fd.IterDirents(ctx, &collector); err != nil {
|
||||
@@ -412,6 +441,7 @@ func iterateDir(ctx context.Context, t *testing.T, s *testutil.System, fd *vfs.F
|
||||
t.Errorf("vfsfs.OpenAt(%v) failed: %v", childPath, err)
|
||||
continue
|
||||
}
|
||||
defer child.DecRef()
|
||||
stat, err := child.Stat(ctx, vfs.StatOptions{})
|
||||
if err != nil {
|
||||
t.Errorf("Stat(%v) failed: %v", childPath, err)
|
||||
@@ -432,6 +462,22 @@ func TestTree(t *testing.T) {
|
||||
defer s.Destroy()
|
||||
|
||||
k := kernel.KernelFromContext(s.Ctx)
|
||||
|
||||
pop := &vfs.PathOperation{
|
||||
Root: s.Root,
|
||||
Start: s.Root,
|
||||
Path: fspath.Parse("test-file"),
|
||||
}
|
||||
opts := &vfs.OpenOptions{
|
||||
Flags: linux.O_RDONLY | linux.O_CREAT,
|
||||
Mode: 0777,
|
||||
}
|
||||
file, err := s.VFS.OpenAt(s.Ctx, s.Creds, pop, opts)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create test file: %v", err)
|
||||
}
|
||||
defer file.DecRef()
|
||||
|
||||
var tasks []*kernel.Task
|
||||
for i := 0; i < 5; i++ {
|
||||
tc := k.NewThreadGroup(nil, k.RootPIDNamespace(), kernel.NewSignalHandlers(), linux.SIGCHLD, k.GlobalInit().Limits())
|
||||
@@ -439,6 +485,8 @@ func TestTree(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("CreateTask(): %v", err)
|
||||
}
|
||||
// Add file to populate /proc/[pid]/fd and fdinfo directories.
|
||||
task.FDTable().NewFDVFS2(task, 0, file, kernel.FDFlags{})
|
||||
tasks = append(tasks, task)
|
||||
}
|
||||
|
||||
@@ -446,11 +494,12 @@ func TestTree(t *testing.T) {
|
||||
fd, err := s.VFS.OpenAt(
|
||||
ctx,
|
||||
auth.CredentialsFromContext(s.Ctx),
|
||||
&vfs.PathOperation{Root: s.Root, Start: s.Root, Path: fspath.Parse("/")},
|
||||
&vfs.PathOperation{Root: s.Root, Start: s.Root, Path: fspath.Parse("/proc")},
|
||||
&vfs.OpenOptions{},
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("vfsfs.OpenAt(/) failed: %v", err)
|
||||
t.Fatalf("vfsfs.OpenAt(/proc) failed: %v", err)
|
||||
}
|
||||
iterateDir(ctx, t, s, fd)
|
||||
fd.DecRef()
|
||||
}
|
||||
|
||||
@@ -16,12 +16,14 @@ go_library(
|
||||
"//pkg/cpuid",
|
||||
"//pkg/fspath",
|
||||
"//pkg/memutil",
|
||||
"//pkg/sentry/fsbridge",
|
||||
"//pkg/sentry/fsimpl/tmpfs",
|
||||
"//pkg/sentry/kernel",
|
||||
"//pkg/sentry/kernel/auth",
|
||||
"//pkg/sentry/kernel/sched",
|
||||
"//pkg/sentry/limits",
|
||||
"//pkg/sentry/loader",
|
||||
"//pkg/sentry/mm",
|
||||
"//pkg/sentry/pgalloc",
|
||||
"//pkg/sentry/platform",
|
||||
"//pkg/sentry/platform/kvm",
|
||||
|
||||
@@ -23,13 +23,16 @@ import (
|
||||
"gvisor.dev/gvisor/pkg/abi/linux"
|
||||
"gvisor.dev/gvisor/pkg/context"
|
||||
"gvisor.dev/gvisor/pkg/cpuid"
|
||||
"gvisor.dev/gvisor/pkg/fspath"
|
||||
"gvisor.dev/gvisor/pkg/memutil"
|
||||
"gvisor.dev/gvisor/pkg/sentry/fsbridge"
|
||||
"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"
|
||||
"gvisor.dev/gvisor/pkg/sentry/limits"
|
||||
"gvisor.dev/gvisor/pkg/sentry/loader"
|
||||
"gvisor.dev/gvisor/pkg/sentry/mm"
|
||||
"gvisor.dev/gvisor/pkg/sentry/pgalloc"
|
||||
"gvisor.dev/gvisor/pkg/sentry/platform"
|
||||
"gvisor.dev/gvisor/pkg/sentry/time"
|
||||
@@ -123,10 +126,17 @@ func Boot() (*kernel.Kernel, error) {
|
||||
// CreateTask creates a new bare bones task for tests.
|
||||
func CreateTask(ctx context.Context, name string, tc *kernel.ThreadGroup, mntns *vfs.MountNamespace, root, cwd vfs.VirtualDentry) (*kernel.Task, error) {
|
||||
k := kernel.KernelFromContext(ctx)
|
||||
exe, err := newFakeExecutable(ctx, k.VFS(), auth.CredentialsFromContext(ctx), root)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
m := mm.NewMemoryManager(k, k, k.SleepForAddressSpaceActivation)
|
||||
m.SetExecutable(fsbridge.NewVFSFile(exe))
|
||||
|
||||
config := &kernel.TaskConfig{
|
||||
Kernel: k,
|
||||
ThreadGroup: tc,
|
||||
TaskContext: &kernel.TaskContext{Name: name},
|
||||
TaskContext: &kernel.TaskContext{Name: name, MemoryManager: m},
|
||||
Credentials: auth.CredentialsFromContext(ctx),
|
||||
NetworkNamespace: k.RootNetworkNamespace(),
|
||||
AllowedCPUMask: sched.NewFullCPUSet(k.ApplicationCores()),
|
||||
@@ -135,10 +145,25 @@ func CreateTask(ctx context.Context, name string, tc *kernel.ThreadGroup, mntns
|
||||
AbstractSocketNamespace: kernel.NewAbstractSocketNamespace(),
|
||||
MountNamespaceVFS2: mntns,
|
||||
FSContext: kernel.NewFSContextVFS2(root, cwd, 0022),
|
||||
FDTable: k.NewFDTable(),
|
||||
}
|
||||
return k.TaskSet().NewTask(config)
|
||||
}
|
||||
|
||||
func newFakeExecutable(ctx context.Context, vfsObj *vfs.VirtualFilesystem, creds *auth.Credentials, root vfs.VirtualDentry) (*vfs.FileDescription, error) {
|
||||
const name = "executable"
|
||||
pop := &vfs.PathOperation{
|
||||
Root: root,
|
||||
Start: root,
|
||||
Path: fspath.Parse(name),
|
||||
}
|
||||
opts := &vfs.OpenOptions{
|
||||
Flags: linux.O_RDONLY | linux.O_CREAT,
|
||||
Mode: 0777,
|
||||
}
|
||||
return vfsObj.OpenAt(ctx, creds, pop, opts)
|
||||
}
|
||||
|
||||
func createMemoryFile() (*pgalloc.MemoryFile, error) {
|
||||
const memfileName = "test-memory"
|
||||
memfd, err := memutil.CreateMemFD(memfileName, 0)
|
||||
|
||||
@@ -191,7 +191,7 @@ func (f *FDTable) Size() int {
|
||||
return int(size)
|
||||
}
|
||||
|
||||
// forEach iterates over all non-nil files.
|
||||
// forEach iterates over all non-nil files in sorted order.
|
||||
//
|
||||
// It is the caller's responsibility to acquire an appropriate lock.
|
||||
func (f *FDTable) forEach(fn func(fd int32, file *fs.File, fileVFS2 *vfs.FileDescription, flags FDFlags)) {
|
||||
@@ -458,7 +458,10 @@ func (f *FDTable) GetVFS2(fd int32) (*vfs.FileDescription, FDFlags) {
|
||||
}
|
||||
}
|
||||
|
||||
// GetFDs returns a list of valid fds.
|
||||
// GetFDs returns a sorted list of valid fds.
|
||||
//
|
||||
// Precondition: The caller must be running on the task goroutine, or Task.mu
|
||||
// must be locked.
|
||||
func (f *FDTable) GetFDs() []int32 {
|
||||
fds := make([]int32, 0, int(atomic.LoadInt32(&f.used)))
|
||||
f.forEach(func(fd int32, _ *fs.File, _ *vfs.FileDescription, _ FDFlags) {
|
||||
|
||||
Reference in New Issue
Block a user