Linux内核中实现open("/proc/self/fd/NUM")的代码在哪里?

3
我一直认为执行open(/proc/self/fd/NUM, flags)等同于dup(NUM),但显然不是这样的!例如,如果你使用dup复制文件描述符,然后将新的fd设置为非阻塞,这也会影响原来的文件描述符(因为非阻塞状态是文件描述符的属性,而两个文件描述符都指向同一个文件描述符)。然而,如果你打开/proc/self/fd/NUM,那么你似乎会得到一个新的独立文件描述符,并且可以分别设置旧和新的fd的非阻塞状态。甚至可以使用此方法获得指向同一匿名管道的两个文件描述符,否则是不可能的(example)。另一方面,虽然你可以dup一个套接字fd,但如果NUM引用套接字,则open("/proc/self/fd/NUM", flags)会失败。
现在我想能够看到这对其他类型的特殊文件如何起作用,并回答诸如“以这种方式重新打开文件时进行了什么权限检查?”这样的问题,因此我试图找到实际实现此路径的Linux代码,但是当我开始阅读fs/proc/fd.c时,我很快就迷失在一个不同的操作结构的复杂迷宫中。

所以我的问题是:有人可以解释一下执行open(“/proc/self/fd/NUM”,flags)的代码路径吗?为了具体说明,让我们假设NUM指的是管道,我们正在谈论最新的内核发布。


有趣的问题。看起来魔法发生在proc_fd_link中,用于获取/proc/self/fd/NUM指向的链接。它只是从打开文件表中复制了struct path*(基本上是dentry)。这使得任何创建原始struct path的东西都可以实现打开dentry的inode的功能,在这种情况下是来自fs/pipe.copen_fifo。还有TIL:Linux有一个“pipefs”,所有管道都在那里 :O - that other guy
1个回答

4

有评论建议查看proc_fd_link,这是个好主意。如果您在跟踪代码时遇到困难,可以使用systemtap来帮助自己。这里有一个神奇的脚本:

probe kernel.function("proc_fd_link") {
    print_backtrace();
}

从fd/目录下打开文件并运行它会得到以下结果:

 0xffffffffbb2cad70 : proc_fd_link+0x0/0xd0 [kernel]
 0xffffffffbb2c4c3b : proc_pid_get_link+0x6b/0x90 [kernel] (inexact)
 0xffffffffbb36341a : security_inode_follow_link+0x4a/0x70 [kernel] (inexact)
 0xffffffffbb25bf13 : trailing_symlink+0x1e3/0x220 [kernel] (inexact)
 0xffffffffbb25f559 : path_openat+0xe9/0x1380 [kernel] (inexact)
 0xffffffffbb261af1 : do_filp_open+0x91/0x100 [kernel] (inexact)
 0xffffffffbb26fd8f : __alloc_fd+0x3f/0x170 [kernel] (inexact)
 0xffffffffbb24f280 : do_sys_open+0x130/0x220 [kernel] (inexact)
 0xffffffffbb24f38e : sys_open+0x1e/0x20 [kernel] (inexact)
 0xffffffffbb003c57 : do_syscall_64+0x67/0x160 [kernel] (inexact)
 0xffffffffbb8039e1 : return_from_SYSCALL_64+0x0/0x6a [kernel] (inexact)

在proc_pid_get_link中,我们看到:

/* Are we allowed to snoop on the tasks file descriptors? */
if (!proc_fd_access_allowed(inode))
        goto out;

aaaand

/* permission checks */
static int proc_fd_access_allowed(struct inode *inode)
{
        struct task_struct *task;
        int allowed = 0;
        /* Allow access to a task's file descriptors if it is us or we
         * may use ptrace attach to the process and find out that
         * information.
         */
        task = get_proc_task(inode);
        if (task) {
                allowed = ptrace_may_access(task, PTRACE_MODE_READ_FSCREDS);
                put_task_struct(task);
        }
        return allowed;
}

显然,你需要与使用ptrace时相同的权限。

最后,为什么打开套接字会失败?strace显示返回了ENXIO。快速搜索git grep ENXIO fs/*.c可以发现:

static int no_open(struct inode *inode, struct file *file)
{
        return -ENXIO;
}

检查代码如何使用no_open的结果留给读者自己练习。同时请注意,systemtap可以用于printf式的调试,而无需修改源代码。它还可以放置在函数的“返回”位置,并报告错误代码。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接