Почему «rm -r» не может удалить эту папку?

12

У меня есть папка с -wxразрешениями, folder1и другая папка внутри называется folder2с rwxразрешениями.

Я попытался удалить folder1с помощью этой команды:

rm -r folder1

Но я получил следующую ошибку:

rm: cannot remove 'folder1': Permission denied

Причина, по которой я думаю, что я получил эту ошибку, заключается в том, что rmпрограмме необходимо сначала получить содержимое folder1(получить имена файлов и папок внутри folder1него), чтобы иметь возможность удалить этот контент (потому что вы не можете удалить файл или папку, не зная ее названия, я думаю), а затем rmпрограмма может удалить folder1себя.

Но поскольку у folder1него нет readразрешения, rmпрограмма не может получить его содержимое, и, следовательно, она не может удалить его содержимое, и, поскольку оно не может удалить его содержимое, то оно не может удалить его.

Я прав?

Джон
источник
1
Выполните «ls -l» и скажите нам, каковы разрешения СПРАВОЧНИКА.
Джеймс

Ответы:

19

Я думаю, что ваш анализ правильный: вы не можете удалить каталог, так как он не пустой, и вы не можете очистить его, так как вы не можете видеть его содержимое.

Я только что попробовал:

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

Когда я писал «ты», я имел в виду любую программу, которую ты можешь запустить. rm -rСначала ваша команда видит, что folder1это каталог, поэтому она пытается обнаружить его содержимое, чтобы очистить его, но ей не удается пропустить разрешение на чтение, затем она пытается удалить его, но не удается, потому что он не пуст. «Отказано в доступе» вводит в заблуждение; Я думаю, что «Каталог не пустой» (например, rmdirотчеты) будет более уместным.)

user2233709
источник
4
Он не может сообщить Directory not emptyв этом случае, так как он не знал бы, пусто это или нет. Вы все равно получите ту же ошибку при попытке удалить пустой каталог, для которого у вас нет прав на чтение. (Также, пожалуйста, не обращайте внимания на мой предыдущий комментарий, у меня не было мышления).
Кусалананда
1
@Kusalananda Звучит нормально, но rmdirможет сообщить «Каталог не пуст». И если вы прочтете мой тест, вы увидите, что он разрешает удалить folder1каталог без разрешения на чтение , как только я его опустошу.
user2233709
2
Ваш тест показывает интересную разницу между нашими системами. Я получаю Permission deniedпри попытке, rm -r folder1когда он пуст. Я на OpenBSD, а не на Linux.
Кусалананда
@Kusalananda Это интересно. Я бы подумал, что это поведение указано в спецификации Single Unix, поэтому Linux и {Free, Net, Open} будут вести себя одинаково. (Для записи я использую Debian Stretch 9.8 с ядром linux 4.9.144-3 x86_64.)
user2233709
Хм ... Единственное, что говорит POSIX, это то, что если операнд является каталогом и -rиспользуется, каждая запись каталога (кроме .и ..) должна быть удалена, как если бы они были файловым операндом rm -r. Похоже, что GNU rmпросто делает rmdir()в каталоге, если он не читается, потому что он не сможет получить его содержимое.
Kusalananda
7

Для того чтобы удаление произошло, система должна иметь возможность прочитать содержимое и определить, что должно быть удалено.

Я попытался смоделировать то, что вы пытаетесь:

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

Если мы попытаемся удалить без разрешения на чтение, это не удастся:

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

В двух разных попытках различие заключается в том, что содержимое каталога не может быть прочитано (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

С разрешениями на чтение:

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

В заключение, даже если у вас есть каталог, в котором есть исполняемый бит, вам все равно нужны разрешения на чтение, чтобы вы могли видеть его содержимое и удалять папку. Это не то же самое для файла, хотя.

ttaran7
источник
0

Ну, у меня недостаточно репутации, чтобы комментировать ответ от ttaran7, так что, похоже, ответ будет. Мой положительный голос также не виден публично из-за низкой репутации. Я проголосовал за этот ответ за фактическое включение трассировки системных вызовов, а не просто предположение.

Чтобы ответить на вопрос ОП: Да, ваши рассуждения были правильными: вы заблокированы при невозможности прочитать каталог

Я выполнил ту же трассировку, что и они (ttaran7), потому что подозревал ту же причину: при сбое rmчтения каталога произойдет сбой вызова, и на этом все закончится, и нет никаких шансов жаловаться на то, что каталог пуст. После второго взгляда на трассу, которую я выбрал, я заметил, что был сделан системный вызов, чтобы попытаться отменить предоставленное имя файла в любом случае:

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)

Посмотрите на 4-ую строку: unlinkat... которая не выполняется, потому что каталог НЕ пуст. Теперь это то, что я считаю неожиданным поведением, тот факт, что он вообще пытается удалить каталог, несмотря на то, что у него нет разрешений на чтение.

ojklan
источник
Ах, вы правы, я исправлю это, когда доберусь до настоящей клавиатуры. Благодарю.
ojklan