GNU/Linux >> Tutoriels Linux >  >> Linux

Pourquoi le montage lié d'un fichier après la suppression du lien échoue-t-il avec ENOENT ?

Le mount(2) l'appel système résoudra complètement ses chemins via les montages et les liens symboliques, mais contrairement à open(2) , n'acceptera pas de chemin vers un fichier supprimé, c'est-à-dire un chemin qui se résout en une entrée de répertoire non liée.

(similaire au <filename> (deleted) chemins de /proc/PID/fd/FD , procfs affichera les dentries non liées sous la forme <filename>//deleted en /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

Tout cela fonctionnait dans les noyaux plus anciens, mais plus depuis la v4.19, introduite pour la première fois par ce changement :

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);
+

Il semble que cet effet n'était pas voulu par le changement. Depuis lors, d'autres changements sans rapport se sont accumulés, le rendant encore plus confus.

Une conséquence de cela est qu'il empêche également d'épingler un fichier supprimé ailleurs dans l'espace de noms via un fd ouvert :

# 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

La dernière commande échoue à cause de la même condition que celle de l'OP.

Vous pouvez même recréer a , pointant vers le même inode exact, mais vous obtenez la même chose

C'est la même chose qu'avec /proc/PID/fd/FD "liens symboliques". Le noyau est assez intelligent pour suivre un fichier par des renommages directs, mais pas par 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 ...

En parcourant le code source, j'ai trouvé exactement un ENOENT qui était pertinent, c'est-à-dire pour une entrée d'annuaire non liée :

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() est généralement appliqué à la cible, pas à la source. Dans cette fonction, elle est appelée en raison de la propagation de montage. Il est nécessaire d'appliquer la règle selon laquelle vous ne pouvez pas ajouter de montages au-dessus d'un fichier supprimé, lors de la propagation du montage. Mais l'application se fait avec impatience, même si aucune propagation de montage ne se produit, ce qui nécessiterait cela. Je pense que c'est bien que la vérification soit cohérente comme celle-ci, elle est juste codée un peu plus obscurément que je ne le préférerais idéalement.

Quoi qu'il en soit, je pense qu'il est raisonnable d'appliquer cela. Tant que cela aide à réduire le nombre de cas étranges à analyser, et que personne n'a de contre-argument particulièrement convaincant.


Linux
  1. Pourquoi git échoue-t-il sur push/fetch avec trop de fichiers ouverts

  2. Pourquoi l'arrêt net rpc échoue-t-il avec les bonnes informations d'identification ?

  3. Que fait Linux avec les fichiers existants dans un point de montage ?

  4. Pourquoi wget'ing une image me donne-t-il un fichier, pas une image ?

  5. Deux points de montage distincts avec un seul appareil

Ssh - Pourquoi la tentative de transfert X11 échoue-t-elle avec "connect /tmp/.x11-unix/x0 :aucun fichier ou répertoire de ce type" ?

Pourquoi Rsync échoue-t-il avec un tuyau cassé (32), erreur dans le socket Io (code 10) à Io.c (820) ??

Lier monter un utilisateur SFTP après avoir utilisé chroot

Commande de montage Linux avec exemples

Pourquoi cette boucle de retard commence-t-elle à s'exécuter plus rapidement après plusieurs itérations sans sommeil ?

Pourquoi rsync ne parvient-il pas à copier les fichiers de /sys sous Linux ?