Merge pull request #5226 from NixOS/client-side-profiles

Move the default profiles to the user’s home
This commit is contained in:
Eelco Dolstra 2023-01-30 12:21:47 +01:00 committed by GitHub
commit c79b1582a7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 72 additions and 41 deletions

View file

@ -1517,7 +1517,7 @@ void LocalDerivationGoal::startDaemon()
try { try {
daemon::processConnection(store, from, to, daemon::processConnection(store, from, to,
daemon::NotTrusted, daemon::Recursive, daemon::NotTrusted, daemon::Recursive,
[&](Store & store) { store.createUser("nobody", 65535); }); [&](Store & store) {});
debug("terminated daemon connection"); debug("terminated daemon connection");
} catch (SysError &) { } catch (SysError &) {
ignoreException(); ignoreException();

View file

@ -201,8 +201,6 @@ LocalStore::LocalStore(const Params & params)
throw SysError("could not set permissions on '%s' to 755", perUserDir); throw SysError("could not set permissions on '%s' to 755", perUserDir);
} }
createUser(getUserName(), getuid());
/* Optionally, create directories and set permissions for a /* Optionally, create directories and set permissions for a
multi-user install. */ multi-user install. */
if (getuid() == 0 && settings.buildUsersGroup != "") { if (getuid() == 0 && settings.buildUsersGroup != "") {
@ -1824,20 +1822,6 @@ void LocalStore::signPathInfo(ValidPathInfo & info)
} }
void LocalStore::createUser(const std::string & userName, uid_t userId)
{
for (auto & dir : {
fmt("%s/profiles/per-user/%s", stateDir, userName),
fmt("%s/gcroots/per-user/%s", stateDir, userName)
}) {
createDirs(dir);
if (chmod(dir.c_str(), 0755) == -1)
throw SysError("changing permissions of directory '%s'", dir);
if (chown(dir.c_str(), userId, getgid()) == -1)
throw SysError("changing owner of directory '%s'", dir);
}
}
std::optional<std::pair<int64_t, Realisation>> LocalStore::queryRealisationCore_( std::optional<std::pair<int64_t, Realisation>> LocalStore::queryRealisationCore_(
LocalStore::State & state, LocalStore::State & state,
const DrvOutput & id) const DrvOutput & id)

View file

@ -281,8 +281,6 @@ private:
void signPathInfo(ValidPathInfo & info); void signPathInfo(ValidPathInfo & info);
void signRealisation(Realisation &); void signRealisation(Realisation &);
void createUser(const std::string & userName, uid_t userId) override;
// XXX: Make a generic `Store` method // XXX: Make a generic `Store` method
FixedOutputHash hashCAPath( FixedOutputHash hashCAPath(
const FileIngestionMethod & method, const FileIngestionMethod & method,

View file

@ -280,16 +280,24 @@ std::string optimisticLockProfile(const Path & profile)
} }
Path profilesDir()
{
auto profileRoot = getDataDir() + "/nix/profiles";
createDirs(profileRoot);
return profileRoot;
}
Path getDefaultProfile() Path getDefaultProfile()
{ {
Path profileLink = getHome() + "/.nix-profile"; Path profileLink = getHome() + "/.nix-profile";
try { try {
auto profile =
getuid() == 0
? settings.nixStateDir + "/profiles/default"
: profilesDir() + "/profile";
if (!pathExists(profileLink)) { if (!pathExists(profileLink)) {
replaceSymlink( replaceSymlink(profile, profileLink);
getuid() == 0
? settings.nixStateDir + "/profiles/default"
: fmt("%s/profiles/per-user/%s/profile", settings.nixStateDir, getUserName()),
profileLink);
} }
return absPath(readLink(profileLink), dirOf(profileLink)); return absPath(readLink(profileLink), dirOf(profileLink));
} catch (Error &) { } catch (Error &) {

View file

@ -68,6 +68,10 @@ void lockProfile(PathLocks & lock, const Path & profile);
rebuilt. */ rebuilt. */
std::string optimisticLockProfile(const Path & profile); std::string optimisticLockProfile(const Path & profile);
/* Creates and returns the path to a directory suitable for storing the users
profiles. */
Path profilesDir();
/* Resolve ~/.nix-profile. If ~/.nix-profile doesn't exist yet, create /* Resolve ~/.nix-profile. If ~/.nix-profile doesn't exist yet, create
it. */ it. */
Path getDefaultProfile(); Path getDefaultProfile();

View file

@ -657,9 +657,6 @@ public:
return toRealPath(printStorePath(storePath)); return toRealPath(printStorePath(storePath));
} }
virtual void createUser(const std::string & userName, uid_t userId)
{ }
/* /*
* Synchronises the options of the client with those of the daemon * Synchronises the options of the client with those of the daemon
* (a no-op when theres no daemon) * (a no-op when theres no daemon)

View file

@ -537,6 +537,16 @@ std::string getUserName()
return name; return name;
} }
Path getHomeOf(uid_t userId)
{
std::vector<char> buf(16384);
struct passwd pwbuf;
struct passwd * pw;
if (getpwuid_r(userId, &pwbuf, buf.data(), buf.size(), &pw) != 0
|| !pw || !pw->pw_dir || !pw->pw_dir[0])
throw Error("cannot determine user's home directory");
return pw->pw_dir;
}
Path getHome() Path getHome()
{ {
@ -558,13 +568,7 @@ Path getHome()
} }
} }
if (!homeDir) { if (!homeDir) {
std::vector<char> buf(16384); homeDir = getHomeOf(geteuid());
struct passwd pwbuf;
struct passwd * pw;
if (getpwuid_r(geteuid(), &pwbuf, buf.data(), buf.size(), &pw) != 0
|| !pw || !pw->pw_dir || !pw->pw_dir[0])
throw Error("cannot determine user's home directory");
homeDir = pw->pw_dir;
if (unownedUserHomeDir.has_value() && unownedUserHomeDir != homeDir) { if (unownedUserHomeDir.has_value() && unownedUserHomeDir != homeDir) {
warn("$HOME ('%s') is not owned by you, falling back to the one defined in the 'passwd' file ('%s')", *unownedUserHomeDir, *homeDir); warn("$HOME ('%s') is not owned by you, falling back to the one defined in the 'passwd' file ('%s')", *unownedUserHomeDir, *homeDir);
} }

View file

@ -137,6 +137,9 @@ void deletePath(const Path & path, uint64_t & bytesFreed);
std::string getUserName(); std::string getUserName();
/* Return the given user's home directory from /etc/passwd. */
Path getHomeOf(uid_t userId);
/* Return $HOME or the user's home directory from /etc/passwd. */ /* Return $HOME or the user's home directory from /etc/passwd. */
Path getHome(); Path getHome();

View file

@ -1,9 +1,11 @@
#include "profiles.hh"
#include "shared.hh" #include "shared.hh"
#include "globals.hh" #include "globals.hh"
#include "filetransfer.hh" #include "filetransfer.hh"
#include "store-api.hh" #include "store-api.hh"
#include "legacy.hh" #include "legacy.hh"
#include "fetchers.hh" #include "fetchers.hh"
#include "util.hh"
#include <fcntl.h> #include <fcntl.h>
#include <regex> #include <regex>
@ -166,7 +168,7 @@ static int main_nix_channel(int argc, char ** argv)
nixDefExpr = home + "/.nix-defexpr"; nixDefExpr = home + "/.nix-defexpr";
// Figure out the name of the channels profile. // Figure out the name of the channels profile.
profile = fmt("%s/profiles/per-user/%s/channels", settings.nixStateDir, getUserName()); profile = profilesDir() + "/channels";
enum { enum {
cNone, cNone,

View file

@ -248,7 +248,6 @@ static void daemonLoop()
querySetting("build-users-group", "") == "") querySetting("build-users-group", "") == "")
throw Error("if you run 'nix-daemon' as root, then you MUST set 'build-users-group'!"); throw Error("if you run 'nix-daemon' as root, then you MUST set 'build-users-group'!");
#endif #endif
store.createUser(user, peer.uid);
}); });
exit(0); exit(0);

View file

@ -62,7 +62,7 @@ readLink() {
} }
clearProfiles() { clearProfiles() {
profiles="$NIX_STATE_DIR"/profiles profiles="$HOME"/.local/share/nix/profiles
rm -rf $profiles rm -rf $profiles
} }

View file

@ -17,6 +17,7 @@ nix_tests = \
fetchMercurial.sh \ fetchMercurial.sh \
gc-auto.sh \ gc-auto.sh \
user-envs.sh \ user-envs.sh \
user-envs-migration.sh \
binary-cache.sh \ binary-cache.sh \
multiple-outputs.sh \ multiple-outputs.sh \
ca/build.sh \ ca/build.sh \

View file

@ -30,7 +30,3 @@ NIX_REMOTE= nix-store --dump-db > $TEST_ROOT/d2
cmp $TEST_ROOT/d1 $TEST_ROOT/d2 cmp $TEST_ROOT/d1 $TEST_ROOT/d2
killDaemon killDaemon
user=$(whoami)
[ -e $NIX_STATE_DIR/gcroots/per-user/$user ]
[ -e $NIX_STATE_DIR/profiles/per-user/$user ]

View file

@ -0,0 +1,35 @@
# Test that the migration of user environments
# (https://github.com/NixOS/nix/pull/5226) does preserve everything
source common.sh
if isDaemonNewer "2.4pre20211005"; then
exit 99
fi
killDaemon
unset NIX_REMOTE
clearStore
clearProfiles
rm -rf ~/.nix-profile
# Fill the environment using the older Nix
PATH_WITH_NEW_NIX="$PATH"
export PATH="$NIX_DAEMON_PACKAGE/bin:$PATH"
nix-env -f user-envs.nix -i foo-1.0
nix-env -f user-envs.nix -i bar-0.1
# Migrate to the new profile dir, and ensure that everythings there
export PATH="$PATH_WITH_NEW_NIX"
nix-env -q # Trigger the migration
( [[ -L ~/.nix-profile ]] && \
[[ $(readlink ~/.nix-profile) == ~/.local/share/nix/profiles/profile ]] ) || \
fail "The nix profile should point to the new location"
(nix-env -q | grep foo && nix-env -q | grep bar && \
[[ -e ~/.nix-profile/bin/foo ]] && \
[[ $(nix-env --list-generations | wc -l) == 2 ]]) ||
fail "The nix profile should have the same content as before the migration"