#c #std-filesystem
Вопрос:
НЕ ПЫТАЙТЕСЬ ДЕЛАТЬ ЭТО ДОМА
У меня странная проблема с 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::exceptionamp; e)
{
fmt::print("{}n", e.what());
}
}
fmt::print("Wrote {} out of {} filesn", i, num_files);
try
{
std::filesystem::remove_all(base_path);
}
catch(const std::exceptionamp; e)
{
fmt::print("{}n", e.what());
}
fmt::print("Donen");
return 0;
}
Скомпилирован со следующим файлом Makefile:
CC = clang
CXX_FLAGS = -std=c 17
LINK_FLAGS = -lfmt
all:
$(CC) $(CXX_FLAGS) main.cpp -o main $(LINK_FLAGS)
Я смог воспроизвести поведение на сервере Fedora 33/34 и Ubuntu с Fedora с использованием XFS и Ubuntu с использованием EXT4 и XFS.
Это ошибка 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
Комментарии:
1. Были ли обе ваши попытки на одной и той же физической машине/жестком диске (так что либо двойная загрузка, либо виртуальные машины на одном хосте)?
2. Даже если в библиотеке C есть ошибка, обычно код пользовательского пространства не должен быть способен повредить файловые системы! Звучит больше похоже на проблему в коде самой операционной системы/файловой системы… возможно, предсказуемую, учитывая, что цель этой программы, похоже, заключается именно в стресс-тестировании.
3. @Scheff’Scat готово 🙂
4. Определенно ошибка операционной системы, эта ошибка может быть вызвана ошибкой/необычным поведением в файловой системе std::, но пользовательский код не должен быть в состоянии повредить файловую систему
5. С некоторой проверкой ошибок и рекурсией в подкаталоги это в значительной степени то, что
remove_all
происходит в любом случае: github.com/gcc-mirror/gcc/blob/… (Я предполагаю, что вы используете libstdc )
Ответ №1:
ПРИМЕЧАНИЕ: Это не решение базовых проблем и проблем операционной системы, а способ избежать их в C .
Изменение, которое нам нужно внести в исходный код, является «минимальным». Все изменения вносятся в блок try
try
{
std::filesystem::remove_all(base_path);
}
catch(const std::exceptionamp; e)
{
fmt::print("{}n", e.what());
}
и замените: std::filesystem::remove_all(base_path);
последовательным удалением.
for (autoamp; 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::exceptionamp; e)
{
fmt::print("{}n", e.what());
}
}
fmt::print("Wrote {} out of {} filesn", i, num_files);
try
{
for (autoamp; path : std::filesystem::directory_iterator(base_path))
{
std::filesystem::remove(path);
}
}
catch(const std::exceptionamp; e)
{
fmt::print("{}n", e.what());
}
fmt::print("Donen");
return 0;
}
Ответ №2:
Я попытался воспроизвести это в Fedora 34, используя эту модифицированную программу (удалив зависимости fmt и cli11).:
#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::exceptionamp; e)
{
std::puts(e.what());
}
}
std::printf("Wrote %zu out of %zu filesn", i - 1, num_files);
std::filesystem::remove_all(base_path);
}
catch(const std::exceptionamp; e)
{
std::puts(e.what());
}
std::puts("Done");
return 0;
}
Я не могу воспроизвести ошибки в F34, используя ext4 или xfs или установив по умолчанию btrfs. Я также не могу воспроизвести его на другом сервере, используя xfs, с clang 13.0.0 и libstdc -11.2.1 и ядром 5.14.0. Это означает, что я не могу отладить, где моя std::filesystem
реализация повреждает файловую систему, и не могу сообщить об этом команде ядра.
Я не уверен, встречается ли в коде ошибка ядра или у вас неисправное оборудование. Вы проверили, что говорилось в системном журнале примерно во время повреждения файловой системы? Где есть какие-либо ошибки со стороны ядра?
Правка: Кроме того, используете ли вы LVM для своих дисков? Я думаю, что все мои тесты были без LVM.