mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Add /proc/[pid]/cwd
PiperOrigin-RevId: 334478850
This commit is contained in:
committed by
gVisor bot
parent
7fbb45e8ed
commit
4a428b13b2
@@ -84,6 +84,7 @@ func (p *proc) newTaskDir(t *kernel.Task, msrc *fs.MountSource, isThreadGroup bo
|
||||
"auxv": newAuxvec(t, msrc),
|
||||
"cmdline": newExecArgInode(t, msrc, cmdlineExecArg),
|
||||
"comm": newComm(t, msrc),
|
||||
"cwd": newCwd(t, msrc),
|
||||
"environ": newExecArgInode(t, msrc, environExecArg),
|
||||
"exe": newExe(t, msrc),
|
||||
"fd": newFdDir(t, msrc),
|
||||
@@ -300,6 +301,49 @@ func (e *exe) Readlink(ctx context.Context, inode *fs.Inode) (string, error) {
|
||||
return exec.PathnameWithDeleted(ctx), nil
|
||||
}
|
||||
|
||||
// cwd is an fs.InodeOperations symlink for the /proc/PID/cwd file.
|
||||
//
|
||||
// +stateify savable
|
||||
type cwd struct {
|
||||
ramfs.Symlink
|
||||
|
||||
t *kernel.Task
|
||||
}
|
||||
|
||||
func newCwd(t *kernel.Task, msrc *fs.MountSource) *fs.Inode {
|
||||
cwdSymlink := &cwd{
|
||||
Symlink: *ramfs.NewSymlink(t, fs.RootOwner, ""),
|
||||
t: t,
|
||||
}
|
||||
return newProcInode(t, cwdSymlink, msrc, fs.Symlink, t)
|
||||
}
|
||||
|
||||
// Readlink implements fs.InodeOperations.
|
||||
func (e *cwd) Readlink(ctx context.Context, inode *fs.Inode) (string, error) {
|
||||
if !kernel.ContextCanTrace(ctx, e.t, false) {
|
||||
return "", syserror.EACCES
|
||||
}
|
||||
if err := checkTaskState(e.t); err != nil {
|
||||
return "", err
|
||||
}
|
||||
cwd := e.t.FSContext().WorkingDirectory()
|
||||
if cwd == nil {
|
||||
// It could have raced with process deletion.
|
||||
return "", syserror.ESRCH
|
||||
}
|
||||
defer cwd.DecRef(ctx)
|
||||
|
||||
root := fs.RootFromContext(ctx)
|
||||
if root == nil {
|
||||
// It could have raced with process deletion.
|
||||
return "", syserror.ESRCH
|
||||
}
|
||||
defer root.DecRef(ctx)
|
||||
|
||||
name, _ := cwd.FullName(root)
|
||||
return name, nil
|
||||
}
|
||||
|
||||
// namespaceSymlink represents a symlink in the namespacefs, such as the files
|
||||
// in /proc/<pid>/ns.
|
||||
//
|
||||
|
||||
@@ -53,6 +53,7 @@ func (fs *filesystem) newTaskInode(task *kernel.Task, pidns *kernel.PIDNamespace
|
||||
"auxv": fs.newTaskOwnedFile(task, fs.NextIno(), 0444, &auxvData{task: task}),
|
||||
"cmdline": fs.newTaskOwnedFile(task, fs.NextIno(), 0444, &cmdlineData{task: task, arg: cmdlineDataArg}),
|
||||
"comm": fs.newComm(task, fs.NextIno(), 0444),
|
||||
"cwd": fs.newCwdSymlink(task, fs.NextIno()),
|
||||
"environ": fs.newTaskOwnedFile(task, fs.NextIno(), 0444, &cmdlineData{task: task, arg: environDataArg}),
|
||||
"exe": fs.newExeSymlink(task, fs.NextIno()),
|
||||
"fd": fs.newFDDirInode(task),
|
||||
|
||||
@@ -669,18 +669,22 @@ func (fs *filesystem) newExeSymlink(task *kernel.Task, ino uint64) *kernfs.Dentr
|
||||
|
||||
// Readlink implements kernfs.Inode.Readlink.
|
||||
func (s *exeSymlink) Readlink(ctx context.Context, _ *vfs.Mount) (string, error) {
|
||||
if !kernel.ContextCanTrace(ctx, s.task, false) {
|
||||
return "", syserror.EACCES
|
||||
}
|
||||
|
||||
// Pull out the executable for /proc/[pid]/exe.
|
||||
exec, err := s.executable()
|
||||
exec, _, err := s.Getlink(ctx, nil)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer exec.DecRef(ctx)
|
||||
|
||||
return exec.PathnameWithDeleted(ctx), nil
|
||||
root := vfs.RootFromContext(ctx)
|
||||
if !root.Ok() {
|
||||
// It could have raced with process deletion.
|
||||
return "", syserror.ESRCH
|
||||
}
|
||||
defer root.DecRef(ctx)
|
||||
|
||||
vfsObj := exec.Mount().Filesystem().VirtualFilesystem()
|
||||
name, _ := vfsObj.PathnameWithDeleted(ctx, root, exec)
|
||||
return name, nil
|
||||
}
|
||||
|
||||
// Getlink implements kernfs.Inode.Getlink.
|
||||
@@ -688,23 +692,12 @@ func (s *exeSymlink) Getlink(ctx context.Context, _ *vfs.Mount) (vfs.VirtualDent
|
||||
if !kernel.ContextCanTrace(ctx, s.task, false) {
|
||||
return vfs.VirtualDentry{}, "", syserror.EACCES
|
||||
}
|
||||
|
||||
exec, err := s.executable()
|
||||
if err != nil {
|
||||
if err := checkTaskState(s.task); err != nil {
|
||||
return vfs.VirtualDentry{}, "", err
|
||||
}
|
||||
defer exec.DecRef(ctx)
|
||||
|
||||
vd := exec.(*fsbridge.VFSFile).FileDescription().VirtualDentry()
|
||||
vd.IncRef()
|
||||
return vd, "", nil
|
||||
}
|
||||
|
||||
func (s *exeSymlink) executable() (file fsbridge.File, err error) {
|
||||
if err := checkTaskState(s.task); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var err error
|
||||
var exec fsbridge.File
|
||||
s.task.WithMuLocked(func(t *kernel.Task) {
|
||||
mm := t.MemoryManager()
|
||||
if mm == nil {
|
||||
@@ -715,12 +708,78 @@ func (s *exeSymlink) executable() (file fsbridge.File, err error) {
|
||||
// The MemoryManager may be destroyed, in which case
|
||||
// MemoryManager.destroy will simply set the executable to nil
|
||||
// (with locks held).
|
||||
file = mm.Executable()
|
||||
if file == nil {
|
||||
exec = mm.Executable()
|
||||
if exec == nil {
|
||||
err = syserror.ESRCH
|
||||
}
|
||||
})
|
||||
return
|
||||
if err != nil {
|
||||
return vfs.VirtualDentry{}, "", err
|
||||
}
|
||||
defer exec.DecRef(ctx)
|
||||
|
||||
vd := exec.(*fsbridge.VFSFile).FileDescription().VirtualDentry()
|
||||
vd.IncRef()
|
||||
return vd, "", nil
|
||||
}
|
||||
|
||||
// cwdSymlink is an symlink for the /proc/[pid]/cwd file.
|
||||
//
|
||||
// +stateify savable
|
||||
type cwdSymlink struct {
|
||||
implStatFS
|
||||
kernfs.InodeAttrs
|
||||
kernfs.InodeNoopRefCount
|
||||
kernfs.InodeSymlink
|
||||
|
||||
task *kernel.Task
|
||||
}
|
||||
|
||||
var _ kernfs.Inode = (*cwdSymlink)(nil)
|
||||
|
||||
func (fs *filesystem) newCwdSymlink(task *kernel.Task, ino uint64) *kernfs.Dentry {
|
||||
inode := &cwdSymlink{task: task}
|
||||
inode.Init(task.Credentials(), linux.UNNAMED_MAJOR, fs.devMinor, ino, linux.ModeSymlink|0777)
|
||||
|
||||
d := &kernfs.Dentry{}
|
||||
d.Init(inode)
|
||||
return d
|
||||
}
|
||||
|
||||
// Readlink implements kernfs.Inode.Readlink.
|
||||
func (s *cwdSymlink) Readlink(ctx context.Context, _ *vfs.Mount) (string, error) {
|
||||
cwd, _, err := s.Getlink(ctx, nil)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer cwd.DecRef(ctx)
|
||||
|
||||
root := vfs.RootFromContext(ctx)
|
||||
if !root.Ok() {
|
||||
// It could have raced with process deletion.
|
||||
return "", syserror.ESRCH
|
||||
}
|
||||
defer root.DecRef(ctx)
|
||||
|
||||
vfsObj := cwd.Mount().Filesystem().VirtualFilesystem()
|
||||
name, _ := vfsObj.PathnameWithDeleted(ctx, root, cwd)
|
||||
return name, nil
|
||||
}
|
||||
|
||||
// Getlink implements kernfs.Inode.Getlink.
|
||||
func (s *cwdSymlink) Getlink(ctx context.Context, _ *vfs.Mount) (vfs.VirtualDentry, string, error) {
|
||||
if !kernel.ContextCanTrace(ctx, s.task, false) {
|
||||
return vfs.VirtualDentry{}, "", syserror.EACCES
|
||||
}
|
||||
if err := checkTaskState(s.task); err != nil {
|
||||
return vfs.VirtualDentry{}, "", err
|
||||
}
|
||||
cwd := s.task.FSContext().WorkingDirectoryVFS2()
|
||||
if !cwd.Ok() {
|
||||
// It could have raced with process deletion.
|
||||
return vfs.VirtualDentry{}, "", syserror.ESRCH
|
||||
}
|
||||
return cwd, "", nil
|
||||
}
|
||||
|
||||
// mountInfoData is used to implement /proc/[pid]/mountinfo.
|
||||
|
||||
@@ -67,6 +67,7 @@ var (
|
||||
taskStaticFiles = map[string]testutil.DirentType{
|
||||
"auxv": linux.DT_REG,
|
||||
"cgroup": linux.DT_REG,
|
||||
"cwd": linux.DT_LNK,
|
||||
"cmdline": linux.DT_REG,
|
||||
"comm": linux.DT_REG,
|
||||
"environ": linux.DT_REG,
|
||||
|
||||
@@ -780,8 +780,12 @@ TEST(ProcSelfFdInfo, Flags) {
|
||||
}
|
||||
|
||||
TEST(ProcSelfExe, Absolute) {
|
||||
auto exe = ASSERT_NO_ERRNO_AND_VALUE(
|
||||
ReadLink(absl::StrCat("/proc/", getpid(), "/exe")));
|
||||
auto exe = ASSERT_NO_ERRNO_AND_VALUE(ReadLink("/proc/self/exe"));
|
||||
EXPECT_EQ(exe[0], '/');
|
||||
}
|
||||
|
||||
TEST(ProcSelfCwd, Absolute) {
|
||||
auto exe = ASSERT_NO_ERRNO_AND_VALUE(ReadLink("/proc/self/cwd"));
|
||||
EXPECT_EQ(exe[0], '/');
|
||||
}
|
||||
|
||||
@@ -1473,6 +1477,16 @@ TEST(ProcPidExe, Subprocess) {
|
||||
EXPECT_EQ(actual, expected_absolute_path);
|
||||
}
|
||||
|
||||
// /proc/PID/cwd points to the correct directory.
|
||||
TEST(ProcPidCwd, Subprocess) {
|
||||
auto want = ASSERT_NO_ERRNO_AND_VALUE(GetCWD());
|
||||
|
||||
char got[PATH_MAX + 1] = {};
|
||||
ASSERT_THAT(ReadlinkWhileRunning("cwd", got, sizeof(got)),
|
||||
SyscallSucceedsWithValue(Gt(0)));
|
||||
EXPECT_EQ(got, want);
|
||||
}
|
||||
|
||||
// Test whether /proc/PID/ files can be read for a running process.
|
||||
TEST(ProcPidFile, SubprocessRunning) {
|
||||
char buf[1];
|
||||
|
||||
Reference in New Issue
Block a user