【问题标题】:recursive_directory_iterator's skip_permission_denied option appears to be ignored on macOS?recursive_directory_iterator 的 skip_permission_denied 选项似乎在 macOS 上被忽略了?
【发布时间】:2022-01-19 17:39:04
【问题描述】:

在 macOS 上使用 C++20 和 std::filesystem::recursive_directory_iterator,此代码:

for (auto& f : recursive_directory_iterator(getenv("HOME"), directory_options::skip_permission_denied)) {
    // dummy
}

根据我对文档的理解,哪个应该跳过它无权递归到的目录,在尝试递归到~/Library/Application Support/MobileSync/时遇到错误。

但是:

in recursive_directory_iterator::operator++(): attempting recursion into "/Users/t/Library/Application Support/MobileSync": Operation not permitted

我认为这意味着存在一些权限/安全功能,即使存在 skip_permission_denied,迭代器也不会跳过 - 这可能是什么,以及我如何让迭代器干净利落地跳过导致无论权限如何,它都会中断?

当遇到导致此问题的已知目录(如 MobileSync 或 .Trash)时,我可以手动 disable_recursion_pending(),但与能够提前检测目录何时会导致此问题相比,这将是一个混乱的解决方案。

【问题讨论】:

  • “Operation not allowed”是EPERM,而不是通常的EACCES(“Permission denied”),大概是因为macOS想要表明失败是由于需要提升的权限而不是需要不同的权限模式位(因为它们破坏了 Unix,因此后者在这种情况下无效)。
  • 我也有同样的问题。即使我设法处理了错误,我似乎也无法在迭代器中“继续”传递这个问题条目,尽管已经设置了 disable_recursion_pending()(发布错误)。
  • @FreudianSlip:我猜你在基于范围的 for 中尝试了 for 使用迭代器的副本,因为这是 fs::begin(iter) 返回的内容,disable_recursion_pending() 在你的预复制实例。

标签: c++ permissions std std-filesystem


【解决方案1】:

恐怕没有简单的方法可以解决它,因为迭代器在错误时“关闭”,所以发布错误disable_recursion_pending 将无济于事。我为libcxx (https://github.com/llvm/llvm-project/issues/48870) 和libstdc++ (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=99533) 打开了一个已解决的问题,但libcxx 尚未修复,即使那样它也必须作为在 macOS 上,标准库是系统的一部分。

一个公认的丑陋但可能的非硬编码解决方法是动态列入黑名单并重试,例如大致如下:

    // ugly workaround to iterate the home dir on macOS with std::filesystem:

    std::set<fs::path> blacklist;
    while(true) {
        fs::path lastPath;
        try {
            for(auto rdi = fs::recursive_directory_iterator(getenv("HOME"), fs::directory_options::skip_permission_denied);
                rdi != fs::recursive_directory_iterator();
                ++rdi) {
                auto& de = *rdi;
                lastPath = de.path();

                if(blacklist.count(de.path())) {
                    rdi.disable_recursion_pending();
                }
                else {

                    // collect info you need here
                    // ...

                }
                ++rdi;
            }
        }
        catch (fs::filesystem_error& fe) {
            if(!blacklist.insert(lastPath).second) {
                // exception on same path, double error, something went really wrong
                break;
            }
            else {
                // we blacklisted a new entry, reset your collected info here,
                // we need to restart
                // ...

                continue;
            }
        }

        // we are through and managed to get all info
        // ...

        break;
    }

当然,这是很多代码,它应该是一行代码,并且只在 macOS 上需要,所以如果有人使用它,它应该被包装掉。

需要注意的一件微妙的事情是,基于范围的 for 使用 fs::begin(fs::recursive_directory_iterator) 函数创建“不可见”副本,并且无法在正确的实例上调用 disable_recursion_pending()。这就是使用常规 for 循环的原因。

该解决方法的另一个难看的部分是,除了标准建议之外,异常的 path1()path2() 都没有提供违规路径,并且由于解析异常文本是一个坏主意,路径被记住在其他无用的 @ 987654332@变量。

总而言之,它可以工作,但是 这不是我实际使用的任何东西,因为树需要多次扫描(在我的笔记本上 "Application Support" 需要重新扫描六次才能通过没有这个hack的实现的运行时间的四倍),所以如果可以在 macOS 上使用std::filesystem 进行一般性的迭代,我会认为这更像是一个实验。

因此,遗憾的是,在这些问题得到解决之前,std::filesystem::recursive_directory_iterator 在 macOS 上并不是那么好,我继续在该平台上使用我的插入式文件系统替换,以表彰 skip_permission_deniedEPERM 和 @987654338 @(但仅限 utf-8)。

【讨论】:

    猜你喜欢
    • 2021-10-28
    • 2016-09-30
    • 2012-03-11
    • 1970-01-01
    • 1970-01-01
    • 2021-06-26
    • 2017-12-05
    • 2012-03-08
    • 2015-12-11
    相关资源
    最近更新 更多