如何通过fs_context获取完整挂载点路径?内核模块技术求助
我在内核模块中实现了fsconfig系统调用钩子,用于管理只读挂载点的重挂载操作。例如用户执行mount /dev/sda /tmp/mytest -o remount,ro时,需要在fsconfig钩子中提取挂载点路径/tmp/mytest。
为实现目标,我尝试通过文件描述符访问fs_context结构体获取挂载点路径:
- 最初从
fc_context->dentry出发,调用dentry_path_raw等函数,仅得到结果"/",无法满足需求; - 尝试使用
d_path,但该函数需要包含dentry和vfsmount的path结构体,而vfsmount在fsconfig系统调用中无法访问,此方法不可行。
随后我在内核源码fs/namespace.c中发现__is_local_mountpoint函数,其提示可通过current访问当前任务的命名空间遍历所有挂载点。我改编代码,通过对比每个挂载点的mnt_root与fs_context的root,成功识别对应挂载,代码如下:
ns = current->nsproxy->mnt_ns; lock_ns_list(ns); list_for_each_entry(mnt, &ns->list, mnt_list) { if (fc->root == mnt->mnt.mnt_root) { target_dentry = mnt->mnt.mnt_root; path.dentry = target_dentry; path.mnt = &mnt->mnt; break; } } unlock_ns_list(ns);
但存在严重问题:namespace.c中的namespace_sem为静态声明(static DECLARE_RWSEM(namespace_sem);),无法在模块中直接访问,存在安全风险。
核心问题:
- 是否存在无需修改内核代码的安全方法,可通过
fs_context获取挂载点路径?(假设fs_context始终对应已挂载的文件系统) - 如果只能遍历挂载点,如何解决无法安全访问该信号量的问题?
(内核版本 >= 6.6)
问题1:从fs_context直接获取挂载点路径的安全方法
在Linux 6.6+内核的重挂载场景中,fs_context结构体的purpose字段会被设为FS_CONTEXT_FOR_REMOUNT,同时mnt字段会被初始化为原挂载的vfsmount指针。利用这个字段可以直接构造path结构体,通过d_path获取挂载点路径,无需遍历挂载点,完全安全且无需修改内核:
char buf[PATH_MAX]; char *mount_path = NULL; if (fc->purpose == FS_CONTEXT_FOR_REMOUNT && fc->mnt) { struct path path = { .dentry = fc->root, .mnt = fc->mnt }; mount_path = d_path(&path, buf, sizeof(buf)); // 此时mount_path即为挂载点路径,如"/tmp/mytest" }
需要注意:fc->mnt仅在重挂载场景下有效,非重挂载场景该字段为NULL,需根据实际场景判断使用。
问题2:遍历挂载点时的信号量安全访问问题
若必须遍历挂载点(如处理非重挂载场景),禁止直接访问静态的namespace_sem,可使用内核公开导出的walk_mounts函数,该函数会自动处理挂载命名空间的锁逻辑,安全遍历所有挂载点:
实现示例
// 遍历回调函数,匹配目标挂载点 static int match_mount_by_root(void *data, struct vfsmount *mnt) { struct fs_context *fc = data; // 对比挂载点的根dentry与fs_context的root if (mnt->mnt_root == fc->root) { struct path path = { .dentry = mnt->mnt_root, .mnt = &mnt->mnt }; char buf[PATH_MAX]; char *mount_path = d_path(&path, buf, sizeof(buf)); // 此处处理获取到的挂载点路径 return 1; // 找到目标后终止遍历 } return 0; // 未找到则继续遍历 } // 在钩子中调用遍历逻辑 void find_target_mount(struct fs_context *fc) { struct mnt_namespace *ns = current->nsproxy->mnt_ns; // walk_mounts会自动获取namespace_sem读锁,遍历完成后释放 walk_mounts(ns, match_mount_by_root, fc); }
补充说明
walk_mounts函数会自动管理挂载命名空间的锁,避免了手动操作静态信号量的安全风险;- 若存在多个挂载点共享同一根dentry(如绑定挂载),需添加额外判断条件(如对比挂载源设备、文件系统类型等)来精准匹配目标挂载点。
内容的提问来源于stack exchange,提问作者hongyun

