Skip to content

Commit

Permalink
Move the default profiles to the user’s home
Browse files Browse the repository at this point in the history
Rather than using `/nix/var/nix/{profiles,gcroots}/per-user/`, put the user
profiles and gcroots under `$XDG_DATA_DIR/nix/{profiles,gcroots}`.

This means that the daemon no longer needs to manage these paths itself
(they are fully handled client-side). In particular, it doesn’t have to
`chown` them anymore (removing one need for root).

This does change the layout of the gc-roots created by nix-env, and is
likely to break some stuff, so I’m not sure how to properly handle that.
  • Loading branch information
Théophane Hufschmitt committed Jan 17, 2023
1 parent deb35c8 commit a5919f4
Show file tree
Hide file tree
Showing 11 changed files with 42 additions and 41 deletions.
2 changes: 1 addition & 1 deletion src/libstore/build/local-derivation-goal.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1517,7 +1517,7 @@ void LocalDerivationGoal::startDaemon()
try {
daemon::processConnection(store, from, to,
daemon::NotTrusted, daemon::Recursive,
[&](Store & store) { store.createUser("nobody", 65535); });
[&](Store & store) {});
debug("terminated daemon connection");
} catch (SysError &) {
ignoreException();
Expand Down
16 changes: 0 additions & 16 deletions src/libstore/local-store.cc
Original file line number Diff line number Diff line change
Expand Up @@ -201,8 +201,6 @@ LocalStore::LocalStore(const Params & params)
throw SysError("could not set permissions on '%s' to 755", perUserDir);
}

createUser(getUserName(), getuid());

/* Optionally, create directories and set permissions for a
multi-user install. */
if (getuid() == 0 && settings.buildUsersGroup != "") {
Expand Down Expand Up @@ -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_(
LocalStore::State & state,
const DrvOutput & id)
Expand Down
2 changes: 0 additions & 2 deletions src/libstore/local-store.hh
Original file line number Diff line number Diff line change
Expand Up @@ -281,8 +281,6 @@ private:
void signPathInfo(ValidPathInfo & info);
void signRealisation(Realisation &);

void createUser(const std::string & userName, uid_t userId) override;

// XXX: Make a generic `Store` method
FixedOutputHash hashCAPath(
const FileIngestionMethod & method,
Expand Down
26 changes: 20 additions & 6 deletions src/libstore/profiles.cc
Original file line number Diff line number Diff line change
Expand Up @@ -280,16 +280,30 @@ std::string optimisticLockProfile(const Path & profile)
}


Path profilesDir()
{
auto profileRoot = getDataDir() + "/nix/profiles";
createDirs(profileRoot);
return profileRoot;
}


Path getDefaultProfile()
{
Path profileLink = getHome() + "/.nix-profile";
try {
if (!pathExists(profileLink)) {
replaceSymlink(
getuid() == 0
? settings.nixStateDir + "/profiles/default"
: fmt("%s/profiles/per-user/%s/profile", settings.nixStateDir, getUserName()),
profileLink);
// Migrate from the “old-style” profiles stored under `/nix/var`:
// If the link exists and points to the old location, rewrite it to the
// new one (otherwise keep-it as-it-is as it might have been
// intentionnally changed, in which case we shouldn’t touch it)
auto legacyProfile = getuid() == 0
? settings.nixStateDir + "/profiles/default"
: fmt("%s/profiles/per-user/%s/profile", settings.nixStateDir, getUserName());
if (!pathExists(profileLink) ||
(isLink(profileLink) &&
readLink(profileLink) == legacyProfile)
) {
replaceSymlink(profilesDir() + "/profile", profileLink);
}
return absPath(readLink(profileLink), dirOf(profileLink));
} catch (Error &) {
Expand Down
4 changes: 4 additions & 0 deletions src/libstore/profiles.hh
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,10 @@ void lockProfile(PathLocks & lock, const Path & profile);
rebuilt. */
std::string optimisticLockProfile(const Path & profile);

/* Creates and returns the path to a directory suitable for storing the user’s
profiles. */
Path profilesDir();

/* Resolve ~/.nix-profile. If ~/.nix-profile doesn't exist yet, create
it. */
Path getDefaultProfile();
Expand Down
3 changes: 0 additions & 3 deletions src/libstore/store-api.hh
Original file line number Diff line number Diff line change
Expand Up @@ -657,9 +657,6 @@ public:
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
* (a no-op when there’s no daemon)
Expand Down
18 changes: 11 additions & 7 deletions src/libutil/util.cc
Original file line number Diff line number Diff line change
Expand Up @@ -537,6 +537,16 @@ std::string getUserName()
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()
{
Expand All @@ -558,13 +568,7 @@ Path getHome()
}
}
if (!homeDir) {
std::vector<char> buf(16384);
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;
homeDir = getHomeOf(geteuid());
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);
}
Expand Down
3 changes: 3 additions & 0 deletions src/libutil/util.hh
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,9 @@ void deletePath(const Path & path, uint64_t & bytesFreed);

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. */
Path getHome();

Expand Down
4 changes: 3 additions & 1 deletion src/nix-channel/nix-channel.cc
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
#include "profiles.hh"
#include "shared.hh"
#include "globals.hh"
#include "filetransfer.hh"
#include "store-api.hh"
#include "legacy.hh"
#include "fetchers.hh"
#include "util.hh"

#include <fcntl.h>
#include <regex>
Expand Down Expand Up @@ -166,7 +168,7 @@ static int main_nix_channel(int argc, char ** argv)
nixDefExpr = home + "/.nix-defexpr";

// Figure out the name of the channels profile.
profile = fmt("%s/profiles/per-user/%s/channels", settings.nixStateDir, getUserName());
profile = profilesDir() + "/channels";

enum {
cNone,
Expand Down
1 change: 0 additions & 1 deletion src/nix/daemon.cc
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,6 @@ static void daemonLoop()
querySetting("build-users-group", "") == "")
throw Error("if you run 'nix-daemon' as root, then you MUST set 'build-users-group'!");
#endif
store.createUser(user, peer.uid);
});

exit(0);
Expand Down
4 changes: 0 additions & 4 deletions tests/remote-store.sh
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,3 @@ NIX_REMOTE= nix-store --dump-db > $TEST_ROOT/d2
cmp $TEST_ROOT/d1 $TEST_ROOT/d2

killDaemon

user=$(whoami)
[ -e $NIX_STATE_DIR/gcroots/per-user/$user ]
[ -e $NIX_STATE_DIR/profiles/per-user/$user ]

0 comments on commit a5919f4

Please sign in to comment.