diff --git a/pkg/sentry/fsbridge/BUILD b/pkg/sentry/fsbridge/BUILD deleted file mode 100644 index e3526d3bc..000000000 --- a/pkg/sentry/fsbridge/BUILD +++ /dev/null @@ -1,21 +0,0 @@ -load("//tools:defs.bzl", "go_library") - -licenses(["notice"]) - -go_library( - name = "fsbridge", - srcs = [ - "bridge.go", - "vfs.go", - ], - visibility = ["//pkg/sentry:internal"], - deps = [ - "//pkg/abi/linux", - "//pkg/context", - "//pkg/fspath", - "//pkg/sentry/kernel/auth", - "//pkg/sentry/memmap", - "//pkg/sentry/vfs", - "//pkg/usermem", - ], -) diff --git a/pkg/sentry/fsbridge/bridge.go b/pkg/sentry/fsbridge/bridge.go deleted file mode 100644 index 83610ddee..000000000 --- a/pkg/sentry/fsbridge/bridge.go +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright 2020 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Package fsbridge provides common interfaces to bridge between VFS1 and VFS2 -// files. -// TODO(gvisor.dev/issue/1624): Delete this package. -package fsbridge - -import ( - "gvisor.dev/gvisor/pkg/abi/linux" - "gvisor.dev/gvisor/pkg/context" - "gvisor.dev/gvisor/pkg/sentry/memmap" - "gvisor.dev/gvisor/pkg/sentry/vfs" - "gvisor.dev/gvisor/pkg/usermem" -) - -// File provides a common interface to bridge between VFS1 and VFS2 files. -type File interface { - // PathnameWithDeleted returns an absolute pathname to vd, consistent with - // Linux's d_path(). In particular, if vd.Dentry() has been disowned, - // PathnameWithDeleted appends " (deleted)" to the returned pathname. - PathnameWithDeleted(ctx context.Context) string - - // ReadFull read all contents from the file. - ReadFull(ctx context.Context, dst usermem.IOSequence, offset int64) (int64, error) - - // ConfigureMMap mutates opts to implement mmap(2) for the file. - ConfigureMMap(context.Context, *memmap.MMapOpts) error - - // Type returns the file type, e.g. linux.S_IFREG. - Type(context.Context) (linux.FileMode, error) - - // IncRef increments reference. - IncRef() - - // DecRef decrements reference. - DecRef(ctx context.Context) -} - -// Lookup provides a common interface to open files. -type Lookup interface { - // OpenPath opens a file. - OpenPath(ctx context.Context, path string, opts vfs.OpenOptions, remainingTraversals *uint, resolveFinal bool) (File, error) -} diff --git a/pkg/sentry/fsbridge/vfs.go b/pkg/sentry/fsbridge/vfs.go deleted file mode 100644 index a7837f402..000000000 --- a/pkg/sentry/fsbridge/vfs.go +++ /dev/null @@ -1,142 +0,0 @@ -// Copyright 2020 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package fsbridge - -import ( - "io" - - "gvisor.dev/gvisor/pkg/abi/linux" - "gvisor.dev/gvisor/pkg/context" - "gvisor.dev/gvisor/pkg/fspath" - "gvisor.dev/gvisor/pkg/sentry/kernel/auth" - "gvisor.dev/gvisor/pkg/sentry/memmap" - "gvisor.dev/gvisor/pkg/sentry/vfs" - "gvisor.dev/gvisor/pkg/usermem" -) - -// VFSFile implements File interface over vfs.FileDescription. -// -// +stateify savable -type VFSFile struct { - file *vfs.FileDescription -} - -var _ File = (*VFSFile)(nil) - -// NewVFSFile creates a new File over fs.File. -func NewVFSFile(file *vfs.FileDescription) File { - return &VFSFile{file: file} -} - -// PathnameWithDeleted implements File. -func (f *VFSFile) PathnameWithDeleted(ctx context.Context) string { - root := vfs.RootFromContext(ctx) - defer root.DecRef(ctx) - - vfsObj := f.file.VirtualDentry().Mount().Filesystem().VirtualFilesystem() - name, _ := vfsObj.PathnameWithDeleted(ctx, root, f.file.VirtualDentry()) - return name -} - -// ReadFull implements File. -func (f *VFSFile) ReadFull(ctx context.Context, dst usermem.IOSequence, offset int64) (int64, error) { - var total int64 - for dst.NumBytes() > 0 { - n, err := f.file.PRead(ctx, dst, offset+total, vfs.ReadOptions{}) - total += n - if err == io.EOF && total != 0 { - return total, io.ErrUnexpectedEOF - } else if err != nil { - return total, err - } - dst = dst.DropFirst64(n) - } - return total, nil -} - -// ConfigureMMap implements File. -func (f *VFSFile) ConfigureMMap(ctx context.Context, opts *memmap.MMapOpts) error { - return f.file.ConfigureMMap(ctx, opts) -} - -// Type implements File. -func (f *VFSFile) Type(ctx context.Context) (linux.FileMode, error) { - stat, err := f.file.Stat(ctx, vfs.StatOptions{}) - if err != nil { - return 0, err - } - return linux.FileMode(stat.Mode).FileType(), nil -} - -// IncRef implements File. -func (f *VFSFile) IncRef() { - f.file.IncRef() -} - -// DecRef implements File. -func (f *VFSFile) DecRef(ctx context.Context) { - f.file.DecRef(ctx) -} - -// FileDescription returns the FileDescription represented by f. It does not -// take an additional reference on the returned FileDescription. -func (f *VFSFile) FileDescription() *vfs.FileDescription { - return f.file -} - -// fsLookup implements Lookup interface using fs.File. -// -// +stateify savable -type vfsLookup struct { - mntns *vfs.MountNamespace - - root vfs.VirtualDentry - workingDir vfs.VirtualDentry -} - -var _ Lookup = (*vfsLookup)(nil) - -// NewVFSLookup creates a new Lookup. -func NewVFSLookup(mntns *vfs.MountNamespace, root, workingDir vfs.VirtualDentry) Lookup { - return &vfsLookup{ - mntns: mntns, - root: root, - workingDir: workingDir, - } -} - -// OpenPath implements Lookup. -// -// remainingTraversals is not configurable, all callers are using the -// default anyways. -func (l *vfsLookup) OpenPath(ctx context.Context, pathname string, opts vfs.OpenOptions, _ *uint, resolveFinal bool) (File, error) { - vfsObj := l.root.Mount().Filesystem().VirtualFilesystem() - creds := auth.CredentialsFromContext(ctx) - path := fspath.Parse(pathname) - pop := &vfs.PathOperation{ - Root: l.root, - Start: l.workingDir, - Path: path, - FollowFinalSymlink: resolveFinal, - } - if path.Absolute { - pop.Start = l.root - } - fd, err := vfsObj.OpenAt(ctx, creds, pop, &opts) - if err != nil { - return nil, err - } - return &VFSFile{file: fd}, nil -} diff --git a/pkg/sentry/fsimpl/proc/BUILD b/pkg/sentry/fsimpl/proc/BUILD index 4344ee594..8124581c5 100644 --- a/pkg/sentry/fsimpl/proc/BUILD +++ b/pkg/sentry/fsimpl/proc/BUILD @@ -87,7 +87,6 @@ go_library( "//pkg/log", "//pkg/refs", "//pkg/safemem", - "//pkg/sentry/fsbridge", "//pkg/sentry/fsimpl/kernfs", "//pkg/sentry/fsimpl/lock", "//pkg/sentry/inet", diff --git a/pkg/sentry/fsimpl/proc/task_files.go b/pkg/sentry/fsimpl/proc/task_files.go index 8a51b7e14..156c420ba 100644 --- a/pkg/sentry/fsimpl/proc/task_files.go +++ b/pkg/sentry/fsimpl/proc/task_files.go @@ -24,7 +24,6 @@ import ( "gvisor.dev/gvisor/pkg/errors/linuxerr" "gvisor.dev/gvisor/pkg/hostarch" "gvisor.dev/gvisor/pkg/safemem" - "gvisor.dev/gvisor/pkg/sentry/fsbridge" "gvisor.dev/gvisor/pkg/sentry/fsimpl/kernfs" "gvisor.dev/gvisor/pkg/sentry/kernel" "gvisor.dev/gvisor/pkg/sentry/kernel/auth" @@ -967,7 +966,7 @@ func (s *exeSymlink) Getlink(ctx context.Context, _ *vfs.Mount) (vfs.VirtualDent } defer exec.DecRef(ctx) - vd := exec.(*fsbridge.VFSFile).FileDescription().VirtualDentry() + vd := exec.VirtualDentry() vd.IncRef() return vd, "", nil } diff --git a/pkg/sentry/fsimpl/testutil/BUILD b/pkg/sentry/fsimpl/testutil/BUILD index b3f9d1010..5ce97ac4a 100644 --- a/pkg/sentry/fsimpl/testutil/BUILD +++ b/pkg/sentry/fsimpl/testutil/BUILD @@ -17,7 +17,6 @@ go_library( "//pkg/fspath", "//pkg/hostarch", "//pkg/memutil", - "//pkg/sentry/fsbridge", "//pkg/sentry/fsimpl/tmpfs", "//pkg/sentry/kernel", "//pkg/sentry/kernel/auth", diff --git a/pkg/sentry/fsimpl/testutil/kernel.go b/pkg/sentry/fsimpl/testutil/kernel.go index f89a7e7f4..4b56eddc6 100644 --- a/pkg/sentry/fsimpl/testutil/kernel.go +++ b/pkg/sentry/fsimpl/testutil/kernel.go @@ -25,7 +25,6 @@ import ( "gvisor.dev/gvisor/pkg/cpuid" "gvisor.dev/gvisor/pkg/fspath" "gvisor.dev/gvisor/pkg/memutil" - "gvisor.dev/gvisor/pkg/sentry/fsbridge" "gvisor.dev/gvisor/pkg/sentry/fsimpl/tmpfs" "gvisor.dev/gvisor/pkg/sentry/kernel" "gvisor.dev/gvisor/pkg/sentry/kernel/auth" @@ -128,7 +127,7 @@ func CreateTask(ctx context.Context, name string, tc *kernel.ThreadGroup, mntns return nil, err } m := mm.NewMemoryManager(k, k, k.SleepForAddressSpaceActivation) - m.SetExecutable(ctx, fsbridge.NewVFSFile(exe)) + m.SetExecutable(ctx, exe) creds := auth.CredentialsFromContext(ctx) config := &kernel.TaskConfig{ diff --git a/pkg/sentry/kernel/BUILD b/pkg/sentry/kernel/BUILD index 7be60e830..13172abaf 100644 --- a/pkg/sentry/kernel/BUILD +++ b/pkg/sentry/kernel/BUILD @@ -306,7 +306,6 @@ go_library( "//pkg/secio", "//pkg/sentry/arch", "//pkg/sentry/device", - "//pkg/sentry/fsbridge", "//pkg/sentry/fsimpl/kernfs", "//pkg/sentry/fsimpl/lock", "//pkg/sentry/fsimpl/mqfs", diff --git a/pkg/sentry/kernel/kernel.go b/pkg/sentry/kernel/kernel.go index 99ae8a4c9..0586c46b7 100644 --- a/pkg/sentry/kernel/kernel.go +++ b/pkg/sentry/kernel/kernel.go @@ -47,7 +47,6 @@ import ( "gvisor.dev/gvisor/pkg/fspath" "gvisor.dev/gvisor/pkg/log" "gvisor.dev/gvisor/pkg/sentry/arch" - "gvisor.dev/gvisor/pkg/sentry/fsbridge" "gvisor.dev/gvisor/pkg/sentry/fsimpl/pipefs" "gvisor.dev/gvisor/pkg/sentry/fsimpl/sockfs" "gvisor.dev/gvisor/pkg/sentry/fsimpl/timerfd" @@ -660,7 +659,7 @@ type CreateProcessArgs struct { // File is a passed host FD pointing to a file to load as the init binary. // // This is checked if and only if Filename is "". - File fsbridge.File + File *vfs.FileDescription // Argvv is a list of arguments. Argv []string @@ -839,7 +838,6 @@ func (k *Kernel) CreateProcess(args CreateProcessArgs) (*ThreadGroup, ThreadID, } defer wd.DecRef(ctx) } - opener := fsbridge.NewVFSLookup(mntns, root, wd) fsContext := NewFSContext(root, wd, args.Umask) tg := k.NewThreadGroup(args.PIDNamespace, NewSignalHandlers(), linux.SIGCHLD, args.Limits) @@ -856,7 +854,7 @@ func (k *Kernel) CreateProcess(args CreateProcessArgs) (*ThreadGroup, ThreadID, args.File = nil case args.File != nil: // If File is set, take the File provided directly. - args.Filename = args.File.PathnameWithDeleted(ctx) + args.Filename = args.File.MappedName(ctx) default: // Otherwise look at Argv and see if the first argument is a valid path. if len(args.Argv) == 0 { @@ -871,7 +869,8 @@ func (k *Kernel) CreateProcess(args CreateProcessArgs) (*ThreadGroup, ThreadID, // Create a fresh task context. remainingTraversals := args.MaxSymlinkTraversals loadArgs := loader.LoadArgs{ - Opener: opener, + Root: root, + WorkingDir: wd, RemainingTraversals: &remainingTraversals, ResolveFinal: true, Filename: args.Filename, diff --git a/pkg/sentry/kernel/task_exec.go b/pkg/sentry/kernel/task_exec.go index 97dd7f2f8..6dda5a2fd 100644 --- a/pkg/sentry/kernel/task_exec.go +++ b/pkg/sentry/kernel/task_exec.go @@ -67,7 +67,6 @@ package kernel import ( "gvisor.dev/gvisor/pkg/abi/linux" "gvisor.dev/gvisor/pkg/errors/linuxerr" - "gvisor.dev/gvisor/pkg/sentry/fsbridge" "gvisor.dev/gvisor/pkg/sentry/mm" "gvisor.dev/gvisor/pkg/sentry/seccheck" pb "gvisor.dev/gvisor/pkg/sentry/seccheck/points/points_go_proto" @@ -92,7 +91,7 @@ func (*execStop) Killable() bool { return true } // // Preconditions: The caller must be running Task.doSyscallInvoke on the task // goroutine. -func (t *Task) Execve(newImage *TaskImage, argv, env []string, executable fsbridge.File, pathname string) (*SyscallControl, error) { +func (t *Task) Execve(newImage *TaskImage, argv, env []string, executable *vfs.FileDescription, pathname string) (*SyscallControl, error) { // We can't clearly hold kernel package locks while stat'ing executable. if seccheck.Global.Enabled(seccheck.PointExecve) { mask, info := getExecveSeccheckInfo(t, argv, env, executable, pathname) @@ -303,7 +302,7 @@ func (t *Task) promoteLocked() { oldLeader.exitNotifyLocked(false) } -func getExecveSeccheckInfo(t *Task, argv, env []string, executable fsbridge.File, pathname string) (seccheck.FieldSet, *pb.ExecveInfo) { +func getExecveSeccheckInfo(t *Task, argv, env []string, executable *vfs.FileDescription, pathname string) (seccheck.FieldSet, *pb.ExecveInfo) { fields := seccheck.Global.GetFieldSet(seccheck.PointExecve) info := &pb.ExecveInfo{ Argv: argv, @@ -311,21 +310,19 @@ func getExecveSeccheckInfo(t *Task, argv, env []string, executable fsbridge.File } if executable != nil { info.BinaryPath = pathname - if vfs2bridgeFile, ok := executable.(*fsbridge.VFSFile); ok { - if fields.Local.Contains(seccheck.FieldSentryExecveBinaryInfo) { - statOpts := vfs.StatOptions{ - Mask: linux.STATX_TYPE | linux.STATX_MODE | linux.STATX_UID | linux.STATX_GID, + if fields.Local.Contains(seccheck.FieldSentryExecveBinaryInfo) { + statOpts := vfs.StatOptions{ + Mask: linux.STATX_TYPE | linux.STATX_MODE | linux.STATX_UID | linux.STATX_GID, + } + if stat, err := executable.Stat(t, statOpts); err == nil { + if stat.Mask&(linux.STATX_TYPE|linux.STATX_MODE) == (linux.STATX_TYPE | linux.STATX_MODE) { + info.BinaryMode = uint32(stat.Mode) } - if stat, err := vfs2bridgeFile.FileDescription().Stat(t, statOpts); err == nil { - if stat.Mask&(linux.STATX_TYPE|linux.STATX_MODE) == (linux.STATX_TYPE | linux.STATX_MODE) { - info.BinaryMode = uint32(stat.Mode) - } - if stat.Mask&linux.STATX_UID != 0 { - info.BinaryUid = stat.UID - } - if stat.Mask&linux.STATX_GID != 0 { - info.BinaryGid = stat.GID - } + if stat.Mask&linux.STATX_UID != 0 { + info.BinaryUid = stat.UID + } + if stat.Mask&linux.STATX_GID != 0 { + info.BinaryGid = stat.GID } } } diff --git a/pkg/sentry/kernel/task_log.go b/pkg/sentry/kernel/task_log.go index 2d2da7fd8..35e548706 100644 --- a/pkg/sentry/kernel/task_log.go +++ b/pkg/sentry/kernel/task_log.go @@ -253,6 +253,6 @@ func (t *Task) traceExecEvent(image *TaskImage) { // traceExecEvent function may be called before the task goroutine // starts, so we must use the async context. - name := file.PathnameWithDeleted(t.AsyncContext()) + name := file.MappedName(t.AsyncContext()) trace.Logf(t.traceContext, traceCategory, "exec: %s", name) } diff --git a/pkg/sentry/loader/BUILD b/pkg/sentry/loader/BUILD index 560a0f33c..863ab2c4c 100644 --- a/pkg/sentry/loader/BUILD +++ b/pkg/sentry/loader/BUILD @@ -21,12 +21,12 @@ go_library( "//pkg/context", "//pkg/cpuid", "//pkg/errors/linuxerr", + "//pkg/fspath", "//pkg/hostarch", "//pkg/log", "//pkg/rand", "//pkg/safemem", "//pkg/sentry/arch", - "//pkg/sentry/fsbridge", "//pkg/sentry/kernel/auth", "//pkg/sentry/limits", "//pkg/sentry/loader/vdsodata", diff --git a/pkg/sentry/loader/elf.go b/pkg/sentry/loader/elf.go index 31926ea0c..73c332495 100644 --- a/pkg/sentry/loader/elf.go +++ b/pkg/sentry/loader/elf.go @@ -28,10 +28,10 @@ import ( "gvisor.dev/gvisor/pkg/hostarch" "gvisor.dev/gvisor/pkg/log" "gvisor.dev/gvisor/pkg/sentry/arch" - "gvisor.dev/gvisor/pkg/sentry/fsbridge" "gvisor.dev/gvisor/pkg/sentry/limits" "gvisor.dev/gvisor/pkg/sentry/memmap" "gvisor.dev/gvisor/pkg/sentry/mm" + "gvisor.dev/gvisor/pkg/sentry/vfs" "gvisor.dev/gvisor/pkg/usermem" ) @@ -238,7 +238,7 @@ func parseHeader(ctx context.Context, f fullReader) (elfInfo, error) { // mapSegment maps a phdr into the Task. offset is the offset to apply to // phdr.Vaddr. -func mapSegment(ctx context.Context, m *mm.MemoryManager, f fsbridge.File, phdr *elf.ProgHeader, offset hostarch.Addr) error { +func mapSegment(ctx context.Context, m *mm.MemoryManager, fd *vfs.FileDescription, phdr *elf.ProgHeader, offset hostarch.Addr) error { // We must make a page-aligned mapping. adjust := hostarch.Addr(phdr.Vaddr).PageOffset() @@ -286,7 +286,7 @@ func mapSegment(ctx context.Context, m *mm.MemoryManager, f fsbridge.File, phdr mopts.MappingIdentity.DecRef(ctx) } }() - if err := f.ConfigureMMap(ctx, &mopts); err != nil { + if err := fd.ConfigureMMap(ctx, &mopts); err != nil { ctx.Infof("File is not memory-mappable: %v", err) return err } @@ -405,7 +405,7 @@ type loadedELF struct { // It does not load the ELF interpreter, or return any auxv entries. // // Preconditions: f is an ELF file. -func loadParsedELF(ctx context.Context, m *mm.MemoryManager, f fsbridge.File, info elfInfo, sharedLoadOffset hostarch.Addr) (loadedELF, error) { +func loadParsedELF(ctx context.Context, m *mm.MemoryManager, fd *vfs.FileDescription, info elfInfo, sharedLoadOffset hostarch.Addr) (loadedELF, error) { first := true var start, end hostarch.Addr var interpreter string @@ -445,7 +445,7 @@ func loadParsedELF(ctx context.Context, m *mm.MemoryManager, f fsbridge.File, in } path := make([]byte, phdr.Filesz) - _, err := f.ReadFull(ctx, usermem.BytesIOSequence(path), int64(phdr.Off)) + _, err := fd.ReadFull(ctx, usermem.BytesIOSequence(path), int64(phdr.Off)) if err != nil { // If an interpreter was specified, it should exist. ctx.Infof("Error reading PT_INTERP path: %v", err) @@ -542,7 +542,7 @@ func loadParsedELF(ctx context.Context, m *mm.MemoryManager, f fsbridge.File, in continue } - if err := mapSegment(ctx, m, f, &phdr, offset); err != nil { + if err := mapSegment(ctx, m, fd, &phdr, offset); err != nil { ctx.Infof("Failed to map PT_LOAD segment: %+v", phdr) return loadedELF{}, err } @@ -580,8 +580,8 @@ func loadParsedELF(ctx context.Context, m *mm.MemoryManager, f fsbridge.File, in // Preconditions: // - f is an ELF file. // - f is the first ELF loaded into m. -func loadInitialELF(ctx context.Context, m *mm.MemoryManager, fs cpuid.FeatureSet, f fsbridge.File) (loadedELF, *arch.Context64, error) { - info, err := parseHeader(ctx, f) +func loadInitialELF(ctx context.Context, m *mm.MemoryManager, fs cpuid.FeatureSet, fd *vfs.FileDescription) (loadedELF, *arch.Context64, error) { + info, err := parseHeader(ctx, fd) if err != nil { ctx.Infof("Failed to parse initial ELF: %v", err) return loadedELF{}, nil, err @@ -606,7 +606,7 @@ func loadInitialELF(ctx context.Context, m *mm.MemoryManager, fs cpuid.FeatureSe // PIELoadAddress tries to move the ELF out of the way of the default // mmap base to ensure that the initial brk has sufficient space to // grow. - le, err := loadParsedELF(ctx, m, f, info, ac.PIELoadAddress(l)) + le, err := loadParsedELF(ctx, m, fd, info, ac.PIELoadAddress(l)) return le, ac, err } @@ -617,8 +617,8 @@ func loadInitialELF(ctx context.Context, m *mm.MemoryManager, fs cpuid.FeatureSe // It does not return any auxv entries. // // Preconditions: f is an ELF file. -func loadInterpreterELF(ctx context.Context, m *mm.MemoryManager, f fsbridge.File, initial loadedELF) (loadedELF, error) { - info, err := parseHeader(ctx, f) +func loadInterpreterELF(ctx context.Context, m *mm.MemoryManager, fd *vfs.FileDescription, initial loadedELF) (loadedELF, error) { + info, err := parseHeader(ctx, fd) if err != nil { if linuxerr.Equals(linuxerr.ENOEXEC, err) { // Bad interpreter. @@ -638,7 +638,7 @@ func loadInterpreterELF(ctx context.Context, m *mm.MemoryManager, f fsbridge.Fil // The interpreter is not given a load offset, as its location does not // affect brk. - return loadParsedELF(ctx, m, f, info, 0) + return loadParsedELF(ctx, m, fd, info, 0) } // loadELF loads args.File into the Task address space. diff --git a/pkg/sentry/loader/interpreter.go b/pkg/sentry/loader/interpreter.go index 1ec0d7019..5e6d8319c 100644 --- a/pkg/sentry/loader/interpreter.go +++ b/pkg/sentry/loader/interpreter.go @@ -20,7 +20,7 @@ import ( "gvisor.dev/gvisor/pkg/context" "gvisor.dev/gvisor/pkg/errors/linuxerr" - "gvisor.dev/gvisor/pkg/sentry/fsbridge" + "gvisor.dev/gvisor/pkg/sentry/vfs" "gvisor.dev/gvisor/pkg/usermem" ) @@ -37,9 +37,9 @@ const ( ) // parseInterpreterScript returns the interpreter path and argv. -func parseInterpreterScript(ctx context.Context, filename string, f fsbridge.File, argv []string) (newpath string, newargv []string, err error) { +func parseInterpreterScript(ctx context.Context, filename string, fd *vfs.FileDescription, argv []string) (newpath string, newargv []string, err error) { line := make([]byte, interpMaxLineLength) - n, err := f.ReadFull(ctx, usermem.BytesIOSequence(line), 0) + n, err := fd.ReadFull(ctx, usermem.BytesIOSequence(line), 0) // Short read is OK. if err != nil && err != io.ErrUnexpectedEOF { if err == io.EOF { diff --git a/pkg/sentry/loader/loader.go b/pkg/sentry/loader/loader.go index 88dce81d9..18ec6c960 100644 --- a/pkg/sentry/loader/loader.go +++ b/pkg/sentry/loader/loader.go @@ -27,10 +27,10 @@ import ( "gvisor.dev/gvisor/pkg/context" "gvisor.dev/gvisor/pkg/cpuid" "gvisor.dev/gvisor/pkg/errors/linuxerr" + "gvisor.dev/gvisor/pkg/fspath" "gvisor.dev/gvisor/pkg/hostarch" "gvisor.dev/gvisor/pkg/rand" "gvisor.dev/gvisor/pkg/sentry/arch" - "gvisor.dev/gvisor/pkg/sentry/fsbridge" "gvisor.dev/gvisor/pkg/sentry/kernel/auth" "gvisor.dev/gvisor/pkg/sentry/mm" "gvisor.dev/gvisor/pkg/sentry/vfs" @@ -55,18 +55,21 @@ type LoadArgs struct { // Filename is the path for the executable. Filename string - // File is an open fs.File object of the executable. If File is not - // nil, then File will be loaded and Filename will be ignored. + // File is an open FD of the executable. If File is not nil, then File will + // be loaded and Filename will be ignored. // // The caller is responsible for checking that the user can execute this file. - File fsbridge.File + File *vfs.FileDescription - // Opener is used to open the executable file when 'File' is nil. - Opener fsbridge.Lookup + // Root is the current filesystem root. + Root vfs.VirtualDentry + + // WorkingDir is the current working directory. + WorkingDir vfs.VirtualDentry // If AfterOpen is not nil, it is called after every successful call to // Opener.OpenPath(). - AfterOpen func(f fsbridge.File) + AfterOpen func(f *vfs.FileDescription) // CloseOnExec indicates that the executable (or one of its parent // directories) was opened with O_CLOEXEC. If the executable is an @@ -91,7 +94,7 @@ type LoadArgs struct { // installed in the Task FDTable. The caller takes ownership of both. // // args.Filename must be a readable, executable, regular file. -func openPath(ctx context.Context, args LoadArgs) (fsbridge.File, error) { +func openPath(ctx context.Context, args LoadArgs) (*vfs.FileDescription, error) { if args.Filename == "" { ctx.Infof("cannot open empty name") return nil, linuxerr.ENOENT @@ -106,23 +109,35 @@ func openPath(ctx context.Context, args LoadArgs) (fsbridge.File, error) { Flags: linux.O_RDONLY, FileExec: true, } - f, err := args.Opener.OpenPath(ctx, args.Filename, opts, args.RemainingTraversals, args.ResolveFinal) + vfsObj := args.Root.Mount().Filesystem().VirtualFilesystem() + creds := auth.CredentialsFromContext(ctx) + path := fspath.Parse(args.Filename) + pop := &vfs.PathOperation{ + Root: args.Root, + Start: args.WorkingDir, + Path: path, + FollowFinalSymlink: args.ResolveFinal, + } + if path.Absolute { + pop.Start = args.Root + } + fd, err := vfsObj.OpenAt(ctx, creds, pop, &opts) if err != nil { - return f, err + return nil, err } if args.AfterOpen != nil { - args.AfterOpen(f) + args.AfterOpen(fd) } - return f, nil + return fd, nil } // checkIsRegularFile prevents us from trying to execute a directory, pipe, etc. -func checkIsRegularFile(ctx context.Context, file fsbridge.File, filename string) error { - t, err := file.Type(ctx) +func checkIsRegularFile(ctx context.Context, fd *vfs.FileDescription, filename string) error { + stat, err := fd.Stat(ctx, vfs.StatOptions{}) if err != nil { return err } - if t != linux.ModeRegular { + if t := linux.FileMode(stat.Mode).FileType(); t != linux.ModeRegular { ctx.Infof("%q is not a regular file: %v", filename, t) return linuxerr.EACCES } @@ -157,7 +172,7 @@ const ( // - arch.Context64 matching the binary arch // - fs.Dirent of the binary file // - Possibly updated args.Argv -func loadExecutable(ctx context.Context, args LoadArgs) (loadedELF, *arch.Context64, fsbridge.File, []string, error) { +func loadExecutable(ctx context.Context, args LoadArgs) (loadedELF, *arch.Context64, *vfs.FileDescription, []string, error) { for i := 0; i < maxLoaderAttempts; i++ { if args.File == nil { var err error diff --git a/pkg/sentry/mm/BUILD b/pkg/sentry/mm/BUILD index ccd054c7d..832ca5f3d 100644 --- a/pkg/sentry/mm/BUILD +++ b/pkg/sentry/mm/BUILD @@ -182,7 +182,6 @@ go_library( "//pkg/safecopy", "//pkg/safemem", "//pkg/sentry/arch", - "//pkg/sentry/fsbridge", "//pkg/sentry/kernel/auth", "//pkg/sentry/kernel/futex", "//pkg/sentry/kernel/shm", @@ -191,6 +190,7 @@ go_library( "//pkg/sentry/pgalloc", "//pkg/sentry/platform", "//pkg/sentry/usage", + "//pkg/sentry/vfs", "//pkg/sync", "//pkg/sync/locking", "//pkg/usermem", diff --git a/pkg/sentry/mm/metadata.go b/pkg/sentry/mm/metadata.go index 4ddbb2490..1315118c1 100644 --- a/pkg/sentry/mm/metadata.go +++ b/pkg/sentry/mm/metadata.go @@ -18,7 +18,7 @@ import ( "gvisor.dev/gvisor/pkg/context" "gvisor.dev/gvisor/pkg/hostarch" "gvisor.dev/gvisor/pkg/sentry/arch" - "gvisor.dev/gvisor/pkg/sentry/fsbridge" + "gvisor.dev/gvisor/pkg/sentry/vfs" ) // Dumpability describes if and how core dumps should be created. @@ -129,7 +129,7 @@ func (mm *MemoryManager) SetAuxv(auxv arch.Auxv) { // // An additional reference will be taken in the case of a non-nil executable, // which must be released by the caller. -func (mm *MemoryManager) Executable() fsbridge.File { +func (mm *MemoryManager) Executable() *vfs.FileDescription { mm.metadataMu.Lock() defer mm.metadataMu.Unlock() @@ -144,15 +144,15 @@ func (mm *MemoryManager) Executable() fsbridge.File { // SetExecutable sets the executable. // // This takes a reference on d. -func (mm *MemoryManager) SetExecutable(ctx context.Context, file fsbridge.File) { +func (mm *MemoryManager) SetExecutable(ctx context.Context, fd *vfs.FileDescription) { mm.metadataMu.Lock() // Grab a new reference. - file.IncRef() + fd.IncRef() // Set the executable. orig := mm.executable - mm.executable = file + mm.executable = fd mm.metadataMu.Unlock() diff --git a/pkg/sentry/mm/mm.go b/pkg/sentry/mm/mm.go index ea5f46177..fb3867d5c 100644 --- a/pkg/sentry/mm/mm.go +++ b/pkg/sentry/mm/mm.go @@ -44,10 +44,10 @@ import ( "gvisor.dev/gvisor/pkg/hostarch" "gvisor.dev/gvisor/pkg/safemem" "gvisor.dev/gvisor/pkg/sentry/arch" - "gvisor.dev/gvisor/pkg/sentry/fsbridge" "gvisor.dev/gvisor/pkg/sentry/memmap" "gvisor.dev/gvisor/pkg/sentry/pgalloc" "gvisor.dev/gvisor/pkg/sentry/platform" + "gvisor.dev/gvisor/pkg/sentry/vfs" ) // MapsCallbackFunc has all the parameters required for populating an entry of /proc/[pid]/maps. @@ -226,7 +226,7 @@ type MemoryManager struct { // is not nil, it holds a reference on the Dirent. // // executable is protected by metadataMu. - executable fsbridge.File + executable *vfs.FileDescription // aioManager keeps track of AIOContexts used for async IOs. AIOManager // must be cloned when CLONE_VM is used. diff --git a/pkg/sentry/syscalls/linux/BUILD b/pkg/sentry/syscalls/linux/BUILD index 93efe37e1..48a50640c 100644 --- a/pkg/sentry/syscalls/linux/BUILD +++ b/pkg/sentry/syscalls/linux/BUILD @@ -79,7 +79,6 @@ go_library( "//pkg/rand", "//pkg/safemem", "//pkg/sentry/arch", - "//pkg/sentry/fsbridge", "//pkg/sentry/fsimpl/eventfd", "//pkg/sentry/fsimpl/host", "//pkg/sentry/fsimpl/iouringfs", diff --git a/pkg/sentry/syscalls/linux/sys_prctl.go b/pkg/sentry/syscalls/linux/sys_prctl.go index 515d14f92..4464ef227 100644 --- a/pkg/sentry/syscalls/linux/sys_prctl.go +++ b/pkg/sentry/syscalls/linux/sys_prctl.go @@ -21,7 +21,6 @@ import ( "gvisor.dev/gvisor/pkg/errors/linuxerr" "gvisor.dev/gvisor/pkg/marshal/primitive" "gvisor.dev/gvisor/pkg/sentry/arch" - "gvisor.dev/gvisor/pkg/sentry/fsbridge" "gvisor.dev/gvisor/pkg/sentry/kernel" "gvisor.dev/gvisor/pkg/sentry/kernel/auth" "gvisor.dev/gvisor/pkg/sentry/mm" @@ -141,7 +140,7 @@ func Prctl(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall } // Set the underlying executable. - t.MemoryManager().SetExecutable(t, fsbridge.NewVFSFile(file)) + t.MemoryManager().SetExecutable(t, file) case linux.PR_SET_MM_AUXV, linux.PR_SET_MM_START_CODE, diff --git a/pkg/sentry/syscalls/linux/sys_thread.go b/pkg/sentry/syscalls/linux/sys_thread.go index e4606a856..b978179da 100644 --- a/pkg/sentry/syscalls/linux/sys_thread.go +++ b/pkg/sentry/syscalls/linux/sys_thread.go @@ -21,7 +21,6 @@ import ( "gvisor.dev/gvisor/pkg/hostarch" "gvisor.dev/gvisor/pkg/marshal/primitive" "gvisor.dev/gvisor/pkg/sentry/arch" - "gvisor.dev/gvisor/pkg/sentry/fsbridge" "gvisor.dev/gvisor/pkg/sentry/kernel" "gvisor.dev/gvisor/pkg/sentry/kernel/sched" "gvisor.dev/gvisor/pkg/sentry/loader" @@ -108,7 +107,7 @@ func execveat(t *kernel.Task, dirfd int32, pathnameAddr, argvAddr, envvAddr host root := t.FSContext().RootDirectory() defer root.DecRef(t) - var executable fsbridge.File + var executable *vfs.FileDescription defer func() { if executable != nil { executable.DecRef(t) @@ -146,17 +145,17 @@ func execveat(t *kernel.Task, dirfd int32, pathnameAddr, argvAddr, envvAddr host if err != nil { return 0, nil, err } - executable = fsbridge.NewVFSFile(file) - pathname = executable.PathnameWithDeleted(t) + executable = file + pathname = executable.MappedName(t) } // Load the new TaskImage. - mntns := t.MountNamespace() wd := t.FSContext().WorkingDirectory() defer wd.DecRef(t) remainingTraversals := uint(linux.MaxSymlinkTraversals) loadArgs := loader.LoadArgs{ - Opener: fsbridge.NewVFSLookup(mntns, root, wd), + Root: root, + WorkingDir: wd, RemainingTraversals: &remainingTraversals, ResolveFinal: flags&linux.AT_SYMLINK_NOFOLLOW == 0, Filename: pathname, @@ -170,11 +169,11 @@ func execveat(t *kernel.Task, dirfd int32, pathnameAddr, argvAddr, envvAddr host // Retain the first executable file that is opened (which may open // multiple executable files while resolving interpreter scripts). if executable == nil { - loadArgs.AfterOpen = func(f fsbridge.File) { + loadArgs.AfterOpen = func(f *vfs.FileDescription) { if executable == nil { f.IncRef() executable = f - pathname = executable.PathnameWithDeleted(t) + pathname = executable.MappedName(t) } } } diff --git a/pkg/sentry/vfs/file_description.go b/pkg/sentry/vfs/file_description.go index 270d3eafb..d54f97f00 100644 --- a/pkg/sentry/vfs/file_description.go +++ b/pkg/sentry/vfs/file_description.go @@ -905,6 +905,22 @@ func (fd *FileDescription) ComputeLockRange(ctx context.Context, start uint64, l return lock.ComputeRange(int64(start), int64(length), off) } +// ReadFull read all contents from the file. +func (fd *FileDescription) ReadFull(ctx context.Context, dst usermem.IOSequence, offset int64) (int64, error) { + var total int64 + for dst.NumBytes() > 0 { + n, err := fd.PRead(ctx, dst, offset+total, ReadOptions{}) + total += n + if err == io.EOF && total != 0 { + return total, io.ErrUnexpectedEOF + } else if err != nil { + return total, err + } + dst = dst.DropFirst64(n) + } + return total, nil +} + // A FileAsync sends signals to its owner when w is ready for IO. This is only // implemented by pkg/sentry/fasync:FileAsync, but we unfortunately need this // interface to avoid circular dependencies. diff --git a/runsc/boot/procfs/dump.go b/runsc/boot/procfs/dump.go index 81ea5e5b3..a41f4a55f 100644 --- a/runsc/boot/procfs/dump.go +++ b/runsc/boot/procfs/dump.go @@ -131,7 +131,7 @@ func getExecutablePath(ctx context.Context, pid kernel.ThreadID, mm *mm.MemoryMa } defer exec.DecRef(ctx) - return exec.PathnameWithDeleted(ctx) + return exec.MappedName(ctx) } func getMetadataArray(ctx context.Context, pid kernel.ThreadID, mm *mm.MemoryManager, metaType proc.MetadataType) []string {