github.com/metacubex/gvisor@v0.0.0-20240320004321-933faba989ec/pkg/sentry/fsimpl/proc/filesystem.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 implements a partial in-memory file system for procfs.
    16  package proc
    17  
    18  import (
    19  	"fmt"
    20  	"strconv"
    21  
    22  	"github.com/metacubex/gvisor/pkg/abi/linux"
    23  	"github.com/metacubex/gvisor/pkg/context"
    24  	"github.com/metacubex/gvisor/pkg/errors/linuxerr"
    25  	"github.com/metacubex/gvisor/pkg/sentry/fsimpl/kernfs"
    26  	"github.com/metacubex/gvisor/pkg/sentry/kernel"
    27  	"github.com/metacubex/gvisor/pkg/sentry/kernel/auth"
    28  	"github.com/metacubex/gvisor/pkg/sentry/vfs"
    29  )
    30  
    31  const (
    32  	// Name is the default filesystem name.
    33  	Name                     = "proc"
    34  	defaultMaxCachedDentries = uint64(1000)
    35  )
    36  
    37  // FilesystemType is the factory class for procfs.
    38  //
    39  // +stateify savable
    40  type FilesystemType struct{}
    41  
    42  // Name implements vfs.FilesystemType.Name.
    43  func (FilesystemType) Name() string {
    44  	return Name
    45  }
    46  
    47  // Release implements vfs.FilesystemType.Release.
    48  func (FilesystemType) Release(ctx context.Context) {}
    49  
    50  // +stateify savable
    51  type filesystem struct {
    52  	kernfs.Filesystem
    53  
    54  	devMinor uint32
    55  }
    56  
    57  func (fs *filesystem) StatFSAt(ctx context.Context, rp *vfs.ResolvingPath) (linux.Statfs, error) {
    58  	d, err := fs.GetDentryAt(ctx, rp, vfs.GetDentryOptions{})
    59  	if err != nil {
    60  		return linux.Statfs{}, err
    61  	}
    62  	d.DecRef(ctx)
    63  	return vfs.GenericStatFS(linux.PROC_SUPER_MAGIC), nil
    64  }
    65  
    66  // GetFilesystem implements vfs.FilesystemType.GetFilesystem.
    67  func (ft FilesystemType) GetFilesystem(ctx context.Context, vfsObj *vfs.VirtualFilesystem, creds *auth.Credentials, source string, opts vfs.GetFilesystemOptions) (*vfs.Filesystem, *vfs.Dentry, error) {
    68  	k := kernel.KernelFromContext(ctx)
    69  	if k == nil {
    70  		return nil, nil, fmt.Errorf("procfs requires a kernel")
    71  	}
    72  	pidns := kernel.PIDNamespaceFromContext(ctx)
    73  	if pidns == nil {
    74  		return nil, nil, fmt.Errorf("procfs requires a PID namespace")
    75  	}
    76  	devMinor, err := vfsObj.GetAnonBlockDevMinor()
    77  	if err != nil {
    78  		return nil, nil, err
    79  	}
    80  
    81  	mopts := vfs.GenericParseMountOptions(opts.Data)
    82  	maxCachedDentries := defaultMaxCachedDentries
    83  	if str, ok := mopts["dentry_cache_limit"]; ok {
    84  		delete(mopts, "dentry_cache_limit")
    85  		maxCachedDentries, err = strconv.ParseUint(str, 10, 64)
    86  		if err != nil {
    87  			ctx.Warningf("proc.FilesystemType.GetFilesystem: invalid dentry cache limit: dentry_cache_limit=%s", str)
    88  			return nil, nil, linuxerr.EINVAL
    89  		}
    90  	}
    91  
    92  	procfs := &filesystem{
    93  		devMinor: devMinor,
    94  	}
    95  	procfs.MaxCachedDentries = maxCachedDentries
    96  	procfs.VFSFilesystem().Init(vfsObj, &ft, procfs)
    97  
    98  	var fakeCgroupControllers map[string]string
    99  	if opts.InternalData != nil {
   100  		data := opts.InternalData.(*InternalData)
   101  		fakeCgroupControllers = data.Cgroups
   102  	}
   103  
   104  	inode := procfs.newTasksInode(ctx, k, pidns, fakeCgroupControllers)
   105  	var dentry kernfs.Dentry
   106  	dentry.InitRoot(&procfs.Filesystem, inode)
   107  	return procfs.VFSFilesystem(), dentry.VFSDentry(), nil
   108  }
   109  
   110  // Release implements vfs.FilesystemImpl.Release.
   111  func (fs *filesystem) Release(ctx context.Context) {
   112  	fs.Filesystem.VFSFilesystem().VirtualFilesystem().PutAnonBlockDevMinor(fs.devMinor)
   113  	fs.Filesystem.Release(ctx)
   114  }
   115  
   116  // MountOptions implements vfs.FilesystemImpl.MountOptions.
   117  func (fs *filesystem) MountOptions() string {
   118  	return fmt.Sprintf("dentry_cache_limit=%d", fs.MaxCachedDentries)
   119  }
   120  
   121  // dynamicInode is an overfitted interface for common Inodes with
   122  // dynamicByteSource types used in procfs.
   123  //
   124  // +stateify savable
   125  type dynamicInode interface {
   126  	kernfs.Inode
   127  	vfs.DynamicBytesSource
   128  
   129  	Init(ctx context.Context, creds *auth.Credentials, devMajor, devMinor uint32, ino uint64, data vfs.DynamicBytesSource, perm linux.FileMode)
   130  }
   131  
   132  func (fs *filesystem) newInode(ctx context.Context, creds *auth.Credentials, perm linux.FileMode, inode dynamicInode) dynamicInode {
   133  	inode.Init(ctx, creds, linux.UNNAMED_MAJOR, fs.devMinor, fs.NextIno(), inode, perm)
   134  	return inode
   135  }
   136  
   137  // +stateify savable
   138  type staticFile struct {
   139  	kernfs.DynamicBytesFile
   140  	vfs.StaticData
   141  }
   142  
   143  var _ dynamicInode = (*staticFile)(nil)
   144  
   145  func newStaticFile(data string) *staticFile {
   146  	return &staticFile{StaticData: vfs.StaticData{Data: data}}
   147  }
   148  
   149  func (fs *filesystem) newStaticDir(ctx context.Context, creds *auth.Credentials, children map[string]kernfs.Inode) kernfs.Inode {
   150  	return kernfs.NewStaticDir(ctx, creds, linux.UNNAMED_MAJOR, fs.devMinor, fs.NextIno(), 0555, children, kernfs.GenericDirectoryFDOptions{
   151  		SeekEnd: kernfs.SeekEndZero,
   152  	})
   153  }
   154  
   155  // InternalData contains internal data passed in to the procfs mount via
   156  // vfs.GetFilesystemOptions.InternalData.
   157  //
   158  // +stateify savable
   159  type InternalData struct {
   160  	Cgroups map[string]string
   161  }
   162  
   163  // +stateify savable
   164  type implStatFS struct{}
   165  
   166  // StatFS implements kernfs.Inode.StatFS.
   167  func (*implStatFS) StatFS(context.Context, *vfs.Filesystem) (linux.Statfs, error) {
   168  	return vfs.GenericStatFS(linux.PROC_SUPER_MAGIC), nil
   169  }