Od Linuxu 3.15 nový renameat2
systémové volání může atomicky vyměňovat dvě cesty na stejném systému souborů. Zatím však pro něj neexistuje ani obal glibc, natož způsob, jak k němu získat přístup pomocí coreutils. Takže by to vypadalo nějak takto:
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");
(Samozřejmě byste měli provést správné zpracování chyb atd. – v tomto souhrnu najdete sofistikovanější renameat2
obal.)
To znamená – řešení symbolického odkazu zmiňované ostatními je jednodušší a přenosné, takže pokud bravo
již existuje a vy musíte atomicky jej aktualizujte, místo toho použijte symbolický odkaz.
Aktualizace 2020:obal glibc pro toto systémové volání je k dispozici od glibc 2.28, vydaného 1. 8. 2018 (Debian Stretch, Fedora 29). Stále však není přístupný prostřednictvím coreutils.
int dirfd = open(".../base", O_PATH | O_DIRECTORY | O_CLOEXEC);
renameat2(dirfd, "alpha", dirfd, "bravo", RENAME_EXCHANGE);
close(dirfd);
system("rm -rf alpha");
Konečným řešením je kombinace přístupu symbolického odkazu a přejmenování:
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
Samozřejmě, že aplikace přistupující k alfa verzi musí být schopna vypořádat se se symbolickými odkazy měnícími se v cestě.
Zde se můžete podívat na Davidovo řešení, které je plně atomové ... jediný problém, na který byste narazili, je, že -T
možnost pro mv
není POSIX, a proto jej některé operační systémy POSIX nemusí podporovat (FreeBSD, Solaris atd. ... http://pubs.opengroup.org/onlinepubs/9699919799/utilities/mv.html). S mírnou úpravou lze tento přístup změnit tak, aby byl plně atomický a přenosný napříč všemi operačními systémy 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 ./
například přes:http://axialcorps.wordpress.com/2013/07/03/atomically-replacing-files-and-directories/
Můžete to udělat, pokud použijete symbolické odkazy:
Řekněme, že alpha je symbolický odkaz na adresář alpha_1 a chcete přepnout symbolický odkaz tak, aby ukazoval na alpha_2. Zde je návod, jak to vypadá před přepnutím:
$ ls -l
lrwxrwxrwx alpha -> alpha_1
drwxr-xr-x alpha_1
drwxr-xr-x alpha_2
Chcete-li, aby alfa odkazovalo na alpha_2, použijte ln -nsf:
$ ln -nsf alpha_2 alpha
$ ls -l
lrwxrwxrwx alpha -> alpha_2
drwxr-xr-x alpha_1
drwxr-xr-x alpha_2
Nyní můžete odstranit starý adresář:
$ rm -rf alpha_1
Všimněte si, že to ve skutečnosti NENÍ plně atomická operace, ale stane se to velmi rychle, protože příkaz "ln" oba odpojí a poté okamžitě znovu vytvoří symbolický odkaz. Toto chování můžete ověřit pomocí strace:
$ strace ln -nsf alpha_2 alpha
...
symlink("alpha_2", "alpha") = -1 EEXIST (File exists)
unlink("alpha") = 0
symlink("alpha_2", "alpha") = 0
...
Tento postup můžete libovolně opakovat:např. když máte novou verzi, alpha_3:
$ ln -nsf alpha_3 alpha
$ rm -rf alpha_2