Desde Linux 3.15, el nuevo renameat2
La llamada al sistema puede intercambiar atómicamente dos rutas en el mismo sistema de archivos. Sin embargo, aún no hay un envoltorio glibc para él, y mucho menos una forma coreutils de acceder a él. Entonces se vería algo como esto:
int dirfd = open(".../base", O_PATH | O_DIRECTORY | O_CLOEXEC);
syscall(SYS_renameat2, dirfd, "alpha", dirfd, "bravo", RENAME_EXCHANGE);
close(dirfd);
system("rm -rf alpha");
(Por supuesto, debe hacer un manejo de errores adecuado, etc.; consulte esta esencia para obtener un renameat2
más sofisticado) envoltorio.)
Dicho esto, la solución de enlace simbólico mencionada por otros es más fácil y portátil, así que a menos que bravo
ya existe y usted debe actualícelo atómicamente, vaya con el enlace simbólico en su lugar.
Actualización 2020:un contenedor de glibc para esta llamada del sistema está disponible desde glibc 2.28, lanzado el 01-08-2018 (Debian Stretch, Fedora 29). Sin embargo, todavía no es accesible a través de coreutils.
int dirfd = open(".../base", O_PATH | O_DIRECTORY | O_CLOEXEC);
renameat2(dirfd, "alpha", dirfd, "bravo", RENAME_EXCHANGE);
close(dirfd);
system("rm -rf alpha");
La solución final es combinar el enlace simbólico y el enfoque de cambio de nombre:
mkdir alpha_real
ln -s alpha_real alpha
# now use "alpha"
mkdir beta_real
ln -s beta_real tmp
# atomically rename "tmp" to "alpha"
# use -T to actually replace "alpha" instead of moving *into* "alpha"
mv -T tmp alpha
Por supuesto, la aplicación que accede a alfa tiene que ser capaz de lidiar con los enlaces simbólicos que cambian en la ruta.
Retomando la solución de David aquí, que es completamente atómica... el único problema con el que te encontrarías es que el -T
opción para mv
no es POSIX, por lo que es posible que ciertos sistemas operativos POSIX no lo admitan (FreeBSD, Solaris, etc. ... http://pubs.opengroup.org/onlinepubs/9699919799/utilities/mv.html). Con una ligera modificación, este enfoque se puede modificar para que sea completamente atómico y portátil en todos los sistemas operativos POSIX:
mkdir -p tmp/real_dir1 tmp/real_dir2
touch tmp/real_dir1/a tmp/real_dir2/a
# start with ./target_dir pointing to tmp/real_dir1
ln -s tmp/real_dir1 target_dir
# create a symlink named target_dir in tmp, pointing to real_dir2
ln -sf tmp/real_dir2 tmp/target_dir
# atomically mv it into ./ replacing ./target_dir
mv tmp/target_dir ./
ejemplo a través de:http://axialcorps.wordpress.com/2013/07/03/atomically-replacing-files-and-directories/
Puedes hacer esto si usas enlaces simbólicos:
Digamos que alfa es un enlace simbólico al directorio alpha_1, y desea cambiar el enlace simbólico para que apunte a alpha_2. Así es como se ve antes del cambio:
$ ls -l
lrwxrwxrwx alpha -> alpha_1
drwxr-xr-x alpha_1
drwxr-xr-x alpha_2
Para hacer que alfa se refiera a alpha_2, use ln -nsf:
$ ln -nsf alpha_2 alpha
$ ls -l
lrwxrwxrwx alpha -> alpha_2
drwxr-xr-x alpha_1
drwxr-xr-x alpha_2
Ahora puede eliminar el directorio antiguo:
$ rm -rf alpha_1
Tenga en cuenta que esto NO es en realidad una operación completamente atómica, pero sucede muy rápidamente ya que el comando "ln" desvincula y luego vuelve a crear inmediatamente el enlace simbólico. Puede verificar este comportamiento con strace:
$ strace ln -nsf alpha_2 alpha
...
symlink("alpha_2", "alpha") = -1 EEXIST (File exists)
unlink("alpha") = 0
symlink("alpha_2", "alpha") = 0
...
Puede repetir este procedimiento como desee:p. cuando tengas una nueva versión, alpha_3:
$ ln -nsf alpha_3 alpha
$ rm -rf alpha_2