From 504c5e7cf9fcdff1f85a7d90a5d83b4f4d0b1a89 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Mon, 26 Aug 2024 17:37:52 -0400 Subject: [PATCH] Convert profiles to use `std::filesystem::path` Co-authored-by: Vinayak Goyal Co-authored-by: Eelco Dolstra --- src/libcmd/command.cc | 6 +- src/libexpr/eval-settings.cc | 12 +-- .../include/nix/store/tests/nix_api_store.hh | 3 +- .../build/derivation-building-goal.cc | 2 +- src/libstore/include/nix/store/pathlocks.hh | 12 +-- src/libstore/include/nix/store/profiles.hh | 44 +++++----- src/libstore/include/nix/store/store-api.hh | 6 ++ src/libstore/pathlocks.cc | 2 +- src/libstore/profiles.cc | 87 ++++++++++--------- src/libstore/store-api.cc | 5 ++ src/libstore/unix/pathlocks.cc | 8 +- src/libstore/windows/pathlocks.cc | 13 +-- src/libutil/current-process.cc | 2 +- src/libutil/file-system.cc | 15 +++- src/libutil/include/nix/util/file-system.hh | 9 +- src/libutil/include/nix/util/util.hh | 6 ++ .../nix-collect-garbage.cc | 2 +- src/nix/nix-env/nix-env.cc | 14 +-- src/nix/nix-env/user-env.cc | 4 +- src/nix/profile.cc | 5 +- 20 files changed, 154 insertions(+), 103 deletions(-) diff --git a/src/libcmd/command.cc b/src/libcmd/command.cc index b06d40902..798ef072e 100644 --- a/src/libcmd/command.cc +++ b/src/libcmd/command.cc @@ -297,7 +297,7 @@ void MixProfile::updateProfile(const BuiltPaths & buildables) MixDefaultProfile::MixDefaultProfile() { - profile = getDefaultProfile(); + profile = getDefaultProfile().string(); } MixEnvironment::MixEnvironment() @@ -391,7 +391,7 @@ void createOutLinks(const std::filesystem::path & outLink, const BuiltPaths & bu auto symlink = outLink; if (i) symlink += fmt("-%d", i); - store.addPermRoot(bo.path, absPath(symlink.string())); + store.addPermRoot(bo.path, absPath(symlink).string()); }, [&](const BuiltPath::Built & bfd) { for (auto & output : bfd.outputs) { @@ -400,7 +400,7 @@ void createOutLinks(const std::filesystem::path & outLink, const BuiltPaths & bu symlink += fmt("-%d", i); if (output.first != "out") symlink += fmt("-%s", output.first); - store.addPermRoot(output.second, absPath(symlink.string())); + store.addPermRoot(output.second, absPath(symlink).string()); } }, }, diff --git a/src/libexpr/eval-settings.cc b/src/libexpr/eval-settings.cc index 33c90259f..8e8b6bcd9 100644 --- a/src/libexpr/eval-settings.cc +++ b/src/libexpr/eval-settings.cc @@ -60,18 +60,18 @@ EvalSettings::EvalSettings(bool & readOnlyMode, EvalSettings::LookupPathHooks lo Strings EvalSettings::getDefaultNixPath() { Strings res; - auto add = [&](const Path & p, const std::string & s = std::string()) { + auto add = [&](const std::filesystem::path & p, const std::string & s = std::string()) { if (std::filesystem::exists(p)) { if (s.empty()) { - res.push_back(p); + res.push_back(p.string()); } else { - res.push_back(s + "=" + p); + res.push_back(s + "=" + p.string()); } } }; - add(getNixDefExpr() + "/channels"); - add(rootChannelsDir() + "/nixpkgs", "nixpkgs"); + add(std::filesystem::path{getNixDefExpr()} / "channels"); + add(rootChannelsDir() / "nixpkgs", "nixpkgs"); add(rootChannelsDir()); return res; @@ -108,4 +108,4 @@ Path getNixDefExpr() return settings.useXDGBaseDirectories ? getStateDir() + "/defexpr" : getHome() + "/.nix-defexpr"; } -} // namespace nix \ No newline at end of file +} // namespace nix diff --git a/src/libstore-test-support/include/nix/store/tests/nix_api_store.hh b/src/libstore-test-support/include/nix/store/tests/nix_api_store.hh index 7ecc5603b..829972d84 100644 --- a/src/libstore-test-support/include/nix/store/tests/nix_api_store.hh +++ b/src/libstore-test-support/include/nix/store/tests/nix_api_store.hh @@ -50,7 +50,8 @@ protected: #else // resolve any symlinks in i.e. on macOS /tmp -> /private/tmp // because this is not allowed for a nix store. - auto tmpl = nix::absPath(std::filesystem::path(nix::defaultTempDir()) / "tests_nix-store.XXXXXX", true); + auto tmpl = + nix::absPath(std::filesystem::path(nix::defaultTempDir()) / "tests_nix-store.XXXXXX", std::nullopt, true); nixDir = mkdtemp((char *) tmpl.c_str()); #endif diff --git a/src/libstore/build/derivation-building-goal.cc b/src/libstore/build/derivation-building-goal.cc index b4fc997a5..8221e12c6 100644 --- a/src/libstore/build/derivation-building-goal.cc +++ b/src/libstore/build/derivation-building-goal.cc @@ -307,7 +307,7 @@ Goal::Co DerivationBuildingGoal::tryToBuild() crashes. If we can't acquire the lock, then continue; hopefully some other goal can start a build, and if not, the main loop will sleep a few seconds and then retry this goal. */ - PathSet lockFiles; + std::set lockFiles; /* FIXME: Should lock something like the drv itself so we don't build same CA drv concurrently */ if (auto * localStore = dynamic_cast(&worker.store)) { diff --git a/src/libstore/include/nix/store/pathlocks.hh b/src/libstore/include/nix/store/pathlocks.hh index 05c7e079a..7e27bec4c 100644 --- a/src/libstore/include/nix/store/pathlocks.hh +++ b/src/libstore/include/nix/store/pathlocks.hh @@ -1,6 +1,8 @@ #pragma once ///@file +#include + #include "nix/util/file-descriptor.hh" namespace nix { @@ -10,12 +12,12 @@ namespace nix { * -1 is returned if create is false and the lock could not be opened * because it doesn't exist. Any other error throws an exception. */ -AutoCloseFD openLockFile(const Path & path, bool create); +AutoCloseFD openLockFile(const std::filesystem::path & path, bool create); /** * Delete an open lock file. */ -void deleteLockFile(const Path & path, Descriptor desc); +void deleteLockFile(const std::filesystem::path & path, Descriptor desc); enum LockType { ltRead, ltWrite, ltNone }; @@ -24,14 +26,14 @@ bool lockFile(Descriptor desc, LockType lockType, bool wait); class PathLocks { private: - typedef std::pair FDPair; + typedef std::pair FDPair; std::list fds; bool deletePaths; public: PathLocks(); - PathLocks(const PathSet & paths, const std::string & waitMsg = ""); - bool lockPaths(const PathSet & _paths, const std::string & waitMsg = "", bool wait = true); + PathLocks(const std::set & paths, const std::string & waitMsg = ""); + bool lockPaths(const std::set & _paths, const std::string & waitMsg = "", bool wait = true); ~PathLocks(); void unlock(); void setDeletion(bool deletePaths); diff --git a/src/libstore/include/nix/store/profiles.hh b/src/libstore/include/nix/store/profiles.hh index 75cd11340..1cc306744 100644 --- a/src/libstore/include/nix/store/profiles.hh +++ b/src/libstore/include/nix/store/profiles.hh @@ -7,12 +7,13 @@ * See the manual for additional information. */ -#include "nix/util/types.hh" -#include "nix/store/pathlocks.hh" - +#include #include #include +#include "nix/util/types.hh" +#include "nix/store/pathlocks.hh" + namespace nix { class StorePath; @@ -47,9 +48,9 @@ struct Generation * distinct contents to avoid bloat, but nothing stops two * non-adjacent generations from having the same contents. * - * @todo Use `StorePath` instead of `Path`? + * @todo Use `StorePath` instead of `std::filesystem::path`? */ - Path path; + std::filesystem::path path; /** * When the generation was created. This is extra metadata about the @@ -81,7 +82,7 @@ typedef std::list Generations; * * Note that the current/active generation need not be the latest one. */ -std::pair> findGenerations(Path profile); +std::pair> findGenerations(std::filesystem::path profile); struct LocalFSStore; @@ -96,7 +97,7 @@ struct LocalFSStore; * The behavior of reusing existing generations like this makes this * procedure idempotent. It also avoids clutter. */ -Path createGeneration(LocalFSStore & store, Path profile, StorePath outPath); +std::filesystem::path createGeneration(LocalFSStore & store, std::filesystem::path profile, StorePath outPath); /** * Unconditionally delete a generation @@ -111,7 +112,7 @@ Path createGeneration(LocalFSStore & store, Path profile, StorePath outPath); * * @todo Should we expose this at all? */ -void deleteGeneration(const Path & profile, GenerationNumber gen); +void deleteGeneration(const std::filesystem::path & profile, GenerationNumber gen); /** * Delete the given set of generations. @@ -128,7 +129,8 @@ void deleteGeneration(const Path & profile, GenerationNumber gen); * Trying to delete the currently active generation will fail, and cause * no generations to be deleted. */ -void deleteGenerations(const Path & profile, const std::set & gensToDelete, bool dryRun); +void deleteGenerations( + const std::filesystem::path & profile, const std::set & gensToDelete, bool dryRun); /** * Delete generations older than `max` passed the current generation. @@ -142,7 +144,7 @@ void deleteGenerations(const Path & profile, const std::set & * @param dryRun Log what would be deleted instead of actually doing * so. */ -void deleteGenerationsGreaterThan(const Path & profile, GenerationNumber max, bool dryRun); +void deleteGenerationsGreaterThan(const std::filesystem::path & profile, GenerationNumber max, bool dryRun); /** * Delete all generations other than the current one @@ -153,7 +155,7 @@ void deleteGenerationsGreaterThan(const Path & profile, GenerationNumber max, bo * @param dryRun Log what would be deleted instead of actually doing * so. */ -void deleteOldGenerations(const Path & profile, bool dryRun); +void deleteOldGenerations(const std::filesystem::path & profile, bool dryRun); /** * Delete generations older than `t`, except for the most recent one @@ -165,7 +167,7 @@ void deleteOldGenerations(const Path & profile, bool dryRun); * @param dryRun Log what would be deleted instead of actually doing * so. */ -void deleteGenerationsOlderThan(const Path & profile, time_t t, bool dryRun); +void deleteGenerationsOlderThan(const std::filesystem::path & profile, time_t t, bool dryRun); /** * Parse a temp spec intended for `deleteGenerationsOlderThan()`. @@ -180,19 +182,19 @@ time_t parseOlderThanTimeSpec(std::string_view timeSpec); * * @todo Always use `switchGeneration()` instead, and delete this. */ -void switchLink(Path link, Path target); +void switchLink(std::filesystem::path link, std::filesystem::path target); /** * Roll back a profile to the specified generation, or to the most * recent one older than the current. */ -void switchGeneration(const Path & profile, std::optional dstGen, bool dryRun); +void switchGeneration(const std::filesystem::path & profile, std::optional dstGen, bool dryRun); /** * Ensure exclusive access to a profile. Any command that modifies * the profile first acquires this lock. */ -void lockProfile(PathLocks & lock, const Path & profile); +void lockProfile(PathLocks & lock, const std::filesystem::path & profile); /** * Optimistic locking is used by long-running operations like `nix-env @@ -205,34 +207,34 @@ void lockProfile(PathLocks & lock, const Path & profile); * store. Most of the time, only the user environment has to be * rebuilt. */ -std::string optimisticLockProfile(const Path & profile); +std::string optimisticLockProfile(const std::filesystem::path & profile); /** * Create and return the path to a directory suitable for storing the user’s * profiles. */ -Path profilesDir(); +std::filesystem::path profilesDir(); /** * Return the path to the profile directory for root (but don't try creating it) */ -Path rootProfilesDir(); +std::filesystem::path rootProfilesDir(); /** * Create and return the path to the file used for storing the users's channels */ -Path defaultChannelsDir(); +std::filesystem::path defaultChannelsDir(); /** * Return the path to the channel directory for root (but don't try creating it) */ -Path rootChannelsDir(); +std::filesystem::path rootChannelsDir(); /** * Resolve the default profile (~/.nix-profile by default, * $XDG_STATE_HOME/nix/profile if XDG Base Directory Support is enabled), * and create if doesn't exist */ -Path getDefaultProfile(); +std::filesystem::path getDefaultProfile(); } // namespace nix diff --git a/src/libstore/include/nix/store/store-api.hh b/src/libstore/include/nix/store/store-api.hh index 4c0b156fa..9535227eb 100644 --- a/src/libstore/include/nix/store/store-api.hh +++ b/src/libstore/include/nix/store/store-api.hh @@ -996,6 +996,12 @@ OutputPathMap resolveDerivedPath(Store &, const DerivedPath::Built &, Store * ev */ std::string showPaths(const PathSet & paths); +/** + * Display a set of paths in human-readable form (i.e., between quotes + * and separated by commas). + */ +std::string showPaths(const std::set paths); + std::optional decodeValidPathInfo(const Store & store, std::istream & str, std::optional hashGiven = std::nullopt); diff --git a/src/libstore/pathlocks.cc b/src/libstore/pathlocks.cc index 068c65625..a8e828655 100644 --- a/src/libstore/pathlocks.cc +++ b/src/libstore/pathlocks.cc @@ -13,7 +13,7 @@ PathLocks::PathLocks() { } -PathLocks::PathLocks(const PathSet & paths, const std::string & waitMsg) +PathLocks::PathLocks(const std::set & paths, const std::string & waitMsg) : deletePaths(false) { lockPaths(paths, waitMsg); diff --git a/src/libstore/profiles.cc b/src/libstore/profiles.cc index 3f6fcb6ff..22d3f8f89 100644 --- a/src/libstore/profiles.cc +++ b/src/libstore/profiles.cc @@ -31,12 +31,12 @@ static std::optional parseName(const std::string & profileName return {}; } -std::pair> findGenerations(Path profile) +std::pair> findGenerations(std::filesystem::path profile) { Generations gens; - std::filesystem::path profileDir = dirOf(profile); - auto profileName = std::string(baseNameOf(profile)); + std::filesystem::path profileDir = profile.parent_path(); + auto profileName = profile.filename().string(); for (auto & i : DirectoryIterator{profileDir}) { checkInterrupt(); @@ -48,18 +48,20 @@ std::pair> findGenerations(Path pro gens.sort([](const Generation & a, const Generation & b) { return a.number < b.number; }); - return {gens, pathExists(profile) ? parseName(profileName, readLink(profile)) : std::nullopt}; + return {gens, pathExists(profile) ? parseName(profileName, readLink(profile).string()) : std::nullopt}; } /** * Create a generation name that can be parsed by `parseName()`. */ -static Path makeName(const Path & profile, GenerationNumber num) +static std::filesystem::path makeName(const std::filesystem::path & profile, GenerationNumber num) { - return fmt("%s-%s-link", profile, num); + /* NB std::filesystem::path when put in format strings is + quoted automatically. */ + return fmt("%s-%s-link", profile.string(), num); } -Path createGeneration(LocalFSStore & store, Path profile, StorePath outPath) +std::filesystem::path createGeneration(LocalFSStore & store, std::filesystem::path profile, StorePath outPath) { /* The new generation number should be higher than old the previous ones. */ @@ -90,21 +92,24 @@ Path createGeneration(LocalFSStore & store, Path profile, StorePath outPath) to the permanent roots (of which the GC would have a stale view). If we didn't do it this way, the GC might remove the user environment etc. we've just built. */ - Path generation = makeName(profile, num + 1); - store.addPermRoot(outPath, generation); + auto generation = makeName(profile, num + 1); + store.addPermRoot(outPath, generation.string()); return generation; } -static void removeFile(const Path & path) +static void removeFile(const std::filesystem::path & path) { - if (remove(path.c_str()) == -1) - throw SysError("cannot unlink '%1%'", path); + try { + std::filesystem::remove(path); + } catch (std::filesystem::filesystem_error & e) { + throw SysError("removing file '%1%'", path); + } } -void deleteGeneration(const Path & profile, GenerationNumber gen) +void deleteGeneration(const std::filesystem::path & profile, GenerationNumber gen) { - Path generation = makeName(profile, gen); + std::filesystem::path generation = makeName(profile, gen); removeFile(generation); } @@ -117,7 +122,7 @@ void deleteGeneration(const Path & profile, GenerationNumber gen) * * - We only actually delete if `dryRun` is false. */ -static void deleteGeneration2(const Path & profile, GenerationNumber gen, bool dryRun) +static void deleteGeneration2(const std::filesystem::path & profile, GenerationNumber gen, bool dryRun) { if (dryRun) notice("would remove profile version %1%", gen); @@ -127,7 +132,8 @@ static void deleteGeneration2(const Path & profile, GenerationNumber gen, bool d } } -void deleteGenerations(const Path & profile, const std::set & gensToDelete, bool dryRun) +void deleteGenerations( + const std::filesystem::path & profile, const std::set & gensToDelete, bool dryRun) { PathLocks lock; lockProfile(lock, profile); @@ -153,7 +159,7 @@ static inline void iterDropUntil(Generations & gens, auto && i, auto && cond) ; } -void deleteGenerationsGreaterThan(const Path & profile, GenerationNumber max, bool dryRun) +void deleteGenerationsGreaterThan(const std::filesystem::path & profile, GenerationNumber max, bool dryRun) { if (max == 0) throw Error("Must keep at least one generation, otherwise the current one would be deleted"); @@ -178,7 +184,7 @@ void deleteGenerationsGreaterThan(const Path & profile, GenerationNumber max, bo deleteGeneration2(profile, i->number, dryRun); } -void deleteOldGenerations(const Path & profile, bool dryRun) +void deleteOldGenerations(const std::filesystem::path & profile, bool dryRun) { PathLocks lock; lockProfile(lock, profile); @@ -190,7 +196,7 @@ void deleteOldGenerations(const Path & profile, bool dryRun) deleteGeneration2(profile, i.number, dryRun); } -void deleteGenerationsOlderThan(const Path & profile, time_t t, bool dryRun) +void deleteGenerationsOlderThan(const std::filesystem::path & profile, time_t t, bool dryRun) { PathLocks lock; lockProfile(lock, profile); @@ -238,16 +244,16 @@ time_t parseOlderThanTimeSpec(std::string_view timeSpec) return curTime - *days * 24 * 3600; } -void switchLink(Path link, Path target) +void switchLink(std::filesystem::path link, std::filesystem::path target) { /* Hacky. */ - if (dirOf(target) == dirOf(link)) - target = baseNameOf(target); + if (target.parent_path() == link.parent_path()) + target = target.filename(); replaceSymlink(target, link); } -void switchGeneration(const Path & profile, std::optional dstGen, bool dryRun) +void switchGeneration(const std::filesystem::path & profile, std::optional dstGen, bool dryRun) { PathLocks lock; lockProfile(lock, profile); @@ -274,44 +280,47 @@ void switchGeneration(const Path & profile, std::optional dstG switchLink(profile, dst->path); } -void lockProfile(PathLocks & lock, const Path & profile) +void lockProfile(PathLocks & lock, const std::filesystem::path & profile) { lock.lockPaths({profile}, fmt("waiting for lock on profile '%1%'", profile)); lock.setDeletion(true); } -std::string optimisticLockProfile(const Path & profile) +std::string optimisticLockProfile(const std::filesystem::path & profile) { - return pathExists(profile) ? readLink(profile) : ""; + return pathExists(profile) ? readLink(profile).string() : ""; } -Path profilesDir() +std::filesystem::path profilesDir() { - auto profileRoot = isRootUser() ? rootProfilesDir() : createNixStateDir() + "/profiles"; + auto profileRoot = isRootUser() ? rootProfilesDir() : std::filesystem::path{createNixStateDir()} / "profiles"; createDirs(profileRoot); return profileRoot; } -Path rootProfilesDir() +std::filesystem::path rootProfilesDir() { - return settings.nixStateDir + "/profiles/per-user/root"; + return std::filesystem::path{settings.nixStateDir} / "profiles/per-user/root"; } -Path getDefaultProfile() +std::filesystem::path getDefaultProfile() { - Path profileLink = settings.useXDGBaseDirectories ? createNixStateDir() + "/profile" : getHome() + "/.nix-profile"; + std::filesystem::path profileLink = settings.useXDGBaseDirectories + ? std::filesystem::path{createNixStateDir()} / "profile" + : std::filesystem::path{getHome()} / ".nix-profile"; try { - auto profile = profilesDir() + "/profile"; + auto profile = profilesDir() / "profile"; if (!pathExists(profileLink)) { replaceSymlink(profile, profileLink); } // Backwards compatibility measure: Make root's profile available as // `.../default` as it's what NixOS and most of the init scripts expect - Path globalProfileLink = settings.nixStateDir + "/profiles/default"; + auto globalProfileLink = std::filesystem::path{settings.nixStateDir} / "profiles" / "default"; if (isRootUser() && !pathExists(globalProfileLink)) { replaceSymlink(profile, globalProfileLink); } - return absPath(readLink(profileLink), dirOf(profileLink)); + auto linkDir = profileLink.parent_path(); + return absPath(readLink(profileLink), &linkDir); } catch (Error &) { return profileLink; } catch (std::filesystem::filesystem_error &) { @@ -319,14 +328,14 @@ Path getDefaultProfile() } } -Path defaultChannelsDir() +std::filesystem::path defaultChannelsDir() { - return profilesDir() + "/channels"; + return profilesDir() / "channels"; } -Path rootChannelsDir() +std::filesystem::path rootChannelsDir() { - return rootProfilesDir() + "/channels"; + return rootProfilesDir() / "channels"; } } // namespace nix diff --git a/src/libstore/store-api.cc b/src/libstore/store-api.cc index c292e2e43..52130668c 100644 --- a/src/libstore/store-api.cc +++ b/src/libstore/store-api.cc @@ -1126,6 +1126,11 @@ std::string StoreDirConfig::showPaths(const StorePathSet & paths) const return s; } +std::string showPaths(const std::set paths) +{ + return concatStringsSep(", ", quoteFSPaths(paths)); +} + std::string showPaths(const PathSet & paths) { return concatStringsSep(", ", quoteStrings(paths)); diff --git a/src/libstore/unix/pathlocks.cc b/src/libstore/unix/pathlocks.cc index e3f411a5d..fc1206012 100644 --- a/src/libstore/unix/pathlocks.cc +++ b/src/libstore/unix/pathlocks.cc @@ -13,7 +13,7 @@ namespace nix { -AutoCloseFD openLockFile(const Path & path, bool create) +AutoCloseFD openLockFile(const std::filesystem::path & path, bool create) { AutoCloseFD fd; @@ -24,7 +24,7 @@ AutoCloseFD openLockFile(const Path & path, bool create) return fd; } -void deleteLockFile(const Path & path, Descriptor desc) +void deleteLockFile(const std::filesystem::path & path, Descriptor desc) { /* Get rid of the lock file. Have to be careful not to introduce races. Write a (meaningless) token to the file to indicate to @@ -69,7 +69,7 @@ bool lockFile(Descriptor desc, LockType lockType, bool wait) return true; } -bool PathLocks::lockPaths(const PathSet & paths, const std::string & waitMsg, bool wait) +bool PathLocks::lockPaths(const std::set & paths, const std::string & waitMsg, bool wait) { assert(fds.empty()); @@ -81,7 +81,7 @@ bool PathLocks::lockPaths(const PathSet & paths, const std::string & waitMsg, bo preventing deadlocks. */ for (auto & path : paths) { checkInterrupt(); - Path lockPath = path + ".lock"; + std::filesystem::path lockPath = path + ".lock"; debug("locking path '%1%'", path); diff --git a/src/libstore/windows/pathlocks.cc b/src/libstore/windows/pathlocks.cc index c4e3a3d39..88fb4e22e 100644 --- a/src/libstore/windows/pathlocks.cc +++ b/src/libstore/windows/pathlocks.cc @@ -13,10 +13,10 @@ namespace nix { using namespace nix::windows; -void deleteLockFile(const Path & path, Descriptor desc) +void deleteLockFile(const std::filesystem::path & path, Descriptor desc) { - int exit = DeleteFileA(path.c_str()); + int exit = DeleteFileW(path.c_str()); if (exit == 0) warn("%s: &s", path, std::to_string(GetLastError())); } @@ -36,9 +36,9 @@ void PathLocks::unlock() fds.clear(); } -AutoCloseFD openLockFile(const Path & path, bool create) +AutoCloseFD openLockFile(const std::filesystem::path & path, bool create) { - AutoCloseFD desc = CreateFileA( + AutoCloseFD desc = CreateFileW( path.c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, @@ -103,13 +103,14 @@ bool lockFile(Descriptor desc, LockType lockType, bool wait) } } -bool PathLocks::lockPaths(const PathSet & paths, const std::string & waitMsg, bool wait) +bool PathLocks::lockPaths(const std::set & paths, const std::string & waitMsg, bool wait) { assert(fds.empty()); for (auto & path : paths) { checkInterrupt(); - Path lockPath = path + ".lock"; + std::filesystem::path lockPath = path; + lockPath += L".lock"; debug("locking path '%1%'", path); AutoCloseFD fd; diff --git a/src/libutil/current-process.cc b/src/libutil/current-process.cc index bc5700803..d2330339e 100644 --- a/src/libutil/current-process.cc +++ b/src/libutil/current-process.cc @@ -109,7 +109,7 @@ std::optional getSelfExe() { static auto cached = []() -> std::optional { #if defined(__linux__) || defined(__GNU__) - return readLink("/proc/self/exe"); + return readLink(std::filesystem::path{"/proc/self/exe"}); #elif defined(__APPLE__) char buf[1024]; uint32_t size = sizeof(buf); diff --git a/src/libutil/file-system.cc b/src/libutil/file-system.cc index fba92dc8e..758173f41 100644 --- a/src/libutil/file-system.cc +++ b/src/libutil/file-system.cc @@ -101,9 +101,11 @@ Path absPath(PathView path, std::optional dir, bool resolveSymlinks) return canonPath(path, resolveSymlinks); } -std::filesystem::path absPath(const std::filesystem::path & path, bool resolveSymlinks) +std::filesystem::path +absPath(const std::filesystem::path & path, const std::filesystem::path * dir_, bool resolveSymlinks) { - return absPath(path.string(), std::nullopt, resolveSymlinks); + std::optional dir = dir_ ? std::optional{dir_->string()} : std::nullopt; + return absPath(PathView{path.string()}, dir.transform([](auto & p) { return PathView(p); }), resolveSymlinks); } Path canonPath(PathView path, bool resolveSymlinks) @@ -242,10 +244,15 @@ bool pathAccessible(const std::filesystem::path & path) } } -Path readLink(const Path & path) +std::filesystem::path readLink(const std::filesystem::path & path) { checkInterrupt(); - return std::filesystem::read_symlink(path).string(); + return std::filesystem::read_symlink(path); +} + +Path readLink(const Path & path) +{ + return readLink(std::filesystem::path{path}).string(); } std::string readFile(const Path & path) diff --git a/src/libutil/include/nix/util/file-system.hh b/src/libutil/include/nix/util/file-system.hh index 98b992472..a203af2aa 100644 --- a/src/libutil/include/nix/util/file-system.hh +++ b/src/libutil/include/nix/util/file-system.hh @@ -55,7 +55,8 @@ inline Path absPath(const Path & path, std::optional dir = {}, bool re return absPath(PathView{path}, dir, resolveSymlinks); } -std::filesystem::path absPath(const std::filesystem::path & path, bool resolveSymlinks = false); +std::filesystem::path +absPath(const std::filesystem::path & path, const std::filesystem::path * dir = nullptr, bool resolveSymlinks = false); /** * Canonicalise a path by removing all `.` or `..` components and @@ -152,6 +153,12 @@ bool pathAccessible(const std::filesystem::path & path); */ Path readLink(const Path & path); +/** + * Read the contents (target) of a symbolic link. The result is not + * in any way canonicalised. + */ +std::filesystem::path readLink(const std::filesystem::path & path); + /** * Open a `Descriptor` with read-only access to the given directory. */ diff --git a/src/libutil/include/nix/util/util.hh b/src/libutil/include/nix/util/util.hh index 2a379fcd3..83419c8c9 100644 --- a/src/libutil/include/nix/util/util.hh +++ b/src/libutil/include/nix/util/util.hh @@ -58,6 +58,12 @@ Strings quoteStrings(const C & c, char quote = '\'') return res; } +inline Strings quoteFSPaths(const std::set & paths, char quote = '\'') +{ + return paths | std::views::transform([&](const auto & p) { return quoteString(p.string(), quote); }) + | std::ranges::to(); +} + /** * Remove trailing whitespace from a string. * diff --git a/src/nix/nix-collect-garbage/nix-collect-garbage.cc b/src/nix/nix-collect-garbage/nix-collect-garbage.cc index 4d6e60bf3..29ca17a5d 100644 --- a/src/nix/nix-collect-garbage/nix-collect-garbage.cc +++ b/src/nix/nix-collect-garbage/nix-collect-garbage.cc @@ -91,7 +91,7 @@ static int main_nix_collect_garbage(int argc, char ** argv) std::set dirsToClean = { profilesDir(), std::filesystem::path{settings.nixStateDir} / "profiles", - std::filesystem::path{getDefaultProfile()}.parent_path(), + getDefaultProfile().parent_path(), }; for (auto & dir : dirsToClean) removeOldGenerations(dir); diff --git a/src/nix/nix-env/nix-env.cc b/src/nix/nix-env/nix-env.cc index 54443a22c..edfccffa6 100644 --- a/src/nix/nix-env/nix-env.cc +++ b/src/nix/nix-env/nix-env.cc @@ -761,7 +761,7 @@ static void opSet(Globals & globals, Strings opFlags, Strings opArgs) globals.state->store->buildPaths(paths, globals.state->repair ? bmRepair : bmNormal); debug("switching to new user environment"); - Path generation = createGeneration(*store2, globals.profile, drv.queryOutPath()); + auto generation = createGeneration(*store2, globals.profile, drv.queryOutPath()); switchLink(globals.profile, generation); } @@ -1407,14 +1407,15 @@ static int main_nix_env(int argc, char ** argv) globals.instSource.type = srcUnknown; globals.instSource.systemFilter = "*"; - Path nixExprPath = getNixDefExpr(); + std::filesystem::path nixExprPath = getNixDefExpr(); if (!pathExists(nixExprPath)) { try { createDirs(nixExprPath); - replaceSymlink(defaultChannelsDir(), nixExprPath + "/channels"); + replaceSymlink(defaultChannelsDir(), nixExprPath / "channels"); if (!isRootUser()) - replaceSymlink(rootChannelsDir(), nixExprPath + "/channels_root"); + replaceSymlink(rootChannelsDir(), nixExprPath / "channels_root"); + } catch (std::filesystem::filesystem_error &) { } catch (Error &) { } } @@ -1511,7 +1512,8 @@ static int main_nix_env(int argc, char ** argv) globals.state->repair = myArgs.repair; globals.instSource.nixExprPath = std::make_shared( - file != "" ? lookupFileArg(*globals.state, file) : globals.state->rootPath(CanonPath(nixExprPath))); + file != "" ? lookupFileArg(*globals.state, file) + : globals.state->rootPath(CanonPath(nixExprPath.string()))); globals.instSource.autoArgs = myArgs.getAutoArgs(*globals.state); @@ -1519,7 +1521,7 @@ static int main_nix_env(int argc, char ** argv) globals.profile = getEnv("NIX_PROFILE").value_or(""); if (globals.profile == "") - globals.profile = getDefaultProfile(); + globals.profile = getDefaultProfile().string(); op(globals, std::move(opFlags), std::move(opArgs)); diff --git a/src/nix/nix-env/user-env.cc b/src/nix/nix-env/user-env.cc index d09365073..5beed78f7 100644 --- a/src/nix/nix-env/user-env.cc +++ b/src/nix/nix-env/user-env.cc @@ -161,14 +161,14 @@ bool createUserEnv( PathLocks lock; lockProfile(lock, profile); - Path lockTokenCur = optimisticLockProfile(profile); + std::filesystem::path lockTokenCur = optimisticLockProfile(profile); if (lockToken != lockTokenCur) { printInfo("profile '%1%' changed while we were busy; restarting", profile); return false; } debug("switching to new user environment"); - Path generation = createGeneration(*store2, profile, topLevelOut); + std::filesystem::path generation = createGeneration(*store2, profile, topLevelOut); switchLink(profile, generation); } diff --git a/src/nix/profile.cc b/src/nix/profile.cc index 9690eacd8..5b0e033e0 100644 --- a/src/nix/profile.cc +++ b/src/nix/profile.cc @@ -849,7 +849,10 @@ struct CmdProfileDiffClosures : virtual StoreCommand, MixDefaultProfile first = false; logger->cout("Version %d -> %d:", prevGen->number, gen.number); printClosureDiff( - store, store->followLinksToStorePath(prevGen->path), store->followLinksToStorePath(gen.path), " "); + store, + store->followLinksToStorePath(prevGen->path.string()), + store->followLinksToStorePath(gen.path.string()), + " "); } prevGen = gen;