Da Linux 3.15, il nuovo renameat2
la chiamata di sistema può scambiare atomicamente due percorsi sullo stesso file system. Tuttavia, non esiste ancora nemmeno un wrapper glibc per esso, per non parlare di un modo coreutils per accedervi. Quindi sarebbe simile a questo:
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");
(Naturalmente, dovresti eseguire una corretta gestione degli errori ecc. – vedi questo riassunto per un renameat2
più sofisticato involucro.)
Detto questo, la soluzione del collegamento simbolico menzionata da altri è sia più semplice che portabile, quindi a meno che bravo
esiste già e devi aggiornalo atomicamente, vai invece con il link simbolico.
Aggiornamento 2020:un wrapper glibc per questa chiamata di sistema è disponibile a partire da glibc 2.28, rilasciato il 01-08-2018 (Debian Stretch, Fedora 29). Tuttavia, non è ancora accessibile tramite 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 soluzione finale è combinare l'approccio symlink e rinomina:
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
Naturalmente, l'applicazione che accede ad alpha deve essere in grado di gestire i collegamenti simbolici che cambiano nel percorso.
Riprendendo la soluzione di David qui, che è completamente atomica... l'unico problema che potresti incontrare è che il -T
opzione per mv
non è POSIX, quindi alcuni sistemi operativi POSIX potrebbero non supportarlo (FreeBSD, Solaris, ecc. ... http://pubs.opengroup.org/onlinepubs/9699919799/utilities/mv.html). Con una leggera modifica, questo approccio può essere modificato per essere completamente atomico e portabile su tutti i sistemi operativi 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 ./
esempio tramite:http://axialcorps.wordpress.com/2013/07/03/atomically-replacing-files-and-directories/
Puoi farlo se usi i collegamenti simbolici:
Diciamo che alpha è un collegamento simbolico alla directory alpha_1 e si desidera cambiare il collegamento simbolico in modo che punti a alpha_2. Ecco come appare prima del passaggio:
$ ls -l
lrwxrwxrwx alpha -> alpha_1
drwxr-xr-x alpha_1
drwxr-xr-x alpha_2
Per fare in modo che alpha si riferisca ad alpha_2, usa ln -nsf:
$ ln -nsf alpha_2 alpha
$ ls -l
lrwxrwxrwx alpha -> alpha_2
drwxr-xr-x alpha_1
drwxr-xr-x alpha_2
Ora puoi rimuovere la vecchia directory:
$ rm -rf alpha_1
Si noti che questa NON è in realtà un'operazione completamente atomica, ma avviene molto rapidamente poiché il comando "ln" scollega entrambi e quindi ricrea immediatamente il collegamento simbolico. Puoi verificare questo comportamento con strace:
$ strace ln -nsf alpha_2 alpha
...
symlink("alpha_2", "alpha") = -1 EEXIST (File exists)
unlink("alpha") = 0
symlink("alpha_2", "alpha") = 0
...
È possibile ripetere questa procedura come desiderato:ad es. quando hai una nuova versione, alpha_3:
$ ln -nsf alpha_3 alpha
$ rm -rf alpha_2