summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/buildtool/file_system/file_system_manager.hpp28
-rw-r--r--test/buildtool/file_system/file_system_manager.test.cpp81
2 files changed, 109 insertions, 0 deletions
diff --git a/src/buildtool/file_system/file_system_manager.hpp b/src/buildtool/file_system/file_system_manager.hpp
index 1be50ac0..0bdfb8a6 100644
--- a/src/buildtool/file_system/file_system_manager.hpp
+++ b/src/buildtool/file_system/file_system_manager.hpp
@@ -118,6 +118,34 @@ class FileSystemManager {
}
}
+ template <ObjectType kType>
+ requires(IsFileObject(kType))
+ [[nodiscard]] static auto CreateFileHardlinkAs(
+ std::filesystem::path const& file_path,
+ std::filesystem::path const& link_path) noexcept -> bool {
+ // Set permissions first (permissions are a property of the file) so
+ // that the created link has the correct permissions as soon as the link
+ // creation is finished.
+ return SetFilePermissions(file_path, IsExecutableObject(kType)) and
+ CreateFileHardlink(file_path, link_path);
+ }
+
+ [[nodiscard]] static auto CreateFileHardlinkAs(
+ std::filesystem::path const& file_path,
+ std::filesystem::path const& link_path,
+ ObjectType output_type) noexcept -> bool {
+ switch (output_type) {
+ case ObjectType::File:
+ return CreateFileHardlinkAs<ObjectType::File>(file_path,
+ link_path);
+ case ObjectType::Executable:
+ return CreateFileHardlinkAs<ObjectType::Executable>(file_path,
+ link_path);
+ case ObjectType::Tree:
+ return false;
+ }
+ }
+
[[nodiscard]] static auto Rename(std::filesystem::path const& src,
std::filesystem::path const& dst,
bool no_clobber = false) noexcept -> bool {
diff --git a/test/buildtool/file_system/file_system_manager.test.cpp b/test/buildtool/file_system/file_system_manager.test.cpp
index fee5dca1..3e4bead9 100644
--- a/test/buildtool/file_system/file_system_manager.test.cpp
+++ b/test/buildtool/file_system/file_system_manager.test.cpp
@@ -404,3 +404,84 @@ TEST_CASE("FileSystemManager", "[file_system]") {
CHECK(FileSystemManager::RemoveFile(copy_file));
CHECK(not FileSystemManager::IsFile(copy_file));
}
+
+TEST_CASE("CreateFileHardlink", "[file_system]") {
+ std::filesystem::path to{"./tmp-CreateFileHardlink/linked_file"};
+ REQUIRE(FileSystemManager::CreateDirectory(to.parent_path()));
+
+ SECTION("Existing file") {
+ std::filesystem::path from{
+ "test/buildtool/file_system/data/example_file"};
+
+ CHECK(FileSystemManager::CreateFileHardlink(from, to));
+ CHECK(std::filesystem::exists(to));
+
+ CHECK_FALSE(FileSystemManager::CreateFileHardlink(from, to));
+ CHECK(std::filesystem::exists(to));
+
+ CHECK(FileSystemManager::RemoveFile(to));
+ CHECK(not std::filesystem::exists(to));
+ }
+ SECTION("Non-existing file") {
+ std::filesystem::path from{
+ "test/buildtool/file_system/data/this_file_does_not_exist"};
+
+ CHECK_FALSE(FileSystemManager::CreateFileHardlink(from, to));
+ CHECK_FALSE(std::filesystem::exists(to));
+ }
+ SECTION("Existing but not file") {
+ std::filesystem::path from{"./tmp-CreateFileHardlink/dir"};
+ CHECK(FileSystemManager::CreateDirectory(from));
+
+ CHECK_FALSE(FileSystemManager::CreateFileHardlink(from, to));
+ CHECK_FALSE(std::filesystem::exists(to));
+ }
+}
+
+TEST_CASE_METHOD(CopyFileFixture, "CreateFileHardlinkAs", "[file_system]") {
+ auto set_perm = [&](bool is_executable) {
+ auto const content = FileSystemManager::ReadFile(from_);
+ REQUIRE(content);
+ REQUIRE(FileSystemManager::RemoveFile(from_));
+ REQUIRE(FileSystemManager::WriteFileAs(
+ *content,
+ from_,
+ is_executable ? ObjectType::Executable : ObjectType::File));
+ };
+ auto run_test = [&](bool is_executable) {
+ // Hard link creation was successful
+ CHECK(FileSystemManager::CreateFileHardlinkAs(
+ from_,
+ to_,
+ is_executable ? ObjectType::Executable : ObjectType::File));
+
+ // file exists
+ CHECK(std::filesystem::exists(to_));
+ CHECK(std::filesystem::is_regular_file(to_));
+ CHECK(is_executable == FileSystemManager::IsExecutable(to_));
+
+ // permissions should be 0555 or 0444
+ CHECK((is_executable ? HasExecutablePermissions(to_)
+ : HasFilePermissions(to_)));
+ };
+ SECTION("as file") {
+ SECTION("from file") {
+ set_perm(false);
+ run_test(false);
+ }
+ SECTION("from executable") {
+ set_perm(true);
+ run_test(false);
+ }
+ }
+ SECTION("as executable") {
+ SECTION("from file") {
+ set_perm(false);
+ run_test(true);
+ }
+ SECTION("from executable") {
+ set_perm(true);
+ run_test(true);
+ }
+ }
+}