Dlaczego „rm -r” nie może usunąć tego folderu?

12

Mam folder z -wxuprawnieniami folder1i innym folderem w nim folder2z rwxuprawnieniami.

Próbowałem usunąć folder1za pomocą tego polecenia:

rm -r folder1

Ale dostałem następujący błąd:

rm: cannot remove 'folder1': Permission denied

Powodem, dla którego myślę, że dostałem ten błąd, jest to, że rmprogram musi najpierw pobrać zawartość folder1(uzyskać nazwy plików i folderów w środku folder1), aby móc usunąć tę zawartość (ponieważ nie można usunąć pliku lub folder bez znajomości jego nazwy, jak sądzę), a następnie rmprogram może się usunąć folder1.

Ponieważ jednak folder1nie ma readpozwolenia, rmprogram nie może pobrać jego zawartości, a zatem nie może usunąć swojej zawartości, a ponieważ nie może usunąć swojej zawartości, nie może jej usunąć.

Mam rację?

Jan
źródło
1
Wykonaj „ls -l” i powiedz nam, jakie są uprawnienia DIRECTORY.
jamesqf

Odpowiedzi:

19

Myślę, że Twoja analiza jest poprawna: nie możesz usunąć katalogu, ponieważ jest on pusty i nie możesz go opróżnić, ponieważ nie możesz zobaczyć jego zawartości.

Właśnie spróbowałem:

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

Kiedy pisałem „ty”, miałem na myśli każdy program, który możesz uruchomić. Twoje rm -rpolecenie najpierw widzi, że folder1jest to katalog, więc próbuje odkryć jego zawartość, aby go opróżnić, ale kończy się niepowodzeniem z powodu braku uprawnień do odczytu, a następnie próbuje go usunąć, ale kończy się niepowodzeniem, ponieważ nie jest pusty. „Odmowa zezwolenia” wprowadza w błąd; Myślę, że rmdirbardziej odpowiedni byłby „Katalog niepusty” (np. Raporty).)

użytkownik2233709
źródło
4
W Directory not emptytym przypadku nie może się zgłosić, ponieważ nie wiedziałby, że jest pusty lub nie. Nadal pojawia się ten sam błąd podczas próby usunięcia pustego katalogu, w którym nie masz uprawnień do odczytu. (Proszę również zignorować mój poprzedni komentarz, nie miałem założonego ograniczenia myślenia).
Kusalananda
1
@Kusalananda Brzmi Sane, ale rmdirjest w stanie zgłosić „Katalog nie jest pusty”. A jeśli przeczytasz mój test, zobaczysz, że akceptuje usunięcie folder1katalogu bez pozwolenia na odczyt , gdy go opróżnię.
user2233709
2
Twój test pokazuje interesującą różnicę między naszymi systemami. Dostaję, Permission deniedkiedy próbuję, rm -r folder1kiedy jest pusty. Korzystam z OpenBSD, nie Linux.
Kusalananda
@Kusalananda To ciekawe. Myślałbym, że takie zachowanie zostało określone w specyfikacji Single Unix, aby Linux i {Free, Net, Open} BSD zachowywały się identycznie. (Dla przypomnienia używam Debian Stretch 9.8 z jądrem Linuksa 4.9.144-3 x86_64.)
user2233709 24.02.19
Hmm ... Jedyne, co mówi POSIX, to to, że jeśli operand jest katalogiem i -rjest używany, każda pozycja katalogu (z wyjątkiem .i ..) powinna zostać usunięta tak, jakby była operandem pliku rm -r. Wygląda na to, że GNU rmpo prostu robi rmdir()w katalogu, jeśli nie jest czytelny, ponieważ nie będzie w stanie uzyskać jego zawartości.
Kusalananda
7

Aby nastąpiło usunięcie, system musi być w stanie odczytać zawartość i określić, co należy usunąć.

Próbowałem symulować to, co próbujesz:

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

Jeśli spróbujemy usunąć bez uprawnień do odczytu, nie powiedzie się:

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

W przypadku dwóch prób różnica polega na tym, że nie można odczytać zawartości katalogu (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

Z uprawnieniami do odczytu:

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

Aby zakończyć, nawet jeśli masz katalog z bitem wykonywalnym, nadal potrzebujesz uprawnień do odczytu, aby zobaczyć jego zawartość i usunąć folder. Jednak dla pliku to nie to samo.

ttaran7
źródło
0

Cóż, nie mam wystarczającej reputacji, aby skomentować odpowiedź ttaran7, więc wygląda na to, że odpowiedź będzie musiała być. Moje głosowanie nie jest również publicznie widoczne z powodu niskiej reputacji. Głosowałem za odpowiedzią na to, że w rzeczywistości uwzględniłem ślad wywołania systemowego, a nie tylko spekulacje.

Aby odpowiedzieć na pytanie OP: Tak, twoje rozumowanie było prawidłowe: blokuje się brak odczytu katalogu

Przebiegłem podobny ślad do tego, co zrobili (ttaran7), ponieważ podejrzewałem to samo rozumowanie: rmpołączenie nie powiedzie się po nie przeczytaniu katalogu i to będzie koniec tego, nie ma szans na narzekać, że katalog jest pusty. Po drugim spojrzeniu na ślad, który wziąłem, zauważyłem, że wywołano systemowe wywołanie, aby mimo to spróbować odłączyć podaną nazwę pliku:

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)

Spójrz na czwartą linię: unlinkat... która kończy się niepowodzeniem, ponieważ katalog NIE jest pusty. To właśnie uważam za nieoczekiwane zachowanie, fakt, że próbuje on w ogóle usunąć katalog, mimo że nie ma uprawnień do odczytu.

ojklan
źródło
Ach, masz rację, poprawię to, kiedy dojdę do prawdziwej klawiatury. Dzięki.
ojklan