aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/libexpr/primops.cc40
-rw-r--r--src/libstore/build/local-derivation-goal.cc52
-rw-r--r--src/libstore/content-address.cc140
-rw-r--r--src/libstore/content-address.hh57
-rw-r--r--src/libstore/daemon.cc12
-rw-r--r--src/libstore/derivations.cc42
-rw-r--r--src/libstore/derivations.hh4
-rw-r--r--src/libstore/local-store.cc2
-rw-r--r--src/libstore/misc.cc18
-rw-r--r--src/libstore/remote-store.cc20
-rw-r--r--src/libstore/remote-store.hh1
-rw-r--r--src/nix/show-derivation.cc7
-rw-r--r--tests/local.mk1
-rw-r--r--tests/text-hashed-output.nix29
-rw-r--r--tests/text-hashed-output.sh26
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