Lo siento por la adición algo a un viejo hilo. Y por hacer una publicación tan larga.
Sólo conozco una sola manera de hacer una condición completa carrera sin rename()
en ausencia de bloqueo que deben trabajar en prácticamente cualquier sistema de archivos, incluso en NFS con reinicios del servidor y el tiempo intermittend cliente deformaciones en su lugar.
La siguiente receta es libre de condiciones de carrera en el sentido de que en ningún caso se pueden perder datos. Tampoco necesita bloqueos y puede ser realizado por clientes que no desean cooperar, excepto que todos usan el mismo algoritmo.
No es una condición de carrera libre en el sentido de que, si algo se rompe seriamente, todo queda en un estado limpio y ordenado. También tiene un corto período de tiempo, donde ni la fuente ni el destino están presentes en su ubicación, sin embargo, la fuente aún se encuentra bajo otro nombre. Y no está reforzado contra los casos en que un atacante intenta provocar daño (el rename()
es el culpable, vaya figura).
S es la Fuente, D es el destino, P (x) es dirname(x)
, C (x, y) es x/y
camino concatenación
- verificación de que el destino no existe. Solo para asegurarnos de que no hagamos los próximos pasos en vano.
- crear un nombre probablemente única T: = C (P (D), al azar)
- mkdir (T), si esto falla bucle al paso anterior
- abierto (C (T, "bloquear"), O_EXCL), si esto falla rmdir (T) haciendo caso omiso de errores y bucle al paso anterior
- de cambio de nombre (S, C (T, "tmp"))
- enlace (C (T, "tmp"), D)
- unlink (C (T, "tmp"))
- unlink (C (T, "bloquear"))
- rmdir (T)
Algoritmo safe_rename(S,D)
explicó:
El problema es que queremos para asegurarse de que no hay una condición de carrera, ni en el origen ni el destino. Se supone que (casi) puede pasar cualquier cosa entre cada paso, pero todos los demás procesos siguen exactamente el mismo algoritmo al hacer cambios de condición de condición de carrera. Esto incluye que los directorios temporales T nunca se tocan, excepto después de asegurarse (este es un proceso manual) de que el proceso que utiliza el directorio ha muerto y no puede resucitar (como continuar una máquina hibernando después de una restauración).
Para hacer correctamente el rename()
, necesitamos un lugar donde escondernos. Así que construimos un directorio de una manera que asegura que nadie más (que esté siguiendo el mismo algoritmo) lo use accidentalmente. Sin embargo, no se garantiza que mkdir()
sea atómico en NFS. Por lo tanto, debemos asegurarnos de tener alguna garantía de que estamos solos en el directorio. Esto es O_EXCL
en el archivo de bloqueo. Esto es, estrictamente hablando, sin bloqueo, es un semáforo.
Excepto en casos tan raros, mkdir()
suele ser atómico. También podemos crear el uso de algún nombre aleatorio criptográficamente seguro para el directorio, agregar algunos GUID, nombre de host y PID para asegurarnos de que sea muy poco probable que otra persona elija el mismo nombre por casualidad. Sin embargo, para probar que el algoritmo es correcto, necesitamos este archivo llamado lock
.
Ahora que tenemos un directorio prácticamente vacío, podemos rename()
con seguridad la fuente allí. Esto asegura que nadie más altera la fuente hasta que lo hagamos unlink()
. (Bueno, el contenido puede cambiar, esto no es un problema.)
Ahora se puede aplicar el truco link()
para asegurarnos de no sobrescribir el destino.
condición de carrera Después, el unlink()
se puede hacer libre de la fuente restante. El resto es limpieza.
Sólo hay un problema de la izquierda:
En caso de que la falla link()
hemos pasado ya la fuente. Para la limpieza adecuada, tenemos que moverlo hacia atrás. Esto se puede hacer llamando al safe_rename(C(T,"tmp"),S)
. Si esto no funciona, también, todo lo que podemos hacer es tratar de limpieza tanto como podemos (unlink(C(T,"lock"))
, rmdir(T)
) y dejar los escombros detrás de limpieza manual por el administrador.
notas finales:
para ayudar a limpiar los escombros en el caso, se puede utilizar posiblemente algunos mejores que el nombre del archivo tmp
. Elegir nombres hábilmente puede endurecer algo el algoritmo contra ataques también.
Y si está trenes cargados de mover archivos en algún lugar se puede volver a utilizar el directorio, por supuesto.
Sin embargo, estoy de acuerdo, que este algoritmo es demasiado simple y algo así como O_EXCL
en rename()
falta.
tal vez debería considerar el uso de un mecanismo de bloqueo explícita en lugar de depender de un bloqueo implícito en (un envoltorio de) la función de cambiar el nombre. Si lo que crea 'B' es un programa bajo su control, posiblemente podría usar primitivas de sincronización entre procesos. –