1
1
Fork 0
mirror of https://github.com/NixOS/nix.git synced 2025-12-02 07:00:59 +01:00

Merge remote-tracking branch 'origin/2.29-maintenance' into detsys-main

This commit is contained in:
Eelco Dolstra 2025-05-16 12:48:44 +02:00
commit c20642ac7b
354 changed files with 6768 additions and 3808 deletions

View file

@ -24,13 +24,21 @@
namespace nix {
BinaryCacheStore::BinaryCacheStore(const Params & params)
: BinaryCacheStoreConfig(params)
, Store(params)
BinaryCacheStore::BinaryCacheStore(Config & config)
: config{config}
{
if (secretKeyFile != "")
signer = std::make_unique<LocalSigner>(
SecretKey { readFile(secretKeyFile) });
if (config.secretKeyFile != "")
signers.push_back(std::make_unique<LocalSigner>(
SecretKey { readFile(config.secretKeyFile) }));
if (config.secretKeyFiles != "") {
std::stringstream ss(config.secretKeyFiles);
Path keyPath;
while (std::getline(ss, keyPath, ',')) {
signers.push_back(std::make_unique<LocalSigner>(
SecretKey { readFile(keyPath) }));
}
}
StringSink sink;
sink << narVersionMagic1;
@ -53,9 +61,9 @@ void BinaryCacheStore::init()
throw Error("binary cache '%s' is for Nix stores with prefix '%s', not '%s'",
getUri(), value, storeDir);
} else if (name == "WantMassQuery") {
wantMassQuery.setDefault(value == "1");
config.wantMassQuery.setDefault(value == "1");
} else if (name == "Priority") {
priority.setDefault(std::stoi(value));
config.priority.setDefault(std::stoi(value));
}
}
}
@ -147,7 +155,11 @@ ref<const ValidPathInfo> BinaryCacheStore::addToStoreCommon(
{
FdSink fileSink(fdTemp.get());
TeeSink teeSinkCompressed { fileSink, fileHashSink };
auto compressionSink = makeCompressionSink(compression, teeSinkCompressed, parallelCompression, compressionLevel);
auto compressionSink = makeCompressionSink(
config.compression,
teeSinkCompressed,
config.parallelCompression,
config.compressionLevel);
TeeSink teeSinkUncompressed { *compressionSink, narHashSink };
TeeSource teeSource { narSource, teeSinkUncompressed };
narAccessor = makeNarAccessor(teeSource);
@ -159,17 +171,17 @@ ref<const ValidPathInfo> BinaryCacheStore::addToStoreCommon(
auto info = mkInfo(narHashSink.finish());
auto narInfo = make_ref<NarInfo>(info);
narInfo->compression = compression;
narInfo->compression = config.compression;
auto [fileHash, fileSize] = fileHashSink.finish();
narInfo->fileHash = fileHash;
narInfo->fileSize = fileSize;
narInfo->url = "nar/" + narInfo->fileHash->to_string(HashFormat::Nix32, false) + ".nar"
+ (compression == "xz" ? ".xz" :
compression == "bzip2" ? ".bz2" :
compression == "zstd" ? ".zst" :
compression == "lzip" ? ".lzip" :
compression == "lz4" ? ".lz4" :
compression == "br" ? ".br" :
+ (config.compression == "xz" ? ".xz" :
config.compression == "bzip2" ? ".bz2" :
config.compression == "zstd" ? ".zst" :
config.compression == "lzip" ? ".lzip" :
config.compression == "lz4" ? ".lz4" :
config.compression == "br" ? ".br" :
"");
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(now2 - now1).count();
@ -191,7 +203,7 @@ ref<const ValidPathInfo> BinaryCacheStore::addToStoreCommon(
/* Optionally write a JSON file containing a listing of the
contents of the NAR. */
if (writeNARListing) {
if (config.writeNARListing) {
nlohmann::json j = {
{"version", 1},
{"root", listNar(ref<SourceAccessor>(narAccessor), CanonPath::root, true)},
@ -203,7 +215,7 @@ ref<const ValidPathInfo> BinaryCacheStore::addToStoreCommon(
/* Optionally maintain an index of DWARF debug info files
consisting of JSON files named 'debuginfo/<build-id>' that
specify the NAR file and member containing the debug info. */
if (writeDebugInfo) {
if (config.writeDebugInfo) {
CanonPath buildIdDir("lib/debug/.build-id");
@ -270,9 +282,9 @@ ref<const ValidPathInfo> BinaryCacheStore::addToStoreCommon(
stats.narWriteCompressedBytes += fileSize;
stats.narWriteCompressionTimeMs += duration;
/* Atomically write the NAR info file.*/
if (signer) narInfo->sign(*this, *signer);
narInfo->sign(*this, signers);
/* Atomically write the NAR info file.*/
writeNarInfo(narInfo);
stats.narInfoWrite++;
@ -515,7 +527,7 @@ void BinaryCacheStore::registerDrvOutput(const Realisation& info) {
ref<SourceAccessor> BinaryCacheStore::getFSAccessor(bool requireValidPath)
{
return make_ref<RemoteFSAccessor>(ref<Store>(shared_from_this()), requireValidPath, localNarCache);
return make_ref<RemoteFSAccessor>(ref<Store>(shared_from_this()), requireValidPath, config.localNarCache);
}
void BinaryCacheStore::addSignatures(const StorePath & storePath, const StringSet & sigs)

File diff suppressed because it is too large Load diff

View file

@ -3,6 +3,7 @@
#include "nix/store/build/worker.hh"
#include "nix/store/build/substitution-goal.hh"
#include "nix/util/callback.hh"
#include "nix/store/store-open.hh"
namespace nix {
@ -11,7 +12,7 @@ DrvOutputSubstitutionGoal::DrvOutputSubstitutionGoal(
Worker & worker,
RepairFlag repair,
std::optional<ContentAddress> ca)
: Goal(worker, DerivedPath::Opaque { StorePath::dummy })
: Goal(worker)
, id(id)
{
name = fmt("substitution of '%s'", id.to_string());
@ -87,6 +88,8 @@ Goal::Co DrvOutputSubstitutionGoal::init()
bool failed = false;
Goals waitees;
for (const auto & [depId, depPath] : outputInfo->dependentRealisations) {
if (depId != id) {
if (auto localOutputInfo = worker.store.queryRealisation(depId);
@ -103,13 +106,13 @@ Goal::Co DrvOutputSubstitutionGoal::init()
failed = true;
break;
}
addWaitee(worker.makeDrvOutputSubstitutionGoal(depId));
waitees.insert(worker.makeDrvOutputSubstitutionGoal(depId));
}
}
if (failed) continue;
co_return realisationFetched(outputInfo, sub);
co_return realisationFetched(std::move(waitees), outputInfo, sub);
}
/* None left. Terminate this goal and let someone else deal
@ -127,10 +130,10 @@ Goal::Co DrvOutputSubstitutionGoal::init()
co_return amDone(substituterFailed ? ecFailed : ecNoSubstituters);
}
Goal::Co DrvOutputSubstitutionGoal::realisationFetched(std::shared_ptr<const Realisation> outputInfo, nix::ref<nix::Store> sub) {
addWaitee(worker.makePathSubstitutionGoal(outputInfo->outPath));
Goal::Co DrvOutputSubstitutionGoal::realisationFetched(Goals waitees, std::shared_ptr<const Realisation> outputInfo, nix::ref<nix::Store> sub) {
waitees.insert(worker.makePathSubstitutionGoal(outputInfo->outPath));
if (!waitees.empty()) co_await Suspend{};
co_await await(std::move(waitees));
trace("output path substituted");

View file

@ -132,38 +132,18 @@ void addToWeakGoals(WeakGoals & goals, GoalPtr p)
goals.insert(p);
}
void Goal::addWaitee(GoalPtr waitee)
Co Goal::await(Goals new_waitees)
{
waitees.insert(waitee);
addToWeakGoals(waitee->waiters, shared_from_this());
}
void Goal::waiteeDone(GoalPtr waitee, ExitCode result)
{
assert(waitees.count(waitee));
waitees.erase(waitee);
trace(fmt("waitee '%s' done; %d left", waitee->name, waitees.size()));
if (result == ecFailed || result == ecNoSubstituters || result == ecIncompleteClosure) ++nrFailed;
if (result == ecNoSubstituters) ++nrNoSubstituters;
if (result == ecIncompleteClosure) ++nrIncompleteClosure;
if (waitees.empty() || (result == ecFailed && !settings.keepGoing)) {
/* If we failed and keepGoing is not set, we remove all
remaining waitees. */
for (auto & goal : waitees) {
goal->waiters.extract(shared_from_this());
assert(waitees.empty());
if (!new_waitees.empty()) {
waitees = std::move(new_waitees);
for (auto waitee : waitees) {
addToWeakGoals(waitee->waiters, shared_from_this());
}
waitees.clear();
worker.wakeUp(shared_from_this());
co_await Suspend{};
assert(waitees.empty());
}
co_return Return{};
}
Goal::Done Goal::amDone(ExitCode result, std::optional<Error> ex)
@ -183,7 +163,32 @@ Goal::Done Goal::amDone(ExitCode result, std::optional<Error> ex)
for (auto & i : waiters) {
GoalPtr goal = i.lock();
if (goal) goal->waiteeDone(shared_from_this(), result);
if (goal) {
auto me = shared_from_this();
assert(goal->waitees.count(me));
goal->waitees.erase(me);
goal->trace(fmt("waitee '%s' done; %d left", name, goal->waitees.size()));
if (result == ecFailed || result == ecNoSubstituters || result == ecIncompleteClosure) ++goal->nrFailed;
if (result == ecNoSubstituters) ++goal->nrNoSubstituters;
if (result == ecIncompleteClosure) ++goal->nrIncompleteClosure;
if (goal->waitees.empty()) {
worker.wakeUp(goal);
} else if (result == ecFailed && !settings.keepGoing) {
/* If we failed and keepGoing is not set, we remove all
remaining waitees. */
for (auto & g : goal->waitees) {
g->waiters.extract(goal);
}
goal->waitees.clear();
worker.wakeUp(goal);
}
}
}
waiters.clear();
worker.removeGoal(shared_from_this());
@ -215,5 +220,22 @@ void Goal::work()
assert(top_co || exitCode != ecBusy);
}
Goal::Co Goal::yield() {
worker.wakeUp(shared_from_this());
co_await Suspend{};
co_return Return{};
}
Goal::Co Goal::waitForAWhile() {
worker.waitForAWhile(shared_from_this());
co_await Suspend{};
co_return Return{};
}
Goal::Co Goal::waitForBuildSlot() {
worker.waitForBuildSlot(shared_from_this());
co_await Suspend{};
co_return Return{};
}
}

View file

@ -1,4 +1,5 @@
#include "nix/store/build/worker.hh"
#include "nix/store/store-open.hh"
#include "nix/store/build/substitution-goal.hh"
#include "nix/store/nar-info.hh"
#include "nix/util/finally.hh"
@ -11,7 +12,7 @@
namespace nix {
PathSubstitutionGoal::PathSubstitutionGoal(const StorePath & storePath, Worker & worker, RepairFlag repair, std::optional<ContentAddress> ca)
: Goal(worker, DerivedPath::Opaque { storePath })
: Goal(worker)
, storePath(storePath)
, repair(repair)
, ca(ca)
@ -133,20 +134,22 @@ Goal::Co PathSubstitutionGoal::init()
/* Bail out early if this substituter lacks a valid
signature. LocalStore::addToStore() also checks for this, but
only after we've downloaded the path. */
if (!sub->isTrusted && worker.store.pathInfoIsUntrusted(*info))
if (!sub->config.isTrusted && worker.store.pathInfoIsUntrusted(*info))
{
warn("ignoring substitute for '%s' from '%s', as it's not signed by any of the keys in 'trusted-public-keys'",
worker.store.printStorePath(storePath), sub->getUri());
continue;
}
Goals waitees;
/* To maintain the closure invariant, we first have to realise the
paths referenced by this one. */
for (auto & i : info->references)
if (i != storePath) /* ignore self-references */
addWaitee(worker.makePathSubstitutionGoal(i));
waitees.insert(worker.makePathSubstitutionGoal(i));
if (!waitees.empty()) co_await Suspend{};
co_await await(std::move(waitees));
// FIXME: consider returning boolean instead of passing in reference
bool out = false; // is mutated by tryToRun
@ -184,11 +187,15 @@ Goal::Co PathSubstitutionGoal::tryToRun(StorePath subPath, nix::ref<Store> sub,
}
for (auto & i : info->references)
if (i != storePath) /* ignore self-references */
assert(worker.store.isValidPath(i));
/* ignore self-references */
if (i != storePath) {
if (!worker.store.isValidPath(i)) {
throw Error("reference '%s' of path '%s' is not a valid path",
worker.store.printStorePath(i), worker.store.printStorePath(storePath));
}
}
worker.wakeUp(shared_from_this());
co_await Suspend{};
co_await yield();
trace("trying to run");
@ -196,8 +203,7 @@ Goal::Co PathSubstitutionGoal::tryToRun(StorePath subPath, nix::ref<Store> sub,
if maxSubstitutionJobs == 0, we still allow a substituter to run. This
prevents infinite waiting. */
while (worker.getNrSubstitutions() >= std::max(1U, (unsigned int) settings.maxSubstitutionJobs)) {
worker.waitForBuildSlot(shared_from_this());
co_await Suspend{};
co_await waitForBuildSlot();
}
auto maintainRunningSubstitutions = std::make_unique<MaintainCount<uint64_t>>(worker.runningSubstitutions);
@ -222,7 +228,7 @@ Goal::Co PathSubstitutionGoal::tryToRun(StorePath subPath, nix::ref<Store> sub,
PushActivity pact(act.id);
copyStorePath(*sub, worker.store,
subPath, repair, sub->isTrusted ? NoCheckSigs : CheckSigs);
subPath, repair, sub->config.isTrusted ? NoCheckSigs : CheckSigs);
promise.set_value();
} catch (...) {

View file

@ -5,7 +5,6 @@
#include "nix/store/build/drv-output-substitution-goal.hh"
#include "nix/store/build/derivation-goal.hh"
#ifndef _WIN32 // TODO Enable building on Windows
# include "nix/store/build/local-derivation-goal.hh"
# include "nix/store/build/hook-instance.hh"
#endif
#include "nix/util/signals.hh"
@ -65,13 +64,7 @@ std::shared_ptr<DerivationGoal> Worker::makeDerivationGoal(const StorePath & drv
const OutputsSpec & wantedOutputs, BuildMode buildMode)
{
return makeDerivationGoalCommon(drvPath, wantedOutputs, [&]() -> std::shared_ptr<DerivationGoal> {
return
#ifndef _WIN32 // TODO Enable building on Windows
dynamic_cast<LocalStore *>(&store)
? std::make_shared<LocalDerivationGoal>(drvPath, wantedOutputs, *this, buildMode)
:
#endif
std::make_shared</* */DerivationGoal>(drvPath, wantedOutputs, *this, buildMode);
return std::make_shared<DerivationGoal>(drvPath, wantedOutputs, *this, buildMode);
});
}
@ -79,13 +72,7 @@ std::shared_ptr<DerivationGoal> Worker::makeBasicDerivationGoal(const StorePath
const BasicDerivation & drv, const OutputsSpec & wantedOutputs, BuildMode buildMode)
{
return makeDerivationGoalCommon(drvPath, wantedOutputs, [&]() -> std::shared_ptr<DerivationGoal> {
return
#ifndef _WIN32 // TODO Enable building on Windows
dynamic_cast<LocalStore *>(&store)
? std::make_shared<LocalDerivationGoal>(drvPath, drv, wantedOutputs, *this, buildMode)
:
#endif
std::make_shared</* */DerivationGoal>(drvPath, drv, wantedOutputs, *this, buildMode);
return std::make_shared<DerivationGoal>(drvPath, drv, wantedOutputs, *this, buildMode);
});
}
@ -524,7 +511,7 @@ bool Worker::pathContentsGood(const StorePath & path)
res = false;
else {
auto current = hashPath(
{store.getFSAccessor(), CanonPath(store.printStorePath(path))},
{store.getFSAccessor(), CanonPath(path.to_string())},
FileIngestionMethod::NixArchive, info->narHash.algo).first;
Hash nullHash(HashAlgorithm::SHA256);
res = info->narHash == nullHash || info->narHash == current;
@ -552,4 +539,9 @@ GoalPtr upcast_goal(std::shared_ptr<DrvOutputSubstitutionGoal> subGoal)
return subGoal;
}
GoalPtr upcast_goal(std::shared_ptr<DerivationGoal> subGoal)
{
return subGoal;
}
}

View file

@ -1,4 +1,5 @@
#include "nix/store/builtins/buildenv.hh"
#include "nix/store/builtins.hh"
#include "nix/store/derivations.hh"
#include "nix/util/signals.hh"
@ -18,12 +19,12 @@ struct State
/* For each activated package, create symlinks */
static void createLinks(State & state, const Path & srcDir, const Path & dstDir, int priority)
{
std::filesystem::directory_iterator srcFiles;
DirectoryIterator srcFiles;
try {
srcFiles = std::filesystem::directory_iterator{srcDir};
} catch (std::filesystem::filesystem_error & e) {
if (e.code() == std::errc::not_a_directory) {
srcFiles = DirectoryIterator{srcDir};
} catch (SysError & e) {
if (e.errNo == ENOTDIR) {
warn("not including '%s' in the user environment because it's not a directory", srcDir);
return;
}
@ -123,7 +124,7 @@ void buildProfile(const Path & out, Packages && pkgs)
{
State state;
std::set<Path> done, postponed;
PathSet done, postponed;
auto addPkg = [&](const Path & pkgDir, int priority) {
if (!done.insert(pkgDir).second) return;
@ -157,7 +158,7 @@ void buildProfile(const Path & out, Packages && pkgs)
*/
auto priorityCounter = 1000;
while (!postponed.empty()) {
std::set<Path> pkgDirs;
PathSet pkgDirs;
postponed.swap(pkgDirs);
for (const auto & pkgDir : pkgDirs)
addPkg(pkgDir, priorityCounter++);
@ -166,17 +167,15 @@ void buildProfile(const Path & out, Packages && pkgs)
debug("created %d symlinks in user environment", state.symlinks);
}
void builtinBuildenv(
const BasicDerivation & drv,
const std::map<std::string, Path> & outputs)
static void builtinBuildenv(const BuiltinBuilderContext & ctx)
{
auto getAttr = [&](const std::string & name) {
auto i = drv.env.find(name);
if (i == drv.env.end()) throw Error("attribute '%s' missing", name);
auto i = ctx.drv.env.find(name);
if (i == ctx.drv.env.end()) throw Error("attribute '%s' missing", name);
return i->second;
};
auto out = outputs.at("out");
auto out = ctx.outputs.at("out");
createDirs(out);
/* Convert the stuff we get from the environment back into a
@ -203,4 +202,6 @@ void builtinBuildenv(
createSymlink(getAttr("manifest"), out + "/manifest.nix");
}
static RegisterBuiltinBuilder registerBuildenv("buildenv", builtinBuildenv);
}

View file

@ -6,33 +6,29 @@
namespace nix {
void builtinFetchurl(
const BasicDerivation & drv,
const std::map<std::string, Path> & outputs,
const std::string & netrcData,
const std::string & caFileData)
static void builtinFetchurl(const BuiltinBuilderContext & ctx)
{
/* Make the host's netrc data available. Too bad curl requires
this to be stored in a file. It would be nice if we could just
pass a pointer to the data. */
if (netrcData != "") {
if (ctx.netrcData != "") {
settings.netrcFile = "netrc";
writeFile(settings.netrcFile, netrcData, 0600);
writeFile(settings.netrcFile, ctx.netrcData, 0600);
}
settings.caFile = "ca-certificates.crt";
writeFile(settings.caFile, caFileData, 0600);
writeFile(settings.caFile, ctx.caFileData, 0600);
auto out = get(drv.outputs, "out");
auto out = get(ctx.drv.outputs, "out");
if (!out)
throw Error("'builtin:fetchurl' requires an 'out' output");
if (!(drv.type().isFixed() || drv.type().isImpure()))
if (!(ctx.drv.type().isFixed() || ctx.drv.type().isImpure()))
throw Error("'builtin:fetchurl' must be a fixed-output or impure derivation");
auto storePath = outputs.at("out");
auto mainUrl = drv.env.at("url");
bool unpack = getOr(drv.env, "unpack", "") == "1";
auto storePath = ctx.outputs.at("out");
auto mainUrl = ctx.drv.env.at("url");
bool unpack = getOr(ctx.drv.env, "unpack", "") == "1";
/* Note: have to use a fresh fileTransfer here because we're in
a forked process. */
@ -56,8 +52,8 @@ void builtinFetchurl(
else
writeFile(storePath, *source);
auto executable = drv.env.find("executable");
if (executable != drv.env.end() && executable->second == "1") {
auto executable = ctx.drv.env.find("executable");
if (executable != ctx.drv.env.end() && executable->second == "1") {
if (chmod(storePath.c_str(), 0755) == -1)
throw SysError("making '%1%' executable", storePath);
}
@ -79,4 +75,6 @@ void builtinFetchurl(
fetch(mainUrl);
}
static RegisterBuiltinBuilder registerFetchurl("fetchurl", builtinFetchurl);
}

View file

@ -3,23 +3,19 @@
namespace nix {
namespace fs { using namespace std::filesystem; }
void builtinUnpackChannel(
const BasicDerivation & drv,
const std::map<std::string, Path> & outputs)
static void builtinUnpackChannel(const BuiltinBuilderContext & ctx)
{
auto getAttr = [&](const std::string & name) -> const std::string & {
auto i = drv.env.find(name);
if (i == drv.env.end()) throw Error("attribute '%s' missing", name);
auto i = ctx.drv.env.find(name);
if (i == ctx.drv.env.end()) throw Error("attribute '%s' missing", name);
return i->second;
};
fs::path out{outputs.at("out")};
std::filesystem::path out{ctx.outputs.at("out")};
auto & channelName = getAttr("channelName");
auto & src = getAttr("src");
if (fs::path{channelName}.filename().string() != channelName) {
if (std::filesystem::path{channelName}.filename().string() != channelName) {
throw Error("channelName is not allowed to contain filesystem separators, got %1%", channelName);
}
@ -29,23 +25,21 @@ void builtinUnpackChannel(
size_t fileCount;
std::string fileName;
try {
auto entries = fs::directory_iterator{out};
fileName = entries->path().string();
fileCount = std::distance(fs::begin(entries), fs::end(entries));
} catch (fs::filesystem_error &) {
throw SysError("failed to read directory %1%", out.string());
}
auto entries = DirectoryIterator{out};
fileName = entries->path().string();
fileCount = std::distance(entries.begin(), entries.end());
if (fileCount != 1)
throw Error("channel tarball '%s' contains more than one file", src);
auto target = out / channelName;
try {
fs::rename(fileName, target);
} catch (fs::filesystem_error &) {
std::filesystem::rename(fileName, target);
} catch (std::filesystem::filesystem_error &) {
throw SysError("failed to rename %1% to %2%", fileName, target.string());
}
}
static RegisterBuiltinBuilder registerUnpackChannel("unpack-channel", builtinUnpackChannel);
}

View file

@ -28,7 +28,7 @@ CommonSSHStoreConfig::CommonSSHStoreConfig(std::string_view scheme, std::string_
{
}
SSHMaster CommonSSHStoreConfig::createSSHMaster(bool useMaster, Descriptor logFD)
SSHMaster CommonSSHStoreConfig::createSSHMaster(bool useMaster, Descriptor logFD) const
{
return {
host,

View file

@ -1,46 +1,124 @@
#include "nix/store/derivation-options.hh"
#include "nix/util/json-utils.hh"
#include "nix/store/parsed-derivations.hh"
#include "nix/store/derivations.hh"
#include "nix/store/store-api.hh"
#include "nix/util/types.hh"
#include "nix/util/util.hh"
#include <optional>
#include <string>
#include <variant>
#include <regex>
namespace nix {
static std::optional<std::string>
getStringAttr(const StringMap & env, const StructuredAttrs * parsed, const std::string & name)
{
if (parsed) {
auto i = parsed->structuredAttrs.find(name);
if (i == parsed->structuredAttrs.end())
return {};
else {
if (!i->is_string())
throw Error("attribute '%s' of must be a string", name);
return i->get<std::string>();
}
} else {
auto i = env.find(name);
if (i == env.end())
return {};
else
return i->second;
}
}
static bool getBoolAttr(const StringMap & env, const StructuredAttrs * parsed, const std::string & name, bool def)
{
if (parsed) {
auto i = parsed->structuredAttrs.find(name);
if (i == parsed->structuredAttrs.end())
return def;
else {
if (!i->is_boolean())
throw Error("attribute '%s' must be a Boolean", name);
return i->get<bool>();
}
} else {
auto i = env.find(name);
if (i == env.end())
return def;
else
return i->second == "1";
}
}
static std::optional<Strings>
getStringsAttr(const StringMap & env, const StructuredAttrs * parsed, const std::string & name)
{
if (parsed) {
auto i = parsed->structuredAttrs.find(name);
if (i == parsed->structuredAttrs.end())
return {};
else {
if (!i->is_array())
throw Error("attribute '%s' must be a list of strings", name);
Strings res;
for (auto j = i->begin(); j != i->end(); ++j) {
if (!j->is_string())
throw Error("attribute '%s' must be a list of strings", name);
res.push_back(j->get<std::string>());
}
return res;
}
} else {
auto i = env.find(name);
if (i == env.end())
return {};
else
return tokenizeString<Strings>(i->second);
}
}
static std::optional<StringSet>
getStringSetAttr(const StringMap & env, const StructuredAttrs * parsed, const std::string & name)
{
auto ss = getStringsAttr(env, parsed, name);
return ss ? (std::optional{StringSet{ss->begin(), ss->end()}}) : (std::optional<StringSet>{});
}
using OutputChecks = DerivationOptions::OutputChecks;
using OutputChecksVariant = std::variant<OutputChecks, std::map<std::string, OutputChecks>>;
DerivationOptions DerivationOptions::fromParsedDerivation(const ParsedDerivation & parsed, bool shouldWarn)
DerivationOptions
DerivationOptions::fromStructuredAttrs(const StringMap & env, const StructuredAttrs * parsed, bool shouldWarn)
{
DerivationOptions defaults = {};
auto structuredAttrs = parsed.structuredAttrs.get();
if (shouldWarn && structuredAttrs) {
if (get(*structuredAttrs, "allowedReferences")) {
if (shouldWarn && parsed) {
if (get(parsed->structuredAttrs, "allowedReferences")) {
warn(
"'structuredAttrs' disables the effect of the top-level attribute 'allowedReferences'; use 'outputChecks' instead");
}
if (get(*structuredAttrs, "allowedRequisites")) {
if (get(parsed->structuredAttrs, "allowedRequisites")) {
warn(
"'structuredAttrs' disables the effect of the top-level attribute 'allowedRequisites'; use 'outputChecks' instead");
}
if (get(*structuredAttrs, "disallowedRequisites")) {
if (get(parsed->structuredAttrs, "disallowedRequisites")) {
warn(
"'structuredAttrs' disables the effect of the top-level attribute 'disallowedRequisites'; use 'outputChecks' instead");
}
if (get(*structuredAttrs, "disallowedReferences")) {
if (get(parsed->structuredAttrs, "disallowedReferences")) {
warn(
"'structuredAttrs' disables the effect of the top-level attribute 'disallowedReferences'; use 'outputChecks' instead");
}
if (get(*structuredAttrs, "maxSize")) {
if (get(parsed->structuredAttrs, "maxSize")) {
warn(
"'structuredAttrs' disables the effect of the top-level attribute 'maxSize'; use 'outputChecks' instead");
}
if (get(*structuredAttrs, "maxClosureSize")) {
if (get(parsed->structuredAttrs, "maxClosureSize")) {
warn(
"'structuredAttrs' disables the effect of the top-level attribute 'maxClosureSize'; use 'outputChecks' instead");
}
@ -48,9 +126,9 @@ DerivationOptions DerivationOptions::fromParsedDerivation(const ParsedDerivation
return {
.outputChecks = [&]() -> OutputChecksVariant {
if (auto structuredAttrs = parsed.structuredAttrs.get()) {
if (parsed) {
std::map<std::string, OutputChecks> res;
if (auto outputChecks = get(*structuredAttrs, "outputChecks")) {
if (auto outputChecks = get(parsed->structuredAttrs, "outputChecks")) {
for (auto & [outputName, output] : getObject(*outputChecks)) {
OutputChecks checks;
@ -87,10 +165,10 @@ DerivationOptions DerivationOptions::fromParsedDerivation(const ParsedDerivation
return OutputChecks{
// legacy non-structured-attributes case
.ignoreSelfRefs = true,
.allowedReferences = parsed.getStringSetAttr("allowedReferences"),
.disallowedReferences = parsed.getStringSetAttr("disallowedReferences").value_or(StringSet{}),
.allowedRequisites = parsed.getStringSetAttr("allowedRequisites"),
.disallowedRequisites = parsed.getStringSetAttr("disallowedRequisites").value_or(StringSet{}),
.allowedReferences = getStringSetAttr(env, parsed, "allowedReferences"),
.disallowedReferences = getStringSetAttr(env, parsed, "disallowedReferences").value_or(StringSet{}),
.allowedRequisites = getStringSetAttr(env, parsed, "allowedRequisites"),
.disallowedRequisites = getStringSetAttr(env, parsed, "disallowedRequisites").value_or(StringSet{}),
};
}
}(),
@ -98,8 +176,8 @@ DerivationOptions DerivationOptions::fromParsedDerivation(const ParsedDerivation
[&] {
std::map<std::string, bool> res;
if (auto structuredAttrs = parsed.structuredAttrs.get()) {
if (auto udr = get(*structuredAttrs, "unsafeDiscardReferences")) {
if (parsed) {
if (auto udr = get(parsed->structuredAttrs, "unsafeDiscardReferences")) {
for (auto & [outputName, output] : getObject(*udr)) {
if (!output.is_boolean())
throw Error("attribute 'unsafeDiscardReferences.\"%s\"' must be a Boolean", outputName);
@ -113,8 +191,8 @@ DerivationOptions DerivationOptions::fromParsedDerivation(const ParsedDerivation
.passAsFile =
[&] {
StringSet res;
if (auto * passAsFileString = get(parsed.drv.env, "passAsFile")) {
if (parsed.hasStructuredAttrs()) {
if (auto * passAsFileString = get(env, "passAsFile")) {
if (parsed) {
if (shouldWarn) {
warn(
"'structuredAttrs' disables the effect of the top-level attribute 'passAsFile'; because all JSON is always passed via file");
@ -125,16 +203,44 @@ DerivationOptions DerivationOptions::fromParsedDerivation(const ParsedDerivation
}
return res;
}(),
.exportReferencesGraph =
[&] {
std::map<std::string, StringSet> ret;
if (parsed) {
auto e = optionalValueAt(parsed->structuredAttrs, "exportReferencesGraph");
if (!e || !e->is_object())
return ret;
for (auto & [key, storePathsJson] : getObject(*e)) {
ret.insert_or_assign(key, storePathsJson);
}
} else {
auto s = getOr(env, "exportReferencesGraph", "");
Strings ss = tokenizeString<Strings>(s);
if (ss.size() % 2 != 0)
throw Error("odd number of tokens in 'exportReferencesGraph': '%1%'", s);
for (Strings::iterator i = ss.begin(); i != ss.end();) {
auto fileName = std::move(*i++);
static std::regex regex("[A-Za-z_][A-Za-z0-9_.-]*");
if (!std::regex_match(fileName, regex))
throw Error("invalid file name '%s' in 'exportReferencesGraph'", fileName);
auto & storePathS = *i++;
ret.insert_or_assign(std::move(fileName), StringSet{storePathS});
}
}
return ret;
}(),
.additionalSandboxProfile =
parsed.getStringAttr("__sandboxProfile").value_or(defaults.additionalSandboxProfile),
.noChroot = parsed.getBoolAttr("__noChroot", defaults.noChroot),
.impureHostDeps = parsed.getStringSetAttr("__impureHostDeps").value_or(defaults.impureHostDeps),
.impureEnvVars = parsed.getStringSetAttr("impureEnvVars").value_or(defaults.impureEnvVars),
.allowLocalNetworking = parsed.getBoolAttr("__darwinAllowLocalNetworking", defaults.allowLocalNetworking),
getStringAttr(env, parsed, "__sandboxProfile").value_or(defaults.additionalSandboxProfile),
.noChroot = getBoolAttr(env, parsed, "__noChroot", defaults.noChroot),
.impureHostDeps = getStringSetAttr(env, parsed, "__impureHostDeps").value_or(defaults.impureHostDeps),
.impureEnvVars = getStringSetAttr(env, parsed, "impureEnvVars").value_or(defaults.impureEnvVars),
.allowLocalNetworking = getBoolAttr(env, parsed, "__darwinAllowLocalNetworking", defaults.allowLocalNetworking),
.requiredSystemFeatures =
parsed.getStringSetAttr("requiredSystemFeatures").value_or(defaults.requiredSystemFeatures),
.preferLocalBuild = parsed.getBoolAttr("preferLocalBuild", defaults.preferLocalBuild),
.allowSubstitutes = parsed.getBoolAttr("allowSubstitutes", defaults.allowSubstitutes),
getStringSetAttr(env, parsed, "requiredSystemFeatures").value_or(defaults.requiredSystemFeatures),
.preferLocalBuild = getBoolAttr(env, parsed, "preferLocalBuild", defaults.preferLocalBuild),
.allowSubstitutes = getBoolAttr(env, parsed, "allowSubstitutes", defaults.allowSubstitutes),
};
}
@ -159,7 +265,7 @@ bool DerivationOptions::canBuildLocally(Store & localStore, const BasicDerivatio
return false;
for (auto & feature : getRequiredSystemFeatures(drv))
if (!localStore.systemFeatures.get().count(feature))
if (!localStore.config.systemFeatures.get().count(feature))
return false;
return true;

View file

@ -1052,49 +1052,36 @@ static void rewriteDerivation(Store & store, BasicDerivation & drv, const String
std::optional<BasicDerivation> Derivation::tryResolve(Store & store, Store * evalStore) const
{
std::map<std::pair<StorePath, std::string>, StorePath> inputDrvOutputs;
std::function<void(const StorePath &, const DerivedPathMap<StringSet>::ChildNode &)> accum;
accum = [&](auto & inputDrv, auto & node) {
for (auto & [outputName, outputPath] : store.queryPartialDerivationOutputMap(inputDrv, evalStore)) {
if (outputPath) {
inputDrvOutputs.insert_or_assign({inputDrv, outputName}, *outputPath);
if (auto p = get(node.childMap, outputName))
accum(*outputPath, *p);
return tryResolve(
store,
[&](ref<const SingleDerivedPath> drvPath, const std::string & outputName) -> std::optional<StorePath> {
try {
return resolveDerivedPath(store, SingleDerivedPath::Built{drvPath, outputName}, evalStore);
} catch (Error &) {
return std::nullopt;
}
}
};
for (auto & [inputDrv, node] : inputDrvs.map)
accum(inputDrv, node);
return tryResolve(store, inputDrvOutputs);
});
}
static bool tryResolveInput(
Store & store, StorePathSet & inputSrcs, StringMap & inputRewrites,
const DownstreamPlaceholder * placeholderOpt,
const StorePath & inputDrv, const DerivedPathMap<StringSet>::ChildNode & inputNode,
const std::map<std::pair<StorePath, std::string>, StorePath> & inputDrvOutputs)
ref<const SingleDerivedPath> drvPath, const DerivedPathMap<StringSet>::ChildNode & inputNode,
std::function<std::optional<StorePath>(ref<const SingleDerivedPath> drvPath, const std::string & outputName)> queryResolutionChain)
{
auto getOutput = [&](const std::string & outputName) {
auto * actualPathOpt = get(inputDrvOutputs, { inputDrv, outputName });
if (!actualPathOpt)
warn("output %s of input %s missing, aborting the resolving",
outputName,
store.printStorePath(inputDrv)
);
return actualPathOpt;
};
auto getPlaceholder = [&](const std::string & outputName) {
return placeholderOpt
? DownstreamPlaceholder::unknownDerivation(*placeholderOpt, outputName)
: DownstreamPlaceholder::unknownCaOutput(inputDrv, outputName);
: [&]{
auto * p = std::get_if<SingleDerivedPath::Opaque>(&drvPath->raw());
// otherwise we should have had a placeholder to build-upon already
assert(p);
return DownstreamPlaceholder::unknownCaOutput(p->path, outputName);
}();
};
for (auto & outputName : inputNode.value) {
auto actualPathOpt = getOutput(outputName);
auto actualPathOpt = queryResolutionChain(drvPath, outputName);
if (!actualPathOpt) return false;
auto actualPath = *actualPathOpt;
if (experimentalFeatureSettings.isEnabled(Xp::CaDerivations)) {
@ -1106,13 +1093,12 @@ static bool tryResolveInput(
}
for (auto & [outputName, childNode] : inputNode.childMap) {
auto actualPathOpt = getOutput(outputName);
if (!actualPathOpt) return false;
auto actualPath = *actualPathOpt;
auto nextPlaceholder = getPlaceholder(outputName);
if (!tryResolveInput(store, inputSrcs, inputRewrites,
&nextPlaceholder, actualPath, childNode,
inputDrvOutputs))
&nextPlaceholder,
make_ref<const SingleDerivedPath>(SingleDerivedPath::Built{drvPath, outputName}),
childNode,
queryResolutionChain))
return false;
}
return true;
@ -1120,7 +1106,7 @@ static bool tryResolveInput(
std::optional<BasicDerivation> Derivation::tryResolve(
Store & store,
const std::map<std::pair<StorePath, std::string>, StorePath> & inputDrvOutputs) const
std::function<std::optional<StorePath>(ref<const SingleDerivedPath> drvPath, const std::string & outputName)> queryResolutionChain) const
{
BasicDerivation resolved { *this };
@ -1129,7 +1115,7 @@ std::optional<BasicDerivation> Derivation::tryResolve(
for (auto & [inputDrv, inputNode] : inputDrvs.map)
if (!tryResolveInput(store, resolved.inputSrcs, inputRewrites,
nullptr, inputDrv, inputNode, inputDrvOutputs))
nullptr, make_ref<const SingleDerivedPath>(SingleDerivedPath::Opaque{inputDrv}), inputNode, queryResolutionChain))
return std::nullopt;
rewriteDerivation(store, resolved, inputRewrites);
@ -1242,13 +1228,13 @@ DerivationOutput DerivationOutput::fromJSON(
keys.insert(key);
auto methodAlgo = [&]() -> std::pair<ContentAddressMethod, HashAlgorithm> {
auto & method_ = getString(valueAt(json, "method"));
ContentAddressMethod method = ContentAddressMethod::parse(method_);
ContentAddressMethod method = ContentAddressMethod::parse(
getString(valueAt(json, "method")));
if (method == ContentAddressMethod::Raw::Text)
xpSettings.require(Xp::DynamicDerivations);
auto & hashAlgo_ = getString(valueAt(json, "hashAlgo"));
auto hashAlgo = parseHashAlgo(hashAlgo_);
auto hashAlgo = parseHashAlgo(
getString(valueAt(json, "hashAlgo")));
return { std::move(method), std::move(hashAlgo) };
};
@ -1365,7 +1351,8 @@ Derivation Derivation::fromJSON(
res.name = getString(valueAt(json, "name"));
try {
for (auto & [outputName, output] : getObject(valueAt(json, "outputs"))) {
auto outputs = getObject(valueAt(json, "outputs"));
for (auto & [outputName, output] : outputs) {
res.outputs.insert_or_assign(
outputName,
DerivationOutput::fromJSON(store, res.name, outputName, output, xpSettings));
@ -1376,7 +1363,8 @@ Derivation Derivation::fromJSON(
}
try {
for (auto & input : getArray(valueAt(json, "inputSrcs")))
auto inputSrcs = getArray(valueAt(json, "inputSrcs"));
for (auto & input : inputSrcs)
res.inputSrcs.insert(store.parseStorePath(static_cast<const std::string &>(input)));
} catch (Error & e) {
e.addTrace({}, "while reading key 'inputSrcs'");
@ -1389,13 +1377,15 @@ Derivation Derivation::fromJSON(
auto & json = getObject(_json);
DerivedPathMap<StringSet>::ChildNode node;
node.value = getStringSet(valueAt(json, "outputs"));
for (auto & [outputId, childNode] : getObject(valueAt(json, "dynamicOutputs"))) {
auto drvs = getObject(valueAt(json, "dynamicOutputs"));
for (auto & [outputId, childNode] : drvs) {
xpSettings.require(Xp::DynamicDerivations);
node.childMap[outputId] = doInput(childNode);
}
return node;
};
for (auto & [inputDrvPath, inputOutputs] : getObject(valueAt(json, "inputDrvs")))
auto drvs = getObject(valueAt(json, "inputDrvs"));
for (auto & [inputDrvPath, inputOutputs] : drvs)
res.inputDrvs.map[store.parseStorePath(inputDrvPath)] =
doInput(inputOutputs);
} catch (Error & e) {

View file

@ -55,17 +55,17 @@ typename DerivedPathMap<V>::ChildNode * DerivedPathMap<V>::findSlot(const Single
namespace nix {
template<>
bool DerivedPathMap<std::set<std::string>>::ChildNode::operator == (
const DerivedPathMap<std::set<std::string>>::ChildNode &) const noexcept = default;
bool DerivedPathMap<StringSet>::ChildNode::operator == (
const DerivedPathMap<StringSet>::ChildNode &) const noexcept = default;
// TODO libc++ 16 (used by darwin) missing `std::map::operator <=>`, can't do yet.
#if 0
template<>
std::strong_ordering DerivedPathMap<std::set<std::string>>::ChildNode::operator <=> (
const DerivedPathMap<std::set<std::string>>::ChildNode &) const noexcept = default;
std::strong_ordering DerivedPathMap<StringSet>::ChildNode::operator <=> (
const DerivedPathMap<StringSet>::ChildNode &) const noexcept = default;
#endif
template struct DerivedPathMap<std::set<std::string>>::ChildNode;
template struct DerivedPathMap<std::set<std::string>>;
template struct DerivedPathMap<StringSet>::ChildNode;
template struct DerivedPathMap<StringSet>;
};

View file

@ -170,7 +170,7 @@ void drvRequireExperiment(
}
SingleDerivedPath::Built SingleDerivedPath::Built::parse(
const StoreDirConfig & store, ref<SingleDerivedPath> drv,
const StoreDirConfig & store, ref<const SingleDerivedPath> drv,
OutputNameView output,
const ExperimentalFeatureSettings & xpSettings)
{
@ -182,7 +182,7 @@ SingleDerivedPath::Built SingleDerivedPath::Built::parse(
}
DerivedPath::Built DerivedPath::Built::parse(
const StoreDirConfig & store, ref<SingleDerivedPath> drv,
const StoreDirConfig & store, ref<const SingleDerivedPath> drv,
OutputNameView outputsS,
const ExperimentalFeatureSettings & xpSettings)
{
@ -201,7 +201,7 @@ static SingleDerivedPath parseWithSingle(
return n == s.npos
? (SingleDerivedPath) SingleDerivedPath::Opaque::parse(store, s)
: (SingleDerivedPath) SingleDerivedPath::Built::parse(store,
make_ref<SingleDerivedPath>(parseWithSingle(
make_ref<const SingleDerivedPath>(parseWithSingle(
store,
s.substr(0, n),
separator,
@ -234,7 +234,7 @@ static DerivedPath parseWith(
return n == s.npos
? (DerivedPath) DerivedPath::Opaque::parse(store, s)
: (DerivedPath) DerivedPath::Built::parse(store,
make_ref<SingleDerivedPath>(parseWithSingle(
make_ref<const SingleDerivedPath>(parseWithSingle(
store,
s.substr(0, n),
separator,

View file

@ -1,9 +1,9 @@
#include "nix/store/store-api.hh"
#include "nix/store/store-registration.hh"
#include "nix/util/callback.hh"
namespace nix {
struct DummyStoreConfig : virtual StoreConfig {
struct DummyStoreConfig : public std::enable_shared_from_this<DummyStoreConfig>, virtual StoreConfig {
using StoreConfig::StoreConfig;
DummyStoreConfig(std::string_view scheme, std::string_view authority, const Params & params)
@ -13,35 +13,36 @@ struct DummyStoreConfig : virtual StoreConfig {
throw UsageError("`%s` store URIs must not contain an authority part %s", scheme, authority);
}
const std::string name() override { return "Dummy Store"; }
static const std::string name() { return "Dummy Store"; }
std::string doc() override
static std::string doc()
{
return
#include "dummy-store.md"
;
}
static std::set<std::string> uriSchemes() {
static StringSet uriSchemes() {
return {"dummy"};
}
ref<Store> openStore() const override;
};
struct DummyStore : public virtual DummyStoreConfig, public virtual Store
struct DummyStore : virtual Store
{
DummyStore(std::string_view scheme, std::string_view authority, const Params & params)
: StoreConfig(params)
, DummyStoreConfig(scheme, authority, params)
, Store(params)
{ }
using Config = DummyStoreConfig;
DummyStore(const Params & params)
: DummyStore("dummy", "", params)
ref<const Config> config;
DummyStore(ref<const Config> config)
: Store{*config}
, config(config)
{ }
std::string getUri() override
{
return *uriSchemes().begin();
return *Config::uriSchemes().begin();
}
void queryPathInfoUncached(const StorePath & path,
@ -83,9 +84,16 @@ struct DummyStore : public virtual DummyStoreConfig, public virtual Store
{ callback(nullptr); }
virtual ref<SourceAccessor> getFSAccessor(bool requireValidPath) override
{ unsupported("getFSAccessor"); }
{
return makeEmptySourceAccessor();
}
};
static RegisterStoreImplementation<DummyStore, DummyStoreConfig> regDummyStore;
ref<Store> DummyStore::Config::openStore() const
{
return make_ref<DummyStore>(ref{shared_from_this()});
}
static RegisterStoreImplementation<DummyStore::Config> regDummyStore;
}

View file

@ -33,6 +33,9 @@ using namespace std::string_literals;
namespace nix {
const unsigned int RETRY_TIME_MS_DEFAULT = 250;
const unsigned int RETRY_TIME_MS_TOO_MANY_REQUESTS = 60000;
FileTransferSettings fileTransferSettings;
static GlobalConfig::Register rFileTransferSettings(&fileTransferSettings);
@ -309,6 +312,23 @@ struct curlFileTransfer : public FileTransfer
}
#endif
size_t seekCallback(curl_off_t offset, int origin)
{
if (origin == SEEK_SET) {
readOffset = offset;
} else if (origin == SEEK_CUR) {
readOffset += offset;
} else if (origin == SEEK_END) {
readOffset = request.data->length() + offset;
}
return CURL_SEEKFUNC_OK;
}
static size_t seekCallbackWrapper(void *clientp, curl_off_t offset, int origin)
{
return ((TransferItem *) clientp)->seekCallback(offset, origin);
}
void init()
{
if (!req) req = curl_easy_init();
@ -363,6 +383,8 @@ struct curlFileTransfer : public FileTransfer
curl_easy_setopt(req, CURLOPT_READFUNCTION, readCallbackWrapper);
curl_easy_setopt(req, CURLOPT_READDATA, this);
curl_easy_setopt(req, CURLOPT_INFILESIZE_LARGE, (curl_off_t) request.data->length());
curl_easy_setopt(req, CURLOPT_SEEKFUNCTION, seekCallbackWrapper);
curl_easy_setopt(req, CURLOPT_SEEKDATA, this);
}
if (request.verifyTLS) {
@ -401,6 +423,8 @@ struct curlFileTransfer : public FileTransfer
{
auto finishTime = std::chrono::steady_clock::now();
auto retryTimeMs = request.baseRetryTimeMs;
auto httpStatus = getHTTPStatus();
debug("finished %s of '%s'; curl status = %d, HTTP status = %d, body = %d bytes, duration = %.2f s",
@ -451,10 +475,12 @@ struct curlFileTransfer : public FileTransfer
} else if (httpStatus == 401 || httpStatus == 403 || httpStatus == 407) {
// Don't retry on authentication/authorization failures
err = Forbidden;
} else if (httpStatus >= 400 && httpStatus < 500 && httpStatus != 408 && httpStatus != 429) {
} else if (httpStatus == 429) {
// 429 means too many requests, so we retry (with a substantially longer delay)
retryTimeMs = RETRY_TIME_MS_TOO_MANY_REQUESTS;
} else if (httpStatus >= 400 && httpStatus < 500 && httpStatus != 408) {
// Most 4xx errors are client errors and are probably not worth retrying:
// * 408 means the server timed out waiting for us, so we try again
// * 429 means too many requests, so we retry (with a delay)
err = Misc;
} else if (httpStatus == 501 || httpStatus == 505 || httpStatus == 511) {
// Let's treat most 5xx (server) errors as transient, except for a handful:
@ -520,7 +546,7 @@ struct curlFileTransfer : public FileTransfer
|| writtenToSink == 0
|| (acceptRanges && encoding.empty())))
{
int ms = request.baseRetryTimeMs * std::pow(2.0f, attempt - 1 + std::uniform_real_distribution<>(0.0, 0.5)(fileTransfer.mt19937));
int ms = retryTimeMs * std::pow(2.0f, attempt - 1 + std::uniform_real_distribution<>(0.0, 0.5)(fileTransfer.mt19937));
if (writtenToSink)
warn("%s; retrying from offset %d in %d ms", exc.what(), writtenToSink, ms);
else
@ -747,7 +773,7 @@ struct curlFileTransfer : public FileTransfer
}
#if NIX_WITH_S3_SUPPORT
std::tuple<std::string, std::string, Store::Params> parseS3Uri(std::string uri)
std::tuple<std::string, std::string, Store::Config::Params> parseS3Uri(std::string uri)
{
auto [path, params] = splitUriAndParams(uri);

View file

@ -43,7 +43,7 @@ static std::string gcRootsDir = "gcroots";
void LocalStore::addIndirectRoot(const Path & path)
{
std::string hash = hashString(HashAlgorithm::SHA1, path).to_string(HashFormat::Nix32, false);
Path realRoot = canonPath(fmt("%1%/%2%/auto/%3%", stateDir, gcRootsDir, hash));
Path realRoot = canonPath(fmt("%1%/%2%/auto/%3%", config->stateDir, gcRootsDir, hash));
makeSymlink(realRoot, path);
}
@ -82,7 +82,7 @@ void LocalStore::createTempRootsFile()
void LocalStore::addTempRoot(const StorePath & path)
{
if (readOnly) {
if (config->readOnly) {
debug("Read-only store doesn't support creating lock files for temp roots, but nothing can be deleted anyways.");
return;
}
@ -109,7 +109,7 @@ void LocalStore::addTempRoot(const StorePath & path)
auto fdRootsSocket(_fdRootsSocket.lock());
if (!*fdRootsSocket) {
auto socketPath = stateDir.get() + gcSocketPath;
auto socketPath = config->stateDir.get() + gcSocketPath;
debug("connecting to '%s'", socketPath);
*fdRootsSocket = createUnixDomainSocket();
try {
@ -164,7 +164,7 @@ void LocalStore::findTempRoots(Roots & tempRoots, bool censor)
{
/* Read the `temproots' directory for per-process temporary root
files. */
for (auto & i : std::filesystem::directory_iterator{tempRootsDir}) {
for (auto & i : DirectoryIterator{tempRootsDir}) {
checkInterrupt();
auto name = i.path().filename().string();
if (name[0] == '.') {
@ -232,7 +232,7 @@ void LocalStore::findRoots(const Path & path, std::filesystem::file_type type, R
type = std::filesystem::symlink_status(path).type();
if (type == std::filesystem::file_type::directory) {
for (auto & i : std::filesystem::directory_iterator{path}) {
for (auto & i : DirectoryIterator{path}) {
checkInterrupt();
findRoots(i.path().string(), i.symlink_status().type(), roots);
}
@ -247,7 +247,7 @@ void LocalStore::findRoots(const Path & path, std::filesystem::file_type type, R
else {
target = absPath(target, dirOf(path));
if (!pathExists(target)) {
if (isInDir(path, stateDir + "/" + gcRootsDir + "/auto")) {
if (isInDir(path, std::filesystem::path{config->stateDir.get()} / gcRootsDir / "auto")) {
printInfo("removing stale link from '%1%' to '%2%'", path, target);
unlink(path.c_str());
}
@ -288,8 +288,8 @@ void LocalStore::findRoots(const Path & path, std::filesystem::file_type type, R
void LocalStore::findRootsNoTemp(Roots & roots, bool censor)
{
/* Process direct roots in {gcroots,profiles}. */
findRoots(stateDir + "/" + gcRootsDir, std::filesystem::file_type::unknown, roots);
findRoots(stateDir + "/profiles", std::filesystem::file_type::unknown, roots);
findRoots(config->stateDir + "/" + gcRootsDir, std::filesystem::file_type::unknown, roots);
findRoots(config->stateDir + "/profiles", std::filesystem::file_type::unknown, roots);
/* Add additional roots returned by different platforms-specific
heuristics. This is typically used to add running programs to
@ -498,7 +498,7 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
readFile(*p);
/* Start the server for receiving new roots. */
auto socketPath = stateDir.get() + gcSocketPath;
auto socketPath = config->stateDir.get() + gcSocketPath;
createDirs(dirOf(socketPath));
auto fdServer = createUnixDomainSocket(socketPath, 0666);
@ -635,7 +635,7 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
auto deleteFromStore = [&](std::string_view baseName)
{
Path path = storeDir + "/" + std::string(baseName);
Path realPath = realStoreDir + "/" + std::string(baseName);
Path realPath = config->realStoreDir + "/" + std::string(baseName);
/* There may be temp directories in the store that are still in use
by another process. We need to be sure that we can acquire an
@ -804,8 +804,8 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
printInfo("determining live/dead paths...");
try {
AutoCloseDir dir(opendir(realStoreDir.get().c_str()));
if (!dir) throw SysError("opening directory '%1%'", realStoreDir);
AutoCloseDir dir(opendir(config->realStoreDir.get().c_str()));
if (!dir) throw SysError("opening directory '%1%'", config->realStoreDir);
/* Read the store and delete all paths that are invalid or
unreachable. We don't use readDirectory() here so that
@ -907,8 +907,8 @@ void LocalStore::autoGC(bool sync)
return std::stoll(readFile(*fakeFreeSpaceFile));
struct statvfs st;
if (statvfs(realStoreDir.get().c_str(), &st))
throw SysError("getting filesystem info about '%s'", realStoreDir);
if (statvfs(config->realStoreDir.get().c_str(), &st))
throw SysError("getting filesystem info about '%s'", config->realStoreDir);
return (uint64_t) st.f_bavail * st.f_frsize;
};

View file

@ -3,12 +3,22 @@
#include "nix/store/globals.hh"
#include "nix/store/nar-info-disk-cache.hh"
#include "nix/util/callback.hh"
#include "nix/store/store-registration.hh"
namespace nix {
MakeError(UploadToHTTP, Error);
StringSet HttpBinaryCacheStoreConfig::uriSchemes()
{
static bool forceHttp = getEnv("_NIX_FORCE_HTTP") == "1";
auto ret = StringSet{"http", "https"};
if (forceHttp)
ret.insert("file");
return ret;
}
HttpBinaryCacheStoreConfig::HttpBinaryCacheStoreConfig(
std::string_view scheme,
std::string_view _cacheUri,
@ -35,10 +45,9 @@ std::string HttpBinaryCacheStoreConfig::doc()
}
class HttpBinaryCacheStore : public virtual HttpBinaryCacheStoreConfig, public virtual BinaryCacheStore
class HttpBinaryCacheStore :
public virtual BinaryCacheStore
{
private:
struct State
{
bool enabled = true;
@ -49,37 +58,37 @@ private:
public:
HttpBinaryCacheStore(
std::string_view scheme,
PathView cacheUri,
const Params & params)
: StoreConfig(params)
, BinaryCacheStoreConfig(params)
, HttpBinaryCacheStoreConfig(scheme, cacheUri, params)
, Store(params)
, BinaryCacheStore(params)
using Config = HttpBinaryCacheStoreConfig;
ref<Config> config;
HttpBinaryCacheStore(ref<Config> config)
: Store{*config}
// TODO it will actually mutate the configuration
, BinaryCacheStore{*config}
, config{config}
{
diskCache = getNarInfoDiskCache();
}
std::string getUri() override
{
return cacheUri;
return config->cacheUri;
}
void init() override
{
// FIXME: do this lazily?
if (auto cacheInfo = diskCache->upToDateCacheExists(cacheUri)) {
wantMassQuery.setDefault(cacheInfo->wantMassQuery);
priority.setDefault(cacheInfo->priority);
if (auto cacheInfo = diskCache->upToDateCacheExists(config->cacheUri)) {
config->wantMassQuery.setDefault(cacheInfo->wantMassQuery);
config->priority.setDefault(cacheInfo->priority);
} else {
try {
BinaryCacheStore::init();
} catch (UploadToHTTP &) {
throw Error("'%s' does not appear to be a binary cache", cacheUri);
throw Error("'%s' does not appear to be a binary cache", config->cacheUri);
}
diskCache->createCache(cacheUri, storeDir, wantMassQuery, priority);
diskCache->createCache(config->cacheUri, config->storeDir, config->wantMassQuery, config->priority);
}
}
@ -137,7 +146,7 @@ protected:
try {
getFileTransfer()->upload(req);
} catch (FileTransferError & e) {
throw UploadToHTTP("while uploading to HTTP binary cache at '%s': %s", cacheUri, e.msg());
throw UploadToHTTP("while uploading to HTTP binary cache at '%s': %s", config->cacheUri, e.msg());
}
}
@ -146,7 +155,7 @@ protected:
return FileTransferRequest(
hasPrefix(path, "https://") || hasPrefix(path, "http://") || hasPrefix(path, "file://")
? path
: cacheUri + "/" + path);
: config->cacheUri + "/" + path);
}
@ -221,6 +230,14 @@ protected:
}
};
static RegisterStoreImplementation<HttpBinaryCacheStore, HttpBinaryCacheStoreConfig> regHttpBinaryCacheStore;
ref<Store> HttpBinaryCacheStore::Config::openStore() const
{
return make_ref<HttpBinaryCacheStore>(ref{
// FIXME we shouldn't actually need a mutable config
std::const_pointer_cast<HttpBinaryCacheStore::Config>(shared_from_this())
});
}
static RegisterStoreImplementation<HttpBinaryCacheStore::Config> regHttpBinaryCacheStore;
}

View file

@ -32,6 +32,9 @@ struct BinaryCacheStoreConfig : virtual StoreConfig
const Setting<Path> secretKeyFile{this, "", "secret-key",
"Path to the secret key used to sign the binary cache."};
const Setting<std::string> secretKeyFiles{this, "", "secret-keys",
"List of comma-separated paths to the secret keys used to sign the binary cache."};
const Setting<Path> localNarCache{this, "", "local-nar-cache",
"Path to a local cache of NARs fetched from this binary cache, used by commands such as `nix store cat`."};
@ -51,13 +54,20 @@ struct BinaryCacheStoreConfig : virtual StoreConfig
* @note subclasses must implement at least one of the two
* virtual getFile() methods.
*/
class BinaryCacheStore : public virtual BinaryCacheStoreConfig,
public virtual Store,
public virtual LogStore
struct BinaryCacheStore :
virtual Store,
virtual LogStore
{
using Config = BinaryCacheStoreConfig;
/**
* Intentionally mutable because some things we update due to the
* cache's own (remote side) settings.
*/
Config & config;
private:
std::unique_ptr<Signer> signer;
std::vector<std::unique_ptr<Signer>> signers;
protected:
@ -66,7 +76,7 @@ protected:
const std::string cacheInfoFile = "nix-cache-info";
BinaryCacheStore(const Params & params);
BinaryCacheStore(Config &);
public:

View file

@ -0,0 +1,58 @@
#pragma once
/**
* @file Misc type defitions for both local building and remote (RPC building)
*/
#include "nix/util/hash.hh"
#include "nix/store/path.hh"
namespace nix {
class Store;
struct Derivation;
/**
* Unless we are repairing, we don't both to test validity and just assume it,
* so the choices are `Absent` or `Valid`.
*/
enum struct PathStatus {
Corrupt,
Absent,
Valid,
};
struct InitialOutputStatus
{
StorePath path;
PathStatus status;
/**
* Valid in the store, and additionally non-corrupt if we are repairing
*/
bool isValid() const
{
return status == PathStatus::Valid;
}
/**
* Merely present, allowed to be corrupt
*/
bool isPresent() const
{
return status == PathStatus::Corrupt || status == PathStatus::Valid;
}
};
struct InitialOutput
{
bool wanted;
Hash outputHash;
std::optional<InitialOutputStatus> known;
};
void runPostBuildHook(Store & store, Logger & logger, const StorePath & drvPath, const StorePathSet & outputPaths);
/**
* Format the known outputs of a derivation for use in error messages.
*/
std::string showKnownOutputs(Store & store, const Derivation & drv);
}

View file

@ -2,10 +2,9 @@
///@file
#include "nix/store/parsed-derivations.hh"
#include "nix/store/derivations.hh"
#include "nix/store/derivation-options.hh"
#ifndef _WIN32
# include "nix/store/user-lock.hh"
#endif
#include "nix/store/build/derivation-building-misc.hh"
#include "nix/store/outputs-spec.hh"
#include "nix/store/store-api.hh"
#include "nix/store/pathlocks.hh"
@ -17,43 +16,17 @@ using std::map;
#ifndef _WIN32 // TODO enable build hook on Windows
struct HookInstance;
struct DerivationBuilder;
#endif
typedef enum {rpAccept, rpDecline, rpPostpone} HookReply;
/**
* Unless we are repairing, we don't both to test validity and just assume it,
* so the choices are `Absent` or `Valid`.
*/
enum struct PathStatus {
Corrupt,
Absent,
Valid,
};
struct InitialOutputStatus {
StorePath path;
PathStatus status;
/**
* Valid in the store, and additionally non-corrupt if we are repairing
*/
bool isValid() const {
return status == PathStatus::Valid;
}
/**
* Merely present, allowed to be corrupt
*/
bool isPresent() const {
return status == PathStatus::Corrupt
|| status == PathStatus::Valid;
}
};
struct InitialOutput {
bool wanted;
Hash outputHash;
std::optional<InitialOutputStatus> known;
};
/** Used internally */
void runPostBuildHook(
Store & store,
Logger & logger,
const StorePath & drvPath,
const StorePathSet & outputPaths);
/**
* A goal for building some or all of the outputs of a derivation.
@ -68,23 +41,11 @@ struct DerivationGoal : public Goal
/** The path of the derivation. */
StorePath drvPath;
/**
* The goal for the corresponding resolved derivation
*/
std::shared_ptr<DerivationGoal> resolvedDrvGoal;
/**
* The specific outputs that we need to build.
*/
OutputsSpec wantedOutputs;
/**
* Mapping from input derivations + output names to actual store
* paths. This is filled in by waiteeDone() as each dependency
* finishes, before `trace("all inputs realised")` is reached.
*/
std::map<std::pair<StorePath, std::string>, StorePath> inputDrvOutputs;
/**
* See `needRestart`; just for that field.
*/
@ -143,7 +104,7 @@ struct DerivationGoal : public Goal
*/
std::unique_ptr<Derivation> drv;
std::unique_ptr<ParsedDerivation> parsedDrv;
std::unique_ptr<StructuredAttrs> parsedDrv;
std::unique_ptr<DerivationOptions> drvOptions;
/**
@ -189,12 +150,9 @@ struct DerivationGoal : public Goal
* The build hook.
*/
std::unique_ptr<HookInstance> hook;
#endif
/**
* The sort of derivation we are building.
*/
std::optional<DerivationType> derivationType;
std::unique_ptr<DerivationBuilder> builder;
#endif
BuildMode buildMode;
@ -220,7 +178,7 @@ struct DerivationGoal : public Goal
DerivationGoal(const StorePath & drvPath, const BasicDerivation & drv,
const OutputsSpec & wantedOutputs, Worker & worker,
BuildMode buildMode = bmNormal);
virtual ~DerivationGoal();
~DerivationGoal();
void timedOut(Error && ex) override;
@ -238,55 +196,24 @@ struct DerivationGoal : public Goal
Co haveDerivation();
Co gaveUpOnSubstitution();
Co tryToBuild();
virtual Co tryLocalBuild();
Co buildDone();
Co resolvedFinished();
Co hookDone();
/**
* Is the build hook willing to perform the build?
*/
HookReply tryBuildHook();
virtual int getChildStatus();
/**
* Check that the derivation outputs all exist and register them
* as valid.
*/
virtual SingleDrvOutputs registerOutputs();
/**
* Open a log file and a pipe to it.
*/
Path openLogFile();
/**
* Sign the newly built realisation if the store allows it
*/
virtual void signRealisation(Realisation&) {}
/**
* Close the log file.
*/
void closeLogFile();
/**
* Close the read side of the logger pipe.
*/
virtual void closeReadPipes();
/**
* Cleanup hooks for buildDone()
*/
virtual void cleanupHookFinally();
virtual void cleanupPreChildKill();
virtual void cleanupPostChildKill();
virtual bool cleanupDecideWhetherDiskFull();
virtual void cleanupPostOutputsRegisteredModeCheck();
virtual void cleanupPostOutputsRegisteredModeNonCheck();
virtual bool isReadDesc(Descriptor fd);
bool isReadDesc(Descriptor fd);
/**
* Callback used by the worker to write to the log.
@ -320,7 +247,7 @@ struct DerivationGoal : public Goal
/**
* Forcibly kill the child process, if any.
*/
virtual void killChild();
void killChild();
Co repairClosure();
@ -331,7 +258,7 @@ struct DerivationGoal : public Goal
SingleDrvOutputs builtOutputs = {},
std::optional<Error> ex = {});
void waiteeDone(GoalPtr waitee, ExitCode result) override;
void appendLogTailErrorMsg(std::string & msg);
StorePathSet exportReferences(const StorePathSet & storePaths);
@ -340,6 +267,4 @@ struct DerivationGoal : public Goal
};
};
MakeError(NotDeterministic, BuildError);
}

View file

@ -34,7 +34,7 @@ public:
GoalState state;
Co init() override;
Co realisationFetched(std::shared_ptr<const Realisation> outputInfo, nix::ref<nix::Store> sub);
Co realisationFetched(Goals waitees, std::shared_ptr<const Realisation> outputInfo, nix::ref<nix::Store> sub);
void timedOut(Error && ex) override { unreachable(); };

View file

@ -54,6 +54,13 @@ enum struct JobCategory {
struct Goal : public std::enable_shared_from_this<Goal>
{
private:
/**
* Goals that this goal is waiting for.
*/
Goals waitees;
public:
typedef enum {ecBusy, ecSuccess, ecFailed, ecNoSubstituters, ecIncompleteClosure} ExitCode;
/**
@ -61,11 +68,6 @@ struct Goal : public std::enable_shared_from_this<Goal>
*/
Worker & worker;
/**
* Goals that this goal is waiting for.
*/
Goals waitees;
/**
* Goals waiting for this one to finish. Must use weak pointers
* here to prevent cycles.
@ -104,8 +106,8 @@ protected:
* Build result.
*/
BuildResult buildResult;
public:
public:
/**
* Suspend our goal and wait until we get `work`-ed again.
* `co_await`-able by @ref Co.
@ -332,6 +334,7 @@ public:
std::suspend_always await_transform(Suspend) { return {}; };
};
protected:
/**
* The coroutine being currently executed.
* MUST be updated when switching the coroutine being executed.
@ -359,6 +362,7 @@ public:
*/
Done amDone(ExitCode result, std::optional<Error> ex = {});
public:
virtual void cleanup() { }
/**
@ -378,7 +382,7 @@ public:
*/
std::optional<Error> ex;
Goal(Worker & worker, DerivedPath path)
Goal(Worker & worker)
: worker(worker), top_co(init_wrapper())
{
// top_co shouldn't have a goal already, should be nullptr.
@ -394,10 +398,6 @@ public:
void work();
void addWaitee(GoalPtr waitee);
virtual void waiteeDone(GoalPtr waitee, ExitCode result);
virtual void handleChildOutput(Descriptor fd, std::string_view data)
{
unreachable();
@ -429,6 +429,13 @@ public:
* @see JobCategory
*/
virtual JobCategory jobCategory() const = 0;
protected:
Co await(Goals waitees);
Co waitForAWhile();
Co waitForBuildSlot();
Co yield();
};
void addToWeakGoals(WeakGoals & goals, GoalPtr p);

View file

@ -5,15 +5,30 @@
namespace nix {
// TODO: make pluggable.
void builtinFetchurl(
const BasicDerivation & drv,
const std::map<std::string, Path> & outputs,
const std::string & netrcData,
const std::string & caFileData);
struct BuiltinBuilderContext
{
const BasicDerivation & drv;
std::map<std::string, Path> outputs;
std::string netrcData;
std::string caFileData;
Path tmpDirInSandbox;
};
void builtinUnpackChannel(
const BasicDerivation & drv,
const std::map<std::string, Path> & outputs);
using BuiltinBuilder = std::function<void(const BuiltinBuilderContext &)>;
struct RegisterBuiltinBuilder
{
typedef std::map<std::string, BuiltinBuilder> BuiltinBuilders;
static BuiltinBuilders & builtinBuilders() {
static BuiltinBuilders builders;
return builders;
}
RegisterBuiltinBuilder(const std::string & name, BuiltinBuilder && fun)
{
builtinBuilders().insert_or_assign(name, std::move(fun));
}
};
}

View file

@ -45,8 +45,4 @@ typedef std::vector<Package> Packages;
void buildProfile(const Path & out, Packages && pkgs);
void builtinBuildenv(
const BasicDerivation & drv,
const std::map<std::string, Path> & outputs);
}

View file

@ -25,11 +25,11 @@ namespace nix {
LengthPrefixedProtoHelper<CommonProto, T >::write(store, conn, t); \
}
#define COMMA_ ,
COMMON_USE_LENGTH_PREFIX_SERIALISER(template<typename T>, std::vector<T>)
COMMON_USE_LENGTH_PREFIX_SERIALISER(template<typename T>, std::set<T>)
COMMON_USE_LENGTH_PREFIX_SERIALISER(template<typename T COMMA_ typename Compare>, std::set<T COMMA_ Compare>)
COMMON_USE_LENGTH_PREFIX_SERIALISER(template<typename... Ts>, std::tuple<Ts...>)
#define COMMA_ ,
COMMON_USE_LENGTH_PREFIX_SERIALISER(
template<typename K COMMA_ typename V>,
std::map<K COMMA_ V>)

View file

@ -72,14 +72,14 @@ DECLARE_COMMON_SERIALISER(DrvOutput);
template<>
DECLARE_COMMON_SERIALISER(Realisation);
#define COMMA_ ,
template<typename T>
DECLARE_COMMON_SERIALISER(std::vector<T>);
template<typename T>
DECLARE_COMMON_SERIALISER(std::set<T>);
template<typename T, typename Compare>
DECLARE_COMMON_SERIALISER(std::set<T COMMA_ Compare>);
template<typename... Ts>
DECLARE_COMMON_SERIALISER(std::tuple<Ts...>);
#define COMMA_ ,
template<typename K, typename V>
DECLARE_COMMON_SERIALISER(std::map<K COMMA_ V>);
#undef COMMA_

View file

@ -56,7 +56,7 @@ struct CommonSSHStoreConfig : virtual StoreConfig
*/
SSHMaster createSSHMaster(
bool useMaster,
Descriptor logFD = INVALID_DESCRIPTOR);
Descriptor logFD = INVALID_DESCRIPTOR) const;
};
}

View file

@ -13,16 +13,16 @@ namespace nix {
class Store;
struct BasicDerivation;
class ParsedDerivation;
struct StructuredAttrs;
/**
* This represents all the special options on a `Derivation`.
*
* Currently, these options are parsed from the environment variables
* with the aid of `ParsedDerivation`.
* with the aid of `StructuredAttrs`.
*
* The first goal of this data type is to make sure that no other code
* uses `ParsedDerivation` to ad-hoc parse some additional options. That
* uses `StructuredAttrs` to ad-hoc parse some additional options. That
* ensures this data type is up to date and fully correct.
*
* The second goal of this data type is to allow an alternative to
@ -95,6 +95,27 @@ struct DerivationOptions
*/
StringSet passAsFile;
/**
* The `exportReferencesGraph' feature allows the references graph
* to be passed to a builder
*
* ### Legacy case
*
* Given a `name` `pathSet` key-value pair, the references graph of
* `pathSet` will be stored in a text file `name' in the temporary
* build directory. The text files have the format used by
* `nix-store
* --register-validity'. However, the `deriver` fields are left
* empty.
*
* ### "Structured attributes" case
*
* The same information will be put put in the final structured
* attributes give to the builder. The set of paths in the original JSON
* is replaced with a list of `PathInfo` in JSON format.
*/
std::map<std::string, StringSet> exportReferencesGraph;
/**
* env: __sandboxProfile
*
@ -152,7 +173,8 @@ struct DerivationOptions
* (e.g. JSON) but is necessary for supporing old formats (e.g.
* ATerm).
*/
static DerivationOptions fromParsedDerivation(const ParsedDerivation & parsed, bool shouldWarn = true);
static DerivationOptions
fromStructuredAttrs(const StringMap & env, const StructuredAttrs * parsed, bool shouldWarn = true);
/**
* @param drv Must be the same derivation we parsed this from. In

View file

@ -343,7 +343,7 @@ struct Derivation : BasicDerivation
/**
* inputs that are sub-derivations
*/
DerivedPathMap<std::set<OutputName>> inputDrvs;
DerivedPathMap<std::set<OutputName, std::less<>>> inputDrvs;
/**
* Print a derivation.
@ -369,7 +369,7 @@ struct Derivation : BasicDerivation
*/
std::optional<BasicDerivation> tryResolve(
Store & store,
const std::map<std::pair<StorePath, std::string>, StorePath> & inputDrvOutputs) const;
std::function<std::optional<StorePath>(ref<const SingleDerivedPath> drvPath, const std::string & outputName)> queryResolutionChain) const;
/**
* Check that the derivation is valid and does not present any

View file

@ -91,20 +91,20 @@ struct DerivedPathMap {
};
template<>
bool DerivedPathMap<std::set<std::string>>::ChildNode::operator == (
const DerivedPathMap<std::set<std::string>>::ChildNode &) const noexcept;
bool DerivedPathMap<StringSet>::ChildNode::operator == (
const DerivedPathMap<StringSet>::ChildNode &) const noexcept;
// TODO libc++ 16 (used by darwin) missing `std::map::operator <=>`, can't do yet.
#if 0
template<>
std::strong_ordering DerivedPathMap<std::set<std::string>>::ChildNode::operator <=> (
const DerivedPathMap<std::set<std::string>>::ChildNode &) const noexcept;
std::strong_ordering DerivedPathMap<StringSet>::ChildNode::operator <=> (
const DerivedPathMap<StringSet>::ChildNode &) const noexcept;
template<>
inline auto DerivedPathMap<std::set<std::string>>::operator <=> (const DerivedPathMap<std::set<std::string>> &) const noexcept = default;
inline auto DerivedPathMap<StringSet>::operator <=> (const DerivedPathMap<StringSet> &) const noexcept = default;
#endif
extern template struct DerivedPathMap<std::set<std::string>>::ChildNode;
extern template struct DerivedPathMap<std::set<std::string>>;
extern template struct DerivedPathMap<StringSet>::ChildNode;
extern template struct DerivedPathMap<StringSet>;
}

View file

@ -45,7 +45,7 @@ struct SingleDerivedPath;
* path of the given output name.
*/
struct SingleDerivedPathBuilt {
ref<SingleDerivedPath> drvPath;
ref<const SingleDerivedPath> drvPath;
OutputName output;
/**
@ -74,7 +74,7 @@ struct SingleDerivedPathBuilt {
* @param xpSettings Stop-gap to avoid globals during unit tests.
*/
static SingleDerivedPathBuilt parse(
const StoreDirConfig & store, ref<SingleDerivedPath> drvPath,
const StoreDirConfig & store, ref<const SingleDerivedPath> drvPath,
OutputNameView outputs,
const ExperimentalFeatureSettings & xpSettings = experimentalFeatureSettings);
nlohmann::json toJSON(Store & store) const;
@ -172,7 +172,7 @@ static inline ref<SingleDerivedPath> makeConstantStorePathRef(StorePath drvPath)
* output name.
*/
struct DerivedPathBuilt {
ref<SingleDerivedPath> drvPath;
ref<const SingleDerivedPath> drvPath;
OutputsSpec outputs;
/**
@ -201,7 +201,7 @@ struct DerivedPathBuilt {
* @param xpSettings Stop-gap to avoid globals during unit tests.
*/
static DerivedPathBuilt parse(
const StoreDirConfig & store, ref<SingleDerivedPath>,
const StoreDirConfig & store, ref<const SingleDerivedPath>,
std::string_view,
const ExperimentalFeatureSettings & xpSettings = experimentalFeatureSettings);
nlohmann::json toJSON(Store & store) const;

View file

@ -30,7 +30,7 @@ struct FileTransferSettings : Config
{"binary-caches-parallel-connections"}};
Setting<unsigned long> connectTimeout{
this, 0, "connect-timeout",
this, 5, "connect-timeout",
R"(
The timeout (in seconds) for establishing connections in the
binary cache substituter. It corresponds to `curl`s
@ -58,6 +58,8 @@ struct FileTransferSettings : Config
extern FileTransferSettings fileTransferSettings;
extern const unsigned int RETRY_TIME_MS_DEFAULT;
struct FileTransferRequest
{
std::string uri;
@ -67,7 +69,7 @@ struct FileTransferRequest
bool head = false;
bool post = false;
size_t tries = fileTransferSettings.tries;
unsigned int baseRetryTimeMs = 250;
unsigned int baseRetryTimeMs = RETRY_TIME_MS_DEFAULT;
ActivityId parentAct;
bool decompress = true;
std::optional<std::string> data;

View file

@ -24,7 +24,7 @@ struct MaxBuildJobsSetting : public BaseSetting<unsigned int>
unsigned int def,
const std::string & name,
const std::string & description,
const std::set<std::string> & aliases = {})
const StringSet & aliases = {})
: BaseSetting<unsigned int>(def, true, name, description, aliases)
{
options->addSetting(this);

View file

@ -2,29 +2,27 @@
namespace nix {
struct HttpBinaryCacheStoreConfig : virtual BinaryCacheStoreConfig
struct HttpBinaryCacheStoreConfig : std::enable_shared_from_this<HttpBinaryCacheStoreConfig>,
virtual Store::Config,
BinaryCacheStoreConfig
{
using BinaryCacheStoreConfig::BinaryCacheStoreConfig;
HttpBinaryCacheStoreConfig(std::string_view scheme, std::string_view _cacheUri, const Params & params);
HttpBinaryCacheStoreConfig(
std::string_view scheme, std::string_view cacheUri, const Store::Config::Params & params);
Path cacheUri;
const std::string name() override
static const std::string name()
{
return "HTTP Binary Cache Store";
}
static std::set<std::string> uriSchemes()
{
static bool forceHttp = getEnv("_NIX_FORCE_HTTP") == "1";
auto ret = std::set<std::string>({"http", "https"});
if (forceHttp)
ret.insert("file");
return ret;
}
static StringSet uriSchemes();
std::string doc() override;
static std::string doc();
ref<Store> openStore() const override;
};
}

View file

@ -10,7 +10,7 @@
namespace nix {
struct LegacySSHStoreConfig : virtual CommonSSHStoreConfig
struct LegacySSHStoreConfig : std::enable_shared_from_this<LegacySSHStoreConfig>, virtual CommonSSHStoreConfig
{
using CommonSSHStoreConfig::CommonSSHStoreConfig;
@ -19,6 +19,15 @@ struct LegacySSHStoreConfig : virtual CommonSSHStoreConfig
std::string_view authority,
const Params & params);
#ifndef _WIN32
// Hack for getting remote build log output.
// Intentionally not in `LegacySSHStoreConfig` so that it doesn't appear in
// the documentation
const Setting<int> logFD{this, INVALID_DESCRIPTOR, "log-fd", "file descriptor to which SSH's stderr is connected"};
#else
Descriptor logFD = INVALID_DESCRIPTOR;
#endif
const Setting<Strings> remoteProgram{this, {"nix-store"}, "remote-program",
"Path to the `nix-store` executable on the remote machine."};
@ -35,23 +44,20 @@ struct LegacySSHStoreConfig : virtual CommonSSHStoreConfig
*/
std::optional<size_t> connPipeSize;
const std::string name() override { return "SSH Store"; }
static const std::string name() { return "SSH Store"; }
static std::set<std::string> uriSchemes() { return {"ssh"}; }
static StringSet uriSchemes() { return {"ssh"}; }
std::string doc() override;
static std::string doc();
ref<Store> openStore() const override;
};
struct LegacySSHStore : public virtual LegacySSHStoreConfig, public virtual Store
struct LegacySSHStore : public virtual Store
{
#ifndef _WIN32
// Hack for getting remote build log output.
// Intentionally not in `LegacySSHStoreConfig` so that it doesn't appear in
// the documentation
const Setting<int> logFD{this, INVALID_DESCRIPTOR, "log-fd", "file descriptor to which SSH's stderr is connected"};
#else
Descriptor logFD = INVALID_DESCRIPTOR;
#endif
using Config = LegacySSHStoreConfig;
ref<const Config> config;
struct Connection;
@ -59,10 +65,7 @@ struct LegacySSHStore : public virtual LegacySSHStoreConfig, public virtual Stor
SSHMaster master;
LegacySSHStore(
std::string_view scheme,
std::string_view host,
const Params & params);
LegacySSHStore(ref<const Config>);
ref<Connection> openConnection();
@ -187,10 +190,7 @@ public:
* The legacy ssh protocol doesn't support checking for trusted-user.
* Try using ssh-ng:// instead if you want to know.
*/
std::optional<TrustedFlag> isTrustedClient() override
{
return std::nullopt;
}
std::optional<TrustedFlag> isTrustedClient() override;
void queryRealisationUncached(const DrvOutput &,
Callback<std::shared_ptr<const Realisation>> callback) noexcept override

View file

@ -52,8 +52,10 @@ struct LengthPrefixedProtoHelper;
template<class Inner, typename T>
LENGTH_PREFIXED_PROTO_HELPER(Inner, std::vector<T>);
template<class Inner, typename T>
LENGTH_PREFIXED_PROTO_HELPER(Inner, std::set<T>);
#define COMMA_ ,
template<class Inner, typename T, typename Compare>
LENGTH_PREFIXED_PROTO_HELPER(Inner, std::set<T COMMA_ Compare>);
#undef COMMA_
template<class Inner, typename... Ts>
LENGTH_PREFIXED_PROTO_HELPER(Inner, std::tuple<Ts...>);
@ -86,12 +88,11 @@ LengthPrefixedProtoHelper<Inner, std::vector<T>>::write(
}
}
template<class Inner, typename T>
std::set<T>
LengthPrefixedProtoHelper<Inner, std::set<T>>::read(
template<class Inner, typename T, typename Compare>
std::set<T, Compare> LengthPrefixedProtoHelper<Inner, std::set<T, Compare>>::read(
const StoreDirConfig & store, typename Inner::ReadConn conn)
{
std::set<T> resSet;
std::set<T, Compare> resSet;
auto size = readNum<size_t>(conn.from);
while (size--) {
resSet.insert(S<T>::read(store, conn));
@ -99,10 +100,9 @@ LengthPrefixedProtoHelper<Inner, std::set<T>>::read(
return resSet;
}
template<class Inner, typename T>
void
LengthPrefixedProtoHelper<Inner, std::set<T>>::write(
const StoreDirConfig & store, typename Inner::WriteConn conn, const std::set<T> & resSet)
template<class Inner, typename T, typename Compare>
void LengthPrefixedProtoHelper<Inner, std::set<T, Compare>>::write(
const StoreDirConfig & store, typename Inner::WriteConn conn, const std::set<T, Compare> & resSet)
{
conn.to << resSet.size();
for (auto & key : resSet) {

View file

@ -2,22 +2,30 @@
namespace nix {
struct LocalBinaryCacheStoreConfig : virtual BinaryCacheStoreConfig
struct LocalBinaryCacheStoreConfig : std::enable_shared_from_this<LocalBinaryCacheStoreConfig>,
virtual Store::Config,
BinaryCacheStoreConfig
{
using BinaryCacheStoreConfig::BinaryCacheStoreConfig;
/**
* @param binaryCacheDir `file://` is a short-hand for `file:///`
* for now.
*/
LocalBinaryCacheStoreConfig(std::string_view scheme, PathView binaryCacheDir, const Params & params);
Path binaryCacheDir;
const std::string name() override
static const std::string name()
{
return "Local Binary Cache Store";
}
static std::set<std::string> uriSchemes();
static StringSet uriSchemes();
std::string doc() override;
static std::string doc();
ref<Store> openStore() const override;
};
}

View file

@ -20,36 +20,39 @@ struct LocalFSStoreConfig : virtual StoreConfig
*/
LocalFSStoreConfig(PathView path, const Params & params);
const OptionalPathSetting rootDir{this, std::nullopt,
OptionalPathSetting rootDir{this, std::nullopt,
"root",
"Directory prefixed to all other paths."};
const PathSetting stateDir{this,
PathSetting stateDir{this,
rootDir.get() ? *rootDir.get() + "/nix/var/nix" : settings.nixStateDir,
"state",
"Directory where Nix will store state."};
const PathSetting logDir{this,
PathSetting logDir{this,
rootDir.get() ? *rootDir.get() + "/nix/var/log/nix" : settings.nixLogDir,
"log",
"directory where Nix will store log files."};
const PathSetting realStoreDir{this,
PathSetting realStoreDir{this,
rootDir.get() ? *rootDir.get() + "/nix/store" : storeDir, "real",
"Physical path of the Nix store."};
};
class LocalFSStore : public virtual LocalFSStoreConfig,
public virtual Store,
public virtual GcStore,
public virtual LogStore
struct LocalFSStore :
virtual Store,
virtual GcStore,
virtual LogStore
{
public:
using Config = LocalFSStoreConfig;
const Config & config;
inline static std::string operationName = "Local Filesystem Store";
const static std::string drvsLogDir;
LocalFSStore(const Params & params);
LocalFSStore(const Config & params);
void narFromPath(const StorePath & path, Sink & sink) override;
ref<SourceAccessor> getFSAccessor(bool requireValidPath = true) override;
@ -70,7 +73,7 @@ public:
*/
virtual Path addPermRoot(const StorePath & storePath, const Path & gcRoot) = 0;
virtual Path getRealStoreDir() { return realStoreDir; }
virtual Path getRealStoreDir() { return config.realStoreDir; }
Path toRealPath(const Path & storePath) override
{

View file

@ -56,19 +56,21 @@ struct LocalOverlayStoreConfig : virtual LocalStoreConfig
The store directory is passed as an argument to the invoked executable.
)"};
const std::string name() override { return "Experimental Local Overlay Store"; }
static const std::string name() { return "Experimental Local Overlay Store"; }
std::optional<ExperimentalFeature> experimentalFeature() const override
static std::optional<ExperimentalFeature> experimentalFeature()
{
return ExperimentalFeature::LocalOverlayStore;
}
static std::set<std::string> uriSchemes()
static StringSet uriSchemes()
{
return { "local-overlay" };
}
std::string doc() override;
static std::string doc();
ref<Store> openStore() const override;
protected:
/**
@ -79,7 +81,9 @@ protected:
* at that file path. It might be stored in the lower layer instead,
* or it might not be part of this store at all.
*/
Path toUpperPath(const StorePath & path);
Path toUpperPath(const StorePath & path) const;
friend struct LocalOverlayStore;
};
/**
@ -88,8 +92,20 @@ protected:
* Documentation on overridden methods states how they differ from their
* `LocalStore` counterparts.
*/
class LocalOverlayStore : public virtual LocalOverlayStoreConfig, public virtual LocalStore
struct LocalOverlayStore : virtual LocalStore
{
using Config = LocalOverlayStoreConfig;
ref<const Config> config;
LocalOverlayStore(ref<const Config>);
std::string getUri() override
{
return "local-overlay://";
}
private:
/**
* The store beneath us.
*
@ -99,20 +115,6 @@ class LocalOverlayStore : public virtual LocalOverlayStoreConfig, public virtual
*/
ref<LocalFSStore> lowerStore;
public:
LocalOverlayStore(const Params & params)
: LocalOverlayStore("local-overlay", "", params)
{
}
LocalOverlayStore(std::string_view scheme, PathView path, const Params & params);
std::string getUri() override
{
return "local-overlay://";
}
private:
/**
* First copy up any lower store realisation with the same key, so we
* merge rather than mask it.

View file

@ -34,7 +34,7 @@ struct OptimiseStats
uint64_t bytesFreed = 0;
};
struct LocalStoreConfig : virtual LocalFSStoreConfig
struct LocalStoreConfig : std::enable_shared_from_this<LocalStoreConfig>, virtual LocalFSStoreConfig
{
using LocalFSStoreConfig::LocalFSStoreConfig;
@ -65,18 +65,26 @@ struct LocalStoreConfig : virtual LocalFSStoreConfig
> While the filesystem the database resides on might appear to be read-only, consider whether another user or system might have write access to it.
)"};
const std::string name() override { return "Local Store"; }
static const std::string name() { return "Local Store"; }
static std::set<std::string> uriSchemes()
static StringSet uriSchemes()
{ return {"local"}; }
std::string doc() override;
static std::string doc();
ref<Store> openStore() const override;
};
class LocalStore : public virtual LocalStoreConfig
, public virtual IndirectRootStore
, public virtual GcStore
class LocalStore :
public virtual IndirectRootStore,
public virtual GcStore
{
public:
using Config = LocalStoreConfig;
ref<const LocalStoreConfig> config;
private:
/**
@ -144,11 +152,7 @@ public:
* Initialise the local store, upgrading the schema if
* necessary.
*/
LocalStore(const Params & params);
LocalStore(
std::string_view scheme,
PathView path,
const Params & params);
LocalStore(ref<const Config> params);
~LocalStore();
@ -396,16 +400,8 @@ private:
bool isValidPath_(State & state, const StorePath & path);
void queryReferrers(State & state, const StorePath & path, StorePathSet & referrers);
/**
* Add signatures to a ValidPathInfo or Realisation using the secret keys
* specified by the secret-key-files option.
*/
void signPathInfo(ValidPathInfo & info);
void signRealisation(Realisation &);
void addBuildLog(const StorePath & drvPath, std::string_view log) override;
friend struct LocalDerivationGoal;
friend struct PathSubstitutionGoal;
friend struct SubstitutionGoal;
friend struct DerivationGoal;

View file

@ -15,12 +15,12 @@ typedef std::vector<Machine> Machines;
struct Machine {
const StoreReference storeUri;
const std::set<std::string> systemTypes;
const StringSet systemTypes;
const std::string sshKey;
const unsigned int maxJobs;
const float speedFactor;
const std::set<std::string> supportedFeatures;
const std::set<std::string> mandatoryFeatures;
const StringSet supportedFeatures;
const StringSet mandatoryFeatures;
const std::string sshPublicHostKey;
bool enabled = true;
@ -34,12 +34,12 @@ struct Machine {
* @return Whether `features` is a subset of the union of `supportedFeatures` and
* `mandatoryFeatures`.
*/
bool allSupported(const std::set<std::string> & features) const;
bool allSupported(const StringSet & features) const;
/**
* @return Whether `mandatoryFeatures` is a subset of `features`.
*/
bool mandatoryMet(const std::set<std::string> & features) const;
bool mandatoryMet(const StringSet & features) const;
Machine(
const std::string & storeUri,
@ -75,7 +75,7 @@ struct Machine {
* with `@` are interpreted as paths to other configuration files in
* the same format.
*/
static Machines parseConfig(const std::set<std::string> & defaultSystems, const std::string & config);
static Machines parseConfig(const StringSet & defaultSystems, const std::string & config);
};
/**

View file

@ -13,6 +13,7 @@ headers = [config_pub_h] + files(
'binary-cache-store.hh',
'build-result.hh',
'build/derivation-goal.hh',
'build/derivation-building-misc.hh',
'build/drv-output-substitution-goal.hh',
'build/goal.hh',
'build/substitution-goal.hh',
@ -62,18 +63,21 @@ headers = [config_pub_h] + files(
'remote-fs-accessor.hh',
'remote-store-connection.hh',
'remote-store.hh',
'restricted-store.hh',
's3-binary-cache-store.hh',
's3.hh',
'ssh-store.hh',
'serve-protocol-connection.hh',
'serve-protocol-impl.hh',
'serve-protocol.hh',
'sqlite.hh',
'ssh-store.hh',
'ssh.hh',
'store-api.hh',
'store-cast.hh',
'store-dir-config.hh',
'store-open.hh',
'store-reference.hh',
'store-registration.hh',
'uds-remote-store.hh',
'worker-protocol-connection.hh',
'worker-protocol-impl.hh',

View file

@ -27,20 +27,24 @@ struct OutputsSpec {
/**
* A non-empty set of outputs, specified by name
*/
struct Names : std::set<OutputName> {
using std::set<OutputName>::set;
struct Names : std::set<OutputName, std::less<>> {
private:
using BaseType = std::set<OutputName, std::less<>>;
public:
using BaseType::BaseType;
/* These need to be "inherited manually" */
Names(const std::set<OutputName> & s)
: std::set<OutputName>(s)
Names(const BaseType & s)
: BaseType(s)
{ assert(!empty()); }
/**
* Needs to be "inherited manually"
*/
Names(std::set<OutputName> && s)
: std::set<OutputName>(s)
Names(BaseType && s)
: BaseType(std::move(s))
{ assert(!empty()); }
/* This set should always be non-empty, so we delete this

View file

@ -1,51 +1,43 @@
#pragma once
///@file
#include "nix/store/derivations.hh"
#include "nix/store/store-api.hh"
#include <nlohmann/json.hpp>
#include <nlohmann/json_fwd.hpp>
#include "nix/util/types.hh"
#include "nix/store/path.hh"
namespace nix {
class Store;
struct DerivationOptions;
struct DerivationOutput;
class ParsedDerivation
typedef std::map<std::string, DerivationOutput> DerivationOutputs;
struct StructuredAttrs
{
StorePath drvPath;
BasicDerivation & drv;
std::unique_ptr<nlohmann::json> structuredAttrs;
nlohmann::json structuredAttrs;
std::optional<std::string> getStringAttr(const std::string & name) const;
static std::optional<StructuredAttrs> tryParse(const StringPairs & env);
bool getBoolAttr(const std::string & name, bool def = false) const;
std::optional<Strings> getStringsAttr(const std::string & name) const;
std::optional<StringSet> getStringSetAttr(const std::string & name) const;
nlohmann::json prepareStructuredAttrs(
Store & store,
const DerivationOptions & drvOptions,
const StorePathSet & inputPaths,
const DerivationOutputs & outputs) const;
/**
* Only `DerivationOptions` is allowed to parse individual fields
* from `ParsedDerivation`. This ensure that it includes all
* derivation options, and, the likes of `LocalDerivationGoal` are
* incapable of more ad-hoc options.
* As a convenience to bash scripts, write a shell file that
* maps all attributes that are representable in bash -
* namely, strings, integers, nulls, Booleans, and arrays and
* objects consisting entirely of those values. (So nested
* arrays or objects are not supported.)
*
* @param prepared This should be the result of
* `prepareStructuredAttrs`, *not* the original `structuredAttrs`
* field.
*/
friend struct DerivationOptions;
public:
ParsedDerivation(const StorePath & drvPath, BasicDerivation & drv);
~ParsedDerivation();
bool hasStructuredAttrs() const
{
return static_cast<bool>(structuredAttrs);
}
std::optional<nlohmann::json> prepareStructuredAttrs(Store & store, const StorePathSet & inputPaths);
static std::string writeShell(const nlohmann::json & prepared);
};
std::string writeStructuredAttrsShell(const nlohmann::json & json);
}

View file

@ -144,6 +144,7 @@ struct ValidPathInfo : UnkeyedValidPathInfo {
std::string fingerprint(const Store & store) const;
void sign(const Store & store, const Signer & signer);
void sign(const Store & store, const std::vector<std::unique_ptr<Signer>> & signers);
/**
* @return The `ContentAddressWithReferences` that determines the

View file

@ -19,7 +19,7 @@ struct StoreDirConfig;
struct StorePathWithOutputs
{
StorePath path;
std::set<std::string> outputs;
StringSet outputs;
std::string to_string(const StoreDirConfig & store) const;

View file

@ -86,7 +86,7 @@ typedef std::list<Generation> Generations;
*/
std::pair<Generations, std::optional<GenerationNumber>> findGenerations(Path profile);
class LocalFSStore;
struct LocalFSStore;
/**
* Create a new generation of the given profile

View file

@ -19,7 +19,7 @@ class RemoteFSAccessor : public SourceAccessor
std::pair<ref<SourceAccessor>, CanonPath> fetch(const CanonPath & path);
friend class BinaryCacheStore;
friend struct BinaryCacheStore;
Path makeCacheFile(std::string_view hashPart, const std::string & ext);

View file

@ -35,14 +35,16 @@ struct RemoteStoreConfig : virtual StoreConfig
* \todo RemoteStore is a misnomer - should be something like
* DaemonStore.
*/
class RemoteStore : public virtual RemoteStoreConfig,
struct RemoteStore :
public virtual Store,
public virtual GcStore,
public virtual LogStore
{
public:
using Config = RemoteStoreConfig;
RemoteStore(const Params & params);
const Config & config;
RemoteStore(const Config & config);
/* Implementations of abstract store API methods. */

View file

@ -0,0 +1,60 @@
#pragma once
///@file
#include "nix/store/local-store.hh"
namespace nix {
/**
* A restricted store has a pointer to one of these, which manages the
* restrictions that are in place.
*
* This is a separate data type so the whitelists can be mutated before
* the restricted store is created: put differently, someones we don't
* know whether we will in fact create a restricted store, but we need
* to prepare the whitelists just in case.
*
* It is possible there are other ways to solve this problem. This was
* just the easiest place to begin, when this was extracted from
* `LocalDerivationGoal`.
*/
struct RestrictionContext
{
/**
* Paths that are already allowed to begin with
*/
virtual const StorePathSet & originalPaths() = 0;
/**
* Paths that were added via recursive Nix calls.
*/
StorePathSet addedPaths;
/**
* Realisations that were added via recursive Nix calls.
*/
std::set<DrvOutput> addedDrvOutputs;
/**
* Recursive Nix calls are only allowed to build or realize paths
* in the original input closure or added via a recursive Nix call
* (so e.g. you can't do 'nix-store -r /nix/store/<bla>' where
* /nix/store/<bla> is some arbitrary path in a binary cache).
*/
virtual bool isAllowed(const StorePath &) = 0;
virtual bool isAllowed(const DrvOutput & id) = 0;
bool isAllowed(const DerivedPath & id);
/**
* Add 'path' to the set of paths that may be referenced by the
* outputs, and make it appear in the sandbox.
*/
virtual void addDependency(const StorePath & path) = 0;
};
/**
* Create a shared pointer to a restricted store.
*/
ref<Store> makeRestrictedStore(ref<LocalStore::Config> config, ref<LocalStore> next, RestrictionContext & context);
}

View file

@ -11,7 +11,7 @@
namespace nix {
struct S3BinaryCacheStoreConfig : virtual BinaryCacheStoreConfig
struct S3BinaryCacheStoreConfig : std::enable_shared_from_this<S3BinaryCacheStoreConfig>, virtual BinaryCacheStoreConfig
{
std::string bucketName;
@ -93,26 +93,28 @@ public:
const Setting<uint64_t> bufferSize{
this, 5 * 1024 * 1024, "buffer-size", "Size (in bytes) of each part in multi-part uploads."};
const std::string name() override
static const std::string name()
{
return "S3 Binary Cache Store";
}
static std::set<std::string> uriSchemes()
static StringSet uriSchemes()
{
return {"s3"};
}
std::string doc() override;
static std::string doc();
ref<Store> openStore() const override;
};
class S3BinaryCacheStore : public virtual BinaryCacheStore
struct S3BinaryCacheStore : virtual BinaryCacheStore
{
protected:
using Config = S3BinaryCacheStoreConfig;
S3BinaryCacheStore(const Params & params);
ref<Config> config;
public:
S3BinaryCacheStore(ref<Config>);
struct Stats
{

View file

@ -26,7 +26,9 @@ namespace nix {
}
SERVE_USE_LENGTH_PREFIX_SERIALISER(template<typename T>, std::vector<T>)
SERVE_USE_LENGTH_PREFIX_SERIALISER(template<typename T>, std::set<T>)
#define COMMA_ ,
SERVE_USE_LENGTH_PREFIX_SERIALISER(template<typename T COMMA_ typename Compare>, std::set<T COMMA_ Compare>)
#undef COMMA_
SERVE_USE_LENGTH_PREFIX_SERIALISER(template<typename... Ts>, std::tuple<Ts...>)
#define SERVE_USE_LENGTH_PREFIX_SERIALISER_COMMA ,

View file

@ -180,12 +180,12 @@ DECLARE_SERVE_SERIALISER(ServeProto::BuildOptions);
template<typename T>
DECLARE_SERVE_SERIALISER(std::vector<T>);
template<typename T>
DECLARE_SERVE_SERIALISER(std::set<T>);
#define COMMA_ ,
template<typename T, typename Compare>
DECLARE_SERVE_SERIALISER(std::set<T COMMA_ Compare>);
template<typename... Ts>
DECLARE_SERVE_SERIALISER(std::tuple<Ts...>);
#define COMMA_ ,
template<typename K, typename V>
DECLARE_SERVE_SERIALISER(std::map<K COMMA_ V>);
#undef COMMA_

View file

@ -8,7 +8,9 @@
namespace nix {
struct SSHStoreConfig : virtual RemoteStoreConfig, virtual CommonSSHStoreConfig
struct SSHStoreConfig : std::enable_shared_from_this<SSHStoreConfig>,
virtual RemoteStoreConfig,
virtual CommonSSHStoreConfig
{
using CommonSSHStoreConfig::CommonSSHStoreConfig;
using RemoteStoreConfig::RemoteStoreConfig;
@ -18,44 +20,44 @@ struct SSHStoreConfig : virtual RemoteStoreConfig, virtual CommonSSHStoreConfig
const Setting<Strings> remoteProgram{
this, {"nix-daemon"}, "remote-program", "Path to the `nix-daemon` executable on the remote machine."};
const std::string name() override
static const std::string name()
{
return "Experimental SSH Store";
}
static std::set<std::string> uriSchemes()
static StringSet uriSchemes()
{
return {"ssh-ng"};
}
std::string doc() override;
static std::string doc();
ref<Store> openStore() const override;
};
struct MountedSSHStoreConfig : virtual SSHStoreConfig, virtual LocalFSStoreConfig
{
using LocalFSStoreConfig::LocalFSStoreConfig;
using SSHStoreConfig::SSHStoreConfig;
MountedSSHStoreConfig(StringMap params);
MountedSSHStoreConfig(std::string_view scheme, std::string_view host, StringMap params);
const std::string name() override
static const std::string name()
{
return "Experimental SSH Store with filesystem mounted";
}
static std::set<std::string> uriSchemes()
static StringSet uriSchemes()
{
return {"mounted-ssh-ng"};
}
std::string doc() override;
static std::string doc();
std::optional<ExperimentalFeature> experimentalFeature() const override
static std::optional<ExperimentalFeature> experimentalFeature()
{
return ExperimentalFeature::MountedSSHStore;
}
ref<Store> openStore() const override;
};
}

View file

@ -26,32 +26,6 @@
namespace nix {
/**
* About the class hierarchy of the store types:
*
* Each store type `Foo` consists of two classes:
*
* 1. A class `FooConfig : virtual StoreConfig` that contains the configuration
* for the store
*
* It should only contain members of type `const Setting<T>` (or subclasses
* of it) and inherit the constructors of `StoreConfig`
* (`using StoreConfig::StoreConfig`).
*
* 2. A class `Foo : virtual Store, virtual FooConfig` that contains the
* implementation of the store.
*
* This class is expected to have a constructor `Foo(const Params & params)`
* that calls `StoreConfig(params)` (otherwise you're gonna encounter an
* `assertion failure` when trying to instantiate it).
*
* You can then register the new store using:
*
* ```
* cpp static RegisterStoreImplementation<Foo, FooConfig> regStore;
* ```
*/
MakeError(SubstError, Error);
/**
* denotes a permanent build failure
@ -97,27 +71,48 @@ struct KeyedBuildResult;
typedef std::map<StorePath, std::optional<ContentAddress>> StorePathCAMap;
/**
* About the class hierarchy of the store types:
*
* Each store type `Foo` consists of two classes:
*
* 1. A class `FooConfig : virtual StoreConfig` that contains the configuration
* for the store
*
* It should only contain members of type `Setting<T>` (or subclasses
* of it) and inherit the constructors of `StoreConfig`
* (`using StoreConfig::StoreConfig`).
*
* 2. A class `Foo : virtual Store` that contains the
* implementation of the store.
*
* This class is expected to have:
*
* 1. an alias `using Config = FooConfig;`
*
* 2. a constructor `Foo(ref<const Config> params)`.
*
* You can then register the new store using:
*
* ```
* cpp static RegisterStoreImplementation<FooConfig> regStore;
* ```
*/
struct StoreConfig : public StoreDirConfig
{
using Params = StoreReference::Params;
using StoreDirConfig::StoreDirConfig;
StoreConfig() = delete;
static StringSet getDefaultSystemFeatures();
virtual ~StoreConfig() { }
/**
* The name of this type of store.
*/
virtual const std::string name() = 0;
static StringSet getDefaultSystemFeatures();
/**
* Documentation for this type of store.
*/
virtual std::string doc()
static std::string doc()
{
return "";
}
@ -126,15 +121,15 @@ struct StoreConfig : public StoreDirConfig
* An experimental feature this type store is gated, if it is to be
* experimental.
*/
virtual std::optional<ExperimentalFeature> experimentalFeature() const
static std::optional<ExperimentalFeature> experimentalFeature()
{
return std::nullopt;
}
const Setting<int> pathInfoCacheSize{this, 65536, "path-info-cache-size",
Setting<int> pathInfoCacheSize{this, 65536, "path-info-cache-size",
"Size of the in-memory store path metadata cache."};
const Setting<bool> isTrusted{this, false, "trusted",
Setting<bool> isTrusted{this, false, "trusted",
R"(
Whether paths from this store can be used as substitutes
even if they are not signed by a key listed in the
@ -163,10 +158,38 @@ struct StoreConfig : public StoreDirConfig
{},
// Don't document the machine-specific default value
false};
/**
* Open a store of the type corresponding to this configuration
* type.
*/
virtual ref<Store> openStore() const = 0;
};
class Store : public std::enable_shared_from_this<Store>, public virtual StoreConfig
/**
* A Store (client)
*
* This is an interface type allowing for create and read operations on
* a collection of store objects, and also building new store objects
* from `Derivation`s. See the manual for further details.
*
* "client" used is because this is just one view/actor onto an
* underlying resource, which could be an external process (daemon
* server), file system state, etc.
*/
class Store : public std::enable_shared_from_this<Store>, public MixStoreDirMethods
{
public:
using Config = StoreConfig;
const Config & config;
/**
* @note Avoid churn, since we used to inherit from `Config`.
*/
operator const Config &() const { return config; }
protected:
struct PathInfoCacheValue {
@ -205,7 +228,7 @@ protected:
std::shared_ptr<NarInfoDiskCache> diskCache;
Store(const Params & params);
Store(const Store::Config & config);
public:
/**
@ -622,6 +645,14 @@ public:
virtual void addSignatures(const StorePath & storePath, const StringSet & sigs)
{ unsupported("addSignatures"); }
/**
* Add signatures to a ValidPathInfo or Realisation using the secret keys
* specified by the secret-key-files option.
*/
void signPathInfo(ValidPathInfo & info);
void signRealisation(Realisation &);
/* Utility functions. */
/**
@ -857,74 +888,6 @@ StorePath resolveDerivedPath(Store &, const SingleDerivedPath &, Store * evalSto
OutputPathMap resolveDerivedPath(Store &, const DerivedPath::Built &, Store * evalStore = nullptr);
/**
* @return a Store object to access the Nix store denoted by
* uri (slight misnomer...).
*/
ref<Store> openStore(StoreReference && storeURI);
/**
* Opens the store at `uri`, where `uri` is in the format expected by `StoreReference::parse`
*/
ref<Store> openStore(const std::string & uri = settings.storeUri.get(),
const Store::Params & extraParams = Store::Params());
/**
* @return the default substituter stores, defined by the
* substituters option and various legacy options.
*/
std::list<ref<Store>> getDefaultSubstituters();
struct StoreFactory
{
std::set<std::string> uriSchemes;
/**
* The `authorityPath` parameter is `<authority>/<path>`, or really
* whatever comes after `<scheme>://` and before `?<query-params>`.
*/
std::function<std::shared_ptr<Store> (
std::string_view scheme,
std::string_view authorityPath,
const Store::Params & params)> create;
std::function<std::shared_ptr<StoreConfig> ()> getConfig;
};
struct Implementations
{
static std::vector<StoreFactory> * registered;
template<typename T, typename TConfig>
static void add()
{
if (!registered) registered = new std::vector<StoreFactory>();
StoreFactory factory{
.uriSchemes = TConfig::uriSchemes(),
.create =
([](auto scheme, auto uri, auto & params)
-> std::shared_ptr<Store>
{ return std::make_shared<T>(scheme, uri, params); }),
.getConfig =
([]()
-> std::shared_ptr<StoreConfig>
{ return std::make_shared<TConfig>(StringMap({})); })
};
registered->push_back(factory);
}
};
template<typename T, typename TConfig>
struct RegisterStoreImplementation
{
RegisterStoreImplementation()
{
Implementations::add<T, TConfig>();
}
};
/**
* Display a set of paths in human-readable form (i.e., between quotes
* and separated by commas).

View file

@ -18,22 +18,18 @@ struct SourcePath;
MakeError(BadStorePath, Error);
MakeError(BadStorePathName, BadStorePath);
struct StoreDirConfig : public Config
/**
* @todo This should just be part of `StoreDirConfig`. However, it would
* be a huge amount of churn if `Store` didn't have these methods
* anymore, forcing a bunch of code to go from `store.method(...)` to
* `store.config.method(...)`.
*
* So we instead pull out the methods into their own mix-in, so can put
* them directly on the Store too.
*/
struct MixStoreDirMethods
{
using Config::Config;
StoreDirConfig() = delete;
virtual ~StoreDirConfig() = default;
const PathSetting storeDir_{this, settings.nixStore,
"store",
R"(
Logical location of the Nix store, usually
`/nix/store`. Note that you can only copy store paths
between stores if they have the same `store` setting.
)"};
const Path storeDir = storeDir_;
const Path & storeDir;
// pure methods
@ -56,7 +52,7 @@ struct StoreDirConfig : public Config
* Display a set of paths in human-readable form (i.e., between quotes
* and separated by commas).
*/
std::string showPaths(const StorePathSet & paths);
std::string showPaths(const StorePathSet & paths) const;
/**
* @return true if *path* is in the Nix store (but not the Nix
@ -104,4 +100,38 @@ struct StoreDirConfig : public Config
PathFilter & filter = defaultPathFilter) const;
};
/**
* Need to make this a separate class so I can get the right
* initialization order in the constructor for `StoreDirConfig`.
*/
struct StoreDirConfigBase : Config
{
using Config::Config;
const PathSetting storeDir_{this, settings.nixStore,
"store",
R"(
Logical location of the Nix store, usually
`/nix/store`. Note that you can only copy store paths
between stores if they have the same `store` setting.
)"};
};
/**
* The order of `StoreDirConfigBase` and then `MixStoreDirMethods` is
* very important. This ensures that `StoreDirConfigBase::storeDir_`
* is initialized before we have our one chance (because references are
* immutable) to initialize `MixStoreDirMethods::storeDir`.
*/
struct StoreDirConfig : StoreDirConfigBase, MixStoreDirMethods
{
using Params = std::map<std::string, std::string>;
StoreDirConfig(const Params & params);
StoreDirConfig() = delete;
virtual ~StoreDirConfig() = default;
};
}

View file

@ -0,0 +1,43 @@
#pragma once
/**
* @file
*
* For opening a store described by an `StoreReference`, which is an "untyped"
* notion which needs to be decoded against a collection of specific
* implementations.
*
* For consumers of the store registration machinery defined in
* `store-registration.hh`. Not needed by store implementation definitions, or
* usages of a given `Store` which will be passed in.
*/
#include "nix/store/store-api.hh"
namespace nix {
/**
* @return The store config denoted by `storeURI` (slight misnomer...).
*/
ref<StoreConfig> resolveStoreConfig(StoreReference && storeURI);
/**
* @return a Store object to access the Nix store denoted by
* uri (slight misnomer...).
*/
ref<Store> openStore(StoreReference && storeURI);
/**
* Opens the store at `uri`, where `uri` is in the format expected by
* `StoreReference::parse`
*/
ref<Store> openStore(
const std::string & uri = settings.storeUri.get(),
const StoreReference::Params & extraParams = StoreReference::Params());
/**
* @return the default substituter stores, defined by the
* substituters option and various legacy options.
*/
std::list<ref<Store>> getDefaultSubstituters();
}

View file

@ -0,0 +1,88 @@
#pragma once
/**
* @file
*
* Infrastructure for "registering" store implementations. Used by the
* store implementation definitions themselves but not by consumers of
* those implementations.
*
* Consumers of an arbitrary store from a URL/JSON configuration instead
* just need the defintions `nix/store/store-open.hh`; those do use this
* but only as an implementation. Consumers of a specific extra type of
* store can skip both these, and just use the definition of the store
* in question directly.
*/
#include "nix/store/store-api.hh"
namespace nix {
struct StoreFactory
{
/**
* Documentation for this type of store.
*/
std::string doc;
/**
* URIs with these schemes should be handled by this factory
*/
StringSet uriSchemes;
/**
* An experimental feature this type store is gated, if it is to be
* experimental.
*/
std::optional<ExperimentalFeature> experimentalFeature;
/**
* The `authorityPath` parameter is `<authority>/<path>`, or really
* whatever comes after `<scheme>://` and before `?<query-params>`.
*/
std::function<ref<StoreConfig>(
std::string_view scheme, std::string_view authorityPath, const Store::Config::Params & params)>
parseConfig;
/**
* Just for dumping the defaults. Kind of awkward this exists,
* because it means we cannot require fields to be manually
* specified so easily.
*/
std::function<ref<StoreConfig>()> getConfig;
};
struct Implementations
{
using Map = std::map<std::string, StoreFactory>;
static Map & registered();
template<typename TConfig>
static void add()
{
StoreFactory factory{
.doc = TConfig::doc(),
.uriSchemes = TConfig::uriSchemes(),
.experimentalFeature = TConfig::experimentalFeature(),
.parseConfig = ([](auto scheme, auto uri, auto & params) -> ref<StoreConfig> {
return make_ref<TConfig>(scheme, uri, params);
}),
.getConfig = ([]() -> ref<StoreConfig> { return make_ref<TConfig>(Store::Config::Params{}); }),
};
auto [it, didInsert] = registered().insert({TConfig::name(), std::move(factory)});
if (!didInsert) {
throw Error("Already registred store with name '%s'", it->first);
}
}
};
template<typename TConfig>
struct RegisterStoreImplementation
{
RegisterStoreImplementation()
{
Implementations::add<TConfig>();
}
};
}

View file

@ -7,7 +7,10 @@
namespace nix {
struct UDSRemoteStoreConfig : virtual LocalFSStoreConfig, virtual RemoteStoreConfig
struct UDSRemoteStoreConfig :
std::enable_shared_from_this<UDSRemoteStoreConfig>,
virtual LocalFSStoreConfig,
virtual RemoteStoreConfig
{
// TODO(fzakaria): Delete this constructor once moved over to the factory pattern
// outlined in https://github.com/NixOS/nix/issues/10766
@ -22,9 +25,11 @@ struct UDSRemoteStoreConfig : virtual LocalFSStoreConfig, virtual RemoteStoreCon
std::string_view authority,
const Params & params);
const std::string name() override { return "Local Daemon Store"; }
UDSRemoteStoreConfig(const Params & params);
std::string doc() override;
static const std::string name() { return "Local Daemon Store"; }
static std::string doc();
/**
* The path to the unix domain socket.
@ -34,32 +39,21 @@ struct UDSRemoteStoreConfig : virtual LocalFSStoreConfig, virtual RemoteStoreCon
*/
Path path;
protected:
static constexpr char const * scheme = "unix";
static StringSet uriSchemes()
{ return {"unix"}; }
public:
static std::set<std::string> uriSchemes()
{ return {scheme}; }
ref<Store> openStore() const override;
};
class UDSRemoteStore : public virtual UDSRemoteStoreConfig
, public virtual IndirectRootStore
, public virtual RemoteStore
struct UDSRemoteStore :
virtual IndirectRootStore,
virtual RemoteStore
{
public:
using Config = UDSRemoteStoreConfig;
/**
* @deprecated This is the old API to construct the store.
*/
UDSRemoteStore(const Params & params);
ref<const Config> config;
/**
* @param authority is the socket path.
*/
UDSRemoteStore(
std::string_view scheme,
std::string_view authority,
const Params & params);
UDSRemoteStore(ref<const Config>);
std::string getUri() override;

View file

@ -26,7 +26,7 @@ struct WorkerProto::BasicConnection
/**
* The set of features that both sides support.
*/
std::set<Feature> features;
FeatureSet features;
/**
* Coercion to `WorkerProto::ReadConn`. This makes it easy to use the
@ -92,11 +92,8 @@ struct WorkerProto::BasicClientConnection : WorkerProto::BasicConnection
* @param supportedFeatures The protocol features that we support.
*/
// FIXME: this should probably be a constructor.
static std::tuple<Version, std::set<Feature>> handshake(
BufferedSink & to,
Source & from,
WorkerProto::Version localVersion,
const std::set<Feature> & supportedFeatures);
static std::tuple<Version, FeatureSet> handshake(
BufferedSink & to, Source & from, WorkerProto::Version localVersion, const FeatureSet & supportedFeatures);
/**
* After calling handshake, must call this to exchange some basic
@ -155,11 +152,8 @@ struct WorkerProto::BasicServerConnection : WorkerProto::BasicConnection
* @param supportedFeatures The protocol features that we support.
*/
// FIXME: this should probably be a constructor.
static std::tuple<Version, std::set<Feature>> handshake(
BufferedSink & to,
Source & from,
WorkerProto::Version localVersion,
const std::set<Feature> & supportedFeatures);
static std::tuple<Version, FeatureSet> handshake(
BufferedSink & to, Source & from, WorkerProto::Version localVersion, const FeatureSet & supportedFeatures);
/**
* After calling handshake, must call this to exchange some basic

View file

@ -26,7 +26,9 @@ namespace nix {
}
WORKER_USE_LENGTH_PREFIX_SERIALISER(template<typename T>, std::vector<T>)
WORKER_USE_LENGTH_PREFIX_SERIALISER(template<typename T>, std::set<T>)
#define COMMA_ ,
WORKER_USE_LENGTH_PREFIX_SERIALISER(template<typename T COMMA_ typename Compare>, std::set<T COMMA_ Compare>)
#undef COMMA_
WORKER_USE_LENGTH_PREFIX_SERIALISER(template<typename... Ts>, std::tuple<Ts...>)
#define WORKER_USE_LENGTH_PREFIX_SERIALISER_COMMA ,

View file

@ -135,8 +135,9 @@ struct WorkerProto
}
using Feature = std::string;
using FeatureSet = std::set<Feature, std::less<>>;
static const std::set<Feature> allFeatures;
static const FeatureSet allFeatures;
};
enum struct WorkerProto::Op : uint64_t
@ -272,12 +273,12 @@ DECLARE_WORKER_SERIALISER(WorkerProto::ClientHandshakeInfo);
template<typename T>
DECLARE_WORKER_SERIALISER(std::vector<T>);
template<typename T>
DECLARE_WORKER_SERIALISER(std::set<T>);
#define COMMA_ ,
template<typename T, typename Compare>
DECLARE_WORKER_SERIALISER(std::set<T COMMA_ Compare>);
template<typename... Ts>
DECLARE_WORKER_SERIALISER(std::tuple<Ts...>);
#define COMMA_ ,
template<typename K, typename V>
DECLARE_WORKER_SERIALISER(std::map<K COMMA_ V>);
#undef COMMA_

View file

@ -12,6 +12,7 @@
#include "nix/store/ssh.hh"
#include "nix/store/derivations.hh"
#include "nix/util/callback.hh"
#include "nix/store/store-registration.hh"
namespace nix {
@ -38,23 +39,19 @@ struct LegacySSHStore::Connection : public ServeProto::BasicClientConnection
bool good = true;
};
LegacySSHStore::LegacySSHStore(
std::string_view scheme,
std::string_view host,
const Params & params)
: StoreConfig(params)
, CommonSSHStoreConfig(scheme, host, params)
, LegacySSHStoreConfig(scheme, host, params)
, Store(params)
LegacySSHStore::LegacySSHStore(ref<const Config> config)
: Store{*config}
, config{config}
, connections(make_ref<Pool<Connection>>(
std::max(1, (int) maxConnections),
std::max(1, (int) config->maxConnections),
[this]() { return openConnection(); },
[](const ref<Connection> & r) { return r->good; }
))
, master(createSSHMaster(
, master(config->createSSHMaster(
// Use SSH master only if using more than 1 connection.
connections->capacity() > 1,
logFD))
config->logFD))
{
}
@ -62,16 +59,16 @@ LegacySSHStore::LegacySSHStore(
ref<LegacySSHStore::Connection> LegacySSHStore::openConnection()
{
auto conn = make_ref<Connection>();
Strings command = remoteProgram.get();
Strings command = config->remoteProgram.get();
command.push_back("--serve");
command.push_back("--write");
if (remoteStore.get() != "") {
if (config->remoteStore.get() != "") {
command.push_back("--store");
command.push_back(remoteStore.get());
command.push_back(config->remoteStore.get());
}
conn->sshConn = master.startCommand(std::move(command), std::list{extraSshArgs});
if (connPipeSize) {
conn->sshConn->trySetBufferSize(*connPipeSize);
conn->sshConn = master.startCommand(std::move(command), std::list{config->extraSshArgs});
if (config->connPipeSize) {
conn->sshConn->trySetBufferSize(*config->connPipeSize);
}
conn->to = FdSink(conn->sshConn->in.get());
conn->from = FdSource(conn->sshConn->out.get());
@ -80,7 +77,7 @@ ref<LegacySSHStore::Connection> LegacySSHStore::openConnection()
TeeSource tee(conn->from, saved);
try {
conn->remoteVersion = ServeProto::BasicClientConnection::handshake(
conn->to, tee, SERVE_PROTOCOL_VERSION, host);
conn->to, tee, SERVE_PROTOCOL_VERSION, config->host);
} catch (SerialisationError & e) {
// in.close(): Don't let the remote block on us not writing.
conn->sshConn->in.close();
@ -89,9 +86,9 @@ ref<LegacySSHStore::Connection> LegacySSHStore::openConnection()
tee.drainInto(nullSink);
}
throw Error("'nix-store --serve' protocol mismatch from '%s', got '%s'",
host, chomp(saved.s));
config->host, chomp(saved.s));
} catch (EndOfFile & e) {
throw Error("cannot connect to '%1%'", host);
throw Error("cannot connect to '%1%'", config->host);
}
return conn;
@ -100,7 +97,7 @@ ref<LegacySSHStore::Connection> LegacySSHStore::openConnection()
std::string LegacySSHStore::getUri()
{
return *uriSchemes().begin() + "://" + host;
return *Config::uriSchemes().begin() + "://" + config->host;
}
std::map<StorePath, UnkeyedValidPathInfo> LegacySSHStore::queryPathInfosUncached(
@ -111,7 +108,7 @@ std::map<StorePath, UnkeyedValidPathInfo> LegacySSHStore::queryPathInfosUncached
/* No longer support missing NAR hash */
assert(GET_PROTOCOL_MINOR(conn->remoteVersion) >= 4);
debug("querying remote host '%s' for info on '%s'", host, concatStringsSep(", ", printStorePathSet(paths)));
debug("querying remote host '%s' for info on '%s'", config->host, concatStringsSep(", ", printStorePathSet(paths)));
auto infos = conn->queryPathInfos(*this, paths);
@ -151,7 +148,7 @@ void LegacySSHStore::queryPathInfoUncached(const StorePath & path,
void LegacySSHStore::addToStore(const ValidPathInfo & info, Source & source,
RepairFlag repair, CheckSigsFlag checkSigs)
{
debug("adding path '%s' to remote host '%s'", printStorePath(info.path), host);
debug("adding path '%s' to remote host '%s'", printStorePath(info.path), config->host);
auto conn(connections->get());
@ -178,7 +175,7 @@ void LegacySSHStore::addToStore(const ValidPathInfo & info, Source & source,
conn->to.flush();
if (readInt(conn->from) != 1)
throw Error("failed to add path '%s' to remote host '%s'", printStorePath(info.path), host);
throw Error("failed to add path '%s' to remote host '%s'", printStorePath(info.path), config->host);
} else {
@ -390,12 +387,17 @@ LegacySSHStore::ConnectionStats LegacySSHStore::getConnectionStats()
* The legacy ssh protocol doesn't support checking for trusted-user.
* Try using ssh-ng:// instead if you want to know.
*/
std::optional<TrustedFlag> isTrustedClient()
std::optional<TrustedFlag> LegacySSHStore::isTrustedClient()
{
return std::nullopt;
}
static RegisterStoreImplementation<LegacySSHStore, LegacySSHStoreConfig> regLegacySSHStore;
ref<Store> LegacySSHStore::Config::openStore() const {
return make_ref<LegacySSHStore>(ref{shared_from_this()});
}
static RegisterStoreImplementation<LegacySSHStore::Config> regLegacySSHStore;
}

View file

@ -2,6 +2,7 @@
#include "nix/store/globals.hh"
#include "nix/store/nar-info-disk-cache.hh"
#include "nix/util/signals.hh"
#include "nix/store/store-registration.hh"
#include <atomic>
@ -10,9 +11,9 @@ namespace nix {
LocalBinaryCacheStoreConfig::LocalBinaryCacheStoreConfig(
std::string_view scheme,
PathView binaryCacheDir,
const Params & params)
: StoreConfig(params)
, BinaryCacheStoreConfig(params)
const StoreReference::Params & params)
: Store::Config{params}
, BinaryCacheStoreConfig{params}
, binaryCacheDir(binaryCacheDir)
{
}
@ -26,29 +27,26 @@ std::string LocalBinaryCacheStoreConfig::doc()
}
struct LocalBinaryCacheStore : virtual LocalBinaryCacheStoreConfig, virtual BinaryCacheStore
struct LocalBinaryCacheStore :
virtual BinaryCacheStore
{
/**
* @param binaryCacheDir `file://` is a short-hand for `file:///`
* for now.
*/
LocalBinaryCacheStore(
std::string_view scheme,
PathView binaryCacheDir,
const Params & params)
: StoreConfig(params)
, BinaryCacheStoreConfig(params)
, LocalBinaryCacheStoreConfig(scheme, binaryCacheDir, params)
, Store(params)
, BinaryCacheStore(params)
using Config = LocalBinaryCacheStoreConfig;
ref<Config> config;
LocalBinaryCacheStore(ref<Config> config)
: Store{*config}
, BinaryCacheStore{*config}
, config{config}
{
init();
}
void init() override;
std::string getUri() override
{
return "file://" + binaryCacheDir;
return "file://" + config->binaryCacheDir;
}
protected:
@ -59,7 +57,7 @@ protected:
std::shared_ptr<std::basic_iostream<char>> istream,
const std::string & mimeType) override
{
auto path2 = binaryCacheDir + "/" + path;
auto path2 = config->binaryCacheDir + "/" + path;
static std::atomic<int> counter{0};
Path tmp = fmt("%s.tmp.%d.%d", path2, getpid(), ++counter);
AutoDelete del(tmp, false);
@ -72,7 +70,7 @@ protected:
void getFile(const std::string & path, Sink & sink) override
{
try {
readFile(binaryCacheDir + "/" + path, sink);
readFile(config->binaryCacheDir + "/" + path, sink);
} catch (SysError & e) {
if (e.errNo == ENOENT)
throw NoSuchBinaryCacheFile("file '%s' does not exist in binary cache", path);
@ -84,7 +82,7 @@ protected:
{
StorePathSet paths;
for (auto & entry : std::filesystem::directory_iterator{binaryCacheDir}) {
for (auto & entry : DirectoryIterator{config->binaryCacheDir}) {
checkInterrupt();
auto name = entry.path().filename().string();
if (name.size() != 40 ||
@ -106,20 +104,20 @@ protected:
void LocalBinaryCacheStore::init()
{
createDirs(binaryCacheDir + "/nar");
createDirs(binaryCacheDir + "/" + realisationsPrefix);
if (writeDebugInfo)
createDirs(binaryCacheDir + "/debuginfo");
createDirs(binaryCacheDir + "/log");
createDirs(config->binaryCacheDir + "/nar");
createDirs(config->binaryCacheDir + "/" + realisationsPrefix);
if (config->writeDebugInfo)
createDirs(config->binaryCacheDir + "/debuginfo");
createDirs(config->binaryCacheDir + "/log");
BinaryCacheStore::init();
}
bool LocalBinaryCacheStore::fileExists(const std::string & path)
{
return pathExists(binaryCacheDir + "/" + path);
return pathExists(config->binaryCacheDir + "/" + path);
}
std::set<std::string> LocalBinaryCacheStoreConfig::uriSchemes()
StringSet LocalBinaryCacheStoreConfig::uriSchemes()
{
if (getEnv("_NIX_FORCE_HTTP") == "1")
return {};
@ -127,6 +125,13 @@ std::set<std::string> LocalBinaryCacheStoreConfig::uriSchemes()
return {"file"};
}
static RegisterStoreImplementation<LocalBinaryCacheStore, LocalBinaryCacheStoreConfig> regLocalBinaryCacheStore;
ref<Store> LocalBinaryCacheStoreConfig::openStore() const {
return make_ref<LocalBinaryCacheStore>(ref{
// FIXME we shouldn't actually need a mutable config
std::const_pointer_cast<LocalBinaryCacheStore::Config>(shared_from_this())
});
}
static RegisterStoreImplementation<LocalBinaryCacheStore::Config> regLocalBinaryCacheStore;
}

View file

@ -22,8 +22,9 @@ LocalFSStoreConfig::LocalFSStoreConfig(PathView rootDir, const Params & params)
{
}
LocalFSStore::LocalFSStore(const Params & params)
: Store(params)
LocalFSStore::LocalFSStore(const Config & config)
: Store{static_cast<const Store::Config &>(*this)}
, config{config}
{
}
@ -33,30 +34,35 @@ struct LocalStoreAccessor : PosixSourceAccessor
bool requireValidPath;
LocalStoreAccessor(ref<LocalFSStore> store, bool requireValidPath)
: store(store)
: PosixSourceAccessor(std::filesystem::path{store->config.realStoreDir.get()})
, store(store)
, requireValidPath(requireValidPath)
{ }
CanonPath toRealPath(const CanonPath & path)
{
auto [storePath, rest] = store->toStorePath(path.abs());
}
void requireStoreObject(const CanonPath & path)
{
auto [storePath, rest] = store->toStorePath(store->storeDir + path.abs());
if (requireValidPath && !store->isValidPath(storePath))
throw InvalidPath("path '%1%' is not a valid store path", store->printStorePath(storePath));
return CanonPath(store->getRealStoreDir()) / storePath.to_string() / CanonPath(rest);
}
std::optional<Stat> maybeLstat(const CanonPath & path) override
{
/* Handle the case where `path` is (a parent of) the store. */
if (isDirOrInDir(store->storeDir, path.abs()))
/* Also allow `path` to point to the entire store, which is
needed for resolving symlinks. */
if (path.isRoot())
return Stat{ .type = tDirectory };
return PosixSourceAccessor::maybeLstat(toRealPath(path));
requireStoreObject(path);
return PosixSourceAccessor::maybeLstat(path);
}
DirEntries readDirectory(const CanonPath & path) override
{
return PosixSourceAccessor::readDirectory(toRealPath(path));
requireStoreObject(path);
return PosixSourceAccessor::readDirectory(path);
}
void readFile(
@ -64,12 +70,14 @@ struct LocalStoreAccessor : PosixSourceAccessor
Sink & sink,
std::function<void(uint64_t)> sizeCallback) override
{
return PosixSourceAccessor::readFile(toRealPath(path), sink, sizeCallback);
requireStoreObject(path);
return PosixSourceAccessor::readFile(path, sink, sizeCallback);
}
std::string readLink(const CanonPath & path) override
{
return PosixSourceAccessor::readLink(toRealPath(path));
requireStoreObject(path);
return PosixSourceAccessor::readLink(path);
}
};
@ -97,8 +105,8 @@ std::optional<std::string> LocalFSStore::getBuildLogExact(const StorePath & path
Path logPath =
j == 0
? fmt("%s/%s/%s/%s", logDir, drvsLogDir, baseName.substr(0, 2), baseName.substr(2))
: fmt("%s/%s/%s", logDir, drvsLogDir, baseName);
? fmt("%s/%s/%s/%s", config.logDir.get(), drvsLogDir, baseName.substr(0, 2), baseName.substr(2))
: fmt("%s/%s/%s", config.logDir.get(), drvsLogDir, baseName);
Path logBz2Path = logPath + ".bz2";
if (pathExists(logPath))

View file

@ -1,9 +1,12 @@
#include <regex>
#include "nix/store/local-overlay-store.hh"
#include "nix/util/callback.hh"
#include "nix/store/realisation.hh"
#include "nix/util/processes.hh"
#include "nix/util/url.hh"
#include <regex>
#include "nix/store/store-open.hh"
#include "nix/store/store-registration.hh"
namespace nix {
@ -14,25 +17,32 @@ std::string LocalOverlayStoreConfig::doc()
;
}
Path LocalOverlayStoreConfig::toUpperPath(const StorePath & path) {
ref<Store> LocalOverlayStoreConfig::openStore() const
{
return make_ref<LocalOverlayStore>(ref{
std::dynamic_pointer_cast<const LocalOverlayStoreConfig>(shared_from_this())
});
}
Path LocalOverlayStoreConfig::toUpperPath(const StorePath & path) const
{
return upperLayer + "/" + path.to_string();
}
LocalOverlayStore::LocalOverlayStore(std::string_view scheme, PathView path, const Params & params)
: StoreConfig(params)
, LocalFSStoreConfig(path, params)
, LocalStoreConfig(params)
, LocalOverlayStoreConfig(scheme, path, params)
, Store(params)
, LocalFSStore(params)
, LocalStore(params)
, lowerStore(openStore(percentDecode(lowerStoreUri.get())).dynamic_pointer_cast<LocalFSStore>())
LocalOverlayStore::LocalOverlayStore(ref<const Config> config)
: Store{*config}
, LocalFSStore{*config}
, LocalStore{static_cast<ref<const LocalStore::Config>>(config)}
, config{config}
, lowerStore(openStore(percentDecode(config->lowerStoreUri.get())).dynamic_pointer_cast<LocalFSStore>())
{
if (checkMount.get()) {
if (config->checkMount.get()) {
std::smatch match;
std::string mountInfo;
auto mounts = readFile(std::filesystem::path{"/proc/self/mounts"});
auto regex = std::regex(R"((^|\n)overlay )" + realStoreDir.get() + R"( .*(\n|$))");
auto regex = std::regex(R"((^|\n)overlay )" + config->realStoreDir.get() + R"( .*(\n|$))");
// Mount points can be stacked, so there might be multiple matching entries.
// Loop until the last match, which will be the current state of the mount point.
@ -45,13 +55,13 @@ LocalOverlayStore::LocalOverlayStore(std::string_view scheme, PathView path, con
return std::regex_search(mountInfo, std::regex("\\b" + option + "=" + value + "( |,)"));
};
auto expectedLowerDir = lowerStore->realStoreDir.get();
if (!checkOption("lowerdir", expectedLowerDir) || !checkOption("upperdir", upperLayer)) {
auto expectedLowerDir = lowerStore->config.realStoreDir.get();
if (!checkOption("lowerdir", expectedLowerDir) || !checkOption("upperdir", config->upperLayer)) {
debug("expected lowerdir: %s", expectedLowerDir);
debug("expected upperdir: %s", upperLayer);
debug("expected upperdir: %s", config->upperLayer);
debug("actual mount: %s", mountInfo);
throw Error("overlay filesystem '%s' mounted incorrectly",
realStoreDir.get());
config->realStoreDir.get());
}
}
}
@ -201,14 +211,14 @@ void LocalOverlayStore::collectGarbage(const GCOptions & options, GCResults & re
void LocalOverlayStore::deleteStorePath(const Path & path, uint64_t & bytesFreed)
{
auto mergedDir = realStoreDir.get() + "/";
auto mergedDir = config->realStoreDir.get() + "/";
if (path.substr(0, mergedDir.length()) != mergedDir) {
warn("local-overlay: unexpected gc path '%s' ", path);
return;
}
StorePath storePath = {path.substr(mergedDir.length())};
auto upperPath = toUpperPath(storePath);
auto upperPath = config->toUpperPath(storePath);
if (pathExists(upperPath)) {
debug("upper exists: %s", path);
@ -257,7 +267,7 @@ LocalStore::VerificationResult LocalOverlayStore::verifyAllValidPaths(RepairFlag
StorePathSet done;
auto existsInStoreDir = [&](const StorePath & storePath) {
return pathExists(realStoreDir.get() + "/" + storePath.to_string());
return pathExists(config->realStoreDir.get() + "/" + storePath.to_string());
};
bool errors = false;
@ -277,16 +287,16 @@ void LocalOverlayStore::remountIfNecessary()
{
if (!_remountRequired) return;
if (remountHook.get().empty()) {
warn("'%s' needs remounting, set remount-hook to do this automatically", realStoreDir.get());
if (config->remountHook.get().empty()) {
warn("'%s' needs remounting, set remount-hook to do this automatically", config->realStoreDir.get());
} else {
runProgram(remountHook, false, {realStoreDir});
runProgram(config->remountHook, false, {config->realStoreDir});
}
_remountRequired = false;
}
static RegisterStoreImplementation<LocalOverlayStore, LocalOverlayStoreConfig> regLocalOverlayStore;
static RegisterStoreImplementation<LocalOverlayStore::Config> regLocalOverlayStore;
}

View file

@ -85,7 +85,7 @@ The parts of a local overlay store are as follows:
> The location of the database instead depends on the [`state`](#store-experimental-local-overlay-store-state) setting.
> It is always `${state}/db`.
This contains the metadata of all of the upper layer [store objects][store object] (everything beyond their file system objects), and also duplicate copies of some lower layer store object's metadta.
This contains the metadata of all of the upper layer [store objects][store object] (everything beyond their file system objects), and also duplicate copies of some lower layer store object's metadata.
The duplication is so the metadata for the [closure](@docroot@/glossary.md#gloss-closure) of upper layer [store objects][store object] can be found entirely within the upper layer.
(This allows us to use the same SQL Schema as the [local store]'s SQLite database, as foreign keys in that schema enforce closure metadata to be self-contained in this way.)

View file

@ -17,6 +17,8 @@
#include "nix/util/posix-source-accessor.hh"
#include "nix/store/keys.hh"
#include "nix/util/users.hh"
#include "nix/store/store-open.hh"
#include "nix/store/store-registration.hh"
#include <iostream>
#include <algorithm>
@ -75,6 +77,11 @@ std::string LocalStoreConfig::doc()
;
}
ref<Store> LocalStore::Config::openStore() const
{
return make_ref<LocalStore>(ref{shared_from_this()});
}
struct LocalStore::State::Stmts {
/* Some precompiled SQLite statements. */
SQLiteStmt RegisterValidPath;
@ -97,39 +104,33 @@ struct LocalStore::State::Stmts {
SQLiteStmt AddRealisationReference;
};
LocalStore::LocalStore(
std::string_view scheme,
PathView path,
const Params & params)
: StoreConfig(params)
, LocalFSStoreConfig(path, params)
, LocalStoreConfig(scheme, path, params)
, Store(params)
, LocalFSStore(params)
, dbDir(stateDir + "/db")
, linksDir(realStoreDir + "/.links")
LocalStore::LocalStore(ref<const Config> config)
: Store{*config}
, LocalFSStore{*config}
, config{config}
, dbDir(config->stateDir + "/db")
, linksDir(config->realStoreDir + "/.links")
, reservedPath(dbDir + "/reserved")
, schemaPath(dbDir + "/schema")
, tempRootsDir(stateDir + "/temproots")
, tempRootsDir(config->stateDir + "/temproots")
, fnTempRoots(fmt("%s/%d", tempRootsDir, getpid()))
, locksHeld(tokenizeString<PathSet>(getEnv("NIX_HELD_LOCKS").value_or("")))
{
auto state(_state.lock());
state->stmts = std::make_unique<State::Stmts>();
/* Create missing state directories if they don't already exist. */
createDirs(realStoreDir.get());
if (readOnly) {
createDirs(config->realStoreDir.get());
if (config->readOnly) {
experimentalFeatureSettings.require(Xp::ReadOnlyLocalStore);
} else {
makeStoreWritable();
}
createDirs(linksDir);
Path profilesDir = stateDir + "/profiles";
Path profilesDir = config->stateDir + "/profiles";
createDirs(profilesDir);
createDirs(tempRootsDir);
createDirs(dbDir);
Path gcRootsDir = stateDir + "/gcroots";
Path gcRootsDir = config->stateDir + "/gcroots";
if (!pathExists(gcRootsDir)) {
createDirs(gcRootsDir);
createSymlink(profilesDir, gcRootsDir + "/profiles");
@ -137,14 +138,11 @@ LocalStore::LocalStore(
for (auto & perUserDir : {profilesDir + "/per-user", gcRootsDir + "/per-user"}) {
createDirs(perUserDir);
if (!readOnly) {
auto st = lstat(perUserDir);
if (!config->readOnly) {
// Skip chmod call if the directory already has the correct permissions (0755).
// This is to avoid failing when the executing user lacks permissions to change the directory's permissions
// even if it would be no-op.
if ((st.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO)) != 0755 && chmod(perUserDir.c_str(), 0755) == -1)
throw SysError("could not set permissions on '%s' to 755", perUserDir);
chmodIfNeeded(perUserDir, 0755, S_IRWXU | S_IRWXG | S_IRWXO);
}
}
@ -157,16 +155,16 @@ LocalStore::LocalStore(
struct group * gr = getgrnam(settings.buildUsersGroup.get().c_str());
if (!gr)
printError("warning: the group '%1%' specified in 'build-users-group' does not exist", settings.buildUsersGroup);
else if (!readOnly) {
else if (!config->readOnly) {
struct stat st;
if (stat(realStoreDir.get().c_str(), &st))
throw SysError("getting attributes of path '%1%'", realStoreDir);
if (stat(config->realStoreDir.get().c_str(), &st))
throw SysError("getting attributes of path '%1%'", config->realStoreDir);
if (st.st_uid != 0 || st.st_gid != gr->gr_gid || (st.st_mode & ~S_IFMT) != perm) {
if (chown(realStoreDir.get().c_str(), 0, gr->gr_gid) == -1)
throw SysError("changing ownership of path '%1%'", realStoreDir);
if (chmod(realStoreDir.get().c_str(), perm) == -1)
throw SysError("changing permissions on path '%1%'", realStoreDir);
if (chown(config->realStoreDir.get().c_str(), 0, gr->gr_gid) == -1)
throw SysError("changing ownership of path '%1%'", config->realStoreDir);
if (chmod(config->realStoreDir.get().c_str(), perm) == -1)
throw SysError("changing permissions on path '%1%'", config->realStoreDir);
}
}
}
@ -174,7 +172,7 @@ LocalStore::LocalStore(
/* Ensure that the store and its parents are not symlinks. */
if (!settings.allowSymlinkedStore) {
std::filesystem::path path = realStoreDir.get();
std::filesystem::path path = config->realStoreDir.get();
std::filesystem::path root = path.root_path();
while (path != root) {
if (std::filesystem::is_symlink(path))
@ -221,12 +219,12 @@ LocalStore::LocalStore(
/* Acquire the big fat lock in shared mode to make sure that no
schema upgrade is in progress. */
if (!readOnly) {
if (!config->readOnly) {
Path globalLockPath = dbDir + "/big-lock";
globalLock = openLockFile(globalLockPath.c_str(), true);
}
if (!readOnly && !lockFile(globalLock.get(), ltRead, false)) {
if (!config->readOnly && !lockFile(globalLock.get(), ltRead, false)) {
printInfo("waiting for the big Nix store lock...");
lockFile(globalLock.get(), ltRead, true);
}
@ -234,7 +232,7 @@ LocalStore::LocalStore(
/* Check the current database schema and if necessary do an
upgrade. */
int curSchema = getSchema();
if (readOnly && curSchema < nixSchemaVersion) {
if (config->readOnly && curSchema < nixSchemaVersion) {
debug("current schema version: %d", curSchema);
debug("supported schema version: %d", nixSchemaVersion);
throw Error(curSchema == 0 ?
@ -382,15 +380,9 @@ LocalStore::LocalStore(
}
LocalStore::LocalStore(const Params & params)
: LocalStore("local", "", params)
{
}
AutoCloseFD LocalStore::openGCLock()
{
Path fnGCLock = stateDir + "/gc.lock";
Path fnGCLock = config->stateDir + "/gc.lock";
auto fdGCLock = open(fnGCLock.c_str(), O_RDWR | O_CREAT
#ifndef _WIN32
| O_CLOEXEC
@ -456,17 +448,17 @@ int LocalStore::getSchema()
void LocalStore::openDB(State & state, bool create)
{
if (create && readOnly) {
if (create && config->readOnly) {
throw Error("cannot create database while in read-only mode");
}
if (access(dbDir.c_str(), R_OK | (readOnly ? 0 : W_OK)))
if (access(dbDir.c_str(), R_OK | (config->readOnly ? 0 : W_OK)))
throw SysError("Nix database directory '%1%' is not writable", dbDir);
/* Open the Nix database. */
std::string dbPath = dbDir + "/db.sqlite";
auto & db(state.db);
auto openMode = readOnly ? SQLiteOpenMode::Immutable
auto openMode = config->readOnly ? SQLiteOpenMode::Immutable
: create ? SQLiteOpenMode::Normal
: SQLiteOpenMode::NoCreate;
state.db = SQLite(dbPath, openMode);
@ -539,7 +531,7 @@ void LocalStore::upgradeDBSchema(State & state)
{
state.db.exec("create table if not exists SchemaMigrations (migration text primary key not null);");
std::set<std::string> schemaMigrations;
StringSet schemaMigrations;
{
SQLiteStmt querySchemaMigrations;
@ -579,12 +571,12 @@ void LocalStore::makeStoreWritable()
if (!isRootUser()) return;
/* Check if /nix/store is on a read-only mount. */
struct statvfs stat;
if (statvfs(realStoreDir.get().c_str(), &stat) != 0)
if (statvfs(config->realStoreDir.get().c_str(), &stat) != 0)
throw SysError("getting info about the Nix store mount point");
if (stat.f_flag & ST_RDONLY) {
if (mount(0, realStoreDir.get().c_str(), "none", MS_REMOUNT | MS_BIND, 0) == -1)
throw SysError("remounting %1% writable", realStoreDir);
if (mount(0, config->realStoreDir.get().c_str(), "none", MS_REMOUNT | MS_BIND, 0) == -1)
throw SysError("remounting %1% writable", config->realStoreDir);
}
#endif
}
@ -924,7 +916,7 @@ StorePathSet LocalStore::querySubstitutablePaths(const StorePathSet & paths)
for (auto & sub : getDefaultSubstituters()) {
if (remaining.empty()) break;
if (sub->storeDir != storeDir) continue;
if (!sub->wantMassQuery) continue;
if (!sub->config.wantMassQuery) continue;
auto valid = sub->queryValidPaths(remaining);
@ -1036,12 +1028,12 @@ const PublicKeys & LocalStore::getPublicKeys()
bool LocalStore::pathInfoIsUntrusted(const ValidPathInfo & info)
{
return requireSigs && !info.checkSignatures(*this, getPublicKeys());
return config->requireSigs && !info.checkSignatures(*this, getPublicKeys());
}
bool LocalStore::realisationIsUntrusted(const Realisation & realisation)
{
return requireSigs && !realisation.checkSignatures(getPublicKeys());
return config->requireSigs && !realisation.checkSignatures(getPublicKeys());
}
void LocalStore::addToStore(const ValidPathInfo & info, Source & source,
@ -1106,7 +1098,7 @@ void LocalStore::addToStore(const ValidPathInfo & info, Source & source,
auto & specified = *info.ca;
auto actualHash = ({
auto accessor = getFSAccessor(false);
CanonPath path { printStorePath(info.path) };
CanonPath path { info.path.to_string() };
Hash h { HashAlgorithm::SHA256 }; // throwaway def to appease C++
auto fim = specified.method.getFileIngestionMethod();
switch (fim) {
@ -1338,7 +1330,7 @@ std::pair<std::filesystem::path, AutoCloseFD> LocalStore::createTempDirInStore()
/* There is a slight possibility that `tmpDir' gets deleted by
the GC between createTempDir() and when we acquire a lock on it.
We'll repeat until 'tmpDir' exists and we've locked it. */
tmpDirFn = createTempDir(realStoreDir, "tmp");
tmpDirFn = createTempDir(config->realStoreDir, "tmp");
tmpDirFd = openDirectory(tmpDirFn);
if (!tmpDirFd) {
continue;
@ -1386,7 +1378,7 @@ bool LocalStore::verifyStore(bool checkContents, RepairFlag repair)
printInfo("checking link hashes...");
for (auto & link : std::filesystem::directory_iterator{linksDir}) {
for (auto & link : DirectoryIterator{linksDir}) {
checkInterrupt();
auto name = link.path().filename();
printMsg(lvlTalkative, "checking contents of '%s'", name);
@ -1479,7 +1471,7 @@ LocalStore::VerificationResult LocalStore::verifyAllValidPaths(RepairFlag repair
database and the filesystem) in the loop below, in order to catch
invalid states.
*/
for (auto & i : std::filesystem::directory_iterator{realStoreDir.to_string()}) {
for (auto & i : DirectoryIterator{config->realStoreDir.get()}) {
checkInterrupt();
try {
storePathsInStoreDir.insert({i.path().filename().string()});
@ -1588,33 +1580,6 @@ void LocalStore::addSignatures(const StorePath & storePath, const StringSet & si
}
void LocalStore::signRealisation(Realisation & realisation)
{
// FIXME: keep secret keys in memory.
auto secretKeyFiles = settings.secretKeyFiles;
for (auto & secretKeyFile : secretKeyFiles.get()) {
SecretKey secretKey(readFile(secretKeyFile));
LocalSigner signer(std::move(secretKey));
realisation.sign(signer);
}
}
void LocalStore::signPathInfo(ValidPathInfo & info)
{
// FIXME: keep secret keys in memory.
auto secretKeyFiles = settings.secretKeyFiles;
for (auto & secretKeyFile : secretKeyFiles.get()) {
SecretKey secretKey(readFile(secretKeyFile));
LocalSigner signer(std::move(secretKey));
info.sign(*this, signer);
}
}
std::optional<std::pair<int64_t, Realisation>> LocalStore::queryRealisationCore_(
LocalStore::State & state,
const DrvOutput & id)
@ -1695,7 +1660,7 @@ void LocalStore::addBuildLog(const StorePath & drvPath, std::string_view log)
auto baseName = drvPath.to_string();
auto logPath = fmt("%s/%s/%s/%s.bz2", logDir, drvsLogDir, baseName.substr(0, 2), baseName.substr(2));
auto logPath = fmt("%s/%s/%s/%s.bz2", config->logDir, drvsLogDir, baseName.substr(0, 2), baseName.substr(2));
if (pathExists(logPath)) return;
@ -1713,6 +1678,6 @@ std::optional<std::string> LocalStore::getVersion()
return nixVersion;
}
static RegisterStoreImplementation<LocalStore, LocalStoreConfig> regLocalStore;
static RegisterStoreImplementation<LocalStore::Config> regLocalStore;
} // namespace nix

View file

@ -1,6 +1,6 @@
#include "nix/store/machines.hh"
#include "nix/store/globals.hh"
#include "nix/store/store-api.hh"
#include "nix/store/store-open.hh"
#include <algorithm>
@ -47,7 +47,7 @@ bool Machine::systemSupported(const std::string & system) const
return system == "builtin" || (systemTypes.count(system) > 0);
}
bool Machine::allSupported(const std::set<std::string> & features) const
bool Machine::allSupported(const StringSet & features) const
{
return std::all_of(features.begin(), features.end(),
[&](const std::string & feature) {
@ -56,7 +56,7 @@ bool Machine::allSupported(const std::set<std::string> & features) const
});
}
bool Machine::mandatoryMet(const std::set<std::string> & features) const
bool Machine::mandatoryMet(const StringSet & features) const
{
return std::all_of(mandatoryFeatures.begin(), mandatoryFeatures.end(),
[&](const std::string & feature) {
@ -134,7 +134,7 @@ static std::vector<std::string> expandBuilderLines(const std::string & builders)
return result;
}
static Machine parseBuilderLine(const std::set<std::string> & defaultSystems, const std::string & line)
static Machine parseBuilderLine(const StringSet & defaultSystems, const std::string & line)
{
const auto tokens = tokenizeString<std::vector<std::string>>(line);
@ -178,7 +178,7 @@ static Machine parseBuilderLine(const std::set<std::string> & defaultSystems, co
// `storeUri`
tokens[0],
// `systemTypes`
isSet(1) ? tokenizeString<std::set<std::string>>(tokens[1], ",") : defaultSystems,
isSet(1) ? tokenizeString<StringSet>(tokens[1], ",") : defaultSystems,
// `sshKey`
isSet(2) ? tokens[2] : "",
// `maxJobs`
@ -186,15 +186,15 @@ static Machine parseBuilderLine(const std::set<std::string> & defaultSystems, co
// `speedFactor`
isSet(4) ? parseFloatField(4) : 1.0f,
// `supportedFeatures`
isSet(5) ? tokenizeString<std::set<std::string>>(tokens[5], ",") : std::set<std::string>{},
isSet(5) ? tokenizeString<StringSet>(tokens[5], ",") : StringSet{},
// `mandatoryFeatures`
isSet(6) ? tokenizeString<std::set<std::string>>(tokens[6], ",") : std::set<std::string>{},
isSet(6) ? tokenizeString<StringSet>(tokens[6], ",") : StringSet{},
// `sshPublicHostKey`
isSet(7) ? ensureBase64(7) : ""
};
}
static Machines parseBuilderLines(const std::set<std::string> & defaultSystems, const std::vector<std::string> & builders)
static Machines parseBuilderLines(const StringSet & defaultSystems, const std::vector<std::string> & builders)
{
Machines result;
std::transform(
@ -203,7 +203,7 @@ static Machines parseBuilderLines(const std::set<std::string> & defaultSystems,
return result;
}
Machines Machine::parseConfig(const std::set<std::string> & defaultSystems, const std::string & s)
Machines Machine::parseConfig(const StringSet & defaultSystems, const std::string & s)
{
const auto builderLines = expandBuilderLines(s);
return parseBuilderLines(defaultSystems, builderLines);

View file

@ -139,6 +139,9 @@ if aws_s3.found()
'-L' + aws_s3.get_variable('libdir'),
'-laws-cpp-sdk-transfer',
'-laws-cpp-sdk-s3',
'-laws-cpp-sdk-identity-management',
'-laws-cpp-sdk-cognito-identity',
'-laws-cpp-sdk-sts',
'-laws-cpp-sdk-core',
'-laws-crt-cpp',
],
@ -304,6 +307,7 @@ sources = files(
'realisation.cc',
'remote-fs-accessor.cc',
'remote-store.cc',
'restricted-store.cc',
's3-binary-cache-store.cc',
'serve-protocol-connection.cc',
'serve-protocol.cc',
@ -311,6 +315,8 @@ sources = files(
'ssh-store.cc',
'ssh.cc',
'store-api.cc',
'store-dir-config.cc',
'store-registration.cc',
'store-reference.cc',
'uds-remote-store.cc',
'worker-protocol-connection.cc',

View file

@ -4,7 +4,7 @@
#include "nix/store/parsed-derivations.hh"
#include "nix/store/derivation-options.hh"
#include "nix/store/globals.hh"
#include "nix/store/store-api.hh"
#include "nix/store/store-open.hh"
#include "nix/util/thread-pool.hh"
#include "nix/store/realisation.hh"
#include "nix/util/topo-sort.hh"
@ -222,8 +222,16 @@ void Store::queryMissing(const std::vector<DerivedPath> & targets,
if (knownOutputPaths && invalid.empty()) return;
auto drv = make_ref<Derivation>(derivationFromPath(drvPath));
ParsedDerivation parsedDrv(StorePath(drvPath), *drv);
DerivationOptions drvOptions = DerivationOptions::fromParsedDerivation(parsedDrv);
auto parsedDrv = StructuredAttrs::tryParse(drv->env);
DerivationOptions drvOptions;
try {
drvOptions = DerivationOptions::fromStructuredAttrs(
drv->env,
parsedDrv ? &*parsedDrv : nullptr);
} catch (Error & e) {
e.addTrace({}, "while parsing derivation '%s'", printStorePath(drvPath));
throw;
}
if (!knownOutputPaths && settings.useSubstitutes && drvOptions.substitutesAllowed()) {
experimentalFeatureSettings.require(Xp::CaDerivations);

View file

@ -176,7 +176,7 @@ NarInfo NarInfo::fromJSON(
std::nullopt);
if (json.contains("downloadSize"))
res.fileSize = getInteger(valueAt(json, "downloadSize"));
res.fileSize = getUnsigned(valueAt(json, "downloadSize"));
return res;
}

View file

@ -101,7 +101,7 @@ void LocalStore::optimisePath_(Activity * act, OptimiseStats & stats,
/* HFS/macOS has some undocumented security feature disabling hardlinking for
special files within .app dirs. Known affected paths include
*.app/Contents/{PkgInfo,Resources/\*.lproj,_CodeSignature} and .DS_Store.
See https://github.com/NixOS/nix/issues/1443 and
See https://github.com/NixOS/nix/issues/1443 and
https://github.com/NixOS/nix/pull/2230 for more discussion. */
if (std::regex_search(path, std::regex("\\.app/Contents/.+$")))
@ -216,14 +216,14 @@ void LocalStore::optimisePath_(Activity * act, OptimiseStats & stats,
the store itself (we don't want or need to mess with its
permissions). */
const Path dirOfPath(dirOf(path));
bool mustToggle = dirOfPath != realStoreDir.get();
bool mustToggle = dirOfPath != config->realStoreDir.get();
if (mustToggle) makeWritable(dirOfPath);
/* When we're done, make the directory read-only again and reset
its timestamp back to 0. */
MakeReadOnly makeReadOnly(mustToggle ? dirOfPath : "");
std::filesystem::path tempLink = fmt("%1%/.tmp-link-%2%-%3%", realStoreDir, getpid(), rand());
std::filesystem::path tempLink = fmt("%1%/.tmp-link-%2%-%3%", config->realStoreDir, getpid(), rand());
try {
std::filesystem::create_hard_link(linkPath, tempLink);
@ -285,7 +285,7 @@ void LocalStore::optimiseStore(OptimiseStats & stats)
if (!isValidPath(i)) continue; /* path was GC'ed, probably */
{
Activity act(*logger, lvlTalkative, actUnknown, fmt("optimising path '%s'", printStorePath(i)));
optimisePath_(&act, stats, realStoreDir + "/" + std::string(i.to_string()), inodeHash, NoRepair);
optimisePath_(&act, stats, config->realStoreDir + "/" + std::string(i.to_string()), inodeHash, NoRepair);
}
done++;
act.progress(done, paths.size());

View file

@ -1,98 +1,27 @@
#include "nix/store/parsed-derivations.hh"
#include "nix/store/store-api.hh"
#include "nix/store/derivations.hh"
#include "nix/store/derivation-options.hh"
#include <nlohmann/json.hpp>
#include <regex>
namespace nix {
ParsedDerivation::ParsedDerivation(const StorePath & drvPath, BasicDerivation & drv)
: drvPath(drvPath), drv(drv)
std::optional<StructuredAttrs> StructuredAttrs::tryParse(const StringPairs & env)
{
/* Parse the __json attribute, if any. */
auto jsonAttr = drv.env.find("__json");
if (jsonAttr != drv.env.end()) {
auto jsonAttr = env.find("__json");
if (jsonAttr != env.end()) {
try {
structuredAttrs = std::make_unique<nlohmann::json>(nlohmann::json::parse(jsonAttr->second));
return StructuredAttrs {
.structuredAttrs = nlohmann::json::parse(jsonAttr->second),
};
} catch (std::exception & e) {
throw Error("cannot process __json attribute of '%s': %s", drvPath.to_string(), e.what());
throw Error("cannot process __json attribute: %s", e.what());
}
}
}
ParsedDerivation::~ParsedDerivation() { }
std::optional<std::string> ParsedDerivation::getStringAttr(const std::string & name) const
{
if (structuredAttrs) {
auto i = structuredAttrs->find(name);
if (i == structuredAttrs->end())
return {};
else {
if (!i->is_string())
throw Error("attribute '%s' of derivation '%s' must be a string", name, drvPath.to_string());
return i->get<std::string>();
}
} else {
auto i = drv.env.find(name);
if (i == drv.env.end())
return {};
else
return i->second;
}
}
bool ParsedDerivation::getBoolAttr(const std::string & name, bool def) const
{
if (structuredAttrs) {
auto i = structuredAttrs->find(name);
if (i == structuredAttrs->end())
return def;
else {
if (!i->is_boolean())
throw Error("attribute '%s' of derivation '%s' must be a Boolean", name, drvPath.to_string());
return i->get<bool>();
}
} else {
auto i = drv.env.find(name);
if (i == drv.env.end())
return def;
else
return i->second == "1";
}
}
std::optional<Strings> ParsedDerivation::getStringsAttr(const std::string & name) const
{
if (structuredAttrs) {
auto i = structuredAttrs->find(name);
if (i == structuredAttrs->end())
return {};
else {
if (!i->is_array())
throw Error("attribute '%s' of derivation '%s' must be a list of strings", name, drvPath.to_string());
Strings res;
for (auto j = i->begin(); j != i->end(); ++j) {
if (!j->is_string())
throw Error("attribute '%s' of derivation '%s' must be a list of strings", name, drvPath.to_string());
res.push_back(j->get<std::string>());
}
return res;
}
} else {
auto i = drv.env.find(name);
if (i == drv.env.end())
return {};
else
return tokenizeString<Strings>(i->second);
}
}
std::optional<StringSet> ParsedDerivation::getStringSetAttr(const std::string & name) const
{
auto ss = getStringsAttr(name);
return ss
? (std::optional{StringSet{ss->begin(), ss->end()}})
: (std::optional<StringSet>{});
return {};
}
static std::regex shVarName("[A-Za-z_][A-Za-z0-9_]*");
@ -151,44 +80,39 @@ static nlohmann::json pathInfoToJSON(
return jsonList;
}
std::optional<nlohmann::json> ParsedDerivation::prepareStructuredAttrs(Store & store, const StorePathSet & inputPaths)
nlohmann::json StructuredAttrs::prepareStructuredAttrs(
Store & store,
const DerivationOptions & drvOptions,
const StorePathSet & inputPaths,
const DerivationOutputs & outputs) const
{
if (!structuredAttrs) return std::nullopt;
auto json = *structuredAttrs;
/* Copy to then modify */
auto json = structuredAttrs;
/* Add an "outputs" object containing the output paths. */
nlohmann::json outputs;
for (auto & i : drv.outputs)
outputs[i.first] = hashPlaceholder(i.first);
json["outputs"] = outputs;
nlohmann::json outputsJson;
for (auto & i : outputs)
outputsJson[i.first] = hashPlaceholder(i.first);
json["outputs"] = std::move(outputsJson);
/* Handle exportReferencesGraph. */
auto e = json.find("exportReferencesGraph");
if (e != json.end() && e->is_object()) {
for (auto i = e->begin(); i != e->end(); ++i) {
StorePathSet storePaths;
for (auto & p : *i)
storePaths.insert(store.toStorePath(p.get<std::string>()).first);
json[i.key()] = pathInfoToJSON(store,
store.exportReferences(storePaths, inputPaths));
}
for (auto & [key, inputPaths] : drvOptions.exportReferencesGraph) {
StorePathSet storePaths;
for (auto & p : inputPaths)
storePaths.insert(store.toStorePath(p).first);
json[key] = pathInfoToJSON(store,
store.exportReferences(storePaths, storePaths));
}
return json;
}
/* As a convenience to bash scripts, write a shell file that
maps all attributes that are representable in bash -
namely, strings, integers, nulls, Booleans, and arrays and
objects consisting entirely of those values. (So nested
arrays or objects are not supported.) */
std::string writeStructuredAttrsShell(const nlohmann::json & json)
std::string StructuredAttrs::writeShell(const nlohmann::json & json)
{
auto handleSimpleType = [](const nlohmann::json & value) -> std::optional<std::string> {
if (value.is_string())
return shellEscape(value.get<std::string_view>());
return escapeShellArgAlways(value.get<std::string_view>());
if (value.is_number()) {
auto f = value.get<float>();
@ -236,7 +160,7 @@ std::string writeStructuredAttrsShell(const nlohmann::json & json)
for (auto & [key2, value2] : value.items()) {
auto s3 = handleSimpleType(value2);
if (!s3) { good = false; break; }
s2 += fmt("[%s]=%s ", shellEscape(key2), *s3);
s2 += fmt("[%s]=%s ", escapeShellArgAlways(key2), *s3);
}
if (good)

View file

@ -40,6 +40,14 @@ void ValidPathInfo::sign(const Store & store, const Signer & signer)
sigs.insert(signer.signDetached(fingerprint(store)));
}
void ValidPathInfo::sign(const Store & store, const std::vector<std::unique_ptr<Signer>> & signers)
{
auto fingerprint = this->fingerprint(store);
for (auto & signer: signers) {
sigs.insert(signer->signDetached(fingerprint));
}
}
std::optional<ContentAddressWithReferences> ValidPathInfo::contentAddressWithReferences() const
{
if (! ca)
@ -192,7 +200,7 @@ UnkeyedValidPathInfo UnkeyedValidPathInfo::fromJSON(
auto & json = getObject(_json);
res.narHash = Hash::parseAny(getString(valueAt(json, "narHash")), std::nullopt);
res.narSize = getInteger(valueAt(json, "narSize"));
res.narSize = getUnsigned(valueAt(json, "narSize"));
try {
auto references = getStringList(valueAt(json, "references"));
@ -216,7 +224,7 @@ UnkeyedValidPathInfo UnkeyedValidPathInfo::fromJSON(
if (json.contains("registrationTime"))
if (auto * rawRegistrationTime = getNullable(valueAt(json, "registrationTime")))
res.registrationTime = getInteger(*rawRegistrationTime);
res.registrationTime = getInteger<time_t>(*rawRegistrationTime);
if (json.contains("ultimate"))
res.ultimate = getBoolean(valueAt(json, "ultimate"));

View file

@ -82,9 +82,9 @@ std::pair<std::string_view, StringSet> parsePathWithOutputs(std::string_view s)
{
size_t n = s.find("!");
return n == s.npos
? std::make_pair(s, std::set<std::string>())
? std::make_pair(s, StringSet())
: std::make_pair(s.substr(0, n),
tokenizeString<std::set<std::string>>(s.substr(n + 1), ","));
tokenizeString<StringSet>(s.substr(n + 1), ","));
}

View file

@ -75,7 +75,7 @@ StorePath StorePath::random(std::string_view name)
return StorePath(Hash::random(HashAlgorithm::SHA1), name);
}
StorePath StoreDirConfig::parseStorePath(std::string_view path) const
StorePath MixStoreDirMethods::parseStorePath(std::string_view path) const
{
// On Windows, `/nix/store` is not a canonical path. More broadly it
// is unclear whether this function should be using the native
@ -94,7 +94,7 @@ StorePath StoreDirConfig::parseStorePath(std::string_view path) const
return StorePath(baseNameOf(p));
}
std::optional<StorePath> StoreDirConfig::maybeParseStorePath(std::string_view path) const
std::optional<StorePath> MixStoreDirMethods::maybeParseStorePath(std::string_view path) const
{
try {
return parseStorePath(path);
@ -103,24 +103,24 @@ std::optional<StorePath> StoreDirConfig::maybeParseStorePath(std::string_view pa
}
}
bool StoreDirConfig::isStorePath(std::string_view path) const
bool MixStoreDirMethods::isStorePath(std::string_view path) const
{
return (bool) maybeParseStorePath(path);
}
StorePathSet StoreDirConfig::parseStorePathSet(const PathSet & paths) const
StorePathSet MixStoreDirMethods::parseStorePathSet(const PathSet & paths) const
{
StorePathSet res;
for (auto & i : paths) res.insert(parseStorePath(i));
return res;
}
std::string StoreDirConfig::printStorePath(const StorePath & path) const
std::string MixStoreDirMethods::printStorePath(const StorePath & path) const
{
return (storeDir + "/").append(path.to_string());
}
PathSet StoreDirConfig::printStorePathSet(const StorePathSet & paths) const
PathSet MixStoreDirMethods::printStorePathSet(const StorePathSet & paths) const
{
PathSet res;
for (auto & i : paths) res.insert(printStorePath(i));

View file

@ -136,7 +136,7 @@ static void canonicalisePathMetaData_(
#endif
if (S_ISDIR(st.st_mode)) {
for (auto & i : std::filesystem::directory_iterator{path}) {
for (auto & i : DirectoryIterator{path}) {
checkInterrupt();
canonicalisePathMetaData_(
i.path().string(),

View file

@ -38,7 +38,7 @@ std::pair<Generations, std::optional<GenerationNumber>> findGenerations(Path pro
std::filesystem::path profileDir = dirOf(profile);
auto profileName = std::string(baseNameOf(profile));
for (auto & i : std::filesystem::directory_iterator{profileDir}) {
for (auto & i : DirectoryIterator{profileDir}) {
checkInterrupt();
if (auto n = parseName(profileName, i.path().filename().string())) {
auto path = i.path().string();

View file

@ -51,7 +51,7 @@ ref<SourceAccessor> RemoteFSAccessor::addToCache(std::string_view hashPart, std:
std::pair<ref<SourceAccessor>, CanonPath> RemoteFSAccessor::fetch(const CanonPath & path)
{
auto [storePath, restPath_] = store->toStorePath(path.abs());
auto [storePath, restPath_] = store->toStorePath(store->storeDir + path.abs());
auto restPath = CanonPath(restPath_);
if (requireValidPath && !store->isValidPath(storePath))

View file

@ -24,11 +24,11 @@
namespace nix {
/* TODO: Separate these store types into different files, give them better names */
RemoteStore::RemoteStore(const Params & params)
: RemoteStoreConfig(params)
, Store(params)
RemoteStore::RemoteStore(const Config & config)
: Store{config}
, config{config}
, connections(make_ref<Pool<Connection>>(
std::max(1, (int) maxConnections),
std::max(1, config.maxConnections.get()),
[this]() {
auto conn = openConnectionWrapper();
try {
@ -44,7 +44,7 @@ RemoteStore::RemoteStore(const Params & params)
r->to.good()
&& r->from.good()
&& std::chrono::duration_cast<std::chrono::seconds>(
std::chrono::steady_clock::now() - r->startTime).count() < maxConnectionAge;
std::chrono::steady_clock::now() - r->startTime).count() < this->config.maxConnectionAge;
}
))
{
@ -122,7 +122,7 @@ void RemoteStore::setOptions(Connection & conn)
<< settings.useSubstitutes;
if (GET_PROTOCOL_MINOR(conn.protoVersion) >= 12) {
std::map<std::string, Config::SettingInfo> overrides;
std::map<std::string, nix::Config::SettingInfo> overrides;
settings.getSettings(overrides, true); // libstore settings
fileTransferSettings.getSettings(overrides, true);
overrides.erase(settings.keepFailed.name);

View file

@ -0,0 +1,332 @@
#include "nix/store/restricted-store.hh"
#include "nix/store/build-result.hh"
#include "nix/util/callback.hh"
#include "nix/store/realisation.hh"
namespace nix {
static StorePath pathPartOfReq(const SingleDerivedPath & req)
{
return std::visit(
overloaded{
[&](const SingleDerivedPath::Opaque & bo) { return bo.path; },
[&](const SingleDerivedPath::Built & bfd) { return pathPartOfReq(*bfd.drvPath); },
},
req.raw());
}
static StorePath pathPartOfReq(const DerivedPath & req)
{
return std::visit(
overloaded{
[&](const DerivedPath::Opaque & bo) { return bo.path; },
[&](const DerivedPath::Built & bfd) { return pathPartOfReq(*bfd.drvPath); },
},
req.raw());
}
bool RestrictionContext::isAllowed(const DerivedPath & req)
{
return isAllowed(pathPartOfReq(req));
}
/**
* A wrapper around LocalStore that only allows building/querying of
* paths that are in the input closures of the build or were added via
* recursive Nix calls.
*/
struct RestrictedStore : public virtual IndirectRootStore, public virtual GcStore
{
ref<const LocalStore::Config> config;
ref<LocalStore> next;
RestrictionContext & goal;
RestrictedStore(ref<LocalStore::Config> config, ref<LocalStore> next, RestrictionContext & goal)
: Store{*config}
, LocalFSStore{*config}
, config{config}
, next(next)
, goal(goal)
{
}
Path getRealStoreDir() override
{
return next->config->realStoreDir;
}
std::string getUri() override
{
return next->getUri();
}
StorePathSet queryAllValidPaths() override;
void queryPathInfoUncached(
const StorePath & path, Callback<std::shared_ptr<const ValidPathInfo>> callback) noexcept override;
void queryReferrers(const StorePath & path, StorePathSet & referrers) override;
std::map<std::string, std::optional<StorePath>>
queryPartialDerivationOutputMap(const StorePath & path, Store * evalStore = nullptr) override;
std::optional<StorePath> queryPathFromHashPart(const std::string & hashPart) override
{
throw Error("queryPathFromHashPart");
}
StorePath addToStore(
std::string_view name,
const SourcePath & srcPath,
ContentAddressMethod method,
HashAlgorithm hashAlgo,
const StorePathSet & references,
PathFilter & filter,
RepairFlag repair) override
{
throw Error("addToStore");
}
void addToStore(
const ValidPathInfo & info,
Source & narSource,
RepairFlag repair = NoRepair,
CheckSigsFlag checkSigs = CheckSigs) override;
StorePath addToStoreFromDump(
Source & dump,
std::string_view name,
FileSerialisationMethod dumpMethod,
ContentAddressMethod hashMethod,
HashAlgorithm hashAlgo,
const StorePathSet & references,
RepairFlag repair) override;
void narFromPath(const StorePath & path, Sink & sink) override;
void ensurePath(const StorePath & path) override;
void registerDrvOutput(const Realisation & info) override;
void queryRealisationUncached(
const DrvOutput & id, Callback<std::shared_ptr<const Realisation>> callback) noexcept override;
void
buildPaths(const std::vector<DerivedPath> & paths, BuildMode buildMode, std::shared_ptr<Store> evalStore) override;
std::vector<KeyedBuildResult> buildPathsWithResults(
const std::vector<DerivedPath> & paths,
BuildMode buildMode = bmNormal,
std::shared_ptr<Store> evalStore = nullptr) override;
BuildResult
buildDerivation(const StorePath & drvPath, const BasicDerivation & drv, BuildMode buildMode = bmNormal) override
{
unsupported("buildDerivation");
}
void addTempRoot(const StorePath & path) override {}
void addIndirectRoot(const Path & path) override {}
Roots findRoots(bool censor) override
{
return Roots();
}
void collectGarbage(const GCOptions & options, GCResults & results) override {}
void addSignatures(const StorePath & storePath, const StringSet & sigs) override
{
unsupported("addSignatures");
}
void queryMissing(
const std::vector<DerivedPath> & targets,
StorePathSet & willBuild,
StorePathSet & willSubstitute,
StorePathSet & unknown,
uint64_t & downloadSize,
uint64_t & narSize) override;
virtual std::optional<std::string> getBuildLogExact(const StorePath & path) override
{
return std::nullopt;
}
virtual void addBuildLog(const StorePath & path, std::string_view log) override
{
unsupported("addBuildLog");
}
std::optional<TrustedFlag> isTrustedClient() override
{
return NotTrusted;
}
};
ref<Store> makeRestrictedStore(ref<LocalStore::Config> config, ref<LocalStore> next, RestrictionContext & context)
{
return make_ref<RestrictedStore>(config, next, context);
}
StorePathSet RestrictedStore::queryAllValidPaths()
{
StorePathSet paths;
for (auto & p : goal.originalPaths())
paths.insert(p);
for (auto & p : goal.addedPaths)
paths.insert(p);
return paths;
}
void RestrictedStore::queryPathInfoUncached(
const StorePath & path, Callback<std::shared_ptr<const ValidPathInfo>> callback) noexcept
{
if (goal.isAllowed(path)) {
try {
/* Censor impure information. */
auto info = std::make_shared<ValidPathInfo>(*next->queryPathInfo(path));
info->deriver.reset();
info->registrationTime = 0;
info->ultimate = false;
info->sigs.clear();
callback(info);
} catch (InvalidPath &) {
callback(nullptr);
}
} else
callback(nullptr);
};
void RestrictedStore::queryReferrers(const StorePath & path, StorePathSet & referrers) {}
std::map<std::string, std::optional<StorePath>>
RestrictedStore::queryPartialDerivationOutputMap(const StorePath & path, Store * evalStore)
{
if (!goal.isAllowed(path))
throw InvalidPath("cannot query output map for unknown path '%s' in recursive Nix", printStorePath(path));
return next->queryPartialDerivationOutputMap(path, evalStore);
}
void RestrictedStore::addToStore(
const ValidPathInfo & info, Source & narSource, RepairFlag repair, CheckSigsFlag checkSigs)
{
next->addToStore(info, narSource, repair, checkSigs);
goal.addDependency(info.path);
}
StorePath RestrictedStore::addToStoreFromDump(
Source & dump,
std::string_view name,
FileSerialisationMethod dumpMethod,
ContentAddressMethod hashMethod,
HashAlgorithm hashAlgo,
const StorePathSet & references,
RepairFlag repair)
{
auto path = next->addToStoreFromDump(dump, name, dumpMethod, hashMethod, hashAlgo, references, repair);
goal.addDependency(path);
return path;
}
void RestrictedStore::narFromPath(const StorePath & path, Sink & sink)
{
if (!goal.isAllowed(path))
throw InvalidPath("cannot dump unknown path '%s' in recursive Nix", printStorePath(path));
LocalFSStore::narFromPath(path, sink);
}
void RestrictedStore::ensurePath(const StorePath & path)
{
if (!goal.isAllowed(path))
throw InvalidPath("cannot substitute unknown path '%s' in recursive Nix", printStorePath(path));
/* Nothing to be done; 'path' must already be valid. */
}
void RestrictedStore::registerDrvOutput(const Realisation & info)
// XXX: This should probably be allowed as a no-op if the realisation
// corresponds to an allowed derivation
{
throw Error("registerDrvOutput");
}
void RestrictedStore::queryRealisationUncached(
const DrvOutput & id, Callback<std::shared_ptr<const Realisation>> callback) noexcept
// XXX: This should probably be allowed if the realisation corresponds to
// an allowed derivation
{
if (!goal.isAllowed(id))
callback(nullptr);
next->queryRealisation(id, std::move(callback));
}
void RestrictedStore::buildPaths(
const std::vector<DerivedPath> & paths, BuildMode buildMode, std::shared_ptr<Store> evalStore)
{
for (auto & result : buildPathsWithResults(paths, buildMode, evalStore))
if (!result.success())
result.rethrow();
}
std::vector<KeyedBuildResult> RestrictedStore::buildPathsWithResults(
const std::vector<DerivedPath> & paths, BuildMode buildMode, std::shared_ptr<Store> evalStore)
{
assert(!evalStore);
if (buildMode != bmNormal)
throw Error("unsupported build mode");
StorePathSet newPaths;
std::set<Realisation> newRealisations;
for (auto & req : paths) {
if (!goal.isAllowed(req))
throw InvalidPath("cannot build '%s' in recursive Nix because path is unknown", req.to_string(*next));
}
auto results = next->buildPathsWithResults(paths, buildMode);
for (auto & result : results) {
for (auto & [outputName, output] : result.builtOutputs) {
newPaths.insert(output.outPath);
newRealisations.insert(output);
}
}
StorePathSet closure;
next->computeFSClosure(newPaths, closure);
for (auto & path : closure)
goal.addDependency(path);
for (auto & real : Realisation::closure(*next, newRealisations))
goal.addedDrvOutputs.insert(real.id);
return results;
}
void RestrictedStore::queryMissing(
const std::vector<DerivedPath> & targets,
StorePathSet & willBuild,
StorePathSet & willSubstitute,
StorePathSet & unknown,
uint64_t & downloadSize,
uint64_t & narSize)
{
/* This is slightly impure since it leaks information to the
client about what paths will be built/substituted or are
already present. Probably not a big deal. */
std::vector<DerivedPath> allowed;
for (auto & req : targets) {
if (goal.isAllowed(req))
allowed.emplace_back(req);
else
unknown.insert(pathPartOfReq(req));
}
next->queryMissing(allowed, willBuild, willSubstitute, unknown, downloadSize, narSize);
}
}

View file

@ -11,6 +11,7 @@
#include "nix/util/compression.hh"
#include "nix/store/filetransfer.hh"
#include "nix/util/signals.hh"
#include "nix/store/store-registration.hh"
#include <aws/core/Aws.h>
#include <aws/core/VersionConfig.h>
@ -21,6 +22,7 @@
#include <aws/core/utils/logging/FormattedLogSystem.h>
#include <aws/core/utils/logging/LogMacros.h>
#include <aws/core/utils/threading/Executor.h>
#include <aws/identity-management/auth/STSProfileCredentialsProvider.h>
#include <aws/s3/S3Client.h>
#include <aws/s3/model/GetObjectRequest.h>
#include <aws/s3/model/HeadObjectRequest.h>
@ -71,6 +73,29 @@ class AwsLogger : public Aws::Utils::Logging::FormattedLogSystem
#endif
};
/* Retrieve the credentials from the list of AWS default providers, with the addition of the STS creds provider. This
last can be used to acquire further permissions with a specific IAM role.
Roughly based on https://github.com/aws/aws-sdk-cpp/issues/150#issuecomment-538548438
*/
struct CustomAwsCredentialsProviderChain : public Aws::Auth::AWSCredentialsProviderChain
{
CustomAwsCredentialsProviderChain(const std::string & profile)
{
if (profile.empty()) {
// Use all the default AWS providers, plus the possibility to acquire a IAM role directly via a profile.
Aws::Auth::DefaultAWSCredentialsProviderChain default_aws_chain;
for (auto provider : default_aws_chain.GetProviders())
AddProvider(provider);
AddProvider(std::make_shared<Aws::Auth::STSProfileCredentialsProvider>());
} else {
// Override the profile name to retrieve from the AWS config and credentials. I believe this option
// comes from the ?profile querystring in nix.conf.
AddProvider(std::make_shared<Aws::Auth::ProfileConfigFileAWSCredentialsProvider>(profile.c_str()));
AddProvider(std::make_shared<Aws::Auth::STSProfileCredentialsProvider>(profile));
}
}
};
static void initAWS()
{
static std::once_flag flag;
@ -102,13 +127,8 @@ S3Helper::S3Helper(
const std::string & endpoint)
: config(makeConfig(region, scheme, endpoint))
, client(make_ref<Aws::S3::S3Client>(
profile == ""
? std::dynamic_pointer_cast<Aws::Auth::AWSCredentialsProvider>(
std::make_shared<Aws::Auth::DefaultAWSCredentialsProviderChain>())
: std::dynamic_pointer_cast<Aws::Auth::AWSCredentialsProvider>(
std::make_shared<Aws::Auth::ProfileConfigFileAWSCredentialsProvider>(profile.c_str())),
std::make_shared<CustomAwsCredentialsProviderChain>(profile),
*config,
// FIXME: https://github.com/aws/aws-sdk-cpp/issues/759
#if AWS_SDK_VERSION_MAJOR == 1 && AWS_SDK_VERSION_MINOR < 3
false,
#else
@ -216,11 +236,6 @@ S3Helper::FileTransferResult S3Helper::getObject(
return res;
}
S3BinaryCacheStore::S3BinaryCacheStore(const Params & params)
: BinaryCacheStoreConfig(params)
, BinaryCacheStore(params)
{ }
S3BinaryCacheStoreConfig::S3BinaryCacheStoreConfig(
std::string_view uriScheme,
@ -239,6 +254,12 @@ S3BinaryCacheStoreConfig::S3BinaryCacheStoreConfig(
throw UsageError("`%s` store requires a bucket name in its Store URI", uriScheme);
}
S3BinaryCacheStore::S3BinaryCacheStore(ref<Config> config)
: BinaryCacheStore(*config)
, config{config}
{ }
std::string S3BinaryCacheStoreConfig::doc()
{
return
@ -247,40 +268,37 @@ std::string S3BinaryCacheStoreConfig::doc()
}
struct S3BinaryCacheStoreImpl : virtual S3BinaryCacheStoreConfig, public virtual S3BinaryCacheStore
struct S3BinaryCacheStoreImpl : virtual S3BinaryCacheStore
{
Stats stats;
S3Helper s3Helper;
S3BinaryCacheStoreImpl(
std::string_view uriScheme,
std::string_view bucketName,
const Params & params)
: StoreConfig(params)
, BinaryCacheStoreConfig(params)
, S3BinaryCacheStoreConfig(uriScheme, bucketName, params)
, Store(params)
, BinaryCacheStore(params)
, S3BinaryCacheStore(params)
, s3Helper(profile, region, scheme, endpoint)
S3BinaryCacheStoreImpl(ref<Config> config)
: Store{*config}
, BinaryCacheStore{*config}
, S3BinaryCacheStore{config}
, s3Helper(config->profile, config->region, config->scheme, config->endpoint)
{
diskCache = getNarInfoDiskCache();
init();
}
std::string getUri() override
{
return "s3://" + bucketName;
return "s3://" + config->bucketName;
}
void init() override
{
if (auto cacheInfo = diskCache->upToDateCacheExists(getUri())) {
wantMassQuery.setDefault(cacheInfo->wantMassQuery);
priority.setDefault(cacheInfo->priority);
config->wantMassQuery.setDefault(cacheInfo->wantMassQuery);
config->priority.setDefault(cacheInfo->priority);
} else {
BinaryCacheStore::init();
diskCache->createCache(getUri(), storeDir, wantMassQuery, priority);
diskCache->createCache(
getUri(), config->storeDir, config->wantMassQuery, config->priority);
}
}
@ -309,7 +327,7 @@ struct S3BinaryCacheStoreImpl : virtual S3BinaryCacheStoreConfig, public virtual
auto res = s3Helper.client->HeadObject(
Aws::S3::Model::HeadObjectRequest()
.WithBucket(bucketName)
.WithBucket(config->bucketName)
.WithKey(path));
if (!res.IsSuccess()) {
@ -353,7 +371,7 @@ struct S3BinaryCacheStoreImpl : virtual S3BinaryCacheStoreConfig, public virtual
const std::string & mimeType,
const std::string & contentEncoding)
{
std::string uri = "s3://" + bucketName + "/" + path;
std::string uri = "s3://" + config->bucketName + "/" + path;
Activity act(*logger, lvlTalkative, actFileTransfer,
fmt("uploading '%s'", uri),
Logger::Fields{uri}, getCurActivity());
@ -368,11 +386,11 @@ struct S3BinaryCacheStoreImpl : virtual S3BinaryCacheStoreConfig, public virtual
std::call_once(transferManagerCreated, [&]()
{
if (multipartUpload) {
if (config->multipartUpload) {
TransferManagerConfiguration transferConfig(executor.get());
transferConfig.s3Client = s3Helper.client;
transferConfig.bufferSize = bufferSize;
transferConfig.bufferSize = config->bufferSize;
transferConfig.uploadProgressCallback =
[](const TransferManager * transferManager,
@ -402,6 +420,8 @@ struct S3BinaryCacheStoreImpl : virtual S3BinaryCacheStoreConfig, public virtual
auto now1 = std::chrono::steady_clock::now();
auto & bucketName = config->bucketName;
if (transferManager) {
if (contentEncoding != "")
@ -489,12 +509,12 @@ struct S3BinaryCacheStoreImpl : virtual S3BinaryCacheStoreConfig, public virtual
return std::make_shared<std::stringstream>(std::move(compressed));
};
if (narinfoCompression != "" && hasSuffix(path, ".narinfo"))
uploadFile(path, compress(narinfoCompression), mimeType, narinfoCompression);
else if (lsCompression != "" && hasSuffix(path, ".ls"))
uploadFile(path, compress(lsCompression), mimeType, lsCompression);
else if (logCompression != "" && hasPrefix(path, "log/"))
uploadFile(path, compress(logCompression), mimeType, logCompression);
if (config->narinfoCompression != "" && hasSuffix(path, ".narinfo"))
uploadFile(path, compress(config->narinfoCompression), mimeType, config->narinfoCompression);
else if (config->lsCompression != "" && hasSuffix(path, ".ls"))
uploadFile(path, compress(config->lsCompression), mimeType, config->lsCompression);
else if (config->logCompression != "" && hasPrefix(path, "log/"))
uploadFile(path, compress(config->logCompression), mimeType, config->logCompression);
else
uploadFile(path, istream, mimeType, "");
}
@ -504,14 +524,14 @@ struct S3BinaryCacheStoreImpl : virtual S3BinaryCacheStoreConfig, public virtual
stats.get++;
// FIXME: stream output to sink.
auto res = s3Helper.getObject(bucketName, path);
auto res = s3Helper.getObject(config->bucketName, path);
stats.getBytes += res.data ? res.data->size() : 0;
stats.getTimeMs += res.durationMs;
if (res.data) {
printTalkative("downloaded 's3://%s/%s' (%d bytes) in %d ms",
bucketName, path, res.data->size(), res.durationMs);
config->bucketName, path, res.data->size(), res.durationMs);
sink(*res.data);
} else
@ -523,6 +543,8 @@ struct S3BinaryCacheStoreImpl : virtual S3BinaryCacheStoreConfig, public virtual
StorePathSet paths;
std::string marker;
auto & bucketName = config->bucketName;
do {
debug("listing bucket 's3://%s' from key '%s'...", bucketName, marker);
@ -561,7 +583,15 @@ struct S3BinaryCacheStoreImpl : virtual S3BinaryCacheStoreConfig, public virtual
}
};
static RegisterStoreImplementation<S3BinaryCacheStoreImpl, S3BinaryCacheStoreConfig> regS3BinaryCacheStore;
ref<Store> S3BinaryCacheStoreImpl::Config::openStore() const
{
return make_ref<S3BinaryCacheStoreImpl>(ref{
// FIXME we shouldn't actually need a mutable config
std::const_pointer_cast<S3BinaryCacheStore::Config>(shared_from_this())
});
}
static RegisterStoreImplementation<S3BinaryCacheStoreImpl::Config> regS3BinaryCacheStore;
}

View file

@ -7,6 +7,7 @@
#include "nix/store/worker-protocol-impl.hh"
#include "nix/util/pool.hh"
#include "nix/store/ssh.hh"
#include "nix/store/store-registration.hh"
namespace nix {
@ -14,12 +15,13 @@ SSHStoreConfig::SSHStoreConfig(
std::string_view scheme,
std::string_view authority,
const Params & params)
: StoreConfig(params)
, RemoteStoreConfig(params)
, CommonSSHStoreConfig(scheme, authority, params)
: Store::Config{params}
, RemoteStore::Config{params}
, CommonSSHStoreConfig{scheme, authority, params}
{
}
std::string SSHStoreConfig::doc()
{
return
@ -27,21 +29,18 @@ std::string SSHStoreConfig::doc()
;
}
class SSHStore : public virtual SSHStoreConfig, public virtual RemoteStore
{
public:
SSHStore(
std::string_view scheme,
std::string_view host,
const Params & params)
: StoreConfig(params)
, RemoteStoreConfig(params)
, CommonSSHStoreConfig(scheme, host, params)
, SSHStoreConfig(scheme, host, params)
, Store(params)
, RemoteStore(params)
, master(createSSHMaster(
struct SSHStore : virtual RemoteStore
{
using Config = SSHStoreConfig;
ref<const Config> config;
SSHStore(ref<const Config> config)
: Store{*config}
, RemoteStore{*config}
, config{config}
, master(config->createSSHMaster(
// Use SSH master only if using more than 1 connection.
connections->capacity() > 1))
{
@ -49,7 +48,7 @@ public:
std::string getUri() override
{
return *uriSchemes().begin() + "://" + host;
return *Config::uriSchemes().begin() + "://" + host;
}
// FIXME extend daemon protocol, move implementation to RemoteStore
@ -101,7 +100,7 @@ MountedSSHStoreConfig::MountedSSHStoreConfig(std::string_view scheme, std::strin
: StoreConfig(params)
, RemoteStoreConfig(params)
, CommonSSHStoreConfig(scheme, host, params)
, SSHStoreConfig(params)
, SSHStoreConfig(scheme, host, params)
, LocalFSStoreConfig(params)
{
}
@ -128,35 +127,21 @@ std::string MountedSSHStoreConfig::doc()
* The difference lies in how they manage GC roots. See addPermRoot
* below for details.
*/
class MountedSSHStore : public virtual MountedSSHStoreConfig, public virtual SSHStore, public virtual LocalFSStore
struct MountedSSHStore : virtual SSHStore, virtual LocalFSStore
{
public:
using Config = MountedSSHStoreConfig;
MountedSSHStore(
std::string_view scheme,
std::string_view host,
const Params & params)
: StoreConfig(params)
, RemoteStoreConfig(params)
, CommonSSHStoreConfig(scheme, host, params)
, SSHStoreConfig(params)
, LocalFSStoreConfig(params)
, MountedSSHStoreConfig(params)
, Store(params)
, RemoteStore(params)
, SSHStore(scheme, host, params)
, LocalFSStore(params)
MountedSSHStore(ref<const Config> config)
: Store{*config}
, RemoteStore{*config}
, SSHStore{config}
, LocalFSStore{*config}
{
extraRemoteProgramArgs = {
"--process-ops",
};
}
std::string getUri() override
{
return *uriSchemes().begin() + "://" + host;
}
void narFromPath(const StorePath & path, Sink & sink) override
{
return LocalFSStore::narFromPath(path, sink);
@ -198,14 +183,26 @@ public:
}
};
ref<Store> SSHStore::Config::openStore() const {
return make_ref<SSHStore>(ref{shared_from_this()});
}
ref<Store> MountedSSHStore::Config::openStore() const {
return make_ref<MountedSSHStore>(ref{
std::dynamic_pointer_cast<const MountedSSHStore::Config>(shared_from_this())
});
}
ref<RemoteStore::Connection> SSHStore::openConnection()
{
auto conn = make_ref<Connection>();
Strings command = remoteProgram.get();
Strings command = config->remoteProgram.get();
command.push_back("--stdio");
if (remoteStore.get() != "") {
if (config->remoteStore.get() != "") {
command.push_back("--store");
command.push_back(remoteStore.get());
command.push_back(config->remoteStore.get());
}
command.insert(command.end(),
extraRemoteProgramArgs.begin(), extraRemoteProgramArgs.end());
@ -215,7 +212,7 @@ ref<RemoteStore::Connection> SSHStore::openConnection()
return conn;
}
static RegisterStoreImplementation<SSHStore, SSHStoreConfig> regSSHStore;
static RegisterStoreImplementation<MountedSSHStore, MountedSSHStoreConfig> regMountedSSHStore;
static RegisterStoreImplementation<SSHStore::Config> regSSHStore;
static RegisterStoreImplementation<MountedSSHStore::Config> regMountedSSHStore;
}

View file

@ -5,6 +5,7 @@
#include "nix/store/realisation.hh"
#include "nix/store/derivations.hh"
#include "nix/store/store-api.hh"
#include "nix/store/store-open.hh"
#include "nix/util/util.hh"
#include "nix/store/nar-info-disk-cache.hh"
#include "nix/util/thread-pool.hh"
@ -29,13 +30,13 @@ using json = nlohmann::json;
namespace nix {
bool StoreDirConfig::isInStore(PathView path) const
bool MixStoreDirMethods::isInStore(PathView path) const
{
return isInDir(path, storeDir);
}
std::pair<StorePath, Path> StoreDirConfig::toStorePath(PathView path) const
std::pair<StorePath, Path> MixStoreDirMethods::toStorePath(PathView path) const
{
if (!isInStore(path))
throw Error("path '%1%' is not in the Nix store", path);
@ -77,7 +78,7 @@ to match.
*/
StorePath StoreDirConfig::makeStorePath(std::string_view type,
StorePath MixStoreDirMethods::makeStorePath(std::string_view type,
std::string_view hash, std::string_view name) const
{
/* e.g., "source:sha256:1abc...:/nix/store:foo.tar.gz" */
@ -88,14 +89,14 @@ StorePath StoreDirConfig::makeStorePath(std::string_view type,
}
StorePath StoreDirConfig::makeStorePath(std::string_view type,
StorePath MixStoreDirMethods::makeStorePath(std::string_view type,
const Hash & hash, std::string_view name) const
{
return makeStorePath(type, hash.to_string(HashFormat::Base16, true), name);
}
StorePath StoreDirConfig::makeOutputPath(std::string_view id,
StorePath MixStoreDirMethods::makeOutputPath(std::string_view id,
const Hash & hash, std::string_view name) const
{
return makeStorePath("output:" + std::string { id }, hash, outputPathName(name, id));
@ -106,7 +107,7 @@ StorePath StoreDirConfig::makeOutputPath(std::string_view id,
hacky, but we can't put them in, say, <s2> (per the grammar above)
since that would be ambiguous. */
static std::string makeType(
const StoreDirConfig & store,
const MixStoreDirMethods & store,
std::string && type,
const StoreReferences & references)
{
@ -119,7 +120,7 @@ static std::string makeType(
}
StorePath StoreDirConfig::makeFixedOutputPath(std::string_view name, const FixedOutputInfo & info) const
StorePath MixStoreDirMethods::makeFixedOutputPath(std::string_view name, const FixedOutputInfo & info) const
{
if (info.method == FileIngestionMethod::Git && info.hash.algo != HashAlgorithm::SHA1)
throw Error("Git file ingestion must use SHA-1 hash");
@ -141,7 +142,7 @@ StorePath StoreDirConfig::makeFixedOutputPath(std::string_view name, const Fixed
}
StorePath StoreDirConfig::makeFixedOutputPathFromCA(std::string_view name, const ContentAddressWithReferences & ca) const
StorePath MixStoreDirMethods::makeFixedOutputPathFromCA(std::string_view name, const ContentAddressWithReferences & ca) const
{
// New template
return std::visit(overloaded {
@ -162,7 +163,7 @@ StorePath StoreDirConfig::makeFixedOutputPathFromCA(std::string_view name, const
}
std::pair<StorePath, Hash> StoreDirConfig::computeStorePath(
std::pair<StorePath, Hash> MixStoreDirMethods::computeStorePath(
std::string_view name,
const SourcePath & path,
ContentAddressMethod method,
@ -424,7 +425,7 @@ ValidPathInfo Store::addToStoreSlow(
return info;
}
StringSet StoreConfig::getDefaultSystemFeatures()
StringSet Store::Config::getDefaultSystemFeatures()
{
auto res = settings.systemFeatures.get();
@ -437,9 +438,10 @@ StringSet StoreConfig::getDefaultSystemFeatures()
return res;
}
Store::Store(const Params & params)
: StoreConfig(params)
, state({(size_t) pathInfoCacheSize})
Store::Store(const Store::Config & config)
: MixStoreDirMethods{config}
, config{config}
, state({(size_t) config.pathInfoCacheSize})
{
assertLibStoreInitialized();
}
@ -573,7 +575,7 @@ bool Store::isValidPath(const StorePath & storePath)
{
{
auto state_(state.lock());
auto res = state_->pathInfoCache.get(std::string(storePath.to_string()));
auto res = state_->pathInfoCache.get(storePath.to_string());
if (res && res->isKnownNow()) {
stats.narInfoReadAverted++;
return res->didExist();
@ -585,7 +587,7 @@ bool Store::isValidPath(const StorePath & storePath)
if (res.first != NarInfoDiskCache::oUnknown) {
stats.narInfoReadAverted++;
auto state_(state.lock());
state_->pathInfoCache.upsert(std::string(storePath.to_string()),
state_->pathInfoCache.upsert(storePath.to_string(),
res.first == NarInfoDiskCache::oInvalid ? PathInfoCacheValue{} : PathInfoCacheValue { .value = res.second });
return res.first == NarInfoDiskCache::oValid;
}
@ -644,7 +646,7 @@ std::optional<std::shared_ptr<const ValidPathInfo>> Store::queryPathInfoFromClie
auto hashPart = std::string(storePath.hashPart());
{
auto res = state.lock()->pathInfoCache.get(std::string(storePath.to_string()));
auto res = state.lock()->pathInfoCache.get(storePath.to_string());
if (res && res->isKnownNow()) {
stats.narInfoReadAverted++;
if (res->didExist())
@ -660,7 +662,7 @@ std::optional<std::shared_ptr<const ValidPathInfo>> Store::queryPathInfoFromClie
stats.narInfoReadAverted++;
{
auto state_(state.lock());
state_->pathInfoCache.upsert(std::string(storePath.to_string()),
state_->pathInfoCache.upsert(storePath.to_string(),
res.first == NarInfoDiskCache::oInvalid ? PathInfoCacheValue{} : PathInfoCacheValue{ .value = res.second });
if (res.first == NarInfoDiskCache::oInvalid ||
!goodStorePath(storePath, res.second->path))
@ -704,7 +706,7 @@ void Store::queryPathInfo(const StorePath & storePath,
{
auto state_(state.lock());
state_->pathInfoCache.upsert(std::string(storePath.to_string()), PathInfoCacheValue { .value = info });
state_->pathInfoCache.upsert(storePath.to_string(), PathInfoCacheValue { .value = info });
}
if (!info || !goodStorePath(storePath, info->path)) {
@ -1209,7 +1211,7 @@ std::optional<ValidPathInfo> decodeValidPathInfo(const Store & store, std::istre
}
std::string StoreDirConfig::showPaths(const StorePathSet & paths)
std::string MixStoreDirMethods::showPaths(const StorePathSet & paths) const
{
std::string s;
for (auto & i : paths) {
@ -1237,7 +1239,7 @@ static Derivation readDerivationCommon(Store & store, const StorePath & drvPath,
auto accessor = store.getFSAccessor(requireValidPath);
try {
return parseDerivation(store,
accessor->readFile(CanonPath(store.printStorePath(drvPath))),
accessor->readFile(CanonPath(drvPath.to_string())),
Derivation::nameFromPath(drvPath));
} catch (FormatError & e) {
throw Error("error parsing derivation '%s': %s", store.printStorePath(drvPath), e.msg());
@ -1278,103 +1280,32 @@ Derivation Store::readDerivation(const StorePath & drvPath)
Derivation Store::readInvalidDerivation(const StorePath & drvPath)
{ return readDerivationCommon(*this, drvPath, false); }
}
#include "nix/store/local-store.hh"
#include "nix/store/uds-remote-store.hh"
namespace nix {
ref<Store> openStore(const std::string & uri,
const Store::Params & extraParams)
void Store::signPathInfo(ValidPathInfo & info)
{
return openStore(StoreReference::parse(uri, extraParams));
// FIXME: keep secret keys in memory.
auto secretKeyFiles = settings.secretKeyFiles;
for (auto & secretKeyFile : secretKeyFiles.get()) {
SecretKey secretKey(readFile(secretKeyFile));
LocalSigner signer(std::move(secretKey));
info.sign(*this, signer);
}
}
ref<Store> openStore(StoreReference && storeURI)
void Store::signRealisation(Realisation & realisation)
{
auto & params = storeURI.params;
// FIXME: keep secret keys in memory.
auto store = std::visit(overloaded {
[&](const StoreReference::Auto &) -> std::shared_ptr<Store> {
auto stateDir = getOr(params, "state", settings.nixStateDir);
if (access(stateDir.c_str(), R_OK | W_OK) == 0)
return std::make_shared<LocalStore>(params);
else if (pathExists(settings.nixDaemonSocketFile))
return std::make_shared<UDSRemoteStore>(params);
#ifdef __linux__
else if (!pathExists(stateDir)
&& params.empty()
&& !isRootUser()
&& !getEnv("NIX_STORE_DIR").has_value()
&& !getEnv("NIX_STATE_DIR").has_value())
{
/* If /nix doesn't exist, there is no daemon socket, and
we're not root, then automatically set up a chroot
store in ~/.local/share/nix/root. */
auto chrootStore = getDataDir() + "/root";
if (!pathExists(chrootStore)) {
try {
createDirs(chrootStore);
} catch (SystemError & e) {
return std::make_shared<LocalStore>(params);
}
warn("'%s' does not exist, so Nix will use '%s' as a chroot store", stateDir, chrootStore);
} else
debug("'%s' does not exist, so Nix will use '%s' as a chroot store", stateDir, chrootStore);
return std::make_shared<LocalStore>("local", chrootStore, params);
}
#endif
else
return std::make_shared<LocalStore>(params);
},
[&](const StoreReference::Specified & g) {
for (const auto & implem : *Implementations::registered)
if (implem.uriSchemes.count(g.scheme))
return implem.create(g.scheme, g.authority, params);
auto secretKeyFiles = settings.secretKeyFiles;
throw Error("don't know how to open Nix store with scheme '%s'", g.scheme);
},
}, storeURI.variant);
experimentalFeatureSettings.require(store->experimentalFeature());
store->warnUnknownSettings();
store->init();
return ref<Store> { store };
for (auto & secretKeyFile : secretKeyFiles.get()) {
SecretKey secretKey(readFile(secretKeyFile));
LocalSigner signer(std::move(secretKey));
realisation.sign(signer);
}
}
std::list<ref<Store>> getDefaultSubstituters()
{
static auto stores([]() {
std::list<ref<Store>> stores;
StringSet done;
auto addStore = [&](const std::string & uri) {
if (!done.insert(uri).second) return;
try {
stores.push_back(openStore(uri));
} catch (Error & e) {
logWarning(e.info());
}
};
for (const auto & uri : settings.substituters.get())
addStore(uri);
stores.sort([](ref<Store> & a, ref<Store> & b) {
return a->priority < b->priority;
});
return stores;
} ());
return stores;
}
std::vector<StoreFactory> * Implementations::registered = 0;
}

View file

@ -0,0 +1,13 @@
#include "nix/store/store-dir-config.hh"
#include "nix/util/util.hh"
#include "nix/store/globals.hh"
namespace nix {
StoreDirConfig::StoreDirConfig(const Params & params)
: StoreDirConfigBase(params)
, MixStoreDirMethods{storeDir_}
{
}
}

View file

@ -0,0 +1,105 @@
#include "nix/store/store-registration.hh"
#include "nix/store/store-open.hh"
#include "nix/store/local-store.hh"
#include "nix/store/uds-remote-store.hh"
namespace nix {
ref<Store> openStore(const std::string & uri, const Store::Config::Params & extraParams)
{
return openStore(StoreReference::parse(uri, extraParams));
}
ref<Store> openStore(StoreReference && storeURI)
{
auto store = resolveStoreConfig(std::move(storeURI))->openStore();
store->init();
return store;
}
ref<StoreConfig> resolveStoreConfig(StoreReference && storeURI)
{
auto & params = storeURI.params;
auto storeConfig = std::visit(
overloaded{
[&](const StoreReference::Auto &) -> ref<StoreConfig> {
auto stateDir = getOr(params, "state", settings.nixStateDir);
if (access(stateDir.c_str(), R_OK | W_OK) == 0)
return make_ref<LocalStore::Config>(params);
else if (pathExists(settings.nixDaemonSocketFile))
return make_ref<UDSRemoteStore::Config>(params);
#ifdef __linux__
else if (
!pathExists(stateDir) && params.empty() && !isRootUser() && !getEnv("NIX_STORE_DIR").has_value()
&& !getEnv("NIX_STATE_DIR").has_value()) {
/* If /nix doesn't exist, there is no daemon socket, and
we're not root, then automatically set up a chroot
store in ~/.local/share/nix/root. */
auto chrootStore = getDataDir() + "/root";
if (!pathExists(chrootStore)) {
try {
createDirs(chrootStore);
} catch (SystemError & e) {
return make_ref<LocalStore::Config>(params);
}
warn("'%s' does not exist, so Nix will use '%s' as a chroot store", stateDir, chrootStore);
} else
debug("'%s' does not exist, so Nix will use '%s' as a chroot store", stateDir, chrootStore);
return make_ref<LocalStore::Config>("local", chrootStore, params);
}
#endif
else
return make_ref<LocalStore::Config>(params);
},
[&](const StoreReference::Specified & g) {
for (const auto & [storeName, implem] : Implementations::registered())
if (implem.uriSchemes.count(g.scheme))
return implem.parseConfig(g.scheme, g.authority, params);
throw Error("don't know how to open Nix store with scheme '%s'", g.scheme);
},
},
storeURI.variant);
experimentalFeatureSettings.require(storeConfig->experimentalFeature());
storeConfig->warnUnknownSettings();
return storeConfig;
}
std::list<ref<Store>> getDefaultSubstituters()
{
static auto stores([]() {
std::list<ref<Store>> stores;
StringSet done;
auto addStore = [&](const std::string & uri) {
if (!done.insert(uri).second)
return;
try {
stores.push_back(openStore(uri));
} catch (Error & e) {
logWarning(e.info());
}
};
for (const auto & uri : settings.substituters.get())
addStore(uri);
stores.sort([](ref<Store> & a, ref<Store> & b) { return a->config.priority < b->config.priority; });
return stores;
}());
return stores;
}
Implementations::Map & Implementations::registered()
{
static Map registered;
return registered;
}
}

View file

@ -1,6 +1,7 @@
#include "nix/store/uds-remote-store.hh"
#include "nix/util/unix-domain-socket.hh"
#include "nix/store/worker-protocol.hh"
#include "nix/store/store-registration.hh"
#include <sys/types.h>
#include <sys/stat.h>
@ -20,13 +21,13 @@ namespace nix {
UDSRemoteStoreConfig::UDSRemoteStoreConfig(
std::string_view scheme,
std::string_view authority,
const Params & params)
: StoreConfig(params)
, LocalFSStoreConfig(params)
, RemoteStoreConfig(params)
const StoreReference::Params & params)
: Store::Config{params}
, LocalFSStore::Config{params}
, RemoteStore::Config{params}
, path{authority.empty() ? settings.nixDaemonSocketFile : authority}
{
if (scheme != UDSRemoteStoreConfig::scheme) {
if (uriSchemes().count(scheme) == 0) {
throw UsageError("Scheme must be 'unix'");
}
}
@ -44,32 +45,30 @@ std::string UDSRemoteStoreConfig::doc()
// empty string will later default to the same nixDaemonSocketFile. Why
// don't we just wire it all through? I believe there are cases where it
// will live reload so we want to continue to account for that.
UDSRemoteStore::UDSRemoteStore(const Params & params)
: UDSRemoteStore(scheme, "", params)
{}
UDSRemoteStoreConfig::UDSRemoteStoreConfig(const Params & params)
: UDSRemoteStoreConfig(*uriSchemes().begin(), "", params)
{
}
UDSRemoteStore::UDSRemoteStore(std::string_view scheme, std::string_view authority, const Params & params)
: StoreConfig(params)
, LocalFSStoreConfig(params)
, RemoteStoreConfig(params)
, UDSRemoteStoreConfig(scheme, authority, params)
, Store(params)
, LocalFSStore(params)
, RemoteStore(params)
UDSRemoteStore::UDSRemoteStore(ref<const Config> config)
: Store{*config}
, LocalFSStore{*config}
, RemoteStore{*config}
, config{config}
{
}
std::string UDSRemoteStore::getUri()
{
return path == settings.nixDaemonSocketFile
return config->path == settings.nixDaemonSocketFile
? // FIXME: Not clear why we return daemon here and not default
// to settings.nixDaemonSocketFile
//
// unix:// with no path also works. Change what we return?
"daemon"
: std::string(scheme) + "://" + path;
: std::string(*Config::uriSchemes().begin()) + "://" + config->path;
}
@ -84,7 +83,7 @@ ref<RemoteStore::Connection> UDSRemoteStore::openConnection()
auto conn = make_ref<Connection>();
/* Connect to a daemon that does the privileged work for us. */
conn->fd = nix::connect(path);
conn->fd = nix::connect(config->path);
conn->from.fd = conn->fd.get();
conn->to.fd = conn->fd.get();
@ -104,6 +103,11 @@ void UDSRemoteStore::addIndirectRoot(const Path & path)
}
static RegisterStoreImplementation<UDSRemoteStore, UDSRemoteStoreConfig> regUDSRemoteStore;
ref<Store> UDSRemoteStore::Config::openStore() const {
return make_ref<UDSRemoteStore>(ref{shared_from_this()});
}
static RegisterStoreImplementation<UDSRemoteStore::Config> regUDSRemoteStore;
}

View file

@ -0,0 +1,209 @@
#pragma once
///@file
#include "nix/store/build-result.hh"
#include "nix/store/derivation-options.hh"
#include "nix/store/build/derivation-building-misc.hh"
#include "nix/store/derivations.hh"
#include "nix/store/parsed-derivations.hh"
#include "nix/util/processes.hh"
#include "nix/store/restricted-store.hh"
#include "nix/store/user-lock.hh"
namespace nix {
/**
* Parameters by (mostly) `const` reference for `DerivationBuilder`.
*/
struct DerivationBuilderParams
{
/** The path of the derivation. */
const StorePath & drvPath;
BuildResult & buildResult;
/**
* The derivation stored at drvPath.
*/
const Derivation & drv;
/**
* The "structured attrs" of `drv`, if it has them.
*
* @todo this should be part of `Derivation`.
*
* @todo this should be renamed from `parsedDrv`.
*/
const StructuredAttrs * parsedDrv;
/**
* The derivation options of `drv`.
*
* @todo this should be part of `Derivation`.
*/
const DerivationOptions & drvOptions;
// The remainder is state held during the build.
/**
* All input paths (that is, the union of FS closures of the
* immediate input paths).
*/
const StorePathSet & inputPaths;
/**
* @note we do in fact mutate this
*/
std::map<std::string, InitialOutput> & initialOutputs;
const BuildMode & buildMode;
DerivationBuilderParams(
const StorePath & drvPath,
const BuildMode & buildMode,
BuildResult & buildResult,
const Derivation & drv,
const StructuredAttrs * parsedDrv,
const DerivationOptions & drvOptions,
const StorePathSet & inputPaths,
std::map<std::string, InitialOutput> & initialOutputs)
: drvPath{drvPath}
, buildResult{buildResult}
, drv{drv}
, parsedDrv{parsedDrv}
, drvOptions{drvOptions}
, inputPaths{inputPaths}
, initialOutputs{initialOutputs}
, buildMode{buildMode}
{ }
DerivationBuilderParams(DerivationBuilderParams &&) = default;
};
/**
* Callbacks that `DerivationBuilder` needs.
*/
struct DerivationBuilderCallbacks
{
virtual ~DerivationBuilderCallbacks() = default;
/**
* Open a log file and a pipe to it.
*/
virtual Path openLogFile() = 0;
/**
* Close the log file.
*/
virtual void closeLogFile() = 0;
/**
* Aborts if any output is not valid or corrupt, and otherwise
* returns a 'SingleDrvOutputs' structure containing all outputs.
*
* @todo Probably should just be in `DerivationGoal`.
*/
virtual SingleDrvOutputs assertPathValidity() = 0;
virtual void appendLogTailErrorMsg(std::string & msg) = 0;
/**
* Hook up `builderOut` to some mechanism to ingest the log
*
* @todo this should be reworked
*/
virtual void childStarted(Descriptor builderOut) = 0;
/**
* @todo this should be reworked
*/
virtual void childTerminated() = 0;
virtual void noteHashMismatch(void) = 0;
virtual void noteCheckMismatch(void) = 0;
virtual void markContentsGood(const StorePath & path) = 0;
};
/**
* This class represents the state for building locally.
*
* @todo Ideally, it would not be a class, but a single function.
* However, besides the main entry point, there are a few more methods
* which are externally called, and need to be gotten rid of. There are
* also some virtual methods (either directly here or inherited from
* `DerivationBuilderCallbacks`, a stop-gap) that represent outgoing
* rather than incoming call edges that either should be removed, or
* become (higher order) function parameters.
*/
struct DerivationBuilder : RestrictionContext
{
/**
* User selected for running the builder.
*/
std::unique_ptr<UserLock> buildUser;
/**
* The process ID of the builder.
*/
Pid pid;
DerivationBuilder() = default;
virtual ~DerivationBuilder() = default;
/**
* Master side of the pseudoterminal used for the builder's
* standard output/error.
*/
AutoCloseFD builderOut;
/**
* Set up build environment / sandbox, acquiring resources (e.g.
* locks as needed). After this is run, the builder should be
* started.
*
* @returns true if successful, false if we could not acquire a build
* user. In that case, the caller must wait and then try again.
*/
virtual bool prepareBuild() = 0;
/**
* Start building a derivation.
*/
virtual void startBuilder() = 0;
/**
* Tear down build environment after the builder exits (either on
* its own or if it is killed).
*
* @returns The first case indicates failure during output
* processing. A status code and exception are returned, providing
* more information. The second case indicates success, and
* realisations for each output of the derivation are returned.
*/
virtual std::variant<std::pair<BuildResult::Status, Error>, SingleDrvOutputs> unprepareBuild() = 0;
/**
* Stop the in-process nix daemon thread.
* @see startDaemon
*/
virtual void stopDaemon() = 0;
/**
* Delete the temporary directory, if we have one.
*/
virtual void deleteTmpDir(bool force) = 0;
/**
* Kill any processes running under the build user UID or in the
* cgroup of the build.
*/
virtual void killSandbox(bool getStats) = 0;
};
std::unique_ptr<DerivationBuilder> makeDerivationBuilder(
Store & store,
std::unique_ptr<DerivationBuilderCallbacks> miscMethods,
DerivationBuilderParams params);
}

View file

@ -1,319 +0,0 @@
#pragma once
///@file
#include "nix/store/build/derivation-goal.hh"
#include "nix/store/local-store.hh"
#include "nix/util/processes.hh"
namespace nix {
struct LocalDerivationGoal : public DerivationGoal
{
LocalStore & getLocalStore();
/**
* User selected for running the builder.
*/
std::unique_ptr<UserLock> buildUser;
/**
* The process ID of the builder.
*/
Pid pid;
/**
* The cgroup of the builder, if any.
*/
std::optional<Path> cgroup;
/**
* The temporary directory used for the build.
*/
Path tmpDir;
/**
* The top-level temporary directory. `tmpDir` is either equal to
* or a child of this directory.
*/
Path topTmpDir;
/**
* The path of the temporary directory in the sandbox.
*/
Path tmpDirInSandbox;
/**
* Master side of the pseudoterminal used for the builder's
* standard output/error.
*/
AutoCloseFD builderOut;
/**
* Pipe for synchronising updates to the builder namespaces.
*/
Pipe userNamespaceSync;
/**
* The mount namespace and user namespace of the builder, used to add additional
* paths to the sandbox as a result of recursive Nix calls.
*/
AutoCloseFD sandboxMountNamespace;
AutoCloseFD sandboxUserNamespace;
/**
* On Linux, whether we're doing the build in its own user
* namespace.
*/
bool usingUserNamespace = true;
/**
* Whether we're currently doing a chroot build.
*/
bool useChroot = false;
/**
* The parent directory of `chrootRootDir`. It has permission 700
* and is owned by root to ensure other users cannot mess with
* `chrootRootDir`.
*/
Path chrootParentDir;
/**
* The root of the chroot environment.
*/
Path chrootRootDir;
/**
* RAII object to delete the chroot directory.
*/
std::shared_ptr<AutoDelete> autoDelChroot;
/**
* Whether to run the build in a private network namespace.
*/
bool privateNetwork = false;
/**
* Stuff we need to pass to initChild().
*/
struct ChrootPath {
Path source;
bool optional;
ChrootPath(Path source = "", bool optional = false)
: source(source), optional(optional)
{ }
};
typedef map<Path, ChrootPath> PathsInChroot; // maps target path to source path
PathsInChroot pathsInChroot;
typedef map<std::string, std::string> Environment;
Environment env;
/**
* Hash rewriting.
*/
StringMap inputRewrites, outputRewrites;
typedef map<StorePath, StorePath> RedirectedOutputs;
RedirectedOutputs redirectedOutputs;
/**
* The output paths used during the build.
*
* - Input-addressed derivations or fixed content-addressed outputs are
* sometimes built when some of their outputs already exist, and can not
* be hidden via sandboxing. We use temporary locations instead and
* rewrite after the build. Otherwise the regular predetermined paths are
* put here.
*
* - Floating content-addressing derivations do not know their final build
* output paths until the outputs are hashed, so random locations are
* used, and then renamed. The randomness helps guard against hidden
* self-references.
*/
OutputPathMap scratchOutputs;
uid_t sandboxUid() { return usingUserNamespace ? (!buildUser || buildUser->getUIDCount() == 1 ? 1000 : 0) : buildUser->getUID(); }
gid_t sandboxGid() { return usingUserNamespace ? (!buildUser || buildUser->getUIDCount() == 1 ? 100 : 0) : buildUser->getGID(); }
const static Path homeDir;
/**
* The recursive Nix daemon socket.
*/
AutoCloseFD daemonSocket;
/**
* The daemon main thread.
*/
std::thread daemonThread;
/**
* The daemon worker threads.
*/
std::vector<std::thread> daemonWorkerThreads;
/**
* Paths that were added via recursive Nix calls.
*/
StorePathSet addedPaths;
/**
* Realisations that were added via recursive Nix calls.
*/
std::set<DrvOutput> addedDrvOutputs;
/**
* Recursive Nix calls are only allowed to build or realize paths
* in the original input closure or added via a recursive Nix call
* (so e.g. you can't do 'nix-store -r /nix/store/<bla>' where
* /nix/store/<bla> is some arbitrary path in a binary cache).
*/
bool isAllowed(const StorePath & path)
{
return inputPaths.count(path) || addedPaths.count(path);
}
bool isAllowed(const DrvOutput & id)
{
return addedDrvOutputs.count(id);
}
bool isAllowed(const DerivedPath & req);
friend struct RestrictedStore;
using DerivationGoal::DerivationGoal;
virtual ~LocalDerivationGoal() override;
/**
* Whether we need to perform hash rewriting if there are valid output paths.
*/
bool needsHashRewrite();
/**
* The additional states.
*/
Goal::Co tryLocalBuild() override;
/**
* Start building a derivation.
*/
void startBuilder();
/**
* Fill in the environment for the builder.
*/
void initEnv();
/**
* Process messages send by the sandbox initialization.
*/
void processSandboxSetupMessages();
/**
* Setup tmp dir location.
*/
void initTmpDir();
/**
* Write a JSON file containing the derivation attributes.
*/
void writeStructuredAttrs();
/**
* Start an in-process nix daemon thread for recursive-nix.
*/
void startDaemon();
/**
* Stop the in-process nix daemon thread.
* @see startDaemon
*/
void stopDaemon();
/**
* Add 'path' to the set of paths that may be referenced by the
* outputs, and make it appear in the sandbox.
*/
void addDependency(const StorePath & path);
/**
* Make a file owned by the builder.
*/
void chownToBuilder(const Path & path);
int getChildStatus() override;
/**
* Run the builder's process.
*/
void runChild();
/**
* Check that the derivation outputs all exist and register them
* as valid.
*/
SingleDrvOutputs registerOutputs() override;
void signRealisation(Realisation &) override;
/**
* Check that an output meets the requirements specified by the
* 'outputChecks' attribute (or the legacy
* '{allowed,disallowed}{References,Requisites}' attributes).
*/
void checkOutputs(const std::map<std::string, ValidPathInfo> & outputs);
/**
* Close the read side of the logger pipe.
*/
void closeReadPipes() override;
/**
* Cleanup hooks for buildDone()
*/
void cleanupHookFinally() override;
void cleanupPreChildKill() override;
void cleanupPostChildKill() override;
bool cleanupDecideWhetherDiskFull() override;
void cleanupPostOutputsRegisteredModeCheck() override;
void cleanupPostOutputsRegisteredModeNonCheck() override;
bool isReadDesc(int fd) override;
/**
* Delete the temporary directory, if we have one.
*/
void deleteTmpDir(bool force);
/**
* Forcibly kill the child process, if any.
*
* Called by destructor, can't be overridden
*/
void killChild() override final;
/**
* Kill any processes running under the build user UID or in the
* cgroup of the build.
*/
void killSandbox(bool getStats);
/**
* Create alternative path calculated from but distinct from the
* input, so we can avoid overwriting outputs (or other store paths)
* that already exist.
*/
StorePath makeFallbackPath(const StorePath & path);
/**
* Make a path to another based on the output name along with the
* derivation hash.
*
* @todo Add option to randomize, so we can audit whether our
* rewrites caught everything
*/
StorePath makeFallbackPath(OutputNameView outputName);
};
}

View file

@ -2,7 +2,7 @@ include_dirs += include_directories('../..')
headers += files(
'build/child.hh',
'build/derivation-builder.hh',
'build/hook-instance.hh',
'build/local-derivation-goal.hh',
'user-lock.hh',
)

View file

@ -1,7 +1,7 @@
sources += files(
'build/child.cc',
'build/derivation-builder.cc',
'build/hook-instance.cc',
'build/local-derivation-goal.cc',
'pathlocks.cc',
'user-lock.cc',
)

View file

@ -5,7 +5,7 @@
namespace nix {
const std::set<WorkerProto::Feature> WorkerProto::allFeatures{};
const WorkerProto::FeatureSet WorkerProto::allFeatures{};
WorkerProto::BasicClientConnection::~BasicClientConnection()
{
@ -146,21 +146,20 @@ void WorkerProto::BasicClientConnection::processStderr(
}
}
static std::set<WorkerProto::Feature>
intersectFeatures(const std::set<WorkerProto::Feature> & a, const std::set<WorkerProto::Feature> & b)
static WorkerProto::FeatureSet intersectFeatures(const WorkerProto::FeatureSet & a, const WorkerProto::FeatureSet & b)
{
std::set<WorkerProto::Feature> res;
WorkerProto::FeatureSet res;
for (auto & x : a)
if (b.contains(x))
res.insert(x);
return res;
}
std::tuple<WorkerProto::Version, std::set<WorkerProto::Feature>> WorkerProto::BasicClientConnection::handshake(
std::tuple<WorkerProto::Version, WorkerProto::FeatureSet> WorkerProto::BasicClientConnection::handshake(
BufferedSink & to,
Source & from,
WorkerProto::Version localVersion,
const std::set<WorkerProto::Feature> & supportedFeatures)
const WorkerProto::FeatureSet & supportedFeatures)
{
to << WORKER_MAGIC_1 << localVersion;
to.flush();
@ -178,21 +177,21 @@ std::tuple<WorkerProto::Version, std::set<WorkerProto::Feature>> WorkerProto::Ba
auto protoVersion = std::min(daemonVersion, localVersion);
/* Exchange features. */
std::set<WorkerProto::Feature> daemonFeatures;
WorkerProto::FeatureSet daemonFeatures;
if (GET_PROTOCOL_MINOR(protoVersion) >= 38) {
to << supportedFeatures;
to.flush();
daemonFeatures = readStrings<std::set<WorkerProto::Feature>>(from);
daemonFeatures = readStrings<WorkerProto::FeatureSet>(from);
}
return {protoVersion, intersectFeatures(daemonFeatures, supportedFeatures)};
}
std::tuple<WorkerProto::Version, std::set<WorkerProto::Feature>> WorkerProto::BasicServerConnection::handshake(
std::tuple<WorkerProto::Version, WorkerProto::FeatureSet> WorkerProto::BasicServerConnection::handshake(
BufferedSink & to,
Source & from,
WorkerProto::Version localVersion,
const std::set<WorkerProto::Feature> & supportedFeatures)
const WorkerProto::FeatureSet & supportedFeatures)
{
unsigned int magic = readInt(from);
if (magic != WORKER_MAGIC_1)
@ -204,9 +203,9 @@ std::tuple<WorkerProto::Version, std::set<WorkerProto::Feature>> WorkerProto::Ba
auto protoVersion = std::min(clientVersion, localVersion);
/* Exchange features. */
std::set<WorkerProto::Feature> clientFeatures;
WorkerProto::FeatureSet clientFeatures;
if (GET_PROTOCOL_MINOR(protoVersion) >= 38) {
clientFeatures = readStrings<std::set<WorkerProto::Feature>>(from);
clientFeatures = readStrings<WorkerProto::FeatureSet>(from);
to << supportedFeatures;
to.flush();
}