From 67a431fa2f18ea113a6ee9d6d71ea3dc2981327b Mon Sep 17 00:00:00 2001 From: Qyriad Date: Sun, 12 May 2024 21:15:54 -0600 Subject: [PATCH] WIP: automatically migrate client-side profiles back to /nix/var/nix Change-Id: I430dbfabfa091417f0883975a0c1611ab62655d1 --- src/libstore/profiles.cc | 59 +++++++++++++++++++++++++++++++++++++++- src/libstore/profiles.hh | 8 ++++++ 2 files changed, 66 insertions(+), 1 deletion(-) diff --git a/src/libstore/profiles.cc b/src/libstore/profiles.cc index 6746d3067..ac66ecc3c 100644 --- a/src/libstore/profiles.cc +++ b/src/libstore/profiles.cc @@ -336,6 +336,18 @@ Path ensureDefaultProfile() replaceSymlink(defaultProfile, profileLink); } + Path const profileLinkTarget = readLink(profileLink); + std::cerr << "profileLinkTarget: " << profileLinkTarget << "\n"; + // Note: getStateDir() is XDG_STATE_DIR, not NIX_STATE_DIR. + if (profileLinkTarget.starts_with(createNixStateDir())) { + notice("migrating client-side profiles to %s", profilesDir()); + Path const cspDir = createNixStateDir() + "/profiles"; + Path const dspDir = profilesDir(); + migrateProfiles(cspDir, dspDir); + std::cerr << fmt("replaceSymlink(%s, %s)\n", defaultProfile, profileLink); + replaceSymlink(defaultProfile, profileLink); + } + // Backwards compatibiliy measure: Make root's profile available as // `.../default` as it's what NixOS and most of the init scripts expect Path const globalProfileLink = settings.nixStateDir + "/profiles/default"; @@ -343,7 +355,7 @@ Path ensureDefaultProfile() replaceSymlink(defaultProfile, globalProfileLink); } - return absPath(readLink(profileLink), dirOf(profileLink)); + return absPath(profileLinkTarget, dirOf(profileLink)); } Path getDefaultProfile() @@ -363,6 +375,51 @@ Path getDefaultProfile() return profileLink; } +void migrateProfiles(Path const & profilesFrom, Path const & profilesTo) +{ + notice("migrating from profiles from %s to be in %s", profilesFrom, profilesTo); + auto const oldProfiles = readDirectory(profilesFrom); + + for (DirEntry const & oldProfileEnt : oldProfiles) { + auto const oldGenPath = fmt("%s/%s", profilesFrom, oldProfileEnt.name); + if (!isLink(oldGenPath)) { + // This is not a profile and we should not touch it. + continue; + } + + auto const newGenPath = fmt("%s/%s", profilesTo, oldProfileEnt.name); + auto const genTarget = readLink(oldGenPath); + notice("migrating profile '%s' to '%s'", oldGenPath, newGenPath); + std::cerr << fmt("replaceSymlink(%s, %s)", genTarget, newGenPath); + //replaceSymlink(genTarget, newGenPath); + + //deletePath(oldGenPath); + } + + // Now that we've deleted all the profile symlinks in ~/.local/state/nix/profiles, + // it's time to make ~/.local/state/nix/profiles itself a symlink. + if (readDirectory(profilesFrom).empty()) { + removeFile(profilesFrom); + replaceSymlink(profilesTo, profilesFrom); + } else { + // But if that directory stuff other than profiles in it, we shouldn't delete that. + // Back them up for the user. + warn("non-profiles found in '%s'; backing up instead of deleting", profilesFrom); + Path backupName = profilesFrom + ".bak"; + if (pathExists(backupName)) { + unsigned suffix = 0; + do { + assert(suffix < UINT_MAX); + suffix += 1; + backupName = fmt("%s.bak-%u", profilesFrom, suffix); + } while (pathExists(backupName)); + } + + renameFile(profilesFrom, backupName); + notice("backed up as '%s'", backupName); + } +} + Path defaultChannelsDir() { return profilesDir() + "/channels"; diff --git a/src/libstore/profiles.hh b/src/libstore/profiles.hh index c1e0dfbe4..c6ab5fb46 100644 --- a/src/libstore/profiles.hh +++ b/src/libstore/profiles.hh @@ -245,4 +245,12 @@ Path getDefaultProfile(); */ Path ensureDefaultProfile(); +/** + * Migrates a set of profiles from one directory to another. + * + * Intended for migration from ~/.local/state/nix/profiles/ + * to /nix/var/nix/profiles/per-user/foo/ + */ +void migrateProfiles(Path const & profilesFrom, Path const & profilesTo); + }