AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / unix / 问题 / 537095
Accepted
Kenneth Duda
Kenneth Duda
Asked: 2019-08-24 08:57:03 +0800 CST2019-08-24 08:57:03 +0800 CST 2019-08-24 08:57:03 +0800 CST

为什么取消链接后绑定挂载文件失败并显示 ENOENT?

  • 772

我不明白为什么在取消链接后绑定挂载时会得到 ENOENT:

kduda@penguin:/tmp$ echo hello > a
kduda@penguin:/tmp$ touch b c
kduda@penguin:/tmp$ sudo unshare -m
root@penguin:/tmp# mount -B a b
root@penguin:/tmp# rm a
root@penguin:/tmp# cat b
hello
root@penguin:/tmp# mount -B b c
mount: mount(2) failed: No such file or directory

这对我来说似乎是一个错误。你甚至可以重新创建“a”,指向同一个确切的 inode,但你得到的是同样的东西:

kduda@penguin:/tmp$ echo hello > a
kduda@penguin:/tmp$ ln a a-save
kduda@penguin:/tmp$ sudo unshare -m
root@penguin:/tmp# mount -B a b
root@penguin:/tmp# rm a
root@penguin:/tmp# ln a-save a
root@penguin:/tmp# mount -B b c
mount: mount(2) failed: No such file or directory

世界上到底发生了什么?

linux bind-mount
  • 3 3 个回答
  • 947 Views

3 个回答

  • Voted
  1. mosvy
    2019-08-24T12:00:03+08:002019-08-24T12:00:03+08:00

    系统调用将mount(2)通过挂载和符号链接完全解析其路径,但与 不同open(2)的是,它不会接受已删除文件的路径,即解析为未链接目录条目的路径。

    (类似于 的<filename> (deleted)路径/proc/PID/fd/FD,procfs 将显示未链接的目录,<filename>//deleted如/proc/PID/mountinfo)

    # unshare -m
    # echo foo > foo; touch bar baz quux
    # mount -B foo bar
    # mount -B bar baz
    # grep foo /proc/self/mountinfo
    56 38 8:7 /tmp/foo /tmp/bar ...
    57 38 8:7 /tmp/foo /tmp/baz ...
    
    # rm foo
    # grep foo /proc/self/mountinfo
    56 38 8:7 /tmp/foo//deleted /tmp/bar ...
    57 38 8:7 /tmp/foo//deleted /tmp/baz ...
    # mount -B baz quux
    mount: mount(2) failed: /tmp/quux: No such file or directory
    

    所有这些过去都可以在较旧的内核中使用,但自 v4.19 以来就没有了,首先由此更改引入:

    commit 1064f874abc0d05eeed8993815f584d847b72486
    Author: Eric W. Biederman <[email protected]>
    Date:   Fri Jan 20 18:28:35 2017 +1300
    
        mnt: Tuck mounts under others instead of creating shadow/side mounts.
    ...
    +       /* Preallocate a mountpoint in case the new mounts need
    +        * to be tucked under other mounts.
    +        */
    +       smp = get_mountpoint(source_mnt->mnt.mnt_root);
    +       if (IS_ERR(smp))
    +               return PTR_ERR(smp);
    +
    

    看起来这种影响是无意的变化。从那以后,其他不相关的变化不断涌现,更加混乱。

    其结果是它还可以防止通过打开的 fd 将已删除的文件固定在命名空间中的其他位置:

    # exec 7>foo; touch bar
    # rm foo
    # mount -B /proc/self/fd/7 bar
    mount: mount(2) failed: /tmp/bar: No such file or directory
    

    由于与 OP 相同的条件,最后一个命令失败。

    你甚至可以重新创建a,指向相同的 inode,但你得到的是同样的东西

    这与/proc/PID/fd/FD“符号链接”相同。内核足够聪明,可以通过直接重命名来跟踪文件,但不能通过ln+ rm( link(2)+ unlink(2)):

    # unshare -m
    # echo foo > foo; touch bar baz
    # mount -B foo bar
    # mount -B bar baz
    # grep foo /proc/self/mountinfo
    56 38 8:7 /tmp/foo /tmp/bar ...
    57 38 8:7 /tmp/foo /tmp/baz ...
    
    # mv foo quux
    # grep bar /proc/self/mountinfo
    56 38 8:7 /tmp/quux /tmp/bar ...
    
    # ln quux foo; rm quux
    # grep bar /proc/self/mountinfo
    56 38 8:7 /tmp/quux//deleted /tmp/bar ...
    
    • 6
  2. Best Answer
    sourcejedi
    2019-08-24T13:54:01+08:002019-08-24T13:54:01+08:00

    浏览源代码,我找到了一个ENOENT相关的,即未链接的目录条目:

    static int attach_recursive_mnt(struct mount *source_mnt,
                struct mount *dest_mnt,
                struct mountpoint *dest_mp,
                struct path *parent_path)
    {
        [...]
    
        /* Preallocate a mountpoint in case the new mounts need
         * to be tucked under other mounts.
         */
        smp = get_mountpoint(source_mnt->mnt.mnt_root);
    
    static struct mountpoint *get_mountpoint(struct dentry *dentry)
    {
        struct mountpoint *mp, *new = NULL;
        int ret;
    
        if (d_mountpoint(dentry)) {
            /* might be worth a WARN_ON() */
            if (d_unlinked(dentry))
                return ERR_PTR(-ENOENT);
    

    https://elixir.bootlin.com/linux/v5.2/source/fs/namespace.c#L3100

    get_mountpoint()通常应用于目标,而不是源。在这个函数中,它是因为挂载传播而被调用的。在挂载传播期间,必须强制执行不能在已删除文件之上添加挂载的规则。但是,即使没有发生需要这样做的挂载传播,强制执行也很迫切。我认为像这样检查是一致的很好,它只是编码比我理想的情况要模糊一些。

    无论哪种方式,我认为执行此操作是合理的。只要它有助于减少要分析的奇怪案例的数量,没有人有特别令人信服的反驳论点。

    • 3
  3. Kenneth Duda
    2019-08-26T04:28:15+08:002019-08-26T04:28:15+08:00

    一个综合的答案是:要理解三件事,然后这一切就说得通了。

    首先,绑定挂载的来源是dentry,而不是 inode。也就是说,您不会在名称上绑定安装 inode;您将一个牙签绑定到另一个牙签上。要查看差异,请查看如果将不同的链接挂载到同一个 inode 会发生什么;挂载是不同的,因为源目录不同,即使 inode 相同:

    root@penguin:/tmp# echo hello > a1
    root@penguin:/tmp# ln a1 a2
    root@penguin:/tmp# touch b1 b2
    root@penguin:/tmp# mount -B a1 b1
    root@penguin:/tmp# mount -B a2 b2
    root@penguin:/tmp# ls -li a1 a2 b1 b2
    9552271 -rw-r--r-- 2 root root 6 Aug 25 05:16 a1
    9552271 -rw-r--r-- 2 root root 6 Aug 25 05:16 a2
    9552271 -rw-r--r-- 2 root root 6 Aug 25 05:16 b1
    9552271 -rw-r--r-- 2 root root 6 Aug 25 05:16 b2
    root@penguin:/tmp# grep /tmp/ /proc/self/mountinfo
    421 364 0:38 /lxd/.../rootfs/tmp/a1 /tmp/b1 rw,...
    422 364 0:38 /lxd/.../rootfs/tmp/a2 /tmp/b2 rw,...
    

    要理解的第二件事是,当您挂载本身就是早期绑定挂载的目标的东西时,它与绑定挂载的源是同一个 dentry 对象(这就是绑定挂载;一个 dentry 在另一个之上。)因此,如果a1是 mount on b1,则 mount b1on与 mount onc1完全相同,因为名称和指的是同一个目录。a1c1a1b1

    要理解的第三件事是内核禁止绑定挂载已删除的目录,因为......我看不出有什么好的理由。似乎针对挂载目标的错误检查(防止挂载到已删除的目录上,这是没有意义的,因为您永远无法引用新挂载)对挂载源应用没有充分的理由也是。这就是这里的代码:

    static struct mountpoint *get_mountpoint(struct dentry *dentry)
    {
        struct mountpoint *mp, *new = NULL;
        int ret;
    
        if (d_mountpoint(dentry)) {
            /* might be worth a WARN_ON() */
            if (d_unlinked(dentry))
                return ERR_PTR(-ENOENT);
    

    这三个事实的结果是(继续上面的 shell 会话)如果ENOENT被删除:b2c2a2

    root@penguin:/tmp# touch c1 c2
    root@penguin:/tmp# rm a2
    root@penguin:/tmp# mount -B b1 c1
    root@penguin:/tmp# mount -B b2 c2
    mount: mount(2) failed: /tmp/c2: No such file or directory
    root@penguin:/tmp# 
    

    这让我觉得这是一个错误,因为如果您在安装后删除 a2,您的 b2-on-c2 安装是有效的,并且顺序无关紧要:在某物上安装已删除的 dentry 是合法的还是不合法的,它不应该它何时被删除无关紧要。然而,理性的人不同意。

    感谢大家。

    • 1

相关问题

  • 有没有办法让 ls 只显示某些目录的隐藏文件?

  • 使用键盘快捷键启动/停止 systemd 服务 [关闭]

  • 需要一些系统调用

  • astyle 不会更改源文件格式

  • 通过标签将根文件系统传递给linux内核

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    模块 i915 可能缺少固件 /lib/firmware/i915/*

    • 3 个回答
  • Marko Smith

    无法获取 jessie backports 存储库

    • 4 个回答
  • Marko Smith

    如何将 GPG 私钥和公钥导出到文件

    • 4 个回答
  • Marko Smith

    我们如何运行存储在变量中的命令?

    • 5 个回答
  • Marko Smith

    如何配置 systemd-resolved 和 systemd-networkd 以使用本地 DNS 服务器来解析本地域和远程 DNS 服务器来解析远程域?

    • 3 个回答
  • Marko Smith

    dist-upgrade 后 Kali Linux 中的 apt-get update 错误 [重复]

    • 2 个回答
  • Marko Smith

    如何从 systemctl 服务日志中查看最新的 x 行

    • 5 个回答
  • Marko Smith

    Nano - 跳转到文件末尾

    • 8 个回答
  • Marko Smith

    grub 错误:你需要先加载内核

    • 4 个回答
  • Marko Smith

    如何下载软件包而不是使用 apt-get 命令安装它?

    • 7 个回答
  • Martin Hope
    user12345 无法获取 jessie backports 存储库 2019-03-27 04:39:28 +0800 CST
  • Martin Hope
    Carl 为什么大多数 systemd 示例都包含 WantedBy=multi-user.target? 2019-03-15 11:49:25 +0800 CST
  • Martin Hope
    rocky 如何将 GPG 私钥和公钥导出到文件 2018-11-16 05:36:15 +0800 CST
  • Martin Hope
    Evan Carroll systemctl 状态显示:“状态:降级” 2018-06-03 18:48:17 +0800 CST
  • Martin Hope
    Tim 我们如何运行存储在变量中的命令? 2018-05-21 04:46:29 +0800 CST
  • Martin Hope
    Ankur S 为什么 /dev/null 是一个文件?为什么它的功能不作为一个简单的程序来实现? 2018-04-17 07:28:04 +0800 CST
  • Martin Hope
    user3191334 如何从 systemctl 服务日志中查看最新的 x 行 2018-02-07 00:14:16 +0800 CST
  • Martin Hope
    Marko Pacak Nano - 跳转到文件末尾 2018-02-01 01:53:03 +0800 CST
  • Martin Hope
    Kidburla 为什么真假这么大? 2018-01-26 12:14:47 +0800 CST
  • Martin Hope
    Christos Baziotis 在一个巨大的(70GB)、一行、文本文件中替换字符串 2017-12-30 06:58:33 +0800 CST

热门标签

linux bash debian shell-script text-processing ubuntu centos shell awk ssh

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve