Pourquoi «rm -r» ne peut-il pas supprimer ce dossier?

12

J'ai un dossier avec des -wxautorisations appelé folder1et un autre dossier à l'intérieur appelé folder2avec des rwxautorisations.

J'ai essayé de supprimer en folder1utilisant cette commande:

rm -r folder1

Mais j'ai eu l'erreur suivante:

rm: cannot remove 'folder1': Permission denied

La raison pour laquelle je pense avoir obtenu cette erreur est que le rmprogramme doit d'abord obtenir le contenu de folder1(obtenir les noms des fichiers et des dossiers à l'intérieur folder1) afin de pouvoir supprimer ce contenu (car vous ne pouvez pas supprimer un fichier ou dossier sans connaître son nom je pense), puis le rmprogramme peut se supprimer folder1.

Mais comme il folder1n'a pas l' readautorisation, le rmprogramme ne peut pas obtenir son contenu, et donc il ne peut pas supprimer son contenu, et comme il ne peut pas supprimer son contenu, il ne peut pas le supprimer.

Ai-je raison?

John
la source
1
Faites "ls -l" et dites-nous quelles sont les autorisations du RÉPERTOIRE.
jamesqf

Réponses:

19

Je pense que votre analyse est correcte: vous ne pouvez pas supprimer le répertoire car il n'est pas vide, et vous ne pouvez pas le vider car vous ne pouvez pas voir son contenu.

Je viens de l'essayer:

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

Quand j'ai écrit «vous», je voulais dire n'importe quel programme que vous pourriez exécuter. Votre rm -rcommande voit d'abord qu'il folder1s'agit d'un répertoire, elle essaie donc de découvrir son contenu pour le vider, mais échoue pour l'autorisation de lecture manquante, puis elle essaie de le supprimer mais échoue car elle n'est pas vide. La «permission refusée» est trompeuse; Je pense que "Répertoire non vide" (comme les rmdirrapports) serait plus approprié.)

user2233709
la source
4
Il ne peut pas signaler Directory not emptydans ce cas car il ne saurait pas qu'il était vide ou non. Vous obtiendriez toujours la même erreur lorsque vous essayez de supprimer un répertoire vide sur lequel vous n'avez pas d'autorisations de lecture. (En outre, veuillez ignorer mon commentaire précédent, je n'avais pas ma casquette de réflexion).
Kusalananda
1
@Kusalananda Cela semble sain d'esprit, mais rmdirest capable de signaler «Répertoire non vide». Et si vous lisez mon test, vous verrez qu'il accepte de supprimer le folder1répertoire, sans autorisation de lecture , une fois que je l'ai vidé.
user2233709
2
Votre test montre une différence intéressante entre nos systèmes. J'obtiens un Permission denieden essayant rm -r folder1quand il est vide. Je suis sur OpenBSD, pas Linux.
Kusalananda
@Kusalananda C'est intéressant. J'aurais pensé que ce comportement était spécifié par la spécification Single Unix, afin que Linux et {Free, Net, Open} BSD se comportent de manière identique. (Pour mémoire, j'utilise Debian Stretch 9.8 avec un noyau linux 4.9.144-3 x86_64.)
user2233709
Hmm ... La seule chose que POSIX dit est que si l'opérande est un répertoire et -rest utilisé, chaque entrée de répertoire (à l'exception de .et ..) doit être supprimée comme s'il s'agissait d'un opérande de fichier de rm -r. Il semble que GNU rmfasse simplement un rmdir()sur le répertoire s'il n'est pas lisible, car il n'aura aucun moyen d'en obtenir le contenu.
Kusalananda
7

Pour que la suppression se produise, le système doit pouvoir lire le contenu et identifier ce qui doit être supprimé.

J'ai essayé de simuler ce que vous tentez:

[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 ~]$ 

Si nous essayons de supprimer sans autorisations de lecture, il échoue:

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

Dans une séquence pour les deux tentatives, la différence est que le contenu du répertoire ne peut pas être lu (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

Avec des autorisations de lecture:

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

Pour conclure même si vous possédez un répertoire et qu'il a le bit exécutable, vous avez toujours besoin d'autorisations de lecture pour que vous puissiez voir son contenu et supprimer le dossier. Ce n'est pas la même chose pour un fichier.

ttaran7
la source
0

Eh bien, je n'ai pas assez de réputation pour commenter la réponse de ttaran7, donc ça devrait être une réponse. Mon vote positif n'est pas non plus visible publiquement, en raison de sa faible réputation. J'ai voté en faveur de cette réponse pour avoir réellement inclus une trace d'appel système, plutôt que simplement de la spéculation.

Pour répondre à la question de l'OP: Oui, votre raisonnement était correct: vous êtes bloqué à défaut de lecture du répertoire

J'ai exécuté une trace similaire à ce qu'ils (ttaran7) avaient fait parce que je soupçonnais le même raisonnement: l' rmappel échouerait en ne lisant pas le répertoire et ce serait la fin de cela, aucune chance de se plaindre que le répertoire était vide. En jetant un deuxième coup d'œil à la trace que j'ai prise, j'ai remarqué qu'un appel système a été effectué pour tenter de dissocier le nom de fichier fourni de toute façon:

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)

Regardez la 4ème ligne: unlinkat... qui échoue car le répertoire n'est PAS vide. Maintenant, c'est ce que je considérerais comme un comportement inattendu, le fait qu'il essaie de supprimer le répertoire, bien qu'il n'ait pas d'autorisations de lecture.

ojklan
la source
Ah, tu as raison, je corrigerai ça quand j'arriverai à un vrai clavier. Merci.
ojklan