GNU/Linux >> Linux の 問題 >  >> Linux

リンク解除後にファイルをバインドマウントすると、ENOENT で失敗するのはなぜですか?

mount(2) システム コールは、マウントとシンボリック リンクを介してパスを完全に解決しますが、open(2) とは異なります。 、削除されたファイルへのパス、つまり、リンクされていないディレクトリ エントリに解決されるパスを受け入れません。

(<filename> (deleted) に似ています) /proc/PID/fd/FD のパス 、procfsはリンクされていないdentryを<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 ...

ソースコードを調べてみると、ちょうど 1 つの 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() 通常、ソースではなくターゲットに適用されます。この関数では、マウントの伝播のために呼び出されます。マウントの伝播中に、削除されたファイルの上にマウントを追加できないというルールを強制する必要があります。ただし、これを必要とするマウントの伝播が発生しない場合でも、強制は熱心に行われています。このようにチェックが一貫しているのは良いことだと思います。理想よりも少しあいまいにコード化されているだけです。

いずれにせよ、これを強制することは合理的だと思います。分析する奇妙なケースの数を減らすのに役立ち、誰も特に説得力のある反論を持っていない限り.


Linux
  1. 国際文字で sed が失敗する理由とその修正方法は?

  2. ENOENT が No such file or directory を意味するのはなぜですか?

  3. スリープなしで数回反復した後、この遅延ループがより速く実行されるのはなぜですか?

  1. net rpc shutdown が正しい資格情報で失敗するのはなぜですか?

  2. Linux はマウント ポイント内の既存のファイルに対して何をしますか?

  3. 画像を wget すると、画像ではなくファイルが返されるのはなぜですか?

  1. 1 つのデバイスで 2 つの異なるマウント ポイント

  2. v2.25.2 以降のマウントのバインド マウント ソース パスを表示するには

  3. 「xdg-mime query filetype ...」が新しく追加されたファイルタイプを見つけられないのはなぜですか?