如何原子地将符号链接更改为 busybox 中的目录?

Sha*_*off 19 linux directory symlink busybox

我正在尝试(尽可能接近)以原子方式更改符号链接。我试过了:

ln -sf other_dir existing_symlink
Run Code Online (Sandbox Code Playgroud)

这只是将新符号链接放在 existing_symlink 指向的目录中。

ln -sf other_dir new_symlink
mv -f new_symlink existing_symlink
Run Code Online (Sandbox Code Playgroud)

这做了同样的事情:它将符号链接移动到目录中。

cp -s other_dir existing_symlink
Run Code Online (Sandbox Code Playgroud)

它拒绝,因为它是一个目录。

我读过这mv -T是为此而制作的,但是busybox没有-T标志。

小智 49

的确可以用原子做rename(2),由下一个临时的名字第一次创建新的符号链接,然后干净利落地覆盖旧的符号链接一气呵成。正如手册页所述:

如果newpath引用符号链接,则该链接将被覆盖。

在 shell 中,您可以mv -T按如下方式执行此操作:

$ mkdir a b
$ ln -s a z
$ ln -s b z.new
$ mv -T z.new z
Run Code Online (Sandbox Code Playgroud)

您可以strace使用最后一个命令来确保它确实rename(2)在后台使用:

$ strace mv -T z.new z
lstat64("z.new", {st_mode=S_IFLNK|0777, st_size=1, ...}) = 0
lstat64("z", {st_mode=S_IFLNK|0777, st_size=1, ...}) = 0
rename("z.new", "z")                    = 0
Run Code Online (Sandbox Code Playgroud)

请注意,在上面,mv -Tstrace都是 Linux 特定的。

在 FreeBSD 上,mv -h交替使用。


小智 9

你有没有尝试过ln -snf

-n当目标是目录的符号链接时,该选项会覆盖目标而不是在目标下写入。

干杯

  • `ln -snf` 不是原子的:它取消目标的链接,然后创建所需的符号链接。 (6认同)
  • 鉴于OP有兴趣“尽可能接近”以原子方式更改符号链接,这是一个完全合理的答案。如果有一个更好的可以更接近(或)原子化的,那么这个就可以被接受。我认为没有必要投反对票。 (2认同)
  • 不确定busybox,但我已经通过strace确认,在现代Linux发行版(例如Ubuntu Bionic)上,ln -snf以原子方式工作,它不会调用unlink,它实际上会执行“ln -s source a_tmp_file && mv -T a_tmp_file symlink” ” (2认同)

小智 8

从 Arto 在这里停下来的地方继续,这是完全可能的,即使没有mv -T,您只需要创建一个与目标目录同名的新符号链接,并将mv其放入目标的父目录中:

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 ./
Run Code Online (Sandbox Code Playgroud)

通过(http://axiscorps.wordpress.com/2013/07/03/atomically-replacing-files-and-directories/)获取的代码示例


War*_*ung 2

我不明白如何获得原子操作。的手册页symlink(2)说它给出了EEXIST目标是否已经存在。如果内核不支持原子操作,那么您的用户空间限制就无关紧要。

我也不知道有什么mv -T帮助,即使你有它。在一台带有 GNU mv 的常规 Linux 机器上尝试一下:

$ mkdir a b
$ ln -s a z
$ mv -T b z
mv: cannot overwrite non-directory `z' with directory `b'
Run Code Online (Sandbox Code Playgroud)

我认为您必须分两步执行此操作:删除旧的符号链接并重新创建它。