首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >std::filesystem::remove_all与clang++中潜在的缺陷

std::filesystem::remove_all与clang++中潜在的缺陷
EN

Stack Overflow用户
提问于 2021-07-01 05:49:38
回答 2查看 558关注 0票数 2

不尝试在家里使用

我和std::filesystem::remove_all有个奇怪的问题。我编写了一个程序,将N文件写入单个目录中的磁盘,然后删除所有文件(这是一个很好的理由)。然而,当我使用std::filesystem::remove_all时,我会得到这样的错误:

代码语言:javascript
运行
复制
filesystem error: cannot remove all: Structure needs cleaning [./tmp_storage] [./tmp_storage/2197772]

并且文件夹没有被删除(显然调用失败),并且在显示文件系统“损坏”之后调用ls

代码语言:javascript
运行
复制
$ ls tmp_storage/
ls: cannot access 'tmp_storage/2197772': Structure needs cleaning
ls: cannot access 'tmp_storage/5493417': Structure needs cleaning
...

我得修复文件系统。整个程序如下所示:

代码语言:javascript
运行
复制
#include <fmt/core.h>
#include <CLI/CLI.hpp>

#include <filesystem>
#include <fstream>
#include <string>
#include <exception>

int main(int argc, char** argv)
{
  size_t num_files{64000000};

  CLI::App app("Writes N number of files to dir in file system to check the maximum number of files in a directory");
  app.add_option("-c,--count", num_files, fmt::format("How many files generate [Default: {}]", num_files));
  CLI11_PARSE(app, argc, argv);

  std::string base_path = "./tmp_storage";

  if (!std::filesystem::exists(base_path))
  {
    std::filesystem::create_directory(base_path); 
  }

  size_t i;

  for (i = 1; i <= num_files; ++i)
  {
    std::string file_path = fmt::format("{}/{}", base_path, std::to_string(i));
    std::ofstream out(file_path, std::ios::binary);

    if (out.fail())
    {
      break; 
    }

    try
    {
      out << std::to_string(i); 
    }
    catch(const std::exception& e)
    {
      fmt::print("{}\n", e.what());
    }
  }

  fmt::print("Wrote {} out of {} files\n", i, num_files);

  try
  {
    std::filesystem::remove_all(base_path);
  }
  catch(const std::exception& e)
  {
    fmt::print("{}\n", e.what());
  }
  
  fmt::print("Done\n");
  
  return 0; 
}

使用以下Makefile编译:

代码语言:javascript
运行
复制
CC = clang++
CXX_FLAGS = -std=c++17
LINK_FLAGS = -lfmt

all:
    $(CC) $(CXX_FLAGS) main.cpp -o main $(LINK_FLAGS)

我已经能够使用XFS在Fedora 33/34和Ubuntu上复制这种行为,使用EXT4和XFS复制Ubuntu。这是std::filesystem::remov_all中的一个bug,还是我做错了什么?

对于Fedora,内核版本是:带有clang版本的Linux 5.12.12-300.fc34.x86_64 x86_64

代码语言:javascript
运行
复制
clang version 12.0.0 (Fedora 12.0.0-2.fc34)
Target: x86_64-unknown-linux-gnu
Thread model: posix
InstalledDir: /usr/bin
EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2021-07-29 17:14:39

注意:这不是底层和操作系统问题的解决方案,而是在C++中避免它的一种方法。

我们需要对原始代码所做的更改是“最小的”。对try块进行了所有更改。

代码语言:javascript
运行
复制
 try
  {
    std::filesystem::remove_all(base_path);
  }
  catch(const std::exception& e)
  {
    fmt::print("{}\n", e.what());
  }

并将:std::filesystem::remove_all(base_path);替换为顺序删除。

代码语言:javascript
运行
复制
for (auto& path : std::filesystem::directory_iterator(base_path))
{
    std::filesystem::remove(path);
}

将原始代码更改为

代码语言:javascript
运行
复制
#include <fmt/core.h>
#include <CLI/CLI.hpp>

#include <filesystem>
#include <fstream>
#include <string>
#include <exception>

int main(int argc, char** argv)
{
    size_t num_files{64000000};
    
    CLI::App app("Writes N number of files to dir in file system to check the maximum number of files in a directory");
    app.add_option("-c,--count", num_files, fmt::format("How many files generate [Default: {}]", num_files));
    CLI11_PARSE(app, argc, argv);

    std::string base_path = "./tmp_storage";

    if (!std::filesystem::exists(base_path))
    {
        std::filesystem::create_directory(base_path); 
    }

    size_t i;

    for (i = 1; i <= num_files; ++i)
    {
        std::string file_path = fmt::format("{}/{}", base_path, std::to_string(i));
        std::ofstream out(file_path, std::ios::binary);

        if (out.fail())
        {
            break; 
        }

        try
        {
            out << std::to_string(i); 
        }
        catch(const std::exception& e)
        {
            fmt::print("{}\n", e.what());
        }
    }

    fmt::print("Wrote {} out of {} files\n", i, num_files);

    try
    {
        for (auto& path : std::filesystem::directory_iterator(base_path))
        {
            std::filesystem::remove(path); 
        }
    }
    catch(const std::exception& e)
    {
        fmt::print("{}\n", e.what());
    }
  
    fmt::print("Done\n");
  
    return 0; 
}
票数 0
EN

Stack Overflow用户

发布于 2021-12-07 09:10:56

我试图在Fedora 34上使用这个经过修改的程序(移除fmt和cli11依赖项)来再现它:

代码语言:javascript
运行
复制
#include <filesystem>
#include <fstream>
#include <string>
#include <exception>

int main(int argc, char** argv)
{
  size_t num_files{64000000};

  if (argc > 1)
    num_files = std::stol(argv[1]);

  std::string base_path = "./tmp_storage";

  try
  {
    if (!std::filesystem::exists(base_path))
    {
      std::filesystem::create_directory(base_path); 
    }

    size_t i;

    for (i = 1; i <= num_files; ++i)
    {
      auto si = std::to_string(i);
      std::string file_path = base_path + '/' + si;
      std::ofstream out(file_path, std::ios::binary);

      if (out.fail())
        throw std::system_error(errno, std::generic_category(), "ofstream failed: " + file_path);

      try
      {
        out << si;
      }
      catch(const std::exception& e)
      {
        std::puts(e.what());
      }
    }

    std::printf("Wrote %zu out of %zu files\n", i - 1, num_files);

    std::filesystem::remove_all(base_path);
  }
  catch(const std::exception& e)
  {
    std::puts(e.what());
  }
  
  std::puts("Done");
  
  return 0; 
}

我不能在F34中使用ext4或xfs或使用btrfs的默认安装选择来再现错误。我也不能使用xfs在另一个服务器上复制它,其中有clang13.0.0和libstdc++-11.2.1,内核为5.14.0。这意味着我无法调试std::filesystem实现破坏文件系统的位置,也无法向内核团队报告。

我不确定该代码是否遇到了内核错误,或者您是否有错误的硬件。您检查过系统日志在文件系统损坏时说了什么吗?哪里有来自内核的错误?

编辑:另外,您是否在磁盘上使用LVM?我想我所有的测试都没有LVM。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/68204937

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档