mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Correctly implement magic symlinks in VFS2 procfs.
Updates #1195 PiperOrigin-RevId: 305143567
This commit is contained in:
+17
-11
@@ -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
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user