summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/buildtool/file_system/TARGETS1
-rw-r--r--src/buildtool/file_system/file_system_manager.hpp72
-rw-r--r--test/buildtool/file_system/file_system_manager.test.cpp102
3 files changed, 121 insertions, 54 deletions
diff --git a/src/buildtool/file_system/TARGETS b/src/buildtool/file_system/TARGETS
index ef817912..d780c47a 100644
--- a/src/buildtool/file_system/TARGETS
+++ b/src/buildtool/file_system/TARGETS
@@ -11,6 +11,7 @@
, "deps":
[ "object_type"
, ["src/buildtool/logging", "logging"]
+ , ["src/buildtool/system", "system"]
, ["@", "gsl-lite", "", "gsl-lite"]
]
, "stage": ["src", "buildtool", "file_system"]
diff --git a/src/buildtool/file_system/file_system_manager.hpp b/src/buildtool/file_system/file_system_manager.hpp
index 75902890..df04bcec 100644
--- a/src/buildtool/file_system/file_system_manager.hpp
+++ b/src/buildtool/file_system/file_system_manager.hpp
@@ -8,12 +8,14 @@
#include <optional>
#ifdef __unix__
+#include <sys/wait.h>
#include <unistd.h>
#endif
#include "gsl-lite/gsl-lite.hpp"
#include "src/buildtool/file_system/object_type.hpp"
#include "src/buildtool/logging/logger.hpp"
+#include "src/buildtool/system/system.hpp"
/// \brief Implements primitive file system functionality.
/// Catches all exceptions for use with exception-free callers.
@@ -139,29 +141,52 @@ class FileSystemManager {
[[nodiscard]] static auto CopyFile(
std::filesystem::path const& src,
std::filesystem::path const& dst,
+ bool fd_less = false,
std::filesystem::copy_options opt =
std::filesystem::copy_options::overwrite_existing) noexcept
-> bool {
- try {
- return std::filesystem::copy_file(src, dst, opt);
- } catch (std::exception const& e) {
- Logger::Log(LogLevel::Error,
- "copying file from {} to {}:\n{}",
- src.string(),
- dst.string(),
- e.what());
- return false;
+ if (fd_less) {
+ pid_t pid = ::fork();
+ if (pid == -1) {
+ Logger::Log(
+ LogLevel::Error,
+ "Failed to copy file: cannot fork a child process.");
+ return false;
+ }
+
+ if (pid == 0) {
+ // Disable logging errors in child to avoid the use of mutexes.
+ System::ExitWithoutCleanup(
+ CopyFileImpl</*kLogError=*/false>(src, dst, opt)
+ ? EXIT_SUCCESS
+ : EXIT_FAILURE);
+ }
+
+ int status{};
+ ::waitpid(pid, &status, 0);
+
+ // NOLINTNEXTLINE(hicpp-signed-bitwise)
+ if (WEXITSTATUS(status) != EXIT_SUCCESS) {
+ Logger::Log(LogLevel::Error,
+ "Failed copying file {} to {}",
+ src.string(),
+ dst.string());
+ return false;
+ }
+ return true;
}
+ return CopyFileImpl(src, dst, opt);
}
template <ObjectType kType>
requires(IsFileObject(kType)) [[nodiscard]] static auto CopyFileAs(
std::filesystem::path const& src,
std::filesystem::path const& dst,
+ bool fd_less = false,
std::filesystem::copy_options opt =
std::filesystem::copy_options::overwrite_existing) noexcept
-> bool {
- return CopyFile(src, dst, opt) and
+ return CopyFile(src, dst, fd_less, opt) and
SetFilePermissions(dst, IsExecutableObject(kType));
}
@@ -169,14 +194,16 @@ class FileSystemManager {
std::filesystem::path const& src,
std::filesystem::path const& dst,
ObjectType type,
+ bool fd_less = false,
std::filesystem::copy_options opt =
std::filesystem::copy_options::overwrite_existing) noexcept
-> bool {
switch (type) {
case ObjectType::File:
- return CopyFileAs<ObjectType::File>(src, dst, opt);
+ return CopyFileAs<ObjectType::File>(src, dst, fd_less, opt);
case ObjectType::Executable:
- return CopyFileAs<ObjectType::Executable>(src, dst, opt);
+ return CopyFileAs<ObjectType::Executable>(
+ src, dst, fd_less, opt);
case ObjectType::Tree:
break;
}
@@ -543,6 +570,27 @@ class FileSystemManager {
}
}
+ template <bool kLogError = true>
+ [[nodiscard]] static auto CopyFileImpl(
+ std::filesystem::path const& src,
+ std::filesystem::path const& dst,
+ std::filesystem::copy_options opt =
+ std::filesystem::copy_options::overwrite_existing) noexcept
+ -> bool {
+ try {
+ return std::filesystem::copy_file(src, dst, opt);
+ } catch (std::exception const& e) {
+ if constexpr (kLogError) {
+ Logger::Log(LogLevel::Error,
+ "copying file from {} to {}:\n{}",
+ src.string(),
+ dst.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 84f9e8a3..2d0016cf 100644
--- a/test/buildtool/file_system/file_system_manager.test.cpp
+++ b/test/buildtool/file_system/file_system_manager.test.cpp
@@ -167,30 +167,13 @@ TEST_CASE("ReadFile", "[file_system]") {
}
TEST_CASE_METHOD(CopyFileFixture, "CopyFile", "[file_system]") {
- // Copy file was successful
- CHECK(FileSystemManager::CopyFile(from_, to_));
-
- // file exists
- CHECK(std::filesystem::exists(to_));
- CHECK(std::filesystem::is_regular_file(to_));
-
- // Contents are equal
- auto const content_from = FileSystemManager::ReadFile(from_);
- CHECK(content_from.has_value());
- auto const content_to = FileSystemManager::ReadFile(to_);
- CHECK(content_to.has_value());
- CHECK(content_from == content_to);
-}
-
-TEST_CASE_METHOD(CopyFileFixture, "CopyFileAs", "[file_system]") {
- SECTION("as file") {
- // Copy as file was successful
- CHECK(FileSystemManager::CopyFileAs(from_, to_, ObjectType::File));
+ auto run_test = [&](bool fd_less) {
+ // Copy file was successful
+ CHECK(FileSystemManager::CopyFile(from_, to_, fd_less));
// file exists
CHECK(std::filesystem::exists(to_));
CHECK(std::filesystem::is_regular_file(to_));
- CHECK(not FileSystemManager::IsExecutable(to_));
// Contents are equal
auto const content_from = FileSystemManager::ReadFile(from_);
@@ -198,29 +181,62 @@ TEST_CASE_METHOD(CopyFileFixture, "CopyFileAs", "[file_system]") {
auto const content_to = FileSystemManager::ReadFile(to_);
CHECK(content_to.has_value());
CHECK(content_from == content_to);
+ };
- // permissions should be 0444
- CHECK(HasFilePermissions(to_));
+ SECTION("direct") { run_test(false); }
+ SECTION("fd_less") { run_test(true); }
+}
+
+TEST_CASE_METHOD(CopyFileFixture, "CopyFileAs", "[file_system]") {
+ SECTION("as file") {
+ auto run_test = [&](bool fd_less) {
+ // Copy as file was successful
+ CHECK(FileSystemManager::CopyFileAs(
+ from_, to_, ObjectType::File, fd_less));
+
+ // file exists
+ CHECK(std::filesystem::exists(to_));
+ CHECK(std::filesystem::is_regular_file(to_));
+ CHECK(not FileSystemManager::IsExecutable(to_));
+
+ // Contents are equal
+ auto const content_from = FileSystemManager::ReadFile(from_);
+ CHECK(content_from.has_value());
+ auto const content_to = FileSystemManager::ReadFile(to_);
+ CHECK(content_to.has_value());
+ CHECK(content_from == content_to);
+
+ // permissions should be 0444
+ CHECK(HasFilePermissions(to_));
+ };
+
+ SECTION("direct") { run_test(false); }
+ SECTION("fd_less") { run_test(true); }
}
SECTION("as executable") {
- // Copy as file was successful
- CHECK(
- FileSystemManager::CopyFileAs(from_, to_, ObjectType::Executable));
-
- // file exists
- CHECK(std::filesystem::exists(to_));
- CHECK(std::filesystem::is_regular_file(to_));
- CHECK(FileSystemManager::IsExecutable(to_));
-
- // Contents are equal
- auto const content_from = FileSystemManager::ReadFile(from_);
- CHECK(content_from.has_value());
- auto const content_to = FileSystemManager::ReadFile(to_);
- CHECK(content_to.has_value());
- CHECK(content_from == content_to);
-
- // permissions should be 0555
- CHECK(HasExecutablePermissions(to_));
+ auto run_test = [&](bool fd_less) {
+ // Copy as file was successful
+ CHECK(FileSystemManager::CopyFileAs(
+ from_, to_, ObjectType::Executable, fd_less));
+
+ // file exists
+ CHECK(std::filesystem::exists(to_));
+ CHECK(std::filesystem::is_regular_file(to_));
+ CHECK(FileSystemManager::IsExecutable(to_));
+
+ // Contents are equal
+ auto const content_from = FileSystemManager::ReadFile(from_);
+ CHECK(content_from.has_value());
+ auto const content_to = FileSystemManager::ReadFile(to_);
+ CHECK(content_to.has_value());
+ CHECK(content_from == content_to);
+
+ // permissions should be 0555
+ CHECK(HasExecutablePermissions(to_));
+ };
+
+ SECTION("direct") { run_test(false); }
+ SECTION("fd_less") { run_test(true); }
}
}
@@ -357,8 +373,10 @@ TEST_CASE("FileSystemManager", "[file_system]") {
CHECK(file_content == test_content);
// copy file without 'overwrite'
- CHECK(FileSystemManager::CopyFile(
- test_file, copy_file, std::filesystem::copy_options::none));
+ CHECK(FileSystemManager::CopyFile(test_file,
+ copy_file,
+ /*fd_less=*/false,
+ std::filesystem::copy_options::none));
// copy file with 'overwrite'
CHECK(FileSystemManager::CopyFile(copy_file, test_file));