Support base-64 hashes

Also simplify the Hash API.

Fixes #1437.
This commit is contained in:
Eelco Dolstra 2017-07-04 14:47:59 +02:00
parent fe97c69898
commit c0015e87af
No known key found for this signature in database
GPG key ID: 8170B4726D7198DE
23 changed files with 205 additions and 213 deletions

View file

@ -81,8 +81,7 @@ SV * queryReferences(char * path)
SV * queryPathHash(char * path) SV * queryPathHash(char * path)
PPCODE: PPCODE:
try { try {
auto hash = store()->queryPathInfo(path)->narHash; auto s = store()->queryPathInfo(path)->narHash.to_string();
string s = "sha256:" + printHash32(hash);
XPUSHs(sv_2mortal(newSVpv(s.c_str(), 0))); XPUSHs(sv_2mortal(newSVpv(s.c_str(), 0)));
} catch (Error & e) { } catch (Error & e) {
croak("%s", e.what()); croak("%s", e.what());
@ -108,7 +107,7 @@ SV * queryPathInfo(char * path, int base32)
XPUSHs(&PL_sv_undef); XPUSHs(&PL_sv_undef);
else else
XPUSHs(sv_2mortal(newSVpv(info->deriver.c_str(), 0))); 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))); XPUSHs(sv_2mortal(newSVpv(s.c_str(), 0)));
mXPUSHi(info->registrationTime); mXPUSHi(info->registrationTime);
mXPUSHi(info->narSize); mXPUSHi(info->narSize);
@ -184,7 +183,7 @@ void importPaths(int fd, int dontCheckSigs)
PPCODE: PPCODE:
try { try {
FdSource source(fd); FdSource source(fd);
store()->importPaths(source, 0, dontCheckSigs); store()->importPaths(source, nullptr, dontCheckSigs ? NoCheckSigs : CheckSigs);
} catch (Error & e) { } catch (Error & e) {
croak("%s", e.what()); croak("%s", e.what());
} }
@ -194,7 +193,7 @@ SV * hashPath(char * algo, int base32, char * path)
PPCODE: PPCODE:
try { try {
Hash h = hashPath(parseHashType(algo), path).first; 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))); XPUSHs(sv_2mortal(newSVpv(s.c_str(), 0)));
} catch (Error & e) { } catch (Error & e) {
croak("%s", e.what()); croak("%s", e.what());
@ -205,7 +204,7 @@ SV * hashFile(char * algo, int base32, char * path)
PPCODE: PPCODE:
try { try {
Hash h = hashFile(parseHashType(algo), path); 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))); XPUSHs(sv_2mortal(newSVpv(s.c_str(), 0)));
} catch (Error & e) { } catch (Error & e) {
croak("%s", e.what()); croak("%s", e.what());
@ -216,7 +215,7 @@ SV * hashString(char * algo, int base32, char * s)
PPCODE: PPCODE:
try { try {
Hash h = hashString(parseHashType(algo), s); 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))); XPUSHs(sv_2mortal(newSVpv(s.c_str(), 0)));
} catch (Error & e) { } catch (Error & e) {
croak("%s", e.what()); croak("%s", e.what());
@ -226,8 +225,8 @@ SV * hashString(char * algo, int base32, char * s)
SV * convertHash(char * algo, char * s, int toBase32) SV * convertHash(char * algo, char * s, int toBase32)
PPCODE: PPCODE:
try { try {
Hash h = parseHash16or32(parseHashType(algo), s); Hash h(s, parseHashType(algo));
string s = toBase32 ? printHash32(h) : printHash(h); string s = h.to_string(toBase32 ? Base32 : Base16, false);
XPUSHs(sv_2mortal(newSVpv(s.c_str(), 0))); XPUSHs(sv_2mortal(newSVpv(s.c_str(), 0)));
} catch (Error & e) { } catch (Error & e) {
croak("%s", e.what()); 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) SV * makeFixedOutputPath(int recursive, char * algo, char * hash, char * name)
PPCODE: PPCODE:
try { try {
HashType ht = parseHashType(algo); Hash h(hash, parseHashType(algo));
Hash h = parseHash16or32(ht, hash);
Path path = store()->makeFixedOutputPath(recursive, h, name); Path path = store()->makeFixedOutputPath(recursive, h, name);
XPUSHs(sv_2mortal(newSVpv(path.c_str(), 0))); XPUSHs(sv_2mortal(newSVpv(path.c_str(), 0)));
} catch (Error & e) { } catch (Error & e) {

View file

@ -708,8 +708,8 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
HashType ht = parseHashType(outputHashAlgo); HashType ht = parseHashType(outputHashAlgo);
if (ht == htUnknown) if (ht == htUnknown)
throw EvalError(format("unknown hash algorithm %1%, at %2%") % outputHashAlgo % posDrvName); throw EvalError(format("unknown hash algorithm %1%, at %2%") % outputHashAlgo % posDrvName);
Hash h = parseHash16or32(ht, *outputHash); Hash h(*outputHash, ht);
outputHash = printHash(h); outputHash = h.to_string(Base16, false);
if (outputHashRecursive) outputHashAlgo = "r:" + outputHashAlgo; if (outputHashRecursive) outputHashAlgo = "r:" + outputHashAlgo;
Path outPath = state.store->makeFixedOutputPath(outputHashRecursive, h, drvName); 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 PathSet context; // discarded
string s = state.forceString(*args[1], context, pos); 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") if (n == "url")
url = state.forceStringNoCtx(*attr.value, *attr.pos); url = state.forceStringNoCtx(*attr.value, *attr.pos);
else if (n == "sha256") 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") else if (n == "name")
name = state.forceStringNoCtx(*attr.value, *attr.pos); name = state.forceStringNoCtx(*attr.value, *attr.pos);
else else

View file

@ -239,7 +239,7 @@ void BinaryCacheStore::addToStore(const ValidPathInfo & info, const ref<std::str
% duration); % duration);
/* Atomically write the NAR file. */ /* 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 == "xz" ? ".xz" :
compression == "bzip2" ? ".bz2" : compression == "bzip2" ? ".bz2" :
compression == "br" ? ".br" : compression == "br" ? ".br" :

View file

@ -3236,7 +3236,7 @@ PathSet DerivationGoal::checkPathValidity(bool returnValid, bool checkHash)
Path DerivationGoal::addHashRewrite(const Path & path) Path DerivationGoal::addHashRewrite(const Path & path)
{ {
string h1 = string(path, worker.store.storeDir.size() + 1, 32); 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); Path p = worker.store.storeDir + "/" + h2 + string(path, worker.store.storeDir.size() + 33);
deletePath(p); deletePath(p);
assert(path.size() == p.size()); assert(path.size() == p.size());

View file

@ -23,7 +23,7 @@ void DerivationOutput::parseHashInfo(bool & recursive, Hash & hash) const
if (hashType == htUnknown) if (hashType == htUnknown)
throw Error(format("unknown hash algorithm %1%") % algo); 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); h = hashDerivationModulo(store, drv2);
drvHashes[i.first] = h; drvHashes[i.first] = h;
} }
inputs2[printHash(h)] = i.second; inputs2[h.to_string(Base16, false)] = i.second;
} }
drv.inputDrvs = inputs2; drv.inputDrvs = inputs2;
@ -437,7 +437,7 @@ Sink & operator << (Sink & out, const BasicDerivation & drv)
std::string hashPlaceholder(const std::string & outputName) std::string hashPlaceholder(const std::string & outputName)
{ {
// FIXME: memoize? // FIXME: memoize?
return "/" + printHash32(hashString(htSHA256, "nix-output:" + outputName)); return "/" + hashString(htSHA256, "nix-output:" + outputName).to_string(Base32, false);
} }

View file

@ -581,7 +581,7 @@ Path Downloader::downloadCached(ref<Store> store, const string & url_, bool unpa
Path cacheDir = getCacheDir() + "/nix/tarballs"; Path cacheDir = getCacheDir() + "/nix/tarballs";
createDirs(cacheDir); createDirs(cacheDir);
string urlHash = printHash32(hashString(htSHA256, url)); string urlHash = hashString(htSHA256, url).to_string(Base32, false);
Path dataFile = cacheDir + "/" + urlHash + ".info"; Path dataFile = cacheDir + "/" + urlHash + ".info";
Path fileLink = cacheDir + "/" + urlHash + "-file"; Path fileLink = cacheDir + "/" + urlHash + "-file";

View file

@ -56,7 +56,7 @@ void Store::exportPath(const Path & path, Sink & sink)
Hash hash = hashAndWriteSink.currentHash(); Hash hash = hashAndWriteSink.currentHash();
if (hash != info->narHash && info->narHash != Hash(info->narHash.type)) if (hash != info->narHash && info->narHash != Hash(info->narHash.type))
throw Error(format("hash of path %1% has changed from %2% to %3%!") % path 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; hashAndWriteSink << exportMagic << path << info->references << info->deriver << 0;
} }

View file

@ -76,7 +76,7 @@ void LocalStore::syncWithGC()
void LocalStore::addIndirectRoot(const Path & path) 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%") Path realRoot = canonPath((format("%1%/%2%/auto/%3%")
% stateDir % gcRootsDir % hash).str()); % stateDir % gcRootsDir % hash).str());
makeSymlink(realRoot, path); makeSymlink(realRoot, path);

View file

@ -572,7 +572,7 @@ uint64_t LocalStore::addValidPath(State & state,
state.stmtRegisterValidPath.use() state.stmtRegisterValidPath.use()
(info.path) (info.path)
("sha256:" + printHash(info.narHash)) (info.narHash.to_string(Base16))
(info.registrationTime == 0 ? time(0) : info.registrationTime) (info.registrationTime == 0 ? time(0) : info.registrationTime)
(info.deriver, info.deriver != "") (info.deriver, info.deriver != "")
(info.narSize, info.narSize != 0) (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, void LocalStore::queryPathInfoUncached(const Path & path,
std::function<void(std::shared_ptr<ValidPathInfo>)> success, std::function<void(std::shared_ptr<ValidPathInfo>)> success,
std::function<void(std::exception_ptr exc)> failure) std::function<void(std::exception_ptr exc)> failure)
@ -650,7 +636,11 @@ void LocalStore::queryPathInfoUncached(const Path & path,
info->id = useQueryPathInfo.getInt(0); 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); info->registrationTime = useQueryPathInfo.getInt(2);
@ -685,7 +675,7 @@ void LocalStore::updatePathInfo(State & state, const ValidPathInfo & info)
{ {
state.stmtUpdatePathInfo.use() state.stmtUpdatePathInfo.use()
(info.narSize, info.narSize != 0) (info.narSize, info.narSize != 0)
("sha256:" + printHash(info.narHash)) (info.narHash.to_string(Base16))
(info.ultimate ? 1 : 0, info.ultimate) (info.ultimate ? 1 : 0, info.ultimate)
(concatStringsSep(" ", info.sigs), !info.sigs.empty()) (concatStringsSep(" ", info.sigs), !info.sigs.empty())
(info.ca, !info.ca.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) { if (info->narHash != nullHash && info->narHash != current.first) {
printError(format("path %1% was modified! " printError(format("path %1% was modified! "
"expected hash %2%, got %3%") "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; if (repair) repairPath(i); else errors = true;
} else { } else {

View file

@ -203,9 +203,9 @@ public:
narInfo->url = queryNAR.getStr(3); narInfo->url = queryNAR.getStr(3);
narInfo->compression = queryNAR.getStr(4); narInfo->compression = queryNAR.getStr(4);
if (!queryNAR.isNull(5)) if (!queryNAR.isNull(5))
narInfo->fileHash = parseHash(queryNAR.getStr(5)); narInfo->fileHash = Hash(queryNAR.getStr(5));
narInfo->fileSize = queryNAR.getInt(6); narInfo->fileSize = queryNAR.getInt(6);
narInfo->narHash = parseHash(queryNAR.getStr(7)); narInfo->narHash = Hash(queryNAR.getStr(7));
narInfo->narSize = queryNAR.getInt(8); narInfo->narSize = queryNAR.getInt(8);
for (auto & r : tokenizeString<Strings>(queryNAR.getStr(9), " ")) for (auto & r : tokenizeString<Strings>(queryNAR.getStr(9), " "))
narInfo->references.insert(cache.storeDir + "/" + r); narInfo->references.insert(cache.storeDir + "/" + r);

View file

@ -11,7 +11,7 @@ NarInfo::NarInfo(const Store & store, const std::string & s, const std::string &
auto parseHashField = [&](const string & s) { auto parseHashField = [&](const string & s) {
try { try {
return parseHash(s); return Hash(s);
} catch (BadHash &) { } catch (BadHash &) {
corrupt(); corrupt();
return Hash(); // never reached return Hash(); // never reached
@ -90,10 +90,10 @@ std::string NarInfo::to_string() const
assert(compression != ""); assert(compression != "");
res += "Compression: " + compression + "\n"; res += "Compression: " + compression + "\n";
assert(fileHash.type == htSHA256); assert(fileHash.type == htSHA256);
res += "FileHash: sha256:" + printHash32(fileHash) + "\n"; res += "FileHash: " + fileHash.to_string(Base32) + "\n";
res += "FileSize: " + std::to_string(fileSize) + "\n"; res += "FileSize: " + std::to_string(fileSize) + "\n";
assert(narHash.type == htSHA256); 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 += "NarSize: " + std::to_string(narSize) + "\n";
res += "References: " + concatStringsSep(" ", shortRefs()) + "\n"; res += "References: " + concatStringsSep(" ", shortRefs()) + "\n";

View file

@ -135,10 +135,10 @@ void LocalStore::optimisePath_(OptimiseStats & stats, const Path & path, InodeHa
contents of the symlink (i.e. the result of readlink()), not contents of the symlink (i.e. the result of readlink()), not
the contents of the target (which may not even exist). */ the contents of the target (which may not even exist). */
Hash hash = hashPath(htSHA256, path).first; 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. */ /* Check if this is a known hash. */
Path linkPath = linksDir + "/" + printHash32(hash); Path linkPath = linksDir + "/" + hash.to_string(Base32, false);
retry: retry:
if (!pathExists(linkPath)) { if (!pathExists(linkPath)) {

View file

@ -294,7 +294,7 @@ void RemoteStore::queryPathInfoUncached(const Path & path,
info->path = path; info->path = path;
info->deriver = readString(conn->from); info->deriver = readString(conn->from);
if (info->deriver != "") assertStorePath(info->deriver); 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); info->references = readStorePaths<PathSet>(*this, conn->from);
conn->from >> info->registrationTime >> info->narSize; conn->from >> info->registrationTime >> info->narSize;
if (GET_PROTOCOL_MINOR(conn->daemonVersion) >= 16) { if (GET_PROTOCOL_MINOR(conn->daemonVersion) >= 16) {
@ -387,7 +387,7 @@ void RemoteStore::addToStore(const ValidPathInfo & info, const ref<std::string>
else { else {
conn->to << wopAddToStoreNar 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.references << info.registrationTime << info.narSize
<< info.ultimate << info.sigs << info.ca << info.ultimate << info.sigs << info.ca
<< repair << !checkSigs; << repair << !checkSigs;

View file

@ -176,13 +176,12 @@ Path Store::makeStorePath(const string & type,
const Hash & hash, const string & name) const const Hash & hash, const string & name) const
{ {
/* e.g., "source:sha256:1abc...:/nix/store:foo.tar.gz" */ /* e.g., "source:sha256:1abc...:/nix/store:foo.tar.gz" */
string s = type + ":sha256:" + printHash(hash) + ":" string s = type + ":" + hash.to_string(Base16) + ":" + storeDir + ":" + name;
+ storeDir + ":" + name;
checkStoreName(name); checkStoreName(name);
return storeDir + "/" return storeDir + "/"
+ printHash32(compressHash(hashString(htSHA256, s), 20)) + compressHash(hashString(htSHA256, s), 20).to_string(Base32, false)
+ "-" + name; + "-" + name;
} }
@ -202,7 +201,7 @@ Path Store::makeFixedOutputPath(bool recursive,
? makeStorePath("source", hash, name) ? makeStorePath("source", hash, name)
: makeStorePath("output:out", hashString(htSHA256, : makeStorePath("output:out", hashString(htSHA256,
"fixed:out:" + (recursive ? (string) "r:" : "") + "fixed:out:" + (recursive ? (string) "r:" : "") +
printHashType(hash.type) + ":" + printHash(hash) + ":"), hash.to_string(Base16) + ":"),
name); name);
} }
@ -438,7 +437,7 @@ string Store::makeValidityRegistration(const PathSet & paths,
auto info = queryPathInfo(i); auto info = queryPathInfo(i);
if (showHash) { if (showHash) {
s += printHash(info->narHash) + "\n"; s += info->narHash.to_string(Base16, false) + "\n";
s += (format("%1%\n") % info->narSize).str(); s += (format("%1%\n") % info->narSize).str();
} }
@ -613,7 +612,7 @@ ValidPathInfo decodeValidPathInfo(std::istream & str, bool hashGiven)
if (hashGiven) { if (hashGiven) {
string s; string s;
getline(str, s); getline(str, s);
info.narHash = parseHash(htSHA256, s); info.narHash = Hash(s, htSHA256);
getline(str, s); getline(str, s);
if (!string2Int(s, info.narSize)) throw Error("number expected"); if (!string2Int(s, info.narSize)) throw Error("number expected");
} }
@ -648,7 +647,7 @@ std::string ValidPathInfo::fingerprint() const
% path); % path);
return return
"1;" + path + ";" "1;" + path + ";"
+ printHashType(narHash.type) + ":" + printHash32(narHash) + ";" + narHash.to_string(Base32) + ";"
+ std::to_string(narSize) + ";" + std::to_string(narSize) + ";"
+ concatStringsSep(",", references); + concatStringsSep(",", references);
} }
@ -667,7 +666,7 @@ bool ValidPathInfo::isContentAddressed(const Store & store) const
}; };
if (hasPrefix(ca, "text:")) { 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) if (store.makeTextPath(storePathToName(path), hash, references) == path)
return true; return true;
else else
@ -676,7 +675,7 @@ bool ValidPathInfo::isContentAddressed(const Store & store) const
else if (hasPrefix(ca, "fixed:")) { else if (hasPrefix(ca, "fixed:")) {
bool recursive = ca.compare(6, 2, "r:") == 0; 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) if (store.makeFixedOutputPath(recursive, hash, storePathToName(path)) == path)
return true; return true;
else else

View file

@ -16,17 +16,8 @@
namespace nix { 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; if (type == htMD5) hashSize = md5HashSize;
else if (type == htSHA1) hashSize = sha1HashSize; else if (type == htSHA1) hashSize = sha1HashSize;
else if (type == htSHA256) hashSize = sha256HashSize; 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"; const string base16Chars = "0123456789abcdef";
string printHash(const Hash & hash) static string printHash16(const Hash & hash)
{ {
char buf[hash.hashSize * 2]; char buf[hash.hashSize * 2];
for (unsigned int i = 0; i < hash.hashSize; i++) { 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 // omitted: E O U T
const string base32Chars = "0123456789abcdfghijklmnpqrsvwxyz"; const string base32Chars = "0123456789abcdfghijklmnpqrsvwxyz";
string printHash32(const Hash & hash) static string printHash32(const Hash & hash)
{ {
assert(hash.hashSize); assert(hash.hashSize);
size_t len = hash.base32Len(); size_t len = hash.base32Len();
@ -142,66 +96,103 @@ string printHash32(const Hash & hash)
string printHash16or32(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); std::string s;
size_t len = hash.base32Len(); if (includeType) {
assert(s.size() == len); s += printHashType(type);
s += ':';
}
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;
}
for (unsigned int n = 0; n < len; ++n) {
char c = s[len - n - 1]; Hash::Hash(const std::string & s, HashType type)
: type(type)
{
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();
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]);
}
}
else if (size == base32Len()) {
for (unsigned int n = 0; n < size; ++n) {
char c = s[pos + size - n - 1];
unsigned char digit; unsigned char digit;
for (digit = 0; digit < base32Chars.size(); ++digit) /* !!! slow */ for (digit = 0; digit < base32Chars.size(); ++digit) /* !!! slow */
if (base32Chars[digit] == c) break; if (base32Chars[digit] == c) break;
if (digit >= 32) if (digit >= 32)
throw BadHash(format("invalid base-32 hash %1%") % s); throw BadHash("invalid base-32 hash %s", s);
unsigned int b = n * 5; unsigned int b = n * 5;
unsigned int i = b / 8; unsigned int i = b / 8;
unsigned int j = b % 8; unsigned int j = b % 8;
hash.hash[i] |= digit << j; hash[i] |= digit << j;
if (i < hash.hashSize - 1) { if (i < hashSize - 1) {
hash.hash[i + 1] |= digit >> (8 - j); hash[i + 1] |= digit >> (8 - j);
} else { } else {
if (digit >> (8 - j)) if (digit >> (8 - j))
throw BadHash(format("invalid base-32 hash %1%") % s); throw BadHash("invalid base-32 hash %s", s);
}
} }
} }
return hash; else if (size == base64Len()) {
auto d = base64Decode(std::string(s, pos));
assert(d.size() == hashSize);
memcpy(hash, d.data(), hashSize);
} }
Hash parseHash16or32(HashType ht, const string & s)
{
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 else
throw BadHash(format("hash %1% has wrong length for hash type %2%") throw BadHash("hash %s has wrong length for hash type %s", s, printHashType(type));
% s % printHashType(ht));
return hash;
}
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;
}
return true;
} }

View file

@ -20,20 +20,30 @@ const int sha512HashSize = 64;
extern const string base32Chars; extern const string base32Chars;
enum Base : int { Base64, Base32, Base16 };
struct Hash struct Hash
{ {
static const unsigned int maxHashSize = 64; static const unsigned int maxHashSize = 64;
unsigned int hashSize; unsigned int hashSize = 0;
unsigned char hash[maxHashSize]; unsigned char hash[maxHashSize] = {};
HashType type; HashType type = htUnknown;
/* Create an unset hash object. */ /* Create an unset hash object. */
Hash(); Hash() { };
/* Create a zero-filled hash object. */ /* 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. */ /* Check whether a hash is set. */
operator bool () const { return type != htUnknown; } operator bool () const { return type != htUnknown; }
@ -59,33 +69,22 @@ struct Hash
return (hashSize * 8 - 1) / 5 + 1; return (hashSize * 8 - 1) / 5 + 1;
} }
std::string to_string(bool base32 = true) const; /* Returns the length of a base-64 representation of this hash. */
size_t base64Len() const
{
return ((4 * hashSize / 3) + 3) & ~3;
}
/* 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 hexadecimal representation. */
string printHash(const Hash & hash);
Hash parseHash(const string & s);
/* Parse a hexadecimal representation of a hash code. */
Hash parseHash(HashType ht, const string & s);
/* 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. */ /* Print a hash in base-16 if it's MD5, or base-32 otherwise. */
string printHash16or32(const Hash & hash); 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. */ /* Compute the hash of the given string. */
Hash hashString(HashType ht, const string & s); Hash hashString(HashType ht, const string & s);

View file

@ -216,7 +216,7 @@ static void performOp(ref<LocalStore> store, bool trusted, unsigned int clientVe
startWork(); startWork();
auto hash = store->queryPathInfo(path)->narHash; auto hash = store->queryPathInfo(path)->narHash;
stopWork(); stopWork();
to << printHash(hash); to << hash.to_string(Base16, false);
break; break;
} }
@ -550,7 +550,7 @@ static void performOp(ref<LocalStore> store, bool trusted, unsigned int clientVe
if (info) { if (info) {
if (GET_PROTOCOL_MINOR(clientVersion) >= 17) if (GET_PROTOCOL_MINOR(clientVersion) >= 17)
to << 1; 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; << info->registrationTime << info->narSize;
if (GET_PROTOCOL_MINOR(clientVersion) >= 16) { if (GET_PROTOCOL_MINOR(clientVersion) >= 16) {
to << info->ultimate to << info->ultimate
@ -610,7 +610,7 @@ static void performOp(ref<LocalStore> store, bool trusted, unsigned int clientVe
from >> info.deriver; from >> info.deriver;
if (!info.deriver.empty()) if (!info.deriver.empty())
store->assertStorePath(info.deriver); store->assertStorePath(info.deriver);
info.narHash = parseHash(htSHA256, readString(from)); info.narHash = Hash(readString(from), htSHA256);
info.references = readStorePaths<PathSet>(*store, from); info.references = readStorePaths<PathSet>(*store, from);
from >> info.registrationTime >> info.narSize >> info.ultimate; from >> info.registrationTime >> info.narSize >> info.ultimate;
info.sigs = readStrings<StringSet>(from); info.sigs = readStrings<StringSet>(from);

View file

@ -145,7 +145,7 @@ int main(int argc, char * * argv)
Hash hash, expectedHash(ht); Hash hash, expectedHash(ht);
Path storePath; Path storePath;
if (args.size() == 2) { if (args.size() == 2) {
expectedHash = parseHash16or32(ht, args[1]); expectedHash = Hash(args[1], ht);
storePath = store->makeFixedOutputPath(unpack, expectedHash, name); storePath = store->makeFixedOutputPath(unpack, expectedHash, name);
if (store->isValidPath(storePath)) if (store->isValidPath(storePath))
hash = expectedHash; hash = expectedHash;

View file

@ -212,7 +212,7 @@ static void opPrintFixedPath(Strings opFlags, Strings opArgs)
string name = *i++; string name = *i++;
cout << format("%1%\n") % 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); auto info = store->queryPathInfo(j);
if (query == qHash) { if (query == qHash) {
assert(info->narHash.type == htSHA256); 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) } else if (query == qSize)
cout << format("%1%\n") % info->narSize; cout << fmt("%d\n", info->narSize);
} }
} }
break; break;
@ -734,7 +734,7 @@ static void opVerifyPath(Strings opFlags, Strings opArgs)
if (current.first != info->narHash) { if (current.first != info->narHash) {
printError( printError(
format("path %1% was modified! expected hash %2%, got %3%") 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; status = 1;
} }
} }

View file

@ -9,15 +9,16 @@ struct CmdHash : Command
{ {
enum Mode { mFile, mPath }; enum Mode { mFile, mPath };
Mode mode; Mode mode;
bool base32 = false; Base base = Base16;
bool truncate = false; bool truncate = false;
HashType ht = htSHA512; HashType ht = htSHA512;
Strings paths; Strings paths;
CmdHash(Mode mode) : mode(mode) CmdHash(Mode mode) : mode(mode)
{ {
mkFlag(0, "base32", "print hash in base-32", &base32); mkFlag(0, "base64", "print hash in base-64", &base, Base64);
mkFlag(0, "base16", "print hash in base-16", &base32, false); 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); mkHashTypeFlag("type", &ht);
expectArgs("paths", &paths); expectArgs("paths", &paths);
} }
@ -40,7 +41,7 @@ struct CmdHash : Command
Hash h = mode == mFile ? hashFile(ht, path) : hashPath(ht, path).first; Hash h = mode == mFile ? hashFile(ht, path) : hashPath(ht, path).first;
if (truncate && h.hashSize > 20) h = compressHash(h, 20); if (truncate && h.hashSize > 20) h = compressHash(h, 20);
std::cout << format("%1%\n") % 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 struct CmdToBase : Command
{ {
bool toBase32; Base base;
HashType ht = htSHA512; HashType ht = htSHA512;
Strings args; Strings args;
CmdToBase(bool toBase32) : toBase32(toBase32) CmdToBase(Base base) : base(base)
{ {
mkHashTypeFlag("type", &ht); mkHashTypeFlag("type", &ht);
expectArgs("strings", &args); expectArgs("strings", &args);
@ -62,28 +63,29 @@ struct CmdToBase : Command
std::string name() override 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 std::string description() override
{ {
return toBase32 return fmt("convert a hash to base-%d representation",
? "convert a hash to base-32 representation" base == Base16 ? 16 :
: "convert a hash to base-16 representation"; base == Base32 ? 32 : 64);
} }
void run() override void run() override
{ {
for (auto s : args) { for (auto s : args)
Hash h = parseHash16or32(ht, s); std::cout << fmt("%s\n", Hash(s, ht).to_string(base, false));
std::cout << format("%1%\n") %
(toBase32 ? printHash32(h) : printHash(h));
}
} }
}; };
static RegisterCommand r3(make_ref<CmdToBase>(false)); static RegisterCommand r3(make_ref<CmdToBase>(Base16));
static RegisterCommand r4(make_ref<CmdToBase>(true)); static RegisterCommand r4(make_ref<CmdToBase>(Base32));
static RegisterCommand r5(make_ref<CmdToBase>(Base64));
/* Legacy nix-hash command. */ /* Legacy nix-hash command. */
static int compatNixHash(int argc, char * * argv) static int compatNixHash(int argc, char * * argv)
@ -121,14 +123,14 @@ static int compatNixHash(int argc, char * * argv)
if (op == opHash) { if (op == opHash) {
CmdHash cmd(flat ? CmdHash::mFile : CmdHash::mPath); CmdHash cmd(flat ? CmdHash::mFile : CmdHash::mPath);
cmd.ht = ht; cmd.ht = ht;
cmd.base32 = base32; cmd.base = base32 ? Base32 : Base16;
cmd.truncate = truncate; cmd.truncate = truncate;
cmd.paths = ss; cmd.paths = ss;
cmd.run(); cmd.run();
} }
else { else {
CmdToBase cmd(op == opTo32); CmdToBase cmd(op == opTo32 ? Base32 : Base16);
cmd.args = ss; cmd.args = ss;
cmd.ht = ht; cmd.ht = ht;
cmd.run(); cmd.run();

View file

@ -94,7 +94,7 @@ struct CmdVerify : StorePathsCommand
corrupted = 1; corrupted = 1;
printError( printError(
format("path %s was modified! expected hash %s, got %s") 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());
} }
} }

View file

@ -9,6 +9,15 @@ outPath=$(nix-build '<nix/fetchurl.nix>' --argstr url file://$(pwd)/fetchurl.sh
cmp $outPath 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. # Test unpacking a NAR.
rm -rf $TEST_ROOT/archive rm -rf $TEST_ROOT/archive
mkdir -p $TEST_ROOT/archive mkdir -p $TEST_ROOT/archive

View file

@ -63,11 +63,15 @@ try2 md5 "f78b733a68f5edbdf9413899339eaa4a"
# Conversion. # Conversion.
try3() { try3() {
h64=$(nix to-base64 --type "$1" "$2")
[ "$h64" = "$4" ]
h32=$(nix-hash --type "$1" --to-base32 "$2") h32=$(nix-hash --type "$1" --to-base32 "$2")
[ "$h32" = "$3" ] [ "$h32" = "$3" ]
h16=$(nix-hash --type "$1" --to-base16 "$h32") h16=$(nix-hash --type "$1" --to-base16 "$h32")
[ "$h16" = "$2" ] [ "$h16" = "$2" ]
h16=$(nix to-base16 --type "$1" "$h64")
[ "$h16" = "$2" ]
} }
try3 sha1 "800d59cfcd3c05e900cb4e214be48f6b886a08df" "vw46m23bizj4n8afrc0fj19wrp7mj3c0" try3 sha1 "800d59cfcd3c05e900cb4e214be48f6b886a08df" "vw46m23bizj4n8afrc0fj19wrp7mj3c0" "gA1Zz808BekAy04hS+SPa4hqCN8="
try3 sha256 "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad" "1b8m03r63zqhnjf7l5wnldhh7c134ap5vpj0850ymkq1iyzicy5s" try3 sha256 "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad" "1b8m03r63zqhnjf7l5wnldhh7c134ap5vpj0850ymkq1iyzicy5s" "ungWv48Bz+pBQUDeXa4iI7ADYaOWF3qctBD/YfIAFa0="
try3 sha512 "204a8fc6dda82f0a0ced7beb8e08a41657c16ef468b228a8279be331a703c33596fd15c13b1b07f9aa1d3bea57789ca031ad85c7a71dd70354ec631238ca3445" "12k9jiq29iyqm03swfsgiw5mlqs173qazm3n7daz43infy12pyrcdf30fkk3qwv4yl2ick8yipc2mqnlh48xsvvxl60lbx8vp38yji0" try3 sha512 "204a8fc6dda82f0a0ced7beb8e08a41657c16ef468b228a8279be331a703c33596fd15c13b1b07f9aa1d3bea57789ca031ad85c7a71dd70354ec631238ca3445" "12k9jiq29iyqm03swfsgiw5mlqs173qazm3n7daz43infy12pyrcdf30fkk3qwv4yl2ick8yipc2mqnlh48xsvvxl60lbx8vp38yji0" "IEqPxt2oLwoM7XvrjgikFlfBbvRosiioJ5vjMacDwzWW/RXBOxsH+aodO+pXeJygMa2Fx6cd1wNU7GMSOMo0RQ=="