No hay forma de hacerlo; las operaciones de copia de archivos nunca son atómicas y no hay forma de hacerlas.
Pero puede escribir el archivo con un nombre temporal aleatorio y luego renombrarlo. Las operaciones de cambio de nombre deben ser atómicas. Si el archivo ya existe, el cambio de nombre fallará y obtendrá un error.
[Edit2]rename()
sólo es atómica si lo hace en el mismo sistema de archivos. La forma más segura es crear el nuevo archivo en la misma carpeta que el destino.
[EDIT] Se discute mucho si el cambio de nombre es siempre atómico o no y sobre el comportamiento de sobrescritura. Así que desenterré algunos recursos.
En Linux, si el destino existe y tanto el origen como el destino son archivos, el destino se sobrescribe silenciosamente (man page). Así que estaba equivocado allí.
Pero rename(2)
aún garantiza que el archivo original o el nuevo archivo siguen siendo válidos si algo sale mal, por lo que la operación es atómica en el sentido de que no puede dañar los datos. No es atómico en el sentido de que impide que dos procesos hagan el mismo cambio de nombre al mismo tiempo y usted puede predecir el resultado. Uno ganará pero no puede decir cuál.
En Windows, si otro proceso está actualmente escribiendo el archivo, se obtiene un error si intenta abrirlo para escritura, por lo que una ventaja para Windows, aquí.
Si su computadora se cuelga mientras la operación se escribe en el disco, la implementación del sistema de archivos decidirá la cantidad de datos que se corrompen. No es nada una aplicación podría hacer al respecto. Así que deja de lloriquear ya :-)
Tampoco hay otro enfoque que funcione mejor o incluso tan bien como este.
Puede usar el bloqueo de archivos en su lugar. Pero eso simplemente haría que todo fuera más complejo y no arrojaría ventajas adicionales (además de ser más complicado que algunas personas ven como una gran ventaja por alguna razón). Y agregaría muchos buenos casos de esquina cuando su archivo esté en una unidad de red.
Puede usar open(2)
con el modo O_CREAT
que haría que la función falle si el archivo ya existe. Pero eso no evitaría que un segundo proceso elimine el archivo y escriba su propia copia.
O podría crear un directorio de bloqueo ya que la creación de directorios tiene que ser atómica también. Pero eso tampoco te compraría mucho. Tendría que escribir el código de bloqueo usted mismo y hacer absolutamente, 100% seguro de que realmente, realmente siempre elimina el directorio de bloqueo en caso de desastre, que no puede.
¿Es solo la creación del destino que debe ser atómica, pero también tiene el contenido fuente, como leído, representa solo un punto en el tiempo? –
Solo la creación. Estoy escribiendo un programa que copia un archivo de zona en/tmp, realiza los cambios necesarios y luego lo copia al final. Solo necesito asegurarme de que si dos personas intentan editar al mismo tiempo, uno de ellos no pierde sus cambios. – Rory
Tenga en cuenta que 'rename()' solo es atómico si el origen y el destino están en el mismo sistema de archivos, por lo que es posible que desee crear su archivo temporal en el directorio de destino, no en '/ tmp'. –