¿Por qué "rm -r" no puede eliminar esta carpeta?

12

Tengo una carpeta con -wxpermisos llamados folder1y otra carpeta dentro llamada folder2con rwxpermisos.

Traté de eliminar folder1usando este comando:

rm -r folder1

Pero recibí el siguiente error:

rm: cannot remove 'folder1': Permission denied

La razón por la que creo que recibí este error es porque el rmprograma primero necesita obtener el contenido de folder1(obtener los nombres de los archivos y carpetas folder1que están dentro ) para poder eliminar ese contenido (porque no puede eliminar un archivo o carpeta sin saber su nombre, creo), y luego el rmprograma puede eliminarse folder1solo.

Pero como folder1no tiene el readpermiso, el rmprograma no puede obtener su contenido y, por lo tanto, no puede eliminar su contenido, y dado que no puede eliminar su contenido, no puede eliminarlo.

¿Estoy en lo correcto?

John
fuente
1
Haga "ls -l" y díganos cuáles son los permisos del DIRECTORIO.
jamesqf

Respuestas:

19

Creo que su análisis es correcto: no puede eliminar el directorio ya que no está vacío, y no puede vaciarlo ya que no puede ver su contenido.

Solo lo intenté:

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

Cuando escribí "usted", me refería a cualquier programa que pueda ejecutar. Su rm -rcomando primero ve que folder1es un directorio, por lo que intenta descubrir su contenido para vaciarlo, pero falla por faltar el permiso de lectura, luego intenta eliminarlo pero falla porque no está vacío. El "Permiso denegado" es engañoso; Creo que "Directorio no vacío" (como rmdirinformes) sería más apropiado).

usuario2233709
fuente
44
No puede informar Directory not emptyen este caso ya que no sabría que está vacío o no. Aún recibirá el mismo error cuando intente eliminar un directorio vacío en el que no tiene permisos de lectura. (Además, ignore mi comentario anterior, no tenía puesto mi límite de pensamiento).
Kusalananda
1
@Kusalananda Eso suena cuerdo, pero rmdirpuede informar "Directorio no vacío". Y si lees mi prueba, verás que acepta eliminar el folder1directorio, sin permiso de lectura , una vez que lo haya vaciado.
user2233709
2
Su prueba muestra una diferencia interesante entre nuestros sistemas. Me sale Permission deniedcuando intento rm -r folder1cuando está vacío. Estoy en OpenBSD, no en Linux.
Kusalananda
@Kusalananda Eso es interesante. Pensé que este comportamiento estaba especificado por la especificación Single Unix, de modo que Linux y {Free, Net, Open} BSD se comportarían de manera idéntica. (Para el registro, estoy usando Debian Stretch 9.8 con un kernel Linux 4.9.144-3 x86_64.)
user2233709
Hmm ... Lo único que dice POSIX es que si el operando es un directorio y -rse usa, cada entrada de directorio (excepto para .y ..) debe eliminarse como si fuera un operando de archivo rm -r. Parece que GNU rmsimplemente hace un rmdir()en el directorio si no es legible, porque no tendrá forma de obtener el contenido del mismo.
Kusalananda
7

Para que se produzca la eliminación, el sistema debe poder leer el contenido e identificar lo que se debe eliminar.

He intentado simular lo que estás intentando:

[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 intentamos eliminar sin permisos de lectura, falla:

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

En una secuencia para los dos intentos, la diferencia es que el contenido del directorio no se puede leer (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

Con permisos de lectura:

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, incluso si posee un directorio y tiene el bit ejecutable, aún necesita permisos de lectura para que pueda ver su contenido y eliminar la carpeta. Sin embargo, no es lo mismo para un archivo.

ttaran7
fuente
0

Bueno, no tengo suficiente reputación para comentar la respuesta de ttaran7, por lo que parece una respuesta que tendrá que ser. Mi voto positivo tampoco es visible públicamente, debido a la baja reputación. He votado esa respuesta por incluir realmente un seguimiento de llamadas del sistema, en lugar de solo especulaciones.

Para responder la pregunta del OP: Sí, su razonamiento era correcto: se bloquea al no leer el directorio

Hice un seguimiento similar a lo que ellos (ttaran7) habían hecho porque sospechaba el mismo razonamiento: la rmllamada fallaría al no leer el directorio y eso sería el final de eso, no hay posibilidad de quejarse de que el directorio esté vacío. Al echar un segundo vistazo al rastro que hice, noté que se realizó una llamada al sistema para intentar desvincular el nombre de archivo proporcionado de todos modos:

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)

Mire la cuarta línea: unlinkat... que falla porque el directorio NO está vacío. Ahora, eso es lo que consideraría un comportamiento inesperado, el hecho de que intenta eliminar el directorio, a pesar de no tener permisos de lectura.

ojklan
fuente
Ah, tienes razón, lo corregiré cuando llegue a un teclado real. Gracias.
ojklan