From adff8e586357985934174ef18ad4b80696a93fca Mon Sep 17 00:00:00 2001 From: Fabricio Voznika Date: Tue, 24 May 2022 10:02:24 -0700 Subject: [PATCH] Start deletion of VFS1 from runsc config Updates #1624 PiperOrigin-RevId: 450708193 --- runsc/boot/BUILD | 5 - runsc/boot/controller.go | 17 +- runsc/boot/fs.go | 601 +---------------------------------- runsc/boot/fs_test.go | 2 +- runsc/boot/loader.go | 95 ++---- runsc/boot/loader_test.go | 293 +---------------- runsc/boot/vfs.go | 10 +- runsc/cmd/gofer.go | 8 +- runsc/config/config.go | 3 - runsc/config/flags.go | 2 +- runsc/container/container.go | 2 +- runsc/specutils/specutils.go | 27 +- 12 files changed, 53 insertions(+), 1012 deletions(-) diff --git a/runsc/boot/BUILD b/runsc/boot/BUILD index 910bda6da..621d9cc63 100644 --- a/runsc/boot/BUILD +++ b/runsc/boot/BUILD @@ -54,10 +54,8 @@ go_library( "//pkg/sentry/fdimport", "//pkg/sentry/fs", "//pkg/sentry/fs/dev", - "//pkg/sentry/fs/gofer", "//pkg/sentry/fs/host", "//pkg/sentry/fs/proc", - "//pkg/sentry/fs/ramfs", "//pkg/sentry/fs/sys", "//pkg/sentry/fs/tmpfs", "//pkg/sentry/fs/tty", @@ -143,12 +141,9 @@ go_test( library = ":boot", deps = [ "//pkg/control/server", - "//pkg/fd", "//pkg/fspath", "//pkg/log", "//pkg/p9", - "//pkg/sentry/contexttest", - "//pkg/sentry/fs", "//pkg/sentry/vfs", "//pkg/sync", "//pkg/unet", diff --git a/runsc/boot/controller.go b/runsc/boot/controller.go index c87876187..dffe3bc15 100644 --- a/runsc/boot/controller.go +++ b/runsc/boot/controller.go @@ -27,7 +27,6 @@ import ( "gvisor.dev/gvisor/pkg/log" "gvisor.dev/gvisor/pkg/sentry/control" controlpb "gvisor.dev/gvisor/pkg/sentry/control/control_go_proto" - "gvisor.dev/gvisor/pkg/sentry/fs" "gvisor.dev/gvisor/pkg/sentry/kernel" "gvisor.dev/gvisor/pkg/sentry/seccheck" "gvisor.dev/gvisor/pkg/sentry/socket/netstack" @@ -446,18 +445,10 @@ func (cm *containerManager) Restore(o *RestoreOpts, _ *struct{}) error { // Set up the restore environment. ctx := k.SupervisorContext() - mntr := newContainerMounter(&cm.l.root, cm.l.k, cm.l.mountHints, kernel.VFS2Enabled, cm.l.productName) - if kernel.VFS2Enabled { - ctx, err = mntr.configureRestore(ctx) - if err != nil { - return fmt.Errorf("configuring filesystem restore: %v", err) - } - } else { - renv, err := mntr.createRestoreEnvironment(cm.l.root.conf) - if err != nil { - return fmt.Errorf("creating RestoreEnvironment: %v", err) - } - fs.SetRestoreEnvironment(*renv) + mntr := newContainerMounter(&cm.l.root, cm.l.k, cm.l.mountHints, cm.l.productName) + ctx, err = mntr.configureRestore(ctx) + if err != nil { + return fmt.Errorf("configuring filesystem restore: %v", err) } // Prepare to load from the state file. diff --git a/runsc/boot/fs.go b/runsc/boot/fs.go index ae6f17246..251a31318 100644 --- a/runsc/boot/fs.go +++ b/runsc/boot/fs.go @@ -22,25 +22,16 @@ import ( "strings" specs "github.com/opencontainers/runtime-spec/specs-go" - "golang.org/x/sys/unix" - "gvisor.dev/gvisor/pkg/abi/linux" - "gvisor.dev/gvisor/pkg/context" - "gvisor.dev/gvisor/pkg/errors/linuxerr" "gvisor.dev/gvisor/pkg/fd" "gvisor.dev/gvisor/pkg/log" "gvisor.dev/gvisor/pkg/sentry/fs" - "gvisor.dev/gvisor/pkg/sentry/fs/gofer" - "gvisor.dev/gvisor/pkg/sentry/fs/ramfs" - "gvisor.dev/gvisor/pkg/sentry/fs/user" "gvisor.dev/gvisor/pkg/sentry/fsimpl/cgroupfs" "gvisor.dev/gvisor/pkg/sentry/fsimpl/devpts" "gvisor.dev/gvisor/pkg/sentry/fsimpl/devtmpfs" - gofervfs2 "gvisor.dev/gvisor/pkg/sentry/fsimpl/gofer" procvfs2 "gvisor.dev/gvisor/pkg/sentry/fsimpl/proc" sysvfs2 "gvisor.dev/gvisor/pkg/sentry/fsimpl/sys" tmpfsvfs2 "gvisor.dev/gvisor/pkg/sentry/fsimpl/tmpfs" "gvisor.dev/gvisor/pkg/sentry/kernel" - "gvisor.dev/gvisor/pkg/sentry/kernel/auth" "gvisor.dev/gvisor/pkg/sentry/vfs" "gvisor.dev/gvisor/runsc/config" "gvisor.dev/gvisor/runsc/specutils" @@ -69,52 +60,15 @@ const ( // tmpfs has some extra supported options that we must pass through. var tmpfsAllowedData = []string{"mode", "size", "uid", "gid"} -func addOverlay(ctx context.Context, lower *fs.Inode, name string, lowerFlags fs.MountSourceFlags) (*fs.Inode, error) { - // Upper layer uses the same flags as lower, but it must be read-write. - upperFlags := lowerFlags - upperFlags.ReadOnly = false - - tmpFS := mustFindFilesystem("tmpfs") - if !fs.IsDir(lower.StableAttr) { - // Create overlay on top of mount file, e.g. /etc/hostname. - msrc := fs.NewCachingMountSource(ctx, tmpFS, upperFlags) - return fs.NewOverlayRootFile(ctx, msrc, lower, upperFlags) - } - - // Create overlay on top of mount dir. - upper, err := tmpFS.Mount(ctx, name+"-upper", upperFlags, "", nil) - if err != nil { - return nil, fmt.Errorf("creating tmpfs overlay: %v", err) - } - - // Replicate permissions and owner from lower to upper mount point. - attr, err := lower.UnstableAttr(ctx) - if err != nil { - return nil, fmt.Errorf("reading attributes from lower mount point: %v", err) - } - if !upper.InodeOperations.SetPermissions(ctx, upper, attr.Perms) { - return nil, fmt.Errorf("error setting permission to upper mount point") - } - if err := upper.InodeOperations.SetOwner(ctx, upper, attr.Owner); err != nil { - return nil, fmt.Errorf("setting owner to upper mount point: %v", err) - } - - return fs.NewOverlayRoot(ctx, upper, lower, upperFlags) -} - // compileMounts returns the supported mounts from the mount spec, adding any // mandatory mounts that are required by the OCI specification. -func compileMounts(spec *specs.Spec, conf *config.Config, vfs2Enabled bool) []specs.Mount { +func compileMounts(spec *specs.Spec, conf *config.Config) []specs.Mount { // Keep track of whether proc and sys were mounted. var procMounted, sysMounted, devMounted, devptsMounted bool var mounts []specs.Mount // Mount all submounts from the spec. for _, m := range spec.Mounts { - if !specutils.IsSupportedDevMount(m, vfs2Enabled) { - log.Warningf("ignoring dev mount at %q", m.Destination) - continue - } // Unconditionally drop any cgroupfs mounts. If requested, we'll add our // own below. if m.Type == cgroupfs.Name { @@ -189,21 +143,16 @@ func compileMounts(spec *specs.Spec, conf *config.Config, vfs2Enabled bool) []sp } // goferMountData creates a slice of gofer mount data. -func goferMountData(fd int, fa config.FileAccessType, vfs2 bool, lisafs bool) []string { +func goferMountData(fd int, fa config.FileAccessType, lisafs bool) []string { opts := []string{ "trans=fd", "rfdno=" + strconv.Itoa(fd), "wfdno=" + strconv.Itoa(fd), } - if !vfs2 { - // privateunixsocket is always enabled in VFS2. VFS1 requires explicit - // enablement. - opts = append(opts, "privateunixsocket=true") - } if fa == config.FileAccessShared { opts = append(opts, "cache=remote_revalidating") } - if vfs2 && lisafs { + if lisafs { opts = append(opts, "lisafs=true") } return opts @@ -233,18 +182,6 @@ func parseMountOption(opt string, allowedKeys ...string) (bool, error) { return specutils.ContainsStr(allowedKeys, kv[0]), nil } -// mountDevice returns a device string based on the fs type and target -// of the mount. -func mountDevice(m *specs.Mount) string { - if m.Type == bind { - // Make a device string that includes the target, which is consistent across - // S/R and uniquely identifies the connection. - return "9pfs-" + m.Destination - } - // All other fs types use device "none". - return "none" -} - func mountFlags(opts []string) fs.MountSourceFlags { mf := fs.MountSourceFlags{} // Note: changes to supported options must be reflected in @@ -284,82 +221,6 @@ func isSupportedMountFlag(fstype, opt string) bool { return false } -func mustFindFilesystem(name string) fs.Filesystem { - fs, ok := fs.FindFilesystem(name) - if !ok { - panic(fmt.Sprintf("could not find filesystem %q", name)) - } - return fs -} - -// addSubmountOverlay overlays the inode over a ramfs tree containing the given -// paths. -func addSubmountOverlay(ctx context.Context, inode *fs.Inode, submounts []string, mf fs.MountSourceFlags) (*fs.Inode, error) { - // Construct a ramfs tree of mount points. The contents never - // change, so this can be fully caching. There's no real - // filesystem backing this tree, so we set the filesystem to - // nil. - msrc := fs.NewCachingMountSource(ctx, nil, fs.MountSourceFlags{}) - mountTree, err := ramfs.MakeDirectoryTree(ctx, msrc, submounts) - if err != nil { - return nil, fmt.Errorf("creating mount tree: %v", err) - } - overlayInode, err := fs.NewOverlayRoot(ctx, inode, mountTree, mf) - if err != nil { - return nil, fmt.Errorf("adding mount overlay: %v", err) - } - return overlayInode, err -} - -// subtargets takes a set of Mounts and returns only the targets that are -// children of the given root. The returned paths are relative to the root. -func subtargets(root string, mnts []specs.Mount) []string { - var targets []string - for _, mnt := range mnts { - if relPath, isSubpath := fs.IsSubpath(mnt.Destination, root); isSubpath { - targets = append(targets, relPath) - } - } - return targets -} - -func setupContainerFS(ctx context.Context, conf *config.Config, mntr *containerMounter, procArgs *kernel.CreateProcessArgs) error { - if conf.VFS2 { - return setupContainerVFS2(ctx, conf, mntr, procArgs) - } - mns, err := mntr.setupFS(conf, procArgs) - if err != nil { - return err - } - - // Set namespace here so that it can be found in ctx. - procArgs.MountNamespace = mns - - // Resolve the executable path from working dir and environment. - resolved, err := user.ResolveExecutablePath(ctx, procArgs) - if err != nil { - return err - } - procArgs.Filename = resolved - return nil -} - -func adjustDirentCache(k *kernel.Kernel) error { - var hl unix.Rlimit - if err := unix.Getrlimit(unix.RLIMIT_NOFILE, &hl); err != nil { - return fmt.Errorf("getting RLIMIT_NOFILE: %v", err) - } - if hl.Cur != unix.RLIM_INFINITY { - newSize := hl.Cur / 2 - if newSize < gofer.DefaultDirentCacheSize { - log.Infof("Setting gofer dirent cache size to %d", newSize) - gofer.DefaultDirentCacheSize = newSize - k.DirentCacheLimiter = fs.NewDirentCacheLimiter(newSize) - } - } - return nil -} - type fdDispenser struct { fds []*fd.FD } @@ -628,10 +489,10 @@ type containerMounter struct { productName string } -func newContainerMounter(info *containerInfo, k *kernel.Kernel, hints *podMountHints, vfs2Enabled bool, productName string) *containerMounter { +func newContainerMounter(info *containerInfo, k *kernel.Kernel, hints *podMountHints, productName string) *containerMounter { return &containerMounter{ root: info.spec.Root, - mounts: compileMounts(info.spec, info.conf, vfs2Enabled), + mounts: compileMounts(info.spec, info.conf), fds: fdDispenser{fds: info.goferFDs}, k: k, hints: hints, @@ -639,99 +500,6 @@ func newContainerMounter(info *containerInfo, k *kernel.Kernel, hints *podMountH } } -// processHints processes annotations that container hints about how volumes -// should be mounted (e.g. a volume shared between containers). It must be -// called for the root container only. -func (c *containerMounter) processHints(conf *config.Config, creds *auth.Credentials) error { - if conf.VFS2 { - return c.processHintsVFS2(conf, creds) - } - ctx := c.k.SupervisorContext() - for _, hint := range c.hints.mounts { - // TODO(b/142076984): Only support tmpfs for now. Bind mounts require a - // common gofer to mount all shared volumes. - if hint.mount.Type != tmpfsvfs2.Name { - continue - } - log.Infof("Mounting master of shared mount %q from %q type %q", hint.name, hint.mount.Source, hint.mount.Type) - inode, err := c.mountSharedMaster(ctx, conf, hint) - if err != nil { - return fmt.Errorf("mounting shared master %q: %v", hint.name, err) - } - hint.root = inode - } - return nil -} - -// setupFS is used to set up the file system for all containers. This is the -// main entry point method, with most of the other being internal only. It -// returns the mount namespace that is created for the container. -func (c *containerMounter) setupFS(conf *config.Config, procArgs *kernel.CreateProcessArgs) (*fs.MountNamespace, error) { - log.Infof("Configuring container's file system") - - // Create context with root credentials to mount the filesystem (the current - // user may not be privileged enough). - rootProcArgs := *procArgs - rootProcArgs.WorkingDirectory = "/" - rootProcArgs.Credentials = auth.NewRootCredentials(procArgs.Credentials.UserNamespace) - rootProcArgs.Umask = 0022 - rootProcArgs.MaxSymlinkTraversals = linux.MaxSymlinkTraversals - rootCtx := rootProcArgs.NewContext(c.k) - - mns, err := c.createMountNamespace(rootCtx, conf) - if err != nil { - return nil, err - } - - // Set namespace here so that it can be found in rootCtx. - rootProcArgs.MountNamespace = mns - - if err := c.mountSubmounts(rootCtx, conf, mns); err != nil { - return nil, err - } - return mns, nil -} - -func (c *containerMounter) createMountNamespace(ctx context.Context, conf *config.Config) (*fs.MountNamespace, error) { - rootInode, err := c.createRootMount(ctx, conf) - if err != nil { - return nil, fmt.Errorf("creating filesystem for container: %v", err) - } - mns, err := fs.NewMountNamespace(ctx, rootInode) - if err != nil { - return nil, fmt.Errorf("creating new mount namespace for container: %v", err) - } - return mns, nil -} - -func (c *containerMounter) mountSubmounts(ctx context.Context, conf *config.Config, mns *fs.MountNamespace) error { - root := mns.Root() - defer root.DecRef(ctx) - - for i := range c.mounts { - m := &c.mounts[i] - log.Debugf("Mounting %q to %q, type: %s, options: %s", m.Source, m.Destination, m.Type, m.Options) - if hint := c.hints.findMount(m); hint != nil && hint.isSupported() { - if err := c.mountSharedSubmount(ctx, mns, root, m, hint); err != nil { - return fmt.Errorf("mount shared mount %q to %q: %v", hint.name, m.Destination, err) - } - } else { - if err := c.mountSubmount(ctx, conf, mns, root, m); err != nil { - return fmt.Errorf("mount submount %q: %v", m.Destination, err) - } - } - } - - if err := c.mountTmp(ctx, conf, mns, root); err != nil { - return fmt.Errorf("mount submount %q: %v", "tmp", err) - } - - if err := c.checkDispenser(); err != nil { - return err - } - return nil -} - func (c *containerMounter) checkDispenser() error { if !c.fds.empty() { return fmt.Errorf("not all gofer FDs were consumed, remaining: %v", c.fds) @@ -739,368 +507,9 @@ func (c *containerMounter) checkDispenser() error { return nil } -// mountSharedMaster mounts the master of a volume that is shared among -// containers in a pod. It returns the root mount's inode. -func (c *containerMounter) mountSharedMaster(ctx context.Context, conf *config.Config, hint *mountHint) (*fs.Inode, error) { - // Map mount type to filesystem name, and parse out the options that we are - // capable of dealing with. - fsName, opts, useOverlay, err := c.getMountNameAndOptions(conf, &hint.mount) - if err != nil { - return nil, err - } - if len(fsName) == 0 { - return nil, fmt.Errorf("mount type not supported %q", hint.mount.Type) - } - - // Mount with revalidate because it's shared among containers. - opts = append(opts, "cache=revalidate") - - // All filesystem names should have been mapped to something we know. - filesystem := mustFindFilesystem(fsName) - - mf := mountFlags(hint.mount.Options) - if useOverlay { - // All writes go to upper, be paranoid and make lower readonly. - mf.ReadOnly = true - } - - inode, err := filesystem.Mount(ctx, mountDevice(&hint.mount), mf, strings.Join(opts, ","), nil) - if err != nil { - return nil, fmt.Errorf("creating mount %q: %v", hint.name, err) - } - - if useOverlay { - log.Debugf("Adding overlay on top of shared mount %q", hint.name) - inode, err = addOverlay(ctx, inode, hint.mount.Type, mf) - if err != nil { - return nil, err - } - } - - return inode, nil -} - -// createRootMount creates the root filesystem. -func (c *containerMounter) createRootMount(ctx context.Context, conf *config.Config) (*fs.Inode, error) { - // First construct the filesystem from the spec.Root. - mf := fs.MountSourceFlags{ReadOnly: c.root.Readonly || conf.Overlay} - - fd := c.fds.remove() - log.Infof("Mounting root over 9P, ioFD: %d", fd) - p9FS := mustFindFilesystem("9p") - opts := goferMountData(fd, conf.FileAccess, false /* vfs2 */, false /* lisafs */) - - // We can't check for overlayfs here because sandbox is chroot'ed and gofer - // can only send mount options for specs.Mounts (specs.Root is missing - // Options field). So assume root is always on top of overlayfs. - opts = append(opts, "overlayfs_stale_read") - - rootInode, err := p9FS.Mount(ctx, rootDevice, mf, strings.Join(opts, ","), nil) - if err != nil { - return nil, fmt.Errorf("creating root mount point: %v", err) - } - - // We need to overlay the root on top of a ramfs with stub directories - // for submount paths. "/dev" "/sys" "/proc" and "/tmp" are always - // mounted even if they are not in the spec. - submounts := append(subtargets("/", c.mounts), "/dev", "/sys", "/proc", "/tmp") - rootInode, err = addSubmountOverlay(ctx, rootInode, submounts, mf) - if err != nil { - return nil, fmt.Errorf("adding submount overlay: %v", err) - } - - if conf.Overlay && !c.root.Readonly { - log.Debugf("Adding overlay on top of root mount") - // Overlay a tmpfs filesystem on top of the root. - rootInode, err = addOverlay(ctx, rootInode, "root-overlay-upper", mf) - if err != nil { - return nil, err - } - } - - log.Infof("Mounted %q to %q type root", c.root.Path, "/") - return rootInode, nil -} - -// getMountNameAndOptions retrieves the fsName, opts, and useOverlay values -// used for mounts. -func (c *containerMounter) getMountNameAndOptions(conf *config.Config, m *specs.Mount) (string, []string, bool, error) { - specutils.MaybeConvertToBindMount(m) - - var ( - fsName string - opts []string - useOverlay bool - ) - switch m.Type { - case devpts.Name, devtmpfs.Name, procvfs2.Name, sysvfs2.Name: - fsName = m.Type - case nonefs: - fsName = sysvfs2.Name - case tmpfsvfs2.Name: - fsName = m.Type - - var err error - opts, err = parseAndFilterOptions(m.Options, tmpfsAllowedData...) - if err != nil { - return "", nil, false, err - } - - case bind: - fd := c.fds.remove() - fsName = gofervfs2.Name - opts = goferMountData(fd, c.getMountAccessType(conf, m), conf.VFS2, conf.Lisafs) - // If configured, add overlay to all writable mounts. - useOverlay = conf.Overlay && !mountFlags(m.Options).ReadOnly - case cgroupfs.Name: - fsName = m.Type - var err error - opts, err = parseAndFilterOptions(m.Options, cgroupfs.SupportedMountOptions...) - if err != nil { - return "", nil, false, err - } - default: - log.Warningf("ignoring unknown filesystem type %q", m.Type) - } - return fsName, opts, useOverlay, nil -} - func (c *containerMounter) getMountAccessType(conf *config.Config, mount *specs.Mount) config.FileAccessType { if hint := c.hints.findMount(mount); hint != nil { return hint.fileAccessType() } return conf.FileAccessMounts } - -// mountSubmount mounts volumes inside the container's root. Because mounts may -// be readonly, a lower ramfs overlay is added to create the mount point dir. -// Another overlay is added with tmpfs on top if Config.Overlay is true. -// 'm.Destination' must be an absolute path with '..' and symlinks resolved. -func (c *containerMounter) mountSubmount(ctx context.Context, conf *config.Config, mns *fs.MountNamespace, root *fs.Dirent, m *specs.Mount) error { - // Map mount type to filesystem name, and parse out the options that we are - // capable of dealing with. - fsName, opts, useOverlay, err := c.getMountNameAndOptions(conf, m) - if err != nil { - return err - } - if fsName == "" { - // Filesystem is not supported (e.g. cgroup), just skip it. - return nil - } - - // All filesystem names should have been mapped to something we know. - filesystem := mustFindFilesystem(fsName) - - mf := mountFlags(m.Options) - if useOverlay { - // All writes go to upper, be paranoid and make lower readonly. - mf.ReadOnly = true - } - - inode, err := filesystem.Mount(ctx, mountDevice(m), mf, strings.Join(opts, ","), nil) - if err != nil { - err := fmt.Errorf("creating mount with source %q: %v", m.Source, err) - // Check to see if this is a common error due to a Linux bug. - // This error is generated here in order to cause it to be - // printed to the user using Docker via 'runsc create' etc. rather - // than simply printed to the logs for the 'runsc boot' command. - // - // We check the error message string rather than type because the - // actual error types (unix.EIO, unix.EPIPE) are lost by file system - // implementation (e.g. p9). - // TODO(gvisor.dev/issue/1765): Remove message when bug is resolved. - if strings.Contains(err.Error(), unix.EIO.Error()) || strings.Contains(err.Error(), unix.EPIPE.Error()) { - return fmt.Errorf("%v: %s", err, specutils.FaqErrorMsg("memlock", "you may be encountering a Linux kernel bug")) - } - return err - } - - // If there are submounts, we need to overlay the mount on top of a ramfs - // with stub directories for submount paths. - submounts := subtargets(m.Destination, c.mounts) - if len(submounts) > 0 { - log.Infof("Adding submount overlay over %q", m.Destination) - inode, err = addSubmountOverlay(ctx, inode, submounts, mf) - if err != nil { - return fmt.Errorf("adding submount overlay: %v", err) - } - } - - if useOverlay { - log.Debugf("Adding overlay on top of mount %q", m.Destination) - inode, err = addOverlay(ctx, inode, m.Type, mf) - if err != nil { - return err - } - } - - maxTraversals := uint(0) - dirent, err := mns.FindInode(ctx, root, root, m.Destination, &maxTraversals) - if err != nil { - return fmt.Errorf("can't find mount destination %q: %v", m.Destination, err) - } - defer dirent.DecRef(ctx) - if err := mns.Mount(ctx, dirent, inode); err != nil { - return fmt.Errorf("mount %q error: %v", m.Destination, err) - } - - log.Infof("Mounted %q to %q type: %s, internal-options: %q", m.Source, m.Destination, m.Type, opts) - return nil -} - -// mountSharedSubmount binds mount to a previously mounted volume that is shared -// among containers in the same pod. -func (c *containerMounter) mountSharedSubmount(ctx context.Context, mns *fs.MountNamespace, root *fs.Dirent, mount *specs.Mount, source *mountHint) error { - if err := source.checkCompatible(mount); err != nil { - return err - } - - maxTraversals := uint(0) - target, err := mns.FindInode(ctx, root, root, mount.Destination, &maxTraversals) - if err != nil { - return fmt.Errorf("can't find mount destination %q: %v", mount.Destination, err) - } - defer target.DecRef(ctx) - - // Take a ref on the inode that is about to be (re)-mounted. - source.root.IncRef() - if err := mns.Mount(ctx, target, source.root); err != nil { - source.root.DecRef(ctx) - return fmt.Errorf("bind mount %q error: %v", mount.Destination, err) - } - - log.Infof("Mounted %q type shared bind to %q", mount.Destination, source.name) - return nil -} - -// addRestoreMount adds a mount to the MountSources map used for restoring a -// checkpointed container. -func (c *containerMounter) addRestoreMount(conf *config.Config, renv *fs.RestoreEnvironment, m *specs.Mount) error { - fsName, opts, useOverlay, err := c.getMountNameAndOptions(conf, m) - if err != nil { - return err - } - if fsName == "" { - // Filesystem is not supported (e.g. cgroup), just skip it. - return nil - } - - newMount := fs.MountArgs{ - Dev: mountDevice(m), - Flags: mountFlags(m.Options), - DataString: strings.Join(opts, ","), - } - if useOverlay { - newMount.Flags.ReadOnly = true - } - renv.MountSources[fsName] = append(renv.MountSources[fsName], newMount) - log.Infof("Added mount at %q: %+v", fsName, newMount) - return nil -} - -// createRestoreEnvironment builds a fs.RestoreEnvironment called renv by adding -// the mounts to the environment. -func (c *containerMounter) createRestoreEnvironment(conf *config.Config) (*fs.RestoreEnvironment, error) { - renv := &fs.RestoreEnvironment{ - MountSources: make(map[string][]fs.MountArgs), - } - - // Add root mount. - fd := c.fds.remove() - opts := goferMountData(fd, conf.FileAccess, conf.VFS2, false /* lisafs */) - - mf := fs.MountSourceFlags{} - if c.root.Readonly || conf.Overlay { - mf.ReadOnly = true - } - - rootMount := fs.MountArgs{ - Dev: rootDevice, - Flags: mf, - DataString: strings.Join(opts, ","), - } - renv.MountSources[gofervfs2.Name] = append(renv.MountSources[gofervfs2.Name], rootMount) - - // Add submounts. - var tmpMounted bool - for i := range c.mounts { - m := &c.mounts[i] - if err := c.addRestoreMount(conf, renv, m); err != nil { - return nil, err - } - if filepath.Clean(m.Destination) == "/tmp" { - tmpMounted = true - } - } - - // TODO(b/67958150): handle '/tmp' properly (see mountTmp()). - if !tmpMounted { - tmpMount := specs.Mount{ - Type: tmpfsvfs2.Name, - Destination: "/tmp", - } - if err := c.addRestoreMount(conf, renv, &tmpMount); err != nil { - return nil, err - } - } - - return renv, nil -} - -// mountTmp mounts an internal tmpfs at '/tmp' if it's safe to do so. -// Technically we don't have to mount tmpfs at /tmp, as we could just rely on -// the host /tmp, but this is a nice optimization, and fixes some apps that call -// mknod in /tmp. It's unsafe to mount tmpfs if: -// 1. /tmp is mounted explicitly: we should not override user's wish -// 2. /tmp is not empty: mounting tmpfs would hide existing files in /tmp -// -// Note that when there are submounts inside of '/tmp', directories for the -// mount points must be present, making '/tmp' not empty anymore. -func (c *containerMounter) mountTmp(ctx context.Context, conf *config.Config, mns *fs.MountNamespace, root *fs.Dirent) error { - for _, m := range c.mounts { - if filepath.Clean(m.Destination) == "/tmp" { - log.Debugf("Explict %q mount found, skipping internal tmpfs, mount: %+v", "/tmp", m) - return nil - } - } - - maxTraversals := uint(0) - tmp, err := mns.FindInode(ctx, root, root, "tmp", &maxTraversals) - switch { - case err == nil: - // Found '/tmp' in filesystem, check if it's empty. - defer tmp.DecRef(ctx) - f, err := tmp.Inode.GetFile(ctx, tmp, fs.FileFlags{Read: true, Directory: true}) - if err != nil { - return err - } - defer f.DecRef(ctx) - serializer := &fs.CollectEntriesSerializer{} - if err := f.Readdir(ctx, serializer); err != nil { - return err - } - // If more than "." and ".." is found, skip internal tmpfs to prevent hiding - // existing files. - if len(serializer.Order) > 2 { - log.Infof("Skipping internal tmpfs on top %q, because it's not empty", "/tmp") - return nil - } - log.Infof("Mounting internal tmpfs on top of empty %q", "/tmp") - fallthrough - - case linuxerr.Equals(linuxerr.ENOENT, err): - // No '/tmp' found (or fallthrough from above). Safe to mount internal - // tmpfs. - tmpMount := specs.Mount{ - Type: tmpfsvfs2.Name, - Destination: "/tmp", - // Sticky bit is added to prevent accidental deletion of files from - // another user. This is normally done for /tmp. - Options: []string{"mode=01777"}, - } - return c.mountSubmount(ctx, conf, mns, root, &tmpMount) - - default: - return err - } -} diff --git a/runsc/boot/fs_test.go b/runsc/boot/fs_test.go index 5b9008d56..09ffda628 100644 --- a/runsc/boot/fs_test.go +++ b/runsc/boot/fs_test.go @@ -243,7 +243,7 @@ func TestGetMountAccessType(t *testing.T) { t.Fatalf("newPodMountHints failed: %v", err) } mounter := containerMounter{hints: podHints} - conf := &config.Config{VFS2: true, FileAccessMounts: config.FileAccessShared} + conf := &config.Config{FileAccessMounts: config.FileAccessShared} if got := mounter.getMountAccessType(conf, &specs.Mount{Source: source}); got != tst.want { t.Errorf("getMountAccessType(), want: %v, got: %v", tst.want, got) } diff --git a/runsc/boot/loader.go b/runsc/boot/loader.go index e1a5751ff..a55d5a1c1 100644 --- a/runsc/boot/loader.go +++ b/runsc/boot/loader.go @@ -253,13 +253,10 @@ func New(args Args) (*Loader, error) { return nil, fmt.Errorf("setting up memory usage: %w", err) } - // Is this a VFSv2 kernel? - if args.Conf.VFS2 { - kernel.VFS2Enabled = true - kernel.FUSEEnabled = args.Conf.FUSE - kernel.LISAFSEnabled = args.Conf.Lisafs - vfs2.Override() - } + kernel.VFS2Enabled = true + kernel.FUSEEnabled = args.Conf.FUSE + kernel.LISAFSEnabled = args.Conf.Lisafs + vfs2.Override() // Make host FDs stable between invocations. Host FDs must map to the exact // same number when the sandbox is restored. Otherwise the wrong FD will be @@ -377,14 +374,8 @@ func New(args Args) (*Loader, error) { return nil, fmt.Errorf("initializing kernel: %w", err) } - if kernel.VFS2Enabled { - if err := registerFilesystems(k); err != nil { - return nil, fmt.Errorf("registering filesystems: %w", err) - } - } - - if err := adjustDirentCache(k); err != nil { - return nil, err + if err := registerFilesystems(k); err != nil { + return nil, fmt.Errorf("registering filesystems: %w", err) } // Turn on packet logging if enabled. @@ -419,15 +410,13 @@ func New(args Args) (*Loader, error) { info.conf = args.Conf info.spec = args.Spec - if kernel.VFS2Enabled { - // Set up host mount that will be used for imported fds. - hostFilesystem, err := hostvfs2.NewFilesystem(k.VFS()) - if err != nil { - return nil, fmt.Errorf("failed to create hostfs filesystem: %w", err) - } - defer hostFilesystem.DecRef(k.SupervisorContext()) - k.SetHostMount(k.VFS().NewDisconnectedMount(hostFilesystem, nil, &vfs.MountOptions{})) + // Set up host mount that will be used for imported fds. + hostFilesystem, err := hostvfs2.NewFilesystem(k.VFS()) + if err != nil { + return nil, fmt.Errorf("failed to create hostfs filesystem: %w", err) } + defer hostFilesystem.DecRef(k.SupervisorContext()) + k.SetHostMount(k.VFS().NewDisconnectedMount(hostFilesystem, nil, &vfs.MountOptions{})) if args.PodInitConfigFD >= 0 { if err := setupSeccheck(args.PodInitConfigFD, args.SinkFDs); err != nil { @@ -837,30 +826,22 @@ func (l *Loader) createContainerProcess(root bool, cid string, info *containerIn } l.startGoferMonitor(cid, int32(info.goferFDs[0].FD())) - mntr := newContainerMounter(info, l.k, l.mountHints, kernel.VFS2Enabled, l.productName) + mntr := newContainerMounter(info, l.k, l.mountHints, l.productName) if root { if err := mntr.processHints(info.conf, info.procArgs.Credentials); err != nil { return nil, nil, nil, err } } - if err := setupContainerFS(ctx, info.conf, mntr, &info.procArgs); err != nil { + if err := setupContainerVFS(ctx, info.conf, mntr, &info.procArgs); err != nil { return nil, nil, nil, err } // Add the HOME environment variable if it is not already set. - var envv []string - if kernel.VFS2Enabled { - envv, err = user.MaybeAddExecUserHomeVFS2(ctx, info.procArgs.MountNamespaceVFS2, - info.procArgs.Credentials.RealKUID, info.procArgs.Envv) - - } else { - envv, err = user.MaybeAddExecUserHome(ctx, info.procArgs.MountNamespace, - info.procArgs.Credentials.RealKUID, info.procArgs.Envv) - } + info.procArgs.Envv, err = user.MaybeAddExecUserHomeVFS2(ctx, info.procArgs.MountNamespaceVFS2, + info.procArgs.Credentials.RealKUID, info.procArgs.Envv) if err != nil { return nil, nil, nil, err } - info.procArgs.Envv = envv // Create and start the new process. tg, _, err := l.k.CreateProcess(info.procArgs) @@ -1012,22 +993,10 @@ func (l *Loader) executeAsync(args *control.ExecArgs) (kernel.ThreadID, error) { // Get the container MountNamespace from the Task. Try to acquire ref may fail // in case it raced with task exit. - if kernel.VFS2Enabled { - // task.MountNamespaceVFS2() does not take a ref, so we must do so ourselves. - args.MountNamespaceVFS2 = tg.Leader().MountNamespaceVFS2() - if args.MountNamespaceVFS2 == nil || !args.MountNamespaceVFS2.TryIncRef() { - return 0, fmt.Errorf("container %q has stopped", args.ContainerID) - } - } else { - var reffed bool - tg.Leader().WithMuLocked(func(t *kernel.Task) { - // task.MountNamespace() does not take a ref, so we must do so ourselves. - args.MountNamespace = t.MountNamespace() - reffed = args.MountNamespace.TryIncRef() - }) - if !reffed { - return 0, fmt.Errorf("container %q has stopped", args.ContainerID) - } + // task.MountNamespaceVFS2() does not take a ref, so we must do so ourselves. + args.MountNamespaceVFS2 = tg.Leader().MountNamespaceVFS2() + if args.MountNamespaceVFS2 == nil || !args.MountNamespaceVFS2.TryIncRef() { + return 0, fmt.Errorf("container %q has stopped", args.ContainerID) } args.Envv, err = specutils.ResolveEnvs(args.Envv) @@ -1036,25 +1005,11 @@ func (l *Loader) executeAsync(args *control.ExecArgs) (kernel.ThreadID, error) { } // Add the HOME environment variable if it is not already set. - if kernel.VFS2Enabled { - root := args.MountNamespaceVFS2.Root() - ctx := vfs.WithRoot(l.k.SupervisorContext(), root) - defer args.MountNamespaceVFS2.DecRef(ctx) - envv, err := user.MaybeAddExecUserHomeVFS2(ctx, args.MountNamespaceVFS2, args.KUID, args.Envv) - if err != nil { - return 0, err - } - args.Envv = envv - } else { - root := args.MountNamespace.Root() - ctx := fs.WithRoot(l.k.SupervisorContext(), root) - defer args.MountNamespace.DecRef(ctx) - defer root.DecRef(ctx) - envv, err := user.MaybeAddExecUserHome(ctx, args.MountNamespace, args.KUID, args.Envv) - if err != nil { - return 0, err - } - args.Envv = envv + ctx := vfs.WithRoot(l.k.SupervisorContext(), args.MountNamespaceVFS2.Root()) + defer args.MountNamespaceVFS2.DecRef(ctx) + args.Envv, err = user.MaybeAddExecUserHomeVFS2(ctx, args.MountNamespaceVFS2, args.KUID, args.Envv) + if err != nil { + return 0, err } args.PIDNamespace = tg.PIDNamespace() diff --git a/runsc/boot/loader_test.go b/runsc/boot/loader_test.go index ed796c75f..695b3c782 100644 --- a/runsc/boot/loader_test.go +++ b/runsc/boot/loader_test.go @@ -18,19 +18,15 @@ import ( "fmt" "math/rand" "os" - "reflect" "testing" "time" specs "github.com/opencontainers/runtime-spec/specs-go" "golang.org/x/sys/unix" "gvisor.dev/gvisor/pkg/control/server" - "gvisor.dev/gvisor/pkg/fd" "gvisor.dev/gvisor/pkg/fspath" "gvisor.dev/gvisor/pkg/log" "gvisor.dev/gvisor/pkg/p9" - "gvisor.dev/gvisor/pkg/sentry/contexttest" - "gvisor.dev/gvisor/pkg/sentry/fs" "gvisor.dev/gvisor/pkg/sentry/vfs" "gvisor.dev/gvisor/pkg/sync" "gvisor.dev/gvisor/pkg/unet" @@ -110,14 +106,12 @@ func startGofer(root string) (int, func(), error) { return sandboxEnd, cleanup, nil } -func createLoader(vfsEnabled bool, spec *specs.Spec) (*Loader, func(), error) { +func createLoader(spec *specs.Spec) (*Loader, func(), error) { fd, err := server.CreateSocket(ControlSocketAddr(fmt.Sprintf("%010d", rand.Int())[:10])) if err != nil { return nil, nil, err } conf := testConfig() - conf.VFS2 = vfsEnabled - sandEnd, cleanup, err := startGofer(spec.Root.Path) if err != nil { return nil, nil, err @@ -152,16 +146,7 @@ func createLoader(vfsEnabled bool, spec *specs.Spec) (*Loader, func(), error) { // TestRun runs a simple application in a sandbox and checks that it succeeds. func TestRun(t *testing.T) { - doRun(t, false) -} - -// TestRunVFS2 runs TestRun in VFSv2. -func TestRunVFS2(t *testing.T) { - doRun(t, true) -} - -func doRun(t *testing.T, vfsEnabled bool) { - l, cleanup, err := createLoader(vfsEnabled, testSpec()) + l, cleanup, err := createLoader(testSpec()) if err != nil { t.Fatalf("error creating loader: %v", err) } @@ -199,16 +184,7 @@ func doRun(t *testing.T, vfsEnabled bool) { // TestStartSignal tests that the controller Start message will cause // WaitForStartSignal to return. func TestStartSignal(t *testing.T) { - doStartSignal(t, false) -} - -// TestStartSignalVFS2 does TestStartSignal with VFS2. -func TestStartSignalVFS2(t *testing.T) { - doStartSignal(t, true) -} - -func doStartSignal(t *testing.T, vfsEnabled bool) { - l, cleanup, err := createLoader(vfsEnabled, testSpec()) + l, cleanup, err := createLoader(testSpec()) if err != nil { t.Fatalf("error creating loader: %v", err) } @@ -253,7 +229,6 @@ func doStartSignal(t *testing.T, vfsEnabled bool) { case <-time.After(50 * time.Millisecond): t.Errorf("WaitForStartSignal did not complete but it should have") } - } type CreateMountTestcase struct { @@ -431,49 +406,6 @@ func createMountTestcases() []*CreateMountTestcase { // Test that MountNamespace can be created with various specs. func TestCreateMountNamespace(t *testing.T) { - for _, tc := range createMountTestcases() { - t.Run(tc.name, func(t *testing.T) { - conf := testConfig() - ctx := contexttest.Context(t) - - sandEnd, cleanup, err := startGofer(tc.spec.Root.Path) - if err != nil { - t.Fatalf("failed to create gofer: %v", err) - } - defer cleanup() - - info := containerInfo{ - conf: conf, - spec: &tc.spec, - goferFDs: []*fd.FD{fd.New(sandEnd)}, - } - - mntr := newContainerMounter(&info, nil, &podMountHints{}, false /* vfs2Enabled */, "") - mns, err := mntr.createMountNamespace(ctx, conf) - if err != nil { - t.Fatalf("failed to create mount namespace: %v", err) - } - ctx = fs.WithRoot(ctx, mns.Root()) - if err := mntr.mountSubmounts(ctx, conf, mns); err != nil { - t.Fatalf("failed to create mount namespace: %v", err) - } - - root := mns.Root() - defer root.DecRef(ctx) - for _, p := range tc.expectedPaths { - maxTraversals := uint(0) - if d, err := mns.FindInode(ctx, root, root, p, &maxTraversals); err != nil { - t.Errorf("expected path %v to exist with spec %v, but got error %v", p, tc.spec, err) - } else { - d.DecRef(ctx) - } - } - }) - } -} - -// Test that MountNamespace can be created with various specs. -func TestCreateMountNamespaceVFS2(t *testing.T) { for _, tc := range createMountTestcases() { t.Run(tc.name, func(t *testing.T) { spec := testSpec() @@ -481,14 +413,14 @@ func TestCreateMountNamespaceVFS2(t *testing.T) { spec.Root = tc.spec.Root t.Logf("Using root: %q", spec.Root.Path) - l, loaderCleanup, err := createLoader(true /* VFS2 Enabled */, spec) + l, loaderCleanup, err := createLoader(spec) if err != nil { t.Fatalf("failed to create loader: %v", err) } defer l.Destroy() defer loaderCleanup() - mntr := newContainerMounter(&l.root, l.k, l.mountHints, true /* vfs2Enabled */, "") + mntr := newContainerMounter(&l.root, l.k, l.mountHints, "") if err := mntr.processHints(l.root.conf, l.root.procArgs.Credentials); err != nil { t.Fatalf("failed process hints: %v", err) } @@ -518,218 +450,3 @@ func TestCreateMountNamespaceVFS2(t *testing.T) { }) } } - -// TestRestoreEnvironment tests that the correct mounts are collected from the spec and config -// in order to build the environment for restoring. -func TestRestoreEnvironment(t *testing.T) { - testCases := []struct { - name string - spec *specs.Spec - ioFDs []int - errorExpected bool - expectedRenv fs.RestoreEnvironment - }{ - { - name: "basic spec test", - spec: &specs.Spec{ - Root: &specs.Root{ - Path: os.TempDir(), - Readonly: true, - }, - Mounts: []specs.Mount{ - { - Destination: "/some/very/very/deep/path", - Type: "tmpfs", - }, - { - Destination: "/proc", - Type: "tmpfs", - }, - }, - }, - ioFDs: []int{0}, - errorExpected: false, - expectedRenv: fs.RestoreEnvironment{ - MountSources: map[string][]fs.MountArgs{ - "9p": { - { - Dev: "9pfs-/", - Flags: fs.MountSourceFlags{ReadOnly: true}, - DataString: "trans=fd,rfdno=0,wfdno=0", - }, - }, - "tmpfs": { - { - Dev: "none", - }, - { - Dev: "none", - }, - { - Dev: "none", - }, - }, - "devtmpfs": { - { - Dev: "none", - }, - }, - "devpts": { - { - Dev: "none", - }, - }, - "sysfs": { - { - Dev: "none", - }, - }, - }, - }, - }, - { - name: "bind type test", - spec: &specs.Spec{ - Root: &specs.Root{ - Path: os.TempDir(), - Readonly: true, - }, - Mounts: []specs.Mount{ - { - Destination: "/dev/fd-foo", - Type: "bind", - }, - }, - }, - ioFDs: []int{0, 1}, - errorExpected: false, - expectedRenv: fs.RestoreEnvironment{ - MountSources: map[string][]fs.MountArgs{ - "9p": { - { - Dev: "9pfs-/", - Flags: fs.MountSourceFlags{ReadOnly: true}, - DataString: "trans=fd,rfdno=0,wfdno=0", - }, - { - Dev: "9pfs-/dev/fd-foo", - DataString: "trans=fd,rfdno=1,wfdno=1,cache=remote_revalidating", - }, - }, - "tmpfs": { - { - Dev: "none", - }, - }, - "devtmpfs": { - { - Dev: "none", - }, - }, - "devpts": { - { - Dev: "none", - }, - }, - "proc": { - { - Dev: "none", - }, - }, - "sysfs": { - { - Dev: "none", - }, - }, - }, - }, - }, - { - name: "options test", - spec: &specs.Spec{ - Root: &specs.Root{ - Path: os.TempDir(), - Readonly: true, - }, - Mounts: []specs.Mount{ - { - Destination: "/dev/fd-foo", - Type: "tmpfs", - Options: []string{"uid=1022", "noatime"}, - }, - }, - }, - ioFDs: []int{0}, - errorExpected: false, - expectedRenv: fs.RestoreEnvironment{ - MountSources: map[string][]fs.MountArgs{ - "9p": { - { - Dev: "9pfs-/", - Flags: fs.MountSourceFlags{ReadOnly: true}, - DataString: "trans=fd,rfdno=0,wfdno=0", - }, - }, - "tmpfs": { - { - Dev: "none", - Flags: fs.MountSourceFlags{NoAtime: true}, - DataString: "uid=1022", - }, - { - Dev: "none", - }, - }, - "devtmpfs": { - { - Dev: "none", - }, - }, - "devpts": { - { - Dev: "none", - }, - }, - "proc": { - { - Dev: "none", - }, - }, - "sysfs": { - { - Dev: "none", - }, - }, - }, - }, - }, - } - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - conf := testConfig() - conf.VFS2 = true - var ioFDs []*fd.FD - for _, ioFD := range tc.ioFDs { - ioFDs = append(ioFDs, fd.New(ioFD)) - } - info := containerInfo{ - conf: conf, - spec: tc.spec, - goferFDs: ioFDs, - } - mntr := newContainerMounter(&info, nil, &podMountHints{}, conf.VFS2, "") - actualRenv, err := mntr.createRestoreEnvironment(conf) - if !tc.errorExpected && err != nil { - t.Fatalf("could not create restore environment for test:%s", tc.name) - } else if tc.errorExpected { - if err == nil { - t.Errorf("expected an error, but no error occurred.") - } - } else { - if !reflect.DeepEqual(*actualRenv, tc.expectedRenv) { - t.Errorf("restore environments did not match for test:%s\ngot:%+v\nwant:%+v\n", tc.name, *actualRenv, tc.expectedRenv) - } - } - }) - } -} diff --git a/runsc/boot/vfs.go b/runsc/boot/vfs.go index 28431f424..47e4065fa 100644 --- a/runsc/boot/vfs.go +++ b/runsc/boot/vfs.go @@ -151,7 +151,7 @@ func registerFilesystems(k *kernel.Kernel) error { return nil } -func setupContainerVFS2(ctx context.Context, conf *config.Config, mntr *containerMounter, procArgs *kernel.CreateProcessArgs) error { +func setupContainerVFS(ctx context.Context, conf *config.Config, mntr *containerMounter, procArgs *kernel.CreateProcessArgs) error { mns, err := mntr.mountAll(conf, procArgs) if err != nil { return fmt.Errorf("failed to setupFS: %w", err) @@ -213,7 +213,7 @@ func (c *containerMounter) mountAll(conf *config.Config, procArgs *kernel.Create // createMountNamespaceVFS2 creates the container's root mount and namespace. func (c *containerMounter) createMountNamespaceVFS2(ctx context.Context, conf *config.Config, creds *auth.Credentials) (*vfs.MountNamespace, error) { fd := c.fds.remove() - data := goferMountData(fd, conf.FileAccess, true /* vfs2 */, conf.Lisafs) + data := goferMountData(fd, conf.FileAccess, conf.Lisafs) // We can't check for overlayfs here because sandbox is chroot'ed and gofer // can only send mount options for specs.Mounts (specs.Root is missing @@ -525,7 +525,7 @@ func (c *containerMounter) getMountNameAndOptionsVFS2(conf *config.Config, m *mo // but unlikely to be correct in this context. return "", nil, false, fmt.Errorf("9P mount requires a connection FD") } - data = goferMountData(m.fd, c.getMountAccessType(conf, m.mount), true /* vfs2 */, conf.Lisafs) + data = goferMountData(m.fd, c.getMountAccessType(conf, m.mount), conf.Lisafs) internalData = gofer.InternalFilesystemOptions{ UniqueID: m.mount.Destination, } @@ -718,10 +718,10 @@ func (c *containerMounter) mountTmpVFS2(ctx context.Context, conf *config.Config } } -// processHintsVFS2 processes annotations that container hints about how volumes +// processHints processes annotations that container hints about how volumes // should be mounted (e.g. a volume shared between containers). It must be // called for the root container only. -func (c *containerMounter) processHintsVFS2(conf *config.Config, creds *auth.Credentials) error { +func (c *containerMounter) processHints(conf *config.Config, creds *auth.Credentials) error { ctx := c.k.SupervisorContext() for _, hint := range c.hints.mounts { // TODO(b/142076984): Only support tmpfs for now. Bind mounts require a diff --git a/runsc/cmd/gofer.go b/runsc/cmd/gofer.go index 3d4cbfc89..261b5d0b2 100644 --- a/runsc/cmd/gofer.go +++ b/runsc/cmd/gofer.go @@ -218,7 +218,7 @@ func (g *Gofer) serveLisafs(spec *specs.Spec, conf *config.Config, root string) mountIdx := 1 // first one is the root for _, m := range spec.Mounts { - if !specutils.IsGoferMount(m, conf.VFS2) { + if !specutils.IsGoferMount(m) { continue } @@ -273,7 +273,7 @@ func (g *Gofer) serve9P(spec *specs.Spec, conf *config.Config, root string) subc mountIdx := 1 // first one is the root for _, m := range spec.Mounts { - if specutils.IsGoferMount(m, conf.VFS2) { + if specutils.IsGoferMount(m) { cfg := fsgofer.Config{ ROMount: isReadonlyMount(m.Options) || conf.Overlay, HostUDS: conf.FSGoferHostUDS, @@ -445,7 +445,7 @@ func setupRootFS(spec *specs.Spec, conf *config.Config) error { // creates directories as needed. func setupMounts(conf *config.Config, mounts []specs.Mount, root, procPath string) error { for _, m := range mounts { - if !specutils.IsGoferMount(m, conf.VFS2) { + if !specutils.IsGoferMount(m) { continue } @@ -485,7 +485,7 @@ func setupMounts(conf *config.Config, mounts []specs.Mount, root, procPath strin func resolveMounts(conf *config.Config, mounts []specs.Mount, root string) ([]specs.Mount, error) { cleanMounts := make([]specs.Mount, 0, len(mounts)) for _, m := range mounts { - if !specutils.IsGoferMount(m, conf.VFS2) { + if !specutils.IsGoferMount(m) { cleanMounts = append(cleanMounts, m) continue } diff --git a/runsc/config/config.go b/runsc/config/config.go index 0f3b2b9f4..df628f312 100644 --- a/runsc/config/config.go +++ b/runsc/config/config.go @@ -204,9 +204,6 @@ type Config struct { // E.g. 0.2 CPU quota will result in 1, and 1.9 in 2. CPUNumFromQuota bool `flag:"cpu-num-from-quota"` - // DEPRECATED: VFS2 is always enabled and cannot be disabled. - VFS2 bool - // Enable lisafs. Lisafs bool `flag:"lisafs"` diff --git a/runsc/config/flags.go b/runsc/config/flags.go index b2672d590..2ba2b24b8 100644 --- a/runsc/config/flags.go +++ b/runsc/config/flags.go @@ -131,7 +131,7 @@ func checkOciSeccomp(name string, value string) error { // NewFromFlags creates a new Config with values coming from command line flags. func NewFromFlags(flagSet *flag.FlagSet) (*Config, error) { - conf := &Config{VFS2: true} + conf := &Config{} obj := reflect.ValueOf(conf).Elem() st := obj.Type() diff --git a/runsc/container/container.go b/runsc/container/container.go index 8600385b0..b6d63120b 100644 --- a/runsc/container/container.go +++ b/runsc/container/container.go @@ -938,7 +938,7 @@ func (c *Container) createGoferProcess(spec *specs.Spec, conf *config.Config, bu // Add root mount and then add any other additional mounts. mountCount := 1 for _, m := range spec.Mounts { - if specutils.IsGoferMount(m, conf.VFS2) { + if specutils.IsGoferMount(m) { mountCount++ } } diff --git a/runsc/specutils/specutils.go b/runsc/specutils/specutils.go index 918374f17..22bd192ce 100644 --- a/runsc/specutils/specutils.go +++ b/runsc/specutils/specutils.go @@ -335,9 +335,9 @@ func capsFromNames(names []string, skipSet map[linux.Capability]struct{}) (auth. // IsGoferMount returns true if the given mount can be mounted as an external // gofer. -func IsGoferMount(m specs.Mount, vfs2Enabled bool) bool { +func IsGoferMount(m specs.Mount) bool { MaybeConvertToBindMount(&m) - return m.Type == "bind" && m.Source != "" && IsSupportedDevMount(m, vfs2Enabled) + return m.Type == "bind" && m.Source != "" } // MaybeConvertToBindMount converts mount type to "bind" in case any of the @@ -357,29 +357,6 @@ func MaybeConvertToBindMount(m *specs.Mount) { } } -// IsSupportedDevMount returns true if m.Destination does not specify a -// path that is hardcoded by VFS1's implementation of /dev. -func IsSupportedDevMount(m specs.Mount, vfs2Enabled bool) bool { - // VFS2 has no hardcoded files under /dev, so everything is allowed. - if vfs2Enabled { - return true - } - - // See pkg/sentry/fs/dev/dev.go. - var existingDevices = []string{ - "/dev/fd", "/dev/stdin", "/dev/stdout", "/dev/stderr", - "/dev/null", "/dev/zero", "/dev/full", "/dev/random", - "/dev/urandom", "/dev/shm", "/dev/ptmx", - } - dst := filepath.Clean(m.Destination) - for _, dev := range existingDevices { - if dst == dev || strings.HasPrefix(dst, dev+"/") { - return false - } - } - return true -} - // WaitForReady waits for a process to become ready. The process is ready when // the 'ready' function returns true. It continues to wait if 'ready' returns // false. It returns error on timeout, if the process stops or if 'ready' fails.