Non c'è modo di farlo; le operazioni di copia dei file non sono mai atomiche e non c'è modo di farle.
Ma è possibile scrivere il file con un nome temporaneo casuale e quindi rinominare. Rinominare le operazioni devono essere atomiche. Se il file esiste già, la ridenominazione fallirà e riceverai un errore.
[EDIT2]rename()
è atomico solo se lo si fa nello stesso file system. Il modo sicuro è creare il nuovo file nella stessa cartella della destinazione.
[EDIT] Si discute molto se la rinomina è sempre atomica o meno e sul comportamento di sovrascrittura. Quindi ho scavato alcune risorse.
Su Linux, se la destinazione esiste e sia l'origine che la destinazione sono file, la destinazione viene automaticamente sovrascritta (man page). Quindi mi sono sbagliato lì.
Ma rename(2)
garantisce comunque che il file originale o il nuovo file rimangano validi se qualcosa va storto, quindi l'operazione è atomica nel senso che non può danneggiare i dati. Non è atomico, nel senso che impedisce a due processi di fare lo stesso rinominare contemporaneamente e si può predire il risultato. Uno vincerà ma non puoi dire quale.
Su Windows, se un altro processo sta attualmente scrivendo il file, si ottiene un errore se si tenta di aprirlo per la scrittura, quindi un vantaggio per Windows, qui.
Se il computer si arresta in modo anomalo mentre l'operazione viene scritta su disco, l'implementazione del file system deciderà la quantità di dati danneggiati. C'è niente un'applicazione potrebbe fare al riguardo. Quindi smettila di piagnucolare :-)
Non c'è anche nessun altro approccio che funzioni meglio o anche solo come questo.
È possibile utilizzare il blocco dei file. Ma ciò renderebbe tutto più complesso e non offrirà ulteriori vantaggi (oltre ad essere più complicato che alcune persone considerano un enorme vantaggio per qualche motivo). E aggiungeresti molti bei casi d'angolo quando il tuo file si trova su un'unità di rete.
È possibile utilizzare open(2)
con la modalità O_CREAT
che renderebbe la funzione non riuscita se il file esiste già. Ma ciò non impedirebbe a un secondo processo di eliminare il file e scrivere la propria copia.
Oppure è possibile creare una directory di blocco poiché la creazione di directory deve essere anche atomica. Ma questo non ti comprerebbe molto. Dovresti scrivere tu stesso il codice di blocco e fare assolutamente, sicuro al 100% che tu davvero, in realtà cancelli sempre la directory di blocco in caso di disastro - cosa che non puoi.
È solo la creazione della destinazione che deve essere atomica, ma anche il contenuto sorgente, come letto, rappresenta solo un singolo punto nel tempo? –
Proprio la creazione. Sto scrivendo un programma che copia un file di zona in/tmp, apporta le modifiche richieste e poi lo copia alla fine. Devo solo assicurarmi che se due persone provano e modificano contemporaneamente, una di queste non perde le modifiche. – Rory
Ricorda che 'rename()' è atomico solo se l'origine e la destinazione si trovano sullo stesso file system, quindi potresti voler creare il tuo file temporaneo nella directory di destinazione, non in '/ tmp'. –