diff options
51 files changed, 555 insertions, 463 deletions
diff --git a/corepkgs/fetchurl.nix b/corepkgs/fetchurl.nix index 62359433971d..e135b947fdbb 100644 --- a/corepkgs/fetchurl.nix +++ b/corepkgs/fetchurl.nix @@ -1,10 +1,10 @@ { system ? builtins.currentSystem , url -, md5 ? "", sha1 ? "", sha256 ? "" +, md5 ? "", sha1 ? "", sha256 ? "", sha512 ? "" , outputHash ? - if sha1 != "" then sha1 else if md5 != "" then md5 else sha256 + if sha512 != "" then sha512 else if sha1 != "" then sha1 else if md5 != "" then md5 else sha256 , outputHashAlgo ? - if sha1 != "" then "sha1" else if md5 != "" then "md5" else "sha256" + if sha512 != "" then "sha512" else if sha1 != "" then "sha1" else if md5 != "" then "md5" else "sha256" , executable ? false , unpack ? false , name ? baseNameOf (toString url) diff --git a/doc/manual/command-ref/conf-file.xml b/doc/manual/command-ref/conf-file.xml index 616983bc7f0e..cde32b35f5b4 100644 --- a/doc/manual/command-ref/conf-file.xml +++ b/doc/manual/command-ref/conf-file.xml @@ -643,6 +643,23 @@ password <replaceable>my-password</replaceable> </varlistentry> + <varlistentry xml:id="conf-allow-new-privileges"><term><literal>allow-new-privileges</literal></term> + + <listitem><para>(Linux-specific.) By default, builders on Linux + cannot acquire new privileges by calling setuid/setgid programs or + programs that have file capabilities. For example, programs such + as <command>sudo</command> or <command>ping</command> will + fail. (Note that in sandbox builds, no such programs are available + unless you bind-mount them into the sandbox via the + <option>build-sandbox-paths</option> option.) You can allow the + use of such programs by enabling this option. This is impure and + usually undesirable, but may be useful in certain scenarios + (e.g. to spin up containers or set up userspace network interfaces + in tests).</para></listitem> + + </varlistentry> + + </variablelist> </para> diff --git a/doc/manual/command-ref/nix-copy-closure.xml b/doc/manual/command-ref/nix-copy-closure.xml index 97e261ae993d..800e1db6ab0f 100644 --- a/doc/manual/command-ref/nix-copy-closure.xml +++ b/doc/manual/command-ref/nix-copy-closure.xml @@ -27,8 +27,10 @@ <arg><option>- -show-progress</option></arg> --> <arg><option>--include-outputs</option></arg> - <arg><option>--use-substitutes</option></arg> - <arg><option>-s</option></arg> + <group> + <arg choice='plain'><option>--use-substitutes</option></arg> + <arg choice='plain'><option>-s</option></arg> + </group> <arg><option>-v</option></arg> <arg choice='plain'> <replaceable>user@</replaceable><replaceable>machine</replaceable> diff --git a/doc/manual/command-ref/nix-env.xml b/doc/manual/command-ref/nix-env.xml index 8462cf8a0270..d4563ac47551 100644 --- a/doc/manual/command-ref/nix-env.xml +++ b/doc/manual/command-ref/nix-env.xml @@ -146,8 +146,7 @@ also <xref linkend="sec-common-options" />.</phrase></para> <variablelist> - <varlistentry><term><option>--file</option></term> - <term><option>-f</option></term> + <varlistentry><term><option>--file</option> / <option>-f</option> <replaceable>path</replaceable></term> <listitem><para>Specifies the Nix expression (designated below as the <emphasis>active Nix expression</emphasis>) used by the @@ -166,8 +165,7 @@ also <xref linkend="sec-common-options" />.</phrase></para> </varlistentry> - <varlistentry><term><option>--profile</option></term> - <term><option>-p</option></term> + <varlistentry><term><option>--profile</option> / <option>-p</option> <replaceable>path</replaceable></term> <listitem><para>Specifies the profile to be used by those operations that operate on a profile (designated below as the diff --git a/doc/manual/command-ref/nix-instantiate.xml b/doc/manual/command-ref/nix-instantiate.xml index 3d03358bea3f..39c1282fcc36 100644 --- a/doc/manual/command-ref/nix-instantiate.xml +++ b/doc/manual/command-ref/nix-instantiate.xml @@ -24,6 +24,7 @@ <arg choice='plain'> <option>--eval</option> <arg><option>--strict</option></arg> + <arg><option>--json</option></arg> <arg><option>--xml</option></arg> </arg> </group> @@ -38,12 +39,13 @@ </arg> <arg><option>--add-root</option> <replaceable>path</replaceable></arg> <arg><option>--indirect</option></arg> - <group choice='req'> + <group> <arg choice='plain'><option>--expr</option></arg> <arg choice='plain'><option>-E</option></arg> </group> <arg choice='plain' rep='repeat'><replaceable>files</replaceable></arg> - <sbr/> + </cmdsynopsis> + <cmdsynopsis> <command>nix-instantiate</command> <arg choice='plain'><option>--find-file</option></arg> <arg choice='plain' rep='repeat'><replaceable>files</replaceable></arg> @@ -115,13 +117,17 @@ input.</para> </varlistentry> - <varlistentry><term><option>--xml</option></term> + <varlistentry><term><option>--strict</option></term> - <listitem><para>When used with <option>--eval</option>, print the resulting - value as an XML representation of the abstract syntax tree rather than as - an ATerm. The schema is the same as that used by the <link - linkend="builtin-toXML"><function>toXML</function> built-in</link>. - </para></listitem> + <listitem><para>When used with <option>--eval</option>, + recursively evaluate list elements and attributes. Normally, such + sub-expressions are left unevaluated (since the Nix expression + language is lazy).</para> + + <warning><para>This option can cause non-termination, because lazy + data structures can be infinitely large.</para></warning> + + </listitem> </varlistentry> @@ -133,17 +139,13 @@ input.</para> </varlistentry> - <varlistentry><term><option>--strict</option></term> - - <listitem><para>When used with <option>--eval</option>, - recursively evaluate list elements and attributes. Normally, such - sub-expressions are left unevaluated (since the Nix expression - language is lazy).</para> - - <warning><para>This option can cause non-termination, because lazy - data structures can be infinitely large.</para></warning> + <varlistentry><term><option>--xml</option></term> - </listitem> + <listitem><para>When used with <option>--eval</option>, print the resulting + value as an XML representation of the abstract syntax tree rather than as + an ATerm. The schema is the same as that used by the <link + linkend="builtin-toXML"><function>toXML</function> built-in</link>. + </para></listitem> </varlistentry> diff --git a/doc/manual/command-ref/nix-prefetch-url.xml b/doc/manual/command-ref/nix-prefetch-url.xml index 016d8863a94c..8ef748c74be3 100644 --- a/doc/manual/command-ref/nix-prefetch-url.xml +++ b/doc/manual/command-ref/nix-prefetch-url.xml @@ -19,14 +19,16 @@ <refsynopsisdiv> <cmdsynopsis> <command>nix-prefetch-url</command> + <arg><option>--version</option></arg> <arg><option>--type</option> <replaceable>hashAlgo</replaceable></arg> <arg><option>--print-path</option></arg> + <arg><option>--unpack</option></arg> + <arg><option>--name</option> <replaceable>name</replaceable></arg> <arg choice='plain'><replaceable>url</replaceable></arg> <arg><replaceable>hash</replaceable></arg> </cmdsynopsis> </refsynopsisdiv> - <refsection><title>Description</title> <para>The command <command>nix-prefetch-url</command> downloads the @@ -91,7 +93,7 @@ downloaded file in the Nix store is also printed.</para> </varlistentry> - <varlistentry><term><option>--name</option></term> + <varlistentry><term><option>--name</option> <replaceable>name</replaceable></term> <listitem><para>Override the name of the file in the Nix store. By default, this is diff --git a/doc/manual/command-ref/nix-shell.xml b/doc/manual/command-ref/nix-shell.xml index c64c93ec3ac2..62d026ac238e 100644 --- a/doc/manual/command-ref/nix-shell.xml +++ b/doc/manual/command-ref/nix-shell.xml @@ -33,13 +33,13 @@ <arg><option>--exclude</option> <replaceable>regexp</replaceable></arg> <arg><option>--pure</option></arg> <group choice='req'> - <group choice='plain'> - <group> + <arg choice='plain'> + <group choice='req'> <arg choice='plain'><option>--packages</option></arg> <arg choice='plain'><option>-p</option></arg> </group> - <replaceable>packages</replaceable> - </group> + <arg choice='plain' rep='repeat'><replaceable>packages</replaceable></arg> + </arg> <arg><replaceable>path</replaceable></arg> </group> </cmdsynopsis> @@ -144,7 +144,7 @@ also <xref linkend="sec-common-options" />.</phrase></para> </varlistentry> - <varlistentry><term><option>--packages</option> / <option>-p</option></term> + <varlistentry><term><option>--packages</option> / <option>-p</option> <replaceable>packages</replaceable>…</term> <listitem><para>Set up an environment in which the specified packages are present. The command line arguments are interpreted diff --git a/doc/manual/command-ref/nix-store.xml b/doc/manual/command-ref/nix-store.xml index fb017b741da9..19c99841a0ac 100644 --- a/doc/manual/command-ref/nix-store.xml +++ b/doc/manual/command-ref/nix-store.xml @@ -234,7 +234,7 @@ linkend="sec-nix-build"><command>nix-build</command></link> does.</para> <para>To test whether a previously-built derivation is deterministic: <screen> -$ nix-build -r '<nixpkgs>' -A hello --check -K +$ nix-build '<nixpkgs>' -A hello --check -K </screen> </para> diff --git a/doc/manual/command-ref/opt-common-syn.xml b/doc/manual/command-ref/opt-common-syn.xml index 5b7936393951..3aff4e1b6357 100644 --- a/doc/manual/command-ref/opt-common-syn.xml +++ b/doc/manual/command-ref/opt-common-syn.xml @@ -2,10 +2,18 @@ <arg><option>--help</option></arg> <arg><option>--version</option></arg> -<arg rep='repeat'><option>--verbose</option></arg> -<arg rep='repeat'><option>-v</option></arg> -<arg><option>--no-build-output</option></arg> -<arg><option>-Q</option></arg> +<arg rep='repeat'> + <group choice='req'> + <arg choice='plain'><option>--verbose</option></arg> + <arg choice='plain'><option>-v</option></arg> + </group> +</arg> +<arg> + <group choice='plain'> + <arg choice='plain'><option>--no-build-output</option></arg> + <arg choice='plain'><option>-Q</option></arg> + </group> +</arg> <arg> <group choice='req'> <arg choice='plain'><option>--max-jobs</option></arg> @@ -25,10 +33,18 @@ <option>--timeout</option> <replaceable>number</replaceable> </arg> -<arg><option>--keep-going</option></arg> -<arg><option>-k</option></arg> -<arg><option>--keep-failed</option></arg> -<arg><option>-K</option></arg> +<arg> + <group choice='plain'> + <arg choice='plain'><option>--keep-going</option></arg> + <arg choice='plain'><option>-k</option></arg> + </group> +</arg> +<arg> + <group choice='plain'> + <arg choice='plain'><option>--keep-failed</option></arg> + <arg choice='plain'><option>-K</option></arg> + </group> +</arg> <arg><option>--fallback</option></arg> <arg><option>--readonly-mode</option></arg> <arg><option>--show-trace</option></arg> diff --git a/doc/manual/command-ref/opt-common.xml b/doc/manual/command-ref/opt-common.xml index 2aa41c4d4389..a930b4a0d781 100644 --- a/doc/manual/command-ref/opt-common.xml +++ b/doc/manual/command-ref/opt-common.xml @@ -22,8 +22,7 @@ </varlistentry> -<varlistentry><term><option>--verbose</option></term> - <term><option>-v</option></term> +<varlistentry><term><option>--verbose</option> / <option>-v</option></term> <listitem> @@ -76,8 +75,7 @@ </varlistentry> -<varlistentry><term><option>--no-build-output</option></term> - <term><option>-Q</option></term> +<varlistentry><term><option>--no-build-output</option> / <option>-Q</option></term> <listitem><para>By default, output written by builders to standard output and standard error is echoed to the Nix command's standard @@ -89,8 +87,8 @@ </varlistentry> -<varlistentry xml:id="opt-max-jobs"><term><option>--max-jobs</option></term> - <term><option>-j</option></term> +<varlistentry xml:id="opt-max-jobs"><term><option>--max-jobs</option> / <option>-j</option> +<replaceable>number</replaceable></term> <listitem><para>Sets the maximum number of build jobs that Nix will perform in parallel to the specified number. Specify @@ -143,8 +141,7 @@ </varlistentry> -<varlistentry><term><option>--keep-going</option></term> - <term><option>-k</option></term> +<varlistentry><term><option>--keep-going</option> / <option>-k</option></term> <listitem><para>Keep going in case of failed builds, to the greatest extent possible. That is, if building an input of some @@ -156,8 +153,7 @@ </varlistentry> -<varlistentry><term><option>--keep-failed</option></term> - <term><option>-K</option></term> +<varlistentry><term><option>--keep-failed</option> / <option>-K</option></term> <listitem><para>Specifies that in case of a build failure, the temporary directory (usually in <filename>/tmp</filename>) in which diff --git a/doc/manual/glossary/glossary.xml b/doc/manual/glossary/glossary.xml index d74940c90b30..e0636044cc25 100644 --- a/doc/manual/glossary/glossary.xml +++ b/doc/manual/glossary/glossary.xml @@ -54,7 +54,7 @@ <glossdef><para>A substitute is a command invocation stored in the Nix database that describes how to build a store object, bypassing - normal the build mechanism (i.e., derivations). Typically, the + the normal build mechanism (i.e., derivations). Typically, the substitute builds the store object by downloading a pre-built version of the store object from some server.</para></glossdef> diff --git a/perl/lib/Nix/Store.xs b/perl/lib/Nix/Store.xs index aa14bfa6270a..bbfb2934315b 100644 --- a/perl/lib/Nix/Store.xs +++ b/perl/lib/Nix/Store.xs @@ -81,8 +81,7 @@ SV * queryReferences(char * path) SV * queryPathHash(char * path) PPCODE: try { - auto hash = store()->queryPathInfo(path)->narHash; - string s = "sha256:" + printHash32(hash); + auto s = store()->queryPathInfo(path)->narHash.to_string(); XPUSHs(sv_2mortal(newSVpv(s.c_str(), 0))); } catch (Error & e) { croak("%s", e.what()); @@ -108,7 +107,7 @@ SV * queryPathInfo(char * path, int base32) XPUSHs(&PL_sv_undef); else XPUSHs(sv_2mortal(newSVpv(info->deriver.c_str(), 0))); - string s = "sha256:" + (base32 ? printHash32(info->narHash) : printHash(info->narHash)); + auto s = info->narHash.to_string(base32 ? Base32 : Base16); XPUSHs(sv_2mortal(newSVpv(s.c_str(), 0))); mXPUSHi(info->registrationTime); mXPUSHi(info->narSize); @@ -184,7 +183,7 @@ void importPaths(int fd, int dontCheckSigs) PPCODE: try { FdSource source(fd); - store()->importPaths(source, 0, dontCheckSigs); + store()->importPaths(source, nullptr, dontCheckSigs ? NoCheckSigs : CheckSigs); } catch (Error & e) { croak("%s", e.what()); } @@ -194,7 +193,7 @@ SV * hashPath(char * algo, int base32, char * path) PPCODE: try { Hash h = hashPath(parseHashType(algo), path).first; - string s = base32 ? printHash32(h) : printHash(h); + auto s = h.to_string(base32 ? Base32 : Base16, false); XPUSHs(sv_2mortal(newSVpv(s.c_str(), 0))); } catch (Error & e) { croak("%s", e.what()); @@ -205,7 +204,7 @@ SV * hashFile(char * algo, int base32, char * path) PPCODE: try { Hash h = hashFile(parseHashType(algo), path); - string s = base32 ? printHash32(h) : printHash(h); + auto s = h.to_string(base32 ? Base32 : Base16, false); XPUSHs(sv_2mortal(newSVpv(s.c_str(), 0))); } catch (Error & e) { croak("%s", e.what()); @@ -216,7 +215,7 @@ SV * hashString(char * algo, int base32, char * s) PPCODE: try { Hash h = hashString(parseHashType(algo), s); - string s = base32 ? printHash32(h) : printHash(h); + auto s = h.to_string(base32 ? Base32 : Base16, false); XPUSHs(sv_2mortal(newSVpv(s.c_str(), 0))); } catch (Error & e) { croak("%s", e.what()); @@ -226,8 +225,8 @@ SV * hashString(char * algo, int base32, char * s) SV * convertHash(char * algo, char * s, int toBase32) PPCODE: try { - Hash h = parseHash16or32(parseHashType(algo), s); - string s = toBase32 ? printHash32(h) : printHash(h); + Hash h(s, parseHashType(algo)); + string s = h.to_string(toBase32 ? Base32 : Base16, false); XPUSHs(sv_2mortal(newSVpv(s.c_str(), 0))); } catch (Error & e) { croak("%s", e.what()); @@ -286,8 +285,7 @@ SV * addToStore(char * srcPath, int recursive, char * algo) SV * makeFixedOutputPath(int recursive, char * algo, char * hash, char * name) PPCODE: try { - HashType ht = parseHashType(algo); - Hash h = parseHash16or32(ht, hash); + Hash h(hash, parseHashType(algo)); Path path = store()->makeFixedOutputPath(recursive, h, name); XPUSHs(sv_2mortal(newSVpv(path.c_str(), 0))); } catch (Error & e) { diff --git a/src/build-remote/build-remote.cc b/src/build-remote/build-remote.cc index 7ffbdca7c0f4..8719959f0b69 100644 --- a/src/build-remote/build-remote.cc +++ b/src/build-remote/build-remote.cc @@ -201,7 +201,7 @@ connected: printError("somebody is hogging the upload lock for ‘%s’, continuing..."); alarm(0); signal(SIGALRM, old); - copyPaths(store, ref<Store>(sshStore), inputs, false, true); + copyPaths(store, ref<Store>(sshStore), inputs, NoRepair, NoCheckSigs); uploadLock = -1; BasicDerivation drv(readDerivation(drvPath)); @@ -219,7 +219,7 @@ connected: if (!missing.empty()) { setenv("NIX_HELD_LOCKS", concatStringsSep(" ", missing).c_str(), 1); /* FIXME: ugly */ - copyPaths(ref<Store>(sshStore), store, missing, false, true); + copyPaths(ref<Store>(sshStore), store, missing, NoRepair, NoCheckSigs); } return; diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 0cdce602d7b2..ca4c9a373a3a 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -293,6 +293,7 @@ EvalState::EvalState(const Strings & _searchPath, ref<Store> store) , sWrong(symbols.create("wrong")) , sStructuredAttrs(symbols.create("__structuredAttrs")) , sBuilder(symbols.create("builder")) + , repair(NoRepair) , store(store) , baseEnv(allocEnv(128)) , staticBaseEnv(false, 0) diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index 46d5a1cc866c..1e32db1e86bd 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -14,6 +14,7 @@ namespace nix { class Store; class EvalState; +enum RepairFlag : bool; typedef void (* PrimOpFun) (EvalState & state, const Pos & pos, Value * * args, Value & v); @@ -73,7 +74,7 @@ public: /* If set, force copying files to the Nix store even if they already exist there. */ - bool repair = false; + RepairFlag repair; /* If set, don't allow access to files outside of the Nix search path or to environment variables. */ diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 99ffddaeb80c..78d73e1f5572 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -708,8 +708,8 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * * HashType ht = parseHashType(outputHashAlgo); if (ht == htUnknown) throw EvalError(format("unknown hash algorithm ‘%1%’, at %2%") % outputHashAlgo % posDrvName); - Hash h = parseHash16or32(ht, *outputHash); - outputHash = printHash(h); + Hash h(*outputHash, ht); + outputHash = h.to_string(Base16, false); if (outputHashRecursive) outputHashAlgo = "r:" + outputHashAlgo; Path outPath = state.store->makeFixedOutputPath(outputHashRecursive, h, drvName); @@ -1701,7 +1701,7 @@ static void prim_hashString(EvalState & state, const Pos & pos, Value * * args, PathSet context; // discarded string s = state.forceString(*args[1], context, pos); - mkString(v, printHash(hashString(ht, s)), context); + mkString(v, hashString(ht, s).to_string(Base16, false), context); } @@ -1734,8 +1734,13 @@ static void prim_match(EvalState & state, const Pos & pos, Value * * args, Value mkString(*(v.listElems()[i] = state.allocValue()), match[i + 1].str().c_str()); } - } catch (std::regex_error &) { - throw EvalError("invalid regular expression ‘%s’, at %s", re, pos); + } catch (std::regex_error &e) { + if (e.code() == std::regex_constants::error_space) { + // limit is _GLIBCXX_REGEX_STATE_LIMIT for libstdc++ + throw EvalError("memory limit exceeded by regular expression ‘%s’, at %s", re, pos); + } else { + throw EvalError("invalid regular expression ‘%s’, at %s", re, pos); + } } } @@ -1852,7 +1857,7 @@ void fetch(EvalState & state, const Pos & pos, Value * * args, Value & v, if (n == "url") url = state.forceStringNoCtx(*attr.value, *attr.pos); else if (n == "sha256") - expectedHash = parseHash16or32(htSHA256, state.forceStringNoCtx(*attr.value, *attr.pos)); + expectedHash = Hash(state.forceStringNoCtx(*attr.value, *attr.pos), htSHA256); else if (n == "name") name = state.forceStringNoCtx(*attr.value, *attr.pos); else diff --git a/src/libstore/binary-cache-store.cc b/src/libstore/binary-cache-store.cc index 46c5aa21b2eb..8147345c2e1c 100644 --- a/src/libstore/binary-cache-store.cc +++ b/src/libstore/binary-cache-store.cc @@ -134,7 +134,7 @@ Path BinaryCacheStore::narInfoFileFor(const Path & storePath) } void BinaryCacheStore::addToStore(const ValidPathInfo & info, const ref<std::string> & nar, - bool repair, bool dontCheckSigs, std::shared_ptr<FSAccessor> accessor) + RepairFlag repair, CheckSigsFlag checkSigs, std::shared_ptr<FSAccessor> accessor) { if (!repair && isValidPath(info.path)) return; @@ -239,7 +239,7 @@ void BinaryCacheStore::addToStore(const ValidPathInfo & info, const ref<std::str % duration); /* Atomically write the NAR file. */ - narInfo->url = "nar/" + printHash32(narInfo->fileHash) + ".nar" + narInfo->url = "nar/" + narInfo->fileHash.to_string(Base32, false) + ".nar" + (compression == "xz" ? ".xz" : compression == "bzip2" ? ".bz2" : compression == "br" ? ".br" : @@ -328,7 +328,7 @@ void BinaryCacheStore::queryPathInfoUncached(const Path & storePath, } Path BinaryCacheStore::addToStore(const string & name, const Path & srcPath, - bool recursive, HashType hashAlgo, PathFilter & filter, bool repair) + bool recursive, HashType hashAlgo, PathFilter & filter, RepairFlag repair) { // FIXME: some cut&paste from LocalStore::addToStore(). @@ -349,13 +349,13 @@ Path BinaryCacheStore::addToStore(const string & name, const Path & srcPath, ValidPathInfo info; info.path = makeFixedOutputPath(recursive, h, name); - addToStore(info, sink.s, repair, false, 0); + addToStore(info, sink.s, repair, CheckSigs, nullptr); return info.path; } Path BinaryCacheStore::addTextToStore(const string & name, const string & s, - const PathSet & references, bool repair) + const PathSet & references, RepairFlag repair) { ValidPathInfo info; info.path = computeStorePathForText(name, s, references); @@ -364,7 +364,7 @@ Path BinaryCacheStore::addTextToStore(const string & name, const string & s, if (repair || !isValidPath(info.path)) { StringSink sink; dumpString(s, sink); - addToStore(info, sink.s, repair, false, 0); + addToStore(info, sink.s, repair, CheckSigs, nullptr); } return info.path; diff --git a/src/libstore/binary-cache-store.hh b/src/libstore/binary-cache-store.hh index 87d4aa43838e..f9c1c2cbe8a8 100644 --- a/src/libstore/binary-cache-store.hh +++ b/src/libstore/binary-cache-store.hh @@ -85,15 +85,15 @@ public: bool wantMassQuery() override { return wantMassQuery_; } void addToStore(const ValidPathInfo & info, const ref<std::string> & nar, - bool repair, bool dontCheckSigs, + RepairFlag repair, CheckSigsFlag checkSigs, std::shared_ptr<FSAccessor> accessor) override; Path addToStore(const string & name, const Path & srcPath, bool recursive, HashType hashAlgo, - PathFilter & filter, bool repair) override; + PathFilter & filter, RepairFlag repair) override; Path addTextToStore(const string & name, const string & s, - const PathSet & references, bool repair) override; + const PathSet & references, RepairFlag repair) override; void narFromPath(const Path & path, Sink & sink) override; @@ -123,6 +123,8 @@ public: std::shared_ptr<std::string> getBuildLog(const Path & path) override; + int getPriority() override { return priority; } + }; } diff --git a/src/libstore/build.cc b/src/libstore/build.cc index c34083d2e2fc..60b0a531f423 100644 --- a/src/libstore/build.cc +++ b/src/libstore/build.cc @@ -262,7 +262,7 @@ public: GoalPtr makeDerivationGoal(const Path & drvPath, const StringSet & wantedOutputs, BuildMode buildMode = bmNormal); std::shared_ptr<DerivationGoal> makeBasicDerivationGoal(const Path & drvPath, const BasicDerivation & drv, BuildMode buildMode = bmNormal); - GoalPtr makeSubstitutionGoal(const Path & storePath, bool repair = false); + GoalPtr makeSubstitutionGoal(const Path & storePath, RepairFlag repair = NoRepair); /* Remove a dead goal. */ void removeGoal(GoalPtr goal); @@ -1087,7 +1087,7 @@ void DerivationGoal::haveDerivation() them. */ if (settings.useSubstitutes && drv->substitutesAllowed()) for (auto & i : invalidOutputs) - addWaitee(worker.makeSubstitutionGoal(i, buildMode == bmRepair)); + addWaitee(worker.makeSubstitutionGoal(i, buildMode == bmRepair ? Repair : NoRepair)); if (waitees.empty()) /* to prevent hang (no wake-up event) */ outputsSubstituted(); @@ -1195,7 +1195,7 @@ void DerivationGoal::repairClosure() printError(format("found corrupted or missing path ‘%1%’ in the output closure of ‘%2%’") % i % drvPath); Path drvPath2 = outputsToDrv[i]; if (drvPath2 == "") - addWaitee(worker.makeSubstitutionGoal(i, true)); + addWaitee(worker.makeSubstitutionGoal(i, Repair)); else addWaitee(worker.makeDerivationGoal(drvPath2, PathSet(), bmRepair)); } @@ -2317,6 +2317,10 @@ void setupSeccomp() seccomp_arch_add(ctx, SCMP_ARCH_X86) != 0) throw SysError("unable to add 32-bit seccomp architecture"); + if (settings.thisSystem == "x86_64-linux" && + seccomp_arch_add(ctx, SCMP_ARCH_X32) != 0) + throw SysError("unable to add X32 seccomp architecture"); + /* Prevent builders from creating setuid/setgid binaries. */ for (int perm : { S_ISUID, S_ISGID }) { if (seccomp_rule_add(ctx, SCMP_ACT_ERRNO(EPERM), SCMP_SYS(chmod), 1, @@ -2340,6 +2344,9 @@ void setupSeccomp() seccomp_rule_add(ctx, SCMP_ACT_ERRNO(ENOTSUP), SCMP_SYS(fsetxattr), 0) != 0) throw SysError("unable to add seccomp rule"); + if (seccomp_attr_set(ctx, SCMP_FLTATR_CTL_NNP, settings.allowNewPrivileges ? 0 : 1) != 0) + throw SysError("unable to set 'no new privileges' seccomp attribute"); + if (seccomp_load(ctx) != 0) throw SysError("unable to load seccomp BPF program"); #endif @@ -3236,7 +3243,7 @@ PathSet DerivationGoal::checkPathValidity(bool returnValid, bool checkHash) Path DerivationGoal::addHashRewrite(const Path & path) { string h1 = string(path, worker.store.storeDir.size() + 1, 32); - string h2 = string(printHash32(hashString(htSHA256, "rewrite:" + drvPath + ":" + path)), 0, 32); + string h2 = string(hashString(htSHA256, "rewrite:" + drvPath + ":" + path).to_string(Base32, false), 0, 32); Path p = worker.store.storeDir + "/" + h2 + string(path, worker.store.storeDir.size() + 33); deletePath(p); assert(path.size() == p.size()); @@ -3291,7 +3298,7 @@ private: std::promise<void> promise; /* Whether to try to repair a valid path. */ - bool repair; + RepairFlag repair; /* Location where we're downloading the substitute. Differs from storePath when doing a repair. */ @@ -3301,7 +3308,7 @@ private: GoalState state; public: - SubstitutionGoal(const Path & storePath, Worker & worker, bool repair = false); + SubstitutionGoal(const Path & storePath, Worker & worker, RepairFlag repair = NoRepair); ~SubstitutionGoal(); void timedOut() override { abort(); }; @@ -3337,7 +3344,7 @@ public: }; -SubstitutionGoal::SubstitutionGoal(const Path & storePath, Worker & worker, bool repair) +SubstitutionGoal::SubstitutionGoal(const Path & storePath, Worker & worker, RepairFlag repair) : Goal(worker) , hasSubstitute(false) , repair(repair) @@ -3600,7 +3607,7 @@ std::shared_ptr<DerivationGoal> Worker::makeBasicDerivationGoal(const Path & drv } -GoalPtr Worker::makeSubstitutionGoal(const Path & path, bool repair) +GoalPtr Worker::makeSubstitutionGoal(const Path & path, RepairFlag repair) { GoalPtr goal = substitutionGoals[path].lock(); if (!goal) { @@ -3953,7 +3960,7 @@ void LocalStore::buildPaths(const PathSet & drvPaths, BuildMode buildMode) if (isDerivation(i2.first)) goals.insert(worker.makeDerivationGoal(i2.first, i2.second, buildMode)); else - goals.insert(worker.makeSubstitutionGoal(i, buildMode)); + goals.insert(worker.makeSubstitutionGoal(i, buildMode == bmRepair ? Repair : NoRepair)); } worker.run(goals); @@ -4011,7 +4018,7 @@ void LocalStore::ensurePath(const Path & path) void LocalStore::repairPath(const Path & path) { Worker worker(*this); - GoalPtr goal = worker.makeSubstitutionGoal(path, true); + GoalPtr goal = worker.makeSubstitutionGoal(path, Repair); Goals goals = {goal}; worker.run(goals); diff --git a/src/libstore/derivations.cc b/src/libstore/derivations.cc index 0c6ceb9f6741..48c0837ffaaa 100644 --- a/src/libstore/derivations.cc +++ b/src/libstore/derivations.cc @@ -23,7 +23,7 @@ void DerivationOutput::parseHashInfo(bool & recursive, Hash & hash) const if (hashType == htUnknown) throw Error(format("unknown hash algorithm ‘%1%’") % algo); - hash = parseHash(hashType, this->hash); + hash = Hash(this->hash, hashType); } @@ -71,7 +71,7 @@ bool BasicDerivation::canBuildLocally() const Path writeDerivation(ref<Store> store, - const Derivation & drv, const string & name, bool repair) + const Derivation & drv, const string & name, RepairFlag repair) { PathSet references; references.insert(drv.inputSrcs.begin(), drv.inputSrcs.end()); @@ -354,7 +354,7 @@ Hash hashDerivationModulo(Store & store, Derivation drv) h = hashDerivationModulo(store, drv2); drvHashes[i.first] = h; } - inputs2[printHash(h)] = i.second; + inputs2[h.to_string(Base16, false)] = i.second; } drv.inputDrvs = inputs2; @@ -437,7 +437,7 @@ Sink & operator << (Sink & out, const BasicDerivation & drv) std::string hashPlaceholder(const std::string & outputName) { // FIXME: memoize? - return "/" + printHash32(hashString(htSHA256, "nix-output:" + outputName)); + return "/" + hashString(htSHA256, "nix-output:" + outputName).to_string(Base32, false); } diff --git a/src/libstore/derivations.hh b/src/libstore/derivations.hh index 9717a81e469c..7b97730d3bf2 100644 --- a/src/libstore/derivations.hh +++ b/src/libstore/derivations.hh @@ -2,6 +2,7 @@ #include "types.hh" #include "hash.hh" +#include "store-api.hh" #include <map> @@ -85,7 +86,7 @@ class Store; /* Write a derivation to the Nix store, and return its path. */ Path writeDerivation(ref<Store> store, - const Derivation & drv, const string & name, bool repair = false); + const Derivation & drv, const string & name, RepairFlag repair = NoRepair); /* Read a derivation from a file. */ Derivation readDerivation(const Path & drvPath); diff --git a/src/libstore/download.cc b/src/libstore/download.cc index 33ab1f027829..15eb68c69ea4 100644 --- a/src/libstore/download.cc +++ b/src/libstore/download.cc @@ -581,7 +581,7 @@ Path Downloader::downloadCached(ref<Store> store, const string & url_, bool unpa Path cacheDir = getCacheDir() + "/nix/tarballs"; createDirs(cacheDir); - string urlHash = printHash32(hashString(htSHA256, url)); + string urlHash = hashString(htSHA256, url).to_string(Base32, false); Path dataFile = cacheDir + "/" + urlHash + ".info"; Path fileLink = cacheDir + "/" + urlHash + "-file"; @@ -631,7 +631,7 @@ Path Downloader::downloadCached(ref<Store> store, const string & url_, bool unpa info.narHash = hashString(htSHA256, *sink.s); info.narSize = sink.s->size(); info.ca = makeFixedOutputCA(false, hash); - store->addToStore(info, sink.s, false, true); + store->addToStore(info, sink.s, NoRepair, NoCheckSigs); storePath = info.path; } @@ -660,7 +660,7 @@ Path Downloader::downloadCached(ref<Store> store, const string & url_, bool unpa AutoDelete autoDelete(tmpDir, true); // FIXME: this requires GNU tar for decompression. runProgram("tar", true, {"xf", storePath, "-C", tmpDir, "--strip-components", "1"}); - unpackedStorePath = store->addToStore(name, tmpDir, true, htSHA256, defaultPathFilter, false); + unpackedStorePath = store->addToStore(name, tmpDir, true, htSHA256, defaultPathFilter, NoRepair); } replaceSymlink(unpackedStorePath, unpackedLink); storePath = unpackedStorePath; diff --git a/src/libstore/export-import.cc b/src/libstore/export-import.cc index 6e8bc692cdff..2cbcedc6fb00 100644 --- a/src/libstore/export-import.cc +++ b/src/libstore/export-import.cc @@ -56,12 +56,12 @@ void Store::exportPath(const Path & path, Sink & sink) Hash hash = hashAndWriteSink.currentHash(); if (hash != info->narHash && info->narHash != Hash(info->narHash.type)) throw Error(format("hash of path ‘%1%’ has changed from ‘%2%’ to ‘%3%’!") % path - % printHash(info->narHash) % printHash(hash)); + % info->narHash.to_string() % hash.to_string()); hashAndWriteSink << exportMagic << path << info->references << info->deriver << 0; } -Paths Store::importPaths(Source & source, std::shared_ptr<FSAccessor> accessor, bool dontCheckSigs) +Paths Store::importPaths(Source & source, std::shared_ptr<FSAccessor> accessor, CheckSigsFlag checkSigs) { Paths res; while (true) { @@ -95,7 +95,7 @@ Paths Store::importPaths(Source & source, std::shared_ptr<FSAccessor> accessor, if (readInt(source) == 1) readString(source); - addToStore(info, tee.source.data, false, dontCheckSigs, accessor); + addToStore(info, tee.source.data, NoRepair, checkSigs, accessor); res.push_back(info.path); } diff --git a/src/libstore/gc.cc b/src/libstore/gc.cc index 3cdbb114a79d..0cf9f87cac32 100644 --- a/src/libstore/gc.cc +++ b/src/libstore/gc.cc @@ -76,7 +76,7 @@ void LocalStore::syncWithGC() void LocalStore::addIndirectRoot(const Path & path) { - string hash = printHash32(hashString(htSHA1, path)); + string hash = hashString(htSHA1, path).to_string(Base32, false); Path realRoot = canonPath((format("%1%/%2%/auto/%3%") % stateDir % gcRootsDir % hash).str()); makeSymlink(realRoot, path); diff --git a/src/libstore/globals.hh b/src/libstore/globals.hh index af37ec61d7a1..c8d67b07110b 100644 --- a/src/libstore/globals.hh +++ b/src/libstore/globals.hh @@ -321,6 +321,12 @@ public: Setting<std::string> userAgentSuffix{this, "", "user-agent-suffix", "String appended to the user agent in HTTP requests."}; + +#if __linux__ + Setting<bool> allowNewPrivileges{this, false, "allow-new-privileges", + "Whether builders can acquire new privileges by calling programs with " + "setuid/setgid bits or with file capabilities."}; +#endif }; diff --git a/src/libstore/legacy-ssh-store.cc b/src/libstore/legacy-ssh-store.cc index e09932e3d182..a84f85c1b95a 100644 --- a/src/libstore/legacy-ssh-store.cc +++ b/src/libstore/legacy-ssh-store.cc @@ -113,7 +113,7 @@ struct LegacySSHStore : public Store } void addToStore(const ValidPathInfo & info, const ref<std::string> & nar, - bool repair, bool dontCheckSigs, + RepairFlag repair, CheckSigsFlag checkSigs, std::shared_ptr<FSAccessor> accessor) override { debug("adding path ‘%s’ to remote host ‘%s’", info.path, host); @@ -168,11 +168,11 @@ struct LegacySSHStore : public Store Path addToStore(const string & name, const Path & srcPath, bool recursive, HashType hashAlgo, - PathFilter & filter, bool repair) override + PathFilter & filter, RepairFlag repair) override { unsupported(); } Path addTextToStore(const string & name, const string & s, - const PathSet & references, bool repair) override + const PathSet & references, RepairFlag repair) override { unsupported(); } BuildResult buildDerivation(const Path & drvPath, const BasicDerivation & drv, @@ -249,7 +249,8 @@ struct LegacySSHStore : public Store out.insert(res.begin(), res.end()); } - PathSet queryValidPaths(const PathSet & paths, bool maybeSubstitute = false) override + PathSet queryValidPaths(const PathSet & paths, + SubstituteFlag maybeSubstitute = NoSubstitute) override { auto conn(connections->get()); diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc index 74c74e672e25..7c41dfca7f31 100644 --- a/src/libstore/local-store.cc +++ b/src/libstore/local-store.cc @@ -30,6 +30,10 @@ #include <sys/xattr.h> #endif +#ifdef __CYGWIN__ +#include <windows.h> +#endif + #include <sqlite3.h> @@ -281,6 +285,16 @@ void LocalStore::openDB(State & state, bool create) SQLITE_OPEN_READWRITE | (create ? SQLITE_OPEN_CREATE : 0), 0) != SQLITE_OK) throw Error(format("cannot open Nix database ‘%1%’") % dbPath); +#ifdef __CYGWIN__ + /* The cygwin version of sqlite3 has a patch which calls + SetDllDirectory("/usr/bin") on init. It was intended to fix extension + loading, which we don't use, and the effect of SetDllDirectory is + inherited by child processes, and causes libraries to be loaded from + /usr/bin instead of $PATH. This breaks quite a few things (e.g. + checkPhase on openssh), so we set it back to default behaviour. */ + SetDllDirectoryW(L""); +#endif + if (sqlite3_busy_timeout(db, 60 * 60 * 1000) != SQLITE_OK) throwSQLiteError(db, "setting timeout"); @@ -558,7 +572,7 @@ uint64_t LocalStore::addValidPath(State & state, state.stmtRegisterValidPath.use() (info.path) - ("sha256:" + printHash(info.narHash)) + (info.narHash.to_string(Base16)) (info.registrationTime == 0 ? time(0) : info.registrationTime) (info.deriver, info.deriver != "") (info.narSize, info.narSize != 0) @@ -600,20 +614,6 @@ uint64_t LocalStore::addValidPath(State & state, } -Hash parseHashField(const Path & path, const string & s) -{ - string::size_type colon = s.find(':'); - if (colon == string::npos) - throw Error(format("corrupt hash ‘%1%’ in valid-path entry for ‘%2%’") - % s % path); - HashType ht = parseHashType(string(s, 0, colon)); - if (ht == htUnknown) - throw Error(format("unknown hash type ‘%1%’ in valid-path entry for ‘%2%’") - % string(s, 0, colon) % path); - return parseHash(ht, string(s, colon + 1)); -} - - void LocalStore::queryPathInfoUncached(const Path & path, std::function<void(std::shared_ptr<ValidPathInfo>)> success, std::function<void(std::exception_ptr exc)> failure) @@ -636,7 +636,11 @@ void LocalStore::queryPathInfoUncached(const Path & path, info->id = useQueryPathInfo.getInt(0); - info->narHash = parseHashField(path, useQueryPathInfo.getStr(1)); + try { + info->narHash = Hash(useQueryPathInfo.getStr(1)); + } catch (BadHash & e) { + throw Error("in valid-path entry for ‘%s’: %s", path, e.what()); + } info->registrationTime = useQueryPathInfo.getInt(2); @@ -671,7 +675,7 @@ void LocalStore::updatePathInfo(State & state, const ValidPathInfo & info) { state.stmtUpdatePathInfo.use() (info.narSize, info.narSize != 0) - ("sha256:" + printHash(info.narHash)) + (info.narHash.to_string(Base16)) (info.ultimate ? 1 : 0, info.ultimate) (concatStringsSep(" ", info.sigs), !info.sigs.empty()) (info.ca, !info.ca.empty()) @@ -704,7 +708,7 @@ bool LocalStore::isValidPathUncached(const Path & path) } -PathSet LocalStore::queryValidPaths(const PathSet & paths, bool maybeSubstitute) +PathSet LocalStore::queryValidPaths(const PathSet & paths, SubstituteFlag maybeSubstitute) { PathSet res; for (auto & i : paths) @@ -947,7 +951,7 @@ void LocalStore::invalidatePath(State & state, const Path & path) void LocalStore::addToStore(const ValidPathInfo & info, const ref<std::string> & nar, - bool repair, bool dontCheckSigs, std::shared_ptr<FSAccessor> accessor) + RepairFlag repair, CheckSigsFlag checkSigs, std::shared_ptr<FSAccessor> accessor) { assert(info.narHash); @@ -960,7 +964,7 @@ void LocalStore::addToStore(const ValidPathInfo & info, const ref<std::string> & throw Error("size mismatch importing path ‘%s’; expected %s, got %s", info.path, info.narSize, nar->size()); - if (requireSigs && !dontCheckSigs && !info.checkSignatures(*this, publicKeys)) + if (requireSigs && checkSigs && !info.checkSignatures(*this, publicKeys)) throw Error("cannot add path ‘%s’ because it lacks a valid signature", info.path); addTempRoot(info.path); @@ -998,7 +1002,7 @@ void LocalStore::addToStore(const ValidPathInfo & info, const ref<std::string> & Path LocalStore::addToStoreFromDump(const string & dump, const string & name, - bool recursive, HashType hashAlgo, bool repair) + bool recursive, HashType hashAlgo, RepairFlag repair) { Hash h = hashString(hashAlgo, dump); @@ -1056,7 +1060,7 @@ Path LocalStore::addToStoreFromDump(const string & dump, const string & name, Path LocalStore::addToStore(const string & name, const Path & _srcPath, - bool recursive, HashType hashAlgo, PathFilter & filter, bool repair) + bool recursive, HashType hashAlgo, PathFilter & filter, RepairFlag repair) { Path srcPath(absPath(_srcPath)); @@ -1074,7 +1078,7 @@ Path LocalStore::addToStore(const string & name, const Path & _srcPath, Path LocalStore::addTextToStore(const string & name, const string & s, - const PathSet & references, bool repair) + const PathSet & references, RepairFlag repair) { auto hash = hashString(htSHA256, s); auto dstPath = makeTextPath(name, hash, references); @@ -1156,7 +1160,7 @@ void LocalStore::invalidatePathChecked(const Path & path) } -bool LocalStore::verifyStore(bool checkContents, bool repair) +bool LocalStore::verifyStore(bool checkContents, RepairFlag repair) { printError(format("reading the Nix store...")); @@ -1197,7 +1201,7 @@ bool LocalStore::verifyStore(bool checkContents, bool repair) if (info->narHash != nullHash && info->narHash != current.first) { printError(format("path ‘%1%’ was modified! " "expected hash ‘%2%’, got ‘%3%’") - % i % printHash(info->narHash) % printHash(current.first)); + % i % info->narHash.to_string() % current.first.to_string()); if (repair) repairPath(i); else errors = true; } else { @@ -1241,7 +1245,7 @@ bool LocalStore::verifyStore(bool checkContents, bool repair) void LocalStore::verifyPath(const Path & path, const PathSet & store, - PathSet & done, PathSet & validPaths, bool repair, bool & errors) + PathSet & done, PathSet & validPaths, RepairFlag repair, bool & errors) { checkInterrupt(); diff --git a/src/libstore/local-store.hh b/src/libstore/local-store.hh index f2c40e96464b..551c6b506fb1 100644 --- a/src/libstore/local-store.hh +++ b/src/libstore/local-store.hh @@ -98,7 +98,8 @@ public: bool isValidPathUncached(const Path & path) override; - PathSet queryValidPaths(const PathSet & paths, bool maybeSubstitute = false) override; + PathSet queryValidPaths(const PathSet & paths, + SubstituteFlag maybeSubstitute = NoSubstitute) override; PathSet queryAllValidPaths() override; @@ -122,22 +123,22 @@ public: SubstitutablePathInfos & infos) override; void addToStore(const ValidPathInfo & info, const ref<std::string> & nar, - bool repair, bool dontCheckSigs, + RepairFlag repair, CheckSigsFlag checkSigs, std::shared_ptr<FSAccessor> accessor) override; Path addToStore(const string & name, const Path & srcPath, bool recursive, HashType hashAlgo, - PathFilter & filter, bool repair) override; + PathFilter & filter, RepairFlag repair) override; /* Like addToStore(), but the contents of the path are contained in `dump', which is either a NAR serialisation (if recursive == true) or simply the contents of a regular file (if recursive == false). */ Path addToStoreFromDump(const string & dump, const string & name, - bool recursive = true, HashType hashAlgo = htSHA256, bool repair = false); + bool recursive = true, HashType hashAlgo = htSHA256, RepairFlag repair = NoRepair); Path addTextToStore(const string & name, const string & s, - const PathSet & references, bool repair) override; + const PathSet & references, RepairFlag repair) override; void buildPaths(const PathSet & paths, BuildMode buildMode) override; @@ -174,7 +175,7 @@ public: /* Optimise a single store path. */ void optimisePath(const Path & path); - bool verifyStore(bool checkContents, bool repair) override; + bool verifyStore(bool checkContents, RepairFlag repair) override; /* Register the validity of a path, i.e., that `path' exists, that the paths referenced by it exists, and in the case of an output @@ -212,7 +213,7 @@ private: void invalidatePathChecked(const Path & path); void verifyPath(const Path & path, const PathSet & store, - PathSet & done, PathSet & validPaths, bool repair, bool & errors); + PathSet & done, PathSet & validPaths, RepairFlag repair, bool & errors); void updatePathInfo(State & state, const ValidPathInfo & info); diff --git a/src/libstore/nar-info-disk-cache.cc b/src/libstore/nar-info-disk-cache.cc index 180a936edb85..6e155e877803 100644 --- a/src/libstore/nar-info-disk-cache.cc +++ b/src/libstore/nar-info-disk-cache.cc @@ -203,9 +203,9 @@ public: narInfo->url = queryNAR.getStr(3); narInfo->compression = queryNAR.getStr(4); if (!queryNAR.isNull(5)) - narInfo->fileHash = parseHash(queryNAR.getStr(5)); + narInfo->fileHash = Hash(queryNAR.getStr(5)); narInfo->fileSize = queryNAR.getInt(6); - narInfo->narHash = parseHash(queryNAR.getStr(7)); + narInfo->narHash = Hash(queryNAR.getStr(7)); narInfo->narSize = queryNAR.getInt(8); for (auto & r : tokenizeString<Strings>(queryNAR.getStr(9), " ")) narInfo->references.insert(cache.storeDir + "/" + r); diff --git a/src/libstore/nar-info.cc b/src/libstore/nar-info.cc index d1042c6de25e..660f6a42a19d 100644 --- a/src/libstore/nar-info.cc +++ b/src/libstore/nar-info.cc @@ -11,7 +11,7 @@ NarInfo::NarInfo(const Store & store, const std::string & s, const std::string & auto parseHashField = [&](const string & s) { try { - return parseHash(s); + return Hash(s); } catch (BadHash &) { corrupt(); return Hash(); // never reached @@ -90,10 +90,10 @@ std::string NarInfo::to_string() const assert(compression != ""); res += "Compression: " + compression + "\n"; assert(fileHash.type == htSHA256); - res += "FileHash: sha256:" + printHash32(fileHash) + "\n"; + res += "FileHash: " + fileHash.to_string(Base32) + "\n"; res += "FileSize: " + std::to_string(fileSize) + "\n"; assert(narHash.type == htSHA256); - res += "NarHash: sha256:" + printHash32(narHash) + "\n"; + res += "NarHash: " + narHash.to_string(Base32) + "\n"; res += "NarSize: " + std::to_string(narSize) + "\n"; res += "References: " + concatStringsSep(" ", shortRefs()) + "\n"; diff --git a/src/libstore/optimise-store.cc b/src/libstore/optimise-store.cc index 56167c4dfae8..8e8002a30db5 100644 --- a/src/libstore/optimise-store.cc +++ b/src/libstore/optimise-store.cc @@ -9,6 +9,7 @@ #include <unistd.h> #include <errno.h> #include <stdio.h> +#include <regex> namespace nix { @@ -96,6 +97,19 @@ void LocalStore::optimisePath_(OptimiseStats & stats, const Path & path, InodeHa if (lstat(path.c_str(), &st)) throw SysError(format("getting attributes of path ‘%1%’") % path); +#if __APPLE__ + /* HFS/OS X has some undocumented security feature disabling hardlinking for + special files within .app dirs. *.app/Contents/PkgInfo and + *.app/Contents/Resources/\*.lproj seem to be the only paths affected. See + https://github.com/NixOS/nix/issues/1443 for more discussion. */ + + if (std::regex_search(path, std::regex("\\.app/Contents/PkgInfo$")) || + std::regex_search(path, std::regex("\\.app/Contents/Resources/.+\\.lproj$"))) { + debug(format("‘%1%’ is not allowed to be linked in OS X") % path); + return; + } +#endif + if (S_ISDIR(st.st_mode)) { Strings names = readDirectoryIgnoringInodes(path, inodeHash); for (auto & i : names) @@ -135,10 +149,10 @@ void LocalStore::optimisePath_(OptimiseStats & stats, const Path & path, InodeHa contents of the symlink (i.e. the result of readlink()), not the contents of the target (which may not even exist). */ Hash hash = hashPath(htSHA256, path).first; - debug(format("‘%1%’ has hash ‘%2%’") % path % printHash(hash)); + debug(format("‘%1%’ has hash ‘%2%’") % path % hash.to_string()); /* Check if this is a known hash. */ - Path linkPath = linksDir + "/" + printHash32(hash); + Path linkPath = linksDir + "/" + hash.to_string(Base32, false); retry: if (!pathExists(linkPath)) { diff --git a/src/libstore/remote-store.cc b/src/libstore/remote-store.cc index be8819bbc004..ab726e79534a 100644 --- a/src/libstore/remote-store.cc +++ b/src/libstore/remote-store.cc @@ -185,7 +185,7 @@ bool RemoteStore::isValidPathUncached(const Path & path) } -PathSet RemoteStore::queryValidPaths(const PathSet & paths, bool maybeSubstitute) +PathSet RemoteStore::queryValidPaths(const PathSet & paths, SubstituteFlag maybeSubstitute) { auto conn(connections->get()); if (GET_PROTOCOL_MINOR(conn->daemonVersion) < 12) { @@ -294,7 +294,7 @@ void RemoteStore::queryPathInfoUncached(const Path & path, info->path = path; info->deriver = readString(conn->from); if (info->deriver != "") assertStorePath(info->deriver); - info->narHash = parseHash(htSHA256, readString(conn->from)); + info->narHash = Hash(readString(conn->from), htSHA256); info->references = readStorePaths<PathSet>(*this, conn->from); conn->from >> info->registrationTime >> info->narSize; if (GET_PROTOCOL_MINOR(conn->daemonVersion) >= 16) { @@ -357,7 +357,7 @@ Path RemoteStore::queryPathFromHashPart(const string & hashPart) void RemoteStore::addToStore(const ValidPathInfo & info, const ref<std::string> & nar, - bool repair, bool dontCheckSigs, std::shared_ptr<FSAccessor> accessor) + RepairFlag repair, CheckSigsFlag checkSigs, std::shared_ptr<FSAccessor> accessor) { auto conn(connections->get()); @@ -387,10 +387,10 @@ void RemoteStore::addToStore(const ValidPathInfo & info, const ref<std::string> else { conn->to << wopAddToStoreNar - << info.path << info.deriver << printHash(info.narHash) + << info.path << info.deriver << info.narHash.to_string(Base16, false) << info.references << info.registrationTime << info.narSize << info.ultimate << info.sigs << info.ca - << repair << dontCheckSigs; + << repair << !checkSigs; conn->to(*nar); conn->processStderr(); } @@ -398,7 +398,7 @@ void RemoteStore::addToStore(const ValidPathInfo & info, const ref<std::string> Path RemoteStore::addToStore(const string & name, const Path & _srcPath, - bool recursive, HashType hashAlgo, PathFilter & filter, bool repair) + bool recursive, HashType hashAlgo, PathFilter & filter, RepairFlag repair) { if (repair) throw Error("repairing is not supported when building through the Nix daemon"); @@ -434,7 +434,7 @@ Path RemoteStore::addToStore(const string & name, const Path & _srcPath, Path RemoteStore::addTextToStore(const string & name, const string & s, - const PathSet & references, bool repair) + const PathSet & references, RepairFlag repair) { if (repair) throw Error("repairing is not supported when building through the Nix daemon"); @@ -570,7 +570,7 @@ void RemoteStore::optimiseStore() } -bool RemoteStore::verifyStore(bool checkContents, bool repair) +bool RemoteStore::verifyStore(bool checkContents, RepairFlag repair) { auto conn(connections->get()); conn->to << wopVerifyStore << checkContents << repair; diff --git a/src/libstore/remote-store.hh b/src/libstore/remote-store.hh index ed430e4cabb6..e370e4797d24 100644 --- a/src/libstore/remote-store.hh +++ b/src/libstore/remote-store.hh @@ -31,7 +31,8 @@ public: bool isValidPathUncached(const Path & path) override; - PathSet queryValidPaths(const PathSet & paths, bool maybeSubstitute = false) override; + PathSet queryValidPaths(const PathSet & paths, + SubstituteFlag maybeSubstitute = NoSubstitute) override; PathSet queryAllValidPaths() override; @@ -55,15 +56,15 @@ public: SubstitutablePathInfos & infos) override; void addToStore(const ValidPathInfo & info, const ref<std::string> & nar, - bool repair, bool dontCheckSigs, + RepairFlag repair, CheckSigsFlag checkSigs, std::shared_ptr<FSAccessor> accessor) override; Path addToStore(const string & name, const Path & srcPath, bool recursive = true, HashType hashAlgo = htSHA256, - PathFilter & filter = defaultPathFilter, bool repair = false) override; + PathFilter & filter = defaultPathFilter, RepairFlag repair = NoRepair) override; Path addTextToStore(const string & name, const string & s, - const PathSet & references, bool repair = false) override; + const PathSet & references, RepairFlag repair) override; void buildPaths(const PathSet & paths, BuildMode buildMode) override; @@ -84,7 +85,7 @@ public: void optimiseStore() override; - bool verifyStore(bool checkContents, bool repair) override; + bool verifyStore(bool checkContents, RepairFlag repair) override; void addSignatures(const Path & storePath, const StringSet & sigs) override; diff --git a/src/libstore/store-api.cc b/src/libstore/store-api.cc index 76ed9942256b..108e2d4ce9b0 100644 --- a/src/libstore/store-api.cc +++ b/src/libstore/store-api.cc @@ -176,13 +176,12 @@ Path Store::makeStorePath(const string & type, const Hash & hash, const string & name) const { /* e.g., "source:sha256:1abc...:/nix/store:foo.tar.gz" */ - string s = type + ":sha256:" + printHash(hash) + ":" - + storeDir + ":" + name; + string s = type + ":" + hash.to_string(Base16) + ":" + storeDir + ":" + name; checkStoreName(name); return storeDir + "/" - + printHash32(compressHash(hashString(htSHA256, s), 20)) + + compressHash(hashString(htSHA256, s), 20).to_string(Base32, false) + "-" + name; } @@ -202,7 +201,7 @@ Path Store::makeFixedOutputPath(bool recursive, ? makeStorePath("source", hash, name) : makeStorePath("output:out", hashString(htSHA256, "fixed:out:" + (recursive ? (string) "r:" : "") + - printHashType(hash.type) + ":" + printHash(hash) + ":"), + hash.to_string(Base16) + ":"), name); } @@ -378,7 +377,7 @@ void Store::queryPathInfo(const Path & storePath, } -PathSet Store::queryValidPaths(const PathSet & paths, bool maybeSubstitute) +PathSet Store::queryValidPaths(const PathSet & paths, SubstituteFlag maybeSubstitute) { struct State { @@ -438,7 +437,7 @@ string Store::makeValidityRegistration(const PathSet & paths, auto info = queryPathInfo(i); if (showHash) { - s += printHash(info->narHash) + "\n"; + s += info->narHash.to_string(Base16, false) + "\n"; s += (format("%1%\n") % info->narSize).str(); } @@ -537,14 +536,14 @@ void Store::buildPaths(const PathSet & paths, BuildMode buildMode) void copyStorePath(ref<Store> srcStore, ref<Store> dstStore, - const Path & storePath, bool repair, bool dontCheckSigs) + const Path & storePath, RepairFlag repair, CheckSigsFlag checkSigs) { auto info = srcStore->queryPathInfo(storePath); StringSink sink; srcStore->narFromPath({storePath}, sink); - if (!info->narHash && dontCheckSigs) { + if (!info->narHash && !checkSigs) { auto info2 = make_ref<ValidPathInfo>(*info); info2->narHash = hashString(htSHA256, *sink.s); if (!info->narSize) info2->narSize = sink.s->size(); @@ -561,33 +560,47 @@ void copyStorePath(ref<Store> srcStore, ref<Store> dstStore, assert(info->narHash); - dstStore->addToStore(*info, sink.s, repair, dontCheckSigs); + dstStore->addToStore(*info, sink.s, repair, checkSigs); } -void copyClosure(ref<Store> srcStore, ref<Store> dstStore, - const PathSet & storePaths, bool repair, bool dontCheckSigs) +void copyPaths(ref<Store> srcStore, ref<Store> dstStore, const PathSet & storePaths, + RepairFlag repair, CheckSigsFlag checkSigs, SubstituteFlag substitute) { - PathSet closure; + PathSet valid = dstStore->queryValidPaths(storePaths, substitute); + + PathSet missing; for (auto & path : storePaths) - srcStore->computeFSClosure(path, closure); + if (!valid.count(path)) missing.insert(path); - // FIXME: use copyStorePaths() + ThreadPool pool; - PathSet valid = dstStore->queryValidPaths(closure); + processGraph<Path>(pool, + PathSet(missing.begin(), missing.end()), - if (valid.size() == closure.size()) return; + [&](const Path & storePath) { + if (dstStore->isValidPath(storePath)) return PathSet(); + return srcStore->queryPathInfo(storePath)->references; + }, - Paths sorted = srcStore->topoSortPaths(closure); + [&](const Path & storePath) { + checkInterrupt(); - Paths missing; - for (auto i = sorted.rbegin(); i != sorted.rend(); ++i) - if (!valid.count(*i)) missing.push_back(*i); + if (!dstStore->isValidPath(storePath)) { + printError("copying ‘%s’...", storePath); + copyStorePath(srcStore, dstStore, storePath, repair, checkSigs); + } + }); +} - printMsg(lvlDebug, format("copying %1% missing paths") % missing.size()); - for (auto & i : missing) - copyStorePath(srcStore, dstStore, i, repair, dontCheckSigs); +void copyClosure(ref<Store> srcStore, ref<Store> dstStore, + const PathSet & storePaths, RepairFlag repair, CheckSigsFlag checkSigs, + SubstituteFlag substitute) +{ + PathSet closure; + srcStore->computeFSClosure({storePaths}, closure); + copyPaths(srcStore, dstStore, closure, repair, checkSigs, substitute); } @@ -599,7 +612,7 @@ ValidPathInfo decodeValidPathInfo(std::istream & str, bool hashGiven) if (hashGiven) { string s; getline(str, s); - info.narHash = parseHash(htSHA256, s); + info.narHash = Hash(s, htSHA256); getline(str, s); if (!string2Int(s, info.narSize)) throw Error("number expected"); } @@ -634,7 +647,7 @@ std::string ValidPathInfo::fingerprint() const % path); return "1;" + path + ";" - + printHashType(narHash.type) + ":" + printHash32(narHash) + ";" + + narHash.to_string(Base32) + ";" + std::to_string(narSize) + ";" + concatStringsSep(",", references); } @@ -653,7 +666,7 @@ bool ValidPathInfo::isContentAddressed(const Store & store) const }; if (hasPrefix(ca, "text:")) { - auto hash = parseHash(std::string(ca, 5)); + Hash hash(std::string(ca, 5)); if (store.makeTextPath(storePathToName(path), hash, references) == path) return true; else @@ -662,7 +675,7 @@ bool ValidPathInfo::isContentAddressed(const Store & store) const else if (hasPrefix(ca, "fixed:")) { bool recursive = ca.compare(6, 2, "r:") == 0; - auto hash = parseHash(std::string(ca, recursive ? 8 : 6)); + Hash hash(std::string(ca, recursive ? 8 : 6)); if (store.makeFixedOutputPath(recursive, hash, storePathToName(path)) == path) return true; else @@ -782,74 +795,31 @@ static RegisterStoreImplementation regStore([]( std::list<ref<Store>> getDefaultSubstituters() { - struct State { - bool done = false; + static auto stores([]() { std::list<ref<Store>> stores; - }; - static Sync<State> state_; - - auto state(state_.lock()); - - if (state->done) return state->stores; - - StringSet done; - - auto addStore = [&](const std::string & uri) { - if (done.count(uri)) return; - done.insert(uri); - state->stores.push_back(openStore(uri)); - }; - for (auto uri : settings.substituters.get()) - addStore(uri); + StringSet done; - for (auto uri : settings.extraSubstituters.get()) - addStore(uri); + auto addStore = [&](const std::string & uri) { + if (done.count(uri)) return; + done.insert(uri); + stores.push_back(openStore(uri)); + }; - state->done = true; + for (auto uri : settings.substituters.get()) + addStore(uri); - return state->stores; -} - - -void copyPaths(ref<Store> from, ref<Store> to, const PathSet & storePaths, - bool substitute, bool dontCheckSigs) -{ - PathSet valid = to->queryValidPaths(storePaths, substitute); - - PathSet missing; - for (auto & path : storePaths) - if (!valid.count(path)) missing.insert(path); - - std::string copiedLabel = "copied"; - - //logger->setExpected(copiedLabel, missing.size()); - - ThreadPool pool; + for (auto uri : settings.extraSubstituters.get()) + addStore(uri); - processGraph<Path>(pool, - PathSet(missing.begin(), missing.end()), - - [&](const Path & storePath) { - if (to->isValidPath(storePath)) return PathSet(); - return from->queryPathInfo(storePath)->references; - }, - - [&](const Path & storePath) { - checkInterrupt(); - - if (!to->isValidPath(storePath)) { - //Activity act(*logger, lvlInfo, format("copying ‘%s’...") % storePath); - - copyStorePath(from, to, storePath, false, dontCheckSigs); - - //logger->incProgress(copiedLabel); - } else - ; - //logger->incExpected(copiedLabel, -1); + stores.sort([](ref<Store> & a, ref<Store> & b) { + return a->getPriority() < b->getPriority(); }); - pool.process(); + return stores; + } ()); + + return stores; } diff --git a/src/libstore/store-api.hh b/src/libstore/store-api.hh index 929c95a0f2f8..cada37653e6f 100644 --- a/src/libstore/store-api.hh +++ b/src/libstore/store-api.hh @@ -32,6 +32,11 @@ class Store; class JSONPlaceholder; +enum RepairFlag : bool { NoRepair = false, Repair = true }; +enum CheckSigsFlag : bool { NoCheckSigs = false, CheckSigs = true }; +enum SubstituteFlag : bool { NoSubstitute = false, Substitute = true }; + + /* Size of the hash part of store paths, in base-32 characters. */ const size_t storePathHashLen = 32; // i.e. 160 bits @@ -332,7 +337,7 @@ public: /* Query which of the given paths is valid. Optionally, try to substitute missing paths. */ virtual PathSet queryValidPaths(const PathSet & paths, - bool maybeSubstitute = false); + SubstituteFlag maybeSubstitute = NoSubstitute); /* Query the set of all valid paths. Note that for some store backends, the name part of store paths may be omitted @@ -392,7 +397,7 @@ public: /* Import a path into the store. */ virtual void addToStore(const ValidPathInfo & info, const ref<std::string> & nar, - bool repair = false, bool dontCheckSigs = false, + RepairFlag repair = NoRepair, CheckSigsFlag checkSigs = CheckSigs, std::shared_ptr<FSAccessor> accessor = 0) = 0; /* Copy the contents of a path to the store and register the @@ -401,12 +406,12 @@ public: libutil/archive.hh). */ virtual Path addToStore(const string & name, const Path & srcPath, bool recursive = true, HashType hashAlgo = htSHA256, - PathFilter & filter = defaultPathFilter, bool repair = false) = 0; + PathFilter & filter = defaultPathFilter, RepairFlag repair = NoRepair) = 0; /* Like addToStore, but the contents written to the output path is a regular file containing the given string. */ virtual Path addTextToStore(const string & name, const string & s, - const PathSet & references, bool repair = false) = 0; + const PathSet & references, RepairFlag repair = NoRepair) = 0; /* Write a NAR dump of a store path. */ virtual void narFromPath(const Path & path, Sink & sink) = 0; @@ -496,7 +501,7 @@ public: /* Check the integrity of the Nix store. Returns true if errors remain. */ - virtual bool verifyStore(bool checkContents, bool repair) { return false; }; + virtual bool verifyStore(bool checkContents, RepairFlag repair = NoRepair) { return false; }; /* Return an object to access files in the Nix store. */ virtual ref<FSAccessor> getFSAccessor() = 0; @@ -548,7 +553,7 @@ public: preloaded into the specified FS accessor to speed up subsequent access. */ Paths importPaths(Source & source, std::shared_ptr<FSAccessor> accessor, - bool dontCheckSigs = false); + CheckSigsFlag checkSigs = CheckSigs); struct Stats { @@ -585,6 +590,11 @@ public: a notion of connection. Otherwise this is a no-op. */ virtual void connect() { }; + /* Get the priority of the store, used to order substituters. In + particular, binary caches can specify a priority field in their + "nix-cache-info" file. Lower value means higher priority. */ + virtual int getPriority() { return 0; } + protected: Stats stats; @@ -650,12 +660,26 @@ void checkStoreName(const string & name); /* Copy a path from one store to another. */ void copyStorePath(ref<Store> srcStore, ref<Store> dstStore, - const Path & storePath, bool repair = false, bool dontCheckSigs = false); + const Path & storePath, RepairFlag repair = NoRepair, CheckSigsFlag checkSigs = CheckSigs); + + +/* Copy store paths from one store to another. The paths may be copied + in parallel. They are copied in a topologically sorted order + (i.e. if A is a reference of B, then A is copied before B), but + the set of store paths is not automatically closed; use + copyClosure() for that. */ +void copyPaths(ref<Store> srcStore, ref<Store> dstStore, const PathSet & storePaths, + RepairFlag repair = NoRepair, + CheckSigsFlag checkSigs = CheckSigs, + SubstituteFlag substitute = NoSubstitute); /* Copy the closure of the specified paths from one store to another. */ void copyClosure(ref<Store> srcStore, ref<Store> dstStore, - const PathSet & storePaths, bool repair = false, bool dontCheckSigs = false); + const PathSet & storePaths, + RepairFlag repair = NoRepair, + CheckSigsFlag checkSigs = CheckSigs, + SubstituteFlag substitute = NoSubstitute); /* Remove the temporary roots file for this process. Any temporary @@ -694,9 +718,6 @@ ref<Store> openStore(const std::string & uri = getEnv("NIX_REMOTE"), const Store::Params & extraParams = Store::Params()); -void copyPaths(ref<Store> from, ref<Store> to, const PathSet & storePaths, - bool substitute = false, bool dontCheckSigs = false); - enum StoreType { tDaemon, tLocal, diff --git a/src/libutil/hash.cc b/src/libutil/hash.cc index e6f8aa85c6d8..817ddc0b8bc9 100644 --- a/src/libutil/hash.cc +++ b/src/libutil/hash.cc @@ -16,17 +16,8 @@ namespace nix { -Hash::Hash() +void Hash::init() { - type = htUnknown; - hashSize = 0; - memset(hash, 0, maxHashSize); -} - - -Hash::Hash(HashType type) -{ - this->type = type; if (type == htMD5) hashSize = md5HashSize; else if (type == htSHA1) hashSize = sha1HashSize; else if (type == htSHA256) hashSize = sha256HashSize; @@ -64,16 +55,10 @@ bool Hash::operator < (const Hash & h) const } -std::string Hash::to_string(bool base32) const -{ - return printHashType(type) + ":" + (base32 ? printHash32(*this) : printHash(*this)); -} - - const string base16Chars = "0123456789abcdef"; -string printHash(const Hash & hash) +static string printHash16(const Hash & hash) { char buf[hash.hashSize * 2]; for (unsigned int i = 0; i < hash.hashSize; i++) { @@ -84,42 +69,11 @@ string printHash(const Hash & hash) } -Hash parseHash(const string & s) -{ - string::size_type colon = s.find(':'); - if (colon == string::npos) - throw BadHash(format("invalid hash ‘%s’") % s); - string hts = string(s, 0, colon); - HashType ht = parseHashType(hts); - if (ht == htUnknown) - throw BadHash(format("unknown hash type ‘%s’") % hts); - return parseHash16or32(ht, string(s, colon + 1)); -} - - -Hash parseHash(HashType ht, const string & s) -{ - Hash hash(ht); - if (s.length() != hash.hashSize * 2) - throw BadHash(format("invalid hash ‘%1%’") % s); - for (unsigned int i = 0; i < hash.hashSize; i++) { - string s2(s, i * 2, 2); - if (!isxdigit(s2[0]) || !isxdigit(s2[1])) - throw BadHash(format("invalid hash ‘%1%’") % s); - istringstream_nocopy str(s2); - int n; - str >> std::hex >> n; - hash.hash[i] = n; - } - return hash; -} - - // omitted: E O U T const string base32Chars = "0123456789abcdfghijklmnpqrsvwxyz"; -string printHash32(const Hash & hash) +static string printHash32(const Hash & hash) { assert(hash.hashSize); size_t len = hash.base32Len(); @@ -144,66 +98,103 @@ string printHash32(const Hash & hash) string printHash16or32(const Hash & hash) { - return hash.type == htMD5 ? printHash(hash) : printHash32(hash); + return hash.to_string(hash.type == htMD5 ? Base16 : Base32); } -Hash parseHash32(HashType ht, const string & s) +std::string Hash::to_string(Base base, bool includeType) const { - Hash hash(ht); - size_t len = hash.base32Len(); - assert(s.size() == len); - - for (unsigned int n = 0; n < len; ++n) { - char c = s[len - n - 1]; - unsigned char digit; - for (digit = 0; digit < base32Chars.size(); ++digit) /* !!! slow */ - if (base32Chars[digit] == c) break; - if (digit >= 32) - throw BadHash(format("invalid base-32 hash ‘%1%’") % s); - unsigned int b = n * 5; - unsigned int i = b / 8; - unsigned int j = b % 8; - hash.hash[i] |= digit << j; - - if (i < hash.hashSize - 1) { - hash.hash[i + 1] |= digit >> (8 - j); - } else { - if (digit >> (8 - j)) - throw BadHash(format("invalid base-32 hash ‘%1%’") % s); - } + std::string s; + if (includeType) { + s += printHashType(type); + s += ':'; } - - return hash; + switch (base) { + case Base16: + s += printHash16(*this); + break; + case Base32: + s += printHash32(*this); + break; + case Base64: + s += base64Encode(std::string((const char *) hash, hashSize)); + break; + } + return s; } -Hash parseHash16or32(HashType ht, const string & s) +Hash::Hash(const std::string & s, HashType type) + : type(type) { - Hash hash(ht); - if (s.size() == hash.hashSize * 2) - /* hexadecimal representation */ - hash = parseHash(ht, s); - else if (s.size() == hash.base32Len()) - /* base-32 representation */ - hash = parseHash32(ht, s); - else - throw BadHash(format("hash ‘%1%’ has wrong length for hash type ‘%2%’") - % s % printHashType(ht)); - return hash; -} + auto colon = s.find(':'); + + size_t pos = 0; + + if (colon == string::npos) { + if (type == htUnknown) + throw BadHash("hash ‘%s’ does not include a type", s); + } else { + string hts = string(s, 0, colon); + this->type = parseHashType(hts); + if (this->type == htUnknown) + throw BadHash("unknown hash type ‘%s’", hts); + if (type != htUnknown && type != this->type) + throw BadHash("hash ‘%s’ should have type ‘%s’", s, printHashType(type)); + pos = colon + 1; + } + init(); -bool isHash(const string & s) -{ - if (s.length() != 32) return false; - for (int i = 0; i < 32; i++) { - char c = s[i]; - if (!((c >= '0' && c <= '9') || - (c >= 'a' && c <= 'f'))) - return false; + size_t size = s.size() - pos; + + if (size == base16Len()) { + + auto parseHexDigit = [&](char c) { + if (c >= '0' && c <= '9') return c - '0'; + if (c >= 'A' && c <= 'F') return c - 'A' + 10; + if (c >= 'a' && c <= 'f') return c - 'a' + 10; + throw BadHash("invalid base-16 hash ‘%s’", s); + }; + + for (unsigned int i = 0; i < hashSize; i++) { + hash[i] = + parseHexDigit(s[pos + i * 2]) << 4 + | parseHexDigit(s[pos + i * 2 + 1]); + } } - return true; + + else if (size == base32Len()) { + + for (unsigned int n = 0; n < size; ++n) { + char c = s[pos + size - n - 1]; + unsigned char digit; + for (digit = 0; digit < base32Chars.size(); ++digit) /* !!! slow */ + if (base32Chars[digit] == c) break; + if (digit >= 32) + throw BadHash("invalid base-32 hash ‘%s’", s); + unsigned int b = n * 5; + unsigned int i = b / 8; + unsigned int j = b % 8; + hash[i] |= digit << j; + + if (i < hashSize - 1) { + hash[i + 1] |= digit >> (8 - j); + } else { + if (digit >> (8 - j)) + throw BadHash("invalid base-32 hash ‘%s’", s); + } + } + } + + else if (size == base64Len()) { + auto d = base64Decode(std::string(s, pos)); + assert(d.size() == hashSize); + memcpy(hash, d.data(), hashSize); + } + + else + throw BadHash("hash ‘%s’ has wrong length for hash type ‘%s’", s, printHashType(type)); } diff --git a/src/libutil/hash.hh b/src/libutil/hash.hh index 02e213fc7b35..b8b432256c97 100644 --- a/src/libutil/hash.hh +++ b/src/libutil/hash.hh @@ -20,20 +20,30 @@ const int sha512HashSize = 64; extern const string base32Chars; +enum Base : int { Base64, Base32, Base16 }; + struct Hash { static const unsigned int maxHashSize = 64; - unsigned int hashSize; - unsigned char hash[maxHashSize]; + unsigned int hashSize = 0; + unsigned char hash[maxHashSize] = {}; - HashType type; + HashType type = htUnknown; /* Create an unset hash object. */ - Hash(); + Hash() { }; /* Create a zero-filled hash object. */ - Hash(HashType type); + Hash(HashType type) : type(type) { init(); }; + + /* Initialize the hash from a string representation, in the format + "[<type>:]<base16|base32|base64>". If the ‘type’ argument is + htUnknown, then the hash type must be specified in the + string. */ + Hash(const std::string & s, HashType type = htUnknown); + + void init(); /* Check whether a hash is set. */ operator bool () const { return type != htUnknown; } @@ -59,33 +69,22 @@ struct Hash return (hashSize * 8 - 1) / 5 + 1; } - std::string to_string(bool base32 = true) const; -}; - - -/* Convert a hash to a hexadecimal representation. */ -string printHash(const Hash & hash); - -Hash parseHash(const string & s); + /* Returns the length of a base-64 representation of this hash. */ + size_t base64Len() const + { + return ((4 * hashSize / 3) + 3) & ~3; + } -/* Parse a hexadecimal representation of a hash code. */ -Hash parseHash(HashType ht, const string & s); + /* Return a string representation of the hash, in base-16, base-32 + or base-64. By default, this is prefixed by the hash type + (e.g. "sha256:"). */ + std::string to_string(Base base = Base32, bool includeType = true) const; +}; -/* Convert a hash to a base-32 representation. */ -string printHash32(const Hash & hash); /* Print a hash in base-16 if it's MD5, or base-32 otherwise. */ string printHash16or32(const Hash & hash); -/* Parse a base-32 representation of a hash code. */ -Hash parseHash32(HashType ht, const string & s); - -/* Parse a base-16 or base-32 representation of a hash code. */ -Hash parseHash16or32(HashType ht, const string & s); - -/* Verify that the given string is a valid hash code. */ -bool isHash(const string & s); - /* Compute the hash of the given string. */ Hash hashString(HashType ht, const string & s); diff --git a/src/libutil/thread-pool.hh b/src/libutil/thread-pool.hh index b64dc52d473e..361a9d33a732 100644 --- a/src/libutil/thread-pool.hh +++ b/src/libutil/thread-pool.hh @@ -70,50 +70,69 @@ void processGraph( struct Graph { std::set<T> left; std::map<T, std::set<T>> refs, rrefs; - std::function<void(T)> wrap; }; - ref<Sync<Graph>> graph_ = make_ref<Sync<Graph>>(); + Sync<Graph> graph_(Graph{nodes, {}, {}}); - auto wrapWork = [&pool, graph_, processNode](const T & node) { + std::function<void(const T &)> worker; + + worker = [&](const T & node) { + + { + auto graph(graph_.lock()); + auto i = graph->refs.find(node); + if (i == graph->refs.end()) + goto getRefs; + goto doWork; + } + + getRefs: + { + auto refs = getEdges(node); + refs.erase(node); + + { + auto graph(graph_.lock()); + for (auto & ref : refs) + if (graph->left.count(ref)) { + graph->refs[node].insert(ref); + graph->rrefs[ref].insert(node); + } + if (graph->refs[node].empty()) + goto doWork; + } + } + + return; + + doWork: processNode(node); - /* Enqueue work for all nodes that were waiting on this one. */ + /* Enqueue work for all nodes that were waiting on this one + and have no unprocessed dependencies. */ { - auto graph(graph_->lock()); - graph->left.erase(node); + auto graph(graph_.lock()); for (auto & rref : graph->rrefs[node]) { auto & refs(graph->refs[rref]); auto i = refs.find(node); assert(i != refs.end()); refs.erase(i); if (refs.empty()) - pool.enqueue(std::bind(graph->wrap, rref)); + pool.enqueue(std::bind(worker, rref)); } + graph->left.erase(node); + graph->refs.erase(node); + graph->rrefs.erase(node); } }; - { - auto graph(graph_->lock()); - graph->left = nodes; - graph->wrap = wrapWork; - } - - /* Build the dependency graph; enqueue all nodes with no - dependencies. */ - for (auto & node : nodes) { - auto refs = getEdges(node); - { - auto graph(graph_->lock()); - for (auto & ref : refs) - if (ref != node && graph->left.count(ref)) { - graph->refs[node].insert(ref); - graph->rrefs[ref].insert(node); - } - if (graph->refs[node].empty()) - pool.enqueue(std::bind(graph->wrap, node)); - } - } + for (auto & node : nodes) + pool.enqueue(std::bind(worker, std::ref(node))); + + pool.process(); + + if (!graph_.lock()->left.empty()) + throw Error("graph processing incomplete (cyclic reference?)"); } } diff --git a/src/nix-build/nix-build.cc b/src/nix-build/nix-build.cc index 42d68fdfdd70..dc80dd6a583e 100755 --- a/src/nix-build/nix-build.cc +++ b/src/nix-build/nix-build.cc @@ -390,6 +390,8 @@ int main(int argc, char ** argv) maybePrintExecError(e); } + if (dryRun) return; + // Set the environment. auto env = getEnv(); diff --git a/src/nix-copy-closure/nix-copy-closure.cc b/src/nix-copy-closure/nix-copy-closure.cc index dc324abcb3ba..0c69bd413569 100755 --- a/src/nix-copy-closure/nix-copy-closure.cc +++ b/src/nix-copy-closure/nix-copy-closure.cc @@ -12,7 +12,7 @@ int main(int argc, char ** argv) auto toMode = true; auto includeOutputs = false; auto dryRun = false; - auto useSubstitutes = false; + auto useSubstitutes = NoSubstitute; std::string sshHost; PathSet storePaths; @@ -36,7 +36,7 @@ int main(int argc, char ** argv) else if (*arg == "--dry-run") dryRun = true; else if (*arg == "--use-substitutes" || *arg == "-s") - useSubstitutes = true; + useSubstitutes = Substitute; else if (sshHost.empty()) sshHost = *arg; else @@ -58,6 +58,6 @@ int main(int argc, char ** argv) PathSet closure; from->computeFSClosure(storePaths2, closure, false, includeOutputs); - copyPaths(from, to, closure, useSubstitutes, true); + copyPaths(from, to, closure, NoRepair, NoCheckSigs, useSubstitutes); }); } diff --git a/src/nix-daemon/nix-daemon.cc b/src/nix-daemon/nix-daemon.cc index 44127635ded8..b029b92db15c 100644 --- a/src/nix-daemon/nix-daemon.cc +++ b/src/nix-daemon/nix-daemon.cc @@ -216,7 +216,7 @@ static void performOp(ref<LocalStore> store, bool trusted, unsigned int clientVe startWork(); auto hash = store->queryPathInfo(path)->narHash; stopWork(); - to << printHash(hash); + to << hash.to_string(Base16, false); break; } @@ -304,7 +304,7 @@ static void performOp(ref<LocalStore> store, bool trusted, unsigned int clientVe string s = readString(from); PathSet refs = readStorePaths<PathSet>(*store, from); startWork(); - Path path = store->addTextToStore(suffix, s, refs, false); + Path path = store->addTextToStore(suffix, s, refs, NoRepair); stopWork(); to << path; break; @@ -324,7 +324,8 @@ static void performOp(ref<LocalStore> store, bool trusted, unsigned int clientVe case wopImportPaths: { startWork(); TunnelSource source(from); - Paths paths = store->importPaths(source, 0, trusted); + Paths paths = store->importPaths(source, nullptr, + trusted ? NoCheckSigs : CheckSigs); stopWork(); to << paths; break; @@ -549,7 +550,7 @@ static void performOp(ref<LocalStore> store, bool trusted, unsigned int clientVe if (info) { if (GET_PROTOCOL_MINOR(clientVersion) >= 17) to << 1; - to << info->deriver << printHash(info->narHash) << info->references + to << info->deriver << info->narHash.to_string(Base16, false) << info->references << info->registrationTime << info->narSize; if (GET_PROTOCOL_MINOR(clientVersion) >= 16) { to << info->ultimate @@ -576,7 +577,7 @@ static void performOp(ref<LocalStore> store, bool trusted, unsigned int clientVe startWork(); if (repair && !trusted) throw Error("you are not privileged to repair paths"); - bool errors = store->verifyStore(checkContents, repair); + bool errors = store->verifyStore(checkContents, (RepairFlag) repair); stopWork(); to << errors; break; @@ -609,7 +610,7 @@ static void performOp(ref<LocalStore> store, bool trusted, unsigned int clientVe from >> info.deriver; if (!info.deriver.empty()) store->assertStorePath(info.deriver); - info.narHash = parseHash(htSHA256, readString(from)); + info.narHash = Hash(readString(from), htSHA256); info.references = readStorePaths<PathSet>(*store, from); from >> info.registrationTime >> info.narSize >> info.ultimate; info.sigs = readStrings<StringSet>(from); @@ -623,7 +624,8 @@ static void performOp(ref<LocalStore> store, bool trusted, unsigned int clientVe parseDump(tee, tee.source); startWork(); - store->addToStore(info, tee.source.data, repair, dontCheckSigs, nullptr); + store->addToStore(info, tee.source.data, (RepairFlag) repair, + dontCheckSigs ? NoCheckSigs : CheckSigs, nullptr); stopWork(); break; } diff --git a/src/nix-env/nix-env.cc b/src/nix-env/nix-env.cc index 464bcee4a848..10100d6a6019 100644 --- a/src/nix-env/nix-env.cc +++ b/src/nix-env/nix-env.cc @@ -1310,7 +1310,7 @@ int main(int argc, char * * argv) Strings opFlags, opArgs, searchPath; std::map<string, string> autoArgs_; Operation op = 0; - bool repair = false; + RepairFlag repair = NoRepair; string file; Globals globals; @@ -1372,7 +1372,7 @@ int main(int argc, char * * argv) else if (*arg == "--prebuilt-only" || *arg == "-b") globals.prebuiltOnly = true; else if (*arg == "--repair") - repair = true; + repair = Repair; else if (*arg != "" && arg->at(0) == '-') { opFlags.push_back(*arg); /* FIXME: hacky */ diff --git a/src/nix-instantiate/nix-instantiate.cc b/src/nix-instantiate/nix-instantiate.cc index 25f0b1bd692e..a5d12c1466f7 100644 --- a/src/nix-instantiate/nix-instantiate.cc +++ b/src/nix-instantiate/nix-instantiate.cc @@ -108,7 +108,7 @@ int main(int argc, char * * argv) Strings attrPaths; bool wantsReadWrite = false; std::map<string, string> autoArgs_; - bool repair = false; + RepairFlag repair = NoRepair; parseCmdLine(argc, argv, [&](Strings::iterator & arg, const Strings::iterator & end) { if (*arg == "--help") @@ -146,7 +146,7 @@ int main(int argc, char * * argv) else if (*arg == "--strict") strict = true; else if (*arg == "--repair") - repair = true; + repair = Repair; else if (*arg == "--dry-run") settings.readOnlyMode = true; else if (*arg != "" && arg->at(0) == '-') diff --git a/src/nix-prefetch-url/nix-prefetch-url.cc b/src/nix-prefetch-url/nix-prefetch-url.cc index b3b2fcac7132..47e66eaa6513 100644 --- a/src/nix-prefetch-url/nix-prefetch-url.cc +++ b/src/nix-prefetch-url/nix-prefetch-url.cc @@ -145,7 +145,7 @@ int main(int argc, char * * argv) Hash hash, expectedHash(ht); Path storePath; if (args.size() == 2) { - expectedHash = parseHash16or32(ht, args[1]); + expectedHash = Hash(args[1], ht); storePath = store->makeFixedOutputPath(unpack, expectedHash, name); if (store->isValidPath(storePath)) hash = expectedHash; diff --git a/src/nix-store/nix-store.cc b/src/nix-store/nix-store.cc index 950222812e2b..6cea57a76714 100644 --- a/src/nix-store/nix-store.cc +++ b/src/nix-store/nix-store.cc @@ -212,7 +212,7 @@ static void opPrintFixedPath(Strings opFlags, Strings opArgs) string name = *i++; cout << format("%1%\n") % - store->makeFixedOutputPath(recursive, parseHash16or32(hashAlgo, hash), name); + store->makeFixedOutputPath(recursive, Hash(hash, hashAlgo), name); } @@ -380,9 +380,9 @@ static void opQuery(Strings opFlags, Strings opArgs) auto info = store->queryPathInfo(j); if (query == qHash) { assert(info->narHash.type == htSHA256); - cout << format("sha256:%1%\n") % printHash32(info->narHash); + cout << fmt("%s\n", info->narHash.to_string(Base32)); } else if (query == qSize) - cout << format("%1%\n") % info->narSize; + cout << fmt("%d\n", info->narSize); } } break; @@ -677,7 +677,7 @@ static void opImport(Strings opFlags, Strings opArgs) if (!opArgs.empty()) throw UsageError("no arguments expected"); FdSource source(STDIN_FILENO); - Paths paths = store->importPaths(source, nullptr, true); + Paths paths = store->importPaths(source, nullptr, NoCheckSigs); for (auto & i : paths) cout << format("%1%\n") % i << std::flush; @@ -702,11 +702,11 @@ static void opVerify(Strings opFlags, Strings opArgs) throw UsageError("no arguments expected"); bool checkContents = false; - bool repair = false; + RepairFlag repair = NoRepair; for (auto & i : opFlags) if (i == "--check-contents") checkContents = true; - else if (i == "--repair") repair = true; + else if (i == "--repair") repair = Repair; else throw UsageError(format("unknown flag ‘%1%’") % i); if (store->verifyStore(checkContents, repair)) { @@ -734,7 +734,7 @@ static void opVerifyPath(Strings opFlags, Strings opArgs) if (current.first != info->narHash) { printError( format("path ‘%1%’ was modified! expected hash ‘%2%’, got ‘%3%’") - % path % printHash(info->narHash) % printHash(current.first)); + % path % info->narHash.to_string() % current.first.to_string()); status = 1; } } @@ -871,7 +871,7 @@ static void opServe(Strings opFlags, Strings opArgs) case cmdImportPaths: { if (!writeAllowed) throw Error("importing paths is not allowed"); - store->importPaths(in, 0, true); // FIXME: should we skip sig checking? + store->importPaths(in, nullptr, NoCheckSigs); // FIXME: should we skip sig checking? out << 1; // indicate success break; } diff --git a/src/nix/hash.cc b/src/nix/hash.cc index 5dd891e8add3..98de88971127 100644 --- a/src/nix/hash.cc +++ b/src/nix/hash.cc @@ -9,15 +9,16 @@ struct CmdHash : Command { enum Mode { mFile, mPath }; Mode mode; - bool base32 = false; + Base base = Base16; bool truncate = false; HashType ht = htSHA512; Strings paths; CmdHash(Mode mode) : mode(mode) { - mkFlag(0, "base32", "print hash in base-32", &base32); - mkFlag(0, "base16", "print hash in base-16", &base32, false); + mkFlag(0, "base64", "print hash in base-64", &base, Base64); + mkFlag(0, "base32", "print hash in base-32 (Nix-specific)", &base, Base32); + mkFlag(0, "base16", "print hash in base-16", &base, Base16); mkHashTypeFlag("type", &ht); expectArgs("paths", &paths); } @@ -40,7 +41,7 @@ struct CmdHash : Command Hash h = mode == mFile ? hashFile(ht, path) : hashPath(ht, path).first; if (truncate && h.hashSize > 20) h = compressHash(h, 20); std::cout << format("%1%\n") % - (base32 ? printHash32(h) : printHash(h)); + h.to_string(base, false); } } }; @@ -50,11 +51,11 @@ static RegisterCommand r2(make_ref<CmdHash>(CmdHash::mPath)); struct CmdToBase : Command { - bool toBase32; + Base base; HashType ht = htSHA512; Strings args; - CmdToBase(bool toBase32) : toBase32(toBase32) + CmdToBase(Base base) : base(base) { mkHashTypeFlag("type", &ht); expectArgs("strings", &args); @@ -62,28 +63,29 @@ struct CmdToBase : Command std::string name() override { - return toBase32 ? "to-base32" : "to-base16"; + return + base == Base16 ? "to-base16" : + base == Base32 ? "to-base32" : + "to-base64"; } std::string description() override { - return toBase32 - ? "convert a hash to base-32 representation" - : "convert a hash to base-16 representation"; + return fmt("convert a hash to base-%d representation", + base == Base16 ? 16 : + base == Base32 ? 32 : 64); } void run() override { - for (auto s : args) { - Hash h = parseHash16or32(ht, s); - std::cout << format("%1%\n") % - (toBase32 ? printHash32(h) : printHash(h)); - } + for (auto s : args) + std::cout << fmt("%s\n", Hash(s, ht).to_string(base, false)); } }; -static RegisterCommand r3(make_ref<CmdToBase>(false)); -static RegisterCommand r4(make_ref<CmdToBase>(true)); +static RegisterCommand r3(make_ref<CmdToBase>(Base16)); +static RegisterCommand r4(make_ref<CmdToBase>(Base32)); +static RegisterCommand r5(make_ref<CmdToBase>(Base64)); /* Legacy nix-hash command. */ static int compatNixHash(int argc, char * * argv) @@ -121,14 +123,14 @@ static int compatNixHash(int argc, char * * argv) if (op == opHash) { CmdHash cmd(flat ? CmdHash::mFile : CmdHash::mPath); cmd.ht = ht; - cmd.base32 = base32; + cmd.base = base32 ? Base32 : Base16; cmd.truncate = truncate; cmd.paths = ss; cmd.run(); } else { - CmdToBase cmd(op == opTo32); + CmdToBase cmd(op == opTo32 ? Base32 : Base16); cmd.args = ss; cmd.ht = ht; cmd.run(); diff --git a/src/nix/installables.cc b/src/nix/installables.cc index f23308b9bc30..9982ff75f4f4 100644 --- a/src/nix/installables.cc +++ b/src/nix/installables.cc @@ -189,7 +189,10 @@ std::vector<std::shared_ptr<Installable>> InstallablesCommand::parseInstallables for (auto & s : ss) { - if (s.find("/") != std::string::npos) { + if (s.compare(0, 1, "(") == 0) + result.push_back(std::make_shared<InstallableExpr>(*this, s)); + + else if (s.find("/") != std::string::npos) { auto path = store->toStorePath(store->followLinksToStore(s)); @@ -201,9 +204,6 @@ std::vector<std::shared_ptr<Installable>> InstallablesCommand::parseInstallables } } - else if (s.compare(0, 1, "(") == 0) - result.push_back(std::make_shared<InstallableExpr>(*this, s)); - else if (s == "" || std::regex_match(s, attrPathRegex)) result.push_back(std::make_shared<InstallableAttrPath>(*this, s)); diff --git a/src/nix/verify.cc b/src/nix/verify.cc index 18533e6066cd..973f60a74ffe 100644 --- a/src/nix/verify.cc +++ b/src/nix/verify.cc @@ -94,7 +94,7 @@ struct CmdVerify : StorePathsCommand corrupted = 1; printError( format("path ‘%s’ was modified! expected hash ‘%s’, got ‘%s’") - % info->path % printHash(info->narHash) % printHash(hash.first)); + % info->path % info->narHash.to_string() % hash.first.to_string()); } } diff --git a/tests/common.sh.in b/tests/common.sh.in index 6c3804a257a7..a8d56707e27d 100644 --- a/tests/common.sh.in +++ b/tests/common.sh.in @@ -1,7 +1,5 @@ set -e -datadir="@datadir@" - export TEST_ROOT=$(realpath ${TMPDIR:-/tmp}/nix-test) export NIX_STORE_DIR if ! NIX_STORE_DIR=$(readlink -f $TEST_ROOT/store 2> /dev/null); then diff --git a/tests/fetchurl.sh b/tests/fetchurl.sh index b6fa3a27edd8..808f460258df 100644 --- a/tests/fetchurl.sh +++ b/tests/fetchurl.sh @@ -9,6 +9,15 @@ outPath=$(nix-build '<nix/fetchurl.nix>' --argstr url file://$(pwd)/fetchurl.sh cmp $outPath fetchurl.sh +# Now using a base-64 hash. +clearStore + +hash=$(nix hash-file --type sha512 --base64 ./fetchurl.sh) + +outPath=$(nix-build '<nix/fetchurl.nix>' --argstr url file://$(pwd)/fetchurl.sh --argstr sha512 $hash --no-out-link) + +cmp $outPath fetchurl.sh + # Test unpacking a NAR. rm -rf $TEST_ROOT/archive mkdir -p $TEST_ROOT/archive diff --git a/tests/hash.sh b/tests/hash.sh index a95c68683f84..9f234bc635b0 100644 --- a/tests/hash.sh +++ b/tests/hash.sh @@ -63,11 +63,15 @@ try2 md5 "f78b733a68f5edbdf9413899339eaa4a" # Conversion. try3() { + h64=$(nix to-base64 --type "$1" "$2") + [ "$h64" = "$4" ] h32=$(nix-hash --type "$1" --to-base32 "$2") [ "$h32" = "$3" ] h16=$(nix-hash --type "$1" --to-base16 "$h32") [ "$h16" = "$2" ] + h16=$(nix to-base16 --type "$1" "$h64") + [ "$h16" = "$2" ] } -try3 sha1 "800d59cfcd3c05e900cb4e214be48f6b886a08df" "vw46m23bizj4n8afrc0fj19wrp7mj3c0" -try3 sha256 "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad" "1b8m03r63zqhnjf7l5wnldhh7c134ap5vpj0850ymkq1iyzicy5s" -try3 sha512 "204a8fc6dda82f0a0ced7beb8e08a41657c16ef468b228a8279be331a703c33596fd15c13b1b07f9aa1d3bea57789ca031ad85c7a71dd70354ec631238ca3445" "12k9jiq29iyqm03swfsgiw5mlqs173qazm3n7daz43infy12pyrcdf30fkk3qwv4yl2ick8yipc2mqnlh48xsvvxl60lbx8vp38yji0" +try3 sha1 "800d59cfcd3c05e900cb4e214be48f6b886a08df" "vw46m23bizj4n8afrc0fj19wrp7mj3c0" "gA1Zz808BekAy04hS+SPa4hqCN8=" +try3 sha256 "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad" "1b8m03r63zqhnjf7l5wnldhh7c134ap5vpj0850ymkq1iyzicy5s" "ungWv48Bz+pBQUDeXa4iI7ADYaOWF3qctBD/YfIAFa0=" +try3 sha512 "204a8fc6dda82f0a0ced7beb8e08a41657c16ef468b228a8279be331a703c33596fd15c13b1b07f9aa1d3bea57789ca031ad85c7a71dd70354ec631238ca3445" "12k9jiq29iyqm03swfsgiw5mlqs173qazm3n7daz43infy12pyrcdf30fkk3qwv4yl2ick8yipc2mqnlh48xsvvxl60lbx8vp38yji0" "IEqPxt2oLwoM7XvrjgikFlfBbvRosiioJ5vjMacDwzWW/RXBOxsH+aodO+pXeJygMa2Fx6cd1wNU7GMSOMo0RQ==" |