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

     1  // Copyright 2018 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 implements a partial in-memory file system for profs.
    16  package proc
    17  
    18  import (
    19  	"fmt"
    20  	"sort"
    21  	"strconv"
    22  
    23  	"github.com/SagerNet/gvisor/pkg/context"
    24  	"github.com/SagerNet/gvisor/pkg/errors/linuxerr"
    25  	"github.com/SagerNet/gvisor/pkg/sentry/fs"
    26  	"github.com/SagerNet/gvisor/pkg/sentry/fs/fsutil"
    27  	"github.com/SagerNet/gvisor/pkg/sentry/fs/proc/device"
    28  	"github.com/SagerNet/gvisor/pkg/sentry/fs/proc/seqfile"
    29  	"github.com/SagerNet/gvisor/pkg/sentry/fs/ramfs"
    30  	"github.com/SagerNet/gvisor/pkg/sentry/kernel"
    31  	"github.com/SagerNet/gvisor/pkg/syserror"
    32  )
    33  
    34  // LINT.IfChange
    35  
    36  // proc is a root proc node.
    37  //
    38  // +stateify savable
    39  type proc struct {
    40  	ramfs.Dir
    41  
    42  	// k is the Kernel containing this proc node.
    43  	k *kernel.Kernel
    44  
    45  	// pidns is the PID namespace of the task that mounted the proc filesystem
    46  	// that this node represents.
    47  	pidns *kernel.PIDNamespace
    48  
    49  	// cgroupControllers is a map of controller name to directory in the
    50  	// cgroup hierarchy. These controllers are immutable and will be listed
    51  	// in /proc/pid/cgroup if not nil.
    52  	cgroupControllers map[string]string
    53  }
    54  
    55  // New returns the root node of a partial simple procfs.
    56  func New(ctx context.Context, msrc *fs.MountSource, cgroupControllers map[string]string) (*fs.Inode, error) {
    57  	k := kernel.KernelFromContext(ctx)
    58  	if k == nil {
    59  		return nil, fmt.Errorf("procfs requires a kernel")
    60  	}
    61  	pidns := kernel.PIDNamespaceFromContext(ctx)
    62  	if pidns == nil {
    63  		return nil, fmt.Errorf("procfs requires a PID namespace")
    64  	}
    65  
    66  	// Note that these are just the static members. There are dynamic
    67  	// members populated in Readdir and Lookup below.
    68  	contents := map[string]*fs.Inode{
    69  		"cpuinfo":     newCPUInfo(ctx, msrc),
    70  		"filesystems": seqfile.NewSeqFileInode(ctx, &filesystemsData{}, msrc),
    71  		"loadavg":     seqfile.NewSeqFileInode(ctx, &loadavgData{}, msrc),
    72  		"meminfo":     seqfile.NewSeqFileInode(ctx, &meminfoData{k}, msrc),
    73  		"mounts":      newProcInode(ctx, ramfs.NewSymlink(ctx, fs.RootOwner, "self/mounts"), msrc, fs.Symlink, nil),
    74  		"net":         newProcInode(ctx, ramfs.NewSymlink(ctx, fs.RootOwner, "self/net"), msrc, fs.Symlink, nil),
    75  		"self":        newSelf(ctx, pidns, msrc),
    76  		"stat":        seqfile.NewSeqFileInode(ctx, &statData{k}, msrc),
    77  		"thread-self": newThreadSelf(ctx, pidns, msrc),
    78  		"uptime":      newUptime(ctx, msrc),
    79  		"version":     seqfile.NewSeqFileInode(ctx, &versionData{k}, msrc),
    80  	}
    81  
    82  	// Construct the proc InodeOperations.
    83  	p := &proc{
    84  		Dir:               *ramfs.NewDir(ctx, contents, fs.RootOwner, fs.FilePermsFromMode(0555)),
    85  		k:                 k,
    86  		pidns:             pidns,
    87  		cgroupControllers: cgroupControllers,
    88  	}
    89  
    90  	// Add more contents that need proc to be initialized.
    91  	p.AddChild(ctx, "sys", p.newSysDir(ctx, msrc))
    92  
    93  	return newProcInode(ctx, p, msrc, fs.SpecialDirectory, nil), nil
    94  }
    95  
    96  // self is a magical link.
    97  //
    98  // +stateify savable
    99  type self struct {
   100  	ramfs.Symlink
   101  
   102  	pidns *kernel.PIDNamespace
   103  }
   104  
   105  // newSelf returns a new "self" node.
   106  func newSelf(ctx context.Context, pidns *kernel.PIDNamespace, msrc *fs.MountSource) *fs.Inode {
   107  	s := &self{
   108  		Symlink: *ramfs.NewSymlink(ctx, fs.RootOwner, ""),
   109  		pidns:   pidns,
   110  	}
   111  	return newProcInode(ctx, s, msrc, fs.Symlink, nil)
   112  }
   113  
   114  // newThreadSelf returns a new "threadSelf" node.
   115  func newThreadSelf(ctx context.Context, pidns *kernel.PIDNamespace, msrc *fs.MountSource) *fs.Inode {
   116  	s := &threadSelf{
   117  		Symlink: *ramfs.NewSymlink(ctx, fs.RootOwner, ""),
   118  		pidns:   pidns,
   119  	}
   120  	return newProcInode(ctx, s, msrc, fs.Symlink, nil)
   121  }
   122  
   123  // Readlink implements fs.InodeOperations.Readlink.
   124  func (s *self) Readlink(ctx context.Context, inode *fs.Inode) (string, error) {
   125  	if t := kernel.TaskFromContext(ctx); t != nil {
   126  		tgid := s.pidns.IDOfThreadGroup(t.ThreadGroup())
   127  		if tgid == 0 {
   128  			return "", syserror.ENOENT
   129  		}
   130  		return strconv.FormatUint(uint64(tgid), 10), nil
   131  	}
   132  
   133  	// Who is reading this link?
   134  	return "", linuxerr.EINVAL
   135  }
   136  
   137  // threadSelf is more magical than "self" link.
   138  //
   139  // +stateify savable
   140  type threadSelf struct {
   141  	ramfs.Symlink
   142  
   143  	pidns *kernel.PIDNamespace
   144  }
   145  
   146  // Readlink implements fs.InodeOperations.Readlink.
   147  func (s *threadSelf) Readlink(ctx context.Context, inode *fs.Inode) (string, error) {
   148  	if t := kernel.TaskFromContext(ctx); t != nil {
   149  		tgid := s.pidns.IDOfThreadGroup(t.ThreadGroup())
   150  		tid := s.pidns.IDOfTask(t)
   151  		if tid == 0 || tgid == 0 {
   152  			return "", syserror.ENOENT
   153  		}
   154  		return fmt.Sprintf("%d/task/%d", tgid, tid), nil
   155  	}
   156  
   157  	// Who is reading this link?
   158  	return "", linuxerr.EINVAL
   159  }
   160  
   161  // Lookup loads an Inode at name into a Dirent.
   162  func (p *proc) Lookup(ctx context.Context, dir *fs.Inode, name string) (*fs.Dirent, error) {
   163  	dirent, walkErr := p.Dir.Lookup(ctx, dir, name)
   164  	if walkErr == nil {
   165  		return dirent, nil
   166  	}
   167  
   168  	// Try to lookup a corresponding task.
   169  	tid, err := strconv.ParseUint(name, 10, 64)
   170  	if err != nil {
   171  		// Ignore the parse error and return the original.
   172  		return nil, walkErr
   173  	}
   174  
   175  	// Grab the other task.
   176  	otherTask := p.pidns.TaskWithID(kernel.ThreadID(tid))
   177  	if otherTask == nil {
   178  		// Per above.
   179  		return nil, walkErr
   180  	}
   181  
   182  	// Wrap it in a taskDir.
   183  	td := p.newTaskDir(ctx, otherTask, dir.MountSource, true)
   184  	return fs.NewDirent(ctx, td, name), nil
   185  }
   186  
   187  // GetFile implements fs.InodeOperations.
   188  func (p *proc) GetFile(ctx context.Context, dirent *fs.Dirent, flags fs.FileFlags) (*fs.File, error) {
   189  	return fs.NewFile(ctx, dirent, flags, &rootProcFile{iops: p}), nil
   190  }
   191  
   192  // rootProcFile implements fs.FileOperations for the proc directory.
   193  //
   194  // +stateify savable
   195  type rootProcFile struct {
   196  	fsutil.DirFileOperations        `state:"nosave"`
   197  	fsutil.FileUseInodeUnstableAttr `state:"nosave"`
   198  
   199  	iops *proc
   200  }
   201  
   202  var _ fs.FileOperations = (*rootProcFile)(nil)
   203  
   204  // Readdir implements fs.FileOperations.Readdir.
   205  func (rpf *rootProcFile) Readdir(ctx context.Context, file *fs.File, ser fs.DentrySerializer) (int64, error) {
   206  	offset := file.Offset()
   207  	dirCtx := &fs.DirCtx{
   208  		Serializer: ser,
   209  	}
   210  
   211  	// Get normal directory contents from ramfs dir.
   212  	names, m := rpf.iops.Dir.Children()
   213  
   214  	// Add dot and dotdot.
   215  	root := fs.RootFromContext(ctx)
   216  	if root != nil {
   217  		defer root.DecRef(ctx)
   218  	}
   219  	dot, dotdot := file.Dirent.GetDotAttrs(root)
   220  	names = append(names, ".", "..")
   221  	m["."] = dot
   222  	m[".."] = dotdot
   223  
   224  	// Collect tasks.
   225  	// Per linux we only include it in directory listings if it's the leader.
   226  	// But for whatever crazy reason, you can still walk to the given node.
   227  	for _, tg := range rpf.iops.pidns.ThreadGroups() {
   228  		if leader := tg.Leader(); leader != nil {
   229  			name := strconv.FormatUint(uint64(rpf.iops.pidns.IDOfThreadGroup(tg)), 10)
   230  			m[name] = fs.GenericDentAttr(fs.SpecialDirectory, device.ProcDevice)
   231  			names = append(names, name)
   232  		}
   233  	}
   234  
   235  	if offset >= int64(len(m)) {
   236  		return offset, nil
   237  	}
   238  	sort.Strings(names)
   239  	names = names[offset:]
   240  	for _, name := range names {
   241  		if err := dirCtx.DirEmit(name, m[name]); err != nil {
   242  			return offset, err
   243  		}
   244  		offset++
   245  	}
   246  	return offset, nil
   247  }
   248  
   249  // LINT.ThenChange(../../fsimpl/proc/tasks.go)