GNU/Linux >> Linux Esercitazione >  >> Linux

Perché il binding del montaggio di un file dopo lo scollegamento non riesce con ENOENT?

Il mount(2) la chiamata di sistema risolverà completamente i suoi percorsi tramite montaggi e collegamenti simbolici, ma a differenza di open(2) , non accetterà un percorso a un file eliminato, ovvero un percorso che si risolve in una voce di directory non collegata.

(simile al <filename> (deleted) percorsi di /proc/PID/fd/FD , procfs visualizzerà le voci non collegate come <filename>//deleted in /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

Tutto questo funzionava nei kernel più vecchi, ma non dalla v4.19, introdotta per la prima volta da questa modifica:

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

Sembra che questo effetto non fosse previsto dal cambiamento. Da allora si sono accumulate altre modifiche non correlate, confondendolo ancora di più.

Una conseguenza di ciò è che impedisce anche di bloccare un file eliminato da qualche altra parte nello spazio dei nomi tramite un fd aperto ad esso:

# 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

L'ultimo comando fallisce a causa della stessa condizione degli OP.

Puoi persino ricreare a , puntando allo stesso esatto inode, ma ottieni la stessa cosa

È la stessa cosa di /proc/PID/fd/FD "link simbolici". Il kernel è abbastanza intelligente da seguire un file attraverso rinominazioni dirette, ma non attraverso 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 ...

Scorrendo il codice sorgente, ho trovato esattamente un ENOENT che era rilevante, ad es. per una voce di directory non collegata:

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() viene generalmente applicato alla destinazione, non alla fonte. In questa funzione, viene chiamato a causa della propagazione del montaggio. È necessario applicare la regola secondo cui non è possibile aggiungere montaggi su un file eliminato, durante la propagazione del montaggio. Ma l'applicazione sta avvenendo con entusiasmo, anche se non si verifica alcuna propagazione del montaggio che lo richieda. Penso che sia positivo che il controllo sia coerente in questo modo, è solo codificato in modo un po' più oscuro di quanto preferirei idealmente.

Ad ogni modo, penso che sia ragionevole imporre questo. Finché aiuta a ridurre il numero di casi strani da analizzare e nessuno ha una controargomentazione particolarmente convincente.


Linux
  1. Perché git fallisce su push/fetch con troppi file aperti

  2. Perché ENOENT significa No such file or directory?

  3. Perché l'arresto di net rpc fallisce con le giuste credenziali?

  4. Cosa fa Linux con i file esistenti in un punto di montaggio?

  5. Perché wget'ing un'immagine mi dà un file, non un'immagine?

Perché Rsync non riesce con tubo rotto (32), errore nella presa Io (codice 10) su Io.c(820)??

Bind montare un utente SFTP dopo aver usato chroot

Comando di montaggio Linux con esempi

Per mostrare il percorso di origine del montaggio del bind per il montaggio dopo la v2.25.2

Perché `xdg-mime query filetype ...` non riesce a trovare un nuovo tipo di file aggiunto?

Perché il mio file system è montato in sola lettura?