
В следующей ситуацииls -alh
total 0
drwxrwx--- 1 user http 20 Nov 30 08:08 .
drwxrws--- 1 user http 310 Nov 30 08:07 ..
drwx------ 1 http http 10 Nov 30 08:08 empty-subdir
drwx------ 1 http http 12 Nov 30 08:08 non-empty-subdir
где существуют два подкаталога (не принадлежащие мне), которые я перечисляю как:
sudo ls empty-subdir -alh
total 0
drwx------ 1 http http 10 Nov 30 08:08 .
drwxrwx--- 1 user http 20 Nov 30 08:08 ..
sudo ls non-empty-subdir -alh
total 0
drwx------ 1 http http 12 Nov 30 08:08 .
drwxrwx--- 1 user http 20 Nov 30 08:08 ..
drwx------ 1 http http 0 Nov 30 08:08 subdir
Разница между двумя подкаталогами в том, что непустой non-empty-subdir
содержит папку.
Мой вопрос в том, намеренно ли так получается, что при попытке rm -rf
удалить подкаталоги я получаю следующие результаты:
$ rm empty-subdir -rf
$ rm non-empty-subdir -rf
rm: cannot remove 'non-empty-subdir': Permission denied
$ ls -alh
total 0
drwxrwx---+ 1 user http 10 Nov 30 08:14 .
drwxrws---+ 1 user http 310 Nov 30 08:07 ..
drwx------+ 1 http http 12 Nov 30 08:08 non-empty-subdir
Похоже, что пользователю с правами записи в каталог разрешено удалять запись для файла или пустого подкаталога другого пользователя, но ненепустойподкаталог.
Идеальным ответом на этот вопрос была бы такая информация:
- подтверждение того, что описанное поведение воспроизводится на других машинах (а не просто причуды моей испорченной коробки)
- обоснование, объясняющее такое поведение (например, есть ли варианты использования?)
- обзор, есть ли различия между системами (BSD, Linux....)
Обновлять: Что касается комментария Ipor Sircer, я перепроверил сценарий без каких-либо функций ACL, и результат тот же. Поэтому я изменил вопрос, удалив es +
из списков, чтобы не давать повода для мысли, что поведение может быть связано с ACL.
решение1
Удалить каталог (с помощью rmdir()
системного вызова) можно только в том случае, если он пуст.
rm -r dir
удаляет каталог и все файлы в нем, начиная с листьев дерева каталогов и до самого корня ( dir
).
Чтобы удалить файл (как rmdir()
для каталогов, так и unlink()
для других типов файлов или *at()
их вариантов), важны не разрешения самого файла, а разрешения каталога, из которого вы удаляете файл (будьте осторожны, часть t
в разрешениях, например, для /tmp
, добавляет еще больше сложностей).
Прежде всего, вы на самом деле не удаляетефайл, вы отсоединяете его от каталога (и когда это последняя ссылка, которую вы удаляете, файл в конечном итоге удаляется), то есть вы изменяете каталог, поэтому вам нужномодифицирующийразрешения (на запись) для этого каталога.
Причина, по которой вы не можете удалить, non-empty-dir
заключается в том, что вы не можете subdir
сначала отсоединиться от него, так как у вас нет прав на изменение non-empty-dir
. У вас будет право отсоединиться non-empty-dir
от вашего домашнего каталога, так как у вас есть разрешение на запись/изменение в нем, только вы не можете удалить каталог, который не является пустым.
В вашем случае, как отметил @PeterCordes в комментариях, rmdir()
системный вызов завершается с ENOTEMPTY
ошибкой (Directory not empty), но поскольку у вас нетчитатьразрешения на доступ к каталогу, rm
не может даже узнать, какие файлы и каталоги (включая subdir
) ему нужно будет удалить из него, чтобы очистить его (не то чтобы он мог удалить их, если бы знал, так как у него нет прав на запись).
Вы также можете попасть в ситуации, когдаrm
могудалить каталог, если только можно узнать, какие файлы в нем находятся, как в случае с каталогом, доступным только для записи:
$ mkdir dir
$ touch dir/file
$ chmod a=,u=wx dir
$ ls -ld dir
d-wx------ 2 me me 4096 Nov 30 19:43 dir/
$ rm -rf dir
rm: cannot remove 'dir': Permission denied
Тем не менее, я могу удалить его, так как знаю, что он содержит только один file
файл:
$ rm dir/file
$ rmdir dir
$
Также обратите внимание, что в современных Unix-системах вы можете переименовать это non-empty-dir
, но в некоторых системах, таких как Linux или FreeBSD (но не Solaris),нетпереместите его в другой каталог, даже если у вас также есть разрешение на запись в этот каталог, как (я думаю, и для Linux, как предложилкомментарий к соответствующему коду) это повлечет за собой изменение non-empty-dir
( ..
запись в нем будет указывать на другой каталог).
Можно утверждать, что удаление вашего файла empty-dir
также подразумевает удаление записей ..
и .
в нем, то есть его изменение, но все же система позволяет вам это сделать.
решение2
Игнорируя потенциальное изменение через ACL, я могу подтвердить такое поведение для моей системы (без ACL).
Наблюдаемое поведение является логическим следствием двух принципов:
1) Права на каталог определяют, кто может изменять каталог, т.е. удалять записи в каталоге. Права записей в этом каталоге не играют никакой роли.
2) Удаление файла потенциально требует удаления и очистки связанной с ним информации, например, инодов, списков распределения блоков и т. д. Вот почему вы не можете удалить непустой подкаталог, не очистив все содержащиеся в нем файлы, поскольку в противном случае содержащиеся в нем файлы станут недоступными, а связанная с ним информация не будет очищена.
Итак, вы можете удалить empty-subdir
, поскольку у вас есть права на запись в каталог, в котором он находится. Вы не можете удалить non-empty-subdir
, поскольку у вас нет прав на очистку файлов, содержащихся в этом подкаталоге.
На самом деле нет никакого обоснования или варианта использования для этого. Можно было бы встроить рекурсивную очистку подкаталога в ядро, но оригинальный Unix делал все простым, а рекурсивная очистка была бы слишком сложной, когда ее можно было бы осуществить с помощью утилиты пользовательского пространства.
Я не могу дать всеобъемлющий обзор различных разновидностей, но именно так было в оригинальном Unix, и я ожидаю, что так будет в каждой разновидности Unix, и я был бы удивлен, если бы существовала разновидность Unix, которая вела бы себя по-другому.
решение3
Я попытался воспроизвести то, что вы описываете, и запустил strace rm -rf ./nonempty
. Это показывает следующее:
unlinkat(4, "subdir", AT_REMOVEDIR) = -1 EACCES (Permission denied)
и согласно unlinkat
руководству (которое в Linux такое же unlink(2)
, выделено мной):
EACCES Доступ на запись в каталог, содержащий pathname, не разрешен для эффективного UID процесса, или один из каталогов в pathname не разрешает поиск. (См. также path_resolution(7).)
Поскольку родительский каталог nonempty
не предоставляет user
разрешение x (поиск), это имеет смысл, исходя из EACCES
описания, которое subdir
не может быть удалено.