You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过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);),无法在模块中直接访问,存在安全风险。

核心问题:

  1. 是否存在无需修改内核代码的安全方法,可通过fs_context获取挂载点路径?(假设fs_context始终对应已挂载的文件系统)
  2. 如果只能遍历挂载点,如何解决无法安全访问该信号量的问题?

(内核版本 >= 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 11:24:55