diff options
-rw-r--r-- | src/libexpr/primops.cc | 40 | ||||
-rw-r--r-- | src/libstore/build/local-derivation-goal.cc | 52 | ||||
-rw-r--r-- | src/libstore/content-address.cc | 140 | ||||
-rw-r--r-- | src/libstore/content-address.hh | 57 | ||||
-rw-r--r-- | src/libstore/daemon.cc | 12 | ||||
-rw-r--r-- | src/libstore/derivations.cc | 42 | ||||
-rw-r--r-- | src/libstore/derivations.hh | 4 | ||||
-rw-r--r-- | src/libstore/local-store.cc | 2 | ||||
-rw-r--r-- | src/libstore/misc.cc | 18 | ||||
-rw-r--r-- | src/libstore/remote-store.cc | 20 | ||||
-rw-r--r-- | src/libstore/remote-store.hh | 1 | ||||
-rw-r--r-- | src/nix/show-derivation.cc | 7 | ||||
-rw-r--r-- | tests/local.mk | 1 | ||||
-rw-r--r-- | tests/text-hashed-output.nix | 29 | ||||
-rw-r--r-- | tests/text-hashed-output.sh | 26 |
15 files changed, 318 insertions, 133 deletions
diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 4626c4327..fd7a34926 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -934,7 +934,7 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * * bool contentAddressed = false; std::optional<std::string> outputHash; std::string outputHashAlgo; - auto ingestionMethod = FileIngestionMethod::Flat; + ContentAddressMethod ingestionMethod = FileIngestionMethod::Flat; StringSet outputs; outputs.insert("out"); @@ -947,6 +947,7 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * * auto handleHashMode = [&](const std::string & s) { if (s == "recursive") ingestionMethod = FileIngestionMethod::Recursive; else if (s == "flat") ingestionMethod = FileIngestionMethod::Flat; + else if (s == "text") ingestionMethod = TextHashMethod {}; else throw EvalError({ .msg = hintfmt("invalid value '%s' for 'outputHashMode' attribute", s), @@ -1078,8 +1079,11 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * * state.store->computeFSClosure(state.store->parseStorePath(std::string_view(path).substr(1)), refs); for (auto & j : refs) { drv.inputSrcs.insert(j); - if (j.isDerivation()) - drv.inputDrvs[j] = state.store->readDerivation(j).outputNames(); + if (j.isDerivation()) { + Derivation jDrv = state.store->readDerivation(j); + if(jDrv.type() != DerivationType::CAFloating) + drv.inputDrvs[j] = jDrv.outputNames(); + } } } @@ -1108,9 +1112,9 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * * }); /* Check whether the derivation name is valid. */ - if (isDerivation(drvName)) + if (isDerivation(drvName) && ingestionMethod != ContentAddressMethod { TextHashMethod { } }) throw EvalError({ - .msg = hintfmt("derivation names are not allowed to end in '%s'", drvExtension), + .msg = hintfmt("derivation names are allowed to end in '%s' only if they produce a single derivation file", drvExtension), .errPos = posDrvName }); @@ -1128,22 +1132,16 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * * std::optional<HashType> ht = parseHashTypeOpt(outputHashAlgo); Hash h = newHashAllowEmpty(*outputHash, ht); - auto outPath = state.store->makeFixedOutputPath(drvName, FixedOutputInfo { - { - .method = ingestionMethod, - .hash = h, - }, - {}, - }); - drv.env["out"] = state.store->printStorePath(outPath); - drv.outputs.insert_or_assign("out", DerivationOutput { - .output = DerivationOutputCAFixed { - .hash = FixedOutputHash { - .method = ingestionMethod, - .hash = std::move(h), - }, - }, - }); + // FIXME non-trivial fixed refs set + auto ca = contentAddressFromMethodHashAndRefs( + ingestionMethod, + std::move(h), + {}); + + DerivationOutputCAFixed dof { .ca = ca }; + + drv.env["out"] = state.store->printStorePath(dof.path(*state.store, drvName, "out")); + drv.outputs.insert_or_assign("out", DerivationOutput { .output = dof }); } else if (contentAddressed) { diff --git a/src/libstore/build/local-derivation-goal.cc b/src/libstore/build/local-derivation-goal.cc index d104d3148..3dd3a48b7 100644 --- a/src/libstore/build/local-derivation-goal.cc +++ b/src/libstore/build/local-derivation-goal.cc @@ -2247,38 +2247,44 @@ void LocalDerivationGoal::registerOutputs() auto newInfoFromCA = [&](const DerivationOutputCAFloating outputHash) -> ValidPathInfo { auto & st = outputStats.at(outputName); - if (outputHash.method == FileIngestionMethod::Flat) { + if (outputHash.method == ContentAddressMethod { FileIngestionMethod::Flat } || + outputHash.method == ContentAddressMethod { TextHashMethod {} }) + { /* The output path should be a regular file without execute permission. */ if (!S_ISREG(st.st_mode) || (st.st_mode & S_IXUSR) != 0) throw BuildError( "output path '%1%' should be a non-executable regular file " - "since recursive hashing is not enabled (outputHashMode=flat)", + "since recursive hashing is not enabled (one of outputHashMode={flat,text} is true)", actualPath); } rewriteOutput(); /* FIXME optimize and deduplicate with addToStore */ std::string oldHashPart { scratchPath.hashPart() }; HashModuloSink caSink { outputHash.hashType, oldHashPart }; - switch (outputHash.method) { - case FileIngestionMethod::Recursive: - dumpPath(actualPath, caSink); - break; - case FileIngestionMethod::Flat: - readFile(actualPath, caSink); - break; - } + std::visit(overloaded { + [&](TextHashMethod _) { + readFile(actualPath, caSink); + }, + [&](FileIngestionMethod m2) { + switch (m2) { + case FileIngestionMethod::Recursive: + dumpPath(actualPath, caSink); + break; + case FileIngestionMethod::Flat: + readFile(actualPath, caSink); + break; + } + }, + }, outputHash.method); auto got = caSink.finish().first; ValidPathInfo newInfo0 { worker.store, { .name = outputPathName(drv->name, outputName), - .info = FixedOutputInfo { - { - .method = outputHash.method, - .hash = got, - }, - rewriteRefs(), - }, + .info = contentAddressFromMethodHashAndRefs( + outputHash.method, + std::move(got), + rewriteRefs()), }, Hash::dummy, }; @@ -2323,13 +2329,14 @@ void LocalDerivationGoal::registerOutputs() return newInfo0; }, [&](DerivationOutputCAFixed dof) { + auto wanted = getContentAddressHash(dof.ca); + auto newInfo0 = newInfoFromCA(DerivationOutputCAFloating { - .method = dof.hash.method, - .hashType = dof.hash.hash.type, + .method = getContentAddressMethod(dof.ca), + .hashType = wanted.type, }); /* Check wanted hash */ - Hash & wanted = dof.hash.hash; assert(newInfo0.ca); auto got = getContentAddressHash(*newInfo0.ca); if (wanted != got) { @@ -2342,6 +2349,11 @@ void LocalDerivationGoal::registerOutputs() wanted.to_string(SRI, true), got.to_string(SRI, true))); } + if (static_cast<const PathReferences<StorePath> &>(newInfo0) != PathReferences<StorePath> {}) + delayedException = std::make_exception_ptr( + BuildError("illegal path references in fixed-output derivation '%s'", + worker.store.printStorePath(drvPath))); + return newInfo0; }, [&](DerivationOutputCAFloating dof) { diff --git a/src/libstore/content-address.cc b/src/libstore/content-address.cc index d68c60f4f..6a695fe68 100644 --- a/src/libstore/content-address.cc +++ b/src/libstore/content-address.cc @@ -10,7 +10,7 @@ std::string FixedOutputHash::printMethodAlgo() const } -std::string makeFileIngestionPrefix(const FileIngestionMethod m) +std::string makeFileIngestionPrefix(FileIngestionMethod m) { switch (m) { case FileIngestionMethod::Flat: @@ -21,6 +21,27 @@ std::string makeFileIngestionPrefix(const FileIngestionMethod m) assert(false); } +std::string makeContentAddressingPrefix(ContentAddressMethod m) { + return std::visit(overloaded { + [](TextHashMethod _) -> std::string { return "text:"; }, + [](FileIngestionMethod m2) { + /* Not prefixed for back compat with things that couldn't produce text before. */ + return makeFileIngestionPrefix(m2); + }, + }, m); +} + +ContentAddressMethod parseContentAddressingPrefix(std::string_view & m) +{ + ContentAddressMethod method = FileIngestionMethod::Flat; + if (splitPrefix(m, "r:")) + method = FileIngestionMethod::Recursive; + else if (splitPrefix(m, "text:")) + method = TextHashMethod {}; + return method; +} + + std::string makeFixedOutputCA(FileIngestionMethod method, const Hash & hash) { return "fixed:" @@ -43,14 +64,14 @@ std::string renderContentAddress(ContentAddress ca) }, ca); } -std::string renderContentAddressMethod(ContentAddressMethod cam) +std::string renderContentAddressMethodAndHash(ContentAddressMethod cam, HashType ht) { return std::visit(overloaded { - [](TextHashMethod &th) { - return std::string{"text:"} + printHashType(htSHA256); + [&](TextHashMethod & th) { + return std::string{"text:"} + printHashType(ht); }, - [](FixedOutputHashMethod &fshm) { - return "fixed:" + makeFileIngestionPrefix(fshm.fileIngestionMethod) + printHashType(fshm.hashType); + [&](FileIngestionMethod & fim) { + return "fixed:" + makeFileIngestionPrefix(fim) + printHashType(ht); } }, cam); } @@ -58,7 +79,7 @@ std::string renderContentAddressMethod(ContentAddressMethod cam) /* Parses content address strings up to the hash. */ -static ContentAddressMethod parseContentAddressMethodPrefix(std::string_view & rest) +static std::pair<ContentAddressMethod, HashType> parseContentAddressMethodPrefix(std::string_view & rest) { std::string_view wholeInput { rest }; @@ -82,19 +103,19 @@ static ContentAddressMethod parseContentAddressMethodPrefix(std::string_view & r if (prefix == "text") { // No parsing of the ingestion method, "text" only support flat. HashType hashType = parseHashType_(); - if (hashType != htSHA256) - throw Error("text content address hash should use %s, but instead uses %s", - printHashType(htSHA256), printHashType(hashType)); - return TextHashMethod {}; + return { + TextHashMethod {}, + std::move(hashType), + }; } else if (prefix == "fixed") { // Parse method auto method = FileIngestionMethod::Flat; if (splitPrefix(rest, "r:")) method = FileIngestionMethod::Recursive; HashType hashType = parseHashType_(); - return FixedOutputHashMethod { - .fileIngestionMethod = method, - .hashType = std::move(hashType), + return { + std::move(method), + std::move(hashType), }; } else throw UsageError("content address prefix '%s' is unrecognized. Recogonized prefixes are 'text' or 'fixed'", prefix); @@ -103,25 +124,25 @@ static ContentAddressMethod parseContentAddressMethodPrefix(std::string_view & r ContentAddress parseContentAddress(std::string_view rawCa) { auto rest = rawCa; - ContentAddressMethod caMethod = parseContentAddressMethodPrefix(rest); - - return std::visit( - overloaded { - [&](TextHashMethod thm) { - return ContentAddress(TextHash { - .hash = Hash::parseNonSRIUnprefixed(rest, htSHA256) - }); - }, - [&](FixedOutputHashMethod fohMethod) { - return ContentAddress(FixedOutputHash { - .method = fohMethod.fileIngestionMethod, - .hash = Hash::parseNonSRIUnprefixed(rest, std::move(fohMethod.hashType)), - }); - }, - }, caMethod); -} - -ContentAddressMethod parseContentAddressMethod(std::string_view caMethod) + auto [caMethod, hashType_] = parseContentAddressMethodPrefix(rest); + auto hashType = hashType_; // work around clang bug + + return std::visit(overloaded { + [&](TextHashMethod _) { + return ContentAddress(TextHash { + .hash = Hash::parseNonSRIUnprefixed(rest, hashType) + }); + }, + [&](FileIngestionMethod fim) { + return ContentAddress(FixedOutputHash { + .method = fim, + .hash = Hash::parseNonSRIUnprefixed(rest, hashType), + }); + }, + }, caMethod); +} + +std::pair<ContentAddressMethod, HashType> parseContentAddressMethod(std::string_view caMethod) { std::string_view asPrefix {std::string{caMethod} + ":"}; return parseContentAddressMethodPrefix(asPrefix); @@ -137,6 +158,42 @@ std::string renderContentAddress(std::optional<ContentAddress> ca) return ca ? renderContentAddress(*ca) : ""; } +ContentAddressWithReferences contentAddressFromMethodHashAndRefs( + ContentAddressMethod method, Hash && hash, PathReferences<StorePath> && refs) +{ + return std::visit(overloaded { + [&](TextHashMethod _) -> ContentAddressWithReferences { + if (refs.hasSelfReference) + throw UsageError("Cannot have a self reference with text hashing scheme"); + return TextInfo { + { .hash = std::move(hash) }, + std::move(refs.references), + }; + }, + [&](FileIngestionMethod m2) -> ContentAddressWithReferences { + return FixedOutputInfo { + { + .method = m2, + .hash = std::move(hash), + }, + std::move(refs), + }; + }, + }, method); +} + +ContentAddressMethod getContentAddressMethod(const ContentAddressWithReferences & ca) +{ + return std::visit(overloaded { + [](TextInfo th) -> ContentAddressMethod { + return TextHashMethod {}; + }, + [](FixedOutputInfo fsh) -> ContentAddressMethod { + return fsh.method; + }, + }, ca); +} + Hash getContentAddressHash(const ContentAddress & ca) { return std::visit(overloaded { @@ -160,4 +217,21 @@ ContentAddressWithReferences caWithoutRefs(const ContentAddress & ca) { }, ca); } +Hash getContentAddressHash(const ContentAddressWithReferences & ca) +{ + return std::visit(overloaded { + [](TextInfo th) { + return th.hash; + }, + [](FixedOutputInfo fsh) { + return fsh.hash; + }, + }, ca); +} + +std::string printMethodAlgo(const ContentAddressWithReferences & ca) { + return makeContentAddressingPrefix(getContentAddressMethod(ca)) + + printHashType(getContentAddressHash(ca).type); +} + } diff --git a/src/libstore/content-address.hh b/src/libstore/content-address.hh index 126244ab5..eb56fe571 100644 --- a/src/libstore/content-address.hh +++ b/src/libstore/content-address.hh @@ -10,35 +10,45 @@ namespace nix { * Content addressing method */ +/* We only have one way to hash text with references, so this is a single-value + type, mainly useful with std::variant. +*/ +struct TextHashMethod : std::monostate { }; + enum struct FileIngestionMethod : uint8_t { Flat = false, Recursive = true }; -/* - We only have one way to hash text with references, so this is single-value - type is only useful in std::variant. -*/ -struct TextHashMethod { }; - -struct FixedOutputHashMethod { - FileIngestionMethod fileIngestionMethod; - HashType hashType; -}; - /* Compute the prefix to the hash algorithm which indicates how the files were ingested. */ -std::string makeFileIngestionPrefix(const FileIngestionMethod m); +std::string makeFileIngestionPrefix(FileIngestionMethod m); +/* Just the type of a content address. Combine with the hash itself, and we + have a `ContentAddress` as defined below. Combine that, in turn, with info + on references, and we have `ContentAddressWithReferences`, as defined + further below. */ typedef std::variant< TextHashMethod, - FixedOutputHashMethod - > ContentAddressMethod; + FileIngestionMethod +> ContentAddressMethod; + +/* Parse and pretty print the algorithm which indicates how the files + were ingested, with the the fixed output case not prefixed for back + compat. */ + +std::string makeContentAddressingPrefix(ContentAddressMethod m); + +ContentAddressMethod parseContentAddressingPrefix(std::string_view & m); -ContentAddressMethod parseContentAddressMethod(std::string_view rawCaMethod); +/* Parse and pretty print a content addressing method and hash in a + nicer way, prefixing both cases. */ + +std::string renderContentAddressMethodAndHash(ContentAddressMethod cam, HashType ht); + +std::pair<ContentAddressMethod, HashType> parseContentAddressMethod(std::string_view caMethod); -std::string renderContentAddressMethod(ContentAddressMethod caMethod); /* * Mini content address @@ -81,6 +91,7 @@ std::optional<ContentAddress> parseContentAddressOpt(std::string_view rawCaOpt); Hash getContentAddressHash(const ContentAddress & ca); + /* * References set */ @@ -97,6 +108,12 @@ struct PathReferences && hasSelfReference == other.hasSelfReference; } + bool operator != (const PathReferences<Ref> & other) const + { + return references != other.references + || hasSelfReference != other.hasSelfReference; + } + /* Functions to view references + hasSelfReference as one set, mainly for compatibility's sake. */ StorePathSet referencesPossiblyToSelf(const Ref & self) const; @@ -156,6 +173,14 @@ typedef std::variant< ContentAddressWithReferences caWithoutRefs(const ContentAddress &); +ContentAddressWithReferences contentAddressFromMethodHashAndRefs( + ContentAddressMethod method, Hash && hash, PathReferences<StorePath> && refs); + +ContentAddressMethod getContentAddressMethod(const ContentAddressWithReferences & ca); +Hash getContentAddressHash(const ContentAddressWithReferences & ca); + +std::string printMethodAlgo(const ContentAddressWithReferences &); + struct StorePathDescriptor { std::string name; ContentAddressWithReferences info; diff --git a/src/libstore/daemon.cc b/src/libstore/daemon.cc index 5bc42e1f7..2b8772a8d 100644 --- a/src/libstore/daemon.cc +++ b/src/libstore/daemon.cc @@ -391,20 +391,24 @@ static void performOp(TunnelLogger * logger, ref<Store> store, logger->startWork(); auto pathInfo = [&]() { // NB: FramedSource must be out of scope before logger->stopWork(); - ContentAddressMethod contentAddressMethod = parseContentAddressMethod(camStr); + auto [contentAddressMethod, hashType_] = parseContentAddressMethod(camStr); + auto hashType = hashType_; // work around clang bug FramedSource source(from); // TODO this is essentially RemoteStore::addCAToStore. Move it up to Store. return std::visit(overloaded { - [&](TextHashMethod &_) { + [&](TextHashMethod _) { + if (hashType != htSHA256) + throw UnimplementedError("Only SHA-256 is supported for adding text-hashed data, but '%1' was given", + printHashType(hashType)); // 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) { + [&](FileIngestionMethod fim) { 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); + auto path = store->addToStoreFromDump(source, name, fim, hashType, repair); return store->queryPathInfo(path); }, }, contentAddressMethod); diff --git a/src/libstore/derivations.cc b/src/libstore/derivations.cc index 717bd56f6..b0aaa0f51 100644 --- a/src/libstore/derivations.cc +++ b/src/libstore/derivations.cc @@ -2,6 +2,7 @@ #include "store-api.hh" #include "globals.hh" #include "util.hh" +#include "split.hh" #include "worker-protocol.hh" #include "fs-accessor.hh" @@ -29,9 +30,10 @@ std::optional<StorePath> DerivationOutput::path(const Store & store, std::string StorePath DerivationOutputCAFixed::path(const Store & store, std::string_view drvName, std::string_view outputName) const { - return store.makeFixedOutputPath( - outputPathName(drvName, outputName), - { hash, {} }); + return store.makeFixedOutputPathFromCA(StorePathDescriptor { + .name = outputPathName(drvName, outputName), + .info = ca, + }); } @@ -167,23 +169,19 @@ static StringSet parseStrings(std::istream & str, bool arePaths) static DerivationOutput parseDerivationOutput(const Store & store, - std::string_view pathS, std::string_view hashAlgo, std::string_view hash) + std::string_view pathS, std::string_view hashAlgo, std::string_view hashS) { if (hashAlgo != "") { - auto method = FileIngestionMethod::Flat; - if (string(hashAlgo, 0, 2) == "r:") { - method = FileIngestionMethod::Recursive; - hashAlgo = hashAlgo.substr(2); - } + ContentAddressMethod method = parseContentAddressingPrefix(hashAlgo); const auto hashType = parseHashType(hashAlgo); - if (hash != "") { + if (hashS != "") { validatePath(pathS); + auto hash = Hash::parseNonSRIUnprefixed(hashS, hashType); return DerivationOutput { .output = DerivationOutputCAFixed { - .hash = FixedOutputHash { - .method = std::move(method), - .hash = Hash::parseNonSRIUnprefixed(hash, hashType), - }, + // FIXME non-trivial fixed refs set + .ca = contentAddressFromMethodHashAndRefs( + method, std::move(hash), {}), }, }; } else { @@ -339,12 +337,12 @@ string Derivation::unparse(const Store & store, bool maskOutputs, }, [&](DerivationOutputCAFixed dof) { s += ','; printUnquotedString(s, maskOutputs ? "" : store.printStorePath(dof.path(store, name, i.first))); - s += ','; printUnquotedString(s, dof.hash.printMethodAlgo()); - s += ','; printUnquotedString(s, dof.hash.hash.to_string(Base16, false)); + s += ','; printUnquotedString(s, printMethodAlgo(dof.ca)); + s += ','; printUnquotedString(s, getContentAddressHash(dof.ca).to_string(Base16, false)); }, [&](DerivationOutputCAFloating dof) { s += ','; printUnquotedString(s, ""); - s += ','; printUnquotedString(s, makeFileIngestionPrefix(dof.method) + printHashType(dof.hashType)); + s += ','; printUnquotedString(s, makeContentAddressingPrefix(dof.method) + printHashType(dof.hashType)); s += ','; printUnquotedString(s, ""); }, [&](DerivationOutputDeferred) { @@ -515,8 +513,8 @@ DrvHashModulo hashDerivationModulo(Store & store, const Derivation & drv, bool m for (const auto & i : drv.outputs) { auto & dof = std::get<DerivationOutputCAFixed>(i.second.output); auto hash = hashString(htSHA256, "fixed:out:" - + dof.hash.printMethodAlgo() + ":" - + dof.hash.hash.to_string(Base16, false) + ":" + + printMethodAlgo(dof.ca) + ":" + + getContentAddressHash(dof.ca).to_string(Base16, false) + ":" + store.printStorePath(dof.path(store, drv.name, i.first))); outputHashes.insert_or_assign(i.first, std::move(hash)); } @@ -673,12 +671,12 @@ void writeDerivation(Sink & out, const Store & store, const BasicDerivation & dr }, [&](DerivationOutputCAFixed dof) { out << store.printStorePath(dof.path(store, drv.name, i.first)) - << dof.hash.printMethodAlgo() - << dof.hash.hash.to_string(Base16, false); + << printMethodAlgo(dof.ca) + << getContentAddressHash(dof.ca).to_string(Base16, false); }, [&](DerivationOutputCAFloating dof) { out << "" - << (makeFileIngestionPrefix(dof.method) + printHashType(dof.hashType)) + << (makeContentAddressingPrefix(dof.method) + printHashType(dof.hashType)) << ""; }, [&](DerivationOutputDeferred) { diff --git a/src/libstore/derivations.hh b/src/libstore/derivations.hh index b1cb68194..caf6062cd 100644 --- a/src/libstore/derivations.hh +++ b/src/libstore/derivations.hh @@ -25,7 +25,7 @@ struct DerivationOutputInputAddressed according to that fixed output. */ struct DerivationOutputCAFixed { - FixedOutputHash hash; /* hash used for expected hash computation */ + ContentAddressWithReferences ca; /* hash and refs used for validating output */ StorePath path(const Store & store, std::string_view drvName, std::string_view outputName) const; }; @@ -35,7 +35,7 @@ struct DerivationOutputCAFixed struct DerivationOutputCAFloating { /* information used for expected hash computation */ - FileIngestionMethod method; + ContentAddressMethod method; HashType hashType; }; diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc index 768f72209..3d6de09d3 100644 --- a/src/libstore/local-store.cc +++ b/src/libstore/local-store.cc @@ -694,7 +694,7 @@ void LocalStore::checkDerivationOutputs(const StorePath & drvPath, const Derivat envHasRightPath(doia.path, i.first); }, [&](DerivationOutputCAFixed dof) { - StorePath path = makeFixedOutputPath(drvName, { dof.hash, {} }); + auto path = dof.path(*this, drvName, i.first); envHasRightPath(path, i.first); }, [&](DerivationOutputCAFloating _) { diff --git a/src/libstore/misc.cc b/src/libstore/misc.cc index e894b63db..44809e014 100644 --- a/src/libstore/misc.cc +++ b/src/libstore/misc.cc @@ -84,9 +84,21 @@ void Store::computeFSClosure(const StorePath & startPath, std::optional<ContentAddress> getDerivationCA(const BasicDerivation & drv) { auto out = drv.outputs.find("out"); - if (out != drv.outputs.end()) { - if (auto v = std::get_if<DerivationOutputCAFixed>(&out->second.output)) - return v->hash; + if (out == drv.outputs.end()) + return std::nullopt; + if (auto dof = std::get_if<DerivationOutputCAFixed>(&out->second.output)) { + return std::visit(overloaded { + [&](TextInfo ti) -> std::optional<ContentAddress> { + if (!ti.references.empty()) + return std::nullopt; + return static_cast<TextHash>(ti); + }, + [&](FixedOutputInfo fi) -> std::optional<ContentAddress> { + if (fi.references != PathReferences<StorePath> {}) + return std::nullopt; + return static_cast<FixedOutputHash>(fi); + }, + }, dof->ca); } return std::nullopt; } diff --git a/src/libstore/remote-store.cc b/src/libstore/remote-store.cc index 0c25bfa59..85f050c6d 100644 --- a/src/libstore/remote-store.cc +++ b/src/libstore/remote-store.cc @@ -498,6 +498,7 @@ ref<const ValidPathInfo> RemoteStore::addCAToStore( Source & dump, const string & name, ContentAddressMethod caMethod, + HashType hashType, const StorePathSet & references, RepairFlag repair) { @@ -509,7 +510,7 @@ ref<const ValidPathInfo> RemoteStore::addCAToStore( conn->to << wopAddToStore << name - << renderContentAddressMethod(caMethod); + << renderContentAddressMethodAndHash(caMethod, hashType); worker_proto::write(*this, conn->to, references); conn->to << repair; @@ -530,18 +531,21 @@ ref<const ValidPathInfo> RemoteStore::addCAToStore( std::visit(overloaded { [&](TextHashMethod thm) -> void { + if (hashType != htSHA256) + throw UnimplementedError("Only SHA-256 is supported for adding text-hashed data, but '%1' was given", + printHashType(hashType)); std::string s = dump.drain(); conn->to << wopAddTextToStore << name << s; worker_proto::write(*this, conn->to, references); conn.processStderr(); }, - [&](FixedOutputHashMethod fohm) -> void { + [&](FileIngestionMethod fim) -> void { conn->to << wopAddToStore << name - << ((fohm.hashType == htSHA256 && fohm.fileIngestionMethod == FileIngestionMethod::Recursive) ? 0 : 1) /* backwards compatibility hack */ - << (fohm.fileIngestionMethod == FileIngestionMethod::Recursive ? 1 : 0) - << printHashType(fohm.hashType); + << ((hashType == htSHA256 && fim == FileIngestionMethod::Recursive) ? 0 : 1) /* backwards compatibility hack */ + << (fim == FileIngestionMethod::Recursive ? 1 : 0) + << printHashType(hashType); try { conn->to.written = 0; @@ -549,7 +553,7 @@ ref<const ValidPathInfo> RemoteStore::addCAToStore( connections->incCapacity(); { Finally cleanup([&]() { connections->decCapacity(); }); - if (fohm.fileIngestionMethod == FileIngestionMethod::Recursive) { + if (fim == FileIngestionMethod::Recursive) { dump.drainInto(conn->to); } else { std::string contents = dump.drain(); @@ -582,7 +586,7 @@ StorePath RemoteStore::addToStoreFromDump(Source & dump, const string & name, FileIngestionMethod method, HashType hashType, RepairFlag repair) { StorePathSet references; - return addCAToStore(dump, name, FixedOutputHashMethod{ .fileIngestionMethod = method, .hashType = hashType }, references, repair)->path; + return addCAToStore(dump, name, method, hashType, references, repair)->path; } @@ -662,7 +666,7 @@ StorePath RemoteStore::addTextToStore(const string & name, const string & s, const StorePathSet & references, RepairFlag repair) { StringSource source(s); - return addCAToStore(source, name, TextHashMethod{}, references, repair)->path; + return addCAToStore(source, name, TextHashMethod {}, htSHA256, references, repair)->path; } void RemoteStore::registerDrvOutput(const Realisation & info) diff --git a/src/libstore/remote-store.hh b/src/libstore/remote-store.hh index ac1eaa19e..880be21bf 100644 --- a/src/libstore/remote-store.hh +++ b/src/libstore/remote-store.hh @@ -68,6 +68,7 @@ public: Source & dump, const string & name, ContentAddressMethod caMethod, + HashType hashType, const StorePathSet & references, RepairFlag repair); diff --git a/src/nix/show-derivation.cc b/src/nix/show-derivation.cc index 2588a011d..d13960280 100644 --- a/src/nix/show-derivation.cc +++ b/src/nix/show-derivation.cc @@ -70,11 +70,12 @@ struct CmdShowDerivation : InstallablesCommand }, [&](DerivationOutputCAFixed dof) { outputObj.attr("path", store->printStorePath(dof.path(*store, drv.name, outputName))); - outputObj.attr("hashAlgo", dof.hash.printMethodAlgo()); - outputObj.attr("hash", dof.hash.hash.to_string(Base16, false)); + outputObj.attr("hashAlgo", printMethodAlgo(dof.ca)); + outputObj.attr("hash", getContentAddressHash(dof.ca).to_string(Base16, false)); + // FIXME print refs? }, [&](DerivationOutputCAFloating dof) { - outputObj.attr("hashAlgo", makeFileIngestionPrefix(dof.method) + printHashType(dof.hashType)); + outputObj.attr("hashAlgo", makeContentAddressingPrefix(dof.method) + printHashType(dof.hashType)); }, [&](DerivationOutputDeferred) {}, }, output.output); diff --git a/tests/local.mk b/tests/local.mk index b100e7f15..067ece24d 100644 --- a/tests/local.mk +++ b/tests/local.mk @@ -44,6 +44,7 @@ nix_tests = \ recursive.sh \ describe-stores.sh \ flakes.sh \ + text-hashed-output.sh \ build.sh \ compute-levels.sh \ repl.sh \ diff --git a/tests/text-hashed-output.nix b/tests/text-hashed-output.nix new file mode 100644 index 000000000..23434c0a1 --- /dev/null +++ b/tests/text-hashed-output.nix @@ -0,0 +1,29 @@ +with import ./config.nix; + +# A simple content-addressed derivation. +# The derivation can be arbitrarily modified by passing a different `seed`, +# but the output will always be the same +rec { + root = mkDerivation { + name = "text-hashed-root"; + buildCommand = '' + set -x + echo "Building a CA derivation" + mkdir -p $out + echo "Hello World" > $out/hello + ''; + __contentAddressed = true; + outputHashMode = "recursive"; + outputHashAlgo = "sha256"; + }; + dependent = mkDerivation { + name = "text-hashed-root.drv"; + buildCommand = '' + echo "Copying the derivation" + cp ${root.drvPath} $out + ''; + __contentAddressed = true; + outputHashMode = "text"; + outputHashAlgo = "sha256"; + }; +} diff --git a/tests/text-hashed-output.sh b/tests/text-hashed-output.sh new file mode 100644 index 000000000..2ee3d6590 --- /dev/null +++ b/tests/text-hashed-output.sh @@ -0,0 +1,26 @@ +#!/usr/bin/env bash + +source common.sh + +# In the corresponding nix file, we have two derivations: the first, named root, +# is a normal recursive derivation, while the second, named dependent, has the +# new outputHashMode "text". Note that in "dependent", we don't refer to the +# build output of root, but only to the path of the drv file. For this reason, +# we only need to: +# +# - instantiate the root derivation +# - build the dependent derivation +# - check that the path of the output coincides with that of the original derivation + +drv=$(nix-instantiate --experimental-features ca-derivations ./text-hashed-output.nix -A root) +nix --experimental-features 'nix-command ca-derivations' show-derivation --derivation "$drv" + +drvDep=$(nix-instantiate --experimental-features ca-derivations ./text-hashed-output.nix -A dependent) +nix --experimental-features 'nix-command ca-derivations' show-derivation --derivation "$drvDep" + +out1=$(nix-build --experimental-features ca-derivations ./text-hashed-output.nix -A dependent --no-out-link) + +nix --experimental-features 'nix-command ca-derivations' path-info $drv --derivation --json | jq +nix --experimental-features 'nix-command ca-derivations' path-info $out1 --derivation --json | jq + +test $out1 == $drv |