È sicuro spostare un file che viene aggiunto?


28

Ho un processo node.js che utilizza fs.appendFileper aggiungere linee a file.log. Vengono aggiunte solo linee complete di circa 40 caratteri per linea, ad es. Le chiamate sono simili fs.appendFile("start-end"), non 2 chiamate come fs.appendFile("start-")e fs.appendFile("end"). Se sposto questo file in file2.logposso essere sicuro che nessuna linea venga persa o copiata parzialmente?

Risposte:


36

Finché non si sposta il file attraverso i bordi del file system, l'operazione dovrebbe essere sicura. Ciò è dovuto al meccanismo, come viene effettivamente fatto lo "spostamento".

Se mvun file si trova sullo stesso file system, il file non viene effettivamente toccato, ma viene modificata solo la voce del file system.

$ mv foo bar

in realtà fa qualcosa di simile

$ ln foo bar
$ rm foo

Ciò creerebbe un forte legame (una seconda voce di directory) per il file (in realtà l'inode puntato dalla voce del file-system) foodi nome bare rimuovere la foovoce di. Da quando in fase di rimozione foo, esiste una seconda voce del file system che punta fooall'inode, la rimozione della voce precedente foonon rimuove effettivamente alcun blocco appartenente all'inode.

Il tuo programma si accetterebbe comunque felicemente al file, poiché il suo handle di file aperto punta all'inode del file, non alla voce del file system.

Nota: se il programma chiude e riapre il file tra le scritture, si finirà per creare un nuovo file con la vecchia voce del file system!

Spostamenti incrociati del file system:

Se sposti il ​​file attraverso i bordi del file system, le cose diventano brutte. In questo caso non puoi garantire che il tuo file rimanga coerente, dato che lo mvfarebbe davvero

  • creare un nuovo file sul file system di destinazione
  • copia il contenuto del vecchio file nel nuovo file
  • rimuovere il vecchio file

o

$ cp /path/to/foo /path/to/bar
$ rm /path/to/foo

resp.

$ touch /path/to/bar
$ cat < /path/to/foo > /path/to/bar
$ rm /path/to/foo

A seconda che la copia raggiunga la fine del file durante una scrittura dell'applicazione, è possibile che nel nuovo file sia presente solo la metà di una riga.

Inoltre, se l'applicazione non si chiude e riapre il vecchio file, continuerebbe a scrivere nel vecchio file, anche se sembra essere eliminato: il kernel sa quali file sono aperti e sebbene eliminerebbe la voce del file system, non cancellerà l'inode del vecchio file e i blocchi associati fino a quando l'applicazione non chiuderà il suo handle di file aperto.


3
Cordiali saluti, le prime versioni di Unix non avevano una rename()chiamata di sistema. Quindi la versione originale di mveffettivamente ha chiamato link()per creare il collegamento reale, seguita da unlink()per rimuovere il nome originale. rename()è stato aggiunto in FreeBSD, per implementarlo atomicamente nel kernel.
Barmar,

Mi dispiace ma cos'è file-system borders?
laike9m,

1
@ laike9m - I bordi del file system si riferiscono al fatto che un semplice file system deve risiedere su una partizione su un dispositivo di memoria come un'unità disco. Se si rinomina un file all'interno del file system, tutto ciò che cambia è il nome in una voce della directory. Ha ancora lo stesso inode - se fosse in un filesystem basato sugli inode per cominciare - come la maggior parte dei filesystem Linux. Tuttavia, se si sposta il file in un altro filesystem, i dati effettivi devono essere spostati e il file otterrà un nuovo inode dal nuovo filesystem. Ciò interromperebbe qualsiasi operazione sul file che era in corso quando si è verificato.
Joe,

9

Dato che dici che stai usando node.js, presumo che avresti usato fs.rename()(o fs.renameSync()) per rinominare i file. Questo metodo node.js è documentato per utilizzare la chiamata di sistema rename (2) , che non tocca il file stesso in alcun modo, ma cambia semplicemente il nome con cui è elencato nel file system:

" rename () rinomina un file, spostandolo tra le directory se necessario. Qualsiasi altro collegamento reale al file (come creato usando il collegamento (2) ) non è interessato. Anche i descrittori di file aperti per oldpath non sono interessati."

In particolare, nota l'ultima frase citata sopra, che dice che qualsiasi descrittore di file aperto (come il tuo programma userebbe per scrivere sul file) continuerà a puntarlo anche dopo che è stato rinominato. Pertanto, non vi sarà alcuna perdita o danneggiamento dei dati anche se il file viene rinominato mentre viene contemporaneamente scritto.


Come osserva Andreas Weise nella sua risposta , la chiamata di sistema rename (2) (e quindi fs.rename()in node.js) non funzionerà oltre i confini del filesystem. Pertanto, il tentativo di spostare un file in un file system diverso in questo modo fallirà semplicemente.

Il mvcomando Unix tenta di nascondere questa limitazione rilevando l'errore e, invece, spostando il file copiando il suo contenuto in un nuovo file ed eliminando l'originale. Sfortunatamente, lo spostamento di file in questo modo comporta la perdita di dati se il file viene spostato mentre viene scritto. Così, se si desidera rinominare in modo sicuro i file che possono essere scritti contemporaneamente a, si dovrebbe non usare mv(o, almeno, si dovrebbe fare assolutamente certi che il percorso vecchi e nuovi sono sullo stesso file system).

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.