github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/sentry/fsimpl/proc/subtasks.go (about)

     1  // Copyright 2019 The gVisor Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package proc
    16  
    17  import (
    18  	"sort"
    19  	"strconv"
    20  
    21  	"github.com/SagerNet/gvisor/pkg/abi/linux"
    22  	"github.com/SagerNet/gvisor/pkg/context"
    23  	"github.com/SagerNet/gvisor/pkg/errors/linuxerr"
    24  	"github.com/SagerNet/gvisor/pkg/sentry/fsimpl/kernfs"
    25  	"github.com/SagerNet/gvisor/pkg/sentry/kernel"
    26  	"github.com/SagerNet/gvisor/pkg/sentry/kernel/auth"
    27  	"github.com/SagerNet/gvisor/pkg/sentry/vfs"
    28  	"github.com/SagerNet/gvisor/pkg/syserror"
    29  )
    30  
    31  // subtasksInode represents the inode for /proc/[pid]/task/ directory.
    32  //
    33  // +stateify savable
    34  type subtasksInode struct {
    35  	implStatFS
    36  	kernfs.InodeAlwaysValid
    37  	kernfs.InodeAttrs
    38  	kernfs.InodeDirectoryNoNewChildren
    39  	kernfs.InodeNotSymlink
    40  	kernfs.InodeTemporary
    41  	kernfs.OrderedChildren
    42  	subtasksInodeRefs
    43  
    44  	locks vfs.FileLocks
    45  
    46  	fs                *filesystem
    47  	task              *kernel.Task
    48  	pidns             *kernel.PIDNamespace
    49  	cgroupControllers map[string]string
    50  }
    51  
    52  var _ kernfs.Inode = (*subtasksInode)(nil)
    53  
    54  func (fs *filesystem) newSubtasks(ctx context.Context, task *kernel.Task, pidns *kernel.PIDNamespace, cgroupControllers map[string]string) kernfs.Inode {
    55  	subInode := &subtasksInode{
    56  		fs:                fs,
    57  		task:              task,
    58  		pidns:             pidns,
    59  		cgroupControllers: cgroupControllers,
    60  	}
    61  	// Note: credentials are overridden by taskOwnedInode.
    62  	subInode.InodeAttrs.Init(ctx, task.Credentials(), linux.UNNAMED_MAJOR, fs.devMinor, fs.NextIno(), linux.ModeDirectory|0555)
    63  	subInode.OrderedChildren.Init(kernfs.OrderedChildrenOptions{})
    64  	subInode.InitRefs()
    65  
    66  	inode := &taskOwnedInode{Inode: subInode, owner: task}
    67  	return inode
    68  }
    69  
    70  // Lookup implements kernfs.inodeDirectory.Lookup.
    71  func (i *subtasksInode) Lookup(ctx context.Context, name string) (kernfs.Inode, error) {
    72  	tid, err := strconv.ParseUint(name, 10, 32)
    73  	if err != nil {
    74  		return nil, syserror.ENOENT
    75  	}
    76  
    77  	subTask := i.pidns.TaskWithID(kernel.ThreadID(tid))
    78  	if subTask == nil {
    79  		return nil, syserror.ENOENT
    80  	}
    81  	if subTask.ThreadGroup() != i.task.ThreadGroup() {
    82  		return nil, syserror.ENOENT
    83  	}
    84  	return i.fs.newTaskInode(ctx, subTask, i.pidns, false, i.cgroupControllers)
    85  }
    86  
    87  // IterDirents implements kernfs.inodeDirectory.IterDirents.
    88  func (i *subtasksInode) IterDirents(ctx context.Context, mnt *vfs.Mount, cb vfs.IterDirentsCallback, offset, relOffset int64) (int64, error) {
    89  	tasks := i.task.ThreadGroup().MemberIDs(i.pidns)
    90  	if len(tasks) == 0 {
    91  		return offset, syserror.ENOENT
    92  	}
    93  	if relOffset >= int64(len(tasks)) {
    94  		return offset, nil
    95  	}
    96  
    97  	tids := make([]int, 0, len(tasks))
    98  	for _, tid := range tasks {
    99  		tids = append(tids, int(tid))
   100  	}
   101  
   102  	sort.Ints(tids)
   103  	for _, tid := range tids[relOffset:] {
   104  		dirent := vfs.Dirent{
   105  			Name:    strconv.FormatUint(uint64(tid), 10),
   106  			Type:    linux.DT_DIR,
   107  			Ino:     i.fs.NextIno(),
   108  			NextOff: offset + 1,
   109  		}
   110  		if err := cb.Handle(dirent); err != nil {
   111  			return offset, err
   112  		}
   113  		offset++
   114  	}
   115  	return offset, nil
   116  }
   117  
   118  // +stateify savable
   119  type subtasksFD struct {
   120  	kernfs.GenericDirectoryFD
   121  
   122  	task *kernel.Task
   123  }
   124  
   125  func (fd *subtasksFD) IterDirents(ctx context.Context, cb vfs.IterDirentsCallback) error {
   126  	if fd.task.ExitState() >= kernel.TaskExitZombie {
   127  		return syserror.ENOENT
   128  	}
   129  	return fd.GenericDirectoryFD.IterDirents(ctx, cb)
   130  }
   131  
   132  // Seek implements vfs.FileDescriptionImpl.Seek.
   133  func (fd *subtasksFD) Seek(ctx context.Context, offset int64, whence int32) (int64, error) {
   134  	if fd.task.ExitState() >= kernel.TaskExitZombie {
   135  		return 0, syserror.ENOENT
   136  	}
   137  	return fd.GenericDirectoryFD.Seek(ctx, offset, whence)
   138  }
   139  
   140  // Stat implements vfs.FileDescriptionImpl.Stat.
   141  func (fd *subtasksFD) Stat(ctx context.Context, opts vfs.StatOptions) (linux.Statx, error) {
   142  	if fd.task.ExitState() >= kernel.TaskExitZombie {
   143  		return linux.Statx{}, syserror.ENOENT
   144  	}
   145  	return fd.GenericDirectoryFD.Stat(ctx, opts)
   146  }
   147  
   148  // SetStat implements vfs.FileDescriptionImpl.SetStat.
   149  func (fd *subtasksFD) SetStat(ctx context.Context, opts vfs.SetStatOptions) error {
   150  	if fd.task.ExitState() >= kernel.TaskExitZombie {
   151  		return syserror.ENOENT
   152  	}
   153  	return fd.GenericDirectoryFD.SetStat(ctx, opts)
   154  }
   155  
   156  // Open implements kernfs.Inode.Open.
   157  func (i *subtasksInode) Open(ctx context.Context, rp *vfs.ResolvingPath, d *kernfs.Dentry, opts vfs.OpenOptions) (*vfs.FileDescription, error) {
   158  	fd := &subtasksFD{task: i.task}
   159  	if err := fd.Init(&i.OrderedChildren, &i.locks, &opts, kernfs.GenericDirectoryFDOptions{
   160  		SeekEnd: kernfs.SeekEndZero,
   161  	}); err != nil {
   162  		return nil, err
   163  	}
   164  	if err := fd.VFSFileDescription().Init(fd, opts.Flags, rp.Mount(), d.VFSDentry(), &vfs.FileDescriptionOptions{}); err != nil {
   165  		return nil, err
   166  	}
   167  	return fd.VFSFileDescription(), nil
   168  }
   169  
   170  // Stat implements kernfs.Inode.Stat.
   171  func (i *subtasksInode) Stat(ctx context.Context, vsfs *vfs.Filesystem, opts vfs.StatOptions) (linux.Statx, error) {
   172  	stat, err := i.InodeAttrs.Stat(ctx, vsfs, opts)
   173  	if err != nil {
   174  		return linux.Statx{}, err
   175  	}
   176  	if opts.Mask&linux.STATX_NLINK != 0 {
   177  		stat.Nlink += uint32(i.task.ThreadGroup().Count())
   178  	}
   179  	return stat, nil
   180  }
   181  
   182  // SetStat implements kernfs.Inode.SetStat not allowing inode attributes to be changed.
   183  func (*subtasksInode) SetStat(context.Context, *vfs.Filesystem, *auth.Credentials, vfs.SetStatOptions) error {
   184  	return linuxerr.EPERM
   185  }
   186  
   187  // DecRef implements kernfs.Inode.DecRef.
   188  func (i *subtasksInode) DecRef(ctx context.Context) {
   189  	i.subtasksInodeRefs.DecRef(func() { i.Destroy(ctx) })
   190  }