github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/sentry/fsimpl/sys/sys.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 sys implements sysfs.
    16  package sys
    17  
    18  import (
    19  	"bytes"
    20  	"fmt"
    21  	"strconv"
    22  
    23  	"github.com/SagerNet/gvisor/pkg/abi/linux"
    24  	"github.com/SagerNet/gvisor/pkg/context"
    25  	"github.com/SagerNet/gvisor/pkg/coverage"
    26  	"github.com/SagerNet/gvisor/pkg/errors/linuxerr"
    27  	"github.com/SagerNet/gvisor/pkg/log"
    28  	"github.com/SagerNet/gvisor/pkg/sentry/fsimpl/kernfs"
    29  	"github.com/SagerNet/gvisor/pkg/sentry/kernel"
    30  	"github.com/SagerNet/gvisor/pkg/sentry/kernel/auth"
    31  	"github.com/SagerNet/gvisor/pkg/sentry/vfs"
    32  )
    33  
    34  const (
    35  	// Name is the default filesystem name.
    36  	Name                     = "sysfs"
    37  	defaultSysDirMode        = linux.FileMode(0755)
    38  	defaultMaxCachedDentries = uint64(1000)
    39  )
    40  
    41  // FilesystemType implements vfs.FilesystemType.
    42  //
    43  // +stateify savable
    44  type FilesystemType struct{}
    45  
    46  // filesystem implements vfs.FilesystemImpl.
    47  //
    48  // +stateify savable
    49  type filesystem struct {
    50  	kernfs.Filesystem
    51  
    52  	devMinor uint32
    53  }
    54  
    55  // Name implements vfs.FilesystemType.Name.
    56  func (FilesystemType) Name() string {
    57  	return Name
    58  }
    59  
    60  // Release implements vfs.FilesystemType.Release.
    61  func (FilesystemType) Release(ctx context.Context) {}
    62  
    63  // GetFilesystem implements vfs.FilesystemType.GetFilesystem.
    64  func (fsType FilesystemType) GetFilesystem(ctx context.Context, vfsObj *vfs.VirtualFilesystem, creds *auth.Credentials, source string, opts vfs.GetFilesystemOptions) (*vfs.Filesystem, *vfs.Dentry, error) {
    65  	devMinor, err := vfsObj.GetAnonBlockDevMinor()
    66  	if err != nil {
    67  		return nil, nil, err
    68  	}
    69  
    70  	mopts := vfs.GenericParseMountOptions(opts.Data)
    71  	maxCachedDentries := defaultMaxCachedDentries
    72  	if str, ok := mopts["dentry_cache_limit"]; ok {
    73  		delete(mopts, "dentry_cache_limit")
    74  		maxCachedDentries, err = strconv.ParseUint(str, 10, 64)
    75  		if err != nil {
    76  			ctx.Warningf("sys.FilesystemType.GetFilesystem: invalid dentry cache limit: dentry_cache_limit=%s", str)
    77  			return nil, nil, linuxerr.EINVAL
    78  		}
    79  	}
    80  
    81  	fs := &filesystem{
    82  		devMinor: devMinor,
    83  	}
    84  	fs.MaxCachedDentries = maxCachedDentries
    85  	fs.VFSFilesystem().Init(vfsObj, &fsType, fs)
    86  
    87  	root := fs.newDir(ctx, creds, defaultSysDirMode, map[string]kernfs.Inode{
    88  		"block": fs.newDir(ctx, creds, defaultSysDirMode, nil),
    89  		"bus":   fs.newDir(ctx, creds, defaultSysDirMode, nil),
    90  		"class": fs.newDir(ctx, creds, defaultSysDirMode, map[string]kernfs.Inode{
    91  			"power_supply": fs.newDir(ctx, creds, defaultSysDirMode, nil),
    92  		}),
    93  		"dev": fs.newDir(ctx, creds, defaultSysDirMode, nil),
    94  		"devices": fs.newDir(ctx, creds, defaultSysDirMode, map[string]kernfs.Inode{
    95  			"system": fs.newDir(ctx, creds, defaultSysDirMode, map[string]kernfs.Inode{
    96  				"cpu": cpuDir(ctx, fs, creds),
    97  			}),
    98  		}),
    99  		"firmware": fs.newDir(ctx, creds, defaultSysDirMode, nil),
   100  		"fs":       fs.newDir(ctx, creds, defaultSysDirMode, nil),
   101  		"kernel":   kernelDir(ctx, fs, creds),
   102  		"module":   fs.newDir(ctx, creds, defaultSysDirMode, nil),
   103  		"power":    fs.newDir(ctx, creds, defaultSysDirMode, nil),
   104  	})
   105  	var rootD kernfs.Dentry
   106  	rootD.InitRoot(&fs.Filesystem, root)
   107  	return fs.VFSFilesystem(), rootD.VFSDentry(), nil
   108  }
   109  
   110  func cpuDir(ctx context.Context, fs *filesystem, creds *auth.Credentials) kernfs.Inode {
   111  	k := kernel.KernelFromContext(ctx)
   112  	maxCPUCores := k.ApplicationCores()
   113  	children := map[string]kernfs.Inode{
   114  		"online":   fs.newCPUFile(ctx, creds, maxCPUCores, linux.FileMode(0444)),
   115  		"possible": fs.newCPUFile(ctx, creds, maxCPUCores, linux.FileMode(0444)),
   116  		"present":  fs.newCPUFile(ctx, creds, maxCPUCores, linux.FileMode(0444)),
   117  	}
   118  	for i := uint(0); i < maxCPUCores; i++ {
   119  		children[fmt.Sprintf("cpu%d", i)] = fs.newDir(ctx, creds, linux.FileMode(0555), nil)
   120  	}
   121  	return fs.newDir(ctx, creds, defaultSysDirMode, children)
   122  }
   123  
   124  func kernelDir(ctx context.Context, fs *filesystem, creds *auth.Credentials) kernfs.Inode {
   125  	// Set up /sys/kernel/debug/kcov. Technically, debugfs should be
   126  	// mounted at debug/, but for our purposes, it is sufficient to keep it
   127  	// in sys.
   128  	var children map[string]kernfs.Inode
   129  	if coverage.KcovSupported() {
   130  		log.Debugf("Set up /sys/kernel/debug/kcov")
   131  		children = map[string]kernfs.Inode{
   132  			"debug": fs.newDir(ctx, creds, linux.FileMode(0700), map[string]kernfs.Inode{
   133  				"kcov": fs.newKcovFile(ctx, creds),
   134  			}),
   135  		}
   136  	}
   137  	return fs.newDir(ctx, creds, defaultSysDirMode, children)
   138  }
   139  
   140  // Release implements vfs.FilesystemImpl.Release.
   141  func (fs *filesystem) Release(ctx context.Context) {
   142  	fs.Filesystem.VFSFilesystem().VirtualFilesystem().PutAnonBlockDevMinor(fs.devMinor)
   143  	fs.Filesystem.Release(ctx)
   144  }
   145  
   146  // MountOptions implements vfs.FilesystemImpl.MountOptions.
   147  func (fs *filesystem) MountOptions() string {
   148  	return fmt.Sprintf("dentry_cache_limit=%d", fs.MaxCachedDentries)
   149  }
   150  
   151  // dir implements kernfs.Inode.
   152  //
   153  // +stateify savable
   154  type dir struct {
   155  	dirRefs
   156  	kernfs.InodeAlwaysValid
   157  	kernfs.InodeAttrs
   158  	kernfs.InodeNotSymlink
   159  	kernfs.InodeDirectoryNoNewChildren
   160  	kernfs.InodeTemporary
   161  	kernfs.OrderedChildren
   162  
   163  	locks vfs.FileLocks
   164  }
   165  
   166  func (fs *filesystem) newDir(ctx context.Context, creds *auth.Credentials, mode linux.FileMode, contents map[string]kernfs.Inode) kernfs.Inode {
   167  	d := &dir{}
   168  	d.InodeAttrs.Init(ctx, creds, linux.UNNAMED_MAJOR, fs.devMinor, fs.NextIno(), linux.ModeDirectory|0755)
   169  	d.OrderedChildren.Init(kernfs.OrderedChildrenOptions{})
   170  	d.InitRefs()
   171  	d.IncLinks(d.OrderedChildren.Populate(contents))
   172  	return d
   173  }
   174  
   175  // SetStat implements kernfs.Inode.SetStat not allowing inode attributes to be changed.
   176  func (*dir) SetStat(context.Context, *vfs.Filesystem, *auth.Credentials, vfs.SetStatOptions) error {
   177  	return linuxerr.EPERM
   178  }
   179  
   180  // Open implements kernfs.Inode.Open.
   181  func (d *dir) Open(ctx context.Context, rp *vfs.ResolvingPath, kd *kernfs.Dentry, opts vfs.OpenOptions) (*vfs.FileDescription, error) {
   182  	fd, err := kernfs.NewGenericDirectoryFD(rp.Mount(), kd, &d.OrderedChildren, &d.locks, &opts, kernfs.GenericDirectoryFDOptions{
   183  		SeekEnd: kernfs.SeekEndStaticEntries,
   184  	})
   185  	if err != nil {
   186  		return nil, err
   187  	}
   188  	return fd.VFSFileDescription(), nil
   189  }
   190  
   191  // DecRef implements kernfs.Inode.DecRef.
   192  func (d *dir) DecRef(ctx context.Context) {
   193  	d.dirRefs.DecRef(func() { d.Destroy(ctx) })
   194  }
   195  
   196  // StatFS implements kernfs.Inode.StatFS.
   197  func (d *dir) StatFS(ctx context.Context, fs *vfs.Filesystem) (linux.Statfs, error) {
   198  	return vfs.GenericStatFS(linux.SYSFS_MAGIC), nil
   199  }
   200  
   201  // cpuFile implements kernfs.Inode.
   202  //
   203  // +stateify savable
   204  type cpuFile struct {
   205  	implStatFS
   206  	kernfs.DynamicBytesFile
   207  
   208  	maxCores uint
   209  }
   210  
   211  // Generate implements vfs.DynamicBytesSource.Generate.
   212  func (c *cpuFile) Generate(ctx context.Context, buf *bytes.Buffer) error {
   213  	fmt.Fprintf(buf, "0-%d\n", c.maxCores-1)
   214  	return nil
   215  }
   216  
   217  func (fs *filesystem) newCPUFile(ctx context.Context, creds *auth.Credentials, maxCores uint, mode linux.FileMode) kernfs.Inode {
   218  	c := &cpuFile{maxCores: maxCores}
   219  	c.DynamicBytesFile.Init(ctx, creds, linux.UNNAMED_MAJOR, fs.devMinor, fs.NextIno(), c, mode)
   220  	return c
   221  }
   222  
   223  // +stateify savable
   224  type implStatFS struct{}
   225  
   226  // StatFS implements kernfs.Inode.StatFS.
   227  func (*implStatFS) StatFS(context.Context, *vfs.Filesystem) (linux.Statfs, error) {
   228  	return vfs.GenericStatFS(linux.SYSFS_MAGIC), nil
   229  }