2016-03-29 12:29:50 +00:00
|
|
|
|
#include "affinity.hh" // FIXME
|
|
|
|
|
#include "command.hh"
|
|
|
|
|
#include "progress-bar.hh"
|
|
|
|
|
#include "shared.hh"
|
|
|
|
|
#include "store-api.hh"
|
|
|
|
|
#include "sync.hh"
|
|
|
|
|
#include "thread-pool.hh"
|
|
|
|
|
|
|
|
|
|
#include <atomic>
|
|
|
|
|
|
|
|
|
|
using namespace nix;
|
|
|
|
|
|
2016-04-14 19:14:29 +00:00
|
|
|
|
struct CmdVerify : StorePathsCommand
|
2016-03-29 12:29:50 +00:00
|
|
|
|
{
|
|
|
|
|
bool noContents = false;
|
2016-04-07 13:14:12 +00:00
|
|
|
|
bool noTrust = false;
|
2016-03-30 09:18:54 +00:00
|
|
|
|
Strings substituterUris;
|
2016-04-07 13:14:12 +00:00
|
|
|
|
size_t sigsNeeded;
|
2016-03-29 12:29:50 +00:00
|
|
|
|
|
2016-04-14 19:14:29 +00:00
|
|
|
|
CmdVerify()
|
2016-03-29 12:29:50 +00:00
|
|
|
|
{
|
|
|
|
|
mkFlag(0, "no-contents", "do not verify the contents of each store path", &noContents);
|
2016-04-07 13:14:12 +00:00
|
|
|
|
mkFlag(0, "no-trust", "do not verify whether each store path is trusted", &noTrust);
|
2016-03-30 09:18:54 +00:00
|
|
|
|
mkFlag('s', "substituter", {"store-uri"}, "use signatures from specified store", 1,
|
|
|
|
|
[&](Strings ss) { substituterUris.push_back(ss.front()); });
|
2016-04-07 13:14:12 +00:00
|
|
|
|
mkIntFlag('n', "sigs-needed", "require that each path has at least N valid signatures", &sigsNeeded);
|
2016-03-29 12:29:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
2016-04-14 19:14:29 +00:00
|
|
|
|
std::string name() override
|
|
|
|
|
{
|
|
|
|
|
return "verify";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::string description() override
|
|
|
|
|
{
|
|
|
|
|
return "verify the integrity of store paths";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void run(ref<Store> store, Paths storePaths) override
|
2016-03-29 12:29:50 +00:00
|
|
|
|
{
|
|
|
|
|
restoreAffinity(); // FIXME
|
|
|
|
|
|
2016-03-30 09:18:54 +00:00
|
|
|
|
std::vector<ref<Store>> substituters;
|
|
|
|
|
for (auto & s : substituterUris)
|
|
|
|
|
substituters.push_back(openStoreAt(s));
|
|
|
|
|
|
2016-03-29 12:29:50 +00:00
|
|
|
|
auto publicKeys = getDefaultPublicKeys();
|
|
|
|
|
|
|
|
|
|
std::atomic<size_t> untrusted{0};
|
|
|
|
|
std::atomic<size_t> corrupted{0};
|
|
|
|
|
std::atomic<size_t> done{0};
|
|
|
|
|
std::atomic<size_t> failed{0};
|
|
|
|
|
|
|
|
|
|
ProgressBar progressBar;
|
|
|
|
|
|
|
|
|
|
auto showProgress = [&](bool final) {
|
|
|
|
|
std::string s;
|
|
|
|
|
if (final)
|
|
|
|
|
s = (format("checked %d paths") % storePaths.size()).str();
|
|
|
|
|
else
|
|
|
|
|
s = (format("[%d/%d checked") % done % storePaths.size()).str();
|
|
|
|
|
if (corrupted > 0)
|
|
|
|
|
s += (format(", %d corrupted") % corrupted).str();
|
|
|
|
|
if (untrusted > 0)
|
|
|
|
|
s += (format(", %d untrusted") % untrusted).str();
|
|
|
|
|
if (failed > 0)
|
|
|
|
|
s += (format(", %d failed") % failed).str();
|
|
|
|
|
if (!final) s += "]";
|
|
|
|
|
return s;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
progressBar.updateStatus(showProgress(false));
|
|
|
|
|
|
|
|
|
|
ThreadPool pool;
|
|
|
|
|
|
|
|
|
|
auto doPath = [&](const Path & storePath) {
|
|
|
|
|
try {
|
2016-03-30 09:18:54 +00:00
|
|
|
|
checkInterrupt();
|
|
|
|
|
|
|
|
|
|
auto activity(progressBar.startActivity(format("checking ‘%s’") % storePath));
|
2016-03-29 12:29:50 +00:00
|
|
|
|
|
|
|
|
|
auto info = store->queryPathInfo(storePath);
|
|
|
|
|
|
|
|
|
|
if (!noContents) {
|
|
|
|
|
|
|
|
|
|
HashSink sink(info.narHash.type);
|
|
|
|
|
store->narFromPath(storePath, sink);
|
|
|
|
|
|
|
|
|
|
auto hash = sink.finish();
|
|
|
|
|
|
|
|
|
|
if (hash.first != info.narHash) {
|
|
|
|
|
corrupted = 1;
|
|
|
|
|
printMsg(lvlError,
|
|
|
|
|
format("path ‘%s’ was modified! expected hash ‘%s’, got ‘%s’")
|
|
|
|
|
% storePath % printHash(info.narHash) % printHash(hash.first));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2016-04-07 13:14:12 +00:00
|
|
|
|
if (!noTrust) {
|
2016-03-29 12:29:50 +00:00
|
|
|
|
|
2016-03-30 09:18:54 +00:00
|
|
|
|
bool good = false;
|
|
|
|
|
|
2016-04-07 13:14:12 +00:00
|
|
|
|
if (info.ultimate && !sigsNeeded)
|
2016-03-30 09:18:54 +00:00
|
|
|
|
good = true;
|
|
|
|
|
|
2016-04-07 13:14:12 +00:00
|
|
|
|
else {
|
|
|
|
|
|
|
|
|
|
StringSet sigsSeen;
|
|
|
|
|
size_t actualSigsNeeded = sigsNeeded ? sigsNeeded : 1;
|
|
|
|
|
size_t validSigs = 0;
|
|
|
|
|
|
|
|
|
|
auto doSigs = [&](StringSet sigs) {
|
|
|
|
|
for (auto sig : sigs) {
|
|
|
|
|
if (sigsSeen.count(sig)) continue;
|
|
|
|
|
sigsSeen.insert(sig);
|
|
|
|
|
if (info.checkSignature(publicKeys, sig))
|
|
|
|
|
validSigs++;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
doSigs(info.sigs);
|
2016-03-30 09:18:54 +00:00
|
|
|
|
|
|
|
|
|
for (auto & store2 : substituters) {
|
2016-04-07 13:14:12 +00:00
|
|
|
|
if (validSigs >= actualSigsNeeded) break;
|
|
|
|
|
try {
|
|
|
|
|
if (!store2->isValidPath(storePath)) continue;
|
|
|
|
|
doSigs(store2->queryPathInfo(storePath).sigs);
|
|
|
|
|
} catch (Error & e) {
|
|
|
|
|
printMsg(lvlError, format(ANSI_RED "error:" ANSI_NORMAL " %s") % e.what());
|
2016-03-30 09:18:54 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2016-04-07 13:14:12 +00:00
|
|
|
|
|
|
|
|
|
if (validSigs >= actualSigsNeeded)
|
|
|
|
|
good = true;
|
2016-03-30 09:18:54 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!good) {
|
2016-03-29 12:29:50 +00:00
|
|
|
|
untrusted++;
|
|
|
|
|
printMsg(lvlError, format("path ‘%s’ is untrusted") % storePath);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
done++;
|
|
|
|
|
|
|
|
|
|
progressBar.updateStatus(showProgress(false));
|
|
|
|
|
|
|
|
|
|
} catch (Error & e) {
|
|
|
|
|
printMsg(lvlError, format(ANSI_RED "error:" ANSI_NORMAL " %s") % e.what());
|
|
|
|
|
failed++;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
for (auto & storePath : storePaths)
|
|
|
|
|
pool.enqueue(std::bind(doPath, storePath));
|
|
|
|
|
|
|
|
|
|
pool.process();
|
|
|
|
|
|
|
|
|
|
progressBar.done();
|
|
|
|
|
|
|
|
|
|
printMsg(lvlInfo, showProgress(true));
|
|
|
|
|
|
|
|
|
|
throw Exit(
|
|
|
|
|
(corrupted ? 1 : 0) |
|
|
|
|
|
(untrusted ? 2 : 0) |
|
|
|
|
|
(failed ? 4 : 0));
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2016-04-14 19:14:29 +00:00
|
|
|
|
static RegisterCommand r1(make_ref<CmdVerify>());
|