summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorPaul Cristian Sarbu <paul.cristian.sarbu@huawei.com>2023-05-30 17:57:21 +0200
committerPaul Cristian Sarbu <paul.cristian.sarbu@huawei.com>2023-06-26 17:57:29 +0200
commit0b924c5c23a89760ddecf8d8f6baa19333f9b667 (patch)
tree53be66938dcfd1f38a7dea98566e6de39d6df10f /src
parentb94f1b857c6bc1eab909c4fb9a0ba569d6d28a03 (diff)
downloadjustbuild-0b924c5c23a89760ddecf8d8f6baa19333f9b667.tar.gz
filesystem: Add logic for handling (non-upwards) symlinks
Diffstat (limited to 'src')
-rw-r--r--src/buildtool/file_system/TARGETS1
-rw-r--r--src/buildtool/file_system/file_system_manager.hpp74
-rw-r--r--src/utils/cpp/path.hpp13
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