¿La búsqueda de RUTA incluye enlaces simbólicos?

9

El estándar de shell POSIX dice en este sitio

http://pubs.opengroup.org/onlinepubs/9699919799/

sobre cómo usan los shells PATHpara buscar ejecutables:

"Se buscará en la lista de principio a fin, aplicando el nombre de archivo a cada prefijo, hasta que se encuentre un archivo ejecutable con el nombre especificado y los permisos de ejecución apropiados".

Bueno, no es así como parece funcionar en la implementación POSIX real:

man which dice:

"devuelve los nombres de ruta de los archivos (o enlaces) que se ejecutarían en el entorno actual, si sus argumentos se hubieran dado como comandos en un shell estrictamente compatible con POSIX. Lo hace buscando en la RUTA archivos ejecutables que coincidan con los nombres de argumentos. No sigue enlaces simbólicos ".

OK, veamos esta situación:

$ pwd /home/mark

$ echo $PATH /home/mark/bin:...

$ ls -l bin/foobar
lrwxrwxrwx 1 mark mark 18 Dec 12 22:51 bin/foobar -> /home/mark/foobar1
$ touch foobar1
$ which foobar
$ chmod a+x foobar1
$ which foobar
/home/mark/bin/foobar

OK, aquí hay un enlace simbólico PATHcon el nombre correcto, y se informa lsque es ejecutable.

which no lo mira en absoluto, pero solo le interesa lo que señala.

Eso a pesar del hecho de que ambos man whichdicen explícitamente que no sigue enlaces simbólicos (y de hecho vemos que no lo hace, porque which foobarno se imprime foobar1), y también que la documentación del shell POSIX citada anteriormente, nunca menciona los siguientes enlaces simbólicos en el PATHalgoritmo.

Entonces, ¿están whichmal los shells existentes o no entiendo la documentación?

PARA ACLARAR:

Sé y puedo explicar el comportamiento existente. Mi pregunta no es "¿cómo funciona esto?". Eso lo sé.

Mi pregunta es sobre la documentación: ¿dónde está mi error al seguir la documentación que cité? ¿O está mal la documentación?

MOTIVACIÓN: ¿Por qué me importa?

Bueno, soy un implementador. Los diferentes implementadores tienen diferentes requisitos. Para mí, el requisito es que la palabra del estándar POSIX actual DEBE seguirse EXACTAMENTE (o, más precisamente, lo mejor que puede ser, porque el estándar en sí es algo defectuoso). Como si fuera la palabra de Dios.

Ahora, la redacción estándar es bastante clara: no se mencionan los enlaces simbólicos, donde en muchos otros lugares, se menciona dónde debe hacerse. Entonces, en este caso, no lo hagas.

Sin embargo, siempre verifico cómo dashy cómo me bashcomporto, solo para asegurarme. Ahora, por supuesto, también hay un pequeño problema aquí, dashaunque se factura como POSIX, tiene muchos pequeños errores conformes con POSIX. bash, Todavía no he encontrado ningún error con POSIX, pero ... bash no es realmente POSIX, es mucho más que eso.

Entonces ahí lo tienes. Por eso me importa.

usuario322908
fuente
No entiendes: que no sigue enlaces simbólicos en los archivos . $PATHlata contiene enlaces simbólicos. Tratar which sh.
Ipor Sircer
De acuerdo, pero en mi caso $PATHno tiene enlaces simbólicos.
user322908
En casi todas las situaciones, los enlaces simbólicos se siguen de forma transparente. Los casos en los que no se mencionan generalmente se mencionan explícitamente (p. Ej., Llamadas al sistema como lstat(2)), por lo general, no se indica su seguimiento. Por ejemplo, la descripción de open(2)solo menciona enlaces simbólicos cuando se habla del comportamiento de O_CREAT | O_EXCL. No es necesario indicar que se abrirá el archivo de destino.
Barmar

Respuestas:

10

Los permisos del enlace simbólico en sí son irrelevantes. Ni siquiera podría cambiarlos si lo intentara.

Lo que importa son los permisos del archivo subyacente.

Está bien que los directorios en su RUTA incluyan enlaces simbólicos a ejecutables. De hecho, es probable que muchos ejecutables en su RUTA sean enlaces simbólicos. Por ejemplo, en sistemas debian / ubuntu-like:

$ ls -l /bin/sh
lrwxrwxrwx 1 root root 4 Jan 23  2017 /bin/sh -> dash

Documentación

De man chmod:

chmod nunca cambia los permisos de los enlaces simbólicos; la llamada al sistema chmod no puede cambiar sus permisos. Esto no es un problema ya que los permisos de los enlaces simbólicos nunca se usan. Sin embargo, para cada enlace simbólico listado en la línea de comando, chmod cambia los permisos del archivo señalado. Por el contrario, chmod ignora los enlaces simbólicos encontrados durante los recorridos recursivos del directorio. [Énfasis añadido.]

Ejemplo

El shell tiene una prueba, -xpara determinar si un archivo es ejecutable. Probemos eso:

$ ls -l
total 0
lrwxrwxrwx 1 john1024 john1024 7 Dec 12 23:36 foo -> foobar1
-rw-rw---- 1 john1024 john1024 0 Dec 12 23:36 foobar1
$ [ -x foo ] && echo foo is executable
$ chmod +x foobar1
$ [ -x foo ] && echo foo is executable
foo is executable

Entonces, tal como lo encontró con which, el shell no considera un ejecutable softlink a menos que el archivo subyacente sea ejecutable.

Como funciona

En un sistema Debian, whiches un script de shell. La sección relevante del código es:

 case $PROGRAM in
  */*)
   if [ -f "$PROGRAM" ] && [ -x "$PROGRAM" ]; then
    puts "$PROGRAM"
    RET=0
   fi
   ;;
  *)
   for ELEMENT in $PATH; do
    if [ -z "$ELEMENT" ]; then
     ELEMENT=.
    fi
    if [ -f "$ELEMENT/$PROGRAM" ] && [ -x "$ELEMENT/$PROGRAM" ]; then
     puts "$ELEMENT/$PROGRAM"
     RET=0
     [ "$ALLMATCHES" -eq 1 ] || break
    fi
   done
   ;;
 esac

Como puede ver, utiliza la -xprueba para determinar si un archivo es ejecutable.

POSIX especifica la -xprueba de la siguiente manera:

-x nombre de ruta
Verdadero si el nombre de ruta se resuelve en una entrada de directorio existente para un archivo para el cual se otorgará permiso para ejecutar el archivo (o buscarlo, si es un directorio), tal como se define en Lectura, escritura y creación de archivos. Falso si pathname no se puede resolver o si pathname se resuelve en una entrada de directorio existente para un archivo para el cual no se otorgará permiso para ejecutar (o buscar) el archivo. [Énfasis añadido.]

Entonces, POSIX verifica a qué se resuelve el nombre de ruta . En otras palabras, acepta enlaces simbólicos.

Función de ejecución POSIX

La función de ejecución POSIX sigue enlaces simbólicos. La especificación POSIX continúa para especificar las condiciones de error que puede informar si los enlaces simbólicos son circulares o demasiado profundos, como:

[ELOOP] Existe
un bucle en los enlaces simbólicos encontrados durante la resolución de la ruta o el argumento del archivo.

[ELOOP] Se encontraron
más de {SYMLOOP_MAX} enlaces simbólicos durante la resolución de la ruta o el argumento del archivo.
[ENAMETOOLONG]
Como resultado de encontrar un enlace simbólico en la resolución del argumento de ruta, la longitud de la cadena de nombre de ruta sustituida excedió {PATH_MAX}.

John1024
fuente
Sé todo lo que escribiste en tu respuesta. Sé cómo "funcionan" las cosas. Esa no es mi pregunta. Mi pregunta es sobre documentación. Indíqueme dónde no entiendo la documentación. O dime que la documentación es incorrecta.
user322908
@ user322908 En la mayoría de los sistemas, whiches un script de shell. Por lo tanto, es probable que solo esté usando la -xprueba que mostré. Según POSIX, -xprueba si un archivo "se resuelve" en un ejecutable. Si estás viendo algo diferente, ¿dónde estás mirando?
John1024
Gracias y lo siento por ser tan doloroso ... Me doy cuenta de que mi pregunta es diferente al 99% de las preguntas, por lo que es difícil entender lo que busco. Nuevamente, no estoy interesado en "cómo" whichfunciona, o si es -xo algo más. Me interesa saber dónde no sigo correctamente la documentación que cité.
user322908
44
@ user322908 La función POSIXexec sigue enlaces simbólicos. Eso parece dejar bastante claro que los archivos con enlaces simbólicos pueden ser ejecutables en POSIX.
John1024
2
También revisé el Ubuntu 17.10 man which que dice "No canonicaliza los nombres de ruta". Eso no significa que no siga los enlaces. Eso solo significa, como observó, que no "canoniza" los nombres.
John1024
3

En este caso, los enlaces simbólicos se siguen de forma transparente, sin canonizar la ruta final. En otras palabras, whichno le importa si /home/mark/bines un enlace simbólico o no. Lo que le importa es si el archivo /home/mark/bin/foobarexiste o no. No necesita aplanar manualmente los enlaces simbólicos a lo largo de la ruta; el sistema operativo puede hacerlo bien solo.

Y, de hecho, cuando se whichle pregunta acerca de la información del archivo /home/mark/bin/foobar, el sistema operativo nota que /home/mark/bines un enlace simbólico, lo sigue y lo encuentra con éxito foobaren el directorio de destino.

Este es el comportamiento predeterminado a menos que el programa use open(…, O_NOFOLLOW)o fstatat(…, AT_SYMLINK_NOFOLLOW)para acceder al archivo.

[comentarios fusionados en]

Si bien se dice que las utilidades de shell lo hacen sobre una base caso por caso, no es lo mismo con llamadas al sistema del kernel: todas las llamadas de archivos relacionados hacen seguir enlaces simbólicos por defecto, a menos que se da la opción "nofollow". (Incluso lstat sigue enlaces simbólicos en todos los componentes de la ruta excepto el último).

Cuando la especificación no menciona explícitamente qué hacer con los enlaces simbólicos, implica que se utilizará el comportamiento predeterminado. Es decir, un shell que sigue el algoritmo de ruta no resuelve los enlaces simbólicos manualmente ni excluye explícitamente que el sistema operativo haga lo mismo. (Simplemente concatena cada componente $ PATH con el nombre ejecutable).

Cuando la página del manual which (1) dice que no sigue enlaces simbólicos, puede significar varias cosas, pero la versión de GNU coreutils lo dice de esta manera:

Lo que considerará que dos directorios equivalentes son diferentes cuando uno de ellos contiene una ruta con un enlace simbólico.

El alcance es mucho más limitado: solo significa whichque no intentará canonizar manualmente todas las rutas para eliminar los duplicados, pero no implica que la herramienta optará por el enlace simbólico que sigue el sistema operativo en general. Por ejemplo, si /bines un enlace simbólico a /usr/bin, la ejecución which -a shdevolverá ambos /bin/sh y /usr/bin/sh.

usuario1686
fuente
Sí, gracias. Sé todo esto. Mi pregunta no es cómo "funcionan" las cosas. Sé como funcionan. Ese no es mi punto. Mi punto es sobre la documentación. ¿Dónde estoy siguiendo la documentación incorrectamente? O es la documentación incorrecta.
user322908
2
Está entendiendo la documentación de manera incorrecta: si no menciona los siguientes enlaces simbólicos, eso significa que no resuelve manualmente los enlaces simbólicos, pero el comportamiento normal del sistema operativo aún se aplica. La whichpágina del manual de GNU lo expresa de manera diferente: "Lo que considerará que dos directorios equivalentes son diferentes cuando uno de ellos contiene una ruta con un enlace simbólico".
user1686
OK, eso es mejor gracias! Estoy tratando de entender ... Pero ... perdón por ser un dolor en el cuello: "el comportamiento normal del sistema operativo" NO es siempre seguir implícitamente enlaces simbólicos. Hay muchas utilidades que no lo hacen. Es caso por caso.
user322908
1
Todas las llamadas al núcleo (chdir, open, chmod, execve ...) seguirán enlaces simbólicos tanto en la ruta como en la cola, a menos que especifique AT_SYMLINK_NOFOLLOW o similar. (lstat es el único que no desreferencia los enlaces simbólicos en la cola, pero aún lo hace para la ruta restante). Por lo tanto, el comportamiento predeterminado es seguir los enlaces simbólicos. Por ejemplo, cuando un shell llama execve("/home/mark/bin/foobar", …), dará como resultado que se sigan todos los enlaces simbólicos.
user1686
OK, creo que estoy comprando el execveargumento, en realidad, en mi implementación, es lo execl()mismo. Por favor, si incluye esto en su respuesta, lo aceptaré.
user322908
1

El shell se ajusta a su documentación en el sentido de que sigue las reglas para la resolución del nombre de ruta. whichse ajusta a su documentación. Los dos hacen cosas ligeramente diferentes.

La salida de whiches el nombre y la ruta del archivo del enlace, no la ruta a la que apunta el enlace simbólico. Esto se explica en la página del manual.

Cuando se ejecuta un comando, el enlace se "sigue" según la Sección 4.13 Resolución de nombre de ruta en el mismo . La cláusula relevante para ejecutar un archivo es:

En todos los demás casos, el sistema deberá anteponer el nombre de ruta restante, si lo hay, con el contenido del enlace simbólico, excepto que si el contenido del enlace simbólico es la cadena vacía, entonces cualquiera de las resoluciones de nombre de ruta fallará con las funciones que informan un [ENOENT ] error y utilidades al escribir un mensaje de diagnóstico equivalente, o el nombre de ruta del directorio que contiene el enlace simbólico se utilizará en lugar del contenido del enlace simbólico. Si el contenido del enlace simbólico consiste únicamente en caracteres, todos los caracteres iniciales del nombre de ruta restante se omitirán del nombre de ruta combinado resultante, dejando solo los caracteres iniciales del contenido del enlace simbólico. En los casos donde se produce el prefijo, si la longitud combinada excede {PATH_MAX}, y la implementación considera que esto es un error, la resolución del nombre de ruta fallará con las funciones que informan un error [ENAMETOOLONG] y las utilidades que escriben un mensaje de diagnóstico equivalente. De lo contrario, el nombre de ruta resuelto será la resolución del nombre de ruta recién creado. Si el nombre de ruta resultante no comienza con a, se considera que el predecesor del primer nombre de archivo del nombre de ruta es el directorio que contiene el enlace simbólico.

Dave
fuente