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:
commit
c20642ac7b
354 changed files with 6768 additions and 3808 deletions
|
|
@ -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
|
|
@ -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");
|
||||
|
||||
|
|
|
|||
|
|
@ -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{};
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 (...) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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>;
|
||||
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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(); };
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>)
|
||||
|
|
|
|||
|
|
@ -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_
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ struct CommonSSHStoreConfig : virtual StoreConfig
|
|||
*/
|
||||
SSHMaster createSSHMaster(
|
||||
bool useMaster,
|
||||
Descriptor logFD = INVALID_DESCRIPTOR);
|
||||
Descriptor logFD = INVALID_DESCRIPTOR) const;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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>;
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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',
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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. */
|
||||
|
||||
|
|
|
|||
60
src/libstore/include/nix/store/restricted-store.hh
Normal file
60
src/libstore/include/nix/store/restricted-store.hh
Normal 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);
|
||||
|
||||
}
|
||||
|
|
@ -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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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 ,
|
||||
|
|
|
|||
|
|
@ -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_
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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).
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
43
src/libstore/include/nix/store/store-open.hh
Normal file
43
src/libstore/include/nix/store/store-open.hh
Normal 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();
|
||||
|
||||
}
|
||||
88
src/libstore/include/nix/store/store-registration.hh
Normal file
88
src/libstore/include/nix/store/store-registration.hh
Normal 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>();
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 ,
|
||||
|
|
|
|||
|
|
@ -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_
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.)
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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',
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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"));
|
||||
|
|
|
|||
|
|
@ -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), ","));
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
332
src/libstore/restricted-store.cc
Normal file
332
src/libstore/restricted-store.cc
Normal 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
}
|
||||
|
|
|
|||
13
src/libstore/store-dir-config.cc
Normal file
13
src/libstore/store-dir-config.cc
Normal 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_}
|
||||
{
|
||||
}
|
||||
|
||||
}
|
||||
105
src/libstore/store-registration.cc
Normal file
105
src/libstore/store-registration.cc
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
209
src/libstore/unix/include/nix/store/build/derivation-builder.hh
Normal file
209
src/libstore/unix/include/nix/store/build/derivation-builder.hh
Normal 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);
|
||||
|
||||
}
|
||||
|
|
@ -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);
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -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',
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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',
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue