mount(2)
panggilan sistem akan sepenuhnya menyelesaikan jalurnya melalui tunggangan dan symlink, tetapi tidak seperti open(2)
, tidak akan menerima jalur ke file yang dihapus, yaitu jalur yang memutuskan entri direktori yang tidak tertaut.
(mirip dengan <filename> (deleted)
jalur /proc/PID/fd/FD
, procfs akan menampilkan gigi palsu yang tidak tertaut sebagai <filename>//deleted
dalam /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
Semua ini dulu berfungsi di kernel lama, tetapi tidak sejak v4.19, pertama kali diperkenalkan oleh perubahan ini:
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);
+
Tampaknya efek ini tidak disengaja oleh perubahan tersebut. Sejak saat itu, perubahan lain yang tidak terkait telah menumpuk, membuatnya semakin bingung.
Konsekuensinya adalah ia juga mencegah menyematkan file yang dihapus di tempat lain di namespace melalui fd terbuka untuknya:
# 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
Perintah terakhir gagal karena kondisi yang sama dengan OP.
Anda bahkan dapat membuat ulang
a
, menunjuk ke inode yang persis sama, tetapi Anda mendapatkan hal yang sama
Ini sama dengan /proc/PID/fd/FD
"symlink". Kernel cukup pintar untuk mengikuti file melalui penggantian nama langsung, tetapi tidak melalui 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 ...
Berjalan melalui kode sumber, saya menemukan satu ENOENT
itu relevan, yaitu untuk entri direktori yang tidak ditautkan:
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()
umumnya diterapkan pada target, bukan sumbernya. Dalam fungsi ini, ini disebut karena mount propagation. Penting untuk menegakkan aturan bahwa Anda tidak dapat menambahkan mount di atas file yang dihapus, selama propagasi mount. Tetapi penegakan terjadi dengan penuh semangat, bahkan jika tidak ada propagasi mount terjadi yang membutuhkan ini. Menurut saya bagus bahwa pemeriksaannya konsisten seperti ini, kodenya sedikit lebih tidak jelas daripada yang saya inginkan.
Bagaimanapun saya melihatnya, saya pikir masuk akal untuk menegakkan ini. Selama itu membantu mengurangi jumlah kasus aneh untuk dianalisis, dan tidak ada yang memiliki argumen tandingan yang sangat meyakinkan.