nix develop: Support chroot stores

Fixes #5024.
This commit is contained in:
Eelco Dolstra 2021-07-27 14:23:24 +02:00
parent c000cec27f
commit ed5ad59dc1
3 changed files with 46 additions and 41 deletions

View file

@ -7,6 +7,7 @@
#include "derivations.hh"
#include "affinity.hh"
#include "progress-bar.hh"
#include "run.hh"
#include <nlohmann/json.hpp>
@ -472,8 +473,6 @@ struct CmdDevelop : Common, MixEnvironment
writeFull(rcFileFd.get(), script);
stopProgressBar();
setEnviron();
// prevent garbage collection until shell exits
setenv("NIX_GCROOT", gcroot.data(), 1);
@ -506,11 +505,7 @@ struct CmdDevelop : Common, MixEnvironment
auto args = phase || !command.empty() ? Strings{std::string(baseNameOf(shell)), rcFilePath}
: Strings{std::string(baseNameOf(shell)), "--rcfile", rcFilePath};
restoreProcessContext();
execvp(shell.c_str(), stringsToCharPtrs(args).data());
throw SysError("executing shell '%s'", shell);
runProgramInStore(store, shell, args);
}
};

View file

@ -1,3 +1,4 @@
#include "run.hh"
#include "command.hh"
#include "common-args.hh"
#include "shared.hh"
@ -20,45 +21,43 @@ using namespace nix;
std::string chrootHelperName = "__run_in_chroot";
struct RunCommon : virtual Command
namespace nix {
void runProgramInStore(ref<Store> store,
const std::string & program,
const Strings & args)
{
stopProgressBar();
using Command::run;
restoreProcessContext();
void runProgram(ref<Store> store,
const std::string & program,
const Strings & args)
{
stopProgressBar();
/* If this is a diverted store (i.e. its "logical" location
(typically /nix/store) differs from its "physical" location
(e.g. /home/eelco/nix/store), then run the command in a
chroot. For non-root users, this requires running it in new
mount and user namespaces. Unfortunately,
unshare(CLONE_NEWUSER) doesn't work in a multithreaded program
(which "nix" is), so we exec() a single-threaded helper program
(chrootHelper() below) to do the work. */
auto store2 = store.dynamic_pointer_cast<LocalStore>();
restoreProcessContext();
if (store2 && store->storeDir != store2->getRealStoreDir()) {
Strings helperArgs = { chrootHelperName, store->storeDir, store2->getRealStoreDir(), program };
for (auto & arg : args) helperArgs.push_back(arg);
/* If this is a diverted store (i.e. its "logical" location
(typically /nix/store) differs from its "physical" location
(e.g. /home/eelco/nix/store), then run the command in a
chroot. For non-root users, this requires running it in new
mount and user namespaces. Unfortunately,
unshare(CLONE_NEWUSER) doesn't work in a multithreaded
program (which "nix" is), so we exec() a single-threaded
helper program (chrootHelper() below) to do the work. */
auto store2 = store.dynamic_pointer_cast<LocalStore>();
execv(readLink("/proc/self/exe").c_str(), stringsToCharPtrs(helperArgs).data());
if (store2 && store->storeDir != store2->getRealStoreDir()) {
Strings helperArgs = { chrootHelperName, store->storeDir, store2->getRealStoreDir(), program };
for (auto & arg : args) helperArgs.push_back(arg);
execv(readLink("/proc/self/exe").c_str(), stringsToCharPtrs(helperArgs).data());
throw SysError("could not execute chroot helper");
}
execvp(program.c_str(), stringsToCharPtrs(args).data());
throw SysError("unable to execute '%s'", program);
throw SysError("could not execute chroot helper");
}
};
struct CmdShell : InstallablesCommand, RunCommon, MixEnvironment
execvp(program.c_str(), stringsToCharPtrs(args).data());
throw SysError("unable to execute '%s'", program);
}
}
struct CmdShell : InstallablesCommand, MixEnvironment
{
using InstallablesCommand::run;
@ -125,13 +124,13 @@ struct CmdShell : InstallablesCommand, RunCommon, MixEnvironment
Strings args;
for (auto & arg : command) args.push_back(arg);
runProgram(store, *command.begin(), args);
runProgramInStore(store, *command.begin(), args);
}
};
static auto rCmdShell = registerCommand<CmdShell>("shell");
struct CmdRun : InstallableCommand, RunCommon
struct CmdRun : InstallableCommand
{
using InstallableCommand::run;
@ -183,7 +182,7 @@ struct CmdRun : InstallableCommand, RunCommon
Strings allArgs{app.program};
for (auto & i : args) allArgs.push_back(i);
runProgram(store, app.program, allArgs);
runProgramInStore(store, app.program, allArgs);
}
};

11
src/nix/run.hh Normal file
View file

@ -0,0 +1,11 @@
#pragma once
#include "store-api.hh"
namespace nix {
void runProgramInStore(ref<Store> store,
const std::string & program,
const Strings & args);
}