Seit Linux 3.15 der neue renameat2
Systemaufruf kann zwei Pfade auf demselben Dateisystem atomar austauschen. Es gibt jedoch noch nicht einmal einen Glibc-Wrapper dafür, geschweige denn eine Coreutils-Methode, um darauf zuzugreifen. Es würde also etwa so aussehen:
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");
(Natürlich sollten Sie eine angemessene Fehlerbehandlung usw. durchführen – sehen Sie sich dieses Wesentliche für einen anspruchsvolleren renameat2
an Verpackung.)
Abgesehen davon ist die von anderen erwähnte Symlink-Lösung sowohl einfacher als auch portierbar, es sei denn bravo
existiert bereits und Sie müssen Aktualisieren Sie es atomar, verwenden Sie stattdessen den Symlink.
2020-Update:Ein glibc-Wrapper für diesen Systemaufruf ist seit glibc 2.28 verfügbar, veröffentlicht am 01.08.2018 (Debian Stretch, Fedora 29). Es ist jedoch immer noch nicht über Coreutils zugänglich.
int dirfd = open(".../base", O_PATH | O_DIRECTORY | O_CLOEXEC);
renameat2(dirfd, "alpha", dirfd, "bravo", RENAME_EXCHANGE);
close(dirfd);
system("rm -rf alpha");
Die endgültige Lösung ist die Kombination des Symlink- und des Rename-Ansatzes:
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
Natürlich muss die Anwendung, die auf Alpha zugreift, mit Symlinks umgehen können, die sich im Pfad ändern.
Wenn Sie hier Davids Lösung aufgreifen, die vollständig atomar ist ... das einzige Problem, auf das Sie stoßen würden, ist das -T
Option für mv
ist kein POSIX, und daher wird es von bestimmten POSIX-Betriebssystemen möglicherweise nicht unterstützt (FreeBSD, Solaris usw. ... http://pubs.opengroup.org/onlinepubs/9699919799/utilities/mv.html). Mit geringfügigen Modifikationen kann dieser Ansatz so geändert werden, dass er vollständig atomar und über alle POSIX-Betriebssysteme hinweg portierbar ist:
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 ./
B. über:http://axialcorps.wordpress.com/2013/07/03/atomically-replacing-files-and-directories/
Sie können dies tun, wenn Sie symbolische Links verwenden:
Nehmen wir an, alpha ist ein symbolischer Link zum Verzeichnis alpha_1, und Sie möchten den symbolischen Link so ändern, dass er auf alpha_2 zeigt. So sieht das vor der Umstellung aus:
$ ls -l
lrwxrwxrwx alpha -> alpha_1
drwxr-xr-x alpha_1
drwxr-xr-x alpha_2
Um alpha auf alpha_2 verweisen zu lassen, verwenden Sie ln -nsf:
$ ln -nsf alpha_2 alpha
$ ls -l
lrwxrwxrwx alpha -> alpha_2
drwxr-xr-x alpha_1
drwxr-xr-x alpha_2
Jetzt können Sie das alte Verzeichnis entfernen:
$ rm -rf alpha_1
Beachten Sie, dass dies KEINE vollständig atomare Operation ist, aber sehr schnell geschieht, da der Befehl "ln" sowohl die Verknüpfung aufhebt als auch den Symlink sofort neu erstellt. Sie können dieses Verhalten mit strace:
überprüfen$ strace ln -nsf alpha_2 alpha
...
symlink("alpha_2", "alpha") = -1 EEXIST (File exists)
unlink("alpha") = 0
symlink("alpha_2", "alpha") = 0
...
Diesen Vorgang können Sie beliebig wiederholen:z. wenn Sie eine neue Version haben, alpha_3:
$ ln -nsf alpha_3 alpha
$ rm -rf alpha_2