2019-04-29 21:25:05 +00:00
|
|
|
// Copyright 2018 The gVisor Authors.
|
2018-04-27 17:37:02 +00:00
|
|
|
//
|
|
|
|
// 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 proc
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"sort"
|
|
|
|
"strconv"
|
|
|
|
|
2019-06-13 23:49:09 +00:00
|
|
|
"gvisor.dev/gvisor/pkg/sentry/context"
|
|
|
|
"gvisor.dev/gvisor/pkg/sentry/fs"
|
|
|
|
"gvisor.dev/gvisor/pkg/sentry/fs/fsutil"
|
|
|
|
"gvisor.dev/gvisor/pkg/sentry/fs/proc/device"
|
|
|
|
"gvisor.dev/gvisor/pkg/sentry/fs/ramfs"
|
|
|
|
"gvisor.dev/gvisor/pkg/sentry/kernel"
|
|
|
|
"gvisor.dev/gvisor/pkg/syserror"
|
2018-04-27 17:37:02 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// walkDescriptors finds the descriptor (file-flag pair) for the fd identified
|
|
|
|
// by p, and calls the toInodeOperations callback with that descriptor. This is a helper
|
|
|
|
// method for implementing fs.InodeOperations.Lookup.
|
|
|
|
func walkDescriptors(t *kernel.Task, p string, toInode func(*fs.File, kernel.FDFlags) *fs.Inode) (*fs.Inode, error) {
|
|
|
|
n, err := strconv.ParseUint(p, 10, 64)
|
|
|
|
if err != nil {
|
|
|
|
// Not found.
|
|
|
|
return nil, syserror.ENOENT
|
|
|
|
}
|
|
|
|
|
|
|
|
var file *fs.File
|
2018-07-31 18:18:02 +00:00
|
|
|
var fdFlags kernel.FDFlags
|
2018-04-27 17:37:02 +00:00
|
|
|
t.WithMuLocked(func(t *kernel.Task) {
|
2019-07-03 02:27:51 +00:00
|
|
|
if fdTable := t.FDTable(); fdTable != nil {
|
|
|
|
file, fdFlags = fdTable.Get(int32(n))
|
2018-04-27 17:37:02 +00:00
|
|
|
}
|
|
|
|
})
|
|
|
|
if file == nil {
|
|
|
|
return nil, syserror.ENOENT
|
|
|
|
}
|
2018-07-31 18:18:02 +00:00
|
|
|
return toInode(file, fdFlags), nil
|
2018-04-27 17:37:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// readDescriptors reads fds in the task starting at offset, and calls the
|
|
|
|
// toDentAttr callback for each to get a DentAttr, which it then emits. This is
|
|
|
|
// a helper for implementing fs.InodeOperations.Readdir.
|
2019-01-15 04:33:29 +00:00
|
|
|
func readDescriptors(t *kernel.Task, c *fs.DirCtx, offset int64, toDentAttr func(int) fs.DentAttr) (int64, error) {
|
2019-07-03 02:27:51 +00:00
|
|
|
var fds []int32
|
2018-04-27 17:37:02 +00:00
|
|
|
t.WithMuLocked(func(t *kernel.Task) {
|
2019-07-03 02:27:51 +00:00
|
|
|
if fdTable := t.FDTable(); fdTable != nil {
|
|
|
|
fds = fdTable.GetFDs()
|
2018-04-27 17:37:02 +00:00
|
|
|
}
|
|
|
|
})
|
|
|
|
|
2019-07-03 02:27:51 +00:00
|
|
|
// Find the appropriate starting point.
|
|
|
|
idx := sort.Search(len(fds), func(i int) bool { return fds[i] >= int32(offset) })
|
|
|
|
if idx == len(fds) {
|
2018-04-27 17:37:02 +00:00
|
|
|
return offset, nil
|
|
|
|
}
|
2019-07-03 02:27:51 +00:00
|
|
|
fds = fds[idx:]
|
2018-04-27 17:37:02 +00:00
|
|
|
|
2019-07-03 02:27:51 +00:00
|
|
|
// Serialize all FDs.
|
|
|
|
for _, fd := range fds {
|
2018-04-27 17:37:02 +00:00
|
|
|
name := strconv.FormatUint(uint64(fd), 10)
|
2019-07-03 02:27:51 +00:00
|
|
|
if err := c.DirEmit(name, toDentAttr(int(fd))); err != nil {
|
2018-04-27 17:37:02 +00:00
|
|
|
// Returned offset is the next fd to serialize.
|
2019-01-15 04:33:29 +00:00
|
|
|
return int64(fd), err
|
2018-04-27 17:37:02 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
// We serialized them all. Next offset should be higher than last
|
|
|
|
// serialized fd.
|
2019-07-03 02:27:51 +00:00
|
|
|
return int64(fds[len(fds)-1] + 1), nil
|
2018-04-27 17:37:02 +00:00
|
|
|
}
|
|
|
|
|
2019-01-15 04:33:29 +00:00
|
|
|
// fd implements fs.InodeOperations for a file in /proc/TID/fd/.
|
2018-04-27 17:37:02 +00:00
|
|
|
type fd struct {
|
|
|
|
ramfs.Symlink
|
2019-04-11 07:41:42 +00:00
|
|
|
file *fs.File
|
2018-04-27 17:37:02 +00:00
|
|
|
}
|
|
|
|
|
2019-01-15 04:33:29 +00:00
|
|
|
var _ fs.InodeOperations = (*fd)(nil)
|
|
|
|
|
2018-08-14 22:48:52 +00:00
|
|
|
// newFd returns a new fd based on an existing file.
|
2018-04-27 17:37:02 +00:00
|
|
|
//
|
|
|
|
// This inherits one reference to the file.
|
|
|
|
func newFd(t *kernel.Task, f *fs.File, msrc *fs.MountSource) *fs.Inode {
|
2019-01-15 04:33:29 +00:00
|
|
|
fd := &fd{
|
|
|
|
// RootOwner overridden by taskOwnedInodeOps.UnstableAttrs().
|
|
|
|
Symlink: *ramfs.NewSymlink(t, fs.RootOwner, ""),
|
2019-04-11 07:41:42 +00:00
|
|
|
file: f,
|
2019-01-15 04:33:29 +00:00
|
|
|
}
|
2019-06-14 01:39:43 +00:00
|
|
|
return newProcInode(t, fd, msrc, fs.Symlink, t)
|
2018-04-27 17:37:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// GetFile returns the fs.File backing this fd. The dirent and flags
|
|
|
|
// arguments are ignored.
|
|
|
|
func (f *fd) GetFile(context.Context, *fs.Dirent, fs.FileFlags) (*fs.File, error) {
|
|
|
|
// Take a reference on the fs.File.
|
2019-04-11 07:41:42 +00:00
|
|
|
f.file.IncRef()
|
|
|
|
return f.file, nil
|
2018-04-27 17:37:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Readlink returns the current target.
|
|
|
|
func (f *fd) Readlink(ctx context.Context, _ *fs.Inode) (string, error) {
|
|
|
|
root := fs.RootFromContext(ctx)
|
2019-04-10 23:35:22 +00:00
|
|
|
if root != nil {
|
|
|
|
defer root.DecRef()
|
|
|
|
}
|
2019-04-11 07:41:42 +00:00
|
|
|
n, _ := f.file.Dirent.FullName(root)
|
2018-04-27 17:37:02 +00:00
|
|
|
return n, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Getlink implements fs.InodeOperations.Getlink.
|
|
|
|
func (f *fd) Getlink(context.Context, *fs.Inode) (*fs.Dirent, error) {
|
2019-04-11 07:41:42 +00:00
|
|
|
f.file.Dirent.IncRef()
|
|
|
|
return f.file.Dirent, nil
|
2018-04-27 17:37:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Truncate is ignored.
|
|
|
|
func (f *fd) Truncate(context.Context, *fs.Inode, int64) error {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2018-08-14 22:48:52 +00:00
|
|
|
func (f *fd) Release(ctx context.Context) {
|
|
|
|
f.Symlink.Release(ctx)
|
2019-04-11 07:41:42 +00:00
|
|
|
f.file.DecRef()
|
2018-08-14 22:48:52 +00:00
|
|
|
}
|
|
|
|
|
2018-04-27 17:37:02 +00:00
|
|
|
// Close releases the reference on the file.
|
|
|
|
func (f *fd) Close() error {
|
2019-04-11 07:41:42 +00:00
|
|
|
f.file.DecRef()
|
2018-04-27 17:37:02 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2019-01-15 04:33:29 +00:00
|
|
|
// fdDir is an InodeOperations for /proc/TID/fd.
|
2018-08-02 17:41:44 +00:00
|
|
|
//
|
|
|
|
// +stateify savable
|
2018-04-27 17:37:02 +00:00
|
|
|
type fdDir struct {
|
|
|
|
ramfs.Dir
|
|
|
|
|
2019-07-03 02:27:51 +00:00
|
|
|
// We hold a reference on the task's FDTable but only keep an indirect
|
|
|
|
// task pointer to avoid Dirent loading circularity caused by the
|
|
|
|
// table's back pointers into the dirent tree.
|
2018-04-27 17:37:02 +00:00
|
|
|
t *kernel.Task
|
|
|
|
}
|
|
|
|
|
2019-01-15 04:33:29 +00:00
|
|
|
var _ fs.InodeOperations = (*fdDir)(nil)
|
|
|
|
|
2018-04-27 17:37:02 +00:00
|
|
|
// newFdDir creates a new fdDir.
|
|
|
|
func newFdDir(t *kernel.Task, msrc *fs.MountSource) *fs.Inode {
|
2019-01-15 04:33:29 +00:00
|
|
|
f := &fdDir{
|
|
|
|
Dir: *ramfs.NewDir(t, nil, fs.RootOwner, fs.FilePermissions{User: fs.PermMask{Read: true, Execute: true}}),
|
|
|
|
t: t,
|
|
|
|
}
|
2019-06-14 01:39:43 +00:00
|
|
|
return newProcInode(t, f, msrc, fs.SpecialDirectory, t)
|
2018-04-27 17:37:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Check implements InodeOperations.Check.
|
|
|
|
//
|
|
|
|
// This is to match Linux, which uses a special permission handler to guarantee
|
|
|
|
// that a process can still access /proc/self/fd after it has executed
|
|
|
|
// setuid. See fs/proc/fd.c:proc_fd_permission.
|
|
|
|
func (f *fdDir) Check(ctx context.Context, inode *fs.Inode, req fs.PermMask) bool {
|
|
|
|
if fs.ContextCanAccessFile(ctx, inode, req) {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
if t := kernel.TaskFromContext(ctx); t != nil {
|
|
|
|
// Allow access if the task trying to access it is in the
|
|
|
|
// thread group corresponding to this directory.
|
|
|
|
if f.t.ThreadGroup() == t.ThreadGroup() {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
// Lookup loads an Inode in /proc/TID/fd into a Dirent.
|
|
|
|
func (f *fdDir) Lookup(ctx context.Context, dir *fs.Inode, p string) (*fs.Dirent, error) {
|
|
|
|
n, err := walkDescriptors(f.t, p, func(file *fs.File, _ kernel.FDFlags) *fs.Inode {
|
|
|
|
return newFd(f.t, file, dir.MountSource)
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2019-06-14 01:39:43 +00:00
|
|
|
return fs.NewDirent(ctx, n, p), nil
|
2018-04-27 17:37:02 +00:00
|
|
|
}
|
|
|
|
|
2019-01-15 04:33:29 +00:00
|
|
|
// GetFile implements fs.FileOperations.GetFile.
|
|
|
|
func (f *fdDir) GetFile(ctx context.Context, dirent *fs.Dirent, flags fs.FileFlags) (*fs.File, error) {
|
|
|
|
fops := &fdDirFile{
|
|
|
|
isInfoFile: false,
|
|
|
|
t: f.t,
|
|
|
|
}
|
|
|
|
return fs.NewFile(ctx, dirent, flags, fops), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// +stateify savable
|
|
|
|
type fdDirFile struct {
|
2019-04-11 07:41:42 +00:00
|
|
|
fsutil.DirFileOperations `state:"nosave"`
|
|
|
|
fsutil.FileUseInodeUnstableAttr `state:"nosave"`
|
2019-01-15 04:33:29 +00:00
|
|
|
|
|
|
|
isInfoFile bool
|
|
|
|
|
|
|
|
t *kernel.Task
|
|
|
|
}
|
|
|
|
|
|
|
|
var _ fs.FileOperations = (*fdDirFile)(nil)
|
|
|
|
|
|
|
|
// Readdir implements fs.FileOperations.Readdir.
|
|
|
|
func (f *fdDirFile) Readdir(ctx context.Context, file *fs.File, ser fs.DentrySerializer) (int64, error) {
|
|
|
|
dirCtx := &fs.DirCtx{
|
|
|
|
Serializer: ser,
|
|
|
|
}
|
|
|
|
typ := fs.RegularFile
|
|
|
|
if f.isInfoFile {
|
|
|
|
typ = fs.Symlink
|
|
|
|
}
|
|
|
|
return readDescriptors(f.t, dirCtx, file.Offset(), func(fd int) fs.DentAttr {
|
|
|
|
return fs.GenericDentAttr(typ, device.ProcDevice)
|
2018-04-27 17:37:02 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
// fdInfoDir implements /proc/TID/fdinfo. It embeds an fdDir, but overrides
|
|
|
|
// Lookup and Readdir.
|
2018-08-02 17:41:44 +00:00
|
|
|
//
|
|
|
|
// +stateify savable
|
2018-04-27 17:37:02 +00:00
|
|
|
type fdInfoDir struct {
|
|
|
|
ramfs.Dir
|
|
|
|
|
|
|
|
t *kernel.Task
|
|
|
|
}
|
|
|
|
|
|
|
|
// newFdInfoDir creates a new fdInfoDir.
|
|
|
|
func newFdInfoDir(t *kernel.Task, msrc *fs.MountSource) *fs.Inode {
|
2019-01-15 04:33:29 +00:00
|
|
|
fdid := &fdInfoDir{
|
|
|
|
Dir: *ramfs.NewDir(t, nil, fs.RootOwner, fs.FilePermsFromMode(0500)),
|
|
|
|
t: t,
|
|
|
|
}
|
2019-06-14 01:39:43 +00:00
|
|
|
return newProcInode(t, fdid, msrc, fs.SpecialDirectory, t)
|
2018-04-27 17:37:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Lookup loads an fd in /proc/TID/fdinfo into a Dirent.
|
|
|
|
func (fdid *fdInfoDir) Lookup(ctx context.Context, dir *fs.Inode, p string) (*fs.Dirent, error) {
|
2019-01-15 04:33:29 +00:00
|
|
|
inode, err := walkDescriptors(fdid.t, p, func(file *fs.File, fdFlags kernel.FDFlags) *fs.Inode {
|
2019-04-29 21:03:04 +00:00
|
|
|
// TODO(b/121266871): Using a static inode here means that the
|
2019-01-15 04:33:29 +00:00
|
|
|
// data can be out-of-date if, for instance, the flags on the
|
|
|
|
// FD change before we read this file. We should switch to
|
|
|
|
// generating the data on Read(). Also, we should include pos,
|
|
|
|
// locks, and other data. For now we only have flags.
|
|
|
|
// See https://www.kernel.org/doc/Documentation/filesystems/proc.txt
|
|
|
|
flags := file.Flags().ToLinux() | fdFlags.ToLinuxFileFlags()
|
2019-04-26 18:08:37 +00:00
|
|
|
file.DecRef()
|
2019-01-15 04:33:29 +00:00
|
|
|
contents := []byte(fmt.Sprintf("flags:\t0%o\n", flags))
|
|
|
|
return newStaticProcInode(ctx, dir.MountSource, contents)
|
2018-04-27 17:37:02 +00:00
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2019-06-14 01:39:43 +00:00
|
|
|
return fs.NewDirent(ctx, inode, p), nil
|
2018-04-27 17:37:02 +00:00
|
|
|
}
|
|
|
|
|
2019-01-15 04:33:29 +00:00
|
|
|
// GetFile implements fs.FileOperations.GetFile.
|
|
|
|
func (fdid *fdInfoDir) GetFile(ctx context.Context, dirent *fs.Dirent, flags fs.FileFlags) (*fs.File, error) {
|
|
|
|
fops := &fdDirFile{
|
|
|
|
isInfoFile: true,
|
|
|
|
t: fdid.t,
|
|
|
|
}
|
|
|
|
return fs.NewFile(ctx, dirent, flags, fops), nil
|
2018-04-27 17:37:02 +00:00
|
|
|
}
|