c - Linux 内核 filp_open 失败并显示 NOENT
问题描述
更新:在@kisch 的出色答案的延续中,我阅读了有关softirq 上下文的信息,并且似乎(出于非常合理的原因)无法从该上下文中访问用户模式。我认为这确实是它失败的原因。
目前在我处理用户空间文件的内核模块上工作。我知道这被认为是一种不好的做法,但我仍然需要。
该模块使用 netfilter 放置一个钩子来捕获系统中的每个传出数据包,并且在调用该钩子时 - 它调用filp_open
.
巫毒从这里开始。当我从环回发送 ping 时,一切正常,/etc/fstab
在这种情况下,文件 ( ) 已成功打开。当我从家里的不同 IP ping 机器时,filp_open
失败并显示ENOENT
.
为了弄清楚它实际失败的地方,我在 QEMU 仿真上运行了该模块,成功地重现了奇怪的行为。显然,它在内核内部函数中失败,do_last
在下一个代码中(取自fs/namei.c
):
if (unlikely(d_is_negative(path.dentry))) {
path_to_nameidata(&path, nd);
return -ENOENT;
}
我完全不知道是什么使它失败,因为文件一直存在。
有人有什么想法吗?
这是代码中失败的部分:
unsigned int nf_sendfile_hook(void *priv,
struct sk_buff *skb,
const struct nf_hook_state *state)
{
if (NULL == g_get_payload_func) {
// as long as we don't have a way to get our payloads, we don't
// have much to do.
return NF_ACCEPT;
}
struct file *filp;
filp = filp_open("/etc/fstab", O_RDONLY, 0);
if (IS_ERR(filp)) {
printk(KERN_ERR "%p\n", filp);
return NF_ACCEPT;
}
...
}
预先感谢。
解决方案
我需要更多细节才能确定。你到底在哪里上钩?
很可能,不同的行为是由内核调用钩子函数的不同上下文引起的。
当你
从环回发送 ping,
您有一个用户空间进程发出 sendmsg() 系统调用。内核在附加到该进程的用户上下文中启动一个调用链。在将数据包放入队列以进行进一步的分离处理之前,可能会直接在该调用链中调用netfilter 挂钩。
当你
从我家的不同 IP ping 机器,
你有一个调用链从 的 Soft-IRQ 上下文NET_RX_SOFTIRQ
开始,从net_rx_action()
. 该调用链将传入的数据包分类为 ICMP,将其传递给内部 ICMP 接收例程,该例程直接发送 ping 回复数据包,然后可能调用 netfilter 挂钩。
Soft-IRQ 上下文与任何用户空间进程无关。
现在取决于您的内核设置,文件系统查找代码完全有可能取决于用户上下文中存在的信息,以决定访问限制。您可能有挂载命名空间,因此如果没有用户上下文的进程 ID,甚至可能不会挂载 /etc 文件系统,这可以解释 ENOENT.
文件系统查找代码也很可能需要调用一些需要执行的操作schedule()
,即阻塞,直到一个耗时的操作完成(例如在文件系统的底层设备的块中分页以进行查找)。这在 SoftIRQ 上下文中不起作用。
这还不是一个完整的答案,太多的“可能”,但我很确定这是在哪里找到它的正确方向。
推荐阅读
- java - 在Java中的IF条件下处理变量字符串零
- php - 将字符串拆分为两个单词后将两个字母大写
- python - 请求(或 httplib2.request)返回状态码的时间过长
- excel - 满足条件时,在另一张纸上复制仅粘贴部分行,其中一个单元格包含图片
- jquery - 向 kendo 对话框标题栏添加按钮/html 内容
- f# - 如何在 f# 中使用 npgsql 处理可能的 Null 值?
- javascript - 第一个字符之前的符号 (!@#$%^&*_) 的正则表达式
- java - 版本:属性的使用版本
- chromium - 如何在 codecept.conf.js 中设置 chromium 启动选项?
- django - 如何将 tasks.py 外部定义的函数转换为 Celery 任务并让 Celery 工作人员在 Django 中处理它