diff --git a/src/libstore/build.cc b/src/libstore/build.cc index afb2bb096..dd932cee9 100644 --- a/src/libstore/build.cc +++ b/src/libstore/build.cc @@ -2756,8 +2756,12 @@ struct RestrictedStore : public LocalFSStore void queryReferrers(const StorePath & path, StorePathSet & referrers) override { } - OutputPathMap queryDerivationOutputMap(const StorePath & path) override - { throw Error("queryDerivationOutputMap"); } + std::map> queryPartialDerivationOutputMap(const StorePath & path) override + { + if (!goal.isAllowed(path)) + throw InvalidPath("cannot query output map for unknown path '%s' in recursive Nix", printStorePath(path)); + return next->queryPartialDerivationOutputMap(path); + } std::optional queryPathFromHashPart(const std::string & hashPart) override { throw Error("queryPathFromHashPart"); } @@ -4983,7 +4987,7 @@ void Worker::waitForInput() std::vector buffer(4096); for (auto & k : fds2) { if (pollStatus.at(fdToPollStatus.at(k)).revents) { - ssize_t rd = read(k, buffer.data(), buffer.size()); + ssize_t rd = ::read(k, buffer.data(), buffer.size()); // FIXME: is there a cleaner way to handle pt close // than EIO? Is this even standard? if (rd == 0 || (rd == -1 && errno == EIO)) { diff --git a/src/libstore/daemon.cc b/src/libstore/daemon.cc index ad3fe1847..f35ddb522 100644 --- a/src/libstore/daemon.cc +++ b/src/libstore/daemon.cc @@ -325,9 +325,9 @@ static void performOp(TunnelLogger * logger, ref 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; } diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc index bccd77b68..a79b2da44 100644 --- a/src/libstore/local-store.cc +++ b/src/libstore/local-store.cc @@ -785,17 +785,21 @@ StorePathSet LocalStore::queryValidDerivers(const StorePath & path) } -OutputPathMap LocalStore::queryDerivationOutputMap(const StorePath & path) +std::map> LocalStore::queryPartialDerivationOutputMap(const StorePath & path) { - return retrySQLite([&]() { + std::map> outputs; + BasicDerivation drv = readDerivation(path); + for (auto & [outName, _] : drv.outputs) { + outputs.insert_or_assign(outName, std::nullopt); + } + return retrySQLite>>([&]() { auto state(_state.lock()); auto useQueryDerivationOutputs(state->stmtQueryDerivationOutputs.use() (queryValidPathId(*state, path))); - OutputPathMap outputs; while (useQueryDerivationOutputs.next()) - outputs.emplace( + outputs.insert_or_assign( useQueryDerivationOutputs.getStr(0), parseStorePath(useQueryDerivationOutputs.getStr(1)) ); diff --git a/src/libstore/local-store.hh b/src/libstore/local-store.hh index 31e6587ac..5af12c2b2 100644 --- a/src/libstore/local-store.hh +++ b/src/libstore/local-store.hh @@ -133,7 +133,7 @@ public: StorePathSet queryValidDerivers(const StorePath & path) override; - OutputPathMap queryDerivationOutputMap(const StorePath & path) override; + std::map> queryPartialDerivationOutputMap(const StorePath & path) override; std::optional queryPathFromHashPart(const std::string & hashPart) override; diff --git a/src/libstore/remote-store.cc b/src/libstore/remote-store.cc index 8dcc1d710..c2c6eff66 100644 --- a/src/libstore/remote-store.cc +++ b/src/libstore/remote-store.cc @@ -31,7 +31,6 @@ template<> StorePathSet readStorePaths(const Store & store, Source & from) return paths; } - void writeStorePaths(const Store & store, Sink & out, const StorePathSet & paths) { out << paths.size(); @@ -39,6 +38,7 @@ void writeStorePaths(const Store & store, Sink & out, const StorePathSet & paths out << store.printStorePath(i); } + StorePathCAMap readStorePathCAMap(const Store & store, Source & from) { StorePathCAMap paths; @@ -57,30 +57,36 @@ void writeStorePathCAMap(const Store & store, Sink & out, const StorePathCAMap & } } -std::map readOutputPathMap(const Store & store, Source & from) + +namespace worker_proto { + +StorePath read(const Store & store, Source & from, Phantom _) { - std::map pathMap; - auto rawInput = readStrings(from); - if (rawInput.size() % 2) - throw Error("got an odd number of elements from the daemon when trying to read a output path map"); - auto curInput = rawInput.begin(); - while (curInput != rawInput.end()) { - auto thisKey = *curInput++; - auto thisValue = *curInput++; - pathMap.emplace(thisKey, store.parseStorePath(thisValue)); - } - return pathMap; + return store.parseStorePath(readString(from)); } -void writeOutputPathMap(const Store & store, Sink & out, const std::map & pathMap) +void write(const Store & store, Sink & out, const StorePath & storePath) { - out << 2*pathMap.size(); - for (auto & i : pathMap) { - out << i.first; - out << store.printStorePath(i.second); - } + out << store.printStorePath(storePath); } + +template<> +std::optional read(const Store & store, Source & from, Phantom> _) +{ + auto s = readString(from); + return s == "" ? std::optional {} : store.parseStorePath(s); +} + +template<> +void write(const Store & store, Sink & out, const std::optional & storePathOpt) +{ + out << (storePathOpt ? store.printStorePath(*storePathOpt) : ""); +} + +} + + /* TODO: Separate these store impls into different files, give them better names */ RemoteStore::RemoteStore(const Params & params) : Store(params) @@ -468,12 +474,12 @@ StorePathSet RemoteStore::queryDerivationOutputs(const StorePath & path) } -OutputPathMap RemoteStore::queryDerivationOutputMap(const StorePath & path) +std::map> RemoteStore::queryPartialDerivationOutputMap(const StorePath & path) { auto conn(getConnection()); conn->to << wopQueryDerivationOutputMap << printStorePath(path); conn.processStderr(); - return readOutputPathMap(*this, conn->from); + return worker_proto::read(*this, conn->from, Phantom>> {}); } diff --git a/src/libstore/remote-store.hh b/src/libstore/remote-store.hh index 72d2a6689..b319e774b 100644 --- a/src/libstore/remote-store.hh +++ b/src/libstore/remote-store.hh @@ -51,7 +51,7 @@ public: StorePathSet queryDerivationOutputs(const StorePath & path) override; - OutputPathMap queryDerivationOutputMap(const StorePath & path) override; + std::map> queryPartialDerivationOutputMap(const StorePath & path) override; std::optional queryPathFromHashPart(const std::string & hashPart) override; StorePathSet querySubstitutablePaths(const StorePathSet & paths) override; diff --git a/src/libstore/store-api.cc b/src/libstore/store-api.cc index 539a66c98..b8ce71f3c 100644 --- a/src/libstore/store-api.cc +++ b/src/libstore/store-api.cc @@ -359,6 +359,17 @@ bool Store::PathInfoCacheValue::isKnownNow() return std::chrono::steady_clock::now() < time_point + ttl; } +OutputPathMap Store::queryDerivationOutputMap(const StorePath & path) { + auto resp = queryPartialDerivationOutputMap(path); + OutputPathMap result; + for (auto & [outName, optOutPath] : resp) { + if (!optOutPath) + throw Error("output '%s' has no store path mapped to it", outName); + result.insert_or_assign(outName, *optOutPath); + } + return result; +} + StorePathSet Store::queryDerivationOutputs(const StorePath & path) { auto outputMap = this->queryDerivationOutputMap(path); diff --git a/src/libstore/store-api.hh b/src/libstore/store-api.hh index 128682e7a..0ec14b5f2 100644 --- a/src/libstore/store-api.hh +++ b/src/libstore/store-api.hh @@ -343,9 +343,15 @@ public: /* Query the outputs of the derivation denoted by `path'. */ virtual StorePathSet queryDerivationOutputs(const StorePath & path); - /* Query the mapping outputName=>outputPath for the given derivation */ - virtual OutputPathMap queryDerivationOutputMap(const StorePath & path) - { unsupported("queryDerivationOutputMap"); } + /* Query the mapping outputName => outputPath for the given derivation. All + outputs are mentioned so ones mising the mapping are mapped to + `std::nullopt`. */ + virtual std::map> queryPartialDerivationOutputMap(const StorePath & path) + { unsupported("queryPartialDerivationOutputMap"); } + + /* Query the mapping outputName=>outputPath for the given derivation. + Assume every output has a mapping and throw an exception otherwise. */ + OutputPathMap queryDerivationOutputMap(const StorePath & path); /* Query the full store path given the hash part of a valid store path, or empty if the path doesn't exist. */ diff --git a/src/libstore/worker-protocol.hh b/src/libstore/worker-protocol.hh index 5eddaff56..13cf8d4ab 100644 --- a/src/libstore/worker-protocol.hh +++ b/src/libstore/worker-protocol.hh @@ -70,10 +70,84 @@ template T readStorePaths(const Store & store, Source & from); void writeStorePaths(const Store & store, Sink & out, const StorePathSet & paths); +/* To guide overloading */ +template +struct Phantom {}; + + +namespace worker_proto { +/* FIXME maybe move more stuff inside here */ + +StorePath read(const Store & store, Source & from, Phantom _); +void write(const Store & store, Sink & out, const StorePath & storePath); + +template +std::map read(const Store & store, Source & from, Phantom> _); +template +void write(const Store & store, Sink & out, const std::map & resMap); +template +std::optional read(const Store & store, Source & from, Phantom> _); +template +void write(const Store & store, Sink & out, const std::optional & optVal); + +/* Specialization which uses and empty string for the empty case, taking + advantage of the fact StorePaths always serialize to a non-empty string. + This is done primarily for backwards compatability, so that StorePath <= + std::optional, where <= is the compatability partial order. + */ +template<> +void write(const Store & store, Sink & out, const std::optional & optVal); + +template +std::map read(const Store & store, Source & from, Phantom> _) +{ + std::map resMap; + auto size = (size_t)readInt(from); + while (size--) { + auto thisKey = readString(from); + resMap.insert_or_assign(std::move(thisKey), nix::worker_proto::read(store, from, Phantom {})); + } + return resMap; +} + +template +void write(const Store & store, Sink & out, const std::map & resMap) +{ + out << resMap.size(); + for (auto & i : resMap) { + out << i.first; + nix::worker_proto::write(store, out, i.second); + } +} + +template +std::optional read(const Store & store, Source & from, Phantom> _) +{ + auto tag = readNum(from); + switch (tag) { + case 0: + return std::nullopt; + case 1: + return nix::worker_proto::read(store, from, Phantom {}); + default: + throw Error("got an invalid tag bit for std::optional: %#04x", tag); + } +} + +template +void write(const Store & store, Sink & out, const std::optional & optVal) +{ + out << (optVal ? 1 : 0); + if (optVal) + nix::worker_proto::write(store, out, *optVal); +} + + +} + + StorePathCAMap readStorePathCAMap(const Store & store, Source & from); void writeStorePathCAMap(const Store & store, Sink & out, const StorePathCAMap & paths); -void writeOutputPathMap(const Store & store, Sink & out, const OutputPathMap & paths); - }