diff options
Diffstat (limited to 'src/libstore/store-api.cc')
-rw-r--r-- | src/libstore/store-api.cc | 332 |
1 files changed, 278 insertions, 54 deletions
diff --git a/src/libstore/store-api.cc b/src/libstore/store-api.cc index d3cbd1e7dee2..f39d6b54787c 100644 --- a/src/libstore/store-api.cc +++ b/src/libstore/store-api.cc @@ -1,21 +1,13 @@ -#include "store-api.hh" +#include "crypto.hh" #include "globals.hh" +#include "store-api.hh" #include "util.hh" - -#include <climits> +#include "nar-info-disk-cache.hh" namespace nix { -GCOptions::GCOptions() -{ - action = gcDeleteDead; - ignoreLiveness = false; - maxFreed = ULLONG_MAX; -} - - bool isInStore(const Path & path) { return isInDir(path, settings.nixStore); @@ -25,6 +17,7 @@ bool isInStore(const Path & path) bool isStorePath(const Path & path) { return isInStore(path) + && path.size() >= settings.nixStore.size() + 1 + storePathHashLen && path.find('/', settings.nixStore.size() + 1) == Path::npos; } @@ -71,7 +64,17 @@ Path followLinksToStorePath(const Path & path) string storePathToName(const Path & path) { assertStorePath(path); - return string(path, settings.nixStore.size() + 34); + auto l = settings.nixStore.size() + 1 + storePathHashLen; + assert(path.size() >= l); + return path.size() == l ? "" : string(path, l + 1); +} + + +string storePathToHash(const Path & path) +{ + assertStorePath(path); + assert(path.size() >= settings.nixStore.size() + 1 + storePathHashLen); + return string(path, settings.nixStore.size() + 1, storePathHashLen); } @@ -82,14 +85,14 @@ void checkStoreName(const string & name) reasons (e.g., "." and ".."). */ if (string(name, 0, 1) == ".") throw Error(format("illegal name: ‘%1%’") % name); - foreach (string::const_iterator, i, name) - if (!((*i >= 'A' && *i <= 'Z') || - (*i >= 'a' && *i <= 'z') || - (*i >= '0' && *i <= '9') || - validChars.find(*i) != string::npos)) + for (auto & i : name) + if (!((i >= 'A' && i <= 'Z') || + (i >= 'a' && i <= 'z') || + (i >= '0' && i <= '9') || + validChars.find(i) != string::npos)) { throw Error(format("invalid character ‘%1%’ in name ‘%2%’") - % *i % name); + % i % name); } } @@ -101,22 +104,22 @@ void checkStoreName(const string & name) where <store> = the location of the Nix store, usually /nix/store - + <name> = a human readable name for the path, typically obtained from the name attribute of the derivation, or the name of the source file from which the store path is created. For derivation outputs other than the default "out" output, the string "-<id>" is suffixed to <name>. - + <h> = base-32 representation of the first 160 bits of a SHA-256 hash of <s>; the hash part of the store name - + <s> = the string "<type>:sha256:<h2>:<store>:<name>"; note that it includes the location of the store as well as the name to make sure that changes to either of those are reflected in the hash (e.g. you won't get /nix/store/<h>-name1 and /nix/store/<h>-name2 with equal hash parts). - + <type> = one of: "text:<r1>:<r2>:...<rN>" for plain text files written to the store using @@ -138,14 +141,14 @@ void checkStoreName(const string & name) if <type> = "source": the serialisation of the path from which this store path is copied, as returned by hashPath() - if <type> = "output:out": + if <type> = "output:<id>": for non-fixed derivation outputs: the derivation (see hashDerivationModulo() in primops.cc) for paths copied by addToStore() or produced by fixed-output derivations: the string "fixed:out:<rec><algo>:<hash>:", where - <rec> = "r:" for recursive (path) hashes, or "" or flat + <rec> = "r:" for recursive (path) hashes, or "" for flat (file) hashes <algo> = "md5", "sha1" or "sha256" <hash> = base-16 representation of the path or flat hash of @@ -219,45 +222,153 @@ Path computeStorePathForText(const string & name, const string & s, hacky, but we can't put them in `s' since that would be ambiguous. */ string type = "text"; - foreach (PathSet::const_iterator, i, references) { + for (auto & i : references) { type += ":"; - type += *i; + type += i; } return makeStorePath(type, hash, name); } +std::string Store::getUri() +{ + return ""; +} + + +bool Store::isValidPath(const Path & storePath) +{ + auto hashPart = storePathToHash(storePath); + + { + auto state_(state.lock()); + auto res = state_->pathInfoCache.get(hashPart); + if (res) { + stats.narInfoReadAverted++; + return *res != 0; + } + } + + if (diskCache) { + auto res = diskCache->lookupNarInfo(getUri(), hashPart); + if (res.first != NarInfoDiskCache::oUnknown) { + stats.narInfoReadAverted++; + auto state_(state.lock()); + state_->pathInfoCache.upsert(hashPart, + res.first == NarInfoDiskCache::oInvalid ? 0 : res.second); + return res.first == NarInfoDiskCache::oValid; + } + } + + return isValidPathUncached(storePath); + + // FIXME: insert result into NARExistence table of diskCache. +} + + +ref<const ValidPathInfo> Store::queryPathInfo(const Path & storePath) +{ + auto hashPart = storePathToHash(storePath); + + { + auto state_(state.lock()); + auto res = state_->pathInfoCache.get(hashPart); + if (res) { + stats.narInfoReadAverted++; + if (!*res) + throw InvalidPath(format("path ‘%s’ is not valid") % storePath); + return ref<ValidPathInfo>(*res); + } + } + + if (diskCache) { + auto res = diskCache->lookupNarInfo(getUri(), hashPart); + if (res.first != NarInfoDiskCache::oUnknown) { + stats.narInfoReadAverted++; + auto state_(state.lock()); + state_->pathInfoCache.upsert(hashPart, + res.first == NarInfoDiskCache::oInvalid ? 0 : res.second); + if (res.first == NarInfoDiskCache::oInvalid || + (res.second->path != storePath && storePathToName(storePath) != "")) + throw InvalidPath(format("path ‘%s’ is not valid") % storePath); + return ref<ValidPathInfo>(res.second); + } + } + + auto info = queryPathInfoUncached(storePath); + + if (diskCache && info) + diskCache->upsertNarInfo(getUri(), hashPart, info); + + { + auto state_(state.lock()); + state_->pathInfoCache.upsert(hashPart, info); + } + + if (!info + || (info->path != storePath && storePathToName(storePath) != "")) + { + stats.narInfoMissing++; + throw InvalidPath(format("path ‘%s’ is not valid") % storePath); + } + + return ref<ValidPathInfo>(info); +} + + /* Return a string accepted by decodeValidPathInfo() that registers the specified paths as valid. Note: it's the responsibility of the caller to provide a closure. */ -string StoreAPI::makeValidityRegistration(const PathSet & paths, +string Store::makeValidityRegistration(const PathSet & paths, bool showDerivers, bool showHash) { string s = ""; - - foreach (PathSet::iterator, i, paths) { - s += *i + "\n"; - ValidPathInfo info = queryPathInfo(*i); + for (auto & i : paths) { + s += i + "\n"; + + auto info = queryPathInfo(i); if (showHash) { - s += printHash(info.hash) + "\n"; - s += (format("%1%\n") % info.narSize).str(); + s += printHash(info->narHash) + "\n"; + s += (format("%1%\n") % info->narSize).str(); } - Path deriver = showDerivers ? info.deriver : ""; + Path deriver = showDerivers ? info->deriver : ""; s += deriver + "\n"; - s += (format("%1%\n") % info.references.size()).str(); + s += (format("%1%\n") % info->references.size()).str(); - foreach (PathSet::iterator, j, info.references) - s += *j + "\n"; + for (auto & j : info->references) + s += j + "\n"; } return s; } +const Store::Stats & Store::getStats() +{ + { + auto state_(state.lock()); + stats.pathInfoCacheSize = state_->pathInfoCache.size(); + } + return stats; +} + + +void copyStorePath(ref<Store> srcStore, ref<Store> dstStore, + const Path & storePath, bool repair) +{ + auto info = srcStore->queryPathInfo(storePath); + + StringSink sink; + srcStore->narFromPath({storePath}, sink); + + dstStore->addToStore(*info, *sink.s, repair); +} + + ValidPathInfo decodeValidPathInfo(std::istream & str, bool hashGiven) { ValidPathInfo info; @@ -266,7 +377,7 @@ ValidPathInfo decodeValidPathInfo(std::istream & str, bool hashGiven) if (hashGiven) { string s; getline(str, s); - info.hash = parseHash(htSHA256, s); + info.narHash = parseHash(htSHA256, s); getline(str, s); if (!string2Int(s, info.narSize)) throw Error("number expected"); } @@ -286,22 +397,55 @@ ValidPathInfo decodeValidPathInfo(std::istream & str, bool hashGiven) string showPaths(const PathSet & paths) { string s; - foreach (PathSet::const_iterator, i, paths) { + for (auto & i : paths) { if (s.size() != 0) s += ", "; - s += "‘" + *i + "’"; + s += "‘" + i + "’"; } return s; } -void exportPaths(StoreAPI & store, const Paths & paths, - bool sign, Sink & sink) +std::string ValidPathInfo::fingerprint() const { - foreach (Paths::const_iterator, i, paths) { - writeInt(1, sink); - store.exportPath(*i, sign, sink); - } - writeInt(0, sink); + if (narSize == 0 || !narHash) + throw Error(format("cannot calculate fingerprint of path ‘%s’ because its size/hash is not known") + % path); + return + "1;" + path + ";" + + printHashType(narHash.type) + ":" + printHash32(narHash) + ";" + + std::to_string(narSize) + ";" + + concatStringsSep(",", references); +} + + +void ValidPathInfo::sign(const SecretKey & secretKey) +{ + sigs.insert(secretKey.signDetached(fingerprint())); +} + + +unsigned int ValidPathInfo::checkSignatures(const PublicKeys & publicKeys) const +{ + unsigned int good = 0; + for (auto & sig : sigs) + if (checkSignature(publicKeys, sig)) + good++; + return good; +} + + +bool ValidPathInfo::checkSignature(const PublicKeys & publicKeys, const std::string & sig) const +{ + return verifyDetached(fingerprint(), sig, publicKeys); +} + + +Strings ValidPathInfo::shortRefs() const +{ + Strings refs; + for (auto & r : references) + refs.push_back(baseNameOf(r)); + return refs; } @@ -309,22 +453,102 @@ void exportPaths(StoreAPI & store, const Paths & paths, #include "local-store.hh" -#include "serialise.hh" #include "remote-store.hh" namespace nix { -std::shared_ptr<StoreAPI> store; +RegisterStoreImplementation::Implementations * RegisterStoreImplementation::implementations = 0; -std::shared_ptr<StoreAPI> openStore(bool reserveSpace) +ref<Store> openStoreAt(const std::string & uri_) { - if (getEnv("NIX_REMOTE") == "") - return std::shared_ptr<StoreAPI>(new LocalStore(reserveSpace)); - else - return std::shared_ptr<StoreAPI>(new RemoteStore()); + auto uri(uri_); + StoreParams params; + auto q = uri.find('?'); + if (q != std::string::npos) { + for (auto s : tokenizeString<Strings>(uri.substr(q + 1), "&")) { + auto e = s.find('='); + if (e != std::string::npos) + params[s.substr(0, e)] = s.substr(e + 1); + } + uri = uri_.substr(0, q); + } + + for (auto fun : *RegisterStoreImplementation::implementations) { + auto store = fun(uri, params); + if (store) return ref<Store>(store); + } + + throw Error(format("don't know how to open Nix store ‘%s’") % uri); +} + + +ref<Store> openStore() +{ + return openStoreAt(getEnv("NIX_REMOTE")); +} + + +static RegisterStoreImplementation regStore([]( + const std::string & uri, const StoreParams & params) + -> std::shared_ptr<Store> +{ + enum { mDaemon, mLocal, mAuto } mode; + + if (uri == "daemon") mode = mDaemon; + else if (uri == "local") mode = mLocal; + else if (uri == "") mode = mAuto; + else return 0; + + if (mode == mAuto) { + if (LocalStore::haveWriteAccess()) + mode = mLocal; + else if (pathExists(settings.nixDaemonSocketFile)) + mode = mDaemon; + else + mode = mLocal; + } + + return mode == mDaemon + ? std::shared_ptr<Store>(std::make_shared<RemoteStore>()) + : std::shared_ptr<Store>(std::make_shared<LocalStore>()); +}); + + +std::list<ref<Store>> getDefaultSubstituters() +{ + struct State { + bool done = false; + std::list<ref<Store>> stores; + }; + static Sync<State> state_; + + auto state(state_.lock()); + + if (state->done) return state->stores; + + StringSet done; + + auto addStore = [&](const std::string & uri) { + if (done.count(uri)) return; + done.insert(uri); + state->stores.push_back(openStoreAt(uri)); + }; + + for (auto uri : settings.get("substituters", Strings())) + addStore(uri); + + for (auto uri : settings.get("binary-caches", Strings())) + addStore(uri); + + for (auto uri : settings.get("extra-binary-caches", Strings())) + addStore(uri); + + state->done = true; + + return state->stores; } |