Make "NAR info file is corrupt" messages more informative

Recently, I encountered the "NAR info file 'xxxx' is corrupt" error
with my binary cache. The message is not helpful in determining, which
kind of corruption happened. The file, fetched with curl, looked
reasonably.

This commit adds more information to the error message, which should
allow debugging and hopefully fixing the problem.
This commit is contained in:
Michal Sojka 2023-04-17 19:34:09 +02:00
parent c56705c025
commit d30d2dc861

View file

@ -7,15 +7,18 @@ namespace nix {
NarInfo::NarInfo(const Store & store, const std::string & s, const std::string & whence) NarInfo::NarInfo(const Store & store, const std::string & s, const std::string & whence)
: ValidPathInfo(StorePath(StorePath::dummy), Hash(Hash::dummy)) // FIXME: hack : ValidPathInfo(StorePath(StorePath::dummy), Hash(Hash::dummy)) // FIXME: hack
{ {
auto corrupt = [&]() { unsigned line = 1;
return Error("NAR info file '%1%' is corrupt", whence);
auto corrupt = [&](const char * reason) {
return Error("NAR info file '%1%' is corrupt: %2%", whence,
std::string(reason) + (line > 0 ? " at line " + std::to_string(line) : ""));
}; };
auto parseHashField = [&](const std::string & s) { auto parseHashField = [&](const std::string & s) {
try { try {
return Hash::parseAnyPrefixed(s); return Hash::parseAnyPrefixed(s);
} catch (BadHash &) { } catch (BadHash &) {
throw corrupt(); throw corrupt("bad hash");
} }
}; };
@ -26,12 +29,12 @@ NarInfo::NarInfo(const Store & store, const std::string & s, const std::string &
while (pos < s.size()) { while (pos < s.size()) {
size_t colon = s.find(':', pos); size_t colon = s.find(':', pos);
if (colon == std::string::npos) throw corrupt(); if (colon == std::string::npos) throw corrupt("expecting ':'");
std::string name(s, pos, colon - pos); std::string name(s, pos, colon - pos);
size_t eol = s.find('\n', colon + 2); size_t eol = s.find('\n', colon + 2);
if (eol == std::string::npos) throw corrupt(); if (eol == std::string::npos) throw corrupt("expecting '\\n'");
std::string value(s, colon + 2, eol - colon - 2); std::string value(s, colon + 2, eol - colon - 2);
@ -47,7 +50,7 @@ NarInfo::NarInfo(const Store & store, const std::string & s, const std::string &
fileHash = parseHashField(value); fileHash = parseHashField(value);
else if (name == "FileSize") { else if (name == "FileSize") {
auto n = string2Int<decltype(fileSize)>(value); auto n = string2Int<decltype(fileSize)>(value);
if (!n) throw corrupt(); if (!n) throw corrupt("invalid FileSize");
fileSize = *n; fileSize = *n;
} }
else if (name == "NarHash") { else if (name == "NarHash") {
@ -56,12 +59,12 @@ NarInfo::NarInfo(const Store & store, const std::string & s, const std::string &
} }
else if (name == "NarSize") { else if (name == "NarSize") {
auto n = string2Int<decltype(narSize)>(value); auto n = string2Int<decltype(narSize)>(value);
if (!n) throw corrupt(); if (!n) throw corrupt("invalid NarSize");
narSize = *n; narSize = *n;
} }
else if (name == "References") { else if (name == "References") {
auto refs = tokenizeString<Strings>(value, " "); auto refs = tokenizeString<Strings>(value, " ");
if (!references.empty()) throw corrupt(); if (!references.empty()) throw corrupt("extra References");
for (auto & r : refs) for (auto & r : refs)
references.insert(StorePath(r)); references.insert(StorePath(r));
} }
@ -72,17 +75,26 @@ NarInfo::NarInfo(const Store & store, const std::string & s, const std::string &
else if (name == "Sig") else if (name == "Sig")
sigs.insert(value); sigs.insert(value);
else if (name == "CA") { else if (name == "CA") {
if (ca) throw corrupt(); if (ca) throw corrupt("extra CA");
// FIXME: allow blank ca or require skipping field? // FIXME: allow blank ca or require skipping field?
ca = ContentAddress::parseOpt(value); ca = ContentAddress::parseOpt(value);
} }
pos = eol + 1; pos = eol + 1;
line += 1;
} }
if (compression == "") compression = "bzip2"; if (compression == "") compression = "bzip2";
if (!havePath || !haveNarHash || url.empty() || narSize == 0) throw corrupt(); if (!havePath || !haveNarHash || url.empty() || narSize == 0) {
line = 0; // don't include line information in the error
throw corrupt(
!havePath ? "StorePath missing" :
!haveNarHash ? "NarHash missing" :
url.empty() ? "URL missing" :
narSize == 0 ? "NarSize missing or zero"
: "?");
}
} }
std::string NarInfo::to_string(const Store & store) const std::string NarInfo::to_string(const Store & store) const