a pesquisa PATH inclui links simbólicos?

9

O padrão de shell POSIX diz neste site

link

sobre como os shells usam PATH para procurar executáveis:

"A lista deve ser pesquisada do início ao fim, aplicando o nome do arquivo a cada prefixo, até que um arquivo executável com o nome especificado e permissões de execução apropriadas seja encontrado."

Bem, não é assim que isso parece funcionar na implementação real do POSIX:

man which diz:

"retorna os nomes de caminho dos arquivos (ou links) que seriam executados no ambiente atual, tiveram seus argumentos dados como comandos em um shell estritamente compatível com POSIX. Ele faz isso procurando no PATH por arquivos executáveis que correspondem ao nomes dos argumentos. Não segue ligações simbólicas. "

OK, vamos analisar esta situação:

$ 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, aqui está um link simbólico em PATH com o nome correto, e é relatado por ls como executável.

which não olha para isso, mas só está interessado no que aponta.

Isso apesar do fato de que man which explicitamente diz que ele não segue links simbólicos (e de fato vemos que não, porque which foobar não imprime foobar1 ), e também que a documentação do shell POSIX citado acima, nunca menciona seguir links simbólicos no algoritmo PATH .

Então, which e os shells existentes estão errados, ou eu não estou entendendo a documentação?

PARA CLARIFICAR:

Eu sei e posso explicar o comportamento existente. Minha pergunta não é "como isso funciona?" Que eu saiba.

A minha pergunta é sobre documentação: onde está o meu erro em seguir a documentação que citei. Ou a documentação está errada?

MOTIVAÇÃO: Por que eu me importo?

Bem, eu sou um implementador. Implementadores diferentes possuem requisitos diferentes. Para mim, a exigência é que a palavra do atual padrão POSIX DEVE ser seguida EXATAMENTE (ou, mais precisamente, o melhor que pode ser, porque o padrão em si é um pouco bugs). Como se fosse a palavra de Deus.

Agora, o texto padrão é bastante claro - os links simbólicos não são mencionados, onde em muitos outros lugares, é mencionado onde ele precisa ser feito. Então, neste caso, não.

No entanto, sempre dou uma olhada em como dash e bash se comportam, apenas para ter certeza. Agora, claro, há um pequeno problema aqui também, dash , embora seja cobrado como POSIX, tem muitos pequenos bugs com conformidade ao POSIX. bash , eu ainda tenho que encontrar algum erro com POSIX, mas ... bash não é na verdade POSIX, é muito mais que isso.

Então você tem isso. É por isso que me importo.

    
por user322908 13.12.2017 / 08:26

3 respostas

10

As permissões do link simbólico em si são irrelevantes. Você não poderia nem mudá-las se você tentasse.

O que importa são permissões do arquivo subjacente.

É bom ter diretórios em seu PATH incluindo links simbólicos para executáveis.  De fato, é provável que muitos executáveis em seu PATH sejam links simbólicos. Por exemplo, em sistemas debian / ubuntu-like:

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

Documentação

De man chmod :

chmod never changes the permissions of symbolic links; the chmod system call cannot change their permissions. This is not a problem since the permissions of symbolic links are never used. However, for each symbolic link listed on the command line, chmod changes the permissions of the pointed-to file. In contrast, chmod ignores symbolic links encountered during recursive directory traversals. [Emphasis added.]

Exemplo

O shell tem um teste, -x , para determinar se um arquivo é executável. Vamos tentar isso:

$ 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

Portanto, assim como você encontrou com which , o shell não considera um executável de softlink a menos que o arquivo subjacente seja executável.

Como funciona?

Em um sistema Debian, which é um script de shell. A seção relevante do código é:

 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 você pode ver, ele usa o teste -x para determinar se um arquivo é executável.

POSIX especifica o teste -x da seguinte forma:

-x pathname
True if pathname resolves to an existing directory entry for a file for which permission to execute the file (or search it, if it is a directory) will be granted, as defined in File Read, Write, and Creation. False if pathname cannot be resolved, or if pathname resolves to an existing directory entry for a file for which permission to execute (or search) the file will not be granted. [Emphasis added.]

Portanto, POSIX verifica o nome do caminho resolve para. Em outras palavras, aceita links simbólicos.

função exec POSIX

A função execut POSIX segue os links simbólicos. A especificação POSIX é longa para especificar condições de erro que podem ser relatadas se os links simbólicos forem circulares ou muito profundos, como:

[ELOOP]
A loop exists in symbolic links encountered during resolution of the path or file argument.

[ELOOP]
More than {SYMLOOP_MAX} symbolic links were encountered during resolution of the path or file argument.
[ENAMETOOLONG]
As a result of encountering a symbolic link in resolution of the path argument, the length of the substituted pathname string exceeded {PATH_MAX}.

    
por 13.12.2017 / 08:33
3

Neste caso, os links simbólicos são seguidos de forma transparente, sem canonizar o caminho final. Em outras palavras, which não se importa se /home/mark/bin é um link simbólico ou não. O que importa é se o arquivo /home/mark/bin/foobar existe ou não. Ele não precisa para achatar manualmente links simbólicos ao longo do caminho - o sistema operacional pode fazer isso muito bem sozinho.

E, de fato, quando which perguntar sobre as informações de arquivo de /home/mark/bin/foobar , o sistema operacional perceberá que /home/mark/bin é um link simbólico, seguirá e localizará foobar no diretório de destino com êxito.

Esse é o comportamento padrão, a menos que o programa use open(…, O_NOFOLLOW) ou fstatat(…, AT_SYMLINK_NOFOLLOW) para acessar o arquivo.

[comentários mesclados em]

Enquanto você diz que os utilitários de shell fazem isso caso a caso, não é o mesmo com o syscalls do kernel: todas as chamadas relacionadas a arquivos do seguem links simbólicos por padrão, a menos que o " nofollow "bandeira é dada. (Mesmo lstat segue links simbólicos em todos os componentes do caminho, exceto o último).

Quando a especificação não menciona explicitamente o que fazer com links simbólicos, isso implica que o comportamento padrão será usado. Ou seja, uma shell seguindo o caminho do algoritmo neithers resolver links simbólicos manualmente nem faz explicitamente optar pelo sistema operacional fazendo o mesmo. (Apenas concatena cada componente $ PATH com o nome do executável).

Quando a (1) página de manual diz que ela não segue links simbólicos, isso pode significar várias coisas, mas a versão GNU do coreutils afirma desta forma:

Which will consider two equivalent directories to be different when one of them contains a path with a symbolic link.

Isso é muito mais restrito no escopo - significa apenas que which não tentará manualmente canonizar todos os caminhos para eliminar duplicatas, mas isso não significa que a ferramenta desativará o uso de links simbólicos pelo SO em geral. Por exemplo, se /bin for um symlink para /usr/bin , executar which -a sh retornará ambos /bin/sh e /usr/bin/sh .

    
por 13.12.2017 / 08:40
1

O shell está em conformidade com sua documentação, pois segue as regras para a resolução do nome do caminho. which está em conformidade com sua documentação. Os dois fazem coisas ligeiramente diferentes.

A saída de which é o nome do arquivo e o caminho do link, não o caminho para o qual o link simbólico aponta. Isso está escrito na man page.

Quando um comando é executado, o link é "seguido" de acordo com a Seção 4.13 Resolução do caminho no mesmo . A cláusula relevante para executar um arquivo é:

In all other cases, the system shall prefix the remaining pathname, if any, with the contents of the symbolic link, except that if the contents of the symbolic link is the empty string, then either pathname resolution shall fail with functions reporting an [ENOENT] error and utilities writing an equivalent diagnostic message, or the pathname of the directory containing the symbolic link shall be used in place of the contents of the symbolic link. If the contents of the symbolic link consist solely of characters, then all leading characters of the remaining pathname shall be omitted from the resulting combined pathname, leaving only the leading characters from the symbolic link contents. In the cases where prefixing occurs, if the combined length exceeds {PATH_MAX}, and the implementation considers this to be an error, pathname resolution shall fail with functions reporting an [ENAMETOOLONG] error and utilities writing an equivalent diagnostic message. Otherwise, the resolved pathname shall be the resolution of the pathname just created. If the resulting pathname does not begin with a , the predecessor of the first filename of the pathname is taken to be the directory containing the symbolic link.

    
por 13.12.2017 / 20:06