Como posso descobrir se um symlink relativo é interno a uma certa subárvore ou não?

7

Eu quero testar se um symlink relativo aponta para a subárvore de um determinado diretório.

Este exemplo produziria false , uma vez que aponta para fora do diretório foo :
/foo>readlink bar
../fie.txt

Enquanto este exemplo produziria true :% /foo>readlink bar
fum/fie.txt

Existe algum utilitário existente que eu possa aproveitar ou será necessário codificá-lo do zero? Estou usando bash .

    
por Fylke 08.01.2014 / 12:36

3 respostas

5

Eu não acho que haja tal utilidade. Com o GNU readlink , você poderia fazer algo como:

is_in() (
  needle=$(readlink -ve -- "$1" && echo .) || exit
  haystack=$(readlink -ve -- "$2" && echo .) || exit
  needle=${needle%??} haystack=${haystack%??}
  haystack=${haystack%/} needle=${needle%/}
  case $needle in
    ("$haystack" | "$haystack"/*) true;;
    (*) false;;
  esac
)

Isso resolve os todos links simbólicos para terminar com um caminho absoluto canônico para agulha e palheiro.

Explicação

  • Obtemos o caminho absoluto canônico da agulha e do palheiro . Usamos -e em vez de -f , pois queremos garantir que os arquivos existam. A opção -v fornece uma mensagem de erro se os arquivos não puderem ser acessados.
  • Como sempre, -- deve ser usado para marcar o final das opções e aspas, já que não queremos chamar o operador split + glob aqui.
  • A substituição de comandos em shells parecidos com Bourne tem um erro na medida em que remove all o caractere de nova linha do final da saída de um comando, não apenas aquele adicionado pelos comandos para terminar a última linha. O que isso significa é que, para um arquivo como /foo<LF><LF> , $(readlink -ve -- "$1") retornaria /foo . A solução comum para isso é acrescentar um caractere não-LF (aqui . ) e remover o caractere LF extra adicionado por readlink com var=${var%??} (remover os dois últimos caracteres).
  • A agulha é considerada como estando no palheiro se é o palheiro ou se é palheiro / alguma coisa. No entanto, isso não funcionaria se o palheiro fosse / ( /etc , em vez disso, não é //something ). / geralmente precisa ser tratado especialmente porque, embora / e /xx tenham o mesmo número de barras, um é um nível acima do outro.

    Uma maneira de resolver isso é substituir / pela string vazia que é feita com var=${var%/} (o único caminho que termina com / que readlink -e é / , removendo, assim, um / à direita está mudando / para a string vazia).

Para a canonização dos caminhos de arquivos, você pode usar uma função auxiliar.

canonicalize_path() {
  # canonicalize paths stored in supplied variables. '/' is returned as 
  # the empty string.
  for _var do
    eval '
      '"$_var"'=$(readlink -ve -- "${'"$_var"'}" && echo .) &&
      '"$_var"'=${'"$_var"'%??} &&
      '"$_var"'=${'"$_var"'%/}' || return
  done
}

is_in() (
  needle=$1 haystack=$2
  canonicalize_path needle haystack || exit
  case $needle in
    ("$haystack" | "$haystack"/*) true;;
    (*) false;;
  esac
)
    
por 08.01.2014 / 16:01
0

Eu resolvi o problema assim:

echo $abs_link_target | grep -qe "^$containing_dir"

A variável $abs_link_target contém o caminho absoluto para o destino do symlink (expandido através de readlink -f ). Em seguida, verifico se o início do caminho de destino corresponde ao início do $containing_dir

    
por 08.01.2014 / 17:32
0

grep -q "^/foo/bar/" <<< "$(readlink -f "anyfile.ext")"

    
por 10.01.2014 / 10:20