From cd3d456c04ba4ae39d97a63a640219ab3bbf9b55 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Fri, 13 Dec 2024 18:37:25 +0100 Subject: [PATCH] Add fs::parent_canonical() --- src/libutil/file-system.cc | 15 +++++++++++++++ src/libutil/file-system.hh | 13 +++++++++++++ 2 files changed, 28 insertions(+) diff --git a/src/libutil/file-system.cc b/src/libutil/file-system.cc index 829700336c1..25cde03238f 100644 --- a/src/libutil/file-system.cc +++ b/src/libutil/file-system.cc @@ -765,4 +765,19 @@ bool isExecutableFileAmbient(const fs::path & exe) { ) == 0; } +namespace fs { + +std::filesystem::path parent_canonical(const std::filesystem::path & path) +{ + auto parent = path.parent_path(); + if (parent == path) { + // `path` is a root directory => trivially canonical + return parent; + } + return std::filesystem::canonical(parent) / path.filename(); } + +} // namespace nix::fs + + +} // namespace nix diff --git a/src/libutil/file-system.hh b/src/libutil/file-system.hh index 3c49181a0ad..faf928a3d20 100644 --- a/src/libutil/file-system.hh +++ b/src/libutil/file-system.hh @@ -141,6 +141,19 @@ inline bool symlink_exists(const std::filesystem::path & path) { return std::filesystem::exists(std::filesystem::symlink_status(path)); } +/** + * Canonicalize a path except for the last component. + * + * This is useful for getting the canonical location of a symlink. + * + * Consider the case where `foo/l` is a symlink. `canonical("foo/l")` will + * resolve the symlink `l` to its target. + * `parent_canonical("foo/l")` will not resolve the symlink `l` to its target, + * but does ensure that the returned parent path's parent equals its `canonical`, + * and can therefore be retrieved without traversing any symlinks. + */ +std::filesystem::path parent_canonical(const std::filesystem::path & path); + } // namespace fs /**