【问题标题】:std::filesystem recursive iterator will throw permission_denied even when skip_permission_denied is in use即使在使用 skip_permission_denied 时,std::filesystem 递归迭代器也会抛出 permission_denied
【发布时间】:2021-03-10 06:04:39
【问题描述】:

我正在编写一个 linux 命令行程序,它将返回目录的大小。该程序按预期工作,除非专门处理根目录。我知道根目录中的许多文件没有大小,因为它们是用于表示系统信息(如 /proc/)或 /dev/null/ 之类的特殊文件,所以我在 for 循环中使用了std::filesystem::directory_options::skip_permission_denied 来跳过权限问题,我使用了多个 try/catch 块来捕获异常。

但是,即使这样,仍然会抛出一个权限被拒绝的异常。见以下代码:

byte_size_and_num_files find_recursive(const std::filesystem::path& path)
{
    byte_size_and_num_files bsnf;
    std::filesystem::path pa;
    try
    {
      for(const auto& p: std::filesystem::recursive_directory_iterator(path, std::filesystem::directory_options::skip_permission_denied))
      {
          pa = p.path();
          if (std::filesystem::exists(p) && !std::filesystem::is_directory(p))
          {
            try
            {
              if(std::filesystem::is_regular_file(pa))
              {
                bsnf.size += std::filesystem::file_size(p);
                bsnf.files++;
              }
              else
                std::cout << "SKIPPED: size is not determinable: " << pa << "\n";
            }
            catch(std::filesystem::filesystem_error& e)
            {
              std::cout << "SKIPPED: size is not determinable: " << pa << "\n";
            }
            catch(std::bad_alloc)
            {
              std::cout << "Allocation error. Exiting..." << "\n";
              byte_size_and_num_files err;
              return err;
            }
          }
        }
    }
    catch(std::filesystem::filesystem_error& e)
    {
      std::cout << "Unable to access file or path " << pa <<": " << e.what() << "\n";
    }
    catch(std::bad_alloc)
    {
      std::cout << "Allocation error. Exiting..." << "\n";
      byte_size_and_num_files err;
      return err;
    }
    return bsnf;
}

输出:

...
SKIPPED: size is not determinable: "/lib/systemd/system/motd.service"
SKIPPED: size is not determinable: "/lib/systemd/system/mountall-bootclean.service"
SKIPPED: size is not determinable: "/lib/systemd/system/mountall.service"
SKIPPED: size is not determinable: "/lib/systemd/system/mountdevsubfs.service"
SKIPPED: size is not determinable: "/lib/systemd/system/mountkernfs.service"
SKIPPED: size is not determinable: "/lib/systemd/system/mountnfs-bootclean.service"
SKIPPED: size is not determinable: "/lib/systemd/system/mountnfs.service"
SKIPPED: size is not determinable: "/lib/systemd/system/rc.service"
SKIPPED: size is not determinable: "/lib/systemd/system/rcS.service"
SKIPPED: size is not determinable: "/lib/systemd/system/reboot.service"
SKIPPED: size is not determinable: "/lib/systemd/system/rmnologin.service"
SKIPPED: size is not determinable: "/lib/systemd/system/sendsigs.service"
SKIPPED: size is not determinable: "/lib/systemd/system/single.service"
SKIPPED: size is not determinable: "/lib/systemd/system/stop-bootlogd-single.service"
SKIPPED: size is not determinable: "/lib/systemd/system/stop-bootlogd.service"
SKIPPED: size is not determinable: "/lib/systemd/system/umountfs.service"
SKIPPED: size is not determinable: "/lib/systemd/system/umountnfs.service"
SKIPPED: size is not determinable: "/lib/systemd/system/umountroot.service"
SKIPPED: size is not determinable: "/lib/systemd/system/x11-common.service"
SKIPPED: size is not determinable: "/lib/systemd/system/lvm2.service"
Unable to access file or path "/proc/1/task/1/cwd": filesystem error: status: Permission denied [/proc/1/task/1/cwd]
/ is 262172.00 gigabytes with 143839 files.

什么给了?它的明确许可被拒绝应该总是被跳过,但它仍然被抛出。也很明显,无法确定大小的文件也会被跳过,但似乎发现并使用了一些不正确的文件大小(262172.00 GB 肯定不正确)。这只发生在通过根目录时,而不是像 home 这样的任何其他目录。

这可能是实施错误吗?

编辑: 一些调试揭示了一些重要信息。 似乎 /dev/pts/ 专门破坏了文件大小。 我不明白为什么不跳过此目录中的文件,因为 它们不是常规文件,因此应该跳过它们。我想 std::filesystem 错误地将它们识别为常规文件。

此外,std::filesystem::directory_options::skip_permission_denied 确实适用于除 /proc/1/task/1/cwd 之外的每个文件。我不确定为什么这个文件没有被跳过。

编辑 2 我已经找到了破坏 std::filesystem 的罪魁祸首目录。 一共有三个,

/dev/
/proc/ 
/var/cache/

/dev/ 会因任何原因弄乱文件大小,我猜 std::filesystem 会考虑这些常规文件,因此当您尝试获取文件大小时,您会得到一个垃圾编号。 /dev/ 的某些部分即使在使用跳过权限时也会抛出权限异常。

/proc/ 也会因为与 /dev/ 相同的原因而弄乱文件大小

/var/cache/ 似乎不会弄乱文件大小,但即使在使用跳过权限时也会抛出权限异常。

我认为这些是实现错误,因为这些目录中的文件不是常规文件,但 std::filesystem 将它们视为常规文件。

【问题讨论】:

  • 这里是one of the zillion examples,关于“否则”你是如何做到的:
  • 您使用的是哪个 C++ 运行时库(我想看看源代码)?也许您可以在调试器下运行程序并检查异常究竟是在哪里引发的!?
  • @WernerHenze G++ 9.3.0 带有标志 -std=c++17 和 -lstdc++fs。我会在 GDB 下运行它,看看它什么时候抛出。

标签: c++ c++17 std-filesystem


【解决方案1】:

docs on cppreference 表示skip_permission_denied 是“跳过会导致权限被拒绝错误的目录。”。它没有提到文件,只提到目录。该选项意味着如果您无法读取“/proc/sys/xyz”,则会跳过此目录并且不会引发异常。在您的情况下,这是受到尊重的,您不会从recursive_directory_iterator 获得filesystem_error 异常。但这与该迭代器返回的任何文件无关。

但是在std::filesystem::exists(p)std::filesystem::is_directory(p) 中抛出了异常。它由两个函数在内部调用的std::filesystem::status 抛出。 en.cppreferencestatus 中说“符号链接被跟踪到它们的目标”。如果您不是 root,则 /proc/1/task/1/cwd 无法做到这一点。

ls -alg /proc/1/task/1/cwd 显示文件模式 lrwxrwxrwx 但权限被拒绝消息。

$ ls -alg /proc/1/task/1/cwd
ls: Lesen der symbolischen Verknüpfung '/proc/1/task/1/cwd' nicht möglich: Keine Berechtigung
lrwxrwxrwx 1 root 0 Mär 11 08:53 /proc/1/task/1/cwd

我相信recursive_directory_iterator 只是查看文件模式,但cwd 似乎很特殊,需要附加访问规则。因此,您的解决方案可能是捕获std::filesystem::existsstd::filesystem::is_directory 引发的异常,忽略它们并继续。

【讨论】:

    猜你喜欢
    • 2014-08-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-08-13
    • 2013-07-07
    • 2021-01-17
    • 2018-10-21
    • 1970-01-01
    相关资源
    最近更新 更多