aboutsummaryrefslogtreecommitdiff
path: root/src/libstore/daemon.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/libstore/daemon.cc')
-rw-r--r--src/libstore/daemon.cc355
1 files changed, 224 insertions, 131 deletions
diff --git a/src/libstore/daemon.cc b/src/libstore/daemon.cc
index a8fb62e0a..99d8add92 100644
--- a/src/libstore/daemon.cc
+++ b/src/libstore/daemon.cc
@@ -2,7 +2,6 @@
#include "monitor-fd.hh"
#include "worker-protocol.hh"
#include "store-api.hh"
-#include "local-store.hh"
#include "finally.hh"
#include "affinity.hh"
#include "archive.hh"
@@ -81,12 +80,12 @@ struct TunnelLogger : public Logger
showErrorInfo(oss, ei, false);
StringSink buf;
- buf << STDERR_NEXT << oss.str() << "\n";
+ buf << STDERR_NEXT << oss.str();
enqueueMsg(*buf.s);
}
/* startWork() means that we're starting an operation for which we
- want to send out stderr to the client. */
+ want to send out stderr to the client. */
void startWork()
{
auto state(state_.lock());
@@ -102,17 +101,20 @@ struct TunnelLogger : public Logger
/* stopWork() means that we're done; stop sending stderr to the
client. */
- void stopWork(bool success = true, const string & msg = "", unsigned int status = 0)
+ void stopWork(const Error * ex = nullptr)
{
auto state(state_.lock());
state->canSendStderr = false;
- if (success)
+ if (!ex)
to << STDERR_LAST;
else {
- to << STDERR_ERROR << msg;
- if (status != 0) to << status;
+ if (GET_PROTOCOL_MINOR(clientVersion) >= 26) {
+ to << STDERR_ERROR << *ex;
+ } else {
+ to << STDERR_ERROR << ex->what() << ex->status;
+ }
}
}
@@ -173,31 +175,6 @@ struct TunnelSource : BufferedSource
}
};
-/* If the NAR archive contains a single file at top-level, then save
- the contents of the file to `s'. Otherwise barf. */
-struct RetrieveRegularNARSink : ParseSink
-{
- bool regular;
- string s;
-
- RetrieveRegularNARSink() : regular(true) { }
-
- void createDirectory(const Path & path)
- {
- regular = false;
- }
-
- void receiveContents(unsigned char * data, unsigned int len)
- {
- s.append((const char *) data, len);
- }
-
- void createSymlink(const Path & path, const string & target)
- {
- regular = false;
- }
-};
-
struct ClientSettings
{
bool keepFailed;
@@ -257,7 +234,7 @@ struct ClientSettings
else if (setSubstituters(settings.extraSubstituters))
;
else
- warn("ignoring the user-specified setting '%s', because it is a restricted setting and you are not a trusted user", name);
+ debug("ignoring the client-specified setting '%s', because it is a restricted setting and you are not a trusted user", name);
} catch (UsageError & e) {
warn(e.what());
}
@@ -265,6 +242,23 @@ struct ClientSettings
}
};
+static void writeValidPathInfo(
+ ref<Store> store,
+ unsigned int clientVersion,
+ Sink & to,
+ std::shared_ptr<const ValidPathInfo> info)
+{
+ to << (info->deriver ? store->printStorePath(*info->deriver) : "")
+ << info->narHash.to_string(Base16, false);
+ worker_proto::write(*store, to, info->references);
+ to << info->registrationTime << info->narSize;
+ if (GET_PROTOCOL_MINOR(clientVersion) >= 16) {
+ to << info->ultimate
+ << info->sigs
+ << renderContentAddress(info->ca);
+ }
+}
+
static void performOp(TunnelLogger * logger, ref<Store> store,
TrustedFlag trusted, RecursiveFlag recursive, unsigned int clientVersion,
Source & from, BufferedSink & to, unsigned int op)
@@ -281,11 +275,11 @@ static void performOp(TunnelLogger * logger, ref<Store> store,
}
case wopQueryValidPaths: {
- auto paths = readStorePaths<StorePathSet>(*store, from);
+ auto paths = worker_proto::read(*store, from, Phantom<StorePathSet> {});
logger->startWork();
auto res = store->queryValidPaths(paths);
logger->stopWork();
- writeStorePaths(*store, to, res);
+ worker_proto::write(*store, to, res);
break;
}
@@ -301,11 +295,11 @@ static void performOp(TunnelLogger * logger, ref<Store> store,
}
case wopQuerySubstitutablePaths: {
- auto paths = readStorePaths<StorePathSet>(*store, from);
+ auto paths = worker_proto::read(*store, from, Phantom<StorePathSet> {});
logger->startWork();
auto res = store->querySubstitutablePaths(paths);
logger->stopWork();
- writeStorePaths(*store, to, res);
+ worker_proto::write(*store, to, res);
break;
}
@@ -334,7 +328,7 @@ static void performOp(TunnelLogger * logger, ref<Store> store,
paths = store->queryValidDerivers(path);
else paths = store->queryDerivationOutputs(path);
logger->stopWork();
- writeStorePaths(*store, to, paths);
+ worker_proto::write(*store, to, paths);
break;
}
@@ -350,9 +344,9 @@ static void performOp(TunnelLogger * logger, ref<Store> store,
case wopQueryDerivationOutputMap: {
auto path = store->parseStorePath(readString(from));
logger->startWork();
- OutputPathMap outputs = store->queryDerivationOutputMap(path);
+ auto outputs = store->queryPartialDerivationOutputMap(path);
logger->stopWork();
- writeOutputPathMap(*store, to, outputs);
+ worker_proto::write(*store, to, outputs);
break;
}
@@ -375,52 +369,90 @@ static void performOp(TunnelLogger * logger, ref<Store> store,
}
case wopAddToStore: {
- std::string s, baseName;
- FileIngestionMethod method;
- {
- bool fixed; uint8_t recursive;
- from >> baseName >> fixed /* obsolete */ >> recursive >> s;
- if (recursive > (uint8_t) FileIngestionMethod::Recursive)
- throw Error("unsupported FileIngestionMethod with value of %i; you may need to upgrade nix-daemon", recursive);
- method = FileIngestionMethod { recursive };
- /* Compatibility hack. */
- if (!fixed) {
- s = "sha256";
- method = FileIngestionMethod::Recursive;
+ if (GET_PROTOCOL_MINOR(clientVersion) >= 25) {
+ auto name = readString(from);
+ auto camStr = readString(from);
+ auto refs = worker_proto::read(*store, from, Phantom<StorePathSet> {});
+ bool repairBool;
+ from >> repairBool;
+ auto repair = RepairFlag{repairBool};
+
+ logger->startWork();
+ auto pathInfo = [&]() {
+ // NB: FramedSource must be out of scope before logger->stopWork();
+ ContentAddressMethod contentAddressMethod = parseContentAddressMethod(camStr);
+ FramedSource source(from);
+ // TODO this is essentially RemoteStore::addCAToStore. Move it up to Store.
+ return std::visit(overloaded {
+ [&](TextHashMethod &_) {
+ // We could stream this by changing Store
+ std::string contents = source.drain();
+ auto path = store->addTextToStore(name, contents, refs, repair);
+ return store->queryPathInfo(path);
+ },
+ [&](FixedOutputHashMethod &fohm) {
+ if (!refs.empty())
+ throw UnimplementedError("cannot yet have refs with flat or nar-hashed data");
+ auto path = store->addToStoreFromDump(source, name, fohm.fileIngestionMethod, fohm.hashType, repair);
+ return store->queryPathInfo(path);
+ },
+ }, contentAddressMethod);
+ }();
+ logger->stopWork();
+
+ to << store->printStorePath(pathInfo->path);
+ writeValidPathInfo(store, clientVersion, to, pathInfo);
+
+ } else {
+ HashType hashAlgo;
+ std::string baseName;
+ FileIngestionMethod method;
+ {
+ bool fixed;
+ uint8_t recursive;
+ std::string hashAlgoRaw;
+ from >> baseName >> fixed /* obsolete */ >> recursive >> hashAlgoRaw;
+ if (recursive > (uint8_t) FileIngestionMethod::Recursive)
+ throw Error("unsupported FileIngestionMethod with value of %i; you may need to upgrade nix-daemon", recursive);
+ method = FileIngestionMethod { recursive };
+ /* Compatibility hack. */
+ if (!fixed) {
+ hashAlgoRaw = "sha256";
+ method = FileIngestionMethod::Recursive;
+ }
+ hashAlgo = parseHashType(hashAlgoRaw);
}
- }
- HashType hashAlgo = parseHashType(s);
- TeeSource savedNAR(from);
- RetrieveRegularNARSink savedRegular;
+ StringSink saved;
+ TeeSource savedNARSource(from, saved);
+ RetrieveRegularNARSink savedRegular { saved };
- if (method == FileIngestionMethod::Recursive) {
- /* Get the entire NAR dump from the client and save it to
- a string so that we can pass it to
- addToStoreFromDump(). */
- ParseSink sink; /* null sink; just parse the NAR */
- parseDump(sink, savedNAR);
- } else
- parseDump(savedRegular, from);
+ if (method == FileIngestionMethod::Recursive) {
+ /* Get the entire NAR dump from the client and save it to
+ a string so that we can pass it to
+ addToStoreFromDump(). */
+ ParseSink sink; /* null sink; just parse the NAR */
+ parseDump(sink, savedNARSource);
+ } else
+ parseDump(savedRegular, from);
- logger->startWork();
- if (!savedRegular.regular) throw Error("regular file expected");
+ logger->startWork();
+ if (!savedRegular.regular) throw Error("regular file expected");
- auto path = store->addToStoreFromDump(
- method == FileIngestionMethod::Recursive ? *savedNAR.data : savedRegular.s,
- baseName,
- method,
- hashAlgo);
- logger->stopWork();
+ // FIXME: try to stream directly from `from`.
+ StringSource dumpSource { *saved.s };
+ auto path = store->addToStoreFromDump(dumpSource, baseName, method, hashAlgo);
+ logger->stopWork();
- to << store->printStorePath(path);
+ to << store->printStorePath(path);
+ }
break;
}
case wopAddTextToStore: {
string suffix = readString(from);
string s = readString(from);
- auto refs = readStorePaths<StorePathSet>(*store, from);
+ auto refs = worker_proto::read(*store, from, Phantom<StorePathSet> {});
logger->startWork();
auto path = store->addTextToStore(suffix, s, refs, NoRepair);
logger->stopWork();
@@ -442,7 +474,7 @@ static void performOp(TunnelLogger * logger, ref<Store> store,
case wopImportPaths: {
logger->startWork();
TunnelSource source(from, to);
- auto paths = store->importPaths(source, nullptr,
+ auto paths = store->importPaths(source,
trusted ? NoCheckSigs : CheckSigs);
logger->stopWork();
Strings paths2;
@@ -474,11 +506,63 @@ static void performOp(TunnelLogger * logger, ref<Store> store,
case wopBuildDerivation: {
auto drvPath = store->parseStorePath(readString(from));
BasicDerivation drv;
- readDerivation(from, *store, drv);
+ readDerivation(from, *store, drv, Derivation::nameFromPath(drvPath));
BuildMode buildMode = (BuildMode) readInt(from);
logger->startWork();
- if (!trusted)
- throw Error("you are not privileged to build derivations");
+
+ /* Content-addressed derivations are trustless because their output paths
+ are verified by their content alone, so any derivation is free to
+ try to produce such a path.
+
+ Input-addressed derivation output paths, however, are calculated
+ from the derivation closure that produced them---even knowing the
+ root derivation is not enough. That the output data actually came
+ from those derivations is fundamentally unverifiable, but the daemon
+ trusts itself on that matter. The question instead is whether the
+ submitted plan has rights to the output paths it wants to fill, and
+ at least the derivation closure proves that.
+
+ It would have been nice if input-address algorithm merely depended
+ on the build time closure, rather than depending on the derivation
+ closure. That would mean input-addressed paths used at build time
+ would just be trusted and not need their own evidence. This is in
+ fact fine as the same guarantees would hold *inductively*: either
+ the remote builder has those paths and already trusts them, or it
+ needs to build them too and thus their evidence must be provided in
+ turn. The advantage of this variant algorithm is that the evidence
+ for input-addressed paths which the remote builder already has
+ doesn't need to be sent again.
+
+ That said, now that we have floating CA derivations, it is better
+ that people just migrate to those which also solve this problem, and
+ others. It's the same migration difficulty with strictly more
+ benefit.
+
+ Lastly, do note that when we parse fixed-output content-addressed
+ derivations, we throw out the precomputed output paths and just
+ store the hashes, so there aren't two competing sources of truth an
+ attacker could exploit. */
+ if (drv.type() == DerivationType::InputAddressed && !trusted)
+ throw Error("you are not privileged to build input-addressed derivations");
+
+ /* Make sure that the non-input-addressed derivations that got this far
+ are in fact content-addressed if we don't trust them. */
+ assert(derivationIsCA(drv.type()) || trusted);
+
+ /* Recompute the derivation path when we cannot trust the original. */
+ if (!trusted) {
+ /* Recomputing the derivation path for input-address derivations
+ makes it harder to audit them after the fact, since we need the
+ original not-necessarily-resolved derivation to verify the drv
+ derivation as adequate claim to the input-addressed output
+ paths. */
+ assert(derivationIsCA(drv.type()));
+
+ Derivation drv2;
+ static_cast<BasicDerivation &>(drv2) = drv;
+ drvPath = writeDerivation(*store, Derivation { drv2 });
+ }
+
auto res = store->buildDerivation(drvPath, drv, buildMode);
logger->stopWork();
to << res.status << res.errorMsg;
@@ -541,7 +625,7 @@ static void performOp(TunnelLogger * logger, ref<Store> store,
case wopCollectGarbage: {
GCOptions options;
options.action = (GCOptions::GCAction) readInt(from);
- options.pathsToDelete = readStorePaths<StorePathSet>(*store, from);
+ options.pathsToDelete = worker_proto::read(*store, from, Phantom<StorePathSet> {});
from >> options.ignoreLiveness >> options.maxFreed;
// obsolete fields
readInt(from);
@@ -602,7 +686,7 @@ static void performOp(TunnelLogger * logger, ref<Store> store,
auto path = store->parseStorePath(readString(from));
logger->startWork();
SubstitutablePathInfos infos;
- store->querySubstitutablePathInfos({path}, infos);
+ store->querySubstitutablePathInfos({{path, std::nullopt}}, infos);
logger->stopWork();
auto i = infos.find(path);
if (i == infos.end())
@@ -610,7 +694,7 @@ static void performOp(TunnelLogger * logger, ref<Store> store,
else {
to << 1
<< (i->second.deriver ? store->printStorePath(*i->second.deriver) : "");
- writeStorePaths(*store, to, i->second.references);
+ worker_proto::write(*store, to, i->second.references);
to << i->second.downloadSize
<< i->second.narSize;
}
@@ -618,16 +702,22 @@ static void performOp(TunnelLogger * logger, ref<Store> store,
}
case wopQuerySubstitutablePathInfos: {
- auto paths = readStorePaths<StorePathSet>(*store, from);
- logger->startWork();
SubstitutablePathInfos infos;
- store->querySubstitutablePathInfos(paths, infos);
+ StorePathCAMap pathsMap = {};
+ if (GET_PROTOCOL_MINOR(clientVersion) < 22) {
+ auto paths = worker_proto::read(*store, from, Phantom<StorePathSet> {});
+ for (auto & path : paths)
+ pathsMap.emplace(path, std::nullopt);
+ } else
+ pathsMap = worker_proto::read(*store, from, Phantom<StorePathCAMap> {});
+ logger->startWork();
+ store->querySubstitutablePathInfos(pathsMap, infos);
logger->stopWork();
to << infos.size();
for (auto & i : infos) {
to << store->printStorePath(i.first)
<< (i.second.deriver ? store->printStorePath(*i.second.deriver) : "");
- writeStorePaths(*store, to, i.second.references);
+ worker_proto::write(*store, to, i.second.references);
to << i.second.downloadSize << i.second.narSize;
}
break;
@@ -637,7 +727,7 @@ static void performOp(TunnelLogger * logger, ref<Store> store,
logger->startWork();
auto paths = store->queryAllValidPaths();
logger->stopWork();
- writeStorePaths(*store, to, paths);
+ worker_proto::write(*store, to, paths);
break;
}
@@ -654,15 +744,7 @@ static void performOp(TunnelLogger * logger, ref<Store> store,
if (info) {
if (GET_PROTOCOL_MINOR(clientVersion) >= 17)
to << 1;
- to << (info->deriver ? store->printStorePath(*info->deriver) : "")
- << info->narHash.to_string(Base16, false);
- writeStorePaths(*store, to, info->references);
- to << info->registrationTime << info->narSize;
- if (GET_PROTOCOL_MINOR(clientVersion) >= 16) {
- to << info->ultimate
- << info->sigs
- << renderContentAddress(info->ca);
- }
+ writeValidPathInfo(store, clientVersion, to, info);
} else {
assert(GET_PROTOCOL_MINOR(clientVersion) >= 17);
to << 0;
@@ -705,18 +787,19 @@ static void performOp(TunnelLogger * logger, ref<Store> store,
auto path = store->parseStorePath(readString(from));
logger->startWork();
logger->stopWork();
- dumpPath(store->printStorePath(path), to);
+ dumpPath(store->toRealPath(path), to);
break;
}
case wopAddToStoreNar: {
bool repair, dontCheckSigs;
- ValidPathInfo info(store->parseStorePath(readString(from)));
+ auto path = store->parseStorePath(readString(from));
auto deriver = readString(from);
+ auto narHash = Hash::parseAny(readString(from), htSHA256);
+ ValidPathInfo info { path, narHash };
if (deriver != "")
info.deriver = store->parseStorePath(deriver);
- info.narHash = Hash(readString(from), htSHA256);
- info.references = readStorePaths<StorePathSet>(*store, from);
+ info.references = worker_proto::read(*store, from, Phantom<StorePathSet> {});
from >> info.registrationTime >> info.narSize >> info.ultimate;
info.sigs = readStrings<StringSet>(from);
info.ca = parseContentAddressOpt(readString(from));
@@ -726,24 +809,37 @@ static void performOp(TunnelLogger * logger, ref<Store> store,
if (!trusted)
info.ultimate = false;
- std::string saved;
- std::unique_ptr<Source> source;
- if (GET_PROTOCOL_MINOR(clientVersion) >= 21)
- source = std::make_unique<TunnelSource>(from, to);
- else {
- TeeSink tee(from);
- parseDump(tee, tee.source);
- saved = std::move(*tee.source.data);
- source = std::make_unique<StringSource>(saved);
+ if (GET_PROTOCOL_MINOR(clientVersion) >= 23) {
+ logger->startWork();
+ {
+ FramedSource source(from);
+ store->addToStore(info, source, (RepairFlag) repair,
+ dontCheckSigs ? NoCheckSigs : CheckSigs);
+ }
+ logger->stopWork();
}
- logger->startWork();
+ else {
+ std::unique_ptr<Source> source;
+ if (GET_PROTOCOL_MINOR(clientVersion) >= 21)
+ source = std::make_unique<TunnelSource>(from, to);
+ else {
+ StringSink saved;
+ TeeSource tee { from, saved };
+ ParseSink ether;
+ parseDump(ether, tee);
+ source = std::make_unique<StringSource>(std::move(*saved.s));
+ }
- // FIXME: race if addToStore doesn't read source?
- store->addToStore(info, *source, (RepairFlag) repair,
- dontCheckSigs ? NoCheckSigs : CheckSigs, nullptr);
+ logger->startWork();
+
+ // FIXME: race if addToStore doesn't read source?
+ store->addToStore(info, *source, (RepairFlag) repair,
+ dontCheckSigs ? NoCheckSigs : CheckSigs);
+
+ logger->stopWork();
+ }
- logger->stopWork();
break;
}
@@ -753,12 +849,12 @@ static void performOp(TunnelLogger * logger, ref<Store> store,
targets.push_back(store->parsePathWithOutputs(s));
logger->startWork();
StorePathSet willBuild, willSubstitute, unknown;
- unsigned long long downloadSize, narSize;
+ uint64_t downloadSize, narSize;
store->queryMissing(targets, willBuild, willSubstitute, unknown, downloadSize, narSize);
logger->stopWork();
- writeStorePaths(*store, to, willBuild);
- writeStorePaths(*store, to, willSubstitute);
- writeStorePaths(*store, to, unknown);
+ worker_proto::write(*store, to, willBuild);
+ worker_proto::write(*store, to, willSubstitute);
+ worker_proto::write(*store, to, unknown);
to << downloadSize << narSize;
break;
}
@@ -774,8 +870,7 @@ void processConnection(
FdSink & to,
TrustedFlag trusted,
RecursiveFlag recursive,
- const std::string & userName,
- uid_t userId)
+ std::function<void(Store &)> authHook)
{
auto monitor = !recursive ? std::make_unique<MonitorFdHup>(from.fd) : nullptr;
@@ -816,15 +911,7 @@ void processConnection(
/* If we can't accept clientVersion, then throw an error
*here* (not above). */
-
-#if 0
- /* Prevent users from doing something very dangerous. */
- if (geteuid() == 0 &&
- querySetting("build-users-group", "") == "")
- throw Error("if you run 'nix-daemon' as root, then you MUST set 'build-users-group'!");
-#endif
-
- store->createUser(userName, userId);
+ authHook(*store);
tunnelLogger->stopWork();
to.flush();
@@ -851,10 +938,11 @@ void processConnection(
during addTextToStore() / importPath(). If that
happens, just send the error message and exit. */
bool errorAllowed = tunnelLogger->state_.lock()->canSendStderr;
- tunnelLogger->stopWork(false, e.msg(), e.status);
+ tunnelLogger->stopWork(&e);
if (!errorAllowed) throw;
} catch (std::bad_alloc & e) {
- tunnelLogger->stopWork(false, "Nix daemon out of memory", 1);
+ auto ex = Error("Nix daemon out of memory");
+ tunnelLogger->stopWork(&ex);
throw;
}
@@ -863,8 +951,13 @@ void processConnection(
assert(!tunnelLogger->state_.lock()->canSendStderr);
};
+ } catch (Error & e) {
+ tunnelLogger->stopWork(&e);
+ to.flush();
+ return;
} catch (std::exception & e) {
- tunnelLogger->stopWork(false, e.what(), 1);
+ auto ex = Error(e.what());
+ tunnelLogger->stopWork(&ex);
to.flush();
return;
}