Quiero copiar un archivo de A a B, que puede estar en diferentes sistemas de archivos.
Hay algunos requisitos adicionales:
- La copia es todo o nada, no queda ningún archivo parcial o corrupto B en caso de bloqueo;
- No sobrescriba un archivo B existente;
- No compita con una ejecución concurrente del mismo comando, a lo sumo uno puede tener éxito.
Creo que esto se acerca:
cp A B.part && \
ln B B.part && \
rm B.part
Pero 3. es violado porque el cp no falla si existe B.part (incluso con el indicador -n). Posteriormente 1. podría fallar si el otro proceso 'gana' el cp y el archivo vinculado en su lugar está incompleto. B.part también podría ser un archivo no relacionado, pero estoy feliz de fallar sin probar otros nombres ocultos en ese caso.
Creo que bash noclobber ayuda, ¿funciona esto completamente? ¿Hay alguna manera de obtener sin el requisito de la versión bash?
#!/usr/bin/env bash
set -o noclobber
cat A > B.part && \
ln B.part B && \
rm B.part
Seguimiento, sé que algunos sistemas de archivos fallarán en esto de todos modos (NFS). ¿Hay alguna manera de detectar tales sistemas de archivos?
Algunas otras preguntas relacionadas pero no exactamente las mismas:
¿Movimiento atómico aproximado a través de los sistemas de archivos?
https://rcrowley.org/2010/01/06/things-unix-can-do-atomically.html
mv
sobrescribirá un archivo existente B.mv -n
no notificará que ha fallado.ln(1)
(rename(2)
) fallará si B ya existe.Respuestas:
rsync
hace este trabajo SeO_EXCL
crea un archivo temporal de forma predeterminada (solo se deshabilita si lo usa--inplace
) y luegorenamed
sobre el archivo de destino. Use--ignore-existing
para no sobrescribir B si existe.En la práctica, nunca tuve ningún problema con esto en ext4, zfs o incluso montajes NFS.
fuente
No te preocupes,
noclobber
es una característica estándar .fuente
Preguntaste sobre NFS. Es probable que este tipo de código se rompa bajo NFS, ya que la verificación
noclobber
implica dos operaciones NFS separadas (verificar si existe un archivo, crear un nuevo archivo) y dos procesos de dos clientes NFS separados pueden entrar en una condición de carrera donde ambos tienen éxito ( ambos verifican queB.part
todavía no existe, luego ambos proceden a crearlo con éxito, como resultado se sobrescriben entre sí).Realmente no hay que hacer una verificación genérica para saber si el sistema de archivos en el que está escribiendo admitirá algo como
noclobber
atómicamente o no. Puede verificar el tipo de sistema de archivos, ya sea NFS, pero eso sería heurístico y no necesariamente una garantía. Es probable que los sistemas de archivos como SMB / CIFS (Samba) sufran los mismos problemas. Los sistemas de archivos expuestos a través de FUSE pueden o no comportarse correctamente, pero eso depende principalmente de la implementación.Un enfoque posiblemente mejor es evitar la colisión en el
B.part
paso, utilizando un nombre de archivo único (a través de la cooperación con otros agentes) para que no necesite depender de élnoclobber
. Por ejemplo, podría incluir, como parte del nombre de archivo, su nombre de host, PID y una marca de tiempo (+ posiblemente un número aleatorio). Dado que debería haber un solo proceso ejecutándose bajo un PID específico en un host en un momento dado, esto debería Garantizar la unicidad.Entonces cualquiera de:
O:
Entonces, si tiene una condición de carrera entre dos agentes, ambos procederán con la operación, pero la última operación será atómica, por lo que B existe con una copia completa de A o B no existe.
Puede reducir el tamaño de la carrera por el control de nuevo después de la copia y antes de que el
mv
o laln
operación, pero todavía hay una condición de carrera pequeña allí. Pero, independientemente de la condición de carrera, el contenido de B debe ser coherente, suponiendo que ambos procesos estén tratando de crearlo desde A (o una copia de un archivo válido como origen).Tenga en cuenta que en la primera situación con
mv
, cuando existe una carrera, el último proceso es el que gana, ya que renombrar (2) reemplazará atómicamente un archivo existente:Por lo tanto, es muy posible que los procesos que consumen B en ese momento puedan ver diferentes versiones de él (diferentes inodos) durante este proceso. Si los escritores simplemente intentan copiar el mismo contenido, y los lectores simplemente consumen el contenido del archivo, eso podría estar bien, si obtienen diferentes inodos para archivos con el mismo contenido, estarán contentos de todos modos.
El segundo enfoque que usa un enlace duro se ve mejor, pero recuerdo haber hecho experimentos con enlaces duros en un circuito cerrado en NFS de muchos clientes concurrentes y contando el éxito y todavía parecía haber algunas condiciones de carrera allí, donde parecía que dos clientes emitían un enlace duro operación al mismo tiempo, con el mismo destino, ambos parecían tener éxito. (Es posible que este comportamiento esté relacionado con la implementación particular del servidor NFS, YMMV). En cualquier caso, es probable que sea el mismo tipo de condición de carrera, donde podría terminar obteniendo dos inodos separados para el mismo archivo en los casos en que haya mucha carga. concurrencia entre escritores para desencadenar estas condiciones de carrera. Si sus escritores son consistentes (ambos copian A a B), y sus lectores solo consumen el contenido, eso podría ser suficiente.
Finalmente, mencionaste el bloqueo. Desafortunadamente, el bloqueo es muy deficiente, al menos en NFSv3 (no estoy seguro acerca de NFSv4, pero apuesto a que tampoco es bueno). Si está considerando bloquear, debería buscar diferentes protocolos para el bloqueo distribuido, posiblemente fuera de banda con el copias de archivos reales, pero eso es a la vez perjudicial, complejo y propenso a problemas como puntos muertos, por lo que diría que es mejor evitarlo.
Para obtener más información sobre el tema de la atomicidad en NFS, es posible que desee leer en el formato de buzón Maildir , que fue creado para evitar bloqueos y funcionar de manera confiable incluso en NFS. Lo hace manteniendo nombres de archivo únicos en todas partes (para que ni siquiera obtenga una B final al final).
Quizás algo más interesante para su caso particular, el formato Maildir ++ extiende Maildir para agregar soporte para la cuota del buzón y lo hace actualizando atómicamente un archivo con un nombre fijo dentro del buzón (para que pueda estar más cerca de su B). Creo que Maildir ++ intenta para agregar, que no es realmente seguro en NFS, pero hay un enfoque de recálculo que utiliza un procedimiento similar a este y es válido como un reemplazo atómico.
¡Esperemos que todos estos consejos sean útiles!
fuente
Puedes escribir un programa para esto.
Use
open(O_CREAT|O_RDWD)
para abrir el archivo de destino, lea todos los bytes y metadatos para verificar si el archivo de destino es completo, de lo contrario, hay dos posibilidades,Escritura incompleta
Otro proceso está ejecutando el mismo programa.
Intente adquirir un bloqueo de descripción de archivo abierto en el archivo de destino.
Fallo significa que hay un proceso concurrente, el proceso actual debería existir.
El éxito significa que la última escritura se bloqueó, debe comenzar de nuevo o intentar solucionarlo escribiendo en el archivo.
También tenga en cuenta que será mejor
fsync()
después de escribir en el archivo de destino antes de cerrar el archivo y liberar el bloqueo, u otro proceso podría leer datos que aún no están en el disco.https://www.gnu.org/software/libc/manual/html_node/Open-File-Description-Locks.html
Esto es importante para ayudarlo a distinguir entre un programa que se ejecuta simultáneamente y una operación bloqueada por último.
fuente
Obtendrá el resultado correcto haciendo un
cp
junto conmv
. Esto reemplazará "B" con una copia nueva de "A" o dejará "B" como estaba antes.actualizar para acomodar existente
B
:Esto no es 100% atómico, pero se acerca. Hay una condición de carrera en la que se ejecutan dos de estas cosas, ambas ingresan a la
if
prueba al mismo tiempo, ambas ven queB
no existe, y ambas ejecutan lamv
.fuente
mv B.tmp B
no se ejecutará a menos que se ejecutecp A B.tmp
primero y devuelva un código de resultado correcto. ¿Cómo es eso un fracaso? Además, estoy de acuerdo en quecp A B.tmp
sobrescribiría un existenteB.tmp
que es lo que desea hacer. Las&&
garantías que el segundo comando se ejecutará si y sólo si el primero se completa con normalidad.