about summary refs log tree commit diff
path: root/src/nix
diff options
context:
space:
mode:
Diffstat (limited to 'src/nix')
-rw-r--r--src/nix/command.cc21
-rw-r--r--src/nix/command.hh17
-rw-r--r--src/nix/progress-bar.cc72
-rw-r--r--src/nix/progress-bar.hh49
-rw-r--r--src/nix/sigs.cc181
-rw-r--r--src/nix/verify.cc211
6 files changed, 551 insertions, 0 deletions
diff --git a/src/nix/command.cc b/src/nix/command.cc
index 0c2103392f8e..a89246a937c1 100644
--- a/src/nix/command.cc
+++ b/src/nix/command.cc
@@ -69,4 +69,25 @@ void StoreCommand::run()
     run(openStoreAt(storeUri));
 }
 
+StorePathsCommand::StorePathsCommand()
+{
+    expectArgs("paths", &storePaths);
+    mkFlag('r', "recursive", "apply operation to closure of the specified paths", &recursive);
+}
+
+void StorePathsCommand::run(ref<Store> store)
+{
+    for (auto & storePath : storePaths)
+        storePath = followLinksToStorePath(storePath);
+
+    if (recursive) {
+        PathSet closure;
+        for (auto & storePath : storePaths)
+            store->computeFSClosure(storePath, closure, false, false);
+        storePaths = store->topoSortPaths(closure);
+    }
+
+    run(store, storePaths);
+}
+
 }
diff --git a/src/nix/command.hh b/src/nix/command.hh
index 5bf391d9367f..8397244ca177 100644
--- a/src/nix/command.hh
+++ b/src/nix/command.hh
@@ -24,6 +24,23 @@ struct StoreCommand : virtual Command
     virtual void run(ref<Store>) = 0;
 };
 
+/* A command that operates on zero or more store paths. */
+struct StorePathsCommand : public StoreCommand
+{
+private:
+
+    Paths storePaths;
+    bool recursive = false;
+
+public:
+
+    StorePathsCommand();
+
+    virtual void run(ref<Store> store, Paths storePaths) = 0;
+
+    void run(ref<Store> store) override;
+};
+
 typedef std::map<std::string, ref<Command>> Commands;
 
 /* An argument parser that supports multiple subcommands,
diff --git a/src/nix/progress-bar.cc b/src/nix/progress-bar.cc
new file mode 100644
index 000000000000..ed7b578e2f49
--- /dev/null
+++ b/src/nix/progress-bar.cc
@@ -0,0 +1,72 @@
+#include "progress-bar.hh"
+
+#include <iostream>
+
+namespace nix {
+
+ProgressBar::ProgressBar()
+{
+    _writeToStderr = [&](const unsigned char * buf, size_t count) {
+        auto state_(state.lock());
+        assert(!state_->done);
+        std::cerr << "\r\e[K" << std::string((const char *) buf, count);
+        render(*state_);
+    };
+}
+
+ProgressBar::~ProgressBar()
+{
+    done();
+}
+
+void ProgressBar::updateStatus(const std::string & s)
+{
+    auto state_(state.lock());
+    assert(!state_->done);
+    state_->status = s;
+    render(*state_);
+}
+
+void ProgressBar::done()
+{
+    auto state_(state.lock());
+    assert(state_->activities.empty());
+    state_->done = true;
+    std::cerr << "\r\e[K";
+    std::cerr.flush();
+    _writeToStderr = decltype(_writeToStderr)();
+}
+
+void ProgressBar::render(State & state_)
+{
+    std::cerr << '\r' << state_.status;
+    if (!state_.activities.empty()) {
+        if (!state_.status.empty()) std::cerr << ' ';
+        std::cerr << *state_.activities.rbegin();
+    }
+    std::cerr << "\e[K";
+    std::cerr.flush();
+}
+
+
+ProgressBar::Activity ProgressBar::startActivity(const FormatOrString & fs)
+{
+    return Activity(*this, fs);
+}
+
+ProgressBar::Activity::Activity(ProgressBar & pb, const FormatOrString & fs)
+    : pb(pb)
+{
+    auto state_(pb.state.lock());
+    state_->activities.push_back(fs.s);
+    it = state_->activities.end(); --it;
+    pb.render(*state_);
+}
+
+ProgressBar::Activity::~Activity()
+{
+    auto state_(pb.state.lock());
+    state_->activities.erase(it);
+}
+
+}
diff --git a/src/nix/progress-bar.hh b/src/nix/progress-bar.hh
new file mode 100644
index 000000000000..2dda24346c90
--- /dev/null
+++ b/src/nix/progress-bar.hh
@@ -0,0 +1,49 @@
+#pragma once
+
+#include "sync.hh"
+#include "util.hh"
+
+namespace nix {
+
+class ProgressBar
+{
+private:
+    struct State
+    {
+        std::string status;
+        bool done = false;
+        std::list<std::string> activities;
+    };
+
+    Sync<State> state;
+
+public:
+
+    ProgressBar();
+
+    ~ProgressBar();
+
+    void updateStatus(const std::string & s);
+
+    void done();
+
+    class Activity
+    {
+        friend class ProgressBar;
+    private:
+        ProgressBar & pb;
+        std::list<std::string>::iterator it;
+        Activity(ProgressBar & pb, const FormatOrString & fs);
+    public:
+        ~Activity();
+    };
+
+    Activity startActivity(const FormatOrString & fs);
+
+private:
+
+    void render(State & state_);
+
+};
+
+}
diff --git a/src/nix/sigs.cc b/src/nix/sigs.cc
new file mode 100644
index 000000000000..bcc46c3e7d4f
--- /dev/null
+++ b/src/nix/sigs.cc
@@ -0,0 +1,181 @@
+#include "affinity.hh" // FIXME
+#include "command.hh"
+#include "progress-bar.hh"
+#include "shared.hh"
+#include "store-api.hh"
+#include "thread-pool.hh"
+
+#include <atomic>
+
+using namespace nix;
+
+struct CmdCopySigs : StorePathsCommand
+{
+    Strings substituterUris;
+
+    CmdCopySigs()
+    {
+        mkFlag('s', "substituter", {"store-uri"}, "use signatures from specified store", 1,
+            [&](Strings ss) { substituterUris.push_back(ss.front()); });
+    }
+
+    std::string name() override
+    {
+        return "copy-sigs";
+    }
+
+    std::string description() override
+    {
+        return "copy path signatures from substituters (like binary caches)";
+    }
+
+    void run(ref<Store> store, Paths storePaths) override
+    {
+        restoreAffinity(); // FIXME
+
+        if (substituterUris.empty())
+            throw UsageError("you must specify at least one substituter using ‘-s’");
+
+        // FIXME: factor out commonality with MixVerify.
+        std::vector<ref<Store>> substituters;
+        for (auto & s : substituterUris)
+            substituters.push_back(openStoreAt(s));
+
+        ProgressBar progressBar;
+
+        ThreadPool pool;
+
+        std::atomic<size_t> done{0};
+        std::atomic<size_t> added{0};
+
+        auto showProgress = [&]() {
+            return (format("[%d/%d done]") % done % storePaths.size()).str();
+        };
+
+        progressBar.updateStatus(showProgress());
+
+        auto doPath = [&](const Path & storePath) {
+            auto activity(progressBar.startActivity(format("getting signatures for ‘%s’") % storePath));
+
+            checkInterrupt();
+
+            auto info = store->queryPathInfo(storePath);
+
+            StringSet newSigs;
+
+            for (auto & store2 : substituters) {
+                if (!store2->isValidPath(storePath)) continue;
+                auto info2 = store2->queryPathInfo(storePath);
+
+                /* Don't import signatures that don't match this
+                   binary. */
+                if (info.narHash != info2.narHash ||
+                    info.narSize != info2.narSize ||
+                    info.references != info2.references)
+                    continue;
+
+                for (auto & sig : info2.sigs)
+                    if (!info.sigs.count(sig))
+                        newSigs.insert(sig);
+            }
+
+            if (!newSigs.empty()) {
+                store->addSignatures(storePath, newSigs);
+                added += newSigs.size();
+            }
+
+            done++;
+            progressBar.updateStatus(showProgress());
+        };
+
+        for (auto & storePath : storePaths)
+            pool.enqueue(std::bind(doPath, storePath));
+
+        pool.process();
+
+        progressBar.done();
+
+        printMsg(lvlInfo, format("imported %d signatures") % added);
+    }
+};
+
+static RegisterCommand r1(make_ref<CmdCopySigs>());
+
+struct CmdQueryPathSigs : StorePathsCommand
+{
+    CmdQueryPathSigs()
+    {
+    }
+
+    std::string name() override
+    {
+        return "query-path-sigs";
+    }
+
+    std::string description() override
+    {
+        return "print store path signatures";
+    }
+
+    void run(ref<Store> store, Paths storePaths) override
+    {
+        for (auto & storePath : storePaths) {
+            auto info = store->queryPathInfo(storePath);
+            std::cout << storePath << " ";
+            if (info.ultimate) std::cout << "ultimate ";
+            for (auto & sig : info.sigs)
+                std::cout << sig << " ";
+            std::cout << "\n";
+        }
+    }
+};
+
+static RegisterCommand r2(make_ref<CmdQueryPathSigs>());
+
+struct CmdSignPaths : StorePathsCommand
+{
+    Path secretKeyFile;
+
+    CmdSignPaths()
+    {
+        mkFlag('k', "key-file", {"file"}, "file containing the secret signing key", &secretKeyFile);
+    }
+
+    std::string name() override
+    {
+        return "sign-paths";
+    }
+
+    std::string description() override
+    {
+        return "sign the specified paths";
+    }
+
+    void run(ref<Store> store, Paths storePaths) override
+    {
+        if (secretKeyFile.empty())
+            throw UsageError("you must specify a secret key file using ‘-k’");
+
+        SecretKey secretKey(readFile(secretKeyFile));
+
+        size_t added{0};
+
+        for (auto & storePath : storePaths) {
+            auto info = store->queryPathInfo(storePath);
+
+            auto info2(info);
+            info2.sigs.clear();
+            info2.sign(secretKey);
+            assert(!info2.sigs.empty());
+
+            if (!info.sigs.count(*info2.sigs.begin())) {
+                store->addSignatures(storePath, info2.sigs);
+                added++;
+            }
+        }
+
+        printMsg(lvlInfo, format("added %d signatures") % added);
+    }
+};
+
+static RegisterCommand r3(make_ref<CmdSignPaths>());
diff --git a/src/nix/verify.cc b/src/nix/verify.cc
new file mode 100644
index 000000000000..9214d3b651d1
--- /dev/null
+++ b/src/nix/verify.cc
@@ -0,0 +1,211 @@
+#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 MixVerify : virtual Args
+{
+    bool noContents = false;
+    bool noTrust = false;
+    Strings substituterUris;
+    size_t sigsNeeded;
+
+    MixVerify()
+    {
+        mkFlag(0, "no-contents", "do not verify the contents of each store path", &noContents);
+        mkFlag(0, "no-trust", "do not verify whether each store path is trusted", &noTrust);
+        mkFlag('s', "substituter", {"store-uri"}, "use signatures from specified store", 1,
+            [&](Strings ss) { substituterUris.push_back(ss.front()); });
+        mkIntFlag('n', "sigs-needed", "require that each path has at least N valid signatures", &sigsNeeded);
+    }
+
+    void verifyPaths(ref<Store> store, const Paths & storePaths)
+    {
+        restoreAffinity(); // FIXME
+
+        std::vector<ref<Store>> substituters;
+        for (auto & s : substituterUris)
+            substituters.push_back(openStoreAt(s));
+
+        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 {
+                checkInterrupt();
+
+                auto activity(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 (!noTrust) {
+
+                    bool good = false;
+
+                    if (info.ultimate && !sigsNeeded)
+                        good = true;
+
+                    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);
+
+                        for (auto & store2 : substituters) {
+                            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());
+                            }
+                        }
+
+                        if (validSigs >= actualSigsNeeded)
+                            good = true;
+                    }
+
+                    if (!good) {
+                        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));
+    }
+};
+
+struct CmdVerifyPaths : StorePathsCommand, MixVerify
+{
+    CmdVerifyPaths()
+    {
+    }
+
+    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
+    {
+        verifyPaths(store, storePaths);
+    }
+};
+
+static RegisterCommand r1(make_ref<CmdVerifyPaths>());
+
+struct CmdVerifyStore : StoreCommand, MixVerify
+{
+    CmdVerifyStore()
+    {
+    }
+
+    std::string name() override
+    {
+        return "verify-store";
+    }
+
+    std::string description() override
+    {
+        return "verify the integrity of all paths in the Nix store";
+    }
+
+    void run(ref<Store> store) override
+    {
+        // FIXME: use store->verifyStore()?
+
+        PathSet validPaths = store->queryAllValidPaths();
+
+        verifyPaths(store, Paths(validPaths.begin(), validPaths.end()));
+    }
+};
+
+static RegisterCommand r2(make_ref<CmdVerifyStore>());