Diferença entre rm my-symlink e rm my-symlink /

3

Só queria saber por que, se eu usar rm -rf my-symlink , exclui apenas o link simbólico, mas rm -rf my-symlink/ exclui os arquivos do diretório vinculado e deixa o link simbólico sozinho?

    
por blindcant 01.06.2018 / 13:42

2 respostas

4

Você pode ver a diferença comparando a saída de stat my-symlink e stat my-symlink/ . my-symlink , sem a barra, é o próprio link simbólico; my-symlink/ , com a barra, é o diretório apontado pelo link simbólico, que você pode verificar separadamente comparando os inodes de my-symlink/ e do diretório para o qual aponta.

Com essa informação em mãos, o comportamento observado corresponde ao descrito em rm 's especificação : ao processar um link simbólico, rm apaga o link sem descer para dentro dele se ele apontar para um diretório; Ao processar um diretório (com a opção -r ), ele exclui seu conteúdo recursivamente. No caso my-symlink/ , rm tenta excluir o "diretório", mas falha, pois não é um diretório, mas um link simbólico - no entanto, devido ao -f sinalizador, isso não causa um erro.

    
por 01.06.2018 / 14:25
1

Eu pensei que investigaria o comportamento um pouco mais, então aqui está outra resposta.

Internamente, rm usa FTS para recursão em hierarquias de arquivos. fts_open usa uma matriz de caminhos como um parâmetro e cria uma estrutura de árvore para cada caminho. Isso permite que o programador explore vários locais, como se eles fossem parte de uma hierarquia unificada.

Aqui está um programa de teste que você pode usar para jogar com o FTS.

#include <stdio.h>
#include <stdlib.h>
#include <fts.h>

int main(int argc, char* argv[])
{
    if(argc < 2) return EXIT_FAILURE;

    char* const* arr = argv + 1;
    FTS* hier = fts_open(arr, FTS_NOSTAT | FTS_PHYSICAL, NULL);

    FTSENT* ent;
    while((ent = fts_read(hier))) {
        printf("%s info=%d (D=%d DP=%d F=%d SL=%d)\n",
               ent->fts_accpath, ent->fts_info,
               ent->fts_info == FTS_D, ent->fts_info == FTS_DP,
               ent->fts_info == FTS_F || ent->fts_info == FTS_NSOK,
               ent->fts_info == FTS_SL);
    }

    fts_close(hier);
    return EXIT_SUCCESS;
}

Vamos supor que criamos a seguinte estrutura de diretório:

$ mkdir dir
$ touch dir/file
$ ln -s dir sym

Agora, vamos considerar seu primeiro caso e ver como o FTS lidera a exploração.

$ gcc fts.c 
$ ./a.out sym
sym info=12 (D=0 DP=0 F=0 SL=1)

Como você pode ver, neste caso, sym é visto como um arquivo. Um link simbólico, para ser mais exato. Com essas informações, rm o trataria como um arquivo e chamaria unlinkat(AT_FDCWD, "sym", 0) . O último parâmetro (0) faz com que unlinkat se comporte como unlink . Em outras palavras: simplesmente exclui um arquivo. Como resultado, seu link desaparece.

Agora, vamos dar uma olhada no que acontece com sym/ .

$ ./a.out sym/
sym/ info=1 (D=1 DP=0 F=0 SL=0)
file info=11 (D=0 DP=0 F=1 SL=0)
sym/ info=6 (D=0 DP=1 F=0 SL=0)

Nesse caso, sym foi tratado como seu diretório de destino. Primeiro, iteramos para sym e, em seguida, sym/file , em seguida, sym novamente. Este último é devido a como o FTS funciona: primeiro, ele itera sobre o conteúdo e retorna ao nó raiz. Isso é realmente muito conveniente para rm . Na primeira passagem ( D ), ele pode apagar arquivos e, no segundo ( DP ), remover os diretórios vazios.

Como você pode ver, neste caso, o FTS reporta sym/ como sendo um diretório em ambos os casos. Isso é porque nós demos ao caminho uma barra final, o que força o kernel a interpretá-lo como um diretório. No caso de um link, isso significa que ele vai segui-lo, não importa o quê. Em termos mais técnicos, as especificações dizem:

A pathname that contains at least one non-slash character and that ends with one or more trailing slashes shall be resolved as if a single dot character ( '.' ) were appended to the pathname.

Como o FTS reporta sym/ como um diretório, rm se comporta como se estivesse excluindo um diretório vazio. Consequentemente, chama unlinkat(AT_FDCWD, "sym/", AT_REMOVEDIR) . Isso faz com que unlinkat se comporte como rmdir .

No entanto, ao resolver o caminho sym/ , a chamada do sistema unlinkat perceberá que, de fato, não está recebendo um diretório. Por conseguinte, irá reportar ENOTDIR , o que desencadeia:

$ rm: cannot remove ‘sym/’: Not a directory

E, na verdade, se você remover o -f de suas chamadas ... Isso é exatamente o que você verá. Agora, se isso é ou não um bug ou um recurso ... não faço ideia.

    
por 01.06.2018 / 15:54

Tags