From c0015e87af70f539f24d2aa2bc224a9d8b84276b Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 4 Jul 2017 14:47:59 +0200 Subject: Support base-64 hashes Also simplify the Hash API. Fixes #1437. --- src/libexpr/primops.cc | 8 +- src/libstore/binary-cache-store.cc | 2 +- src/libstore/build.cc | 2 +- src/libstore/derivations.cc | 6 +- src/libstore/download.cc | 2 +- src/libstore/export-import.cc | 2 +- src/libstore/gc.cc | 2 +- src/libstore/local-store.cc | 26 ++--- src/libstore/nar-info-disk-cache.cc | 4 +- src/libstore/nar-info.cc | 6 +- src/libstore/optimise-store.cc | 4 +- src/libstore/remote-store.cc | 4 +- src/libstore/store-api.cc | 17 ++- src/libutil/hash.cc | 185 +++++++++++++++---------------- src/libutil/hash.hh | 51 +++++---- src/nix-daemon/nix-daemon.cc | 6 +- src/nix-prefetch-url/nix-prefetch-url.cc | 2 +- src/nix-store/nix-store.cc | 8 +- src/nix/hash.cc | 40 +++---- src/nix/verify.cc | 2 +- 20 files changed, 180 insertions(+), 199 deletions(-) (limited to 'src') diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 99ffddaeb80c..b753d84e2e69 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); } @@ -1852,7 +1852,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 8ce5f5bbc7c9..8147345c2e1c 100644 --- a/src/libstore/binary-cache-store.cc +++ b/src/libstore/binary-cache-store.cc @@ -239,7 +239,7 @@ void BinaryCacheStore::addToStore(const ValidPathInfo & info, const refurl = "nar/" + printHash32(narInfo->fileHash) + ".nar" + narInfo->url = "nar/" + narInfo->fileHash.to_string(Base32, false) + ".nar" + (compression == "xz" ? ".xz" : compression == "bzip2" ? ".bz2" : compression == "br" ? ".br" : diff --git a/src/libstore/build.cc b/src/libstore/build.cc index 6c740d99c585..f40a8c5498ee 100644 --- a/src/libstore/build.cc +++ b/src/libstore/build.cc @@ -3236,7 +3236,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()); diff --git a/src/libstore/derivations.cc b/src/libstore/derivations.cc index bb7b8fe628a2..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); } @@ -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/download.cc b/src/libstore/download.cc index 4f3bf2d14f1f..15eb68c69ea4 100644 --- a/src/libstore/download.cc +++ b/src/libstore/download.cc @@ -581,7 +581,7 @@ Path Downloader::downloadCached(ref 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"; diff --git a/src/libstore/export-import.cc b/src/libstore/export-import.cc index 1b3a43df32d5..2cbcedc6fb00 100644 --- a/src/libstore/export-import.cc +++ b/src/libstore/export-import.cc @@ -56,7 +56,7 @@ 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; } 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/local-store.cc b/src/libstore/local-store.cc index a7a94a8b9e84..7c41dfca7f31 100644 --- a/src/libstore/local-store.cc +++ b/src/libstore/local-store.cc @@ -572,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) @@ -614,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)> success, std::function failure) @@ -650,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); @@ -685,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()) @@ -1211,7 +1201,7 @@ bool LocalStore::verifyStore(bool checkContents, RepairFlag 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 { 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(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..adaf313131f4 100644 --- a/src/libstore/optimise-store.cc +++ b/src/libstore/optimise-store.cc @@ -135,10 +135,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 7337e406d2e7..ab726e79534a 100644 --- a/src/libstore/remote-store.cc +++ b/src/libstore/remote-store.cc @@ -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(*this, conn->from); conn->from >> info->registrationTime >> info->narSize; if (GET_PROTOCOL_MINOR(conn->daemonVersion) >= 16) { @@ -387,7 +387,7 @@ void RemoteStore::addToStore(const ValidPathInfo & info, const ref 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 << !checkSigs; diff --git a/src/libstore/store-api.cc b/src/libstore/store-api.cc index 39b9466162fe..d7b784cfbc20 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); } @@ -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(); } @@ -613,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"); } @@ -648,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); } @@ -667,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 @@ -676,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 diff --git a/src/libutil/hash.cc b/src/libutil/hash.cc index fa1bb5d97183..6b45ac859d59 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; @@ -62,16 +53,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++) { @@ -82,42 +67,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(); @@ -142,66 +96,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 + "[:]". 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/nix-daemon/nix-daemon.cc b/src/nix-daemon/nix-daemon.cc index c9c167766345..b029b92db15c 100644 --- a/src/nix-daemon/nix-daemon.cc +++ b/src/nix-daemon/nix-daemon.cc @@ -216,7 +216,7 @@ static void performOp(ref store, bool trusted, unsigned int clientVe startWork(); auto hash = store->queryPathInfo(path)->narHash; stopWork(); - to << printHash(hash); + to << hash.to_string(Base16, false); break; } @@ -550,7 +550,7 @@ static void performOp(ref 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 @@ -610,7 +610,7 @@ static void performOp(ref 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(*store, from); from >> info.registrationTime >> info.narSize >> info.ultimate; info.sigs = readStrings(from); 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 314c9423907d..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; @@ -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; } } 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::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(false)); -static RegisterCommand r4(make_ref(true)); +static RegisterCommand r3(make_ref(Base16)); +static RegisterCommand r4(make_ref(Base32)); +static RegisterCommand r5(make_ref(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/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()); } } -- cgit 1.4.1