¿Cómo se puede cambiar atómicamente un enlace simbólico a un directorio en busybox?

18

Estoy intentando (lo más cerca posible) cambiar atómicamente un enlace simbólico. He intentado:

ln -sf other_dir existing_symlink

Eso solo puso el nuevo enlace simbólico en el directorio al que apuntaba el enlace simbólico existente.

ln -sf other_dir new_symlink
mv -f new_symlink existing_symlink

Eso hizo lo mismo: movió el enlace simbólico al directorio.

cp -s other_dir existing_symlink

Se niega porque es un directorio.

He leído que mv -Tse hizo para esto, pero busybox no tiene la -Tbandera.

Shawn J. Goff
fuente

Respuestas:

1

No veo cómo puedes obtener una operación atómica. La página del manual symlink(2)dice que da EEXISTsi el objetivo ya existe. Si el kernel no admite la operación atómica, las limitaciones de su tierra de usuario son irrelevantes.

Tampoco veo cómo mv -Tayuda, incluso si lo tienes. Pruébelo en una caja de Linux normal, una con GNU mv:

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

Creo que tendrá que hacer esto en dos pasos: elimine el enlace simbólico anterior y vuelva a crearlo.

Warren Young
fuente
1
De hecho, desafortunadamente no hay forma de que pueda modificar un símbolo simbólico atómicamente. Lo mejor que puede hacer es eliminar el enlace anterior y crear uno nuevo. GNU coreutils tiene una opción para hacer esto con un solo comando ( ln -snf), pero todavía hay dos llamadas al sistema bajo el capó.
Gilles 'SO- deja de ser malvado'
43

De hecho, esto se puede hacer atómicamente rename(2), primero creando el nuevo enlace simbólico con un nombre temporal y luego sobrescribiendo limpiamente el enlace simbólico anterior de una vez. Como dice la página del manual :

Si newpath se refiere a un enlace simbólico, el enlace se sobrescribirá.

En el shell, haría esto de la mv -Tsiguiente manera:

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

Puede straceusar ese último comando para asegurarse de que realmente se está utilizando rename(2)debajo del capó:

$ 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

Tenga en cuenta que en lo anterior, ambos mv -Ty straceson específicos de Linux.

En FreeBSD, use mv -halternativamente.

Arto Bendiken
fuente
1
¡Muy agradable! ¡Puede ser útil decir que al final tiene un enlace de z a b!
Vincenzo Pii
Para una solución independiente del sistema operativo, use un lenguaje de secuencias de comandos que sea capaz de usar la renamellamada al sistema directamente en lugar de mv -ho mv -T. Por ejemplo con Perl:perl -e 'rename "z.new", "z" or die $!'
Slaven Rezic
8

Retomando donde dejó Arto aquí, esto es completamente posible, incluso sin mv -Teso, solo necesita crear un nuevo enlace simbólico con el mismo nombre que el directorio de destino y mven el directorio principal de su objetivo:

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

Ejemplo de código tomado a través de ( http://axialcorps.wordpress.com/2013/07/03/atomically-replacing-files-and-directories/ )

mssaxm
fuente
3

¿Lo has intentado ln -snf?

La opción -nsobrescribe el destino en lugar de escribir debajo de él cuando el destino es un enlace simbólico a un directorio.

Salud

sokai
fuente
3
ln -snfno es atómico: desvincula el destino y luego crea el enlace simbólico deseado.
Gilles 'SO- deja de ser malvado'
2
Dado que el OP estaba interesado en llegar "lo más cerca posible [e]" de cambiar atómicamente un enlace simbólico, esta es una respuesta perfectamente razonable. Si hay uno mejor que puede acercarse (o ser) atómico, ese puede ser aceptado. No creo que haya necesidad de votar en contra.
Wilco