为什么“chown”命令不能使用“-R”标志对用户无人拥有的目录递归地工作?

为什么“chown”命令不能使用“-R”标志对用户无人拥有的目录递归地工作?

系统:x86_64 Linux 5.13.19-2-MANJARO。

我在“~/.local/share/Trash/expunged”中有一个奇怪的目录,其中包含用户拥有的一些目录没有人。我尝试以 root 身份执行以下命令:

rm -rf pathToWeirdDirectory

这会产生以下错误消息

rm:无法删除“pathToWeirdDirectory”:对于定义的数据类型来说值太大

使用更改目录所有权后

chown me:me pathToWeirdDirectory

我终于成功删除了该目录。还有一些这种类型的目录,其中有几个包含多个文件的子目录。我尝试执行以下命令:

chown -R me:me pathToSecondWeirdDirectory

这会产生以下错误消息:

chown:无法读取目录“pathToSeconWeirdDirectory/subdirectory1/subdirectory2”:权限被拒绝

就我而言,“-R”标志会递归地更改给定目录的所有权,因此我创建了以下测试用例:

directory/
    file
    subdirectory/
    file2

由用户拥有。我使用“chown -R root:根目录”成功更改了其所有权,包括预期的所有子目录和文件。

问题:为什么“chown”命令不能对用户拥有的目录使用“-R”标志递归地工作没有人?为什么无法删除用户拥有的文件没有人作为

编辑:作为对评论的回应,我想提供“pathToSecondWeirdDirectory”的全名。

〜/.local/share/Trash/expunged/294376611/5e5a7b41-3df4-4b2f-b7ac-f57d09ed1823/.sage/matplotlib-1.5.1

另一个例子

〜/.local/share/Trash/expunged/294376611/5e5a7b41-3df4-4b2f-b7ac-f57d09ed1823/.sage/R

编辑2:在马库斯给出了一个很好的答案之后,我使用相同的技术做了一些更多的调试。 chown 单个目录可以按预期工作。使用 -R 进行 chown 失败。我比较了输出,直到最后几行都是相同的。他们是这样的:

close(8)                                = 0
close(5)                                = 0
close(6)                                = 0
rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
newfstatat(AT_FDCWD, "security", {st_mode=S_IFDIR|0700, st_size=4096, ...}, AT_SYMLINK_NOFOLLOW) = 0
fchownat(AT_FDCWD, "security", 60202, 60202, 0) = 0
close(1)                                = 0
close(2)                                = 0
exit_group(0)                           = ?
+++ exited with 0 +++

乔恩-R

close(8)                                = 0
close(5)                                = 0
close(6)                                = 0
rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
newfstatat(AT_FDCWD, "startup", {st_mode=S_IFDIR|0700, st_size=4096, ...}, AT_SYMLINK_NOFOLLOW) = 0
openat(AT_FDCWD, "startup", O_RDONLY|O_NOCTTY|O_NONBLOCK|O_NOFOLLOW|O_CLOEXEC|O_DIRECTORY) = -1 EACCES (Permission denied)
openat(AT_FDCWD, "/usr/share/locale/locale.alias", O_RDONLY|O_CLOEXEC) = 3
newfstatat(3, "", {st_mode=S_IFREG|0644, st_size=2998, ...}, AT_EMPTY_PATH) = 0
read(3, "# Locale name alias data base.\n#"..., 4096) = 2998
read(3, "", 4096)                       = 0
close(3)                                = 0
openat(AT_FDCWD, "/usr/share/locale/en_US.UTF-8/LC_MESSAGES/coreutils.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/share/locale/en_US.utf8/LC_MESSAGES/coreutils.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/share/locale/en_US/LC_MESSAGES/coreutils.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/share/locale/en.UTF-8/LC_MESSAGES/coreutils.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/share/locale/en.utf8/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)
write(2, "chown: ", 7)                  = 7
write(2, "cannot read directory 'startup'", 31) = 31
openat(AT_FDCWD, "/usr/share/locale/en_US.UTF-8/LC_MESSAGES/libc.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/share/locale/en_US.utf8/LC_MESSAGES/libc.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/share/locale/en_US/LC_MESSAGES/libc.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/share/locale/en.UTF-8/LC_MESSAGES/libc.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/share/locale/en.utf8/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)
write(2, ": Permission denied", 19)     = 19
write(2, "\n", 1)                       = 1
close(1)                                = 0
close(2)                                = 0
exit_group(1)                           = ?
+++ exited with 1 +++

答案1

如果没有更多的调试,就不可能确定,但​​这确实看起来像 Linux 曾经遇到的 stat/stat64 问题(现在仍然如此)。

基本上,fstat系统调用用于查询“这个文件是目录吗?”之类的事情。 (如果你想递归地删除东西,知道这一点非常重要),“它是一个符号链接吗?”,“它的大小是多少?”。

最后一点正是有趣的地方:该系统调用需要一个指向 a 的指针struct stat,其中包含所有这些类型信息的字段。最初,文件大小整数为 32 位。现在,文件可以比这个更大。因此,需要一个新的调用来为您提供 64 位数字的信息。

现在,当您在大于 32 位变量可以容纳的文件上使用 32 位 stat 变体时该怎么办?显然,需要有一个错误条件,这样您就不会意外地认为 65 GB 的文件只有 1 GB 大小。当该错误传递给error//时,strerrorperror准确打印您所看到的错误消息。

当然,当文件统计信息的其他部分不适合您想要返回的类型时(或者超出最大页数时,可能还有其他几种情况),也适用相同的机制。

所以,现在有趣的部分来了:这不太可能是你相对现代的

/usr/bin/rm:ELF 64 位 LSB 饼可执行文件,x86-64,版本 1 (SYSV),动态链接,解释器 /lib64/ld-linux-x86-64.so.2,BuildID[sha1]=5d1d8b77d21f9362855e93f8cff5fc685127a26f ,对于 GNU/Linux 4.4.0,已删除

正在使用老的 fstat[at]系统调用。你可以试试:

cd /tmp
touch base
strace -o log.strace -e /stat rm base
grep '"base"' log.strace

应该大概yield 调用newfstatat,它肯定处理 64 位数字。

因此,有了这些知识,我们现在尝试删除一个奇怪的目录:

strace -o /tmp/log.strace rm -r /path/to/one/single_one/of/the/undeletable_directories

并滚动到日志底部:打印错误消息需要几行调用,但上面应该是一个产生的调用EOVERFLOW(提示:搜索该字符串!)。我的假设是这是不是一个通常的统计调用,但很可能是一个openat调用或由于不同原因而失败的东西。


¹ 我会运行gdb --args rm /path/to/weirddirectory,在对 的任何调用上设置断点error,如果这不起作用,则对 的任何调用设置断点write

² 甚至还有 Coreutils常见问题解答入口,archive.org 链接,因为我不相信 FSF 能够永远运行可靠的基础设施

答案2

请考虑您可能会在递归路径中遍历软链接。

建议尝试:

 chown -L -R me:me pathToSecondWeirdDirectory
 

可以用find命令删除遍历符号链接

 find -L <target dir1> <target dir2> ... -delete

相关内容