Как атомарно изменить символическую ссылку на каталог в busybox?

18

Я пытаюсь (как можно ближе) атомно изменить символическую ссылку. Я пробовал:

ln -sf other_dir existing_symlink

Это просто поместит новую символическую ссылку в каталог, на который указывает существующая_симлинк.

ln -sf other_dir new_symlink
mv -f new_symlink existing_symlink

Это сделало то же самое: оно переместило символическую ссылку в каталог.

cp -s other_dir existing_symlink

Он отказывается, потому что это каталог.

Я читал, что mv -Tэто было сделано для этого, но у busybox нет -Tфлага.

Шон Дж. Гофф
источник

Ответы:

1

Я не понимаю, как вы можете получить атомную операцию. Страница руководства для symlink(2)говорит, что дает, EEXISTесли цель уже существует. Если ядро ​​не поддерживает атомарную работу, ваши пользовательские ограничения не имеют значения.

Я тоже не вижу, как mv -Tпомогает, даже если он у тебя есть. Попробуйте это на обычном Linux-боксе, один с GNU mv:

$ mkdir a b
$ ln -s a z
$ mv -T b z
mv: cannot overwrite non-directory `z' with directory `b'

Я думаю, вам придется сделать это в два этапа: удалить старую символическую ссылку и воссоздать ее.

Уоррен Янг
источник
1
На самом деле, к сожалению, нет способа изменить символику атомарно. Лучшее, что вы можете сделать, это удалить старую ссылку и создать новую. В GNU coreutils есть возможность сделать это с помощью одной команды ( ln -snf), но под капотом все еще есть два системных вызова.
Жиль "ТАК - перестань быть злым"
43

Это может действительно быть сделано атомарно с rename(2), сначала создать новую символическую ссылку под временным именем , а затем аккуратно перезаписывать старый симлинк на одном дыхании. Как говорится на странице руководства :

Если newpath ссылается на символическую ссылку, ссылка будет перезаписана.

В оболочке вы можете сделать это mv -Tследующим образом:

$ mkdir a b
$ ln -s a z
$ ln -s b z.new
$ mv -T z.new z

Вы можете использовать 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

Обратите внимание, что в приведенном выше, оба mv -Tи straceявляются специфичными для Linux.

На FreeBSD используйте mv -hпоочередно.

Арто Бендикен
источник
1
Очень хорошо! Было бы полезно сказать, что в конце у вас есть ссылка от z до b!
Винченцо Пии,
Для независимого от ОС решения используйте язык сценариев, который способен использовать renameсистемный вызов напрямую вместо mv -hили mv -T. Например с Perl:perl -e 'rename "z.new", "z" or die $!'
Slaven Rezic
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 ./

Пример кода взят через ( http://axialcorps.wordpress.com/2013/07/03/atomically-replacing-files-and-directories/ )

mssaxm
источник
3

Ты пробовал ln -snf?

Опция -nперезаписывает место назначения, а не записывает под ним, когда место назначения является символической ссылкой на каталог.

ура

sokai
источник
3
ln -snfне является атомарным: он отменяет связь с адресатом, а затем создает желаемую символическую ссылку.
Жиль "ТАК - перестань быть злым"
2
Учитывая, что OP был заинтересован в том, чтобы "как можно ближе" приблизиться к атомному изменению символической ссылки, это вполне разумный ответ. Если есть лучшая, которая может стать ближе (или быть) атомной, то это можно принять. Я не думаю, что есть необходимость понижать голос.
Wilco