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->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); | ||||
| 
 | ||||
|     /* Compress the NAR. */ | ||||
|  | @ -96,7 +96,6 @@ void BinaryCacheStore::addToCache(const ValidPathInfo & info, | |||
|     { | ||||
|         auto state_(state.lock()); | ||||
|         state_->pathInfoCache.upsert(narInfo->path, std::shared_ptr<NarInfo>(narInfo)); | ||||
|         stats.pathInfoCacheSize = state_->pathInfoCache.size(); | ||||
|     } | ||||
| 
 | ||||
|     stats.narInfoWrite++; | ||||
|  |  | |||
|  | @ -290,7 +290,7 @@ Hash hashDerivationModulo(Store & store, Derivation drv) | |||
|     DerivationInputs inputs2; | ||||
|     for (auto & i : drv.inputDrvs) { | ||||
|         Hash h = drvHashes[i.first]; | ||||
|         if (h.type == htUnknown) { | ||||
|         if (!h) { | ||||
|             assert(store.isValidPath(i.first)); | ||||
|             Derivation drv2 = readDerivation(i.first); | ||||
|             h = hashDerivationModulo(store, drv2); | ||||
|  |  | |||
|  | @ -225,7 +225,7 @@ Path Downloader::downloadCached(ref<Store> store, const string & url_, bool unpa | |||
| { | ||||
|     auto url = resolveUri(url_); | ||||
| 
 | ||||
|     Path cacheDir = getEnv("XDG_CACHE_HOME", getEnv("HOME", "") + "/.cache") + "/nix/tarballs"; | ||||
|     Path cacheDir = getCacheDir() + "/nix/tarballs"; | ||||
|     createDirs(cacheDir); | ||||
| 
 | ||||
|     string urlHash = printHash32(hashString(htSHA256, url)); | ||||
|  |  | |||
|  | @ -1,6 +1,7 @@ | |||
| #include "binary-cache-store.hh" | ||||
| #include "download.hh" | ||||
| #include "globals.hh" | ||||
| #include "nar-info-disk-cache.hh" | ||||
| 
 | ||||
| namespace nix { | ||||
| 
 | ||||
|  | @ -24,13 +25,23 @@ public: | |||
|     { | ||||
|         if (cacheUri.back() == '/') | ||||
|             cacheUri.pop_back(); | ||||
| 
 | ||||
|         diskCache = getNarInfoDiskCache(); | ||||
|     } | ||||
| 
 | ||||
|     std::string getUri() override | ||||
|     { | ||||
|         return cacheUri; | ||||
|     } | ||||
| 
 | ||||
|     void init() override | ||||
|     { | ||||
|         // FIXME: do this lazily?
 | ||||
|         if (!diskCache->cacheExists(cacheUri)) { | ||||
|             if (!fileExists("nix-cache-info")) | ||||
|                 throw Error(format("‘%s’ does not appear to be a binary cache") % cacheUri); | ||||
|             diskCache->createCache(cacheUri); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| protected: | ||||
|  |  | |||
|  | @ -580,7 +580,6 @@ uint64_t LocalStore::addValidPath(State & state, | |||
|     { | ||||
|         auto state_(Store::state.lock()); | ||||
|         state_->pathInfoCache.upsert(info.path, std::make_shared<ValidPathInfo>(info)); | ||||
|         stats.pathInfoCacheSize = state_->pathInfoCache.size(); | ||||
|     } | ||||
| 
 | ||||
|     return id; | ||||
|  | @ -1069,7 +1068,6 @@ void LocalStore::invalidatePath(State & state, const Path & path) | |||
|     { | ||||
|         auto state_(Store::state.lock()); | ||||
|         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) | ||||
| { | ||||
|     auto corrupt = [&]() { | ||||
|     auto corrupt = [&]() [[noreturn]] { | ||||
|         throw Error("NAR info file ‘%1%’ is corrupt"); | ||||
|     }; | ||||
| 
 | ||||
|     auto parseHashField = [&](const string & s) { | ||||
|         string::size_type colon = s.find(':'); | ||||
|         if (colon == string::npos) corrupt(); | ||||
|         HashType ht = parseHashType(string(s, 0, colon)); | ||||
|         if (ht == htUnknown) corrupt(); | ||||
|         return parseHash16or32(ht, string(s, colon + 1)); | ||||
|         try { | ||||
|             return parseHash(s); | ||||
|         } catch (BadHash &) { | ||||
|             corrupt(); | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
|     size_t pos = 0; | ||||
|  | @ -103,12 +103,4 @@ std::string NarInfo::to_string() const | |||
|     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); | ||||
| 
 | ||||
|     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()); | ||||
|         state_->pathInfoCache.clear(); | ||||
|         stats.pathInfoCacheSize = 0; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -139,6 +139,11 @@ int64_t SQLiteStmt::Use::getInt(int 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) | ||||
| { | ||||
|     this->db = db; | ||||
|  |  | |||
|  | @ -58,6 +58,7 @@ struct SQLiteStmt | |||
| 
 | ||||
|         std::string getStr(int col); | ||||
|         int64_t getInt(int col); | ||||
|         bool isNull(int col); | ||||
|     }; | ||||
| 
 | ||||
|     Use use() | ||||
|  |  | |||
|  | @ -2,6 +2,7 @@ | |||
| #include "globals.hh" | ||||
| #include "store-api.hh" | ||||
| #include "util.hh" | ||||
| #include "nar-info-disk-cache.hh" | ||||
| 
 | ||||
| 
 | ||||
| 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) | ||||
| { | ||||
|     { | ||||
|  | @ -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); | ||||
| 
 | ||||
|     // 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); | ||||
| 
 | ||||
|     if (diskCache && info) | ||||
|         diskCache->upsertNarInfo(getUri(), info); | ||||
| 
 | ||||
|     { | ||||
|         auto state_(state.lock()); | ||||
|         state_->pathInfoCache.upsert(storePath, info); | ||||
|         stats.pathInfoCacheSize = state_->pathInfoCache.size(); | ||||
|     } | ||||
| 
 | ||||
|     if (!info) { | ||||
|  | @ -303,6 +336,10 @@ string Store::makeValidityRegistration(const PathSet & paths, | |||
| 
 | ||||
| const Store::Stats & Store::getStats() | ||||
| { | ||||
|     { | ||||
|         auto state_(state.lock()); | ||||
|         stats.pathInfoCacheSize = state_->pathInfoCache.size(); | ||||
|     } | ||||
|     return stats; | ||||
| } | ||||
| 
 | ||||
|  | @ -356,7 +393,7 @@ void Store::exportPaths(const Paths & paths, | |||
| 
 | ||||
| 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") | ||||
|             % path); | ||||
|     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. */ | ||||
|     bool checkSignature(const PublicKeys & publicKeys, const std::string & sig) const; | ||||
| 
 | ||||
|     Strings shortRefs() const; | ||||
| 
 | ||||
|     virtual ~ValidPathInfo() { } | ||||
| }; | ||||
| 
 | ||||
|  | @ -170,6 +172,7 @@ struct BuildResult | |||
| struct BasicDerivation; | ||||
| struct Derivation; | ||||
| class FSAccessor; | ||||
| class NarInfoDiskCache; | ||||
| 
 | ||||
| 
 | ||||
| class Store : public std::enable_shared_from_this<Store> | ||||
|  | @ -183,10 +186,14 @@ protected: | |||
| 
 | ||||
|     Sync<State> state; | ||||
| 
 | ||||
|     std::shared_ptr<NarInfoDiskCache> diskCache; | ||||
| 
 | ||||
| public: | ||||
| 
 | ||||
|     virtual ~Store() { } | ||||
| 
 | ||||
|     virtual std::string getUri(); | ||||
| 
 | ||||
|     /* Check whether a path is valid. */ | ||||
|     bool isValidPath(const Path & path); | ||||
| 
 | ||||
|  |  | |||
|  | @ -33,7 +33,7 @@ Hash::Hash(HashType type) | |||
|     else if (type == htSHA1) hashSize = sha1HashSize; | ||||
|     else if (type == htSHA256) hashSize = sha256HashSize; | ||||
|     else if (type == htSHA512) hashSize = sha512HashSize; | ||||
|     else throw Error("unknown hash type"); | ||||
|     else abort(); | ||||
|     assert(hashSize <= 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"; | ||||
| 
 | ||||
| 
 | ||||
|  | @ -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 hash(ht); | ||||
|     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++) { | ||||
|         string s2(s, i * 2, 2); | ||||
|         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); | ||||
|         int n; | ||||
|         str >> std::hex >> n; | ||||
|  | @ -103,6 +122,7 @@ const string base32Chars = "0123456789abcdfghijklmnpqrsvwxyz"; | |||
| string printHash32(const Hash & hash) | ||||
| { | ||||
|     size_t len = hash.base32Len(); | ||||
|     assert(len); | ||||
| 
 | ||||
|     string s; | ||||
|     s.reserve(len); | ||||
|  | @ -139,7 +159,7 @@ Hash parseHash32(HashType ht, const string & s) | |||
|         for (digit = 0; digit < base32Chars.size(); ++digit) /* !!! slow */ | ||||
|             if (base32Chars[digit] == c) break; | ||||
|         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 i = b / 8; | ||||
|         unsigned int j = b % 8; | ||||
|  | @ -161,7 +181,7 @@ Hash parseHash16or32(HashType ht, const string & s) | |||
|         /* base-32 representation */ | ||||
|         hash = parseHash32(ht, s); | ||||
|     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)); | ||||
|     return hash; | ||||
| } | ||||
|  | @ -322,7 +342,7 @@ string printHashType(HashType ht) | |||
|     else if (ht == htSHA1) return "sha1"; | ||||
|     else if (ht == htSHA256) return "sha256"; | ||||
|     else if (ht == htSHA512) return "sha512"; | ||||
|     else throw Error("cannot print unknown hash type"); | ||||
|     else abort(); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -7,6 +7,9 @@ | |||
| namespace nix { | ||||
| 
 | ||||
| 
 | ||||
| MakeError(BadHash, Error); | ||||
| 
 | ||||
| 
 | ||||
| enum HashType : char { htUnknown, htMD5, htSHA1, htSHA256, htSHA512 }; | ||||
| 
 | ||||
| 
 | ||||
|  | @ -26,12 +29,15 @@ struct Hash | |||
| 
 | ||||
|     HashType type; | ||||
| 
 | ||||
|     /* Create an unusable hash object. */ | ||||
|     /* Create an unset hash object. */ | ||||
|     Hash(); | ||||
| 
 | ||||
|     /* Create a zero-filled hash object. */ | ||||
|     Hash(HashType type); | ||||
| 
 | ||||
|     /* Check whether a hash is set. */ | ||||
|     operator bool () const { return type != htUnknown; } | ||||
| 
 | ||||
|     /* Check whether two hash are equal. */ | ||||
|     bool operator == (const Hash & h2) const; | ||||
| 
 | ||||
|  | @ -52,12 +58,16 @@ struct Hash | |||
|     { | ||||
|         return (hashSize * 8 - 1) / 5 + 1; | ||||
|     } | ||||
| 
 | ||||
|     std::string to_string(bool base32 = true) const; | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| /* Convert a hash to a hexadecimal representation. */ | ||||
| string printHash(const Hash & hash); | ||||
| 
 | ||||
| Hash parseHash(const string & s); | ||||
| 
 | ||||
| /* Parse a hexadecimal representation of a hash code. */ | ||||
| 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 created; | ||||
|  |  | |||
|  | @ -102,6 +102,9 @@ void deletePath(const Path & path, unsigned long long & bytesFreed); | |||
| Path createTempDir(const Path & tmpRoot = "", const Path & prefix = "nix", | ||||
|     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
 | ||||
|    list of created directories, in order of creation. */ | ||||
| Paths createDirs(const Path & path); | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue