diff --git a/doc/manual/installation/installing-binary.xml b/doc/manual/installation/installing-binary.xml
index d25c46b85..64c7a37fb 100644
--- a/doc/manual/installation/installing-binary.xml
+++ b/doc/manual/installation/installing-binary.xml
@@ -97,7 +97,7 @@ $ rm -rf /nix
installation on your system:
- sh <(curl https://nixos.org/nix/install) --daemon
+ sh <(curl -L https://nixos.org/nix/install) --daemon
The multi-user installation of Nix will create build users between
@@ -178,7 +178,7 @@ sudo rm /Library/LaunchDaemons/org.nixos.nix-daemon.plist
is a bit of a misnomer). To use this approach, just install Nix with:
- $ sh <(curl https://nixos.org/nix/install) --darwin-use-unencrypted-nix-store-volume
+ $ sh <(curl -L https://nixos.org/nix/install) --darwin-use-unencrypted-nix-store-volume
If you don't like the sound of this, you'll want to weigh the
@@ -429,7 +429,7 @@ LABEL=Nix\040Store /nix apfs rw,nobrowse
NixOS.org installation script:
- sh <(curl https://nixos.org/nix/install)
+ sh <(curl -L https://nixos.org/nix/install)
diff --git a/scripts/install-multi-user.sh b/scripts/install-multi-user.sh
index 157e8ddb4..00c9d540b 100644
--- a/scripts/install-multi-user.sh
+++ b/scripts/install-multi-user.sh
@@ -526,7 +526,7 @@ This script is going to call sudo a lot. Normally, it would show you
exactly what commands it is running and why. However, the script is
run in a headless fashion, like this:
- $ curl https://nixos.org/nix/install | sh
+ $ curl -L https://nixos.org/nix/install | sh
or maybe in a CI pipeline. Because of that, we're going to skip the
verbose output in the interest of brevity.
@@ -534,7 +534,7 @@ verbose output in the interest of brevity.
If you would like to
see the output, try like this:
- $ curl -o install-nix https://nixos.org/nix/install
+ $ curl -L -o install-nix https://nixos.org/nix/install
$ sh ./install-nix
EOF
diff --git a/scripts/install-nix-from-closure.sh b/scripts/install-nix-from-closure.sh
index 826ca8b8c..5824c2217 100644
--- a/scripts/install-nix-from-closure.sh
+++ b/scripts/install-nix-from-closure.sh
@@ -113,7 +113,7 @@ if [ "$(uname -s)" = "Darwin" ]; then
(
echo ""
echo "Installing on macOS >=10.15 requires relocating the store to an apfs volume."
- echo "Use sh <(curl https://nixos.org/nix/install) --darwin-use-unencrypted-nix-store-volume or run the preparation steps manually."
+ echo "Use sh <(curl -L https://nixos.org/nix/install) --darwin-use-unencrypted-nix-store-volume or run the preparation steps manually."
echo "See https://nixos.org/nix/manual/#sect-macos-installation"
echo ""
) >&2
diff --git a/src/libexpr/get-drvs.cc b/src/libexpr/get-drvs.cc
index a4937e722..9055f59a1 100644
--- a/src/libexpr/get-drvs.cc
+++ b/src/libexpr/get-drvs.cc
@@ -1,7 +1,7 @@
#include "get-drvs.hh"
#include "util.hh"
#include "eval-inline.hh"
-#include "derivations.hh"
+#include "store-api.hh"
#include
#include
diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc
index f5fbd3fa6..3830d8107 100644
--- a/src/libexpr/primops.cc
+++ b/src/libexpr/primops.cc
@@ -776,7 +776,7 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
if (!jsonObject) drv.env["out"] = state.store->printStorePath(outPath);
drv.outputs.insert_or_assign("out", DerivationOutput {
.path = std::move(outPath),
- .hash = DerivationOutputHash {
+ .hash = FixedOutputHash {
.method = ingestionMethod,
.hash = std::move(h),
},
@@ -795,7 +795,7 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
drv.outputs.insert_or_assign(i,
DerivationOutput {
.path = StorePath::dummy,
- .hash = std::optional {},
+ .hash = std::optional {},
});
}
@@ -807,7 +807,7 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
drv.outputs.insert_or_assign(i,
DerivationOutput {
.path = std::move(outPath),
- .hash = std::optional(),
+ .hash = std::optional(),
});
}
}
diff --git a/src/libexpr/primops/context.cc b/src/libexpr/primops/context.cc
index efa2e9576..301e8c5dd 100644
--- a/src/libexpr/primops/context.cc
+++ b/src/libexpr/primops/context.cc
@@ -1,6 +1,6 @@
#include "primops.hh"
#include "eval-inline.hh"
-#include "derivations.hh"
+#include "store-api.hh"
namespace nix {
diff --git a/src/libfetchers/tarball.cc b/src/libfetchers/tarball.cc
index ac83d52b9..f5356f0af 100644
--- a/src/libfetchers/tarball.cc
+++ b/src/libfetchers/tarball.cc
@@ -70,7 +70,10 @@ DownloadFileResult downloadFile(
ValidPathInfo info(store->makeFixedOutputPath(FileIngestionMethod::Flat, hash, name));
info.narHash = hashString(htSHA256, *sink.s);
info.narSize = sink.s->size();
- info.ca = makeFixedOutputCA(FileIngestionMethod::Flat, hash);
+ info.ca = FixedOutputHash {
+ .method = FileIngestionMethod::Flat,
+ .hash = hash,
+ };
auto source = StringSource { *sink.s };
store->addToStore(info, source, NoRepair, NoCheckSigs);
storePath = std::move(info.path);
diff --git a/src/libstore/build.cc b/src/libstore/build.cc
index ab729fc68..80992a7cd 100644
--- a/src/libstore/build.cc
+++ b/src/libstore/build.cc
@@ -3714,7 +3714,7 @@ void DerivationGoal::registerOutputs()
/* Check that fixed-output derivations produced the right
outputs (i.e., the content hash should match the specified
hash). */
- std::string ca;
+ std::optional ca;
if (derivationIsFixed(derivationType)) {
@@ -3764,7 +3764,10 @@ void DerivationGoal::registerOutputs()
else
assert(worker.store.parseStorePath(path) == dest);
- ca = makeFixedOutputCA(i.second.hash->method, h2);
+ ca = FixedOutputHash {
+ .method = i.second.hash->method,
+ .hash = h2,
+ };
}
/* Get rid of all weird permissions. This also checks that
@@ -3837,7 +3840,10 @@ void DerivationGoal::registerOutputs()
info.ca = ca;
worker.store.signPathInfo(info);
- if (!info.references.empty()) info.ca.clear();
+ if (!info.references.empty()) {
+ // FIXME don't we have an experimental feature for fixed output with references?
+ info.ca = {};
+ }
infos.emplace(i.first, std::move(info));
}
diff --git a/src/libstore/content-address.cc b/src/libstore/content-address.cc
new file mode 100644
index 000000000..3d753836f
--- /dev/null
+++ b/src/libstore/content-address.cc
@@ -0,0 +1,85 @@
+#include "content-address.hh"
+
+namespace nix {
+
+std::string FixedOutputHash::printMethodAlgo() const {
+ return makeFileIngestionPrefix(method) + printHashType(*hash.type);
+}
+
+std::string makeFileIngestionPrefix(const FileIngestionMethod m) {
+ switch (m) {
+ case FileIngestionMethod::Flat:
+ return "";
+ case FileIngestionMethod::Recursive:
+ return "r:";
+ default:
+ throw Error("impossible, caught both cases");
+ }
+}
+
+std::string makeFixedOutputCA(FileIngestionMethod method, const Hash & hash)
+{
+ return "fixed:"
+ + makeFileIngestionPrefix(method)
+ + hash.to_string(Base32, true);
+}
+
+// FIXME Put this somewhere?
+template struct overloaded : Ts... { using Ts::operator()...; };
+template overloaded(Ts...) -> overloaded;
+
+std::string renderContentAddress(ContentAddress ca) {
+ return std::visit(overloaded {
+ [](TextHash th) {
+ return "text:" + th.hash.to_string(Base32, true);
+ },
+ [](FixedOutputHash fsh) {
+ return makeFixedOutputCA(fsh.method, fsh.hash);
+ }
+ }, ca);
+}
+
+ContentAddress parseContentAddress(std::string_view rawCa) {
+ auto prefixSeparator = rawCa.find(':');
+ if (prefixSeparator != string::npos) {
+ auto prefix = string(rawCa, 0, prefixSeparator);
+ if (prefix == "text") {
+ auto hashTypeAndHash = rawCa.substr(prefixSeparator+1, string::npos);
+ Hash hash = Hash(string(hashTypeAndHash));
+ if (*hash.type != htSHA256) {
+ throw Error("parseContentAddress: the text hash should have type SHA256");
+ }
+ return TextHash { hash };
+ } else if (prefix == "fixed") {
+ // This has to be an inverse of makeFixedOutputCA
+ auto methodAndHash = rawCa.substr(prefixSeparator+1, string::npos);
+ if (methodAndHash.substr(0,2) == "r:") {
+ std::string_view hashRaw = methodAndHash.substr(2,string::npos);
+ return FixedOutputHash {
+ .method = FileIngestionMethod::Recursive,
+ .hash = Hash(string(hashRaw)),
+ };
+ } else {
+ std::string_view hashRaw = methodAndHash;
+ return FixedOutputHash {
+ .method = FileIngestionMethod::Flat,
+ .hash = Hash(string(hashRaw)),
+ };
+ }
+ } else {
+ throw Error("parseContentAddress: format not recognized; has to be text or fixed");
+ }
+ } else {
+ throw Error("Not a content address because it lacks an appropriate prefix");
+ }
+};
+
+std::optional parseContentAddressOpt(std::string_view rawCaOpt) {
+ return rawCaOpt == "" ? std::optional {} : parseContentAddress(rawCaOpt);
+};
+
+std::string renderContentAddress(std::optional ca) {
+ return ca ? renderContentAddress(*ca) : "";
+}
+
+}
diff --git a/src/libstore/content-address.hh b/src/libstore/content-address.hh
new file mode 100644
index 000000000..ba4797f5b
--- /dev/null
+++ b/src/libstore/content-address.hh
@@ -0,0 +1,56 @@
+#pragma once
+
+#include
+#include "hash.hh"
+
+namespace nix {
+
+enum struct FileIngestionMethod : uint8_t {
+ Flat = false,
+ Recursive = true
+};
+
+struct TextHash {
+ Hash hash;
+};
+
+/// Pair of a hash, and how the file system was ingested
+struct FixedOutputHash {
+ FileIngestionMethod method;
+ Hash hash;
+ std::string printMethodAlgo() const;
+};
+
+/*
+ We've accumulated several types of content-addressed paths over the years;
+ fixed-output derivations support multiple hash algorithms and serialisation
+ methods (flat file vs NAR). Thus, ‘ca’ has one of the following forms:
+
+ * ‘text:sha256:’: For paths
+ computed by makeTextPath() / addTextToStore().
+
+ * ‘fixed:::’: For paths computed by
+ makeFixedOutputPath() / addToStore().
+*/
+typedef std::variant<
+ TextHash, // for paths computed by makeTextPath() / addTextToStore
+ FixedOutputHash // for path computed by makeFixedOutputPath
+> ContentAddress;
+
+/* Compute the prefix to the hash algorithm which indicates how the files were
+ ingested. */
+std::string makeFileIngestionPrefix(const FileIngestionMethod m);
+
+/* Compute the content-addressability assertion (ValidPathInfo::ca)
+ for paths created by makeFixedOutputPath() / addToStore(). */
+std::string makeFixedOutputCA(FileIngestionMethod method, const Hash & hash);
+
+std::string renderContentAddress(ContentAddress ca);
+
+std::string renderContentAddress(std::optional ca);
+
+ContentAddress parseContentAddress(std::string_view rawCa);
+
+std::optional parseContentAddressOpt(std::string_view rawCaOpt);
+
+}
diff --git a/src/libstore/daemon.cc b/src/libstore/daemon.cc
index e370e278c..842aef20c 100644
--- a/src/libstore/daemon.cc
+++ b/src/libstore/daemon.cc
@@ -652,7 +652,7 @@ static void performOp(TunnelLogger * logger, ref store,
if (GET_PROTOCOL_MINOR(clientVersion) >= 16) {
to << info->ultimate
<< info->sigs
- << info->ca;
+ << renderContentAddress(info->ca);
}
} else {
assert(GET_PROTOCOL_MINOR(clientVersion) >= 17);
@@ -710,7 +710,8 @@ static void performOp(TunnelLogger * logger, ref store,
info.references = readStorePaths(*store, from);
from >> info.registrationTime >> info.narSize >> info.ultimate;
info.sigs = readStrings(from);
- from >> info.ca >> repair >> dontCheckSigs;
+ info.ca = parseContentAddressOpt(readString(from));
+ from >> repair >> dontCheckSigs;
if (!trusted && dontCheckSigs)
dontCheckSigs = false;
if (!trusted)
diff --git a/src/libstore/derivations.cc b/src/libstore/derivations.cc
index cec2ba792..ce2b66fcf 100644
--- a/src/libstore/derivations.cc
+++ b/src/libstore/derivations.cc
@@ -8,11 +8,6 @@
namespace nix {
-std::string DerivationOutputHash::printMethodAlgo() const {
- return makeFileIngestionPrefix(method) + printHashType(*hash.type);
-}
-
-
bool derivationIsCA(DerivationType dt) {
switch (dt) {
case DerivationType::Regular: return false;
@@ -139,7 +134,7 @@ static DerivationOutput parseDerivationOutput(const Store & store, istringstream
expect(str, ","); const auto hash = parseString(str);
expect(str, ")");
- std::optional fsh;
+ std::optional fsh;
if (hashAlgo != "") {
auto method = FileIngestionMethod::Flat;
if (string(hashAlgo, 0, 2) == "r:") {
@@ -147,7 +142,7 @@ static DerivationOutput parseDerivationOutput(const Store & store, istringstream
hashAlgo = string(hashAlgo, 2);
}
const HashType hashType = parseHashType(hashAlgo);
- fsh = DerivationOutputHash {
+ fsh = FixedOutputHash {
.method = std::move(method),
.hash = Hash(hash, hashType),
};
@@ -446,7 +441,7 @@ static DerivationOutput readDerivationOutput(Source & in, const Store & store)
auto hashAlgo = readString(in);
const auto hash = readString(in);
- std::optional fsh;
+ std::optional fsh;
if (hashAlgo != "") {
auto method = FileIngestionMethod::Flat;
if (string(hashAlgo, 0, 2) == "r:") {
@@ -454,7 +449,7 @@ static DerivationOutput readDerivationOutput(Source & in, const Store & store)
hashAlgo = string(hashAlgo, 2);
}
const HashType hashType = parseHashType(hashAlgo);
- fsh = DerivationOutputHash {
+ fsh = FixedOutputHash {
.method = std::move(method),
.hash = Hash(hash, hashType),
};
diff --git a/src/libstore/derivations.hh b/src/libstore/derivations.hh
index 1dcfc035d..df31e6a93 100644
--- a/src/libstore/derivations.hh
+++ b/src/libstore/derivations.hh
@@ -1,8 +1,9 @@
#pragma once
+#include "path.hh"
#include "types.hh"
#include "hash.hh"
-#include "store-api.hh"
+#include "content-address.hh"
#include