带有 clang++

问题描述

请勿在家尝试

我对 std::filesystem::remove_all一个奇怪的问题。 我编写了一个程序,将 N 文件写入磁盘的单个目录中,然后删除所有文件(这是有充分理由的)。 但是,当我使用 std::filesystem::remove_all 时,我会收到如下错误

filesystem error: cannot remove all: Structure needs cleaning [./tmp_storage] [./tmp_storage/2197772]

而且文件夹没有删除(显然调用失败),调用ls显示文件系统“损坏”:

$ ls tmp_storage/
ls: cannot access 'tmp_storage/2197772': Structure needs cleaning
ls: cannot access 'tmp_storage/5493417': Structure needs cleaning
...

我必须修复文件系统。完整的程序如下所示:

#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 编译:

CC = clang++
CXX_FLAGS = -std=c++17
LINK_FLAGS = -lfmt

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

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

对于 Fedora,内核版本为:Linux 5.12.12-300.fc34.x86_64 x86_64 和 clang 版本

clang version 12.0.0 (Fedora 12.0.0-2.fc34)
Target: x86_64-unkNown-linux-gnu
Thread model: posix
InstalledDir: /usr/bin

解决方法

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

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

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

并用顺序删除替换:std::filesystem::remove_all(base_path);

for (auto& path : std::filesystem::directory_iterator(base_path))
{
    std::filesystem::remove(path);
}

将原代码改为

#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; 
}