Fixes for procfs

- Return ENOENT for /proc/[pid]/task if task is zoombied or terminated
- Allow directory to be Seek() to the end
- Construct synthetic files for /proc/[pid]/ns/*
- Changed GenericDirectoryFD.Init to not register with FileDescription,
  otherwise other implementation cannot change behavior.

Updates #1195,1193

PiperOrigin-RevId: 308294649
This commit is contained in:
Fabricio Voznika
2020-04-24 11:45:19 -07:00
committed by gVisor bot
parent 1b88c63b3e
commit 2cc0fd42f4
15 changed files with 192 additions and 36 deletions
+4 -2
View File
@@ -161,8 +161,10 @@ func (i *rootInode) masterClose(t *Terminal) {
// Open implements kernfs.Inode.Open.
func (i *rootInode) Open(rp *vfs.ResolvingPath, vfsd *vfs.Dentry, opts vfs.OpenOptions) (*vfs.FileDescription, error) {
fd := &kernfs.GenericDirectoryFD{}
fd.Init(rp.Mount(), vfsd, &i.OrderedChildren, &opts)
fd, err := kernfs.NewGenericDirectoryFD(rp.Mount(), vfsd, &i.OrderedChildren, &opts)
if err != nil {
return nil, err
}
return fd.VFSFileDescription(), nil
}
+23 -5
View File
@@ -15,6 +15,8 @@
package kernfs
import (
"math"
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
@@ -43,15 +45,27 @@ type GenericDirectoryFD struct {
off int64
}
// Init initializes a GenericDirectoryFD.
func (fd *GenericDirectoryFD) Init(m *vfs.Mount, d *vfs.Dentry, children *OrderedChildren, opts *vfs.OpenOptions) error {
// NewGenericDirectoryFD creates a new GenericDirectoryFD and returns its
// dentry.
func NewGenericDirectoryFD(m *vfs.Mount, d *vfs.Dentry, children *OrderedChildren, opts *vfs.OpenOptions) (*GenericDirectoryFD, error) {
fd := &GenericDirectoryFD{}
if err := fd.Init(children, opts); err != nil {
return nil, err
}
if err := fd.vfsfd.Init(fd, opts.Flags, m, d, &vfs.FileDescriptionOptions{}); err != nil {
return nil, err
}
return fd, nil
}
// Init initializes a GenericDirectoryFD. Use it when overriding
// GenericDirectoryFD. Caller must call fd.VFSFileDescription.Init() with the
// correct implementation.
func (fd *GenericDirectoryFD) Init(children *OrderedChildren, opts *vfs.OpenOptions) error {
if vfs.AccessTypesForOpenFlags(opts)&vfs.MayWrite != 0 {
// Can't open directories for writing.
return syserror.EISDIR
}
if err := fd.vfsfd.Init(fd, opts.Flags, m, d, &vfs.FileDescriptionOptions{}); err != nil {
return err
}
fd.children = children
return nil
}
@@ -187,6 +201,10 @@ func (fd *GenericDirectoryFD) Seek(ctx context.Context, offset int64, whence int
// Use offset as given.
case linux.SEEK_CUR:
offset += fd.off
case linux.SEEK_END:
// TODO(gvisor.dev/issue/1193): This can prevent new files from showing up
// if they are added after SEEK_END.
offset = math.MaxInt64
default:
return 0, syserror.EINVAL
}
+2 -2
View File
@@ -82,7 +82,7 @@ afterSymlink:
}
// Resolve any symlink at current path component.
if rp.ShouldFollowSymlink() && next.isSymlink() {
targetVD, targetPathname, err := next.inode.Getlink(ctx)
targetVD, targetPathname, err := next.inode.Getlink(ctx, rp.Mount())
if err != nil {
return nil, err
}
@@ -477,7 +477,7 @@ afterTrailingSymlink:
}
child := childVFSD.Impl().(*Dentry)
if rp.ShouldFollowSymlink() && child.isSymlink() {
targetVD, targetPathname, err := child.inode.Getlink(ctx)
targetVD, targetPathname, err := child.inode.Getlink(ctx, rp.Mount())
if err != nil {
return nil, err
}
+5 -3
View File
@@ -182,7 +182,7 @@ func (InodeNotSymlink) Readlink(context.Context) (string, error) {
}
// Getlink implements Inode.Getlink.
func (InodeNotSymlink) Getlink(context.Context) (vfs.VirtualDentry, string, error) {
func (InodeNotSymlink) Getlink(context.Context, *vfs.Mount) (vfs.VirtualDentry, string, error) {
return vfs.VirtualDentry{}, "", syserror.EINVAL
}
@@ -568,8 +568,10 @@ func (s *StaticDirectory) Init(creds *auth.Credentials, ino uint64, perm linux.F
// Open implements kernfs.Inode.
func (s *StaticDirectory) Open(rp *vfs.ResolvingPath, vfsd *vfs.Dentry, opts vfs.OpenOptions) (*vfs.FileDescription, error) {
fd := &GenericDirectoryFD{}
fd.Init(rp.Mount(), vfsd, &s.OrderedChildren, &opts)
fd, err := NewGenericDirectoryFD(rp.Mount(), vfsd, &s.OrderedChildren, &opts)
if err != nil {
return nil, err
}
return fd.VFSFileDescription(), nil
}
+1 -1
View File
@@ -439,5 +439,5 @@ type inodeSymlink interface {
//
// - If the inode is not a symlink, Getlink returns (zero-value
// VirtualDentry, "", EINVAL).
Getlink(ctx context.Context) (vfs.VirtualDentry, string, error)
Getlink(ctx context.Context, mnt *vfs.Mount) (vfs.VirtualDentry, string, error)
}
+6 -4
View File
@@ -117,8 +117,8 @@ func (fs *filesystem) newReadonlyDir(creds *auth.Credentials, mode linux.FileMod
}
func (d *readonlyDir) Open(rp *vfs.ResolvingPath, vfsd *vfs.Dentry, opts vfs.OpenOptions) (*vfs.FileDescription, error) {
fd := &kernfs.GenericDirectoryFD{}
if err := fd.Init(rp.Mount(), vfsd, &d.OrderedChildren, &opts); err != nil {
fd, err := kernfs.NewGenericDirectoryFD(rp.Mount(), vfsd, &d.OrderedChildren, &opts)
if err != nil {
return nil, err
}
return fd.VFSFileDescription(), nil
@@ -147,8 +147,10 @@ func (fs *filesystem) newDir(creds *auth.Credentials, mode linux.FileMode, conte
}
func (d *dir) Open(rp *vfs.ResolvingPath, vfsd *vfs.Dentry, opts vfs.OpenOptions) (*vfs.FileDescription, error) {
fd := &kernfs.GenericDirectoryFD{}
fd.Init(rp.Mount(), vfsd, &d.OrderedChildren, &opts)
fd, err := kernfs.NewGenericDirectoryFD(rp.Mount(), vfsd, &d.OrderedChildren, &opts)
if err != nil {
return nil, err
}
return fd.VFSFileDescription(), nil
}
+1 -1
View File
@@ -56,7 +56,7 @@ func (s *StaticSymlink) Readlink(_ context.Context) (string, error) {
}
// Getlink implements Inode.Getlink.
func (s *StaticSymlink) Getlink(_ context.Context) (vfs.VirtualDentry, string, error) {
func (s *StaticSymlink) Getlink(context.Context, *vfs.Mount) (vfs.VirtualDentry, string, error) {
return vfs.VirtualDentry{}, s.target, nil
}
+47 -2
View File
@@ -88,6 +88,9 @@ func (i *subtasksInode) IterDirents(ctx context.Context, cb vfs.IterDirentsCallb
if len(tasks) == 0 {
return offset, syserror.ENOENT
}
if relOffset >= int64(len(tasks)) {
return offset, nil
}
tids := make([]int, 0, len(tasks))
for _, tid := range tasks {
@@ -110,10 +113,52 @@ func (i *subtasksInode) IterDirents(ctx context.Context, cb vfs.IterDirentsCallb
return offset, nil
}
type subtasksFD struct {
kernfs.GenericDirectoryFD
task *kernel.Task
}
func (fd *subtasksFD) IterDirents(ctx context.Context, cb vfs.IterDirentsCallback) error {
if fd.task.ExitState() >= kernel.TaskExitZombie {
return syserror.ENOENT
}
return fd.GenericDirectoryFD.IterDirents(ctx, cb)
}
// Seek implements vfs.FileDecriptionImpl.Seek.
func (fd *subtasksFD) Seek(ctx context.Context, offset int64, whence int32) (int64, error) {
if fd.task.ExitState() >= kernel.TaskExitZombie {
return 0, syserror.ENOENT
}
return fd.GenericDirectoryFD.Seek(ctx, offset, whence)
}
// Stat implements vfs.FileDescriptionImpl.Stat.
func (fd *subtasksFD) Stat(ctx context.Context, opts vfs.StatOptions) (linux.Statx, error) {
if fd.task.ExitState() >= kernel.TaskExitZombie {
return linux.Statx{}, syserror.ENOENT
}
return fd.GenericDirectoryFD.Stat(ctx, opts)
}
// SetStat implements vfs.FileDescriptionImpl.SetStat.
func (fd *subtasksFD) SetStat(ctx context.Context, opts vfs.SetStatOptions) error {
if fd.task.ExitState() >= kernel.TaskExitZombie {
return syserror.ENOENT
}
return fd.GenericDirectoryFD.SetStat(ctx, opts)
}
// Open implements kernfs.Inode.
func (i *subtasksInode) Open(rp *vfs.ResolvingPath, vfsd *vfs.Dentry, opts vfs.OpenOptions) (*vfs.FileDescription, error) {
fd := &kernfs.GenericDirectoryFD{}
fd.Init(rp.Mount(), vfsd, &i.OrderedChildren, &opts)
fd := &subtasksFD{task: i.task}
if err := fd.Init(&i.OrderedChildren, &opts); err != nil {
return nil, err
}
if err := fd.VFSFileDescription().Init(fd, opts.Flags, rp.Mount(), vfsd, &vfs.FileDescriptionOptions{}); err != nil {
return nil, err
}
return fd.VFSFileDescription(), nil
}
+5 -2
View File
@@ -44,6 +44,7 @@ type taskInode struct {
var _ kernfs.Inode = (*taskInode)(nil)
func newTaskInode(inoGen InoGenerator, task *kernel.Task, pidns *kernel.PIDNamespace, isThreadGroup bool, cgroupControllers map[string]string) *kernfs.Dentry {
// TODO(gvisor.dev/issue/164): Fail with ESRCH if task exited.
contents := map[string]*kernfs.Dentry{
"auxv": newTaskOwnedFile(task, inoGen.NextIno(), 0444, &auxvData{task: task}),
"cmdline": newTaskOwnedFile(task, inoGen.NextIno(), 0444, &cmdlineData{task: task, arg: cmdlineDataArg}),
@@ -102,8 +103,10 @@ func (i *taskInode) Valid(ctx context.Context) bool {
// Open implements kernfs.Inode.
func (i *taskInode) Open(rp *vfs.ResolvingPath, vfsd *vfs.Dentry, opts vfs.OpenOptions) (*vfs.FileDescription, error) {
fd := &kernfs.GenericDirectoryFD{}
fd.Init(rp.Mount(), vfsd, &i.OrderedChildren, &opts)
fd, err := kernfs.NewGenericDirectoryFD(rp.Mount(), vfsd, &i.OrderedChildren, &opts)
if err != nil {
return nil, err
}
return fd.VFSFileDescription(), nil
}
+9 -5
View File
@@ -143,8 +143,10 @@ func (i *fdDirInode) Lookup(ctx context.Context, name string) (*vfs.Dentry, erro
// Open implements kernfs.Inode.
func (i *fdDirInode) Open(rp *vfs.ResolvingPath, vfsd *vfs.Dentry, opts vfs.OpenOptions) (*vfs.FileDescription, error) {
fd := &kernfs.GenericDirectoryFD{}
fd.Init(rp.Mount(), vfsd, &i.OrderedChildren, &opts)
fd, err := kernfs.NewGenericDirectoryFD(rp.Mount(), vfsd, &i.OrderedChildren, &opts)
if err != nil {
return nil, err
}
return fd.VFSFileDescription(), nil
}
@@ -207,7 +209,7 @@ func (s *fdSymlink) Readlink(ctx context.Context) (string, error) {
return s.task.Kernel().VFS().PathnameWithDeleted(ctx, root, file.VirtualDentry())
}
func (s *fdSymlink) Getlink(ctx context.Context) (vfs.VirtualDentry, string, error) {
func (s *fdSymlink) Getlink(ctx context.Context, mnt *vfs.Mount) (vfs.VirtualDentry, string, error) {
file, _ := getTaskFD(s.task, s.fd)
if file == nil {
return vfs.VirtualDentry{}, "", syserror.ENOENT
@@ -268,8 +270,10 @@ func (i *fdInfoDirInode) Lookup(ctx context.Context, name string) (*vfs.Dentry,
// Open implements kernfs.Inode.
func (i *fdInfoDirInode) Open(rp *vfs.ResolvingPath, vfsd *vfs.Dentry, opts vfs.OpenOptions) (*vfs.FileDescription, error) {
fd := &kernfs.GenericDirectoryFD{}
fd.Init(rp.Mount(), vfsd, &i.OrderedChildren, &opts)
fd, err := kernfs.NewGenericDirectoryFD(rp.Mount(), vfsd, &i.OrderedChildren, &opts)
if err != nil {
return nil, err
}
return fd.VFSFileDescription(), nil
}
+73 -3
View File
@@ -622,7 +622,7 @@ func (s *exeSymlink) Readlink(ctx context.Context) (string, error) {
}
// Getlink implements kernfs.Inode.Getlink.
func (s *exeSymlink) Getlink(ctx context.Context) (vfs.VirtualDentry, string, error) {
func (s *exeSymlink) Getlink(ctx context.Context, _ *vfs.Mount) (vfs.VirtualDentry, string, error) {
if !kernel.ContextCanTrace(ctx, s.task, false) {
return vfs.VirtualDentry{}, "", syserror.EACCES
}
@@ -754,9 +754,79 @@ func (s *namespaceSymlink) Readlink(ctx context.Context) (string, error) {
}
// Getlink implements Inode.Getlink.
func (s *namespaceSymlink) Getlink(ctx context.Context) (vfs.VirtualDentry, string, error) {
func (s *namespaceSymlink) Getlink(ctx context.Context, mnt *vfs.Mount) (vfs.VirtualDentry, string, error) {
if err := checkTaskState(s.task); err != nil {
return vfs.VirtualDentry{}, "", err
}
return s.StaticSymlink.Getlink(ctx)
// Create a synthetic inode to represent the namespace.
dentry := &kernfs.Dentry{}
dentry.Init(&namespaceInode{})
vd := vfs.MakeVirtualDentry(mnt, dentry.VFSDentry())
vd.IncRef()
dentry.DecRef()
return vd, "", nil
}
// namespaceInode is a synthetic inode created to represent a namespace in
// /proc/[pid]/ns/*.
type namespaceInode struct {
kernfs.InodeAttrs
kernfs.InodeNoopRefCount
kernfs.InodeNotDirectory
kernfs.InodeNotSymlink
}
var _ kernfs.Inode = (*namespaceInode)(nil)
// Init initializes a namespace inode.
func (i *namespaceInode) Init(creds *auth.Credentials, ino uint64, perm linux.FileMode) {
if perm&^linux.PermissionsMask != 0 {
panic(fmt.Sprintf("Only permission mask must be set: %x", perm&linux.PermissionsMask))
}
i.InodeAttrs.Init(creds, ino, linux.ModeRegular|perm)
}
// Open implements Inode.Open.
func (i *namespaceInode) Open(rp *vfs.ResolvingPath, vfsd *vfs.Dentry, opts vfs.OpenOptions) (*vfs.FileDescription, error) {
fd := &namespaceFD{inode: i}
i.IncRef()
if err := fd.vfsfd.Init(fd, opts.Flags, rp.Mount(), vfsd, &vfs.FileDescriptionOptions{}); err != nil {
return nil, err
}
return &fd.vfsfd, nil
}
// namespace FD is a synthetic file that represents a namespace in
// /proc/[pid]/ns/*.
type namespaceFD struct {
vfs.FileDescriptionDefaultImpl
vfsfd vfs.FileDescription
inode *namespaceInode
}
var _ vfs.FileDescriptionImpl = (*namespaceFD)(nil)
// Stat implements FileDescriptionImpl.
func (fd *namespaceFD) Stat(ctx context.Context, opts vfs.StatOptions) (linux.Statx, error) {
vfs := fd.vfsfd.VirtualDentry().Mount().Filesystem()
return fd.inode.Stat(vfs, opts)
}
// SetStat implements FileDescriptionImpl.
func (fd *namespaceFD) SetStat(ctx context.Context, opts vfs.SetStatOptions) error {
vfs := fd.vfsfd.VirtualDentry().Mount().Filesystem()
creds := auth.CredentialsFromContext(ctx)
return fd.inode.SetStat(ctx, vfs, creds, opts)
}
// Release implements FileDescriptionImpl.
func (fd *namespaceFD) Release() {
fd.inode.DecRef()
}
// OnClose implements FileDescriptionImpl.
func (*namespaceFD) OnClose(context.Context) error {
return nil
}
+4 -2
View File
@@ -202,8 +202,10 @@ func (i *tasksInode) IterDirents(ctx context.Context, cb vfs.IterDirentsCallback
// Open implements kernfs.Inode.
func (i *tasksInode) Open(rp *vfs.ResolvingPath, vfsd *vfs.Dentry, opts vfs.OpenOptions) (*vfs.FileDescription, error) {
fd := &kernfs.GenericDirectoryFD{}
fd.Init(rp.Mount(), vfsd, &i.OrderedChildren, &opts)
fd, err := kernfs.NewGenericDirectoryFD(rp.Mount(), vfsd, &i.OrderedChildren, &opts)
if err != nil {
return nil, err
}
return fd.VFSFileDescription(), nil
}
+2 -2
View File
@@ -63,7 +63,7 @@ func (s *selfSymlink) Readlink(ctx context.Context) (string, error) {
return strconv.FormatUint(uint64(tgid), 10), nil
}
func (s *selfSymlink) Getlink(ctx context.Context) (vfs.VirtualDentry, string, error) {
func (s *selfSymlink) Getlink(ctx context.Context, _ *vfs.Mount) (vfs.VirtualDentry, string, error) {
target, err := s.Readlink(ctx)
return vfs.VirtualDentry{}, target, err
}
@@ -106,7 +106,7 @@ func (s *threadSelfSymlink) Readlink(ctx context.Context) (string, error) {
return fmt.Sprintf("%d/task/%d", tgid, tid), nil
}
func (s *threadSelfSymlink) Getlink(ctx context.Context) (vfs.VirtualDentry, string, error) {
func (s *threadSelfSymlink) Getlink(ctx context.Context, _ *vfs.Mount) (vfs.VirtualDentry, string, error) {
target, err := s.Readlink(ctx)
return vfs.VirtualDentry{}, target, err
}
+2 -2
View File
@@ -106,8 +106,8 @@ func (*dir) SetStat(context.Context, *vfs.Filesystem, *auth.Credentials, vfs.Set
// Open implements kernfs.Inode.Open.
func (d *dir) Open(rp *vfs.ResolvingPath, vfsd *vfs.Dentry, opts vfs.OpenOptions) (*vfs.FileDescription, error) {
fd := &kernfs.GenericDirectoryFD{}
if err := fd.Init(rp.Mount(), vfsd, &d.OrderedChildren, &opts); err != nil {
fd, err := kernfs.NewGenericDirectoryFD(rp.Mount(), vfsd, &d.OrderedChildren, &opts)
if err != nil {
return nil, err
}
return fd.VFSFileDescription(), nil
+8
View File
@@ -797,6 +797,14 @@ type VirtualDentry struct {
dentry *Dentry
}
// MakeVirtualDentry creates a VirtualDentry.
func MakeVirtualDentry(mount *Mount, dentry *Dentry) VirtualDentry {
return VirtualDentry{
mount: mount,
dentry: dentry,
}
}
// Ok returns true if vd is not empty. It does not require that a reference is
// held.
func (vd VirtualDentry) Ok() bool {