Linux 3.15 以降、新しい renameat2
システム コールは、同じファイル システム上の 2 つのパスをアトミックに交換できます。ただし、それにアクセスするための coreutils の方法は言うまでもなく、まだ glibc ラッパーすらありません。したがって、次のようになります:
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");
(もちろん、適切なエラー処理などを行う必要があります。より洗練された renameat2
については、この要点を参照してください。 ラッパー)
そうは言っても、他の人が言及したシンボリックリンクソリューションは、より簡単で移植性が高いため、 bravo
すでに存在しており、する必要があります アトミックに更新し、代わりにシンボリック リンクを使用してください。
2020 更新:このシステム コールの glibc ラッパーは、2018 年 8 月 1 日にリリースされた glibc 2.28 (Debian Stretch、Fedora 29) 以降で利用できます。ただし、coreutils からはまだアクセスできません。
int dirfd = open(".../base", O_PATH | O_DIRECTORY | O_CLOEXEC);
renameat2(dirfd, "alpha", dirfd, "bravo", RENAME_EXCHANGE);
close(dirfd);
system("rm -rf alpha");
最終的な解決策は、シンボリックリンクと名前変更のアプローチを組み合わせることです:
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
もちろん、アルファにアクセスするアプリケーションは、パス内で変化するシンボリック リンクを処理できなければなりません。
ここでDavidのソリューションを取り上げます。これは完全にアトミックです...あなたが遭遇する唯一の問題は、 -T
mv
のオプション は非 POSIX であるため、特定の POSIX OS ではサポートされていない場合があります (FreeBSD、Solaris など ... http://pubs.opengroup.org/onlinepubs/9699919799/utilities/mv.html)。わずかな変更により、このアプローチは完全にアトミックになり、すべての POSIX OS で移植可能になります。
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 ./
例:http://axialcorps.wordpress.com/2013/07/03/atomically-replacing-files-and-directories/
シンボリックリンクを使用すると、これを行うことができます:
alpha がディレクトリ alpha_1 へのシンボリック リンクであり、そのシンボリック リンクを alpha_2 を指すように切り替えたいとします。切り替え前は次のようになります:
$ ls -l
lrwxrwxrwx alpha -> alpha_1
drwxr-xr-x alpha_1
drwxr-xr-x alpha_2
alpha が alpha_2 を参照するようにするには、ln -nsf を使用します:
$ ln -nsf alpha_2 alpha
$ ls -l
lrwxrwxrwx alpha -> alpha_2
drwxr-xr-x alpha_1
drwxr-xr-x alpha_2
これで、古いディレクトリを削除できます:
$ rm -rf alpha_1
これは実際には完全にアトミックな操作ではないことに注意してください。ただし、「ln」コマンドはリンクを解除してすぐにシンボリック リンクを再作成するため、非常に迅速に行われます。この動作は strace で確認できます:
$ strace ln -nsf alpha_2 alpha
...
symlink("alpha_2", "alpha") = -1 EEXIST (File exists)
unlink("alpha") = 0
symlink("alpha_2", "alpha") = 0
...
必要に応じてこの手順を繰り返すことができます。新しいバージョンの alpha_3 がある場合:
$ ln -nsf alpha_3 alpha
$ rm -rf alpha_2