diff options
author | Paul Cristian Sarbu <paul.cristian.sarbu@huawei.com> | 2023-06-14 16:53:33 +0200 |
---|---|---|
committer | Paul Cristian Sarbu <paul.cristian.sarbu@huawei.com> | 2023-06-26 17:57:29 +0200 |
commit | 59677e140ed064813660641d8e841e0fcc4af0ea (patch) | |
tree | a20db484c091e7bda753dad63c4e44270fa1de0d /src/buildtool/file_system/file_system_manager.hpp | |
parent | 5e3cc5a7a38e93159a81b940b19ced879a2d280c (diff) | |
download | justbuild-59677e140ed064813660641d8e841e0fcc4af0ea.tar.gz |
Allow non-upwards symlinks with local API
Diffstat (limited to 'src/buildtool/file_system/file_system_manager.hpp')
-rw-r--r-- | src/buildtool/file_system/file_system_manager.hpp | 78 |
1 files changed, 78 insertions, 0 deletions
diff --git a/src/buildtool/file_system/file_system_manager.hpp b/src/buildtool/file_system/file_system_manager.hpp index a319cc0e..d5b4bf41 100644 --- a/src/buildtool/file_system/file_system_manager.hpp +++ b/src/buildtool/file_system/file_system_manager.hpp @@ -27,6 +27,8 @@ #include <fcntl.h> #ifdef __unix__ +#include <fcntl.h> +#include <sys/stat.h> #include <sys/wait.h> #include <unistd.h> #endif @@ -338,6 +340,7 @@ class FileSystemManager { kSetEpochTime, kSetWritable>(src, dst, fd_less, opt); case ObjectType::Symlink: + return CopySymlinkAs<kSetEpochTime>(src, dst); case ObjectType::Tree: break; } @@ -381,6 +384,52 @@ class FileSystemManager { } } + /// \brief Create a symlink with option to set epoch time. + template <bool kSetEpochTime = false> + [[nodiscard]] static auto CreateSymlinkAs( + std::filesystem::path const& to, + std::filesystem::path const& link) noexcept -> bool { + return CreateSymlink(to, link) and + (not kSetEpochTime or SetEpochTime(link)); + } + + /// \brief Create symlink copy at location, with option to overwriting any + /// existing. Uses the content of src directly as the new target, whether + /// src is a regular file (CAS entry) or another symlink. + template <bool kSetEpochTime = false> + [[nodiscard]] static auto CopySymlinkAs( + std::filesystem::path const& src, + std::filesystem::path const& dst, + bool overwrite_existing = true) noexcept -> bool { + try { + if (overwrite_existing and Exists(dst) and + not std::filesystem::remove(dst)) { + Logger::Log(LogLevel::Debug, + "could not overwrite existing path {}", + dst.string()); + return false; + } + if (std::filesystem::is_symlink(src)) { + if (auto content = ReadSymlink(src)) { + return CreateSymlinkAs<kSetEpochTime>(*content, dst); + } + } + else { + if (auto content = ReadFile(src)) { + return CreateSymlinkAs<kSetEpochTime>(*content, dst); + } + } + } catch (std::exception const& e) { + Logger::Log(LogLevel::Error, + "copying symlink from {} to {}:\n{}", + src.string(), + dst.string(), + e.what()); + } + + return false; + } + [[nodiscard]] static auto RemoveFile( std::filesystem::path const& file) noexcept -> bool { try { @@ -946,11 +995,40 @@ class FileSystemManager { } } + /// \brief Set the last time of modification for a file (or symlink -- + /// POSIX-only). static auto SetEpochTime(std::filesystem::path const& file_path) noexcept -> bool { static auto const kPosixEpochTime = System::GetPosixEpoch<std::chrono::file_clock>(); try { + if (std::filesystem::is_symlink(file_path)) { + // Because std::filesystem::last_write_time follows + // symlinks, one has instead to manually call utimensat with + // the AT_SYMLINK_NOFOLLOW flag. On non-POSIX systems, we + // return false by default for symlinks. +#ifdef __unix__ + std::array<timespec, 2> times{}; // default is POSIX epoch + if (utimensat(AT_FDCWD, + file_path.c_str(), + times.data(), + AT_SYMLINK_NOFOLLOW) != 0) { + Logger::Log(LogLevel::Error, + "Call to utimensat for symlink {} failed with " + "error: {}", + file_path.string(), + strerror(errno)); + return false; + } + return true; +#else + Logger::Log( + LogLevel::Warning, + "Setting the last modification time attribute for a " + "symlink is unsupported!"); + return false; +#endif + } std::filesystem::last_write_time(file_path, kPosixEpochTime); return true; } catch (std::exception const& e) { |