aboutsummaryrefslogtreecommitdiff
path: root/src/libstore
diff options
context:
space:
mode:
Diffstat (limited to 'src/libstore')
-rw-r--r--src/libstore/binary-cache-store.cc29
-rw-r--r--src/libstore/build/derivation-goal.cc128
-rw-r--r--src/libstore/build/drv-output-substitution-goal.cc27
-rw-r--r--src/libstore/build/local-derivation-goal.cc74
-rw-r--r--src/libstore/build/local-derivation-goal.hh8
-rw-r--r--src/libstore/ca-specific-schema.sql11
-rw-r--r--src/libstore/daemon.cc25
-rw-r--r--src/libstore/derived-path.cc51
-rw-r--r--src/libstore/derived-path.hh42
-rw-r--r--src/libstore/filetransfer.cc6
-rw-r--r--src/libstore/gc.cc4
-rw-r--r--src/libstore/globals.hh6
-rw-r--r--src/libstore/local-fs-store.hh5
-rw-r--r--src/libstore/local-store.cc275
-rw-r--r--src/libstore/local-store.hh20
-rw-r--r--src/libstore/local.mk2
-rw-r--r--src/libstore/machines.cc17
-rw-r--r--src/libstore/misc.cc182
-rw-r--r--src/libstore/nar-info-disk-cache.cc99
-rw-r--r--src/libstore/nar-info-disk-cache.hh10
-rw-r--r--src/libstore/optimise-store.cc2
-rw-r--r--src/libstore/parsed-derivations.cc2
-rw-r--r--src/libstore/realisation.cc58
-rw-r--r--src/libstore/realisation.hh13
-rw-r--r--src/libstore/remote-store.cc23
-rw-r--r--src/libstore/sandbox-defaults.sb4
-rw-r--r--src/libstore/store-api.cc38
-rw-r--r--src/libstore/store-api.hh9
-rw-r--r--src/libstore/worker-protocol.hh2
29 files changed, 892 insertions, 280 deletions
diff --git a/src/libstore/binary-cache-store.cc b/src/libstore/binary-cache-store.cc
index 09e1c254b..df401e6f4 100644
--- a/src/libstore/binary-cache-store.cc
+++ b/src/libstore/binary-cache-store.cc
@@ -450,18 +450,43 @@ StorePath BinaryCacheStore::addTextToStore(const string & name, const string & s
std::optional<const Realisation> BinaryCacheStore::queryRealisation(const DrvOutput & id)
{
+ if (diskCache) {
+ auto [cacheOutcome, maybeCachedRealisation] =
+ diskCache->lookupRealisation(getUri(), id);
+ switch (cacheOutcome) {
+ case NarInfoDiskCache::oValid:
+ debug("Returning a cached realisation for %s", id.to_string());
+ return *maybeCachedRealisation;
+ case NarInfoDiskCache::oInvalid:
+ debug("Returning a cached missing realisation for %s", id.to_string());
+ return {};
+ case NarInfoDiskCache::oUnknown:
+ break;
+ }
+ }
+
auto outputInfoFilePath = realisationsPrefix + "/" + id.to_string() + ".doi";
auto rawOutputInfo = getFile(outputInfoFilePath);
if (rawOutputInfo) {
- return {Realisation::fromJSON(
- nlohmann::json::parse(*rawOutputInfo), outputInfoFilePath)};
+ auto realisation = Realisation::fromJSON(
+ nlohmann::json::parse(*rawOutputInfo), outputInfoFilePath);
+
+ if (diskCache)
+ diskCache->upsertRealisation(
+ getUri(), realisation);
+
+ return {realisation};
} else {
+ if (diskCache)
+ diskCache->upsertAbsentRealisation(getUri(), id);
return std::nullopt;
}
}
void BinaryCacheStore::registerDrvOutput(const Realisation& info) {
+ if (diskCache)
+ diskCache->upsertRealisation(getUri(), info);
auto filePath = realisationsPrefix + "/" + info.id.to_string() + ".doi";
upsertFile(filePath, info.toJSON().dump(), "application/json");
}
diff --git a/src/libstore/build/derivation-goal.cc b/src/libstore/build/derivation-goal.cc
index 9100d3333..73d1ed7cc 100644
--- a/src/libstore/build/derivation-goal.cc
+++ b/src/libstore/build/derivation-goal.cc
@@ -739,6 +739,63 @@ void DerivationGoal::cleanupPostOutputsRegisteredModeNonCheck()
{
}
+void runPostBuildHook(
+ Store & store,
+ Logger & logger,
+ const StorePath & drvPath,
+ StorePathSet outputPaths
+)
+{
+ auto hook = settings.postBuildHook;
+ if (hook == "")
+ return;
+
+ Activity act(logger, lvlInfo, actPostBuildHook,
+ fmt("running post-build-hook '%s'", settings.postBuildHook),
+ Logger::Fields{store.printStorePath(drvPath)});
+ PushActivity pact(act.id);
+ std::map<std::string, std::string> hookEnvironment = getEnv();
+
+ hookEnvironment.emplace("DRV_PATH", store.printStorePath(drvPath));
+ hookEnvironment.emplace("OUT_PATHS", chomp(concatStringsSep(" ", store.printStorePathSet(outputPaths))));
+
+ RunOptions opts(settings.postBuildHook, {});
+ opts.environment = hookEnvironment;
+
+ struct LogSink : Sink {
+ Activity & act;
+ std::string currentLine;
+
+ LogSink(Activity & act) : act(act) { }
+
+ void operator() (std::string_view data) override {
+ for (auto c : data) {
+ if (c == '\n') {
+ flushLine();
+ } else {
+ currentLine += c;
+ }
+ }
+ }
+
+ void flushLine() {
+ act.result(resPostBuildLogLine, currentLine);
+ currentLine.clear();
+ }
+
+ ~LogSink() {
+ if (currentLine != "") {
+ currentLine += '\n';
+ flushLine();
+ }
+ }
+ };
+ LogSink sink(act);
+
+ opts.standardOut = &sink;
+ opts.mergeStderrToStdout = true;
+ runProgram2(opts);
+}
void DerivationGoal::buildDone()
{
@@ -804,57 +861,15 @@ void DerivationGoal::buildDone()
being valid. */
registerOutputs();
- if (settings.postBuildHook != "") {
- Activity act(*logger, lvlInfo, actPostBuildHook,
- fmt("running post-build-hook '%s'", settings.postBuildHook),
- Logger::Fields{worker.store.printStorePath(drvPath)});
- PushActivity pact(act.id);
- StorePathSet outputPaths;
- for (auto i : drv->outputs) {
- outputPaths.insert(finalOutputs.at(i.first));
- }
- std::map<std::string, std::string> hookEnvironment = getEnv();
-
- hookEnvironment.emplace("DRV_PATH", worker.store.printStorePath(drvPath));
- hookEnvironment.emplace("OUT_PATHS", chomp(concatStringsSep(" ", worker.store.printStorePathSet(outputPaths))));
-
- RunOptions opts(settings.postBuildHook, {});
- opts.environment = hookEnvironment;
-
- struct LogSink : Sink {
- Activity & act;
- std::string currentLine;
-
- LogSink(Activity & act) : act(act) { }
-
- void operator() (std::string_view data) override {
- for (auto c : data) {
- if (c == '\n') {
- flushLine();
- } else {
- currentLine += c;
- }
- }
- }
-
- void flushLine() {
- act.result(resPostBuildLogLine, currentLine);
- currentLine.clear();
- }
-
- ~LogSink() {
- if (currentLine != "") {
- currentLine += '\n';
- flushLine();
- }
- }
- };
- LogSink sink(act);
-
- opts.standardOut = &sink;
- opts.mergeStderrToStdout = true;
- runProgram2(opts);
- }
+ StorePathSet outputPaths;
+ for (auto & [_, path] : finalOutputs)
+ outputPaths.insert(path);
+ runPostBuildHook(
+ worker.store,
+ *logger,
+ drvPath,
+ outputPaths
+ );
if (buildMode == bmCheck) {
cleanupPostOutputsRegisteredModeCheck();
@@ -910,6 +925,8 @@ void DerivationGoal::resolvedFinished() {
auto resolvedHashes = staticOutputHashes(worker.store, *resolvedDrv);
+ StorePathSet outputPaths;
+
// `wantedOutputs` might be empty, which means “all the outputs”
auto realWantedOutputs = wantedOutputs;
if (realWantedOutputs.empty())
@@ -927,8 +944,10 @@ void DerivationGoal::resolvedFinished() {
auto newRealisation = *realisation;
newRealisation.id = DrvOutput{initialOutputs.at(wantedOutput).outputHash, wantedOutput};
newRealisation.signatures.clear();
+ newRealisation.dependentRealisations = drvOutputReferences(worker.store, *drv, realisation->outPath);
signRealisation(newRealisation);
worker.store.registerDrvOutput(newRealisation);
+ outputPaths.insert(realisation->outPath);
} else {
// If we don't have a realisation, then it must mean that something
// failed when building the resolved drv
@@ -936,6 +955,13 @@ void DerivationGoal::resolvedFinished() {
}
}
+ runPostBuildHook(
+ worker.store,
+ *logger,
+ drvPath,
+ outputPaths
+ );
+
// This is potentially a bit fishy in terms of error reporting. Not sure
// how to do it in a cleaner way
amDone(nrFailed == 0 ? ecSuccess : ecFailed, ex);
diff --git a/src/libstore/build/drv-output-substitution-goal.cc b/src/libstore/build/drv-output-substitution-goal.cc
index a5ac4c49d..be270d079 100644
--- a/src/libstore/build/drv-output-substitution-goal.cc
+++ b/src/libstore/build/drv-output-substitution-goal.cc
@@ -17,6 +17,13 @@ DrvOutputSubstitutionGoal::DrvOutputSubstitutionGoal(const DrvOutput& id, Worker
void DrvOutputSubstitutionGoal::init()
{
trace("init");
+
+ /* If the derivation already exists, we’re done */
+ if (worker.store.queryRealisation(id)) {
+ amDone(ecSuccess);
+ return;
+ }
+
subs = settings.useSubstitutes ? getDefaultSubstituters() : std::list<ref<Store>>();
tryNext();
}
@@ -53,6 +60,26 @@ void DrvOutputSubstitutionGoal::tryNext()
return;
}
+ for (const auto & [depId, depPath] : outputInfo->dependentRealisations) {
+ if (depId != id) {
+ if (auto localOutputInfo = worker.store.queryRealisation(depId);
+ localOutputInfo && localOutputInfo->outPath != depPath) {
+ warn(
+ "substituter '%s' has an incompatible realisation for '%s', ignoring.\n"
+ "Local: %s\n"
+ "Remote: %s",
+ sub->getUri(),
+ depId.to_string(),
+ worker.store.printStorePath(localOutputInfo->outPath),
+ worker.store.printStorePath(depPath)
+ );
+ tryNext();
+ return;
+ }
+ addWaitee(worker.makeDrvOutputSubstitutionGoal(depId));
+ }
+ }
+
addWaitee(worker.makePathSubstitutionGoal(outputInfo->outPath));
if (waitees.empty()) outPathValid();
diff --git a/src/libstore/build/local-derivation-goal.cc b/src/libstore/build/local-derivation-goal.cc
index 4a67b3f47..8320dd1c4 100644
--- a/src/libstore/build/local-derivation-goal.cc
+++ b/src/libstore/build/local-derivation-goal.cc
@@ -153,6 +153,7 @@ void LocalDerivationGoal::killChild()
void LocalDerivationGoal::tryLocalBuild() {
unsigned int curBuilds = worker.getNrLocalBuilds();
if (curBuilds >= settings.maxBuildJobs) {
+ state = &DerivationGoal::tryToBuild;
worker.waitForBuildSlot(shared_from_this());
outputLocks.unlock();
return;
@@ -291,7 +292,7 @@ bool LocalDerivationGoal::cleanupDecideWhetherDiskFull()
auto & localStore = getLocalStore();
uint64_t required = 8ULL * 1024 * 1024; // FIXME: make configurable
struct statvfs st;
- if (statvfs(localStore.realStoreDir.c_str(), &st) == 0 &&
+ if (statvfs(localStore.realStoreDir.get().c_str(), &st) == 0 &&
(uint64_t) st.f_bavail * st.f_bsize < required)
diskFull = true;
if (statvfs(tmpDir.c_str(), &st) == 0 &&
@@ -416,7 +417,7 @@ void LocalDerivationGoal::startBuilder()
}
auto & localStore = getLocalStore();
- if (localStore.storeDir != localStore.realStoreDir) {
+ if (localStore.storeDir != localStore.realStoreDir.get()) {
#if __linux__
useChroot = true;
#else
@@ -1332,13 +1333,18 @@ struct RestrictedStore : public virtual RestrictedStoreConfig, public virtual Lo
std::optional<const Realisation> queryRealisation(const DrvOutput & id) override
// XXX: This should probably be allowed if the realisation corresponds to
// an allowed derivation
- { throw Error("queryRealisation"); }
+ {
+ if (!goal.isAllowed(id))
+ throw InvalidPath("cannot query an unknown output id '%s' in recursive Nix", id.to_string());
+ return next->queryRealisation(id);
+ }
void buildPaths(const std::vector<DerivedPath> & paths, BuildMode buildMode) override
{
if (buildMode != bmNormal) throw Error("unsupported build mode");
StorePathSet newPaths;
+ std::set<Realisation> newRealisations;
for (auto & req : paths) {
if (!goal.isAllowed(req))
@@ -1351,16 +1357,28 @@ struct RestrictedStore : public virtual RestrictedStoreConfig, public virtual Lo
auto p = std::get_if<DerivedPath::Built>(&path);
if (!p) continue;
auto & bfd = *p;
+ auto drv = readDerivation(bfd.drvPath);
+ auto drvHashes = staticOutputHashes(*this, drv);
auto outputs = next->queryDerivationOutputMap(bfd.drvPath);
for (auto & [outputName, outputPath] : outputs)
- if (wantOutput(outputName, bfd.outputs))
+ if (wantOutput(outputName, bfd.outputs)) {
newPaths.insert(outputPath);
+ if (settings.isExperimentalFeatureEnabled("ca-derivations")) {
+ auto thisRealisation = next->queryRealisation(
+ DrvOutput{drvHashes.at(outputName), outputName}
+ );
+ assert(thisRealisation);
+ newRealisations.insert(*thisRealisation);
+ }
+ }
}
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);
}
BuildResult buildDerivation(const StorePath & drvPath, const BasicDerivation & drv,
@@ -2300,10 +2318,6 @@ void LocalDerivationGoal::registerOutputs()
sink.s = make_ref<std::string>(rewriteStrings(*sink.s, outputRewrites));
StringSource source(*sink.s);
restorePath(actualPath, source);
-
- /* FIXME: set proper permissions in restorePath() so
- we don't have to do another traversal. */
- canonicalisePathMetaData(actualPath, -1, inodesSeen);
}
};
@@ -2357,32 +2371,19 @@ void LocalDerivationGoal::registerOutputs()
}
auto got = caSink.finish().first;
auto refs = rewriteRefs();
- HashModuloSink narSink { htSHA256, oldHashPart };
- dumpPath(actualPath, narSink);
- auto narHashAndSize = narSink.finish();
- ValidPathInfo newInfo0 {
- worker.store.makeFixedOutputPath(
+
+ auto finalPath = worker.store.makeFixedOutputPath(
outputHash.method,
got,
outputPathName(drv->name, outputName),
refs.second,
- refs.first),
- narHashAndSize.first,
- };
- newInfo0.narSize = narHashAndSize.second;
- newInfo0.ca = FixedOutputHash {
- .method = outputHash.method,
- .hash = got,
- };
- newInfo0.references = refs.second;
- if (refs.first)
- newInfo0.references.insert(newInfo0.path);
- if (scratchPath != newInfo0.path) {
+ refs.first);
+ if (scratchPath != finalPath) {
// Also rewrite the output path
auto source = sinkToSource([&](Sink & nextSink) {
StringSink sink;
dumpPath(actualPath, sink);
- RewritingSink rsink2(oldHashPart, std::string(newInfo0.path.hashPart()), nextSink);
+ RewritingSink rsink2(oldHashPart, std::string(finalPath.hashPart()), nextSink);
rsink2(*sink.s);
rsink2.flush();
});
@@ -2392,6 +2393,21 @@ void LocalDerivationGoal::registerOutputs()
movePath(tmpPath, actualPath);
}
+ HashResult narHashAndSize = hashPath(htSHA256, actualPath);
+ ValidPathInfo newInfo0 {
+ finalPath,
+ narHashAndSize.first,
+ };
+
+ newInfo0.narSize = narHashAndSize.second;
+ newInfo0.ca = FixedOutputHash {
+ .method = outputHash.method,
+ .hash = got,
+ };
+ newInfo0.references = refs.second;
+ if (refs.first)
+ newInfo0.references.insert(newInfo0.path);
+
assert(newInfo0.ca);
return newInfo0;
};
@@ -2452,6 +2468,10 @@ void LocalDerivationGoal::registerOutputs()
},
}, output.output);
+ /* FIXME: set proper permissions in restorePath() so
+ we don't have to do another traversal. */
+ canonicalisePathMetaData(actualPath, -1, inodesSeen);
+
/* Calculate where we'll move the output files. In the checking case we
will leave leave them where they are, for now, rather than move to
their usual "final destination" */
@@ -2461,6 +2481,7 @@ void LocalDerivationGoal::registerOutputs()
floating CA derivations and hash-mismatching fixed-output
derivations. */
PathLocks dynamicOutputLock;
+ dynamicOutputLock.setDeletion(true);
auto optFixedPath = output.path(worker.store, drv->name, outputName);
if (!optFixedPath ||
worker.store.printStorePath(*optFixedPath) != finalDestPath)
@@ -2484,6 +2505,7 @@ void LocalDerivationGoal::registerOutputs()
assert(newInfo.ca);
} else {
auto destPath = worker.store.toRealPath(finalDestPath);
+ deletePath(destPath);
movePath(actualPath, destPath);
actualPath = destPath;
}
diff --git a/src/libstore/build/local-derivation-goal.hh b/src/libstore/build/local-derivation-goal.hh
index d30be2351..088a57209 100644
--- a/src/libstore/build/local-derivation-goal.hh
+++ b/src/libstore/build/local-derivation-goal.hh
@@ -108,6 +108,9 @@ struct LocalDerivationGoal : public DerivationGoal
/* 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
@@ -116,6 +119,11 @@ struct LocalDerivationGoal : public DerivationGoal
{
return inputPaths.count(path) || addedPaths.count(path);
}
+ bool isAllowed(const DrvOutput & id)
+ {
+ return addedDrvOutputs.count(id);
+ }
+
bool isAllowed(const DerivedPath & req);
friend struct RestrictedStore;
diff --git a/src/libstore/ca-specific-schema.sql b/src/libstore/ca-specific-schema.sql
index 20ee046a1..08af0cc1f 100644
--- a/src/libstore/ca-specific-schema.sql
+++ b/src/libstore/ca-specific-schema.sql
@@ -3,10 +3,19 @@
-- is enabled
create table if not exists Realisations (
+ id integer primary key autoincrement not null,
drvPath text not null,
outputName text not null, -- symbolic output id, usually "out"
outputPath integer not null,
signatures text, -- space-separated list
- primary key (drvPath, outputName),
foreign key (outputPath) references ValidPaths(id) on delete cascade
);
+
+create index if not exists IndexRealisations on Realisations(drvPath, outputName);
+
+create table if not exists RealisationsRefs (
+ referrer integer not null,
+ realisationReference integer,
+ foreign key (referrer) references Realisations(id) on delete cascade,
+ foreign key (realisationReference) references Realisations(id) on delete restrict
+);
diff --git a/src/libstore/daemon.cc b/src/libstore/daemon.cc
index 72b3e3e13..e06fb9ce2 100644
--- a/src/libstore/daemon.cc
+++ b/src/libstore/daemon.cc
@@ -885,10 +885,15 @@ static void performOp(TunnelLogger * logger, ref<Store> store,
case wopRegisterDrvOutput: {
logger->startWork();
- auto outputId = DrvOutput::parse(readString(from));
- auto outputPath = StorePath(readString(from));
- store->registerDrvOutput(Realisation{
- .id = outputId, .outPath = outputPath});
+ if (GET_PROTOCOL_MINOR(clientVersion) < 31) {
+ auto outputId = DrvOutput::parse(readString(from));
+ auto outputPath = StorePath(readString(from));
+ store->registerDrvOutput(Realisation{
+ .id = outputId, .outPath = outputPath});
+ } else {
+ auto realisation = worker_proto::read(*store, from, Phantom<Realisation>());
+ store->registerDrvOutput(realisation);
+ }
logger->stopWork();
break;
}
@@ -898,9 +903,15 @@ static void performOp(TunnelLogger * logger, ref<Store> store,
auto outputId = DrvOutput::parse(readString(from));
auto info = store->queryRealisation(outputId);
logger->stopWork();
- std::set<StorePath> outPaths;
- if (info) outPaths.insert(info->outPath);
- worker_proto::write(*store, to, outPaths);
+ if (GET_PROTOCOL_MINOR(clientVersion) < 31) {
+ std::set<StorePath> outPaths;
+ if (info) outPaths.insert(info->outPath);
+ worker_proto::write(*store, to, outPaths);
+ } else {
+ std::set<Realisation> realisations;
+ if (info) realisations.insert(*info);
+ worker_proto::write(*store, to, realisations);
+ }
break;
}
diff --git a/src/libstore/derived-path.cc b/src/libstore/derived-path.cc
index 13833c58e..8da81d0ac 100644
--- a/src/libstore/derived-path.cc
+++ b/src/libstore/derived-path.cc
@@ -11,18 +11,33 @@ nlohmann::json DerivedPath::Opaque::toJSON(ref<Store> store) const {
return res;
}
-nlohmann::json DerivedPathWithHints::Built::toJSON(ref<Store> store) const {
+nlohmann::json BuiltPath::Built::toJSON(ref<Store> store) const {
nlohmann::json res;
res["drvPath"] = store->printStorePath(drvPath);
for (const auto& [output, path] : outputs) {
- res["outputs"][output] = path ? store->printStorePath(*path) : "";
+ res["outputs"][output] = store->printStorePath(path);
}
return res;
}
-nlohmann::json derivedPathsWithHintsToJSON(const DerivedPathsWithHints & buildables, ref<Store> store) {
+StorePathSet BuiltPath::outPaths() const
+{
+ return std::visit(
+ overloaded{
+ [](BuiltPath::Opaque p) { return StorePathSet{p.path}; },
+ [](BuiltPath::Built b) {
+ StorePathSet res;
+ for (auto & [_, path] : b.outputs)
+ res.insert(path);
+ return res;
+ },
+ }, raw()
+ );
+}
+
+nlohmann::json derivedPathsWithHintsToJSON(const BuiltPaths & buildables, ref<Store> store) {
auto res = nlohmann::json::array();
- for (const DerivedPathWithHints & buildable : buildables) {
+ for (const BuiltPath & buildable : buildables) {
std::visit([&res, store](const auto & buildable) {
res.push_back(buildable.toJSON(store));
}, buildable.raw());
@@ -62,7 +77,7 @@ DerivedPath::Built DerivedPath::Built::parse(const Store & store, std::string_vi
auto outputsS = s.substr(n + 1);
std::set<string> outputs;
if (outputsS != "*")
- outputs = tokenizeString<std::set<string>>(outputsS);
+ outputs = tokenizeString<std::set<string>>(outputsS, ",");
return {drvPath, outputs};
}
@@ -74,4 +89,30 @@ DerivedPath DerivedPath::parse(const Store & store, std::string_view s)
: (DerivedPath) DerivedPath::Built::parse(store, s);
}
+RealisedPath::Set BuiltPath::toRealisedPaths(Store & store) const
+{
+ RealisedPath::Set res;
+ std::visit(
+ overloaded{
+ [&](BuiltPath::Opaque p) { res.insert(p.path); },
+ [&](BuiltPath::Built p) {
+ auto drvHashes =
+ staticOutputHashes(store, store.readDerivation(p.drvPath));
+ for (auto& [outputName, outputPath] : p.outputs) {
+ if (settings.isExperimentalFeatureEnabled(
+ "ca-derivations")) {
+ auto thisRealisation = store.queryRealisation(
+ DrvOutput{drvHashes.at(outputName), outputName});
+ assert(thisRealisation); // We’ve built it, so we must h
+ // ve the realisation
+ res.insert(*thisRealisation);
+ } else {
+ res.insert(outputPath);
+ }
+ }
+ },
+ },
+ raw());
+ return res;
+}
}
diff --git a/src/libstore/derived-path.hh b/src/libstore/derived-path.hh
index 7a2fe59de..9d6ace069 100644
--- a/src/libstore/derived-path.hh
+++ b/src/libstore/derived-path.hh
@@ -2,6 +2,7 @@
#include "util.hh"
#include "path.hh"
+#include "realisation.hh"
#include <optional>
@@ -79,51 +80,44 @@ struct DerivedPath : _DerivedPathRaw {
/**
* A built derived path with hints in the form of optional concrete output paths.
*
- * See 'DerivedPathWithHints' for more an explanation.
+ * See 'BuiltPath' for more an explanation.
*/
-struct DerivedPathWithHintsBuilt {
+struct BuiltPathBuilt {
StorePath drvPath;
- std::map<std::string, std::optional<StorePath>> outputs;
+ std::map<std::string, StorePath> outputs;
nlohmann::json toJSON(ref<Store> store) const;
- static DerivedPathWithHintsBuilt parse(const Store & store, std::string_view);
+ static BuiltPathBuilt parse(const Store & store, std::string_view);
};
-using _DerivedPathWithHintsRaw = std::variant<
+using _BuiltPathRaw = std::variant<
DerivedPath::Opaque,
- DerivedPathWithHintsBuilt
+ BuiltPathBuilt
>;
/**
- * A derived path with hints in the form of optional concrete output paths in the built case.
- *
- * This type is currently just used by the CLI. The paths are filled in
- * during evaluation for derivations that know what paths they will
- * produce in advanced, i.e. input-addressed or fixed-output content
- * addressed derivations.
- *
- * That isn't very good, because it puts floating content-addressed
- * derivations "at a disadvantage". It would be better to never rely on
- * the output path of unbuilt derivations, and exclusively use the
- * realizations types to work with built derivations' concrete output
- * paths.
+ * A built path. Similar to a `DerivedPath`, but enriched with the corresponding
+ * output path(s).
*/
-// FIXME Stop using and delete this, or if that is not possible move out of libstore to libcmd.
-struct DerivedPathWithHints : _DerivedPathWithHintsRaw {
- using Raw = _DerivedPathWithHintsRaw;
+struct BuiltPath : _BuiltPathRaw {
+ using Raw = _BuiltPathRaw;
using Raw::Raw;
using Opaque = DerivedPathOpaque;
- using Built = DerivedPathWithHintsBuilt;
+ using Built = BuiltPathBuilt;
inline const Raw & raw() const {
return static_cast<const Raw &>(*this);
}
+ StorePathSet outPaths() const;
+ RealisedPath::Set toRealisedPaths(Store & store) const;
+
};
-typedef std::vector<DerivedPathWithHints> DerivedPathsWithHints;
+typedef std::vector<DerivedPath> DerivedPaths;
+typedef std::vector<BuiltPath> BuiltPaths;
-nlohmann::json derivedPathsWithHintsToJSON(const DerivedPathsWithHints & buildables, ref<Store> store);
+nlohmann::json derivedPathsWithHintsToJSON(const BuiltPaths & buildables, ref<Store> store);
}
diff --git a/src/libstore/filetransfer.cc b/src/libstore/filetransfer.cc
index 514ab3bf9..2cf35ec83 100644
--- a/src/libstore/filetransfer.cc
+++ b/src/libstore/filetransfer.cc
@@ -7,7 +7,7 @@
#include "finally.hh"
#include "callback.hh"
-#ifdef ENABLE_S3
+#if ENABLE_S3
#include <aws/core/client/ClientConfiguration.h>
#endif
@@ -665,7 +665,7 @@ struct curlFileTransfer : public FileTransfer
writeFull(wakeupPipe.writeSide.get(), " ");
}
-#ifdef ENABLE_S3
+#if ENABLE_S3
std::tuple<std::string, std::string, Store::Params> parseS3Uri(std::string uri)
{
auto [path, params] = splitUriAndParams(uri);
@@ -688,7 +688,7 @@ struct curlFileTransfer : public FileTransfer
if (hasPrefix(request.uri, "s3://")) {
// FIXME: do this on a worker thread
try {
-#ifdef ENABLE_S3
+#if ENABLE_S3
auto [bucketName, key, params] = parseS3Uri(request.uri);
std::string profile = get(params, "profile").value_or("");
diff --git a/src/libstore/gc.cc b/src/libstore/gc.cc
index bc692ca42..5a62c6529 100644
--- a/src/libstore/gc.cc
+++ b/src/libstore/gc.cc
@@ -775,7 +775,7 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
try {
- AutoCloseDir dir(opendir(realStoreDir.c_str()));
+ AutoCloseDir dir(opendir(realStoreDir.get().c_str()));
if (!dir) throw SysError("opening directory '%1%'", realStoreDir);
/* Read the store and immediately delete all paths that
@@ -856,7 +856,7 @@ void LocalStore::autoGC(bool sync)
return std::stoll(readFile(*fakeFreeSpaceFile));
struct statvfs st;
- if (statvfs(realStoreDir.c_str(), &st))
+ if (statvfs(realStoreDir.get().c_str(), &st))
throw SysError("getting filesystem info about '%s'", realStoreDir);
return (uint64_t) st.f_bavail * st.f_frsize;
diff --git a/src/libstore/globals.hh b/src/libstore/globals.hh
index 6f8749254..dd570cd63 100644
--- a/src/libstore/globals.hh
+++ b/src/libstore/globals.hh
@@ -617,8 +617,10 @@ public:
Strings{"https://cache.nixos.org/"},
"substituters",
R"(
- A list of URLs of substituters, separated by whitespace. The default
- is `https://cache.nixos.org`.
+ A list of URLs of substituters, separated by whitespace. Substituters
+ are tried based on their Priority value, which each substituter can set
+ independently. Lower value means higher priority.
+ The default is `https://cache.nixos.org`, with a Priority of 40.
)",
{"binary-caches"}};
diff --git a/src/libstore/local-fs-store.hh b/src/libstore/local-fs-store.hh
index 55941b771..f8b19d00d 100644
--- a/src/libstore/local-fs-store.hh
+++ b/src/libstore/local-fs-store.hh
@@ -18,6 +18,9 @@ struct LocalFSStoreConfig : virtual StoreConfig
const PathSetting logDir{(StoreConfig*) this, false,
rootDir != "" ? rootDir + "/nix/var/log/nix" : settings.nixLogDir,
"log", "directory where Nix will store state"};
+ const PathSetting realStoreDir{(StoreConfig*) this, false,
+ rootDir != "" ? rootDir + "/nix/store" : storeDir, "real",
+ "physical path to the Nix store"};
};
class LocalFSStore : public virtual LocalFSStoreConfig, public virtual Store
@@ -34,7 +37,7 @@ public:
/* Register a permanent GC root. */
Path addPermRoot(const StorePath & storePath, const Path & gcRoot);
- virtual Path getRealStoreDir() { return storeDir; }
+ virtual Path getRealStoreDir() { return realStoreDir; }
Path toRealPath(const Path & storePath) override
{
diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc
index 83daa7506..d7c7f8e1d 100644
--- a/src/libstore/local-store.cc
+++ b/src/libstore/local-store.cc
@@ -53,12 +53,15 @@ struct LocalStore::State::Stmts {
SQLiteStmt InvalidatePath;
SQLiteStmt AddDerivationOutput;
SQLiteStmt RegisterRealisedOutput;
+ SQLiteStmt UpdateRealisedOutput;
SQLiteStmt QueryValidDerivers;
SQLiteStmt QueryDerivationOutputs;
SQLiteStmt QueryRealisedOutput;
SQLiteStmt QueryAllRealisedOutputs;
SQLiteStmt QueryPathFromHashPart;
SQLiteStmt QueryValidPaths;
+ SQLiteStmt QueryRealisationReferences;
+ SQLiteStmt AddRealisationReference;
};
int getSchema(Path schemaPath)
@@ -76,7 +79,7 @@ int getSchema(Path schemaPath)
void migrateCASchema(SQLite& db, Path schemaPath, AutoCloseFD& lockFd)
{
- const int nixCASchemaVersion = 1;
+ const int nixCASchemaVersion = 2;
int curCASchema = getSchema(schemaPath);
if (curCASchema != nixCASchemaVersion) {
if (curCASchema > nixCASchemaVersion) {
@@ -94,7 +97,39 @@ void migrateCASchema(SQLite& db, Path schemaPath, AutoCloseFD& lockFd)
#include "ca-specific-schema.sql.gen.hh"
;
db.exec(schema);
+ curCASchema = nixCASchemaVersion;
}
+
+ if (curCASchema < 2) {
+ SQLiteTxn txn(db);
+ // Ugly little sql dance to add a new `id` column and make it the primary key
+ db.exec(R"(
+ create table Realisations2 (
+ id integer primary key autoincrement not null,
+ drvPath text not null,
+ outputName text not null, -- symbolic output id, usually "out"
+ outputPath integer not null,
+ signatures text, -- space-separated list
+ foreign key (outputPath) references ValidPaths(id) on delete cascade
+ );
+ insert into Realisations2 (drvPath, outputName, outputPath, signatures)
+ select drvPath, outputName, outputPath, signatures from Realisations;
+ drop table Realisations;
+ alter table Realisations2 rename to Realisations;
+ )");
+ db.exec(R"(
+ create index if not exists IndexRealisations on Realisations(drvPath, outputName);
+
+ create table if not exists RealisationsRefs (
+ referrer integer not null,
+ realisationReference integer,
+ foreign key (referrer) references Realisations(id) on delete cascade,
+ foreign key (realisationReference) references Realisations(id) on delete restrict
+ );
+ )");
+ txn.commit();
+ }
+
writeFile(schemaPath, fmt("%d", nixCASchemaVersion));
lockFile(lockFd.get(), ltRead, true);
}
@@ -106,9 +141,6 @@ LocalStore::LocalStore(const Params & params)
, LocalStoreConfig(params)
, Store(params)
, LocalFSStore(params)
- , realStoreDir_{this, false, rootDir != "" ? rootDir + "/nix/store" : storeDir, "real",
- "physical path to the Nix store"}
- , realStoreDir(realStoreDir_)
, dbDir(stateDir + "/db")
, linksDir(realStoreDir + "/.links")
, reservedPath(dbDir + "/reserved")
@@ -153,13 +185,13 @@ LocalStore::LocalStore(const Params & params)
printError("warning: the group '%1%' specified in 'build-users-group' does not exist", settings.buildUsersGroup);
else {
struct stat st;
- if (stat(realStoreDir.c_str(), &st))
+ if (stat(realStoreDir.get().c_str(), &st))
throw SysError("getting attributes of path '%1%'", realStoreDir);
if (st.st_uid != 0 || st.st_gid != gr->gr_gid || (st.st_mode & ~S_IFMT) != perm) {
- if (chown(realStoreDir.c_str(), 0, gr->gr_gid) == -1)
+ if (chown(realStoreDir.get().c_str(), 0, gr->gr_gid) == -1)
throw SysError("changing ownership of path '%1%'", realStoreDir);
- if (chmod(realStoreDir.c_str(), perm) == -1)
+ if (chmod(realStoreDir.get().c_str(), perm) == -1)
throw SysError("changing permissions on path '%1%'", realStoreDir);
}
}
@@ -314,9 +346,18 @@ LocalStore::LocalStore(const Params & params)
values (?, ?, (select id from ValidPaths where path = ?), ?)
;
)");
+ state->stmts->UpdateRealisedOutput.create(state->db,
+ R"(
+ update Realisations
+ set signatures = ?
+ where
+ drvPath = ? and
+ outputName = ?
+ ;
+ )");
state->stmts->QueryRealisedOutput.create(state->db,
R"(
- select Output.path, Realisations.signatures from Realisations
+ select Realisations.id, Output.path, Realisations.signatures from Realisations
inner join ValidPaths as Output on Output.id = Realisations.outputPath
where drvPath = ? and outputName = ?
;
@@ -328,6 +369,19 @@ LocalStore::LocalStore(const Params & params)
where drvPath = ?
;
)");
+ state->stmts->QueryRealisationReferences.create(state->db,
+ R"(
+ select drvPath, outputName from Realisations
+ join RealisationsRefs on realisationReference = Realisations.id
+ where referrer = ?;
+ )");
+ state->stmts->AddRealisationReference.create(state->db,
+ R"(
+ insert or replace into RealisationsRefs (referrer, realisationReference)
+ values (
+ ?,
+ (select id from Realisations where drvPath = ? and outputName = ?));
+ )");
}
}
@@ -437,14 +491,14 @@ void LocalStore::makeStoreWritable()
if (getuid() != 0) return;
/* Check if /nix/store is on a read-only mount. */
struct statvfs stat;
- if (statvfs(realStoreDir.c_str(), &stat) != 0)
+ if (statvfs(realStoreDir.get().c_str(), &stat) != 0)
throw SysError("getting info about the Nix store mount point");
if (stat.f_flag & ST_RDONLY) {
if (unshare(CLONE_NEWNS) == -1)
throw SysError("setting up a private mount namespace");
- if (mount(0, realStoreDir.c_str(), "none", MS_REMOUNT | MS_BIND, 0) == -1)
+ if (mount(0, realStoreDir.get().c_str(), "none", MS_REMOUNT | MS_BIND, 0) == -1)
throw SysError("remounting %1% writable", realStoreDir);
}
#endif
@@ -664,14 +718,54 @@ void LocalStore::registerDrvOutput(const Realisation & info, CheckSigsFlag check
void LocalStore::registerDrvOutput(const Realisation & info)
{
settings.requireExperimentalFeature("ca-derivations");
- auto state(_state.lock());
retrySQLite<void>([&]() {
- state->stmts->RegisterRealisedOutput.use()
- (info.id.strHash())
- (info.id.outputName)
- (printStorePath(info.outPath))
- (concatStringsSep(" ", info.signatures))
- .exec();
+ auto state(_state.lock());
+ if (auto oldR = queryRealisation_(*state, info.id)) {
+ if (info.isCompatibleWith(*oldR)) {
+ auto combinedSignatures = oldR->signatures;
+ combinedSignatures.insert(info.signatures.begin(),
+ info.signatures.end());
+ state->stmts->UpdateRealisedOutput.use()
+ (concatStringsSep(" ", combinedSignatures))
+ (info.id.strHash())
+ (info.id.outputName)
+ .exec();
+ } else {
+ throw Error("Trying to register a realisation of '%s', but we already "
+ "have another one locally.\n"
+ "Local: %s\n"
+ "Remote: %s",
+ info.id.to_string(),
+ printStorePath(oldR->outPath),
+ printStorePath(info.outPath)
+ );
+ }
+ } else {
+ state->stmts->RegisterRealisedOutput.use()
+ (info.id.strHash())
+ (info.id.outputName)
+ (printStorePath(info.outPath))
+ (concatStringsSep(" ", info.signatures))
+ .exec();
+ }
+ uint64_t myId = state->db.getLastInsertedRowId();
+ for (auto & [outputId, depPath] : info.dependentRealisations) {
+ auto localRealisation = queryRealisationCore_(*state, outputId);
+ if (!localRealisation)
+ throw Error("unable to register the derivation '%s' as it "
+ "depends on the non existent '%s'",
+ info.id.to_string(), outputId.to_string());
+ if (localRealisation->second.outPath != depPath)
+ throw Error("unable to register the derivation '%s' as it "
+ "depends on a realisation of '%s' that doesn’t"
+ "match what we have locally",
+ info.id.to_string(), outputId.to_string());
+ state->stmts->AddRealisationReference.use()
+ (myId)
+ (outputId.strHash())
+ (outputId.outputName)
+ .exec();
+ }
});
}
@@ -1152,17 +1246,13 @@ void LocalStore::addToStore(const ValidPathInfo & info, Source & source,
/* While restoring the path from the NAR, compute the hash
of the NAR. */
- std::unique_ptr<AbstractHashSink> hashSink;
- if (!info.ca.has_value() || !info.references.count(info.path))
- hashSink = std::make_unique<HashSink>(htSHA256);
- else
- hashSink = std::make_unique<HashModuloSink>(htSHA256, std::string(info.path.hashPart()));
+ HashSink hashSink(htSHA256);
- TeeSource wrapperSource { source, *hashSink };
+ TeeSource wrapperSource { source, hashSink };
restorePath(realPath, wrapperSource);
- auto hashResult = hashSink->finish();
+ auto hashResult = hashSink.finish();
if (hashResult.first != info.narHash)
throw Error("hash mismatch importing path '%s';\n specified: %s\n got: %s",
@@ -1172,6 +1262,31 @@ void LocalStore::addToStore(const ValidPathInfo & info, Source & source,
throw Error("size mismatch importing path '%s';\n specified: %s\n got: %s",
printStorePath(info.path), info.narSize, hashResult.second);
+ if (info.ca) {
+ if (auto foHash = std::get_if<FixedOutputHash>(&*info.ca)) {
+ auto actualFoHash = hashCAPath(
+ foHash->method,
+ foHash->hash.type,
+ info.path
+ );
+ if (foHash->hash != actualFoHash.hash) {
+ throw Error("ca hash mismatch importing path '%s';\n specified: %s\n got: %s",
+ printStorePath(info.path),
+ foHash->hash.to_string(Base32, true),
+ actualFoHash.hash.to_string(Base32, true));
+ }
+ }
+ if (auto textHash = std::get_if<TextHash>(&*info.ca)) {
+ auto actualTextHash = hashString(htSHA256, readFile(realPath));
+ if (textHash->hash != actualTextHash) {
+ throw Error("ca hash mismatch importing path '%s';\n specified: %s\n got: %s",
+ printStorePath(info.path),
+ textHash->hash.to_string(Base32, true),
+ actualTextHash.to_string(Base32, true));
+ }
+ }
+ }
+
autoGC();
canonicalisePathMetaData(realPath, -1);
@@ -1440,14 +1555,10 @@ bool LocalStore::verifyStore(bool checkContents, RepairFlag repair)
/* Check the content hash (optionally - slow). */
printMsg(lvlTalkative, "checking contents of '%s'", printStorePath(i));
- std::unique_ptr<AbstractHashSink> hashSink;
- if (!info->ca || !info->references.count(info->path))
- hashSink = std::make_unique<HashSink>(info->narHash.type);
- else
- hashSink = std::make_unique<HashModuloSink>(info->narHash.type, std::string(info->path.hashPart()));
+ auto hashSink = HashSink(info->narHash.type);
- dumpPath(Store::toRealPath(i), *hashSink);
- auto current = hashSink->finish();
+ dumpPath(Store::toRealPath(i), hashSink);
+ auto current = hashSink.finish();
if (info->narHash != nullHash && info->narHash != current.first) {
printError("path '%s' was modified! expected hash '%s', got '%s'",
@@ -1665,19 +1776,97 @@ void LocalStore::createUser(const std::string & userName, uid_t userId)
}
}
-std::optional<const Realisation> LocalStore::queryRealisation(
- const DrvOutput& id) {
- typedef std::optional<const Realisation> Ret;
- return retrySQLite<Ret>([&]() -> Ret {
+std::optional<std::pair<int64_t, Realisation>> LocalStore::queryRealisationCore_(
+ LocalStore::State & state,
+ const DrvOutput & id)
+{
+ auto useQueryRealisedOutput(
+ state.stmts->QueryRealisedOutput.use()
+ (id.strHash())
+ (id.outputName));
+ if (!useQueryRealisedOutput.next())
+ return std::nullopt;
+ auto realisationDbId = useQueryRealisedOutput.getInt(0);
+ auto outputPath = parseStorePath(useQueryRealisedOutput.getStr(1));
+ auto signatures =
+ tokenizeString<StringSet>(useQueryRealisedOutput.getStr(2));
+
+ return {{
+ realisationDbId,
+ Realisation{
+ .id = id,
+ .outPath = outputPath,
+ .signatures = signatures,
+ }
+ }};
+}
+
+std::optional<const Realisation> LocalStore::queryRealisation_(
+ LocalStore::State & state,
+ const DrvOutput & id)
+{
+ auto maybeCore = queryRealisationCore_(state, id);
+ if (!maybeCore)
+ return std::nullopt;
+ auto [realisationDbId, res] = *maybeCore;
+
+ std::map<DrvOutput, StorePath> dependentRealisations;
+ auto useRealisationRefs(
+ state.stmts->QueryRealisationReferences.use()
+ (realisationDbId));
+ while (useRealisationRefs.next()) {
+ auto depId = DrvOutput {
+ Hash::parseAnyPrefixed(useRealisationRefs.getStr(0)),
+ useRealisationRefs.getStr(1),
+ };
+ auto dependentRealisation = queryRealisationCore_(state, depId);
+ assert(dependentRealisation); // Enforced by the db schema
+ auto outputPath = dependentRealisation->second.outPath;
+ dependentRealisations.insert({depId, outputPath});
+ }
+
+ res.dependentRealisations = dependentRealisations;
+
+ return { res };
+}
+
+std::optional<const Realisation>
+LocalStore::queryRealisation(const DrvOutput & id)
+{
+ return retrySQLite<std::optional<const Realisation>>([&]() {
auto state(_state.lock());
- auto use(state->stmts->QueryRealisedOutput.use()(id.strHash())(
- id.outputName));
- if (!use.next())
- return std::nullopt;
- auto outputPath = parseStorePath(use.getStr(0));
- auto signatures = tokenizeString<StringSet>(use.getStr(1));
- return Ret{Realisation{
- .id = id, .outPath = outputPath, .signatures = signatures}};
+ return queryRealisation_(*state, id);
});
}
+
+FixedOutputHash LocalStore::hashCAPath(
+ const FileIngestionMethod & method, const HashType & hashType,
+ const StorePath & path)
+{
+ return hashCAPath(method, hashType, Store::toRealPath(path), path.hashPart());
+}
+
+FixedOutputHash LocalStore::hashCAPath(
+ const FileIngestionMethod & method,
+ const HashType & hashType,
+ const Path & path,
+ const std::string_view pathHash
+)
+{
+ HashModuloSink caSink ( hashType, std::string(pathHash) );
+ switch (method) {
+ case FileIngestionMethod::Recursive:
+ dumpPath(path, caSink);
+ break;
+ case FileIngestionMethod::Flat:
+ readFile(path, caSink);
+ break;
+ }
+ auto hash = caSink.finish().first;
+ return FixedOutputHash{
+ .method = method,
+ .hash = hash,
+ };
+}
+
} // namespace nix
diff --git a/src/libstore/local-store.hh b/src/libstore/local-store.hh
index 26e034a82..a01d48c4b 100644
--- a/src/libstore/local-store.hh
+++ b/src/libstore/local-store.hh
@@ -83,9 +83,6 @@ private:
public:
- PathSetting realStoreDir_;
-
- const Path realStoreDir;
const Path dbDir;
const Path linksDir;
const Path reservedPath;
@@ -206,6 +203,8 @@ public:
void registerDrvOutput(const Realisation & info, CheckSigsFlag checkSigs) override;
void cacheDrvOutputMapping(State & state, const uint64_t deriver, const string & outputName, const StorePath & output);
+ std::optional<const Realisation> queryRealisation_(State & state, const DrvOutput & id);
+ std::optional<std::pair<int64_t, Realisation>> queryRealisationCore_(State & state, const DrvOutput & id);
std::optional<const Realisation> queryRealisation(const DrvOutput&) override;
private:
@@ -279,10 +278,21 @@ private:
void signPathInfo(ValidPathInfo & info);
void signRealisation(Realisation &);
- Path getRealStoreDir() override { return realStoreDir; }
-
void createUser(const std::string & userName, uid_t userId) override;
+ // XXX: Make a generic `Store` method
+ FixedOutputHash hashCAPath(
+ const FileIngestionMethod & method,
+ const HashType & hashType,
+ const StorePath & path);
+
+ FixedOutputHash hashCAPath(
+ const FileIngestionMethod & method,
+ const HashType & hashType,
+ const Path & path,
+ const std::string_view pathHash
+ );
+
friend struct LocalDerivationGoal;
friend struct PathSubstitutionGoal;
friend struct SubstitutionGoal;
diff --git a/src/libstore/local.mk b/src/libstore/local.mk
index cf0933705..b6652984c 100644
--- a/src/libstore/local.mk
+++ b/src/libstore/local.mk
@@ -9,7 +9,7 @@ libstore_SOURCES := $(wildcard $(d)/*.cc $(d)/builtins/*.cc $(d)/build/*.cc)
libstore_LIBS = libutil
libstore_LDFLAGS = $(SQLITE3_LIBS) -lbz2 $(LIBCURL_LIBS) $(SODIUM_LIBS) -pthread
-ifneq ($(OS), FreeBSD)
+ifeq ($(OS), Linux)
libstore_LDFLAGS += -ldl
endif
diff --git a/src/libstore/machines.cc b/src/libstore/machines.cc
index b42e5e434..9843ccf04 100644
--- a/src/libstore/machines.cc
+++ b/src/libstore/machines.cc
@@ -16,13 +16,18 @@ Machine::Machine(decltype(storeUri) storeUri,
decltype(mandatoryFeatures) mandatoryFeatures,
decltype(sshPublicHostKey) sshPublicHostKey) :
storeUri(
- // Backwards compatibility: if the URI is a hostname,
- // prepend ssh://.
+ // Backwards compatibility: if the URI is schemeless, is not a path,
+ // and is not one of the special store connection words, prepend
+ // ssh://.
storeUri.find("://") != std::string::npos
- || hasPrefix(storeUri, "local")
- || hasPrefix(storeUri, "remote")
- || hasPrefix(storeUri, "auto")
- || hasPrefix(storeUri, "/")
+ || storeUri.find("/") != std::string::npos
+ || storeUri == "auto"
+ || storeUri == "daemon"
+ || storeUri == "local"
+ || hasPrefix(storeUri, "auto?")
+ || hasPrefix(storeUri, "daemon?")
+ || hasPrefix(storeUri, "local?")
+ || hasPrefix(storeUri, "?")
? storeUri
: "ssh://" + storeUri),
systemTypes(systemTypes),
diff --git a/src/libstore/misc.cc b/src/libstore/misc.cc
index a99a2fc78..b4929b445 100644
--- a/src/libstore/misc.cc
+++ b/src/libstore/misc.cc
@@ -6,98 +6,73 @@
#include "thread-pool.hh"
#include "topo-sort.hh"
#include "callback.hh"
+#include "closure.hh"
namespace nix {
-
void Store::computeFSClosure(const StorePathSet & startPaths,
StorePathSet & paths_, bool flipDirection, bool includeOutputs, bool includeDerivers)
{
- struct State
- {
- size_t pending;
- StorePathSet & paths;
- std::exception_ptr exc;
- };
-
- Sync<State> state_(State{0, paths_, 0});
-
- std::function<void(const StorePath &)> enqueue;
-
- std::condition_variable done;
-
- enqueue = [&](const StorePath & path) -> void {
- {
- auto state(state_.lock());
- if (state->exc) return;
- if (!state->paths.insert(path).second) return;
- state->pending++;
- }
-
- queryPathInfo(path, {[&](std::future<ref<const ValidPathInfo>> fut) {
- // FIXME: calls to isValidPath() should be async
-
- try {
- auto info = fut.get();
-
- if (flipDirection) {
-
- StorePathSet referrers;
- queryReferrers(path, referrers);
- for (auto & ref : referrers)
- if (ref != path)
- enqueue(ref);
-
- if (includeOutputs)
- for (auto & i : queryValidDerivers(path))
- enqueue(i);
-
- if (includeDerivers && path.isDerivation())
- for (auto & i : queryDerivationOutputs(path))
- if (isValidPath(i) && queryPathInfo(i)->deriver == path)
- enqueue(i);
-
- } else {
-
- for (auto & ref : info->references)
- if (ref != path)
- enqueue(ref);
-
- if (includeOutputs && path.isDerivation())
- for (auto & i : queryDerivationOutputs(path))
- if (isValidPath(i)) enqueue(i);
-
- if (includeDerivers && info->deriver && isValidPath(*info->deriver))
- enqueue(*info->deriver);
-
- }
-
- {
- auto state(state_.lock());
- assert(state->pending);
- if (!--state->pending) done.notify_one();
- }
-
- } catch (...) {
- auto state(state_.lock());
- if (!state->exc) state->exc = std::current_exception();
- assert(state->pending);
- if (!--state->pending) done.notify_one();
- };
- }});
- };
-
- for (auto & startPath : startPaths)
- enqueue(startPath);
-
- {
- auto state(state_.lock());
- while (state->pending) state.wait(done);
- if (state->exc) std::rethrow_exception(state->exc);
- }
+ std::function<std::set<StorePath>(const StorePath & path, std::future<ref<const ValidPathInfo>> &)> queryDeps;
+ if (flipDirection)
+ queryDeps = [&](const StorePath& path,
+ std::future<ref<const ValidPathInfo>> & fut) {
+ StorePathSet res;
+ StorePathSet referrers;
+ queryReferrers(path, referrers);
+ for (auto& ref : referrers)
+ if (ref != path)
+ res.insert(ref);
+
+ if (includeOutputs)
+ for (auto& i : queryValidDerivers(path))
+ res.insert(i);
+
+ if (includeDerivers && path.isDerivation())
+ for (auto& [_, maybeOutPath] : queryPartialDerivationOutputMap(path))
+ if (maybeOutPath && isValidPath(*maybeOutPath))
+ res.insert(*maybeOutPath);
+ return res;
+ };
+ else
+ queryDeps = [&](const StorePath& path,
+ std::future<ref<const ValidPathInfo>> & fut) {
+ StorePathSet res;
+ auto info = fut.get();
+ for (auto& ref : info->references)
+ if (ref != path)
+ res.insert(ref);
+
+ if (includeOutputs && path.isDerivation())
+ for (auto& [_, maybeOutPath] : queryPartialDerivationOutputMap(path))
+ if (maybeOutPath && isValidPath(*maybeOutPath))
+ res.insert(*maybeOutPath);
+
+ if (includeDerivers && info->deriver && isValidPath(*info->deriver))
+ res.insert(*info->deriver);
+ return res;
+ };
+
+ computeClosure<StorePath>(
+ startPaths, paths_,
+ [&](const StorePath& path,
+ std::function<void(std::promise<std::set<StorePath>>&)>
+ processEdges) {
+ std::promise<std::set<StorePath>> promise;
+ std::function<void(std::future<ref<const ValidPathInfo>>)>
+ getDependencies =
+ [&](std::future<ref<const ValidPathInfo>> fut) {
+ try {
+ promise.set_value(queryDeps(path, fut));
+ } catch (...) {
+ promise.set_exception(std::current_exception());
+ }
+ };
+ queryPathInfo(path, getDependencies);
+ processEdges(promise);
+ });
}
-
void Store::computeFSClosure(const StorePath & startPath,
StorePathSet & paths_, bool flipDirection, bool includeOutputs, bool includeDerivers)
{
@@ -279,5 +254,44 @@ StorePaths Store::topoSortPaths(const StorePathSet & paths)
}});
}
+std::map<DrvOutput, StorePath> drvOutputReferences(
+ const std::set<Realisation> & inputRealisations,
+ const StorePathSet & pathReferences)
+{
+ std::map<DrvOutput, StorePath> res;
+ for (const auto & input : inputRealisations) {
+ if (pathReferences.count(input.outPath)) {
+ res.insert({input.id, input.outPath});
+ }
+ }
+
+ return res;
+}
+
+std::map<DrvOutput, StorePath> drvOutputReferences(
+ Store & store,
+ const Derivation & drv,
+ const StorePath & outputPath)
+{
+ std::set<Realisation> inputRealisations;
+
+ for (const auto& [inputDrv, outputNames] : drv.inputDrvs) {
+ auto outputHashes =
+ staticOutputHashes(store, store.readDerivation(inputDrv));
+ for (const auto& outputName : outputNames) {
+ auto thisRealisation = store.queryRealisation(
+ DrvOutput{outputHashes.at(outputName), outputName});
+ if (!thisRealisation)
+ throw Error(
+ "output '%s' of derivation '%s' isn’t built", outputName,
+ store.printStorePath(inputDrv));
+ inputRealisations.insert(*thisRealisation);
+ }
+ }
+
+ auto info = store.queryPathInfo(outputPath);
+
+ return drvOutputReferences(Realisation::closure(store, inputRealisations), info->references);
+}
}
diff --git a/src/libstore/nar-info-disk-cache.cc b/src/libstore/nar-info-disk-cache.cc
index 1d8d2d57e..9dd81ddfb 100644
--- a/src/libstore/nar-info-disk-cache.cc
+++ b/src/libstore/nar-info-disk-cache.cc
@@ -4,6 +4,7 @@
#include "globals.hh"
#include <sqlite3.h>
+#include <nlohmann/json.hpp>
namespace nix {
@@ -38,6 +39,15 @@ create table if not exists NARs (
foreign key (cache) references BinaryCaches(id) on delete cascade
);
+create table if not exists Realisations (
+ cache integer not null,
+ outputId text not null,
+ content blob, -- Json serialisation of the realisation, or null if the realisation is absent
+ timestamp integer not null,
+ primary key (cache, outputId),
+ foreign key (cache) references BinaryCaches(id) on delete cascade
+);
+
create table if not exists LastPurge (
dummy text primary key,
value integer
@@ -63,7 +73,9 @@ public:
struct State
{
SQLite db;
- SQLiteStmt insertCache, queryCache, insertNAR, insertMissingNAR, queryNAR, purgeCache;
+ SQLiteStmt insertCache, queryCache, insertNAR, insertMissingNAR,
+ queryNAR, insertRealisation, insertMissingRealisation,
+ queryRealisation, purgeCache;
std::map<std::string, Cache> caches;
};
@@ -98,6 +110,26 @@ public:
state->queryNAR.create(state->db,
"select present, namePart, url, compression, fileHash, fileSize, narHash, narSize, refs, deriver, sigs, ca from NARs where cache = ? and hashPart = ? and ((present = 0 and timestamp > ?) or (present = 1 and timestamp > ?))");
+ state->insertRealisation.create(state->db,
+ R"(
+ insert or replace into Realisations(cache, outputId, content, timestamp)
+ values (?, ?, ?, ?)
+ )");
+
+ state->insertMissingRealisation.create(state->db,
+ R"(
+ insert or replace into Realisations(cache, outputId, timestamp)
+ values (?, ?, ?)
+ )");
+
+ state->queryRealisation.create(state->db,
+ R"(
+ select content from Realisations
+ where cache = ? and outputId = ? and
+ ((content is null and timestamp > ?) or
+ (content is not null and timestamp > ?))
+ )");
+
/* Periodically purge expired entries from the database. */
retrySQLite<void>([&]() {
auto now = time(0);
@@ -212,6 +244,38 @@ public:
});
}
+ std::pair<Outcome, std::shared_ptr<Realisation>> lookupRealisation(
+ const std::string & uri, const DrvOutput & id) override
+ {
+ return retrySQLite<std::pair<Outcome, std::shared_ptr<Realisation>>>(
+ [&]() -> std::pair<Outcome, std::shared_ptr<Realisation>> {
+ auto state(_state.lock());
+
+ auto & cache(getCache(*state, uri));
+
+ auto now = time(0);
+
+ auto queryRealisation(state->queryRealisation.use()
+ (cache.id)
+ (id.to_string())
+ (now - settings.ttlNegativeNarInfoCache)
+ (now - settings.ttlPositiveNarInfoCache));
+
+ if (!queryRealisation.next())
+ return {oUnknown, 0};
+
+ if (queryRealisation.isNull(0))
+ return {oInvalid, 0};
+
+ auto realisation =
+ std::make_shared<Realisation>(Realisation::fromJSON(
+ nlohmann::json::parse(queryRealisation.getStr(0)),
+ "Local disk cache"));
+
+ return {oValid, realisation};
+ });
+ }
+
void upsertNarInfo(
const std::string & uri, const std::string & hashPart,
std::shared_ptr<const ValidPathInfo> info) override
@@ -251,6 +315,39 @@ public:
}
});
}
+
+ void upsertRealisation(
+ const std::string & uri,
+ const Realisation & realisation) override
+ {
+ retrySQLite<void>([&]() {
+ auto state(_state.lock());
+
+ auto & cache(getCache(*state, uri));
+
+ state->insertRealisation.use()
+ (cache.id)
+ (realisation.id.to_string())
+ (realisation.toJSON().dump())
+ (time(0)).exec();
+ });
+
+ }
+
+ virtual void upsertAbsentRealisation(
+ const std::string & uri,
+ const DrvOutput & id) override
+ {
+ retrySQLite<void>([&]() {
+ auto state(_state.lock());
+
+ auto & cache(getCache(*state, uri));
+ state->insertMissingRealisation.use()
+ (cache.id)
+ (id.to_string())
+ (time(0)).exec();
+ });
+ }
};
ref<NarInfoDiskCache> getNarInfoDiskCache()
diff --git a/src/libstore/nar-info-disk-cache.hh b/src/libstore/nar-info-disk-cache.hh
index 04de2c5eb..2dcaa76a4 100644
--- a/src/libstore/nar-info-disk-cache.hh
+++ b/src/libstore/nar-info-disk-cache.hh
@@ -2,6 +2,7 @@
#include "ref.hh"
#include "nar-info.hh"
+#include "realisation.hh"
namespace nix {
@@ -29,6 +30,15 @@ public:
virtual void upsertNarInfo(
const std::string & uri, const std::string & hashPart,
std::shared_ptr<const ValidPathInfo> info) = 0;
+
+ virtual void upsertRealisation(
+ const std::string & uri,
+ const Realisation & realisation) = 0;
+ virtual void upsertAbsentRealisation(
+ const std::string & uri,
+ const DrvOutput & id) = 0;
+ virtual std::pair<Outcome, std::shared_ptr<Realisation>> lookupRealisation(
+ const std::string & uri, const DrvOutput & id) = 0;
};
/* Return a singleton cache object that can be used concurrently by
diff --git a/src/libstore/optimise-store.cc b/src/libstore/optimise-store.cc
index 78d587139..d95e54af1 100644
--- a/src/libstore/optimise-store.cc
+++ b/src/libstore/optimise-store.cc
@@ -198,7 +198,7 @@ void LocalStore::optimisePath_(Activity * act, OptimiseStats & stats,
/* Make the containing directory writable, but only if it's not
the store itself (we don't want or need to mess with its
permissions). */
- bool mustToggle = dirOf(path) != realStoreDir;
+ bool mustToggle = dirOf(path) != realStoreDir.get();
if (mustToggle) makeWritable(dirOf(path));
/* When we're done, make the directory read-only again and reset
diff --git a/src/libstore/parsed-derivations.cc b/src/libstore/parsed-derivations.cc
index c5c3ae3dc..5e383a9a4 100644
--- a/src/libstore/parsed-derivations.cc
+++ b/src/libstore/parsed-derivations.cc
@@ -91,6 +91,8 @@ StringSet ParsedDerivation::getRequiredSystemFeatures() const
StringSet res;
for (auto & i : getStringsAttr("requiredSystemFeatures").value_or(Strings()))
res.insert(i);
+ if (!derivationHasKnownOutputPaths(drv.type()))
+ res.insert("ca-derivations");
return res;
}
diff --git a/src/libstore/realisation.cc b/src/libstore/realisation.cc
index 638065547..eadec594c 100644
--- a/src/libstore/realisation.cc
+++ b/src/libstore/realisation.cc
@@ -1,5 +1,6 @@
#include "realisation.hh"
#include "store-api.hh"
+#include "closure.hh"
#include <nlohmann/json.hpp>
namespace nix {
@@ -21,11 +22,52 @@ std::string DrvOutput::to_string() const {
return strHash() + "!" + outputName;
}
+std::set<Realisation> Realisation::closure(Store & store, const std::set<Realisation> & startOutputs)
+{
+ std::set<Realisation> res;
+ Realisation::closure(store, startOutputs, res);
+ return res;
+}
+
+void Realisation::closure(Store & store, const std::set<Realisation> & startOutputs, std::set<Realisation> & res)
+{
+ auto getDeps = [&](const Realisation& current) -> std::set<Realisation> {
+ std::set<Realisation> res;
+ for (auto& [currentDep, _] : current.dependentRealisations) {
+ if (auto currentRealisation = store.queryRealisation(currentDep))
+ res.insert(*currentRealisation);
+ else
+ throw Error(
+ "Unrealised derivation '%s'", currentDep.to_string());
+ }
+ return res;
+ };
+
+ computeClosure<Realisation>(
+ startOutputs, res,
+ [&](const Realisation& current,
+ std::function<void(std::promise<std::set<Realisation>>&)>
+ processEdges) {
+ std::promise<std::set<Realisation>> promise;
+ try {
+ auto res = getDeps(current);
+ promise.set_value(res);
+ } catch (...) {
+ promise.set_exception(std::current_exception());
+ }
+ return processEdges(promise);
+ });
+}
+
nlohmann::json Realisation::toJSON() const {
+ auto jsonDependentRealisations = nlohmann::json::object();
+ for (auto & [depId, depOutPath] : dependentRealisations)
+ jsonDependentRealisations.emplace(depId.to_string(), depOutPath.to_string());
return nlohmann::json{
{"id", id.to_string()},
{"outPath", outPath.to_string()},
{"signatures", signatures},
+ {"dependentRealisations", jsonDependentRealisations},
};
}
@@ -51,10 +93,16 @@ Realisation Realisation::fromJSON(
if (auto signaturesIterator = json.find("signatures"); signaturesIterator != json.end())
signatures.insert(signaturesIterator->begin(), signaturesIterator->end());
+ std::map <DrvOutput, StorePath> dependentRealisations;
+ if (auto jsonDependencies = json.find("dependentRealisations"); jsonDependencies != json.end())
+ for (auto & [jsonDepId, jsonDepOutPath] : jsonDependencies->get<std::map<std::string, std::string>>())
+ dependentRealisations.insert({DrvOutput::parse(jsonDepId), StorePath(jsonDepOutPath)});
+
return Realisation{
.id = DrvOutput::parse(getField("id")),
.outPath = StorePath(getField("outPath")),
.signatures = signatures,
+ .dependentRealisations = dependentRealisations,
};
}
@@ -92,6 +140,16 @@ StorePath RealisedPath::path() const {
return std::visit([](auto && arg) { return arg.getPath(); }, raw);
}
+bool Realisation::isCompatibleWith(const Realisation & other) const
+{
+ assert (id == other.id);
+ if (outPath == other.outPath) {
+ assert(dependentRealisations == other.dependentRealisations);
+ return true;
+ }
+ return false;
+}
+
void RealisedPath::closure(
Store& store,
const RealisedPath::Set& startPaths,
diff --git a/src/libstore/realisation.hh b/src/libstore/realisation.hh
index f5049c9e9..05d2bc44f 100644
--- a/src/libstore/realisation.hh
+++ b/src/libstore/realisation.hh
@@ -28,6 +28,14 @@ struct Realisation {
StringSet signatures;
+ /**
+ * The realisations that are required for the current one to be valid.
+ *
+ * When importing this realisation, the store will first check that all its
+ * dependencies exist, and map to the correct output path
+ */
+ std::map<DrvOutput, StorePath> dependentRealisations;
+
nlohmann::json toJSON() const;
static Realisation fromJSON(const nlohmann::json& json, const std::string& whence);
@@ -36,6 +44,11 @@ struct Realisation {
bool checkSignature(const PublicKeys & publicKeys, const std::string & sig) const;
size_t checkSignatures(const PublicKeys & publicKeys) const;
+ static std::set<Realisation> closure(Store &, const std::set<Realisation> &);
+ static void closure(Store &, const std::set<Realisation> &, std::set<Realisation>& res);
+
+ bool isCompatibleWith(const Realisation & other) const;
+
StorePath getPath() const { return outPath; }
GENERATE_CMP(Realisation, me->id, me->outPath);
diff --git a/src/libstore/remote-store.cc b/src/libstore/remote-store.cc
index d9b6e9488..aec243637 100644
--- a/src/libstore/remote-store.cc
+++ b/src/libstore/remote-store.cc
@@ -653,8 +653,12 @@ void RemoteStore::registerDrvOutput(const Realisation & info)
{
auto conn(getConnection());
conn->to << wopRegisterDrvOutput;
- conn->to << info.id.to_string();
- conn->to << std::string(info.outPath.to_string());
+ if (GET_PROTOCOL_MINOR(conn->daemonVersion) < 31) {
+ conn->to << info.id.to_string();
+ conn->to << std::string(info.outPath.to_string());
+ } else {
+ worker_proto::write(*this, conn->to, info);
+ }
conn.processStderr();
}
@@ -664,10 +668,17 @@ std::optional<const Realisation> RemoteStore::queryRealisation(const DrvOutput &
conn->to << wopQueryRealisation;
conn->to << id.to_string();
conn.processStderr();
- auto outPaths = worker_proto::read(*this, conn->from, Phantom<std::set<StorePath>>{});
- if (outPaths.empty())
- return std::nullopt;
- return {Realisation{.id = id, .outPath = *outPaths.begin()}};
+ if (GET_PROTOCOL_MINOR(conn->daemonVersion) < 31) {
+ auto outPaths = worker_proto::read(*this, conn->from, Phantom<std::set<StorePath>>{});
+ if (outPaths.empty())
+ return std::nullopt;
+ return {Realisation{.id = id, .outPath = *outPaths.begin()}};
+ } else {
+ auto realisations = worker_proto::read(*this, conn->from, Phantom<std::set<Realisation>>{});
+ if (realisations.empty())
+ return std::nullopt;
+ return *realisations.begin();
+ }
}
static void writeDerivedPaths(RemoteStore & store, ConnectionHandle & conn, const std::vector<DerivedPath> & reqs)
diff --git a/src/libstore/sandbox-defaults.sb b/src/libstore/sandbox-defaults.sb
index 351037822..2bb1ea130 100644
--- a/src/libstore/sandbox-defaults.sb
+++ b/src/libstore/sandbox-defaults.sb
@@ -32,7 +32,9 @@
(literal "/tmp") (subpath TMPDIR))
; Some packages like to read the system version.
-(allow file-read* (literal "/System/Library/CoreServices/SystemVersion.plist"))
+(allow file-read*
+ (literal "/System/Library/CoreServices/SystemVersion.plist")
+ (literal "/System/Library/CoreServices/SystemVersionCompat.plist"))
; Without this line clang cannot write to /dev/null, breaking some configure tests.
(allow file-read-metadata (literal "/dev"))
diff --git a/src/libstore/store-api.cc b/src/libstore/store-api.cc
index 93fcb068f..6736adb24 100644
--- a/src/libstore/store-api.cc
+++ b/src/libstore/store-api.cc
@@ -337,6 +337,13 @@ ValidPathInfo Store::addToStoreSlow(std::string_view name, const Path & srcPath,
return info;
}
+StringSet StoreConfig::getDefaultSystemFeatures()
+{
+ auto res = settings.systemFeatures.get();
+ if (settings.isExperimentalFeatureEnabled("ca-derivations"))
+ res.insert("ca-derivations");
+ return res;
+}
Store::Store(const Params & params)
: StoreConfig(params)
@@ -780,20 +787,39 @@ std::map<StorePath, StorePath> copyPaths(ref<Store> srcStore, ref<Store> dstStor
RepairFlag repair, CheckSigsFlag checkSigs, SubstituteFlag substitute)
{
StorePathSet storePaths;
- std::set<Realisation> realisations;
+ std::set<Realisation> toplevelRealisations;
for (auto & path : paths) {
storePaths.insert(path.path());
if (auto realisation = std::get_if<Realisation>(&path.raw)) {
settings.requireExperimentalFeature("ca-derivations");
- realisations.insert(*realisation);
+ toplevelRealisations.insert(*realisation);
}
}
auto pathsMap = copyPaths(srcStore, dstStore, storePaths, repair, checkSigs, substitute);
+
+ ThreadPool pool;
+
try {
- for (auto & realisation : realisations) {
- dstStore->registerDrvOutput(realisation, checkSigs);
- }
- } catch (MissingExperimentalFeature & e) {
+ // Copy the realisation closure
+ processGraph<Realisation>(
+ pool, Realisation::closure(*srcStore, toplevelRealisations),
+ [&](const Realisation& current) -> std::set<Realisation> {
+ std::set<Realisation> children;
+ for (const auto& [drvOutput, _] : current.dependentRealisations) {
+ auto currentChild = srcStore->queryRealisation(drvOutput);
+ if (!currentChild)
+ throw Error(
+ "Incomplete realisation closure: '%s' is a "
+ "dependency of '%s' but isn’t registered",
+ drvOutput.to_string(), current.id.to_string());
+ children.insert(*currentChild);
+ }
+ return children;
+ },
+ [&](const Realisation& current) -> void {
+ dstStore->registerDrvOutput(current, checkSigs);
+ });
+ } catch (MissingExperimentalFeature& e) {
// Don't fail if the remote doesn't support CA derivations is it might
// not be within our control to change that, and we might still want
// to at least copy the output paths.
diff --git a/src/libstore/store-api.hh b/src/libstore/store-api.hh
index f66298991..9657d2adf 100644
--- a/src/libstore/store-api.hh
+++ b/src/libstore/store-api.hh
@@ -180,6 +180,8 @@ struct StoreConfig : public Config
StoreConfig() = delete;
+ StringSet getDefaultSystemFeatures();
+
virtual ~StoreConfig() { }
virtual const std::string name() = 0;
@@ -196,7 +198,7 @@ struct StoreConfig : public Config
Setting<bool> wantMassQuery{this, false, "want-mass-query", "whether this substituter can be queried efficiently for path validity"};
- Setting<StringSet> systemFeatures{this, settings.systemFeatures,
+ Setting<StringSet> systemFeatures{this, getDefaultSystemFeatures(),
"system-features",
"Optional features that the system this store builds on implements (like \"kvm\")."};
@@ -864,4 +866,9 @@ std::pair<std::string, Store::Params> splitUriAndParams(const std::string & uri)
std::optional<ContentAddress> getDerivationCA(const BasicDerivation & drv);
+std::map<DrvOutput, StorePath> drvOutputReferences(
+ Store & store,
+ const Derivation & drv,
+ const StorePath & outputPath);
+
}
diff --git a/src/libstore/worker-protocol.hh b/src/libstore/worker-protocol.hh
index fdd692cf0..e89183d40 100644
--- a/src/libstore/worker-protocol.hh
+++ b/src/libstore/worker-protocol.hh
@@ -9,7 +9,7 @@ namespace nix {
#define WORKER_MAGIC_1 0x6e697863
#define WORKER_MAGIC_2 0x6478696f
-#define PROTOCOL_VERSION (1 << 8 | 30)
+#define PROTOCOL_VERSION (1 << 8 | 31)
#define GET_PROTOCOL_MAJOR(x) ((x) & 0xff00)
#define GET_PROTOCOL_MINOR(x) ((x) & 0x00ff)