diff --git a/pkg/sentry/control/BUILD b/pkg/sentry/control/BUILD index 65df8a93a..4761aad46 100644 --- a/pkg/sentry/control/BUILD +++ b/pkg/sentry/control/BUILD @@ -34,8 +34,6 @@ go_library( "//pkg/fspath", "//pkg/log", "//pkg/sentry/fdimport", - "//pkg/sentry/fs", - "//pkg/sentry/fs/host", "//pkg/sentry/fs/user", "//pkg/sentry/fsimpl/host", "//pkg/sentry/fsmetric", diff --git a/pkg/sentry/control/lifecycle.go b/pkg/sentry/control/lifecycle.go index b11457ba1..fabae99c5 100644 --- a/pkg/sentry/control/lifecycle.go +++ b/pkg/sentry/control/lifecycle.go @@ -246,7 +246,7 @@ func (l *Lifecycle) StartContainer(args *StartContainerArgs, _ *uint32) error { for i, appFD := range args.DonatedFDs { fdMap[appFD] = hostFDs[i] } - if _, _, err := fdimport.Import(ctx, fdTable, false, args.KUID, args.KGID, fdMap); err != nil { + if _, err := fdimport.Import(ctx, fdTable, false, args.KUID, args.KGID, fdMap); err != nil { return fmt.Errorf("error importing host files: %w", err) } initArgs.FDTable = fdTable @@ -258,9 +258,9 @@ func (l *Lifecycle) StartContainer(args *StartContainerArgs, _ *uint32) error { l.mu.RUnlock() return fmt.Errorf("mount namespace is nil for %s", initArgs.ContainerID) } - initArgs.MountNamespaceVFS2 = mntns + initArgs.MountNamespace = mntns l.mu.RUnlock() - initArgs.MountNamespaceVFS2.IncRef() + initArgs.MountNamespace.IncRef() if args.ResolveBinaryPath { resolved, err := user.ResolveExecutablePath(ctx, &initArgs) @@ -271,7 +271,7 @@ func (l *Lifecycle) StartContainer(args *StartContainerArgs, _ *uint32) error { } if args.ResolveHome { - envVars, err := user.MaybeAddExecUserHomeVFS2(ctx, initArgs.MountNamespaceVFS2, creds.RealKUID, initArgs.Envv) + envVars, err := user.MaybeAddExecUserHome(ctx, initArgs.MountNamespace, creds.RealKUID, initArgs.Envv) if err != nil { return fmt.Errorf("failed to get user home dir: %w", err) } diff --git a/pkg/sentry/control/proc.go b/pkg/sentry/control/proc.go index 513e28a49..1d46236b4 100644 --- a/pkg/sentry/control/proc.go +++ b/pkg/sentry/control/proc.go @@ -26,10 +26,8 @@ import ( "gvisor.dev/gvisor/pkg/abi/linux" "gvisor.dev/gvisor/pkg/fd" "gvisor.dev/gvisor/pkg/sentry/fdimport" - "gvisor.dev/gvisor/pkg/sentry/fs" - "gvisor.dev/gvisor/pkg/sentry/fs/host" "gvisor.dev/gvisor/pkg/sentry/fs/user" - hostvfs2 "gvisor.dev/gvisor/pkg/sentry/fsimpl/host" + "gvisor.dev/gvisor/pkg/sentry/fsimpl/host" "gvisor.dev/gvisor/pkg/sentry/kernel" "gvisor.dev/gvisor/pkg/sentry/kernel/auth" ktime "gvisor.dev/gvisor/pkg/sentry/kernel/time" @@ -63,13 +61,7 @@ type ExecArgs struct { // A reference on MountNamespace must be held for the lifetime of the // ExecArgs. If MountNamespace is nil, it will default to the init // process's MountNamespace. - MountNamespace *fs.MountNamespace - - // MountNamespaceVFS2 is the mount namespace to execute the new process in. - // A reference on MountNamespace must be held for the lifetime of the - // ExecArgs. If MountNamespace is nil, it will default to the init - // process's MountNamespace. - MountNamespaceVFS2 *vfs.MountNamespace + MountNamespace *vfs.MountNamespace // WorkingDirectory defines the working directory for the new process. WorkingDirectory string `json:"wd"` @@ -119,7 +111,7 @@ func (args ExecArgs) String() string { // Exec runs a new task. func (proc *Proc) Exec(args *ExecArgs, waitStatus *uint32) error { - newTG, _, _, _, err := proc.execAsync(args) + newTG, _, _, err := proc.execAsync(args) if err != nil { return err } @@ -132,14 +124,14 @@ func (proc *Proc) Exec(args *ExecArgs, waitStatus *uint32) error { // ExecAsync runs a new task, but doesn't wait for it to finish. It is defined // as a function rather than a method to avoid exposing execAsync as an RPC. -func ExecAsync(proc *Proc, args *ExecArgs) (*kernel.ThreadGroup, kernel.ThreadID, *host.TTYFileOperations, *hostvfs2.TTYFileDescription, error) { +func ExecAsync(proc *Proc, args *ExecArgs) (*kernel.ThreadGroup, kernel.ThreadID, *host.TTYFileDescription, error) { return proc.execAsync(args) } // execAsync runs a new task, but doesn't wait for it to finish. It returns the // newly created thread group and its PID. If the stdio FDs are TTYs, then a // TTYFileOperations that wraps the TTY is also returned. -func (proc *Proc) execAsync(args *ExecArgs) (*kernel.ThreadGroup, kernel.ThreadID, *host.TTYFileOperations, *hostvfs2.TTYFileDescription, error) { +func (proc *Proc) execAsync(args *ExecArgs) (*kernel.ThreadGroup, kernel.ThreadID, *host.TTYFileDescription, error) { // Import file descriptors. fdTable := proc.Kernel.NewFDTable() @@ -164,7 +156,6 @@ func (proc *Proc) execAsync(args *ExecArgs) (*kernel.ThreadGroup, kernel.ThreadI Envv: args.Envv, WorkingDirectory: args.WorkingDirectory, MountNamespace: args.MountNamespace, - MountNamespaceVFS2: args.MountNamespaceVFS2, Credentials: creds, FDTable: fdTable, Umask: 0022, @@ -177,46 +168,30 @@ func (proc *Proc) execAsync(args *ExecArgs) (*kernel.ThreadGroup, kernel.ThreadI PIDNamespace: pidns, } if initArgs.MountNamespace != nil { - // initArgs must hold a reference on MountNamespace, which will - // be donated to the new process in CreateProcess. - initArgs.MountNamespace.IncRef() - } - if initArgs.MountNamespaceVFS2 != nil { // initArgs must hold a reference on MountNamespaceVFS2, which will // be donated to the new process in CreateProcess. - initArgs.MountNamespaceVFS2.IncRef() + initArgs.MountNamespace.IncRef() } ctx := initArgs.NewContext(proc.Kernel) defer fdTable.DecRef(ctx) - if kernel.VFS2Enabled { - // Get the full path to the filename from the PATH env variable. - if initArgs.MountNamespaceVFS2 == nil { - // Set initArgs so that 'ctx' returns the namespace. - // - // Add a reference to the namespace, which is transferred to the new process. - initArgs.MountNamespaceVFS2 = proc.Kernel.GlobalInit().Leader().MountNamespaceVFS2() - initArgs.MountNamespaceVFS2.IncRef() - } - } else { - if initArgs.MountNamespace == nil { - // Set initArgs so that 'ctx' returns the namespace. - initArgs.MountNamespace = proc.Kernel.GlobalInit().Leader().MountNamespace() - - // initArgs must hold a reference on MountNamespace, which will - // be donated to the new process in CreateProcess. - initArgs.MountNamespace.IncRef() - } + // Get the full path to the filename from the PATH env variable. + if initArgs.MountNamespace == nil { + // Set initArgs so that 'ctx' returns the namespace. + // + // Add a reference to the namespace, which is transferred to the new process. + initArgs.MountNamespace = proc.Kernel.GlobalInit().Leader().MountNamespaceVFS2() + initArgs.MountNamespace.IncRef() } resolved, err := user.ResolveExecutablePath(ctx, &initArgs) if err != nil { - return nil, 0, nil, nil, err + return nil, 0, nil, err } initArgs.Filename = resolved fds, err := fd.NewFromFiles(args.Files) if err != nil { - return nil, 0, nil, nil, fmt.Errorf("duplicating payload files: %w", err) + return nil, 0, nil, fmt.Errorf("duplicating payload files: %w", err) } defer func() { for _, fd := range fds { @@ -227,28 +202,25 @@ func (proc *Proc) execAsync(args *ExecArgs) (*kernel.ThreadGroup, kernel.ThreadI for appFD, hostFD := range fds { fdMap[appFD] = hostFD } - ttyFile, ttyFileVFS2, err := fdimport.Import(ctx, fdTable, args.StdioIsPty, args.KUID, args.KGID, fdMap) + ttyFile, err := fdimport.Import(ctx, fdTable, args.StdioIsPty, args.KUID, args.KGID, fdMap) if err != nil { - return nil, 0, nil, nil, err + return nil, 0, nil, err } tg, tid, err := proc.Kernel.CreateProcess(initArgs) if err != nil { - return nil, 0, nil, nil, err + return nil, 0, nil, err } // Set the foreground process group on the TTY before starting the process. - switch { - case ttyFile != nil: + if ttyFile != nil { ttyFile.InitForegroundProcessGroup(tg.ProcessGroup()) - case ttyFileVFS2 != nil: - ttyFileVFS2.InitForegroundProcessGroup(tg.ProcessGroup()) } // Start the newly created process. proc.Kernel.StartProcess(tg) - return tg, tid, ttyFile, ttyFileVFS2, nil + return tg, tid, ttyFile, nil } // PsArgs is the set of arguments to ps. diff --git a/pkg/sentry/fdimport/BUILD b/pkg/sentry/fdimport/BUILD index 563e96e0d..cf310ac18 100644 --- a/pkg/sentry/fdimport/BUILD +++ b/pkg/sentry/fdimport/BUILD @@ -11,8 +11,6 @@ go_library( deps = [ "//pkg/context", "//pkg/fd", - "//pkg/sentry/fs", - "//pkg/sentry/fs/host", "//pkg/sentry/fsimpl/host", "//pkg/sentry/kernel", "//pkg/sentry/kernel/auth", diff --git a/pkg/sentry/fdimport/fdimport.go b/pkg/sentry/fdimport/fdimport.go index 866008854..49157d094 100644 --- a/pkg/sentry/fdimport/fdimport.go +++ b/pkg/sentry/fdimport/fdimport.go @@ -20,9 +20,7 @@ import ( "gvisor.dev/gvisor/pkg/context" "gvisor.dev/gvisor/pkg/fd" - "gvisor.dev/gvisor/pkg/sentry/fs" - "gvisor.dev/gvisor/pkg/sentry/fs/host" - hostvfs2 "gvisor.dev/gvisor/pkg/sentry/fsimpl/host" + "gvisor.dev/gvisor/pkg/sentry/fsimpl/host" "gvisor.dev/gvisor/pkg/sentry/kernel" "gvisor.dev/gvisor/pkg/sentry/kernel/auth" "gvisor.dev/gvisor/pkg/sentry/vfs" @@ -32,65 +30,7 @@ import ( // sets up TTY for sentry stdin, stdout, and stderr FDs. Used FDs are either // closed or released. It's safe for the caller to close any remaining files // upon return. -func Import(ctx context.Context, fdTable *kernel.FDTable, console bool, uid auth.KUID, gid auth.KGID, fds map[int]*fd.FD) (*host.TTYFileOperations, *hostvfs2.TTYFileDescription, error) { - if kernel.VFS2Enabled { - ttyFile, err := importVFS2(ctx, fdTable, console, uid, gid, fds) - return nil, ttyFile, err - } - ttyFile, err := importFS(ctx, fdTable, console, fds) - return ttyFile, nil, err -} - -func importFS(ctx context.Context, fdTable *kernel.FDTable, console bool, fds map[int]*fd.FD) (*host.TTYFileOperations, error) { - var ttyFile *fs.File - for appFD, hostFD := range fds { - var appFile *fs.File - - if console && appFD < 3 { - // Import the file as a host TTY file. - if ttyFile == nil { - var err error - appFile, err = host.ImportFile(ctx, hostFD.FD(), true /* isTTY */) - if err != nil { - return nil, err - } - defer appFile.DecRef(ctx) - _ = hostFD.Close() // FD is dup'd i ImportFile. - - // Remember this in the TTY file, as we will - // use it for the other stdio FDs. - ttyFile = appFile - } else { - // Re-use the existing TTY file, as all three - // stdio FDs must point to the same fs.File in - // order to share TTY state, specifically the - // foreground process group id. - appFile = ttyFile - } - } else { - // Import the file as a regular host file. - var err error - appFile, err = host.ImportFile(ctx, hostFD.FD(), false /* isTTY */) - if err != nil { - return nil, err - } - defer appFile.DecRef(ctx) - _ = hostFD.Close() // FD is dup'd i ImportFile. - } - - // Add the file to the FD map. - if err := fdTable.NewFDAt(ctx, int32(appFD), appFile, kernel.FDFlags{}); err != nil { - return nil, err - } - } - - if ttyFile == nil { - return nil, nil - } - return ttyFile.FileOperations.(*host.TTYFileOperations), nil -} - -func importVFS2(ctx context.Context, fdTable *kernel.FDTable, console bool, uid auth.KUID, gid auth.KGID, stdioFDs map[int]*fd.FD) (*hostvfs2.TTYFileDescription, error) { +func Import(ctx context.Context, fdTable *kernel.FDTable, console bool, uid auth.KUID, gid auth.KGID, stdioFDs map[int]*fd.FD) (*host.TTYFileDescription, error) { k := kernel.KernelFromContext(ctx) if k == nil { return nil, fmt.Errorf("cannot find kernel from context") @@ -104,7 +44,7 @@ func importVFS2(ctx context.Context, fdTable *kernel.FDTable, console bool, uid // Import the file as a host TTY file. if ttyFile == nil { var err error - appFile, err = hostvfs2.NewFD(ctx, k.HostMount(), hostFD.FD(), &hostvfs2.NewFDOptions{ + appFile, err = host.NewFD(ctx, k.HostMount(), hostFD.FD(), &host.NewFDOptions{ Savable: true, IsTTY: true, VirtualOwner: true, @@ -128,7 +68,7 @@ func importVFS2(ctx context.Context, fdTable *kernel.FDTable, console bool, uid } } else { var err error - appFile, err = hostvfs2.NewFD(ctx, k.HostMount(), hostFD.FD(), &hostvfs2.NewFDOptions{ + appFile, err = host.NewFD(ctx, k.HostMount(), hostFD.FD(), &host.NewFDOptions{ Savable: true, VirtualOwner: true, UID: uid, @@ -149,5 +89,5 @@ func importVFS2(ctx context.Context, fdTable *kernel.FDTable, console bool, uid if ttyFile == nil { return nil, nil } - return ttyFile.Impl().(*hostvfs2.TTYFileDescription), nil + return ttyFile.Impl().(*host.TTYFileDescription), nil } diff --git a/pkg/sentry/fs/user/BUILD b/pkg/sentry/fs/user/BUILD index 23b5508fd..f446f183c 100644 --- a/pkg/sentry/fs/user/BUILD +++ b/pkg/sentry/fs/user/BUILD @@ -15,7 +15,6 @@ go_library( "//pkg/errors/linuxerr", "//pkg/fspath", "//pkg/log", - "//pkg/sentry/fs", "//pkg/sentry/kernel", "//pkg/sentry/kernel/auth", "//pkg/sentry/vfs", @@ -31,10 +30,11 @@ go_test( deps = [ "//pkg/abi/linux", "//pkg/context", - "//pkg/sentry/fs", - "//pkg/sentry/fs/tmpfs", + "//pkg/fspath", + "//pkg/sentry/fsimpl/tmpfs", "//pkg/sentry/kernel/auth", "//pkg/sentry/kernel/contexttest", + "//pkg/sentry/vfs", "//pkg/usermem", ], ) diff --git a/pkg/sentry/fs/user/path.go b/pkg/sentry/fs/user/path.go index 370c0d54a..0a240ee64 100644 --- a/pkg/sentry/fs/user/path.go +++ b/pkg/sentry/fs/user/path.go @@ -24,7 +24,6 @@ import ( "gvisor.dev/gvisor/pkg/errors/linuxerr" "gvisor.dev/gvisor/pkg/fspath" "gvisor.dev/gvisor/pkg/log" - "gvisor.dev/gvisor/pkg/sentry/fs" "gvisor.dev/gvisor/pkg/sentry/kernel" "gvisor.dev/gvisor/pkg/sentry/kernel/auth" "gvisor.dev/gvisor/pkg/sentry/vfs" @@ -66,65 +65,14 @@ func ResolveExecutablePath(ctx context.Context, args *kernel.CreateProcessArgs) // Otherwise, We must lookup the name in the paths. paths := getPath(args.Envv) - if kernel.VFS2Enabled { - f, err := resolveVFS2(ctx, args.Credentials, args.MountNamespaceVFS2, paths, name) - if err != nil { - return "", &ExecutableResolveError{fmt.Errorf("error finding executable %q in PATH %v: %v", name, paths, err)} - } - return f, nil - } - - f, err := resolve(ctx, args.MountNamespace, paths, name) + f, err := resolve(ctx, args.Credentials, args.MountNamespace, paths, name) if err != nil { return "", &ExecutableResolveError{fmt.Errorf("error finding executable %q in PATH %v: %v", name, paths, err)} } return f, nil } -func resolve(ctx context.Context, mns *fs.MountNamespace, paths []string, name string) (string, error) { - root := fs.RootFromContext(ctx) - if root == nil { - // Caller has no root. Don't bother traversing anything. - return "", linuxerr.ENOENT - } - defer root.DecRef(ctx) - for _, p := range paths { - if !path.IsAbs(p) { - // Relative paths aren't safe, no one should be using them. - log.Warningf("Skipping relative path %q in $PATH", p) - continue - } - - binPath := path.Join(p, name) - traversals := uint(linux.MaxSymlinkTraversals) - d, err := mns.FindInode(ctx, root, nil, binPath, &traversals) - if linuxerr.Equals(linuxerr.ENOENT, err) || linuxerr.Equals(linuxerr.EACCES, err) { - // Didn't find it here. - continue - } - if err != nil { - return "", err - } - defer d.DecRef(ctx) - - // Check that it is a regular file. - if !fs.IsRegular(d.Inode.StableAttr) { - continue - } - - // Check whether we can read and execute the found file. - if err := d.Inode.CheckPermission(ctx, fs.PermMask{Read: true, Execute: true}); err != nil { - log.Infof("Found executable at %q, but user cannot execute it: %v", binPath, err) - continue - } - return path.Join("/", p, name), nil - } - - // Couldn't find it. - return "", linuxerr.ENOENT -} - -func resolveVFS2(ctx context.Context, creds *auth.Credentials, mns *vfs.MountNamespace, paths []string, name string) (string, error) { +func resolve(ctx context.Context, creds *auth.Credentials, mns *vfs.MountNamespace, paths []string, name string) (string, error) { root := mns.Root() root.IncRef() defer root.DecRef(ctx) diff --git a/pkg/sentry/fs/user/user.go b/pkg/sentry/fs/user/user.go index 9847c5b82..372bc581d 100644 --- a/pkg/sentry/fs/user/user.go +++ b/pkg/sentry/fs/user/user.go @@ -26,82 +26,22 @@ import ( "gvisor.dev/gvisor/pkg/abi/linux" "gvisor.dev/gvisor/pkg/context" "gvisor.dev/gvisor/pkg/fspath" - "gvisor.dev/gvisor/pkg/sentry/fs" "gvisor.dev/gvisor/pkg/sentry/kernel/auth" "gvisor.dev/gvisor/pkg/sentry/vfs" "gvisor.dev/gvisor/pkg/usermem" ) type fileReader struct { - // Ctx is the context for the file reader. - Ctx context.Context - - // File is the file to read from. - File *fs.File -} - -// Read implements io.Reader.Read. -func (r *fileReader) Read(buf []byte) (int, error) { - n, err := r.File.Readv(r.Ctx, usermem.BytesIOSequence(buf)) - return int(n), err -} - -// getExecUserHome returns the home directory of the executing user read from -// /etc/passwd as read from the container filesystem. -func getExecUserHome(ctx context.Context, rootMns *fs.MountNamespace, uid auth.KUID) (string, error) { - // The default user home directory to return if no user matching the user - // if found in the /etc/passwd found in the image. - const defaultHome = "/" - - // Open the /etc/passwd file from the dirent via the root mount namespace. - mnsRoot := rootMns.Root() - maxTraversals := uint(linux.MaxSymlinkTraversals) - dirent, err := rootMns.FindInode(ctx, mnsRoot, nil, "/etc/passwd", &maxTraversals) - if err != nil { - // NOTE: Ignore errors opening the passwd file. If the passwd file - // doesn't exist we will return the default home directory. - return defaultHome, nil - } - defer dirent.DecRef(ctx) - - // Check read permissions on the file. - if err := dirent.Inode.CheckPermission(ctx, fs.PermMask{Read: true}); err != nil { - // NOTE: Ignore permissions errors here and return default root dir. - return defaultHome, nil - } - - // Only open regular files. We don't open other files like named pipes as - // they may block and might present some attack surface to the container. - // Note that runc does not seem to do this kind of checking. - if !fs.IsRegular(dirent.Inode.StableAttr) { - return defaultHome, nil - } - - f, err := dirent.Inode.GetFile(ctx, dirent, fs.FileFlags{Read: true, Directory: false}) - if err != nil { - return "", err - } - defer f.DecRef(ctx) - - r := &fileReader{ - Ctx: ctx, - File: f, - } - - return findHomeInPasswd(uint32(uid), r, defaultHome) -} - -type fileReaderVFS2 struct { ctx context.Context fd *vfs.FileDescription } -func (r *fileReaderVFS2) Read(buf []byte) (int, error) { +func (r *fileReader) Read(buf []byte) (int, error) { n, err := r.fd.Read(r.ctx, usermem.BytesIOSequence(buf), vfs.ReadOptions{}) return int(n), err } -func getExecUserHomeVFS2(ctx context.Context, mns *vfs.MountNamespace, uid auth.KUID) (string, error) { +func getExecUserHome(ctx context.Context, mns *vfs.MountNamespace, uid auth.KUID) (string, error) { const defaultHome = "/" root := mns.Root() @@ -116,17 +56,24 @@ func getExecUserHomeVFS2(ctx context.Context, mns *vfs.MountNamespace, uid auth. Path: fspath.Parse("/etc/passwd"), } + stat, err := root.Mount().Filesystem().VirtualFilesystem().StatAt(ctx, creds, target, &vfs.StatOptions{Mask: linux.STATX_TYPE}) + if err != nil { + return defaultHome, nil + } + if stat.Mask&linux.STATX_TYPE == 0 || stat.Mode&linux.FileTypeMask != linux.ModeRegular { + return defaultHome, nil + } + opts := &vfs.OpenOptions{ Flags: linux.O_RDONLY, } - fd, err := root.Mount().Filesystem().VirtualFilesystem().OpenAt(ctx, creds, target, opts) if err != nil { return defaultHome, nil } defer fd.DecRef(ctx) - r := &fileReaderVFS2{ + r := &fileReader{ ctx: ctx, fd: fd, } @@ -139,33 +86,10 @@ func getExecUserHomeVFS2(ctx context.Context, mns *vfs.MountNamespace, uid auth. return homeDir, nil } -// MaybeAddExecUserHome returns a new slice with the HOME enviroment variable -// set if the slice does not already contain it, otherwise it returns the -// original slice unmodified. -func MaybeAddExecUserHome(ctx context.Context, mns *fs.MountNamespace, uid auth.KUID, envv []string) ([]string, error) { - // Check if the envv already contains HOME. - for _, env := range envv { - if strings.HasPrefix(env, "HOME=") { - // We have it. Return the original slice unmodified. - return envv, nil - } - } - - // Read /etc/passwd for the user's HOME directory and set the HOME - // environment variable as required by POSIX if it is not overridden by - // the user. - homeDir, err := getExecUserHome(ctx, mns, uid) - if err != nil { - return nil, fmt.Errorf("error reading exec user: %v", err) - } - - return append(envv, "HOME="+homeDir), nil -} - -// MaybeAddExecUserHomeVFS2 returns a new slice with the HOME enviroment +// MaybeAddExecUserHome returns a new slice with the HOME environment // variable set if the slice does not already contain it, otherwise it returns // the original slice unmodified. -func MaybeAddExecUserHomeVFS2(ctx context.Context, vmns *vfs.MountNamespace, uid auth.KUID, envv []string) ([]string, error) { +func MaybeAddExecUserHome(ctx context.Context, vmns *vfs.MountNamespace, uid auth.KUID, envv []string) ([]string, error) { // Check if the envv already contains HOME. for _, env := range envv { if strings.HasPrefix(env, "HOME=") { @@ -177,7 +101,7 @@ func MaybeAddExecUserHomeVFS2(ctx context.Context, vmns *vfs.MountNamespace, uid // Read /etc/passwd for the user's HOME directory and set the HOME // environment variable as required by POSIX if it is not overridden by // the user. - homeDir, err := getExecUserHomeVFS2(ctx, vmns, uid) + homeDir, err := getExecUserHome(ctx, vmns, uid) if err != nil { return nil, fmt.Errorf("error reading exec user: %v", err) } diff --git a/pkg/sentry/fs/user/user_test.go b/pkg/sentry/fs/user/user_test.go index 7f8fa8038..30ea44fee 100644 --- a/pkg/sentry/fs/user/user_test.go +++ b/pkg/sentry/fs/user/user_test.go @@ -21,43 +21,50 @@ import ( "gvisor.dev/gvisor/pkg/abi/linux" "gvisor.dev/gvisor/pkg/context" - "gvisor.dev/gvisor/pkg/sentry/fs" - "gvisor.dev/gvisor/pkg/sentry/fs/tmpfs" + "gvisor.dev/gvisor/pkg/fspath" + "gvisor.dev/gvisor/pkg/sentry/fsimpl/tmpfs" "gvisor.dev/gvisor/pkg/sentry/kernel/auth" "gvisor.dev/gvisor/pkg/sentry/kernel/contexttest" + "gvisor.dev/gvisor/pkg/sentry/vfs" "gvisor.dev/gvisor/pkg/usermem" ) // createEtcPasswd creates /etc/passwd with the given contents and mode. If // mode is empty, then no file will be created. If mode is not a regular file // mode, then contents is ignored. -func createEtcPasswd(ctx context.Context, root *fs.Dirent, contents string, mode linux.FileMode) error { - if err := root.CreateDirectory(ctx, root, "etc", fs.FilePermsFromMode(0755)); err != nil { - return err +func createEtcPasswd(ctx context.Context, vfsObj *vfs.VirtualFilesystem, creds *auth.Credentials, root vfs.VirtualDentry, contents string, mode linux.FileMode) error { + pop := vfs.PathOperation{ + Root: root, + Start: root, + Path: fspath.Parse("etc"), } - etc, err := root.Walk(ctx, root, "etc") - if err != nil { - return err + if err := vfsObj.MkdirAt(ctx, creds, &pop, &vfs.MkdirOptions{ + Mode: 0755, + }); err != nil { + return fmt.Errorf("failed to create directory etc: %v", err) + } + + pop = vfs.PathOperation{ + Root: root, + Start: root, + Path: fspath.Parse("etc/passwd"), } - defer etc.DecRef(ctx) switch mode.FileType() { case 0: // Don't create anything. return nil case linux.S_IFREG: - passwd, err := etc.Create(ctx, root, "passwd", fs.FileFlags{Write: true}, fs.FilePermsFromMode(mode)) + fd, err := vfsObj.OpenAt(ctx, creds, &pop, &vfs.OpenOptions{Flags: linux.O_CREAT | linux.O_WRONLY, Mode: mode}) if err != nil { return err } - defer passwd.DecRef(ctx) - if _, err := passwd.Writev(ctx, usermem.BytesIOSequence([]byte(contents))); err != nil { - return err - } - return nil + defer fd.DecRef(ctx) + _, err = fd.Write(ctx, usermem.BytesIOSequence([]byte(contents)), vfs.WriteOptions{}) + return err case linux.S_IFDIR: - return etc.CreateDirectory(ctx, root, "passwd", fs.FilePermsFromMode(mode)) + return vfsObj.MkdirAt(ctx, creds, &pop, &vfs.MkdirOptions{Mode: mode}) case linux.S_IFIFO: - return etc.CreateFifo(ctx, root, "passwd", fs.FilePermsFromMode(mode)) + return vfsObj.MknodAt(ctx, creds, &pop, &vfs.MknodOptions{Mode: mode}) default: return fmt.Errorf("unknown file type %x", mode.FileType()) } @@ -103,22 +110,26 @@ func TestGetExecUserHome(t *testing.T) { for name, tc := range tests { t.Run(name, func(t *testing.T) { ctx := contexttest.Context(t) - msrc := fs.NewPseudoMountSource(ctx) - rootInode, err := tmpfs.NewDir(ctx, nil, fs.RootOwner, fs.FilePermsFromMode(0777), msrc, nil /* parent */) - if err != nil { - t.Fatalf("tmpfs.NewDir failed: %v", err) - } + creds := auth.CredentialsFromContext(ctx) - mns, err := fs.NewMountNamespace(ctx, rootInode) + // Create VFS. + vfsObj := vfs.VirtualFilesystem{} + if err := vfsObj.Init(ctx); err != nil { + t.Fatalf("VFS init: %v", err) + } + vfsObj.MustRegisterFilesystemType("tmpfs", tmpfs.FilesystemType{}, &vfs.RegisterFilesystemTypeOptions{ + AllowUserMount: true, + }) + mns, err := vfsObj.NewMountNamespace(ctx, creds, "", "tmpfs", &vfs.MountOptions{}) if err != nil { - t.Fatalf("NewMountNamespace failed: %v", err) + t.Fatalf("failed to create tmpfs root mount: %v", err) } defer mns.DecRef(ctx) root := mns.Root() + root.IncRef() defer root.DecRef(ctx) - ctx = fs.WithRoot(ctx, root) - if err := createEtcPasswd(ctx, root, tc.passwdContents, tc.passwdMode); err != nil { + if err := createEtcPasswd(ctx, &vfsObj, creds, root, tc.passwdContents, tc.passwdMode); err != nil { t.Fatalf("createEtcPasswd failed: %v", err) } diff --git a/pkg/sentry/fsimpl/testutil/kernel.go b/pkg/sentry/fsimpl/testutil/kernel.go index e0fd2ff39..1644abeea 100644 --- a/pkg/sentry/fsimpl/testutil/kernel.go +++ b/pkg/sentry/fsimpl/testutil/kernel.go @@ -63,7 +63,6 @@ func Boot() (*kernel.Kernel, error) { return nil, fmt.Errorf("creating platform: %v", err) } - kernel.VFS2Enabled = true k := &kernel.Kernel{ Platform: plat, } @@ -142,8 +141,8 @@ func CreateTask(ctx context.Context, name string, tc *kernel.ThreadGroup, mntns UTSNamespace: kernel.UTSNamespaceFromContext(ctx), IPCNamespace: kernel.IPCNamespaceFromContext(ctx), AbstractSocketNamespace: kernel.NewAbstractSocketNamespace(), - MountNamespaceVFS2: mntns, - FSContext: kernel.NewFSContextVFS2(root, cwd, 0022), + MountNamespace: mntns, + FSContext: kernel.NewFSContext(root, cwd, 0022), FDTable: k.NewFDTable(), UserCounters: k.GetUserCounters(creds.RealKUID), } diff --git a/pkg/sentry/kernel/fs_context.go b/pkg/sentry/kernel/fs_context.go index dfde4deee..2606f8db4 100644 --- a/pkg/sentry/kernel/fs_context.go +++ b/pkg/sentry/kernel/fs_context.go @@ -54,21 +54,8 @@ type FSContext struct { umask uint } -// newFSContext returns a new filesystem context. -func newFSContext(root, cwd *fs.Dirent, umask uint) *FSContext { - root.IncRef() - cwd.IncRef() - f := FSContext{ - root: root, - cwd: cwd, - umask: umask, - } - f.InitRefs() - return &f -} - -// NewFSContextVFS2 returns a new filesystem context. -func NewFSContextVFS2(root, cwd vfs.VirtualDentry, umask uint) *FSContext { +// NewFSContext returns a new filesystem context. +func NewFSContext(root, cwd vfs.VirtualDentry, umask uint) *FSContext { root.IncRef() cwd.IncRef() f := FSContext{ @@ -95,17 +82,10 @@ func (f *FSContext) DecRef(ctx context.Context) { f.mu.Lock() defer f.mu.Unlock() - if VFS2Enabled { - f.rootVFS2.DecRef(ctx) - f.rootVFS2 = vfs.VirtualDentry{} - f.cwdVFS2.DecRef(ctx) - f.cwdVFS2 = vfs.VirtualDentry{} - } else { - f.root.DecRef(ctx) - f.root = nil - f.cwd.DecRef(ctx) - f.cwd = nil - } + f.rootVFS2.DecRef(ctx) + f.rootVFS2 = vfs.VirtualDentry{} + f.cwdVFS2.DecRef(ctx) + f.cwdVFS2 = vfs.VirtualDentry{} }) } @@ -116,19 +96,11 @@ func (f *FSContext) Fork() *FSContext { f.mu.Lock() defer f.mu.Unlock() - if VFS2Enabled { - if !f.cwdVFS2.Ok() { - panic("FSContext.Fork() called after destroy") - } - f.cwdVFS2.IncRef() - f.rootVFS2.IncRef() - } else { - if f.cwd == nil { - panic("FSContext.Fork() called after destroy") - } - f.cwd.IncRef() - f.root.IncRef() + if !f.cwdVFS2.Ok() { + panic("FSContext.Fork() called after destroy") } + f.cwdVFS2.IncRef() + f.rootVFS2.IncRef() ctx := &FSContext{ cwd: f.cwd, diff --git a/pkg/sentry/kernel/kernel.go b/pkg/sentry/kernel/kernel.go index 1157dd9fe..0f6defcda 100644 --- a/pkg/sentry/kernel/kernel.go +++ b/pkg/sentry/kernel/kernel.go @@ -49,7 +49,6 @@ import ( "gvisor.dev/gvisor/pkg/refs" "gvisor.dev/gvisor/pkg/sentry/arch" "gvisor.dev/gvisor/pkg/sentry/fs" - oldtimerfd "gvisor.dev/gvisor/pkg/sentry/fs/timerfd" "gvisor.dev/gvisor/pkg/sentry/fsbridge" "gvisor.dev/gvisor/pkg/sentry/fsimpl/pipefs" "gvisor.dev/gvisor/pkg/sentry/fsimpl/sockfs" @@ -79,12 +78,6 @@ import ( "gvisor.dev/gvisor/pkg/tcpip" ) -// VFS2Enabled is set to true when VFS2 is enabled. Added as a global to allow -// easy access everywhere. -// -// TODO(gvisor.dev/issue/1624): Remove when VFS1 is no longer used. -var VFS2Enabled = false - // LISAFSEnabled is set to true when lisafs protocol is enabled. Added as a // global to allow easy access everywhere. // @@ -442,44 +435,42 @@ func (k *Kernel) Init(args InitKernelArgs) error { k.YAMAPtraceScope = atomicbitops.FromInt32(linux.YAMA_SCOPE_RELATIONAL) k.userCountersMap = make(map[auth.KUID]*userCounters) - if VFS2Enabled { - ctx := k.SupervisorContext() - if err := k.vfs.Init(ctx); err != nil { - return fmt.Errorf("failed to initialize VFS: %v", err) - } - - err := k.rootIPCNamespace.InitPosixQueues(ctx, &k.vfs, auth.CredentialsFromContext(ctx)) - if err != nil { - return fmt.Errorf("failed to create mqfs filesystem: %v", err) - } - - pipeFilesystem, err := pipefs.NewFilesystem(&k.vfs) - if err != nil { - return fmt.Errorf("failed to create pipefs filesystem: %v", err) - } - defer pipeFilesystem.DecRef(ctx) - pipeMount := k.vfs.NewDisconnectedMount(pipeFilesystem, nil, &vfs.MountOptions{}) - k.pipeMount = pipeMount - - tmpfsFilesystem, tmpfsRoot, err := tmpfs.NewFilesystem(ctx, &k.vfs, auth.NewRootCredentials(k.rootUserNamespace)) - if err != nil { - return fmt.Errorf("failed to create tmpfs filesystem: %v", err) - } - defer tmpfsFilesystem.DecRef(ctx) - defer tmpfsRoot.DecRef(ctx) - k.shmMount = k.vfs.NewDisconnectedMount(tmpfsFilesystem, tmpfsRoot, &vfs.MountOptions{}) - - socketFilesystem, err := sockfs.NewFilesystem(&k.vfs) - if err != nil { - return fmt.Errorf("failed to create sockfs filesystem: %v", err) - } - defer socketFilesystem.DecRef(ctx) - k.socketMount = k.vfs.NewDisconnectedMount(socketFilesystem, nil, &vfs.MountOptions{}) - - k.socketsVFS2 = make(map[*vfs.FileDescription]*SocketRecord) - - k.cgroupRegistry = newCgroupRegistry() + ctx := k.SupervisorContext() + if err := k.vfs.Init(ctx); err != nil { + return fmt.Errorf("failed to initialize VFS: %v", err) } + + err := k.rootIPCNamespace.InitPosixQueues(ctx, &k.vfs, auth.CredentialsFromContext(ctx)) + if err != nil { + return fmt.Errorf("failed to create mqfs filesystem: %v", err) + } + + pipeFilesystem, err := pipefs.NewFilesystem(&k.vfs) + if err != nil { + return fmt.Errorf("failed to create pipefs filesystem: %v", err) + } + defer pipeFilesystem.DecRef(ctx) + pipeMount := k.vfs.NewDisconnectedMount(pipeFilesystem, nil, &vfs.MountOptions{}) + k.pipeMount = pipeMount + + tmpfsFilesystem, tmpfsRoot, err := tmpfs.NewFilesystem(ctx, &k.vfs, auth.NewRootCredentials(k.rootUserNamespace)) + if err != nil { + return fmt.Errorf("failed to create tmpfs filesystem: %v", err) + } + defer tmpfsFilesystem.DecRef(ctx) + defer tmpfsRoot.DecRef(ctx) + k.shmMount = k.vfs.NewDisconnectedMount(tmpfsFilesystem, tmpfsRoot, &vfs.MountOptions{}) + + socketFilesystem, err := sockfs.NewFilesystem(&k.vfs) + if err != nil { + return fmt.Errorf("failed to create sockfs filesystem: %v", err) + } + defer socketFilesystem.DecRef(ctx) + k.socketMount = k.vfs.NewDisconnectedMount(socketFilesystem, nil, &vfs.MountOptions{}) + + k.socketsVFS2 = make(map[*vfs.FileDescription]*SocketRecord) + + k.cgroupRegistry = newCgroupRegistry() return nil } @@ -501,50 +492,16 @@ func (k *Kernel) SaveTo(ctx context.Context, w wire.Writer) error { k.mf.StartEvictions() k.mf.WaitForEvictions() - if VFS2Enabled { - // Discard unsavable mappings, such as those for host file descriptors. - if err := k.invalidateUnsavableMappings(ctx); err != nil { - return fmt.Errorf("failed to invalidate unsavable mappings: %v", err) - } + // Discard unsavable mappings, such as those for host file descriptors. + if err := k.invalidateUnsavableMappings(ctx); err != nil { + return fmt.Errorf("failed to invalidate unsavable mappings: %v", err) + } - // Prepare filesystems for saving. This must be done after - // invalidateUnsavableMappings(), since dropping memory mappings may - // affect filesystem state (e.g. page cache reference counts). - if err := k.vfs.PrepareSave(ctx); err != nil { - return err - } - } else { - // Flush cached file writes to backing storage. This must come after - // MemoryFile eviction since eviction may cause file writes. - if err := k.flushWritesToFiles(ctx); err != nil { - return err - } - - // Clear the dirent cache before saving because Dirents must be Loaded in a - // particular order (parents before children), and Loading dirents from a cache - // breaks that order. - if err := k.flushMountSourceRefs(ctx); err != nil { - return err - } - - // Ensure that all inode and mount release operations have completed. - fs.AsyncBarrier() - - // Once all fs work has completed (flushed references have all been released), - // reset mount mappings. This allows individual mounts to save how inodes map - // to filesystem resources. Without this, fs.Inodes cannot be restored. - fs.SaveInodeMappings() - - // Discard unsavable mappings, such as those for host file descriptors. - // This must be done after waiting for "asynchronous fs work", which - // includes async I/O that may touch application memory. - // - // TODO(gvisor.dev/issue/1624): This rationale is believed to be - // obsolete since AIO callbacks are now waited-for by Kernel.Pause(), - // but this order is conservatively retained for VFS1. - if err := k.invalidateUnsavableMappings(ctx); err != nil { - return fmt.Errorf("failed to invalidate unsavable mappings: %v", err) - } + // Prepare filesystems for saving. This must be done after + // invalidateUnsavableMappings(), since dropping memory mappings may + // affect filesystem state (e.g. page cache reference counts). + if err := k.vfs.PrepareSave(ctx); err != nil { + return err } // Save the CPUID FeatureSet before the rest of the kernel so we can @@ -746,17 +703,8 @@ func (k *Kernel) LoadFrom(ctx context.Context, r wire.Reader, timeReady chan str net.Resume() } - if VFS2Enabled { - if err := k.vfs.CompleteRestore(ctx, vfsOpts); err != nil { - return err - } - } else { - // Ensure that all pending asynchronous work is complete: - // - namedpipe opening - // - inode file opening - if err := fs.AsyncErrorBarrier(); err != nil { - return err - } + if err := k.vfs.CompleteRestore(ctx, vfsOpts); err != nil { + return err } tcpip.AsyncLoading.Wait() @@ -843,14 +791,7 @@ type CreateProcessArgs struct { // // Anyone setting MountNamespace must donate a reference (i.e. // increment it). - MountNamespace *fs.MountNamespace - - // MountNamespaceVFS2 optionally contains the mount namespace for this - // process. If nil, the init process's mount namespace is used. - // - // Anyone setting MountNamespaceVFS2 must donate a reference (i.e. - // increment it). - MountNamespaceVFS2 *vfs.MountNamespace + MountNamespace *vfs.MountNamespace // ContainerID is the container that the process belongs to. ContainerID string @@ -889,17 +830,11 @@ func (ctx *createProcessContext) Value(key interface{}) interface{} { return ipcns case auth.CtxCredentials: return ctx.args.Credentials - case fs.CtxRoot: - if ctx.args.MountNamespace != nil { - // MountNamespace.Root() will take a reference on the root dirent for us. - return ctx.args.MountNamespace.Root() - } - return nil case vfs.CtxRoot: - if ctx.args.MountNamespaceVFS2 == nil { + if ctx.args.MountNamespace == nil { return nil } - root := ctx.args.MountNamespaceVFS2.Root() + root := ctx.args.MountNamespace.Root() root.IncRef() return root case vfs.CtxMountNamespace: @@ -953,86 +888,47 @@ func (k *Kernel) CreateProcess(args CreateProcessArgs) (*ThreadGroup, ThreadID, log.Infof("EXEC: %v", args.Argv) ctx := args.NewContext(k) - - var ( - opener fsbridge.Lookup - fsContext *FSContext - mntns *fs.MountNamespace - mntnsVFS2 *vfs.MountNamespace - ) - - if VFS2Enabled { - mntnsVFS2 = args.MountNamespaceVFS2 - if mntnsVFS2 == nil { - if k.globalInit == nil { - return nil, 0, fmt.Errorf("mount namespace is nil") - } - // Add a reference to the namespace, which is transferred to the new process. - mntnsVFS2 = k.globalInit.Leader().MountNamespaceVFS2() - mntnsVFS2.IncRef() + mntns := args.MountNamespace + if mntns == nil { + if k.globalInit == nil { + return nil, 0, fmt.Errorf("mount namespace is nil") } - // Get the root directory from the MountNamespace. - root := mntnsVFS2.Root() - root.IncRef() - defer root.DecRef(ctx) - - // Grab the working directory. - wd := root // Default. - if args.WorkingDirectory != "" { - pop := vfs.PathOperation{ - Root: root, - Start: wd, - Path: fspath.Parse(args.WorkingDirectory), - FollowFinalSymlink: true, - } - // NOTE(b/236028361): Do not set CheckSearchable flag to true. - // Application is allowed to start with a working directory that it can - // not access/search. This is consistent with Docker and VFS1. Runc - // explicitly allows for this in 6ce2d63a5db6 ("libct/init_linux: retry - // chdir to fix EPERM"). As described in the commit, runc unintentionally - // allowed this behavior in a couple of releases and applications started - // relying on it. So they decided to allow it for backward compatibility. - var err error - wd, err = k.VFS().GetDentryAt(ctx, args.Credentials, &pop, &vfs.GetDentryOptions{}) - if err != nil { - return nil, 0, fmt.Errorf("failed to find initial working directory %q: %v", args.WorkingDirectory, err) - } - defer wd.DecRef(ctx) - } - opener = fsbridge.NewVFSLookup(mntnsVFS2, root, wd) - fsContext = NewFSContextVFS2(root, wd, args.Umask) - - } else { - mntns = args.MountNamespace - if mntns == nil { - if k.globalInit == nil { - return nil, 0, fmt.Errorf("mount namespace is nil") - } - mntns = k.GlobalInit().Leader().MountNamespace() - mntns.IncRef() - } - // Get the root directory from the MountNamespace. - root := mntns.Root() - // The call to newFSContext below will take a reference on root, so we - // don't need to hold this one. - defer root.DecRef(ctx) - - // Grab the working directory. - remainingTraversals := args.MaxSymlinkTraversals - wd := root // Default. - if args.WorkingDirectory != "" { - var err error - wd, err = mntns.FindInode(ctx, root, nil, args.WorkingDirectory, &remainingTraversals) - if err != nil { - return nil, 0, fmt.Errorf("failed to find initial working directory %q: %v", args.WorkingDirectory, err) - } - defer wd.DecRef(ctx) - } - opener = fsbridge.NewFSLookup(mntns, root, wd) - fsContext = newFSContext(root, wd, args.Umask) + // Add a reference to the namespace, which is transferred to the new process. + mntns = k.globalInit.Leader().MountNamespaceVFS2() + mntns.IncRef() } + // Get the root directory from the MountNamespace. + root := mntns.Root() + root.IncRef() + defer root.DecRef(ctx) - tg := k.NewThreadGroup(mntns, args.PIDNamespace, NewSignalHandlers(), linux.SIGCHLD, args.Limits) + // Grab the working directory. + wd := root // Default. + if args.WorkingDirectory != "" { + pop := vfs.PathOperation{ + Root: root, + Start: wd, + Path: fspath.Parse(args.WorkingDirectory), + FollowFinalSymlink: true, + } + // NOTE(b/236028361): Do not set CheckSearchable flag to true. + // Application is allowed to start with a working directory that it can + // not access/search. This is consistent with Docker and VFS1. Runc + // explicitly allows for this in 6ce2d63a5db6 ("libct/init_linux: retry + // chdir to fix EPERM"). As described in the commit, runc unintentionally + // allowed this behavior in a couple of releases and applications started + // relying on it. So they decided to allow it for backward compatibility. + var err error + wd, err = k.VFS().GetDentryAt(ctx, args.Credentials, &pop, &vfs.GetDentryOptions{}) + if err != nil { + return nil, 0, fmt.Errorf("failed to find initial working directory %q: %v", args.WorkingDirectory, err) + } + defer wd.DecRef(ctx) + } + opener := fsbridge.NewVFSLookup(mntns, root, wd) + fsContext := NewFSContext(root, wd, args.Umask) + + tg := k.NewThreadGroup(nil, args.PIDNamespace, NewSignalHandlers(), linux.SIGCHLD, args.Limits) cu := cleanup.Make(func() { tg.Release(ctx) }) @@ -1094,7 +990,7 @@ func (k *Kernel) CreateProcess(args CreateProcessArgs) (*ThreadGroup, ThreadID, UTSNamespace: args.UTSNamespace, IPCNamespace: args.IPCNamespace, AbstractSocketNamespace: args.AbstractSocketNamespace, - MountNamespaceVFS2: mntnsVFS2, + MountNamespace: mntns, ContainerID: args.ContainerID, UserCounters: k.GetUserCounters(args.Credentials.RealKUID), } @@ -1193,14 +1089,8 @@ func (k *Kernel) pauseTimeLocked(ctx context.Context) { // but ktime.Timer.Pause is idempotent so this is harmless. if t.fdTable != nil { t.fdTable.forEach(ctx, func(_ int32, file *fs.File, fd *vfs.FileDescription, _ FDFlags) { - if VFS2Enabled { - if tfd, ok := fd.Impl().(*timerfd.TimerFileDescription); ok { - tfd.PauseTimer() - } - } else { - if tfd, ok := file.FileOperations.(*oldtimerfd.TimerOperations); ok { - tfd.PauseTimer() - } + if tfd, ok := fd.Impl().(*timerfd.TimerFileDescription); ok { + tfd.PauseTimer() } }) } @@ -1229,14 +1119,8 @@ func (k *Kernel) resumeTimeLocked(ctx context.Context) { } if t.fdTable != nil { t.fdTable.forEach(ctx, func(_ int32, file *fs.File, fd *vfs.FileDescription, _ FDFlags) { - if VFS2Enabled { - if tfd, ok := fd.Impl().(*timerfd.TimerFileDescription); ok { - tfd.ResumeTimer() - } - } else { - if tfd, ok := file.FileOperations.(*oldtimerfd.TimerOperations); ok { - tfd.ResumeTimer() - } + if tfd, ok := fd.Impl().(*timerfd.TimerFileDescription); ok { + tfd.ResumeTimer() } }) } @@ -1678,14 +1562,8 @@ func (k *Kernel) DeleteSocketVFS2(sock *vfs.FileDescription) { func (k *Kernel) ListSockets() []*SocketRecord { k.extMu.Lock() var socks []*SocketRecord - if VFS2Enabled { - for _, s := range k.socketsVFS2 { - socks = append(socks, s) - } - } else { - for s := k.sockets.Front(); s != nil; s = s.Next() { - socks = append(socks, &s.SocketRecord) - } + for _, s := range k.socketsVFS2 { + socks = append(socks, s) } k.extMu.Unlock() return socks @@ -1847,13 +1725,11 @@ func (k *Kernel) CgroupRegistry() *CgroupRegistry { // initialized, e.g. after k.Start() has been called. func (k *Kernel) Release() { ctx := k.SupervisorContext() - if VFS2Enabled { - k.hostMount.DecRef(ctx) - k.pipeMount.DecRef(ctx) - k.shmMount.DecRef(ctx) - k.socketMount.DecRef(ctx) - k.vfs.Release(ctx) - } + k.hostMount.DecRef(ctx) + k.pipeMount.DecRef(ctx) + k.shmMount.DecRef(ctx) + k.socketMount.DecRef(ctx) + k.vfs.Release(ctx) k.timekeeper.Destroy() k.vdso.Release(ctx) k.RootNetworkNamespace().DecRef() diff --git a/pkg/sentry/kernel/ptrace.go b/pkg/sentry/kernel/ptrace.go index 82d0562d9..e603c283a 100644 --- a/pkg/sentry/kernel/ptrace.go +++ b/pkg/sentry/kernel/ptrace.go @@ -123,11 +123,6 @@ func (t *Task) CanTrace(target *Task, attach bool) bool { return false } - // YAMA only supported for vfs2. - if !VFS2Enabled { - return true - } - if t.k.YAMAPtraceScope.Load() == linux.YAMA_SCOPE_RELATIONAL { t.tg.pidns.owner.mu.RLock() defer t.tg.pidns.owner.mu.RUnlock() @@ -149,11 +144,6 @@ func (t *Task) canTraceLocked(target *Task, attach bool) bool { return false } - // YAMA only supported for vfs2. - if !VFS2Enabled { - return true - } - if t.k.YAMAPtraceScope.Load() == linux.YAMA_SCOPE_RELATIONAL { if !t.canTraceYAMALocked(target) { return false diff --git a/pkg/sentry/kernel/syslog.go b/pkg/sentry/kernel/syslog.go index 3fee7aa68..4681a639b 100644 --- a/pkg/sentry/kernel/syslog.go +++ b/pkg/sentry/kernel/syslog.go @@ -104,13 +104,11 @@ func (s *syslog) Log() []byte { s.msg = append(s.msg, []byte(fmt.Sprintf(format, time, selectMessage()))...) } - if VFS2Enabled { + time += rand.Float64() / 2 + s.msg = append(s.msg, []byte(fmt.Sprintf(format, time, "Setting up VFS..."))...) + if FUSEEnabled { time += rand.Float64() / 2 - s.msg = append(s.msg, []byte(fmt.Sprintf(format, time, "Setting up VFS2..."))...) - if FUSEEnabled { - time += rand.Float64() / 2 - s.msg = append(s.msg, []byte(fmt.Sprintf(format, time, "Setting up FUSE..."))...) - } + s.msg = append(s.msg, []byte(fmt.Sprintf(format, time, "Setting up FUSE..."))...) } time += rand.Float64() / 2 diff --git a/pkg/sentry/kernel/task.go b/pkg/sentry/kernel/task.go index 73e46fb65..5ac4106b1 100644 --- a/pkg/sentry/kernel/task.go +++ b/pkg/sentry/kernel/task.go @@ -447,10 +447,10 @@ type Task struct { // abstractSockets is protected by mu. abstractSockets *AbstractSocketNamespace - // mountNamespaceVFS2 is the task's mount namespace. + // mountNamespace is the task's mount namespace. // // It is protected by mu. It is owned by the task goroutine. - mountNamespaceVFS2 *vfs.MountNamespace + mountNamespace *vfs.MountNamespace // parentDeathSignal is sent to this task's thread group when its parent exits. // @@ -705,19 +705,9 @@ func (t *Task) SyscallRestartBlock() SyscallRestartBlock { // Preconditions: The caller must be running on the task goroutine, or t.mu // must be locked. func (t *Task) IsChrooted() bool { - if VFS2Enabled { - realRoot := t.mountNamespaceVFS2.Root() - root := t.fsContext.RootDirectoryVFS2() - defer root.DecRef(t) - return root != realRoot - } - - realRoot := t.tg.mounts.Root() - defer realRoot.DecRef(t) - root := t.fsContext.RootDirectory() - if root != nil { - defer root.DecRef(t) - } + realRoot := t.mountNamespace.Root() + root := t.fsContext.RootDirectoryVFS2() + defer root.DecRef(t) return root != realRoot } @@ -839,7 +829,7 @@ func (t *Task) MountNamespace() *fs.MountNamespace { func (t *Task) MountNamespaceVFS2() *vfs.MountNamespace { t.mu.Lock() defer t.mu.Unlock() - return t.mountNamespaceVFS2 + return t.mountNamespace } // AbstractSockets returns t's AbstractSocketNamespace. diff --git a/pkg/sentry/kernel/task_clone.go b/pkg/sentry/kernel/task_clone.go index 888ed0dde..a3eccdc5c 100644 --- a/pkg/sentry/kernel/task_clone.go +++ b/pkg/sentry/kernel/task_clone.go @@ -103,9 +103,7 @@ func (t *Task) Clone(args *linux.CloneArgs) (ThreadID, *SyscallControl, error) { ipcns := t.IPCNamespace() if args.Flags&linux.CLONE_NEWIPC != 0 { ipcns = NewIPCNamespace(userns) - if VFS2Enabled { - ipcns.InitPosixQueues(t, t.k.VFS(), creds) - } + ipcns.InitPosixQueues(t, t.k.VFS(), creds) } else { ipcns.IncRef() } @@ -125,11 +123,11 @@ func (t *Task) Clone(args *linux.CloneArgs) (ThreadID, *SyscallControl, error) { }) // TODO(b/63601033): Implement CLONE_NEWNS. - mntnsVFS2 := t.mountNamespaceVFS2 - if mntnsVFS2 != nil { - mntnsVFS2.IncRef() + mntns := t.mountNamespace + if mntns != nil { + mntns.IncRef() cu.Add(func() { - mntnsVFS2.DecRef(t) + mntns.DecRef(t) }) } @@ -210,7 +208,7 @@ func (t *Task) Clone(args *linux.CloneArgs) (ThreadID, *SyscallControl, error) { UTSNamespace: utsns, IPCNamespace: ipcns, AbstractSocketNamespace: t.abstractSockets, - MountNamespaceVFS2: mntnsVFS2, + MountNamespace: mntns, RSeqAddr: rseqAddr, RSeqSignature: rseqSignature, ContainerID: t.ContainerID(), @@ -487,9 +485,7 @@ func (t *Task) Unshare(flags int32) error { // namespace" oldIPCNS = t.ipcns t.ipcns = NewIPCNamespace(creds.UserNamespace) - if VFS2Enabled { - t.ipcns.InitPosixQueues(t, t.k.VFS(), creds) - } + t.ipcns.InitPosixQueues(t, t.k.VFS(), creds) } var oldFDTable *FDTable if flags&linux.CLONE_FILES != 0 { diff --git a/pkg/sentry/kernel/task_context.go b/pkg/sentry/kernel/task_context.go index aa25a8d39..54d145921 100644 --- a/pkg/sentry/kernel/task_context.go +++ b/pkg/sentry/kernel/task_context.go @@ -105,8 +105,8 @@ func (t *Task) contextValue(key interface{}, isTaskGoroutine bool) interface{} { t.mu.Lock() defer t.mu.Unlock() } - t.mountNamespaceVFS2.IncRef() - return t.mountNamespaceVFS2 + t.mountNamespace.IncRef() + return t.mountNamespace case fs.CtxDirentCacheLimiter: return t.k.DirentCacheLimiter case inet.CtxStack: diff --git a/pkg/sentry/kernel/task_exit.go b/pkg/sentry/kernel/task_exit.go index bd2276e6f..97d8751f7 100644 --- a/pkg/sentry/kernel/task_exit.go +++ b/pkg/sentry/kernel/task_exit.go @@ -285,8 +285,8 @@ func (*runExitMain) execute(t *Task) taskRunState { t.LeaveCgroups() t.mu.Lock() - mntns := t.mountNamespaceVFS2 - t.mountNamespaceVFS2 = nil + mntns := t.mountNamespace + t.mountNamespace = nil ipcns := t.ipcns netns := t.NetworkNamespace() t.mu.Unlock() diff --git a/pkg/sentry/kernel/task_start.go b/pkg/sentry/kernel/task_start.go index 16a6bc6e0..99c6911c0 100644 --- a/pkg/sentry/kernel/task_start.go +++ b/pkg/sentry/kernel/task_start.go @@ -85,8 +85,8 @@ type TaskConfig struct { // AbstractSocketNamespace is the AbstractSocketNamespace of the new task. AbstractSocketNamespace *AbstractSocketNamespace - // MountNamespaceVFS2 is the MountNamespace of the new task. - MountNamespaceVFS2 *vfs.MountNamespace + // MountNamespace is the MountNamespace of the new task. + MountNamespace *vfs.MountNamespace // RSeqAddr is a pointer to the the userspace linux.RSeq structure. RSeqAddr hostarch.Addr @@ -116,8 +116,8 @@ func (ts *TaskSet) NewTask(ctx context.Context, cfg *TaskConfig) (*Task, error) cfg.FDTable.DecRef(ctx) cfg.IPCNamespace.DecRef(ctx) cfg.NetworkNamespace.DecRef() - if cfg.MountNamespaceVFS2 != nil { - cfg.MountNamespaceVFS2.DecRef(ctx) + if cfg.MountNamespace != nil { + cfg.MountNamespace.DecRef(ctx) } } if err := cfg.UserCounters.incRLimitNProc(ctx); err != nil { @@ -145,29 +145,29 @@ func (ts *TaskSet) newTask(ctx context.Context, cfg *TaskConfig) (*Task, error) parent: cfg.Parent, children: make(map[*Task]struct{}), }, - runState: (*runApp)(nil), - interruptChan: make(chan struct{}, 1), - signalMask: atomicbitops.FromUint64(uint64(cfg.SignalMask)), - signalStack: linux.SignalStack{Flags: linux.SS_DISABLE}, - image: *image, - fsContext: cfg.FSContext, - fdTable: cfg.FDTable, - k: cfg.Kernel, - ptraceTracees: make(map[*Task]struct{}), - allowedCPUMask: cfg.AllowedCPUMask.Copy(), - ioUsage: &usage.IO{}, - niceness: cfg.Niceness, - utsns: cfg.UTSNamespace, - ipcns: cfg.IPCNamespace, - abstractSockets: cfg.AbstractSocketNamespace, - mountNamespaceVFS2: cfg.MountNamespaceVFS2, - rseqCPU: -1, - rseqAddr: cfg.RSeqAddr, - rseqSignature: cfg.RSeqSignature, - futexWaiter: futex.NewWaiter(), - containerID: cfg.ContainerID, - cgroups: make(map[Cgroup]struct{}), - userCounters: cfg.UserCounters, + runState: (*runApp)(nil), + interruptChan: make(chan struct{}, 1), + signalMask: atomicbitops.FromUint64(uint64(cfg.SignalMask)), + signalStack: linux.SignalStack{Flags: linux.SS_DISABLE}, + image: *image, + fsContext: cfg.FSContext, + fdTable: cfg.FDTable, + k: cfg.Kernel, + ptraceTracees: make(map[*Task]struct{}), + allowedCPUMask: cfg.AllowedCPUMask.Copy(), + ioUsage: &usage.IO{}, + niceness: cfg.Niceness, + utsns: cfg.UTSNamespace, + ipcns: cfg.IPCNamespace, + abstractSockets: cfg.AbstractSocketNamespace, + mountNamespace: cfg.MountNamespace, + rseqCPU: -1, + rseqAddr: cfg.RSeqAddr, + rseqSignature: cfg.RSeqSignature, + futexWaiter: futex.NewWaiter(), + containerID: cfg.ContainerID, + cgroups: make(map[Cgroup]struct{}), + userCounters: cfg.UserCounters, } t.netns.Store(cfg.NetworkNamespace) t.creds.Store(cfg.Credentials) @@ -238,10 +238,8 @@ func (ts *TaskSet) newTask(ctx context.Context, cfg *TaskConfig) (*Task, error) t.parent.children[t] = struct{}{} } - if VFS2Enabled { - // srcT may be nil, in which case we default to root cgroups. - t.EnterInitialCgroups(srcT) - } + // srcT may be nil, in which case we default to root cgroups. + t.EnterInitialCgroups(srcT) if tg.leader == nil { // New thread group. diff --git a/pkg/sentry/socket/control/control.go b/pkg/sentry/socket/control/control.go index fa79f9c94..c98947da0 100644 --- a/pkg/sentry/socket/control/control.go +++ b/pkg/sentry/socket/control/control.go @@ -712,19 +712,11 @@ func Parse(t *kernel.Task, socketOrEndpoint interface{}, buf []byte, width uint) } if len(fds) > 0 { - if kernel.VFS2Enabled { - rights, err := NewSCMRightsVFS2(t, fds) - if err != nil { - return socket.ControlMessages{}, err - } - cmsgs.Unix.Rights = rights - } else { - rights, err := NewSCMRights(t, fds) - if err != nil { - return socket.ControlMessages{}, err - } - cmsgs.Unix.Rights = rights + rights, err := NewSCMRightsVFS2(t, fds) + if err != nil { + return socket.ControlMessages{}, err } + cmsgs.Unix.Rights = rights } return cmsgs, nil diff --git a/pkg/sentry/socket/hostinet/socket.go b/pkg/sentry/socket/hostinet/socket.go index ba8a4ce3e..196cbec72 100644 --- a/pkg/sentry/socket/hostinet/socket.go +++ b/pkg/sentry/socket/hostinet/socket.go @@ -319,31 +319,17 @@ func (s *socketOpsCommon) Accept(t *kernel.Task, peerRequested bool, flags int, kfd int32 kerr error ) - if kernel.VFS2Enabled { - f, err := newVFS2Socket(t, s.family, s.stype, s.protocol, fd, uint32(flags&unix.SOCK_NONBLOCK)) - if err != nil { - _ = unix.Close(fd) - return 0, nil, 0, err - } - defer f.DecRef(t) - - kfd, kerr = t.NewFDFromVFS2(0, f, kernel.FDFlags{ - CloseOnExec: flags&unix.SOCK_CLOEXEC != 0, - }) - t.Kernel().RecordSocketVFS2(f) - } else { - f, err := newSocketFile(t, s.family, s.stype, s.protocol, fd, flags&unix.SOCK_NONBLOCK != 0) - if err != nil { - _ = unix.Close(fd) - return 0, nil, 0, err - } - defer f.DecRef(t) - - kfd, kerr = t.NewFDFrom(0, f, kernel.FDFlags{ - CloseOnExec: flags&unix.SOCK_CLOEXEC != 0, - }) - t.Kernel().RecordSocket(f) + f, err := newVFS2Socket(t, s.family, s.stype, s.protocol, fd, uint32(flags&unix.SOCK_NONBLOCK)) + if err != nil { + _ = unix.Close(fd) + return 0, nil, 0, err } + defer f.DecRef(t) + + kfd, kerr = t.NewFDFromVFS2(0, f, kernel.FDFlags{ + CloseOnExec: flags&unix.SOCK_CLOEXEC != 0, + }) + t.Kernel().RecordSocketVFS2(f) return kfd, peerAddr, peerAddrlen, syserr.FromError(kerr) } diff --git a/pkg/sentry/socket/unix/unix.go b/pkg/sentry/socket/unix/unix.go index aaccc8ac7..7c5ce6bb9 100644 --- a/pkg/sentry/socket/unix/unix.go +++ b/pkg/sentry/socket/unix/unix.go @@ -385,49 +385,27 @@ func extractEndpoint(t *kernel.Task, sockaddr []byte) (transport.BoundEndpoint, return ep, nil } - if kernel.VFS2Enabled { - p := fspath.Parse(path) - root := t.FSContext().RootDirectoryVFS2() - start := root - relPath := !p.Absolute - if relPath { - start = t.FSContext().WorkingDirectoryVFS2() - } - pop := vfs.PathOperation{ - Root: root, - Start: start, - Path: p, - FollowFinalSymlink: true, - } - ep, e := t.Kernel().VFS().BoundEndpointAt(t, t.Credentials(), &pop, &vfs.BoundEndpointOptions{path}) - root.DecRef(t) - if relPath { - start.DecRef(t) - } - if e != nil { - return nil, syserr.FromError(e) - } - return ep, nil + p := fspath.Parse(path) + root := t.FSContext().RootDirectoryVFS2() + start := root + relPath := !p.Absolute + if relPath { + start = t.FSContext().WorkingDirectoryVFS2() } - - // Find the node in the filesystem. - root := t.FSContext().RootDirectory() - cwd := t.FSContext().WorkingDirectory() - remainingTraversals := uint(fs.DefaultTraversalLimit) - d, e := t.MountNamespace().FindInode(t, root, cwd, path, &remainingTraversals) - cwd.DecRef(t) + pop := vfs.PathOperation{ + Root: root, + Start: start, + Path: p, + FollowFinalSymlink: true, + } + ep, e := t.Kernel().VFS().BoundEndpointAt(t, t.Credentials(), &pop, &vfs.BoundEndpointOptions{path}) root.DecRef(t) + if relPath { + start.DecRef(t) + } if e != nil { return nil, syserr.FromError(e) } - - // Extract the endpoint if one is there. - ep := d.Inode.BoundEndpoint(path) - d.DecRef(t) - if ep == nil { - // No socket! - return nil, syserr.ErrConnectionRefused - } return ep, nil } diff --git a/pkg/sentry/strace/strace.go b/pkg/sentry/strace/strace.go index 3b8db436f..b5c6610f3 100644 --- a/pkg/sentry/strace/strace.go +++ b/pkg/sentry/strace/strace.go @@ -151,39 +151,6 @@ func path(t *kernel.Task, addr hostarch.Addr) string { } func fd(t *kernel.Task, fd int32) string { - if kernel.VFS2Enabled { - return fdVFS2(t, fd) - } - - root := t.FSContext().RootDirectory() - if root != nil { - defer root.DecRef(t) - } - - if fd == linux.AT_FDCWD { - wd := t.FSContext().WorkingDirectory() - var name string - if wd != nil { - defer wd.DecRef(t) - name, _ = wd.FullName(root) - } else { - name = "(unknown cwd)" - } - return fmt.Sprintf("AT_FDCWD %s", name) - } - - file := t.GetFile(fd) - if file == nil { - // Cast FD to uint64 to avoid printing negative hex. - return fmt.Sprintf("%#x (bad FD)", uint64(fd)) - } - defer file.DecRef(t) - - name, _ := file.Dirent.FullName(root) - return fmt.Sprintf("%#x %s", fd, name) -} - -func fdVFS2(t *kernel.Task, fd int32) string { root := t.FSContext().RootDirectoryVFS2() defer root.DecRef(t) diff --git a/runsc/boot/loader.go b/runsc/boot/loader.go index de75c4f8a..6392c52e4 100644 --- a/runsc/boot/loader.go +++ b/runsc/boot/loader.go @@ -238,7 +238,6 @@ func New(args Args) (*Loader, error) { return nil, fmt.Errorf("setting up memory usage: %w", err) } - kernel.VFS2Enabled = true kernel.FUSEEnabled = args.Conf.FUSE kernel.LISAFSEnabled = args.Conf.Lisafs bufferv2.PoolingEnabled = args.Conf.BufferPooling @@ -823,7 +822,7 @@ func (l *Loader) createContainerProcess(root bool, cid string, info *containerIn } // Add the HOME environment variable if it is not already set. - info.procArgs.Envv, err = user.MaybeAddExecUserHomeVFS2(ctx, info.procArgs.MountNamespaceVFS2, + info.procArgs.Envv, err = user.MaybeAddExecUserHome(ctx, info.procArgs.MountNamespace, info.procArgs.Credentials.RealKUID, info.procArgs.Envv) if err != nil { return nil, nil, err @@ -966,8 +965,8 @@ 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. // 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() { + args.MountNamespace = tg.Leader().MountNamespaceVFS2() + if args.MountNamespace == nil || !args.MountNamespace.TryIncRef() { return 0, fmt.Errorf("container %q has stopped", args.ContainerID) } @@ -977,9 +976,9 @@ func (l *Loader) executeAsync(args *control.ExecArgs) (kernel.ThreadID, error) { } // Add the HOME environment variable if it is not already set. - 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) + ctx := vfs.WithRoot(l.k.SupervisorContext(), args.MountNamespace.Root()) + defer args.MountNamespace.DecRef(ctx) + args.Envv, err = user.MaybeAddExecUserHome(ctx, args.MountNamespace, args.KUID, args.Envv) if err != nil { return 0, err } @@ -992,7 +991,7 @@ func (l *Loader) executeAsync(args *control.ExecArgs) (kernel.ThreadID, error) { // Start the process. proc := control.Proc{Kernel: l.k} - newTG, tgid, _, ttyFile, err := control.ExecAsync(&proc, args) + newTG, tgid, ttyFile, err := control.ExecAsync(&proc, args) if err != nil { return 0, err } @@ -1370,7 +1369,7 @@ func createFDTable(ctx context.Context, console bool, stdioFDs []*fd.FD, user sp k := kernel.KernelFromContext(ctx) fdTable := k.NewFDTable() - _, ttyFile, err := fdimport.Import(ctx, fdTable, console, auth.KUID(user.UID), auth.KGID(user.GID), fdMap) + ttyFile, err := fdimport.Import(ctx, fdTable, console, auth.KUID(user.UID), auth.KGID(user.GID), fdMap) if err != nil { fdTable.DecRef(ctx) return nil, nil, err diff --git a/runsc/boot/vfs.go b/runsc/boot/vfs.go index 5c78a94b0..4da525e83 100644 --- a/runsc/boot/vfs.go +++ b/runsc/boot/vfs.go @@ -162,7 +162,7 @@ func setupContainerVFS(ctx context.Context, conf *config.Config, mntr *container if err != nil { return fmt.Errorf("failed to setupFS: %w", err) } - procArgs.MountNamespaceVFS2 = mns + procArgs.MountNamespace = mns // Resolve the executable path from working dir and environment. resolved, err := user.ResolveExecutablePath(ctx, procArgs) @@ -373,7 +373,7 @@ func (c *containerMounter) mountAll(conf *config.Config, procArgs *kernel.Create if err != nil { return nil, fmt.Errorf("creating mount namespace: %w", err) } - rootProcArgs.MountNamespaceVFS2 = mns + rootProcArgs.MountNamespace = mns root := mns.Root() root.IncRef()