损坏的符号链接和神秘的(已删除)

Bri*_*ker 5 linux bash symlink procfs

我一直在用 linux 上的 proc 文件系统做一些事情,我遇到了一些我想澄清的行为。

每个进程/proc都有一个指向它的可执行文件的符号链接,/proc/{pid}/exe. 如果一个进程在其可执行文件被删除后继续运行,读取此符号链接将返回可执行文件的路径,(deleted)并附加到末尾。

运行此命令,您甚至可能会在系统上看到一些:

grep '(deleted)' <(for dir in $(ls /proc | grep -E '^[0-9]+'); do echo "$dir $(readlink /proc/$dir/exe)"; done)
Run Code Online (Sandbox Code Playgroud)

我尝试使用一些简单的 bash 命令重新创建此行为:

>>> echo "temporary file" >> tmpfile.test
>>> ln -s tmpfile.test tmpfile.link
>>> rm tmpfile.test
>>> readlink tmpfile.link
tmpfile.test
Run Code Online (Sandbox Code Playgroud)

没有(deleted)附加到名称!尝试cat tmpfile.link确认链接已断开 ( cat: tmpfile.link: No such file or directory)。

然而,前几天,同样的测试确实导致了一个(deleted)被附加到 readlink 的输出。是什么赋予了?

这是我想知道的:

  • 是否有一系列事件保证(deleted)将附加到名称?
  • 为什么/proc/{pid}/exe显示(deleted)已删除的可执行文件?
  • 如何在/proc/{pid}/exe 不附加任何内容的情况下获取可执行文件的名称(deleted)并保证原始可执行文件不只是命名some_executable (deleted)

Alo*_*hal 5

它不是readlink,但 Linux 将符号链接更改为指向<filename> (deleted),即(deleted)附加到链接的目标。


Mik*_* C. 5

FWIW,特殊<filename> (deleted)行为在 Linux 内核函数中实现d_path()https://elixir.bootlin.com/linux/v4.1.13/source/fs/dcache.c#L3080

源注释(在下面的代码片段中)表明特殊行为仅适用于某些“合成文件系统”(例如)和“伪索引节点”动态生成的名称(路径)procfs

/**
 * d_path - return the path of a dentry
 * @path: path to report
 * @buf: buffer to return value in
 * @buflen: buffer length
 *
 * Convert a dentry into an ASCII path name. If the entry has been deleted
 * the string " (deleted)" is appended. Note that this is ambiguous.
 *
 * Returns a pointer into the buffer or an error code if the path was
 * too long. Note: Callers should use the returned pointer, not the passed
 * in buffer, to use the name! The implementation often starts at an offset
 * into the buffer, and may leave 0 bytes at the start.
 *
 * "buflen" should be positive.
 */
char *d_path(const struct path *path, char *buf, int buflen)
{
    char *res = buf + buflen;
    struct path root;
    int error;

    /*
     * We have various synthetic filesystems that never get mounted.  On
     * these filesystems dentries are never used for lookup purposes, and
     * thus don't need to be hashed.  They also don't need a name until a
     * user wants to identify the object in /proc/pid/fd/.  The little hack
     * below allows us to generate a name for these objects on demand:
     *
     * Some pseudo inodes are mountable.  When they are mounted
     * path->dentry == path->mnt->mnt_root.  In that case don't call d_dname
     * and instead have d_path return the mounted path.
     */
    if (path->dentry->d_op && path->dentry->d_op->d_dname &&
        (!IS_ROOT(path->dentry) || path->dentry != path->mnt->mnt_root))
        return path->dentry->d_op->d_dname(path->dentry, buf, buflen);

    rcu_read_lock();
    get_fs_root_rcu(current->fs, &root);
    error = path_with_deleted(path, &root, &res, &buflen);
    rcu_read_unlock();

    if (error < 0)
        res = ERR_PTR(error);
    return res;
}
Run Code Online (Sandbox Code Playgroud)