Correctly implement magic symlinks in VFS2 procfs.

Updates #1195

PiperOrigin-RevId: 305143567
This commit is contained in:
Jamie Liu
2020-04-06 16:32:37 -07:00
committed by gVisor bot
parent f332a864e8
commit dd98fdd5be
8 changed files with 99 additions and 24 deletions
+17 -11
View File
@@ -26,22 +26,22 @@ import (
"gvisor.dev/gvisor/pkg/usermem"
)
// fsFile implements File interface over vfs.FileDescription.
// VFSFile implements File interface over vfs.FileDescription.
//
// +stateify savable
type vfsFile struct {
type VFSFile struct {
file *vfs.FileDescription
}
var _ File = (*vfsFile)(nil)
var _ File = (*VFSFile)(nil)
// NewVFSFile creates a new File over fs.File.
func NewVFSFile(file *vfs.FileDescription) File {
return &vfsFile{file: file}
return &VFSFile{file: file}
}
// PathnameWithDeleted implements File.
func (f *vfsFile) PathnameWithDeleted(ctx context.Context) string {
func (f *VFSFile) PathnameWithDeleted(ctx context.Context) string {
root := vfs.RootFromContext(ctx)
defer root.DecRef()
@@ -51,7 +51,7 @@ func (f *vfsFile) PathnameWithDeleted(ctx context.Context) string {
}
// ReadFull implements File.
func (f *vfsFile) ReadFull(ctx context.Context, dst usermem.IOSequence, offset int64) (int64, error) {
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{})
@@ -67,12 +67,12 @@ func (f *vfsFile) ReadFull(ctx context.Context, dst usermem.IOSequence, offset i
}
// ConfigureMMap implements File.
func (f *vfsFile) ConfigureMMap(ctx context.Context, opts *memmap.MMapOpts) error {
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) {
func (f *VFSFile) Type(ctx context.Context) (linux.FileMode, error) {
stat, err := f.file.Stat(ctx, vfs.StatOptions{})
if err != nil {
return 0, err
@@ -81,15 +81,21 @@ func (f *vfsFile) Type(ctx context.Context) (linux.FileMode, error) {
}
// IncRef implements File.
func (f *vfsFile) IncRef() {
func (f *VFSFile) IncRef() {
f.file.IncRef()
}
// DecRef implements File.
func (f *vfsFile) DecRef() {
func (f *VFSFile) DecRef() {
f.file.DecRef()
}
// 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
@@ -132,5 +138,5 @@ func (l *vfsLookup) OpenPath(ctx context.Context, pathname string, opts vfs.Open
if err != nil {
return nil, err
}
return &vfsFile{file: fd}, nil
return &VFSFile{file: fd}, nil
}
+24 -12
View File
@@ -79,16 +79,22 @@ afterSymlink:
}
// Resolve any symlink at current path component.
if rp.ShouldFollowSymlink() && next.isSymlink() {
// TODO: VFS2 needs something extra for /proc/[pid]/fd/ "magic symlinks".
target, err := next.inode.Readlink(ctx)
targetVD, targetPathname, err := next.inode.Getlink(ctx)
if err != nil {
return nil, err
}
if err := rp.HandleSymlink(target); err != nil {
return nil, err
if targetVD.Ok() {
err := rp.HandleJump(targetVD)
targetVD.DecRef()
if err != nil {
return nil, err
}
} else {
if err := rp.HandleSymlink(targetPathname); err != nil {
return nil, err
}
}
goto afterSymlink
}
rp.Advance()
return &next.vfsd, nil
@@ -470,19 +476,25 @@ afterTrailingSymlink:
}
childDentry := childVFSD.Impl().(*Dentry)
childInode := childDentry.inode
if rp.ShouldFollowSymlink() {
if childDentry.isSymlink() {
target, err := childInode.Readlink(ctx)
if rp.ShouldFollowSymlink() && childDentry.isSymlink() {
targetVD, targetPathname, err := childInode.Getlink(ctx)
if err != nil {
return nil, err
}
if targetVD.Ok() {
err := rp.HandleJump(targetVD)
targetVD.DecRef()
if err != nil {
return nil, err
}
if err := rp.HandleSymlink(target); err != nil {
} else {
if err := rp.HandleSymlink(targetPathname); err != nil {
return nil, err
}
// rp.Final() may no longer be true since we now need to resolve the
// symlink target.
goto afterTrailingSymlink
}
// rp.Final() may no longer be true since we now need to resolve the
// symlink target.
goto afterTrailingSymlink
}
if err := childInode.CheckPermissions(ctx, rp.Credentials(), ats); err != nil {
return nil, err
@@ -181,6 +181,11 @@ func (InodeNotSymlink) Readlink(context.Context) (string, error) {
return "", syserror.EINVAL
}
// Getlink implements Inode.Getlink.
func (InodeNotSymlink) Getlink(context.Context) (vfs.VirtualDentry, string, error) {
return vfs.VirtualDentry{}, "", syserror.EINVAL
}
// InodeAttrs partially implements the Inode interface, specifically the
// inodeMetadata sub interface. InodeAttrs provides functionality related to
// inode attributes.
+15 -1
View File
@@ -414,7 +414,21 @@ type inodeDynamicLookup interface {
}
type inodeSymlink interface {
// Readlink resolves the target of a symbolic link. If an inode is not a
// Readlink returns the target of a symbolic link. If an inode is not a
// symlink, the implementation should return EINVAL.
Readlink(ctx context.Context) (string, error)
// Getlink returns the target of a symbolic link, as used by path
// resolution:
//
// - If the inode is a "magic link" (a link whose target is most accurately
// represented as a VirtualDentry), Getlink returns (ok VirtualDentry, "",
// nil). A reference is taken on the returned VirtualDentry.
//
// - If the inode is an ordinary symlink, Getlink returns (zero-value
// VirtualDentry, symlink target, nil).
//
// - If the inode is not a symlink, Getlink returns (zero-value
// VirtualDentry, "", EINVAL).
Getlink(ctx context.Context) (vfs.VirtualDentry, string, error)
}
+5
View File
@@ -55,6 +55,11 @@ func (s *StaticSymlink) Readlink(_ context.Context) (string, error) {
return s.target, nil
}
// Getlink implements Inode.Getlink.
func (s *StaticSymlink) Getlink(_ context.Context) (vfs.VirtualDentry, string, error) {
return vfs.VirtualDentry{}, s.target, nil
}
// SetStat implements Inode.SetStat not allowing inode attributes to be changed.
func (*StaticSymlink) SetStat(context.Context, *vfs.Filesystem, *auth.Credentials, vfs.SetStatOptions) error {
return syserror.EPERM
+6
View File
@@ -196,6 +196,12 @@ func (s *fdSymlink) Readlink(ctx context.Context) (string, error) {
return vfsObj.PathnameWithDeleted(ctx, root, s.file.VirtualDentry())
}
func (s *fdSymlink) Getlink(ctx context.Context) (vfs.VirtualDentry, string, error) {
vd := s.file.VirtualDentry()
vd.IncRef()
return vd, "", nil
}
func (s *fdSymlink) DecRef() {
s.AtomicRefCount.DecRefWithDestructor(func() {
s.Destroy()
+17
View File
@@ -610,6 +610,23 @@ func (s *exeSymlink) Readlink(ctx context.Context) (string, error) {
return exec.PathnameWithDeleted(ctx), nil
}
// Getlink implements kernfs.Inode.Getlink.
func (s *exeSymlink) Getlink(ctx context.Context) (vfs.VirtualDentry, string, error) {
if !kernel.ContextCanTrace(ctx, s.task, false) {
return vfs.VirtualDentry{}, "", syserror.EACCES
}
exec, err := s.executable()
if err != nil {
return vfs.VirtualDentry{}, "", err
}
defer exec.DecRef()
vd := exec.(*fsbridge.VFSFile).FileDescription().VirtualDentry()
vd.IncRef()
return vd, "", nil
}
func (s *exeSymlink) executable() (file fsbridge.File, err error) {
s.task.WithMuLocked(func(t *kernel.Task) {
mm := t.MemoryManager()
+10
View File
@@ -63,6 +63,11 @@ 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) {
target, err := s.Readlink(ctx)
return vfs.VirtualDentry{}, target, err
}
// SetStat implements Inode.SetStat not allowing inode attributes to be changed.
func (*selfSymlink) SetStat(context.Context, *vfs.Filesystem, *auth.Credentials, vfs.SetStatOptions) error {
return syserror.EPERM
@@ -101,6 +106,11 @@ 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) {
target, err := s.Readlink(ctx)
return vfs.VirtualDentry{}, target, err
}
// SetStat implements Inode.SetStat not allowing inode attributes to be changed.
func (*threadSelfSymlink) SetStat(context.Context, *vfs.Filesystem, *auth.Credentials, vfs.SetStatOptions) error {
return syserror.EPERM