Jak atomowo zmienić dowiązanie symboliczne do katalogu w busybox?

18

Próbuję (jak najbliżej) atomowo zmienić dowiązanie symboliczne. Próbowałem:

ln -sf other_dir existing_symlink

To po prostu umieściło nowe dowiązanie symboliczne w katalogu, który wskazywał istniejący link symboliczny.

ln -sf other_dir new_symlink
mv -f new_symlink existing_symlink

To samo zrobiło: przeniosło dowiązanie symboliczne do katalogu.

cp -s other_dir existing_symlink

Odmawia, ponieważ jest to katalog.

Przeczytałem, że mv -Tzostał stworzony do tego, ale busybox nie ma -Tflagi.

Shawn J. Goff
źródło

Odpowiedzi:

1

Nie rozumiem, jak można uzyskać operację atomową. Strona podręcznika dla symlink(2)mówi, że podaje, EEXISTjeśli cel już istnieje. Jeśli jądro nie obsługuje operacji atomowej, ograniczenia przestrzeni użytkownika są nieistotne.

Nie widzę też, jak to mv -Tpomaga, nawet jeśli masz. Wypróbuj na zwykłym Linux-ie, jednym z GNU mv:

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

Myślę, że będziesz musiał to zrobić w dwóch krokach: usuń stare dowiązanie symboliczne i odtwórz je ponownie.

Warren Young
źródło
1
Rzeczywiście, niestety nie ma możliwości zmodyfikowania symbolicznego atomu. Najlepsze, co możesz zrobić, to usunąć stary link i utworzyć nowy. Coreutils GNU ma taką opcję za pomocą jednego polecenia ( ln -snf), ale pod maską są jeszcze dwa wywołania systemowe.
Gilles „SO- przestań być zły”
43

To może być rzeczywiście wykonywane atomowo z rename(2), najpierw tworzenia nowego dowiązania pod tymczasową nazwą, a następnie czysto nadpisanie starego dowiązania w jednym zamachem. Jak podaje strona podręcznika :

Jeśli newpath odnosi się do dowiązania symbolicznego, łącze zostanie zastąpione.

W powłoce zrobiłbyś to mv -Tw następujący sposób:

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

Możesz straceto ostatnie polecenie, aby upewnić się, że rzeczywiście używa ono rename(2)pod maską:

$ 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

Zauważmy, że w powyższym zarówno mv -Ti stracesą specyficzne dla Linuksa.

Na FreeBSD używaj na mv -hprzemian.

Arto Bendiken
źródło
1
Bardzo dobrze! Pomocne może być stwierdzenie, że na końcu masz link od z do b!
Vincenzo Pii,
W przypadku rozwiązania niezależnego od systemu operacyjnego użyj języka skryptowego, który jest w stanie renamebezpośrednio korzystać z syscall zamiast mv -hlub mv -T. Na przykład z Perlem:perl -e 'rename "z.new", "z" or die $!'
Slaven Rezic
8

Odbierając przerwane tutaj Arto, jest to całkowicie możliwe, nawet bez mv -T, wystarczy utworzyć nowe dowiązanie symboliczne o takiej samej nazwie jak katalog docelowy i mvdo katalogu nadrzędnego celu:

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 ./

Przykład kodu wzięty przez ( http://axialcorps.wordpress.com/2013/07/03/atomically-replacing-files-and-directories/ )

mssaxm
źródło
3

Próbowałeś ln -snf?

Opcja -nzastępuje miejsce docelowe, zamiast pisać pod nim, gdy miejsce docelowe jest dowiązaniem symbolicznym do katalogu.

Twoje zdrowie

sokai
źródło
3
ln -snfnie jest atomowy: rozłącza miejsce docelowe, a następnie tworzy pożądane dowiązanie symboliczne.
Gilles 'SO - przestań być zły'
2
Biorąc pod uwagę, że PO był zainteresowany zbliżeniem się do atomowej zmiany dowiązania symbolicznego, jest to całkowicie rozsądna odpowiedź. Jeśli istnieje lepszy, który może zbliżyć się (lub być) atomowy, można go zaakceptować. Nie sądzę, że trzeba głosować z góry.
Wilco