Cache path info lookups in SQLite
This re-implements the binary cache database in C++, allowing it to be used by other Store backends, in particular the S3 backend.
This commit is contained in:
parent
e0204f8d46
commit
451ebf24ce
18 changed files with 380 additions and 36 deletions
|
@ -59,7 +59,7 @@ void BinaryCacheStore::addToCache(const ValidPathInfo & info,
|
||||||
narInfo->narSize = nar.size();
|
narInfo->narSize = nar.size();
|
||||||
narInfo->narHash = hashString(htSHA256, nar);
|
narInfo->narHash = hashString(htSHA256, nar);
|
||||||
|
|
||||||
if (info.narHash.type != htUnknown && info.narHash != narInfo->narHash)
|
if (info.narHash && info.narHash != narInfo->narHash)
|
||||||
throw Error(format("refusing to copy corrupted path ‘%1%’ to binary cache") % info.path);
|
throw Error(format("refusing to copy corrupted path ‘%1%’ to binary cache") % info.path);
|
||||||
|
|
||||||
/* Compress the NAR. */
|
/* Compress the NAR. */
|
||||||
|
@ -96,7 +96,6 @@ void BinaryCacheStore::addToCache(const ValidPathInfo & info,
|
||||||
{
|
{
|
||||||
auto state_(state.lock());
|
auto state_(state.lock());
|
||||||
state_->pathInfoCache.upsert(narInfo->path, std::shared_ptr<NarInfo>(narInfo));
|
state_->pathInfoCache.upsert(narInfo->path, std::shared_ptr<NarInfo>(narInfo));
|
||||||
stats.pathInfoCacheSize = state_->pathInfoCache.size();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
stats.narInfoWrite++;
|
stats.narInfoWrite++;
|
||||||
|
|
|
@ -290,7 +290,7 @@ Hash hashDerivationModulo(Store & store, Derivation drv)
|
||||||
DerivationInputs inputs2;
|
DerivationInputs inputs2;
|
||||||
for (auto & i : drv.inputDrvs) {
|
for (auto & i : drv.inputDrvs) {
|
||||||
Hash h = drvHashes[i.first];
|
Hash h = drvHashes[i.first];
|
||||||
if (h.type == htUnknown) {
|
if (!h) {
|
||||||
assert(store.isValidPath(i.first));
|
assert(store.isValidPath(i.first));
|
||||||
Derivation drv2 = readDerivation(i.first);
|
Derivation drv2 = readDerivation(i.first);
|
||||||
h = hashDerivationModulo(store, drv2);
|
h = hashDerivationModulo(store, drv2);
|
||||||
|
|
|
@ -225,7 +225,7 @@ Path Downloader::downloadCached(ref<Store> store, const string & url_, bool unpa
|
||||||
{
|
{
|
||||||
auto url = resolveUri(url_);
|
auto url = resolveUri(url_);
|
||||||
|
|
||||||
Path cacheDir = getEnv("XDG_CACHE_HOME", getEnv("HOME", "") + "/.cache") + "/nix/tarballs";
|
Path cacheDir = getCacheDir() + "/nix/tarballs";
|
||||||
createDirs(cacheDir);
|
createDirs(cacheDir);
|
||||||
|
|
||||||
string urlHash = printHash32(hashString(htSHA256, url));
|
string urlHash = printHash32(hashString(htSHA256, url));
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#include "binary-cache-store.hh"
|
#include "binary-cache-store.hh"
|
||||||
#include "download.hh"
|
#include "download.hh"
|
||||||
#include "globals.hh"
|
#include "globals.hh"
|
||||||
|
#include "nar-info-disk-cache.hh"
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
|
@ -24,13 +25,23 @@ public:
|
||||||
{
|
{
|
||||||
if (cacheUri.back() == '/')
|
if (cacheUri.back() == '/')
|
||||||
cacheUri.pop_back();
|
cacheUri.pop_back();
|
||||||
|
|
||||||
|
diskCache = getNarInfoDiskCache();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string getUri() override
|
||||||
|
{
|
||||||
|
return cacheUri;
|
||||||
}
|
}
|
||||||
|
|
||||||
void init() override
|
void init() override
|
||||||
{
|
{
|
||||||
// FIXME: do this lazily?
|
// FIXME: do this lazily?
|
||||||
if (!fileExists("nix-cache-info"))
|
if (!diskCache->cacheExists(cacheUri)) {
|
||||||
throw Error(format("‘%s’ does not appear to be a binary cache") % cacheUri);
|
if (!fileExists("nix-cache-info"))
|
||||||
|
throw Error(format("‘%s’ does not appear to be a binary cache") % cacheUri);
|
||||||
|
diskCache->createCache(cacheUri);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
|
@ -580,7 +580,6 @@ uint64_t LocalStore::addValidPath(State & state,
|
||||||
{
|
{
|
||||||
auto state_(Store::state.lock());
|
auto state_(Store::state.lock());
|
||||||
state_->pathInfoCache.upsert(info.path, std::make_shared<ValidPathInfo>(info));
|
state_->pathInfoCache.upsert(info.path, std::make_shared<ValidPathInfo>(info));
|
||||||
stats.pathInfoCacheSize = state_->pathInfoCache.size();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return id;
|
return id;
|
||||||
|
@ -1069,7 +1068,6 @@ void LocalStore::invalidatePath(State & state, const Path & path)
|
||||||
{
|
{
|
||||||
auto state_(Store::state.lock());
|
auto state_(Store::state.lock());
|
||||||
state_->pathInfoCache.erase(path);
|
state_->pathInfoCache.erase(path);
|
||||||
stats.pathInfoCacheSize = state_->pathInfoCache.size();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
217
src/libstore/nar-info-disk-cache.cc
Normal file
217
src/libstore/nar-info-disk-cache.cc
Normal file
|
@ -0,0 +1,217 @@
|
||||||
|
#include "nar-info-disk-cache.hh"
|
||||||
|
#include "sync.hh"
|
||||||
|
#include "sqlite.hh"
|
||||||
|
#include "globals.hh"
|
||||||
|
|
||||||
|
#include <sqlite3.h>
|
||||||
|
|
||||||
|
namespace nix {
|
||||||
|
|
||||||
|
static const char * schema = R"sql(
|
||||||
|
|
||||||
|
create table if not exists BinaryCaches (
|
||||||
|
id integer primary key autoincrement not null,
|
||||||
|
url text unique not null,
|
||||||
|
timestamp integer not null,
|
||||||
|
storeDir text not null,
|
||||||
|
wantMassQuery integer not null,
|
||||||
|
priority integer not null
|
||||||
|
);
|
||||||
|
|
||||||
|
create table if not exists NARs (
|
||||||
|
cache integer not null,
|
||||||
|
storePath text not null,
|
||||||
|
url text,
|
||||||
|
compression text,
|
||||||
|
fileHash text,
|
||||||
|
fileSize integer,
|
||||||
|
narHash text,
|
||||||
|
narSize integer,
|
||||||
|
refs text,
|
||||||
|
deriver text,
|
||||||
|
sigs text,
|
||||||
|
timestamp integer not null,
|
||||||
|
primary key (cache, storePath),
|
||||||
|
foreign key (cache) references BinaryCaches(id) on delete cascade
|
||||||
|
);
|
||||||
|
|
||||||
|
create table if not exists NARExistence (
|
||||||
|
cache integer not null,
|
||||||
|
storePath text not null,
|
||||||
|
exist integer not null,
|
||||||
|
timestamp integer not null,
|
||||||
|
primary key (cache, storePath),
|
||||||
|
foreign key (cache) references BinaryCaches(id) on delete cascade
|
||||||
|
);
|
||||||
|
|
||||||
|
)sql";
|
||||||
|
|
||||||
|
class NarInfoDiskCacheImpl : public NarInfoDiskCache
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
/* How long negative lookups are valid. */
|
||||||
|
const int ttlNegative = 3600;
|
||||||
|
|
||||||
|
struct State
|
||||||
|
{
|
||||||
|
SQLite db;
|
||||||
|
SQLiteStmt insertCache, queryCache, insertNAR, queryNAR, insertNARExistence, queryNARExistence;
|
||||||
|
std::map<std::string, int> caches;
|
||||||
|
};
|
||||||
|
|
||||||
|
Sync<State> _state;
|
||||||
|
|
||||||
|
NarInfoDiskCacheImpl()
|
||||||
|
{
|
||||||
|
auto state(_state.lock());
|
||||||
|
|
||||||
|
Path dbPath = getCacheDir() + "/nix/binary-cache-v3.sqlite";
|
||||||
|
createDirs(dirOf(dbPath));
|
||||||
|
|
||||||
|
if (sqlite3_open_v2(dbPath.c_str(), &state->db.db,
|
||||||
|
SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, 0) != SQLITE_OK)
|
||||||
|
throw Error(format("cannot open store cache ‘%s’") % dbPath);
|
||||||
|
|
||||||
|
if (sqlite3_busy_timeout(state->db, 60 * 60 * 1000) != SQLITE_OK)
|
||||||
|
throwSQLiteError(state->db, "setting timeout");
|
||||||
|
|
||||||
|
// We can always reproduce the cache.
|
||||||
|
if (sqlite3_exec(state->db, "pragma synchronous = off", 0, 0, 0) != SQLITE_OK)
|
||||||
|
throwSQLiteError(state->db, "making database asynchronous");
|
||||||
|
if (sqlite3_exec(state->db, "pragma main.journal_mode = truncate", 0, 0, 0) != SQLITE_OK)
|
||||||
|
throwSQLiteError(state->db, "setting journal mode");
|
||||||
|
|
||||||
|
if (sqlite3_exec(state->db, schema, 0, 0, 0) != SQLITE_OK)
|
||||||
|
throwSQLiteError(state->db, "initialising database schema");
|
||||||
|
|
||||||
|
state->insertCache.create(state->db,
|
||||||
|
"insert or replace into BinaryCaches(url, timestamp, storeDir, wantMassQuery, priority) values (?, ?, ?, ?, ?)");
|
||||||
|
|
||||||
|
state->queryCache.create(state->db,
|
||||||
|
"select id, storeDir, wantMassQuery, priority from BinaryCaches where url = ?");
|
||||||
|
|
||||||
|
state->insertNAR.create(state->db,
|
||||||
|
"insert or replace into NARs(cache, storePath, url, compression, fileHash, fileSize, narHash, "
|
||||||
|
"narSize, refs, deriver, sigs, timestamp) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
|
||||||
|
|
||||||
|
state->queryNAR.create(state->db,
|
||||||
|
"select * from NARs where cache = ? and storePath = ?");
|
||||||
|
|
||||||
|
state->insertNARExistence.create(state->db,
|
||||||
|
"insert or replace into NARExistence(cache, storePath, exist, timestamp) values (?, ?, ?, ?)");
|
||||||
|
|
||||||
|
state->queryNARExistence.create(state->db,
|
||||||
|
"select exist, timestamp from NARExistence where cache = ? and storePath = ?");
|
||||||
|
}
|
||||||
|
|
||||||
|
int uriToInt(State & state, const std::string & uri)
|
||||||
|
{
|
||||||
|
auto i = state.caches.find(uri);
|
||||||
|
if (i == state.caches.end()) abort();
|
||||||
|
return i->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
void createCache(const std::string & uri) override
|
||||||
|
{
|
||||||
|
auto state(_state.lock());
|
||||||
|
|
||||||
|
// FIXME: race
|
||||||
|
|
||||||
|
state->insertCache.use()(uri)(time(0))(settings.nixStore)(1)(0).exec();
|
||||||
|
assert(sqlite3_changes(state->db) == 1);
|
||||||
|
state->caches[uri] = sqlite3_last_insert_rowid(state->db);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cacheExists(const std::string & uri) override
|
||||||
|
{
|
||||||
|
auto state(_state.lock());
|
||||||
|
|
||||||
|
auto i = state->caches.find(uri);
|
||||||
|
if (i != state->caches.end()) return true;
|
||||||
|
|
||||||
|
auto queryCache(state->queryCache.use()(uri));
|
||||||
|
|
||||||
|
if (queryCache.next()) {
|
||||||
|
state->caches[uri] = queryCache.getInt(0);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<Outcome, std::shared_ptr<NarInfo>> lookupNarInfo(
|
||||||
|
const std::string & uri, const Path & storePath) override
|
||||||
|
{
|
||||||
|
auto state(_state.lock());
|
||||||
|
|
||||||
|
auto queryNAR(state->queryNAR.use()
|
||||||
|
(uriToInt(*state, uri))
|
||||||
|
(baseNameOf(storePath)));
|
||||||
|
|
||||||
|
if (!queryNAR.next())
|
||||||
|
// FIXME: check NARExistence
|
||||||
|
return {oUnknown, 0};
|
||||||
|
|
||||||
|
auto narInfo = make_ref<NarInfo>();
|
||||||
|
|
||||||
|
// FIXME: implement TTL.
|
||||||
|
|
||||||
|
narInfo->path = storePath;
|
||||||
|
narInfo->url = queryNAR.getStr(2);
|
||||||
|
narInfo->compression = queryNAR.getStr(3);
|
||||||
|
if (!queryNAR.isNull(4))
|
||||||
|
narInfo->fileHash = parseHash(queryNAR.getStr(4));
|
||||||
|
narInfo->fileSize = queryNAR.getInt(5);
|
||||||
|
narInfo->narHash = parseHash(queryNAR.getStr(6));
|
||||||
|
narInfo->narSize = queryNAR.getInt(7);
|
||||||
|
for (auto & r : tokenizeString<Strings>(queryNAR.getStr(8), " "))
|
||||||
|
narInfo->references.insert(settings.nixStore + "/" + r);
|
||||||
|
if (!queryNAR.isNull(9))
|
||||||
|
narInfo->deriver = settings.nixStore + "/" + queryNAR.getStr(9);
|
||||||
|
for (auto & sig : tokenizeString<Strings>(queryNAR.getStr(10), " "))
|
||||||
|
narInfo->sigs.insert(sig);
|
||||||
|
|
||||||
|
return {oValid, narInfo};
|
||||||
|
}
|
||||||
|
|
||||||
|
void upsertNarInfo(
|
||||||
|
const std::string & uri, std::shared_ptr<ValidPathInfo> info) override
|
||||||
|
{
|
||||||
|
auto state(_state.lock());
|
||||||
|
|
||||||
|
if (info) {
|
||||||
|
|
||||||
|
auto narInfo = std::dynamic_pointer_cast<NarInfo>(info);
|
||||||
|
|
||||||
|
state->insertNAR.use()
|
||||||
|
(uriToInt(*state, uri))
|
||||||
|
(baseNameOf(info->path))
|
||||||
|
(narInfo ? narInfo->url : "", narInfo != 0)
|
||||||
|
(narInfo ? narInfo->compression : "", narInfo != 0)
|
||||||
|
(narInfo && narInfo->fileHash ? narInfo->fileHash.to_string() : "", narInfo && narInfo->fileHash)
|
||||||
|
(narInfo ? narInfo->fileSize : 0, narInfo != 0 && narInfo->fileSize)
|
||||||
|
(info->narHash.to_string())
|
||||||
|
(info->narSize)
|
||||||
|
(concatStringsSep(" ", info->shortRefs()))
|
||||||
|
(info->deriver != "" ? baseNameOf(info->deriver) : "", info->deriver != "")
|
||||||
|
(concatStringsSep(" ", info->sigs))
|
||||||
|
(time(0)).exec();
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// not implemented
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
ref<NarInfoDiskCache> getNarInfoDiskCache()
|
||||||
|
{
|
||||||
|
static Sync<std::shared_ptr<NarInfoDiskCache>> cache;
|
||||||
|
|
||||||
|
auto cache_(cache.lock());
|
||||||
|
if (!*cache_) *cache_ = std::make_shared<NarInfoDiskCacheImpl>();
|
||||||
|
return ref<NarInfoDiskCache>(*cache_);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
28
src/libstore/nar-info-disk-cache.hh
Normal file
28
src/libstore/nar-info-disk-cache.hh
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "ref.hh"
|
||||||
|
#include "nar-info.hh"
|
||||||
|
|
||||||
|
namespace nix {
|
||||||
|
|
||||||
|
class NarInfoDiskCache
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef enum { oValid, oInvalid, oUnknown } Outcome;
|
||||||
|
|
||||||
|
virtual void createCache(const std::string & uri) = 0;
|
||||||
|
|
||||||
|
virtual bool cacheExists(const std::string & uri) = 0;
|
||||||
|
|
||||||
|
virtual std::pair<Outcome, std::shared_ptr<NarInfo>> lookupNarInfo(
|
||||||
|
const std::string & uri, const Path & storePath) = 0;
|
||||||
|
|
||||||
|
virtual void upsertNarInfo(
|
||||||
|
const std::string & uri, std::shared_ptr<ValidPathInfo> narInfo) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Return a singleton cache object that can be used concurrently by
|
||||||
|
multiple threads. */
|
||||||
|
ref<NarInfoDiskCache> getNarInfoDiskCache();
|
||||||
|
|
||||||
|
}
|
|
@ -5,16 +5,16 @@ namespace nix {
|
||||||
|
|
||||||
NarInfo::NarInfo(const std::string & s, const std::string & whence)
|
NarInfo::NarInfo(const std::string & s, const std::string & whence)
|
||||||
{
|
{
|
||||||
auto corrupt = [&]() {
|
auto corrupt = [&]() [[noreturn]] {
|
||||||
throw Error("NAR info file ‘%1%’ is corrupt");
|
throw Error("NAR info file ‘%1%’ is corrupt");
|
||||||
};
|
};
|
||||||
|
|
||||||
auto parseHashField = [&](const string & s) {
|
auto parseHashField = [&](const string & s) {
|
||||||
string::size_type colon = s.find(':');
|
try {
|
||||||
if (colon == string::npos) corrupt();
|
return parseHash(s);
|
||||||
HashType ht = parseHashType(string(s, 0, colon));
|
} catch (BadHash &) {
|
||||||
if (ht == htUnknown) corrupt();
|
corrupt();
|
||||||
return parseHash16or32(ht, string(s, colon + 1));
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
size_t pos = 0;
|
size_t pos = 0;
|
||||||
|
@ -103,12 +103,4 @@ std::string NarInfo::to_string() const
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
Strings NarInfo::shortRefs() const
|
|
||||||
{
|
|
||||||
Strings refs;
|
|
||||||
for (auto & r : references)
|
|
||||||
refs.push_back(baseNameOf(r));
|
|
||||||
return refs;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,10 +19,6 @@ struct NarInfo : ValidPathInfo
|
||||||
NarInfo(const std::string & s, const std::string & whence);
|
NarInfo(const std::string & s, const std::string & whence);
|
||||||
|
|
||||||
std::string to_string() const;
|
std::string to_string() const;
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
Strings shortRefs() const;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -489,7 +489,6 @@ void RemoteStore::collectGarbage(const GCOptions & options, GCResults & results)
|
||||||
{
|
{
|
||||||
auto state_(Store::state.lock());
|
auto state_(Store::state.lock());
|
||||||
state_->pathInfoCache.clear();
|
state_->pathInfoCache.clear();
|
||||||
stats.pathInfoCacheSize = 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -139,6 +139,11 @@ int64_t SQLiteStmt::Use::getInt(int col)
|
||||||
return sqlite3_column_int64(stmt, col);
|
return sqlite3_column_int64(stmt, col);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool SQLiteStmt::Use::isNull(int col)
|
||||||
|
{
|
||||||
|
return sqlite3_column_type(stmt, col) == SQLITE_NULL;
|
||||||
|
}
|
||||||
|
|
||||||
SQLiteTxn::SQLiteTxn(sqlite3 * db)
|
SQLiteTxn::SQLiteTxn(sqlite3 * db)
|
||||||
{
|
{
|
||||||
this->db = db;
|
this->db = db;
|
||||||
|
|
|
@ -58,6 +58,7 @@ struct SQLiteStmt
|
||||||
|
|
||||||
std::string getStr(int col);
|
std::string getStr(int col);
|
||||||
int64_t getInt(int col);
|
int64_t getInt(int col);
|
||||||
|
bool isNull(int col);
|
||||||
};
|
};
|
||||||
|
|
||||||
Use use()
|
Use use()
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
#include "globals.hh"
|
#include "globals.hh"
|
||||||
#include "store-api.hh"
|
#include "store-api.hh"
|
||||||
#include "util.hh"
|
#include "util.hh"
|
||||||
|
#include "nar-info-disk-cache.hh"
|
||||||
|
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
@ -225,6 +226,12 @@ Path computeStorePathForText(const string & name, const string & s,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::string Store::getUri()
|
||||||
|
{
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
bool Store::isValidPath(const Path & storePath)
|
bool Store::isValidPath(const Path & storePath)
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
|
@ -236,7 +243,19 @@ bool Store::isValidPath(const Path & storePath)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (diskCache) {
|
||||||
|
auto res = diskCache->lookupNarInfo(getUri(), storePath);
|
||||||
|
if (res.first != NarInfoDiskCache::oUnknown) {
|
||||||
|
auto state_(state.lock());
|
||||||
|
state_->pathInfoCache.upsert(storePath,
|
||||||
|
res.first == NarInfoDiskCache::oInvalid ? 0 : res.second);
|
||||||
|
return res.first == NarInfoDiskCache::oValid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return isValidPathUncached(storePath);
|
return isValidPathUncached(storePath);
|
||||||
|
|
||||||
|
// FIXME: insert result into NARExistence table of diskCache.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -253,12 +272,26 @@ ref<const ValidPathInfo> Store::queryPathInfo(const Path & storePath)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (diskCache) {
|
||||||
|
auto res = diskCache->lookupNarInfo(getUri(), storePath);
|
||||||
|
if (res.first != NarInfoDiskCache::oUnknown) {
|
||||||
|
auto state_(state.lock());
|
||||||
|
state_->pathInfoCache.upsert(storePath,
|
||||||
|
res.first == NarInfoDiskCache::oInvalid ? 0 : res.second);
|
||||||
|
if (res.first == NarInfoDiskCache::oInvalid)
|
||||||
|
throw InvalidPath(format("path ‘%s’ is not valid") % storePath);
|
||||||
|
return ref<ValidPathInfo>(res.second);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
auto info = queryPathInfoUncached(storePath);
|
auto info = queryPathInfoUncached(storePath);
|
||||||
|
|
||||||
|
if (diskCache && info)
|
||||||
|
diskCache->upsertNarInfo(getUri(), info);
|
||||||
|
|
||||||
{
|
{
|
||||||
auto state_(state.lock());
|
auto state_(state.lock());
|
||||||
state_->pathInfoCache.upsert(storePath, info);
|
state_->pathInfoCache.upsert(storePath, info);
|
||||||
stats.pathInfoCacheSize = state_->pathInfoCache.size();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!info) {
|
if (!info) {
|
||||||
|
@ -303,6 +336,10 @@ string Store::makeValidityRegistration(const PathSet & paths,
|
||||||
|
|
||||||
const Store::Stats & Store::getStats()
|
const Store::Stats & Store::getStats()
|
||||||
{
|
{
|
||||||
|
{
|
||||||
|
auto state_(state.lock());
|
||||||
|
stats.pathInfoCacheSize = state_->pathInfoCache.size();
|
||||||
|
}
|
||||||
return stats;
|
return stats;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -356,7 +393,7 @@ void Store::exportPaths(const Paths & paths,
|
||||||
|
|
||||||
std::string ValidPathInfo::fingerprint() const
|
std::string ValidPathInfo::fingerprint() const
|
||||||
{
|
{
|
||||||
if (narSize == 0 || narHash.type == htUnknown)
|
if (narSize == 0 || !narHash)
|
||||||
throw Error(format("cannot calculate fingerprint of path ‘%s’ because its size/hash is not known")
|
throw Error(format("cannot calculate fingerprint of path ‘%s’ because its size/hash is not known")
|
||||||
% path);
|
% path);
|
||||||
return
|
return
|
||||||
|
@ -389,6 +426,15 @@ bool ValidPathInfo::checkSignature(const PublicKeys & publicKeys, const std::str
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Strings ValidPathInfo::shortRefs() const
|
||||||
|
{
|
||||||
|
Strings refs;
|
||||||
|
for (auto & r : references)
|
||||||
|
refs.push_back(baseNameOf(r));
|
||||||
|
return refs;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -134,6 +134,8 @@ struct ValidPathInfo
|
||||||
/* Verify a single signature. */
|
/* Verify a single signature. */
|
||||||
bool checkSignature(const PublicKeys & publicKeys, const std::string & sig) const;
|
bool checkSignature(const PublicKeys & publicKeys, const std::string & sig) const;
|
||||||
|
|
||||||
|
Strings shortRefs() const;
|
||||||
|
|
||||||
virtual ~ValidPathInfo() { }
|
virtual ~ValidPathInfo() { }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -170,6 +172,7 @@ struct BuildResult
|
||||||
struct BasicDerivation;
|
struct BasicDerivation;
|
||||||
struct Derivation;
|
struct Derivation;
|
||||||
class FSAccessor;
|
class FSAccessor;
|
||||||
|
class NarInfoDiskCache;
|
||||||
|
|
||||||
|
|
||||||
class Store : public std::enable_shared_from_this<Store>
|
class Store : public std::enable_shared_from_this<Store>
|
||||||
|
@ -183,10 +186,14 @@ protected:
|
||||||
|
|
||||||
Sync<State> state;
|
Sync<State> state;
|
||||||
|
|
||||||
|
std::shared_ptr<NarInfoDiskCache> diskCache;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
virtual ~Store() { }
|
virtual ~Store() { }
|
||||||
|
|
||||||
|
virtual std::string getUri();
|
||||||
|
|
||||||
/* Check whether a path is valid. */
|
/* Check whether a path is valid. */
|
||||||
bool isValidPath(const Path & path);
|
bool isValidPath(const Path & path);
|
||||||
|
|
||||||
|
|
|
@ -33,7 +33,7 @@ Hash::Hash(HashType type)
|
||||||
else if (type == htSHA1) hashSize = sha1HashSize;
|
else if (type == htSHA1) hashSize = sha1HashSize;
|
||||||
else if (type == htSHA256) hashSize = sha256HashSize;
|
else if (type == htSHA256) hashSize = sha256HashSize;
|
||||||
else if (type == htSHA512) hashSize = sha512HashSize;
|
else if (type == htSHA512) hashSize = sha512HashSize;
|
||||||
else throw Error("unknown hash type");
|
else abort();
|
||||||
assert(hashSize <= maxHashSize);
|
assert(hashSize <= maxHashSize);
|
||||||
memset(hash, 0, maxHashSize);
|
memset(hash, 0, maxHashSize);
|
||||||
}
|
}
|
||||||
|
@ -64,6 +64,12 @@ bool Hash::operator < (const Hash & h) const
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::string Hash::to_string(bool base32) const
|
||||||
|
{
|
||||||
|
return printHashType(type) + ":" + (base32 ? printHash32(*this) : printHash(*this));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
const string base16Chars = "0123456789abcdef";
|
const string base16Chars = "0123456789abcdef";
|
||||||
|
|
||||||
|
|
||||||
|
@ -78,15 +84,28 @@ string printHash(const Hash & hash)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Hash parseHash(const string & s)
|
||||||
|
{
|
||||||
|
string::size_type colon = s.find(':');
|
||||||
|
if (colon == string::npos)
|
||||||
|
throw BadHash(format("invalid hash ‘%s’") % s);
|
||||||
|
string hts = string(s, 0, colon);
|
||||||
|
HashType ht = parseHashType(hts);
|
||||||
|
if (ht == htUnknown)
|
||||||
|
throw BadHash(format("unknown hash type ‘%s’") % hts);
|
||||||
|
return parseHash16or32(ht, string(s, colon + 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
Hash parseHash(HashType ht, const string & s)
|
Hash parseHash(HashType ht, const string & s)
|
||||||
{
|
{
|
||||||
Hash hash(ht);
|
Hash hash(ht);
|
||||||
if (s.length() != hash.hashSize * 2)
|
if (s.length() != hash.hashSize * 2)
|
||||||
throw Error(format("invalid hash ‘%1%’") % s);
|
throw BadHash(format("invalid hash ‘%1%’") % s);
|
||||||
for (unsigned int i = 0; i < hash.hashSize; i++) {
|
for (unsigned int i = 0; i < hash.hashSize; i++) {
|
||||||
string s2(s, i * 2, 2);
|
string s2(s, i * 2, 2);
|
||||||
if (!isxdigit(s2[0]) || !isxdigit(s2[1]))
|
if (!isxdigit(s2[0]) || !isxdigit(s2[1]))
|
||||||
throw Error(format("invalid hash ‘%1%’") % s);
|
throw BadHash(format("invalid hash ‘%1%’") % s);
|
||||||
std::istringstream str(s2);
|
std::istringstream str(s2);
|
||||||
int n;
|
int n;
|
||||||
str >> std::hex >> n;
|
str >> std::hex >> n;
|
||||||
|
@ -103,6 +122,7 @@ const string base32Chars = "0123456789abcdfghijklmnpqrsvwxyz";
|
||||||
string printHash32(const Hash & hash)
|
string printHash32(const Hash & hash)
|
||||||
{
|
{
|
||||||
size_t len = hash.base32Len();
|
size_t len = hash.base32Len();
|
||||||
|
assert(len);
|
||||||
|
|
||||||
string s;
|
string s;
|
||||||
s.reserve(len);
|
s.reserve(len);
|
||||||
|
@ -139,7 +159,7 @@ Hash parseHash32(HashType ht, const string & s)
|
||||||
for (digit = 0; digit < base32Chars.size(); ++digit) /* !!! slow */
|
for (digit = 0; digit < base32Chars.size(); ++digit) /* !!! slow */
|
||||||
if (base32Chars[digit] == c) break;
|
if (base32Chars[digit] == c) break;
|
||||||
if (digit >= 32)
|
if (digit >= 32)
|
||||||
throw Error(format("invalid base-32 hash ‘%1%’") % s);
|
throw BadHash(format("invalid base-32 hash ‘%1%’") % s);
|
||||||
unsigned int b = n * 5;
|
unsigned int b = n * 5;
|
||||||
unsigned int i = b / 8;
|
unsigned int i = b / 8;
|
||||||
unsigned int j = b % 8;
|
unsigned int j = b % 8;
|
||||||
|
@ -161,7 +181,7 @@ Hash parseHash16or32(HashType ht, const string & s)
|
||||||
/* base-32 representation */
|
/* base-32 representation */
|
||||||
hash = parseHash32(ht, s);
|
hash = parseHash32(ht, s);
|
||||||
else
|
else
|
||||||
throw Error(format("hash ‘%1%’ has wrong length for hash type ‘%2%’")
|
throw BadHash(format("hash ‘%1%’ has wrong length for hash type ‘%2%’")
|
||||||
% s % printHashType(ht));
|
% s % printHashType(ht));
|
||||||
return hash;
|
return hash;
|
||||||
}
|
}
|
||||||
|
@ -322,7 +342,7 @@ string printHashType(HashType ht)
|
||||||
else if (ht == htSHA1) return "sha1";
|
else if (ht == htSHA1) return "sha1";
|
||||||
else if (ht == htSHA256) return "sha256";
|
else if (ht == htSHA256) return "sha256";
|
||||||
else if (ht == htSHA512) return "sha512";
|
else if (ht == htSHA512) return "sha512";
|
||||||
else throw Error("cannot print unknown hash type");
|
else abort();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,9 @@
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
|
|
||||||
|
MakeError(BadHash, Error);
|
||||||
|
|
||||||
|
|
||||||
enum HashType : char { htUnknown, htMD5, htSHA1, htSHA256, htSHA512 };
|
enum HashType : char { htUnknown, htMD5, htSHA1, htSHA256, htSHA512 };
|
||||||
|
|
||||||
|
|
||||||
|
@ -26,12 +29,15 @@ struct Hash
|
||||||
|
|
||||||
HashType type;
|
HashType type;
|
||||||
|
|
||||||
/* Create an unusable hash object. */
|
/* Create an unset hash object. */
|
||||||
Hash();
|
Hash();
|
||||||
|
|
||||||
/* Create a zero-filled hash object. */
|
/* Create a zero-filled hash object. */
|
||||||
Hash(HashType type);
|
Hash(HashType type);
|
||||||
|
|
||||||
|
/* Check whether a hash is set. */
|
||||||
|
operator bool () const { return type != htUnknown; }
|
||||||
|
|
||||||
/* Check whether two hash are equal. */
|
/* Check whether two hash are equal. */
|
||||||
bool operator == (const Hash & h2) const;
|
bool operator == (const Hash & h2) const;
|
||||||
|
|
||||||
|
@ -52,12 +58,16 @@ struct Hash
|
||||||
{
|
{
|
||||||
return (hashSize * 8 - 1) / 5 + 1;
|
return (hashSize * 8 - 1) / 5 + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string to_string(bool base32 = true) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/* Convert a hash to a hexadecimal representation. */
|
/* Convert a hash to a hexadecimal representation. */
|
||||||
string printHash(const Hash & hash);
|
string printHash(const Hash & hash);
|
||||||
|
|
||||||
|
Hash parseHash(const string & s);
|
||||||
|
|
||||||
/* Parse a hexadecimal representation of a hash code. */
|
/* Parse a hexadecimal representation of a hash code. */
|
||||||
Hash parseHash(HashType ht, const string & s);
|
Hash parseHash(HashType ht, const string & s);
|
||||||
|
|
||||||
|
|
|
@ -403,6 +403,18 @@ Path createTempDir(const Path & tmpRoot, const Path & prefix,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Path getCacheDir()
|
||||||
|
{
|
||||||
|
Path cacheDir = getEnv("XDG_CACHE_HOME");
|
||||||
|
if (cacheDir.empty()) {
|
||||||
|
Path homeDir = getEnv("HOME");
|
||||||
|
if (homeDir.empty()) throw Error("$XDG_CACHE_HOME and $HOME are not set");
|
||||||
|
cacheDir = homeDir + "/.cache";
|
||||||
|
}
|
||||||
|
return cacheDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
Paths createDirs(const Path & path)
|
Paths createDirs(const Path & path)
|
||||||
{
|
{
|
||||||
Paths created;
|
Paths created;
|
||||||
|
|
|
@ -102,6 +102,9 @@ void deletePath(const Path & path, unsigned long long & bytesFreed);
|
||||||
Path createTempDir(const Path & tmpRoot = "", const Path & prefix = "nix",
|
Path createTempDir(const Path & tmpRoot = "", const Path & prefix = "nix",
|
||||||
bool includePid = true, bool useGlobalCounter = true, mode_t mode = 0755);
|
bool includePid = true, bool useGlobalCounter = true, mode_t mode = 0755);
|
||||||
|
|
||||||
|
/* Return the path to $XDG_CACHE_HOME/.cache. */
|
||||||
|
Path getCacheDir();
|
||||||
|
|
||||||
/* Create a directory and all its parents, if necessary. Returns the
|
/* Create a directory and all its parents, if necessary. Returns the
|
||||||
list of created directories, in order of creation. */
|
list of created directories, in order of creation. */
|
||||||
Paths createDirs(const Path & path);
|
Paths createDirs(const Path & path);
|
||||||
|
|
Loading…
Reference in a new issue