Add "nix verify-paths" command
Unlike "nix-store --verify-path", this command verifies signatures in addition to store path contents, is multi-threaded (especially useful when verifying binary caches), and has a progress indicator. Example use: $ nix verify-paths --store https://cache.nixos.org -r $(type -p thunderbird) ... [17/132 checked] checking ‘/nix/store/rawakphadqrqxr6zri2rmnxh03gqkrl3-autogen-5.18.6’
This commit is contained in:
parent
0ebe69dc67
commit
784ee35c80
11 changed files with 432 additions and 2 deletions
124
src/nix/verify.cc
Normal file
124
src/nix/verify.cc
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
#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;
|
||||
|
||||
struct CmdVerifyPaths : StorePathsCommand
|
||||
{
|
||||
bool noContents = false;
|
||||
bool noSigs = false;
|
||||
|
||||
CmdVerifyPaths()
|
||||
{
|
||||
mkFlag(0, "no-contents", "do not verify the contents of each store path", &noContents);
|
||||
mkFlag(0, "no-sigs", "do not verify whether each store path has a valid signature", &noSigs);
|
||||
}
|
||||
|
||||
std::string name() override
|
||||
{
|
||||
return "verify-paths";
|
||||
}
|
||||
|
||||
std::string description() override
|
||||
{
|
||||
return "verify the integrity of store paths";
|
||||
}
|
||||
|
||||
void run(ref<Store> store, Paths storePaths) override
|
||||
{
|
||||
restoreAffinity(); // FIXME
|
||||
|
||||
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 {
|
||||
progressBar.startActivity(format("checking ‘%s’") % storePath);
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (!noSigs) {
|
||||
|
||||
if (!info.ultimate && !info.checkSignatures(publicKeys)) {
|
||||
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));
|
||||
}
|
||||
};
|
||||
|
||||
static RegisterCommand r1(make_ref<CmdVerifyPaths>());
|
||||
Loading…
Add table
Add a link
Reference in a new issue