¿Puedes cambiar a qué apunta un enlace simbólico después de que se crea?

122

¿Algún sistema operativo proporciona un mecanismo (llamada al sistema, no un programa de línea de comando) para cambiar el nombre de ruta al que hace referencia un enlace simbólico (enlace simbólico), aparte de desvincular el antiguo y crear uno nuevo?

El estándar POSIX no. Solaris 10 no lo hace. MacOS X 10.5 (Leopard) no. (Estoy bastante seguro de que ni AIX ni HP-UX tampoco. A juzgar por esta lista de llamadas al sistema Linux, Linux tampoco tiene esa llamada al sistema).

¿Hay algo que haga?

(Espero que la respuesta sea "No").


Como probar que un negativo es difícil, reorganicemos la pregunta.

Si sabe que algunos sistemas operativos (similares a Unix) que no figuran en la lista no tienen ninguna llamada al sistema para reescribir el valor de un enlace simbólico (la cadena devuelta por readlink()) sin eliminar el enlace simbólico anterior y crear uno nuevo, agréguelo, o ellos. en una respuesta

Jonathan Leffler
fuente
¿Qué tiene de malo simplemente volver a vincular? ¿Por qué no simplemente emitir el lncomando (o la API equivalente) sobrescribiendo el enlace anterior? ¿Qué problema estás teniendo?
S.Lott
9
Divertido: estoy preguntando si hay una llamada al sistema para hacer un trabajo de programación, y la pregunta se marca como 'pertenece a otro sitio'.
Jonathan Leffler
3
Divertido: no estaba claro si estaba buscando una llamada al sistema y simplemente editó la pregunta para agregar este detalle. Entonces, ¿cómo puede esperar que la gente deduzca algo antes de que lo escriba?
Pascal Thivent
2
@ S.Lott: estoy escribiendo un documento sobre seguridad y enlaces simbólicos. En un momento, afirmo que "el propietario real, el grupo, los permisos en el enlace simbólico en sí mismo son irrelevantes" y el razonamiento es que el propietario del enlace simbólico solo puede eliminarlo y no cambiar el valor. Estoy comprobando que no hay otra forma que no sea eliminar el enlace simbólico de lograr el efecto de 'reescribir el valor del enlace simbólico'. Estoy ignorando el acceso directo al disco sin procesar y pirateando el FS de esa manera: requiere privilegio de root y mis preocupaciones son con usuarios no root, no con lo que root puede hacer.
Jonathan Leffler
44
@Pascal: Lo siento, no me di cuenta de que no estaba claro que estaba hablando de llamadas al sistema hasta que la gente se fue por la tangente de lo que pretendía (que evidentemente era diferente de lo que dije). Lamento haberme engañado; No fue intencional.
Jonathan Leffler

Respuestas:

106

AFAIK, no, no puedes. Tienes que eliminarlo y recrearlo. En realidad, puede sobrescribir un enlace simbólico y así actualizar el nombre de ruta al que hace referencia:

$ ln -s .bashrc test
$ ls -al test
lrwxrwxrwx 1 pascal pascal 7 2009-09-23 17:12 test -> .bashrc
$ ln -s .profile test
ln: creating symbolic link `test': File exists
$ ln -s -f .profile test
$ ls -al test
lrwxrwxrwx 1 pascal pascal 8 2009-09-23 17:12 test -> .profile

EDITAR : Como el OP señaló en un comentario, el uso de la --forceopción hará que se lnrealice una llamada al sistema unlink()antes symlink(). A continuación, la salida de stracemi caja de Linux lo demuestra:

$ strace -o /tmp/output.txt ln -s -f .bash_aliases test
$ grep -C3 ^unlink /tmp/output.txt 
lstat64("test", {st_mode=S_IFLNK|0777, st_size=7, ...}) = 0
stat64(".bash_aliases", {st_mode=S_IFREG|0644, st_size=2043, ...}) = 0
symlink(".bash_aliases", "test")        = -1 EEXIST (File exists)
unlink("test")                          = 0
symlink(".bash_aliases", "test")        = 0
close(0)                                = 0
close(1)                                = 0

Así que supongo que la respuesta final es "no".

EDITAR : Lo siguiente se copia de la respuesta de Arto Bendiken en unix.stackexchange.com, alrededor de 2016.

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

Nota del editor: así es como Capistrano lo ha hecho durante años, desde ~ 2.15. Ver esta solicitud de extracción .

Pascal Thivent
fuente
2
¿La opción '-f' no obliga a 'ln' a hacer las llamadas al sistema unlink () y luego symlink ()?
Jonathan Leffler
1
Lo hace. Pero la pregunta podría haber sido percibida como "¿cómo hago eso en un paso" y luego se reduce a la definición de "paso" - línea de comando? syscall?
Michael Krelin - hacker
1
Me acabo de dar cuenta de que agregaste texto syscall a tu pregunta ;-)
Michael Krelin - hacker
2
@Pascal: lo hace. En Solaris, la salida de 'truss ln -spx' y 'truss ln -s -fpx' muestra una llamada unlink () antes de la llamada symlink () en el segundo caso (y una llamada symlink () fallida en el primero). Esperaría que se aplique a la mayoría, si no a todas, las variantes de Unix.
Jonathan Leffler
12
+1 al comentario de @Taai: si se trata de un enlace simbólico que elimina la referencia (apunta a) un directorio, debe especificar "-n" para asegurarse de que este truco funciona.
wally
161

¡Sí tu puedes!

$ ln -sfn source_file_or_directory_name softlink_name
Taai
fuente
9
Gracias por responder. La -fopción significa 'eliminar el destino existente' antes de crear el nuevo. Entonces, este comando logra el resultado, pero al hacerlo unlink(2)seguido de symlink(2). De esto no se trataba la pregunta original. También tenga en cuenta que la respuesta aceptada y la siguiente respuesta más votada se utilizan ln -sfpara hacer el trabajo, pero como lo stracemuestra el resultado, lo hace unlink()y symlink()porque no hay una llamada al sistema para modificar un enlace simbólico.
Jonathan Leffler
17
Parece que se requiere -n cuando el enlace original va a un directorio en lugar de a un archivo.
impulsado por vapor el
11
El interruptor 'n' es importante. Luché con el problema de que el enlace simbólico no se actualizó, pero el comando creó otro enlace dentro del existente porque la opción 'no desarmar' no estaba configurada.
Jonathan Gruber
14

No es necesario desvincular explícitamente el antiguo enlace simbólico. Puedes hacerlo:

ln -s newtarget temp
mv temp mylink

(o use el enlace simbólico equivalente y cambie el nombre de las llamadas). Esto es mejor que desvincular explícitamente porque el cambio de nombre es atómico, por lo que puede estar seguro de que el enlace siempre apuntará al objetivo antiguo o al nuevo. Sin embargo, esto no reutilizará el inodo original.

En algunos sistemas de archivos, el objetivo del enlace simbólico se almacena en el inodo mismo (en lugar de la lista de bloqueo) si es lo suficientemente corto; Esto se determina en el momento en que se crea.

Con respecto a la afirmación de que el propietario y el grupo reales son irrelevantes, el enlace simbólico (7) en Linux dice que hay un caso en el que es significativo:

El propietario y el grupo de un enlace simbólico existente se pueden cambiar usando lchown (2). El único momento en que la propiedad de un enlace simbólico es importante es cuando el enlace se elimina o cambia de nombre en un directorio que tiene el bit fijo establecido (ver stat (2)).

Las marcas de tiempo de último acceso y última modificación de un enlace simbólico se pueden cambiar utilizando utimensat (2) o lutimes (3).

En Linux, los permisos de un enlace simbólico no se utilizan en ninguna operación; los permisos son siempre 0777 (lectura, escritura y ejecución para todas las categorías de usuarios), y no se pueden cambiar.

mark4o
fuente
@ mark4o: The Good - La referencia a lchown () y la propiedad en un directorio adhesivo es útil, gracias. Era consciente de lchown () y no era material para mi tesis. También estaba al tanto de los directorios de bits fijos; Creo que la propiedad es casi una consecuencia estándar de las reglas: la diferencia, y presumiblemente por qué se llama, es que normalmente puede eliminar un archivo si puede escribir en él, y nominalmente, cualquiera puede escribir en un enlace simbólico porque de los 777 permisos, pero en este caso, las reglas son ligeramente diferentes.
Jonathan Leffler
1
@ mark4o: The Not So Good: la secuencia de comandos que se muestra no hace lo que crees que hace (al menos, en el escenario:) set -x -e; mkdir junk; ( cd junk; mkdir olddir newdir; ln -s olddir mylink; ls -ilR; ln -s newdir temp; ls -ilR; mv temp mylink; ls -ilR; ); rm -fr junk. Si crea archivos oldfile y newfile en lugar de directorios y ejecuta el script equivalente (principalmente cambiando olddir a oldfile, newdir a newfile), obtendrá el efecto que esperaba. ¡Solo una complejidad extra! Gracias por la respuesta.
Jonathan Leffler
2

Solo una advertencia a las respuestas correctas anteriores:

El uso del método -f / --force proporciona un riesgo de perder el archivo si mezcla origen y destino:

mbucher@server2:~/test$ ls -la
total 11448
drwxr-xr-x  2 mbucher www-data    4096 May 25 15:27 .
drwxr-xr-x 18 mbucher www-data    4096 May 25 15:13 ..
-rw-r--r--  1 mbucher www-data 4109466 May 25 15:26 data.tar.gz
-rw-r--r--  1 mbucher www-data 7582480 May 25 15:27 otherdata.tar.gz
lrwxrwxrwx  1 mbucher www-data      11 May 25 15:26 thesymlink -> data.tar.gz
mbucher@server2:~/test$ 
mbucher@server2:~/test$ ln -s -f thesymlink otherdata.tar.gz 
mbucher@server2:~/test$ 
mbucher@server2:~/test$ ls -la
total 4028
drwxr-xr-x  2 mbucher www-data    4096 May 25 15:28 .
drwxr-xr-x 18 mbucher www-data    4096 May 25 15:13 ..
-rw-r--r--  1 mbucher www-data 4109466 May 25 15:26 data.tar.gz
lrwxrwxrwx  1 mbucher www-data      10 May 25 15:28 otherdata.tar.gz -> thesymlink
lrwxrwxrwx  1 mbucher www-data      11 May 25 15:26 thesymlink -> data.tar.gz

Por supuesto, esto es lo que se pretende, pero generalmente ocurren errores. Entonces, eliminar y reconstruir el enlace simbólico es un poco más trabajo pero también un poco más ahorrador:

mbucher@server2:~/test$ rm thesymlink && ln -s thesymlink otherdata.tar.gz 
ln: creating symbolic link `otherdata.tar.gz': File exists

que al menos guarda mi archivo.

Markus Bucher
fuente
Usar una opción -fu --forcesiempre es un poco peligroso; supone que el usuario sabe de qué se trata. Es doblemente letal si lo usa habitualmente ( rm -fr …cada vez es peligroso).
Jonathan Leffler
1

¿No lo desvincularía y crearía uno nuevo, al final, haría lo mismo?

mate b
fuente
2
¿Por qué desvincularse en primer lugar? ¿Por qué no simplemente sobrescribirlo?
S.Lott
55
El resultado neto es aproximadamente el mismo, pero el propietario y el grupo y los últimos tiempos de modificación (y probablemente el número de inodo) serían todos diferentes, en general.
Jonathan Leffler
2
@ S.Lott: ¿qué llamada al sistema 'sobrescribe'? En POSIX, no hay tal llamada. En Solaris y MacOS X, no hay tal llamada. ¿Hay una llamada para hacer eso en ... Linux, AIX, HP-UX, cualquier otra cosa que se parezca a Unix? Windows realmente no tiene enlaces simbólicos de la misma manera, AFAICT, por lo que no es crítico para mi análisis, pero la información sobre la API equivalente de Windows sería útil.
Jonathan Leffler
@ Jonathan Leffler: Ummm ... el ln --forcecomando sobrescribirá absolutamente un enlace existente.
S.Lott
Chicos, creo que están hablando con propósitos cruzados. S.Lott está discutiendo un ejecutable ln (1), mientras que Matt b. está discutiendo el hecho de que symlink (2)no no admite sobreescritura. Ambos tienen razón, y sugeriría que observar la implementación de ln (1)sería la forma más idónea de lograr el procedimiento de desvinculación de enlaces.
dmckee --- ex-gatito moderador
0

Por si acaso ayuda: hay una manera de editar un enlace simbólico con Midnight Commander (MC). El comando de menú es (en francés en mi interfaz mc):

Fichier / Éditer le lien symbolique

que puede traducirse a:

File / Edit symbolic link

El atajo es Cx Cs

Quizás internamente usa el ln --forcecomando, no lo sé.

Ahora, estoy tratando de encontrar una manera de editar una gran cantidad de enlaces simbólicos a la vez (así es como llegué aquí).

Pierre
fuente
1
¿Has verificado lo que MC hace detrás de escena? Apostaría a que, de hecho, hace un unlink()seguido de un symlink(), simplemente porque eso es lo que proporcionan los sistemas tipo Unix.
Jonathan Leffler
No, no lo he comprobado. Y sí, es muy probable que haga lo que usted dice, de hecho. El hecho de que edite un cuadro de texto da la sensación de que realmente estoy editando el objetivo del enlace simbólico, pero probablemente me han engañado ...
Pierre
0

Técnicamente, no hay un comando incorporado para editar un enlace simbólico existente. Se puede lograr fácilmente con unos pocos comandos cortos.

Aquí hay una pequeña función bash / zsh que escribí para actualizar un enlace simbólico existente:

# -----------------------------------------
# Edit an existing symbolic link
#
# @1 = Name of symbolic link to edit
# @2 = Full destination path to update existing symlink with 
# -----------------------------------------
function edit-symlink () {
    if [ -z "$1" ]; then
        echo "Name of symbolic link you would like to edit:"
        read LINK
    else
        LINK="$1"
    fi
    LINKTMP="$LINK-tmp"
    if [ -z "$2" ]; then
        echo "Full destination path to update existing symlink with:"
        read DEST
    else
        DEST="$2"
    fi
    ln -s $DEST $LINKTMP
    rm $LINK
    mv $LINKTMP $LINK
    printf "Updated $LINK to point to new destination -> $DEST"
}
blizzrdof77
fuente
2
Gracias por el esfuerzo. Esto no cumple con mi requisito de ser una llamada al sistema de lenguaje C. Existen numerosas formas de cambiar un enlace siempre que tenga permiso de escritura en el directorio que contiene el enlace (que esto requiere). No hay formas que yo sepa, incluso ahora, de cambiar el valor de un enlace simbólico sin hacerlo unlink()y symlink()con el nuevo valor, que es lo que sucede detrás de escena con su código. Personalmente, quisiera que la función insista en exactamente dos (o tal vez un múltiplo de dos) argumentos y rescate si la invocación no es correcta. Sin embargo, esa es una perspectiva particular.
Jonathan Leffler