summaryrefslogtreecommitdiffhomepage
path: root/pkg/sentry/fs/proc/task.go
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/sentry/fs/proc/task.go')
-rw-r--r--pkg/sentry/fs/proc/task.go264
1 files changed, 178 insertions, 86 deletions
diff --git a/pkg/sentry/fs/proc/task.go b/pkg/sentry/fs/proc/task.go
index 91bda8a95..41981a973 100644
--- a/pkg/sentry/fs/proc/task.go
+++ b/pkg/sentry/fs/proc/task.go
@@ -24,6 +24,7 @@ import (
"gvisor.googlesource.com/gvisor/pkg/abi/linux"
"gvisor.googlesource.com/gvisor/pkg/sentry/context"
"gvisor.googlesource.com/gvisor/pkg/sentry/fs"
+ "gvisor.googlesource.com/gvisor/pkg/sentry/fs/fsutil"
"gvisor.googlesource.com/gvisor/pkg/sentry/fs/proc/device"
"gvisor.googlesource.com/gvisor/pkg/sentry/fs/proc/seqfile"
"gvisor.googlesource.com/gvisor/pkg/sentry/fs/ramfs"
@@ -32,6 +33,7 @@ import (
"gvisor.googlesource.com/gvisor/pkg/sentry/usage"
"gvisor.googlesource.com/gvisor/pkg/sentry/usermem"
"gvisor.googlesource.com/gvisor/pkg/syserror"
+ "gvisor.googlesource.com/gvisor/pkg/waiter"
)
// getTaskMM returns t's MemoryManager. If getTaskMM succeeds, the MemoryManager's
@@ -57,19 +59,19 @@ func getTaskMM(t *kernel.Task) (*mm.MemoryManager, error) {
type taskDir struct {
ramfs.Dir
- // t is the associated kernel task that owns this file.
- t *kernel.Task
+ t *kernel.Task
+ pidns *kernel.PIDNamespace
}
+var _ fs.InodeOperations = (*taskDir)(nil)
+
// newTaskDir creates a new proc task entry.
func newTaskDir(t *kernel.Task, msrc *fs.MountSource, pidns *kernel.PIDNamespace, showSubtasks bool) *fs.Inode {
- d := &taskDir{t: t}
- // TODO: Set EUID/EGID based on dumpability.
- d.InitDir(t, map[string]*fs.Inode{
+ contents := map[string]*fs.Inode{
"auxv": newAuxvec(t, msrc),
- "cmdline": newExecArgFile(t, msrc, cmdlineExecArg),
+ "cmdline": newExecArgInode(t, msrc, cmdlineExecArg),
"comm": newComm(t, msrc),
- "environ": newExecArgFile(t, msrc, environExecArg),
+ "environ": newExecArgInode(t, msrc, environExecArg),
"exe": newExe(t, msrc),
"fd": newFdDir(t, msrc),
"fdinfo": newFdInfoDir(t, msrc),
@@ -87,11 +89,18 @@ func newTaskDir(t *kernel.Task, msrc *fs.MountSource, pidns *kernel.PIDNamespace
"statm": newStatm(t, msrc),
"status": newStatus(t, msrc, pidns),
"uid_map": newUIDMap(t, msrc),
- }, fs.RootOwner, fs.FilePermsFromMode(0555))
+ }
if showSubtasks {
- d.AddChild(t, "task", newSubtasks(t, msrc, pidns))
+ contents["task"] = newSubtasks(t, msrc, pidns)
}
- return newFile(d, msrc, fs.SpecialDirectory, t)
+
+ // TODO: Set EUID/EGID based on dumpability.
+ d := &taskDir{
+ Dir: *ramfs.NewDir(t, contents, fs.RootOwner, fs.FilePermsFromMode(0555)),
+ t: t,
+ pidns: pidns,
+ }
+ return newProcInode(d, msrc, fs.SpecialDirectory, t)
}
// subtasks represents a /proc/TID/task directory.
@@ -100,15 +109,19 @@ func newTaskDir(t *kernel.Task, msrc *fs.MountSource, pidns *kernel.PIDNamespace
type subtasks struct {
ramfs.Dir
- t *kernel.Task
-
+ t *kernel.Task
pidns *kernel.PIDNamespace
}
+var _ fs.InodeOperations = (*subtasks)(nil)
+
func newSubtasks(t *kernel.Task, msrc *fs.MountSource, pidns *kernel.PIDNamespace) *fs.Inode {
- s := &subtasks{t: t, pidns: pidns}
- s.InitDir(t, nil, fs.RootOwner, fs.FilePermsFromMode(0555))
- return newFile(s, msrc, fs.SpecialDirectory, t)
+ s := &subtasks{
+ Dir: *ramfs.NewDir(t, nil, fs.RootOwner, fs.FilePermsFromMode(0555)),
+ t: t,
+ pidns: pidns,
+ }
+ return newProcInode(s, msrc, fs.SpecialDirectory, t)
}
// UnstableAttr returns unstable attributes of the subtasks.
@@ -123,35 +136,52 @@ func (s *subtasks) UnstableAttr(ctx context.Context, inode *fs.Inode) (fs.Unstab
return uattr, nil
}
-// Lookup loads an Inode in a task's subtask directory into a Dirent.
-func (s *subtasks) Lookup(ctx context.Context, dir *fs.Inode, p string) (*fs.Dirent, error) {
- tid, err := strconv.ParseUint(p, 10, 32)
- if err != nil {
- return nil, syserror.ENOENT
- }
+// GetFile implements fs.InodeOperations.GetFile.
+func (s *subtasks) GetFile(ctx context.Context, dirent *fs.Dirent, flags fs.FileFlags) (*fs.File, error) {
+ return fs.NewFile(ctx, dirent, flags, &subtasksFile{t: s.t, pidns: s.pidns}), nil
+}
- task := s.pidns.TaskWithID(kernel.ThreadID(tid))
- if task == nil {
- return nil, syserror.ENOENT
- }
- if task.ThreadGroup() != s.t.ThreadGroup() {
- return nil, syserror.ENOENT
- }
+// +stateify savable
+type subtasksFile struct {
+ fsutil.DirFileOperations `state:"nosave"`
- td := newTaskDir(task, dir.MountSource, s.pidns, false)
- return fs.NewDirent(td, p), nil
+ t *kernel.Task
+ pidns *kernel.PIDNamespace
}
-// DeprecatedReaddir lists a task's subtask directory.
-func (s *subtasks) DeprecatedReaddir(ctx context.Context, dirCtx *fs.DirCtx, offset int) (int, error) {
- tasks := s.t.ThreadGroup().MemberIDs(s.pidns)
+// Readdir implements fs.FileOperations.Readdir.
+func (f *subtasksFile) Readdir(ctx context.Context, file *fs.File, ser fs.DentrySerializer) (int64, error) {
+ dirCtx := fs.DirCtx{
+ Serializer: ser,
+ }
+
+ // Note that unlike most Readdir implementations, the offset here is
+ // not an index into the subtasks, but rather the TID of the next
+ // subtask to emit.
+ offset := file.Offset()
+
+ if offset == 0 {
+ // Serialize "." and "..".
+ root := fs.RootFromContext(ctx)
+ defer root.DecRef()
+ dot, dotdot := file.Dirent.GetDotAttrs(root)
+ if err := dirCtx.DirEmit(".", dot); err != nil {
+ return offset, err
+ }
+ if err := dirCtx.DirEmit("..", dotdot); err != nil {
+ return offset, err
+ }
+ }
+
+ // Serialize tasks.
+ tasks := f.t.ThreadGroup().MemberIDs(f.pidns)
taskInts := make([]int, 0, len(tasks))
for _, tid := range tasks {
taskInts = append(taskInts, int(tid))
}
// Find the task to start at.
- idx := sort.SearchInts(taskInts, offset)
+ idx := sort.SearchInts(taskInts, int(offset))
if idx == len(taskInts) {
return offset, nil
}
@@ -163,12 +193,33 @@ func (s *subtasks) DeprecatedReaddir(ctx context.Context, dirCtx *fs.DirCtx, off
attr := fs.GenericDentAttr(fs.SpecialDirectory, device.ProcDevice)
if err := dirCtx.DirEmit(name, attr); err != nil {
// Returned offset is next tid to serialize.
- return tid, err
+ return int64(tid), err
}
}
// We serialized them all. Next offset should be higher than last
// serialized tid.
- return tid + 1, nil
+ return int64(tid) + 1, nil
+}
+
+var _ fs.FileOperations = (*subtasksFile)(nil)
+
+// Lookup loads an Inode in a task's subtask directory into a Dirent.
+func (s *subtasks) Lookup(ctx context.Context, dir *fs.Inode, p string) (*fs.Dirent, error) {
+ tid, err := strconv.ParseUint(p, 10, 32)
+ if err != nil {
+ return nil, syserror.ENOENT
+ }
+
+ task := s.pidns.TaskWithID(kernel.ThreadID(tid))
+ if task == nil {
+ return nil, syserror.ENOENT
+ }
+ if task.ThreadGroup() != s.t.ThreadGroup() {
+ return nil, syserror.ENOENT
+ }
+
+ td := newTaskDir(task, dir.MountSource, s.pidns, false)
+ return fs.NewDirent(td, p), nil
}
// exe is an fs.InodeOperations symlink for the /proc/PID/exe file.
@@ -181,9 +232,11 @@ type exe struct {
}
func newExe(t *kernel.Task, msrc *fs.MountSource) *fs.Inode {
- exeSymlink := &exe{t: t}
- exeSymlink.InitSymlink(t, fs.RootOwner, "")
- return newFile(exeSymlink, msrc, fs.Symlink, t)
+ exeSymlink := &exe{
+ Symlink: *ramfs.NewSymlink(t, fs.RootOwner, ""),
+ t: t,
+ }
+ return newProcInode(exeSymlink, msrc, fs.Symlink, t)
}
func (e *exe) executable() (d *fs.Dirent, err error) {
@@ -231,55 +284,48 @@ func (e *exe) Readlink(ctx context.Context, inode *fs.Inode) (string, error) {
return n, nil
}
-// namespaceFile represents a file in the namespacefs, such as the files in
-// /proc/<pid>/ns.
+// namespaceSymlink represents a symlink in the namespacefs, such as the files
+// in /proc/<pid>/ns.
//
// +stateify savable
-type namespaceFile struct {
+type namespaceSymlink struct {
ramfs.Symlink
t *kernel.Task
}
-func newNamespaceFile(t *kernel.Task, msrc *fs.MountSource, name string) *fs.Inode {
- n := &namespaceFile{t: t}
- n.InitSymlink(t, fs.RootOwner, "")
-
+func newNamespaceSymlink(t *kernel.Task, msrc *fs.MountSource, name string) *fs.Inode {
// TODO: Namespace symlinks should contain the namespace name and the
// inode number for the namespace instance, so for example user:[123456]. We
// currently fake the inode number by sticking the symlink inode in its
// place.
- n.Target = fmt.Sprintf("%s:[%d]", name, device.ProcDevice.NextIno())
-
- return newFile(n, msrc, fs.Symlink, t)
+ target := fmt.Sprintf("%s:[%d]", name, device.ProcDevice.NextIno())
+ n := &namespaceSymlink{
+ Symlink: *ramfs.NewSymlink(t, fs.RootOwner, target),
+ t: t,
+ }
+ return newProcInode(n, msrc, fs.Symlink, t)
}
// Getlink implements fs.InodeOperations.Getlink.
-func (n *namespaceFile) Getlink(ctx context.Context, inode *fs.Inode) (*fs.Dirent, error) {
+func (n *namespaceSymlink) Getlink(ctx context.Context, inode *fs.Inode) (*fs.Dirent, error) {
if !kernel.ContextCanTrace(ctx, n.t, false) {
return nil, syserror.EACCES
}
// Create a new regular file to fake the namespace file.
- node := &ramfs.Entry{}
- node.InitEntry(ctx, fs.RootOwner, fs.FilePermsFromMode(0777))
- sattr := fs.StableAttr{
- DeviceID: device.ProcDevice.DeviceID(),
- InodeID: device.ProcDevice.NextIno(),
- BlockSize: usermem.PageSize,
- Type: fs.RegularFile,
- }
- return fs.NewDirent(fs.NewInode(node, inode.MountSource, sattr), n.Symlink.Target), nil
+ iops := fsutil.NewNoReadWriteFileInode(ctx, fs.RootOwner, fs.FilePermsFromMode(0777), linux.PROC_SUPER_MAGIC)
+ return fs.NewDirent(newProcInode(iops, inode.MountSource, fs.RegularFile, nil), n.Symlink.Target), nil
}
func newNamespaceDir(t *kernel.Task, msrc *fs.MountSource) *fs.Inode {
- d := &ramfs.Dir{}
- d.InitDir(t, map[string]*fs.Inode{
- "net": newNamespaceFile(t, msrc, "net"),
- "pid": newNamespaceFile(t, msrc, "pid"),
- "user": newNamespaceFile(t, msrc, "user"),
- }, fs.RootOwner, fs.FilePermsFromMode(0511))
- return newFile(d, msrc, fs.SpecialDirectory, t)
+ contents := map[string]*fs.Inode{
+ "net": newNamespaceSymlink(t, msrc, "net"),
+ "pid": newNamespaceSymlink(t, msrc, "pid"),
+ "user": newNamespaceSymlink(t, msrc, "user"),
+ }
+ d := ramfs.NewDir(t, contents, fs.RootOwner, fs.FilePermsFromMode(0511))
+ return newProcInode(d, msrc, fs.SpecialDirectory, t)
}
// mapsData implements seqfile.SeqSource for /proc/[pid]/maps.
@@ -290,7 +336,7 @@ type mapsData struct {
}
func newMaps(t *kernel.Task, msrc *fs.MountSource) *fs.Inode {
- return newFile(seqfile.NewSeqFile(t, &mapsData{t}), msrc, fs.SpecialFile, t)
+ return newProcInode(seqfile.NewSeqFile(t, &mapsData{t}), msrc, fs.SpecialFile, t)
}
func (md *mapsData) mm() *mm.MemoryManager {
@@ -330,7 +376,7 @@ type smapsData struct {
}
func newSmaps(t *kernel.Task, msrc *fs.MountSource) *fs.Inode {
- return newFile(seqfile.NewSeqFile(t, &smapsData{t}), msrc, fs.SpecialFile, t)
+ return newProcInode(seqfile.NewSeqFile(t, &smapsData{t}), msrc, fs.SpecialFile, t)
}
func (sd *smapsData) mm() *mm.MemoryManager {
@@ -376,7 +422,7 @@ type taskStatData struct {
}
func newTaskStat(t *kernel.Task, msrc *fs.MountSource, showSubtasks bool, pidns *kernel.PIDNamespace) *fs.Inode {
- return newFile(seqfile.NewSeqFile(t, &taskStatData{t, showSubtasks /* tgstats */, pidns}), msrc, fs.SpecialFile, t)
+ return newProcInode(seqfile.NewSeqFile(t, &taskStatData{t, showSubtasks /* tgstats */, pidns}), msrc, fs.SpecialFile, t)
}
// NeedsUpdate returns whether the generation is old or not.
@@ -450,7 +496,7 @@ type statmData struct {
}
func newStatm(t *kernel.Task, msrc *fs.MountSource) *fs.Inode {
- return newFile(seqfile.NewSeqFile(t, &statmData{t}), msrc, fs.SpecialFile, t)
+ return newProcInode(seqfile.NewSeqFile(t, &statmData{t}), msrc, fs.SpecialFile, t)
}
// NeedsUpdate implements seqfile.SeqSource.NeedsUpdate.
@@ -487,7 +533,7 @@ type statusData struct {
}
func newStatus(t *kernel.Task, msrc *fs.MountSource, pidns *kernel.PIDNamespace) *fs.Inode {
- return newFile(seqfile.NewSeqFile(t, &statusData{t, pidns}), msrc, fs.SpecialFile, t)
+ return newProcInode(seqfile.NewSeqFile(t, &statusData{t, pidns}), msrc, fs.SpecialFile, t)
}
// NeedsUpdate implements seqfile.SeqSource.NeedsUpdate.
@@ -552,7 +598,7 @@ type ioData struct {
}
func newIO(t *kernel.Task, msrc *fs.MountSource) *fs.Inode {
- return newFile(seqfile.NewSeqFile(t, &ioData{t.ThreadGroup()}), msrc, fs.SpecialFile, t)
+ return newProcInode(seqfile.NewSeqFile(t, &ioData{t.ThreadGroup()}), msrc, fs.SpecialFile, t)
}
// NeedsUpdate returns whether the generation is old or not.
@@ -590,25 +636,49 @@ func (i *ioData) ReadSeqFileData(ctx context.Context, h seqfile.SeqHandle) ([]se
//
// +stateify savable
type comm struct {
- ramfs.Entry
+ fsutil.SimpleFileInode
t *kernel.Task
}
// newComm returns a new comm file.
func newComm(t *kernel.Task, msrc *fs.MountSource) *fs.Inode {
- c := &comm{t: t}
- c.InitEntry(t, fs.RootOwner, fs.FilePermsFromMode(0444))
- return newFile(c, msrc, fs.SpecialFile, t)
+ c := &comm{
+ SimpleFileInode: *fsutil.NewSimpleFileInode(t, fs.RootOwner, fs.FilePermsFromMode(0444), linux.PROC_SUPER_MAGIC),
+ t: t,
+ }
+ return newProcInode(c, msrc, fs.SpecialFile, t)
+}
+
+// GetFile implements fs.InodeOperations.GetFile.
+func (c *comm) GetFile(ctx context.Context, dirent *fs.Dirent, flags fs.FileFlags) (*fs.File, error) {
+ return fs.NewFile(ctx, dirent, flags, &commFile{t: c.t}), nil
+}
+
+// +stateify savable
+type commFile struct {
+ waiter.AlwaysReady `state:"nosave"`
+ fsutil.FileGenericSeek `state:"nosave"`
+ fsutil.FileNoIoctl `state:"nosave"`
+ fsutil.FileNoMMap `state:"nosave"`
+ fsutil.FileNoopFlush `state:"nosave"`
+ fsutil.FileNoopFsync `state:"nosave"`
+ fsutil.FileNoopRelease `state:"nosave"`
+ fsutil.FileNotDirReaddir `state:"nosave"`
+ fsutil.FileNoWrite `state:"nosave"`
+
+ t *kernel.Task
}
-// DeprecatedPreadv reads the current command name.
-func (c *comm) DeprecatedPreadv(ctx context.Context, dst usermem.IOSequence, offset int64) (int64, error) {
+var _ fs.FileOperations = (*commFile)(nil)
+
+// Read implements fs.FileOperations.Read.
+func (f *commFile) Read(ctx context.Context, _ *fs.File, dst usermem.IOSequence, offset int64) (int64, error) {
if offset < 0 {
return 0, syserror.EINVAL
}
- buf := []byte(c.t.Name() + "\n")
+ buf := []byte(f.t.Name() + "\n")
if offset >= int64(len(buf)) {
return 0, io.EOF
}
@@ -621,25 +691,47 @@ func (c *comm) DeprecatedPreadv(ctx context.Context, dst usermem.IOSequence, off
//
// +stateify savable
type auxvec struct {
- ramfs.Entry
+ fsutil.SimpleFileInode
t *kernel.Task
}
// newAuxvec returns a new auxvec file.
func newAuxvec(t *kernel.Task, msrc *fs.MountSource) *fs.Inode {
- a := &auxvec{t: t}
- a.InitEntry(t, fs.RootOwner, fs.FilePermsFromMode(0400))
- return newFile(a, msrc, fs.SpecialFile, t)
+ a := &auxvec{
+ SimpleFileInode: *fsutil.NewSimpleFileInode(t, fs.RootOwner, fs.FilePermsFromMode(0444), linux.PROC_SUPER_MAGIC),
+ t: t,
+ }
+ return newProcInode(a, msrc, fs.SpecialFile, t)
+}
+
+// GetFile implements fs.InodeOperations.GetFile.
+func (a *auxvec) GetFile(ctx context.Context, dirent *fs.Dirent, flags fs.FileFlags) (*fs.File, error) {
+ return fs.NewFile(ctx, dirent, flags, &auxvecFile{t: a.t}), nil
+}
+
+// +stateify savable
+type auxvecFile struct {
+ waiter.AlwaysReady `state:"nosave"`
+ fsutil.FileGenericSeek `state:"nosave"`
+ fsutil.FileNoIoctl `state:"nosave"`
+ fsutil.FileNoMMap `state:"nosave"`
+ fsutil.FileNoopFlush `state:"nosave"`
+ fsutil.FileNoopFsync `state:"nosave"`
+ fsutil.FileNoopRelease `state:"nosave"`
+ fsutil.FileNotDirReaddir `state:"nosave"`
+ fsutil.FileNoWrite `state:"nosave"`
+
+ t *kernel.Task
}
-// DeprecatedPreadv reads the current auxiliary vector.
-func (a *auxvec) DeprecatedPreadv(ctx context.Context, dst usermem.IOSequence, offset int64) (int64, error) {
+// Read implements fs.FileOperations.Read.
+func (f *auxvecFile) Read(ctx context.Context, _ *fs.File, dst usermem.IOSequence, offset int64) (int64, error) {
if offset < 0 {
return 0, syserror.EINVAL
}
- m, err := getTaskMM(a.t)
+ m, err := getTaskMM(f.t)
if err != nil {
return 0, err
}