Por que "rm -r" não pode excluir esta pasta?

12

Eu tenho uma pasta com -wxpermissões chamadas folder1e outra pasta dentro dela chamada folder2com rwxpermissões.

Eu tentei excluir folder1usando este comando:

rm -r folder1

Mas eu recebi o seguinte erro:

rm: cannot remove 'folder1': Permission denied

A razão pela qual acho que recebi esse erro é porque o rmprograma precisa primeiro obter o conteúdo de folder1(obter os nomes dos arquivos e pastas dentro dele folder1) para poder excluí-lo (porque você não pode excluir um arquivo ou pasta sem saber o nome que eu acho) e o rmprograma pode se excluir folder1.

Mas como folder1não tem readpermissão, o rmprograma não pode obter seu conteúdo e, portanto, não pode excluí-lo, e como não pode excluir seu conteúdo, não pode excluí-lo.

Estou correcto?

John
fonte
1
Faça "ls -l" e diga-nos quais são as permissões do DIRETÓRIO.
jamesqf

Respostas:

19

Acho que sua análise está correta: você não pode excluir o diretório, pois não está vazio, e não pode esvaziá-lo, pois não pode ver seu conteúdo.

Eu apenas tentei:

$ mkdir -p folder1/folder2
$ chmod -r folder1
$ rm -rf folder1
rm: cannot remove 'folder1': Permission denied
$ rmdir folder1/folder2
$ rm -rf folder1
$ 

Quando escrevi "você", quis dizer qualquer programa que você possa executar. Seu rm -rcomando primeiro vê que folder1é um diretório e, portanto, tenta descobrir seu conteúdo para esvaziá-lo, mas falha por perder a permissão de leitura, depois tenta excluí-lo, mas falha porque não está vazio. A "permissão negada" é enganosa; Eu acho que "Diretório não vazio" (como rmdirrelatórios) seria mais apropriado.)

user2233709
fonte
4
Não pode ser relatado Directory not emptyneste caso, pois não saberia que estava vazio ou não. Você ainda receberia o mesmo erro ao tentar excluir um diretório vazio no qual você não tem permissão de leitura. (Além disso, desconsidere meu comentário anterior, eu não estava usando o meu pensamento).
Kusalananda
1
@Kusalananda Isso parece sensato, mas rmdiré capaz de relatar “Diretório não vazio”. E se você ler meu teste, verá que ele aceita remover o folder1diretório, sem permissão de leitura , depois de esvaziá-lo.
user2233709
2
Seu teste mostra uma diferença interessante entre nossos sistemas. Recebo um Permission deniedquando estou tentando rm -r folder1quando está vazio. Estou no OpenBSD, não no Linux.
Kusalananda
@ Kusalananda Isso é interessante. Eu pensaria que esse comportamento foi especificado pela Especificação Unix Única, para que o Linux e o {Free, Net, Open} BSD se comportassem de forma idêntica. (Para o registro, estou usando o Debian Stretch 9.8 com um kernel linux 4.9.144-3 x86_64.) #
User2233709
Hmm ... A única coisa que o POSIX diz é que, se o operando for um diretório e -rfor usado, cada entrada de diretório (exceto .e ..) deve ser removida como se fosse um operando de arquivo rm -r. Parece que o GNU rmsimplesmente faz um rmdir()no diretório se não for legível, porque não terá como obter o conteúdo dele.
Kusalananda
7

Para que a exclusão ocorra, o sistema deve ser capaz de ler o conteúdo e identificar o que deve ser excluído.

Eu tentei simular o que você está tentando:

[vagrant@desktop1 ~]$ sudo rm -rf folder1/ && mkdir -pv folder1/folder2 && sudo chmod 333 -v folder1/ && sudo chmod 777 -v folder1/folder2
mkdir: created directory 'folder1'
mkdir: created directory 'folder1/folder2'
mode of 'folder1/' changed from 0775 (rwxrwxr-x) to 0333 (-wx-wx-wx)
mode of 'folder1/folder2' changed from 0775 (rwxrwxr-x) to 0777 (rwxrwxrwx)
[vagrant@desktop1 ~]$ ls -lh
total 0
d-wx-wx-wx. 3 vagrant vagrant 21 Feb 24 10:40 folder1
[vagrant@desktop1 ~]$ 

Se tentarmos excluir sem permissões de leitura, ele falhará:

[vagrant@desktop1 ~]$ rm -r folder1/
rm: cannot remove 'folder1/': Permission denied
[vagrant@desktop1 ~]$ sudo chmod +r folder1/
[vagrant@desktop1 ~]$ rm -r folder1/
[vagrant@desktop1 ~]$ 

Em um rastreio para as duas tentativas, a diferença é que o conteúdo do diretório não pode ser lido (getdents):

newfstatat(AT_FDCWD, "folder1/", {st_mode=S_IFDIR|0333, st_size=21, ...}, AT_SYMLINK_NOFOLLOW) = 0
openat(AT_FDCWD, "folder1/", O_RDONLY|O_NOCTTY|O_NONBLOCK|O_DIRECTORY|O_NOFOLLOW) = -1 EACCES (Permission denied)
geteuid()                               = 1000
newfstatat(AT_FDCWD, "folder1/", {st_mode=S_IFDIR|0333, st_size=21, ...}, AT_SYMLINK_NOFOLLOW) = 0
faccessat(AT_FDCWD, "folder1/", W_OK)   = 0
openat(AT_FDCWD, "folder1/", O_RDONLY|O_NOCTTY|O_NONBLOCK|O_DIRECTORY|O_NOFOLLOW) = -1 EACCES (Permission denied)
newfstatat(AT_FDCWD, "folder1/", {st_mode=S_IFDIR|0333, st_size=21, ...}, AT_SYMLINK_NOFOLLOW) = 0

Com permissões de leitura:

newfstatat(AT_FDCWD, "folder1/", {st_mode=S_IFDIR|0777, st_size=21, ...}, AT_SYMLINK_NOFOLLOW) = 0
openat(AT_FDCWD, "folder1/", O_RDONLY|O_NOCTTY|O_NONBLOCK|O_DIRECTORY|O_NOFOLLOW) = 3
fstat(3, {st_mode=S_IFDIR|0777, st_size=21, ...}) = 0
fcntl(3, F_GETFL)                       = 0x38800 (flags O_RDONLY|O_NONBLOCK|O_LARGEFILE|O_DIRECTORY|O_NOFOLLOW)
fcntl(3, F_SETFD, FD_CLOEXEC)           = 0
getdents(3, /* 3 entries */, 32768)     = 80
close(3)                                = 0
geteuid()                               = 1000

Para concluir, mesmo que você possua um diretório e ele possua o bit executável, você ainda precisará de permissões de leitura para poder ver seu conteúdo e excluir a pasta. Não é o mesmo para um arquivo.

ttaran7
fonte
0

Bem, eu não tenho reputação suficiente para comentar a resposta de ttaran7, então parece que deve ser uma resposta. Meu voto positivo também não é visível ao público, devido à baixa reputação. Eu votei nessa resposta por realmente incluir um rastreamento de chamada do sistema, em vez de apenas especulação.

Para responder à pergunta do OP: Sim, seu raciocínio estava correto: você é bloqueado pela falha ao ler o diretório

Corri um rastreio semelhante ao que eles (ttaran7) haviam feito porque suspeitava do mesmo raciocínio: a rmchamada falharia ao não ler o diretório e isso seria o fim disso, sem chance de reclamar sobre o diretório estar vazio. Ao examinar novamente o rastreamento que fiz, notei que foi feita uma chamada do sistema para tentar desvincular o nome do arquivo fornecido de qualquer maneira:

newfstatat(AT_FDCWD, "folder1", {st_mode=S_IFDIR|0311, st_size=4096, ...}, AT_SYMLINK_NOFOLLOW) = 0
openat(AT_FDCWD, "folder1", O_RDONLY|O_NOCTTY|O_NONBLOCK|O_NOFOLLOW|O_DIRECTORY) = -1 EACCES (Permission denied)
openat(AT_FDCWD, "folder1", O_RDONLY|O_NOCTTY|O_NONBLOCK|O_NOFOLLOW|O_CLOEXEC|O_DIRECTORY) = -1 EACCES (Permission denied)
unlinkat(AT_FDCWD, "folder1", AT_REMOVEDIR) = -1 ENOTEMPTY (Directory not empty)
openat(AT_FDCWD, "/usr/share/locale/locale.alias", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=2995, ...}) = 0
read(3, "# Locale name alias data base.\n#"..., 4096) = 2995
read(3, "", 4096)                       = 0
close(3)                                = 0
openat(AT_FDCWD, "/usr/share/locale/en_AU/LC_MESSAGES/coreutils.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/share/locale/en/LC_MESSAGES/coreutils.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/share/locale-langpack/en_AU/LC_MESSAGES/coreutils.mo", O_RDONLY) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=45256, ...}) = 0
mmap(NULL, 45256, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f8db25ca000
close(3)                                = 0
openat(AT_FDCWD, "/usr/share/locale- langpack/en/LC_MESSAGES/coreutils.mo", O_RDONLY) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=578, ...}) = 0
mmap(NULL, 578, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f8db25c9000
close(3)                                = 0
write(2, "rm: ", 4rm: )                     = 4
write(2, "cannot remove 'folder1'", 23cannot remove 'folder1') = 23
openat(AT_FDCWD, "/usr/share/locale/en_AU/LC_MESSAGES/libc.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/share/locale/en/LC_MESSAGES/libc.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/share/locale-langpack/en_AU/LC_MESSAGES/libc.mo", O_RDONLY) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=2893, ...}) = 0
mmap(NULL, 2893, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f8db25c8000
close(3)                                = 0
openat(AT_FDCWD, "/usr/share/locale-langpack/en/LC_MESSAGES/libc.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
write(2, ": Permission denied", 19: Permission denied)     = 19
write(2, "\n", 1
lseek(0, 0, SEEK_CUR)                   = -1 ESPIPE (Illegal seek)
close(0)                                = 0
close(1)                                = 0
close(2)                                = 0
exitgroup(1)

Veja a quarta linha: unlinkat... que falha porque o diretório NÃO está vazio. Agora é isso que eu consideraria um comportamento inesperado, o fato de ele tentar excluir o diretório, apesar de não ter permissões de leitura.

ojklan
fonte
Ah, você está certo, eu vou corrigir isso quando chegar a um teclado real. Obrigado.
ojklan