summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/buildtool/file_system/file_system_manager.hpp85
-rw-r--r--test/buildtool/file_system/file_system_manager.test.cpp77
2 files changed, 112 insertions, 50 deletions
diff --git a/src/buildtool/file_system/file_system_manager.hpp b/src/buildtool/file_system/file_system_manager.hpp
index df04bcec..1be50ac0 100644
--- a/src/buildtool/file_system/file_system_manager.hpp
+++ b/src/buildtool/file_system/file_system_manager.hpp
@@ -445,49 +445,67 @@ class FileSystemManager {
return true;
}
- [[nodiscard]] static auto WriteFile(
- std::string const& content,
- std::filesystem::path const& file) noexcept -> auto {
+ [[nodiscard]] static auto WriteFile(std::string const& content,
+ std::filesystem::path const& file,
+ bool fd_less = false) noexcept -> bool {
if (not CreateDirectory(file.parent_path())) {
Logger::Log(LogLevel::Error,
"can not create directory {}",
file.parent_path().string());
return false;
}
- try {
- std::ofstream writer{file};
- if (!writer.is_open()) {
+ if (fd_less) {
+ pid_t pid = ::fork();
+ if (pid == -1) {
Logger::Log(
- LogLevel::Error, "can not open file {}", file.string());
+ LogLevel::Error,
+ "Failed to write file: cannot fork a child process.");
+ return false;
+ }
+
+ if (pid == 0) {
+ // Disable logging errors in child to avoid the use of mutexes.
+ System::ExitWithoutCleanup(
+ WriteFileImpl</*kLogError=*/false>(content, file)
+ ? EXIT_SUCCESS
+ : EXIT_FAILURE);
+ }
+
+ int status{};
+ ::waitpid(pid, &status, 0);
+
+ // NOLINTNEXTLINE(hicpp-signed-bitwise)
+ if (WEXITSTATUS(status) != EXIT_SUCCESS) {
+ Logger::Log(
+ LogLevel::Error, "Failed writing file {}", file.string());
return false;
}
- writer << content;
- writer.close();
return true;
- } catch (std::exception const& e) {
- Logger::Log(
- LogLevel::Error, "writing to {}:\n{}", file.string(), e.what());
- return false;
}
+ return WriteFileImpl(content, file);
}
template <ObjectType kType>
- requires(IsFileObject(kType)) [[nodiscard]] static auto WriteFileAs(
- std::string const& content,
- std::filesystem::path const& file) noexcept -> bool {
- return WriteFile(content, file) and
+ requires(IsFileObject(kType))
+ [[nodiscard]] static auto WriteFileAs(std::string const& content,
+ std::filesystem::path const& file,
+ bool fd_less = false) noexcept
+ -> bool {
+ return WriteFile(content, file, fd_less) and
SetFilePermissions(file, IsExecutableObject(kType));
}
[[nodiscard]] static auto WriteFileAs(std::string const& content,
std::filesystem::path const& file,
- ObjectType output_type) noexcept
+ ObjectType output_type,
+ bool fd_less = false) noexcept
-> bool {
switch (output_type) {
case ObjectType::File:
- return WriteFileAs<ObjectType::File>(content, file);
+ return WriteFileAs<ObjectType::File>(content, file, fd_less);
case ObjectType::Executable:
- return WriteFileAs<ObjectType::Executable>(content, file);
+ return WriteFileAs<ObjectType::Executable>(
+ content, file, fd_less);
case ObjectType::Tree:
return false;
}
@@ -591,6 +609,33 @@ class FileSystemManager {
}
}
+ template <bool kLogError = true>
+ [[nodiscard]] static auto WriteFileImpl(
+ std::string const& content,
+ std::filesystem::path const& file) noexcept -> bool {
+ try {
+ std::ofstream writer{file};
+ if (!writer.is_open()) {
+ if constexpr (kLogError) {
+ Logger::Log(
+ LogLevel::Error, "can not open file {}", file.string());
+ }
+ return false;
+ }
+ writer << content;
+ writer.close();
+ return true;
+ } catch (std::exception const& e) {
+ if constexpr (kLogError) {
+ Logger::Log(LogLevel::Error,
+ "writing to {}:\n{}",
+ file.string(),
+ e.what());
+ }
+ return false;
+ }
+ }
+
/// \brief Set special permissions for files.
/// Set to 0444 for non-executables and set to 0555 for executables.
static auto SetFilePermissions(std::filesystem::path const& path,
diff --git a/test/buildtool/file_system/file_system_manager.test.cpp b/test/buildtool/file_system/file_system_manager.test.cpp
index 2d0016cf..fee5dca1 100644
--- a/test/buildtool/file_system/file_system_manager.test.cpp
+++ b/test/buildtool/file_system/file_system_manager.test.cpp
@@ -274,50 +274,67 @@ TEST_CASE("RemoveFile", "[file_system]") {
TEST_CASE_METHOD(WriteFileFixture, "WriteFile", "[file_system]") {
std::string const content{"This are the contents\nof the file.\n"};
- CHECK(FileSystemManager::WriteFile(content, file_path_));
- CHECK(std::filesystem::exists(file_path_));
- CHECK(std::filesystem::is_directory(file_path_.parent_path()));
- CHECK(std::filesystem::is_regular_file(file_path_));
-
- auto const written_content = FileSystemManager::ReadFile(file_path_);
- CHECK(written_content.has_value());
- CHECK(written_content == content);
-}
-
-TEST_CASE_METHOD(WriteFileFixture, "WriteFileAs", "[file_system]") {
- SECTION("as a file") {
- std::string const content{"This are the contents\nof the file.\n"};
-
- CHECK(FileSystemManager::WriteFileAs(
- content, file_path_, ObjectType::File));
+ auto run_test = [&](bool fd_less) {
+ CHECK(FileSystemManager::WriteFile(content, file_path_, fd_less));
CHECK(std::filesystem::exists(file_path_));
CHECK(std::filesystem::is_directory(file_path_.parent_path()));
CHECK(std::filesystem::is_regular_file(file_path_));
- CHECK(not FileSystemManager::IsExecutable(file_path_));
auto const written_content = FileSystemManager::ReadFile(file_path_);
CHECK(written_content.has_value());
CHECK(written_content == content);
+ };
- // permissions should be 0444
- CHECK(HasFilePermissions(file_path_));
+ SECTION("direct") { run_test(false); }
+ SECTION("fd-less") { run_test(true); }
+}
+
+TEST_CASE_METHOD(WriteFileFixture, "WriteFileAs", "[file_system]") {
+ SECTION("as a file") {
+ std::string const content{"This are the contents\nof the file.\n"};
+
+ auto run_test = [&](bool fd_less) {
+ CHECK(FileSystemManager::WriteFileAs(
+ content, file_path_, ObjectType::File, fd_less));
+ CHECK(std::filesystem::exists(file_path_));
+ CHECK(std::filesystem::is_directory(file_path_.parent_path()));
+ CHECK(std::filesystem::is_regular_file(file_path_));
+ CHECK(not FileSystemManager::IsExecutable(file_path_));
+
+ auto const written_content =
+ FileSystemManager::ReadFile(file_path_);
+ CHECK(written_content.has_value());
+ CHECK(written_content == content);
+
+ // permissions should be 0444
+ CHECK(HasFilePermissions(file_path_));
+ };
+
+ SECTION("direct") { run_test(false); }
+ SECTION("fd-less") { run_test(true); }
}
SECTION("as an executable") {
std::string const content{"\n"};
- CHECK(FileSystemManager::WriteFileAs(
- content, file_path_, ObjectType::Executable));
- CHECK(std::filesystem::exists(file_path_));
- CHECK(std::filesystem::is_directory(file_path_.parent_path()));
- CHECK(std::filesystem::is_regular_file(file_path_));
- CHECK(FileSystemManager::IsExecutable(file_path_));
+ auto run_test = [&](bool fd_less) {
+ CHECK(FileSystemManager::WriteFileAs(
+ content, file_path_, ObjectType::Executable, fd_less));
+ CHECK(std::filesystem::exists(file_path_));
+ CHECK(std::filesystem::is_directory(file_path_.parent_path()));
+ CHECK(std::filesystem::is_regular_file(file_path_));
+ CHECK(FileSystemManager::IsExecutable(file_path_));
+
+ auto const written_content =
+ FileSystemManager::ReadFile(file_path_);
+ CHECK(written_content.has_value());
+ CHECK(written_content == content);
- auto const written_content = FileSystemManager::ReadFile(file_path_);
- CHECK(written_content.has_value());
- CHECK(written_content == content);
+ // permissions should be 0555
+ CHECK(HasExecutablePermissions(file_path_));
+ };
- // permissions should be 0555
- CHECK(HasExecutablePermissions(file_path_));
+ SECTION("direct") { run_test(false); }
+ SECTION("fd-less") { run_test(true); }
}
}