Depuis Linux 3.15, le nouveau renameat2
L'appel système peut échanger atomiquement deux chemins sur le même système de fichiers. Cependant, il n'y a même pas encore de wrapper glibc pour cela, et encore moins un moyen coreutils d'y accéder. Cela ressemblerait donc à ceci :
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");
(Bien sûr, vous devez gérer correctement les erreurs, etc. - voir cet essentiel pour un renameat2
plus sophistiqué emballage.)
Cela dit - la solution de lien symbolique mentionnée par d'autres est à la fois plus facile et portable, donc à moins que bravo
existe déjà et vous devez mettez-le à jour de manière atomique, utilisez plutôt le lien symbolique.
Mise à jour 2020 :un wrapper glibc pour cet appel système est disponible depuis la glibc 2.28, publiée le 01/08/2018 (Debian Stretch, Fedora 29). Cependant, il n'est toujours pas accessible via 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 solution finale est de combiner le lien symbolique et l'approche de changement de nom :
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
Bien sûr, l'application accédant à alpha doit être capable de gérer les changements de liens symboliques dans le chemin.
Reprenant la solution de David ici, qui est entièrement atomique ... le seul problème que vous rencontrerez est que le -T
option pour mv
n'est pas POSIX, et donc certains systèmes d'exploitation POSIX peuvent ne pas le supporter (FreeBSD, Solaris, etc. ... http://pubs.opengroup.org/onlinepubs/9699919799/utilities/mv.html). Avec de légères modifications, cette approche peut être modifiée pour être entièrement atomique et portable sur tous les systèmes d'exploitation 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 ./
exemple via :http://axialcorps.wordpress.com/2013/07/03/atomically-replacing-files-and-directories/
Vous pouvez le faire si vous utilisez des liens symboliques :
Supposons que alpha soit un lien symbolique vers le répertoire alpha_1 et que vous souhaitiez basculer le lien symbolique pour qu'il pointe vers alpha_2. Voici à quoi cela ressemble avant le changement :
$ ls -l
lrwxrwxrwx alpha -> alpha_1
drwxr-xr-x alpha_1
drwxr-xr-x alpha_2
Pour faire référence à alpha_2, utilisez ln -nsf :
$ ln -nsf alpha_2 alpha
$ ls -l
lrwxrwxrwx alpha -> alpha_2
drwxr-xr-x alpha_1
drwxr-xr-x alpha_2
Vous pouvez maintenant supprimer l'ancien répertoire :
$ rm -rf alpha_1
Notez qu'il ne s'agit PAS en fait d'une opération entièrement atomique, mais cela se produit très rapidement puisque la commande "ln" dissocie puis recrée immédiatement le lien symbolique. Vous pouvez vérifier ce comportement avec strace :
$ strace ln -nsf alpha_2 alpha
...
symlink("alpha_2", "alpha") = -1 EEXIST (File exists)
unlink("alpha") = 0
symlink("alpha_2", "alpha") = 0
...
Vous pouvez répéter cette procédure si vous le souhaitez :par ex. lorsque vous avez une nouvelle version, alpha_3 :
$ ln -nsf alpha_3 alpha
$ rm -rf alpha_2