diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/buildtool/file_system/TARGETS | 1 | ||||
-rw-r--r-- | src/buildtool/file_system/file_system_manager.hpp | 74 | ||||
-rw-r--r-- | src/utils/cpp/path.hpp | 13 |
3 files changed, 86 insertions, 2 deletions
diff --git a/src/buildtool/file_system/TARGETS b/src/buildtool/file_system/TARGETS index 26bb3933..53be1bae 100644 --- a/src/buildtool/file_system/TARGETS +++ b/src/buildtool/file_system/TARGETS @@ -36,6 +36,7 @@ [ "object_type" , ["src/buildtool/logging", "logging"] , ["src/buildtool/system", "system"] + , ["src/utils/cpp", "path"] , ["@", "gsl", "", "gsl"] ] , "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 674d87f4..49357130 100644 --- a/src/buildtool/file_system/file_system_manager.hpp +++ b/src/buildtool/file_system/file_system_manager.hpp @@ -35,6 +35,7 @@ #include "src/buildtool/file_system/object_type.hpp" #include "src/buildtool/logging/logger.hpp" #include "src/buildtool/system/system.hpp" +#include "src/utils/cpp/path.hpp" namespace detail { static inline consteval auto BitWidth(int max_val) -> int { @@ -131,6 +132,54 @@ class FileSystemManager { return CreateFileImpl(file) == CreationStatus::Created; } + /// \brief We are POSIX-compliant, therefore we only care about the string + /// value the symlinks points to, whether it exists or not, not the target + /// type. As such, we don't distinguish directory or file targets. However, + /// for maximum compliance, we use the directory symlink creator. + [[nodiscard]] static auto CreateSymlink( + std::filesystem::path const& to, + std::filesystem::path const& link, + LogLevel log_failure_at = LogLevel::Error) noexcept -> bool { + try { + if (not CreateDirectory(link.parent_path())) { + Logger::Log(log_failure_at, + "can not create directory {}", + link.parent_path().string()); + return false; + } +#ifdef __unix__ + std::filesystem::create_directory_symlink(to, link); + return std::filesystem::is_symlink(link); +#else +// For non-unix systems one would have to differentiate between file and +// directory symlinks[1], which would require filesystem access and could lead +// to inconsistencies due to order of creation of existing symlink targets. +// [1]https://en.cppreference.com/w/cpp/filesystem/create_symlink +#error "Non-unix is not supported yet" +#endif + } catch (std::exception const& e) { + Logger::Log(log_failure_at, + "symlinking {} to {}\n{}", + to.string(), + link.string(), + e.what()); + return false; + } + } + + [[nodiscard]] static auto CreateNonUpwardsSymlink( + std::filesystem::path const& to, + std::filesystem::path const& link, + LogLevel log_failure_at = LogLevel::Error) noexcept -> bool { + if (PathIsNonUpwards(to)) { + return CreateSymlink(to, link, log_failure_at); + } + Logger::Log(log_failure_at, + "symlink failure: target {} is not non-upwards", + to.string()); + return false; + } + [[nodiscard]] static auto CreateFileHardlink( std::filesystem::path const& file_path, std::filesystem::path const& link_path, @@ -370,11 +419,32 @@ class FileSystemManager { } } + /// \brief Returns if symlink is non-upwards, i.e., its string content path + /// never passes itself in the directory tree. + [[nodiscard]] static auto IsNonUpwardsSymlink( + std::filesystem::path const& link) noexcept -> bool { + try { + if (not std::filesystem::is_symlink(link)) { + return false; + } + auto dest = std::filesystem::read_symlink(link); + return PathIsNonUpwards(dest); + } catch (std::exception const& e) { + Logger::Log(LogLevel::Error, e.what()); + return false; + } + } + + /// \brief Follow a symlink chain without existence check on resulting path [[nodiscard]] static auto ResolveSymlinks( - gsl::not_null<std::filesystem::path*> const& path) noexcept -> bool { + gsl::not_null<std::filesystem::path*> path) noexcept -> bool { try { while (std::filesystem::is_symlink(*path)) { - *path = std::filesystem::read_symlink(*path); + auto dest = std::filesystem::read_symlink(*path); + *path = dest.is_relative() + ? (std::filesystem::absolute(*path).parent_path() / + dest) + : dest; } } catch (std::exception const& e) { Logger::Log(LogLevel::Error, e.what()); diff --git a/src/utils/cpp/path.hpp b/src/utils/cpp/path.hpp index d872535e..a9fb08e5 100644 --- a/src/utils/cpp/path.hpp +++ b/src/utils/cpp/path.hpp @@ -16,6 +16,7 @@ #define INCLUDED_SRC_UTILS_CPP_PATH_HPP #include <filesystem> +#include <sstream> [[nodiscard]] static inline auto ToNormalPath(std::filesystem::path const& p) -> std::filesystem::path { @@ -29,4 +30,16 @@ return n; } +/// \brief Perform a non-upwards condition check on the given path. +/// A path is non-upwards if it is relative and it never references any other +/// path on a higher level in the directory tree than itself. +[[nodiscard]] static auto PathIsNonUpwards( + std::filesystem::path const& path) noexcept -> bool { + if (path.is_absolute()) { + return false; + } + // check non-upwards condition + return *path.lexically_normal().begin() != ".."; +} + #endif |