Mark content-addressed paths in the Nix database and in .narinfo
This allows such paths to be imported without signatures.
This commit is contained in:
		
							parent
							
								
									36a51ecab3
								
							
						
					
					
						commit
						d961c29c9c
					
				
					 11 changed files with 146 additions and 43 deletions
				
			
		|  | @ -3213,7 +3213,7 @@ void SubstitutionGoal::tryNext() | ||||||
|     /* Bail out early if this substituter lacks a valid
 |     /* Bail out early if this substituter lacks a valid
 | ||||||
|        signature. LocalStore::addToStore() also checks for this, but |        signature. LocalStore::addToStore() also checks for this, but | ||||||
|        only after we've downloaded the path. */ |        only after we've downloaded the path. */ | ||||||
|     if (worker.store.requireSigs && !info->checkSignatures(worker.store.publicKeys)) { |     if (worker.store.requireSigs && !info->checkSignatures(worker.store, worker.store.publicKeys)) { | ||||||
|         printMsg(lvlInfo, format("warning: substituter ‘%s’ does not have a valid signature for path ‘%s’") |         printMsg(lvlInfo, format("warning: substituter ‘%s’ does not have a valid signature for path ‘%s’") | ||||||
|             % sub->getUri() % storePath); |             % sub->getUri() % storePath); | ||||||
|         tryNext(); |         tryNext(); | ||||||
|  |  | ||||||
|  | @ -195,6 +195,13 @@ LocalStore::LocalStore(const Params & params) | ||||||
|             txn.commit(); |             txn.commit(); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |         if (curSchema < 10) { | ||||||
|  |             SQLiteTxn txn(state->db); | ||||||
|  |             if (sqlite3_exec(state->db, "alter table ValidPaths add column ca text", 0, 0, 0) != SQLITE_OK) | ||||||
|  |                 throwSQLiteError(state->db, "upgrading database schema"); | ||||||
|  |             txn.commit(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|         writeFile(schemaPath, (format("%1%") % nixSchemaVersion).str()); |         writeFile(schemaPath, (format("%1%") % nixSchemaVersion).str()); | ||||||
| 
 | 
 | ||||||
|         lockFile(globalLock.get(), ltRead, true); |         lockFile(globalLock.get(), ltRead, true); | ||||||
|  | @ -204,13 +211,13 @@ LocalStore::LocalStore(const Params & params) | ||||||
| 
 | 
 | ||||||
|     /* Prepare SQL statements. */ |     /* Prepare SQL statements. */ | ||||||
|     state->stmtRegisterValidPath.create(state->db, |     state->stmtRegisterValidPath.create(state->db, | ||||||
|         "insert into ValidPaths (path, hash, registrationTime, deriver, narSize, ultimate, sigs) values (?, ?, ?, ?, ?, ?, ?);"); |         "insert into ValidPaths (path, hash, registrationTime, deriver, narSize, ultimate, sigs, ca) values (?, ?, ?, ?, ?, ?, ?, ?);"); | ||||||
|     state->stmtUpdatePathInfo.create(state->db, |     state->stmtUpdatePathInfo.create(state->db, | ||||||
|         "update ValidPaths set narSize = ?, hash = ?, ultimate = ?, sigs = ? where path = ?;"); |         "update ValidPaths set narSize = ?, hash = ?, ultimate = ?, sigs = ?, ca = ? where path = ?;"); | ||||||
|     state->stmtAddReference.create(state->db, |     state->stmtAddReference.create(state->db, | ||||||
|         "insert or replace into Refs (referrer, reference) values (?, ?);"); |         "insert or replace into Refs (referrer, reference) values (?, ?);"); | ||||||
|     state->stmtQueryPathInfo.create(state->db, |     state->stmtQueryPathInfo.create(state->db, | ||||||
|         "select id, hash, registrationTime, deriver, narSize, ultimate, sigs from ValidPaths where path = ?;"); |         "select id, hash, registrationTime, deriver, narSize, ultimate, sigs, ca from ValidPaths where path = ?;"); | ||||||
|     state->stmtQueryReferences.create(state->db, |     state->stmtQueryReferences.create(state->db, | ||||||
|         "select path from Refs join ValidPaths on reference = id where referrer = ?;"); |         "select path from Refs join ValidPaths on reference = id where referrer = ?;"); | ||||||
|     state->stmtQueryReferrers.create(state->db, |     state->stmtQueryReferrers.create(state->db, | ||||||
|  | @ -527,6 +534,7 @@ uint64_t LocalStore::addValidPath(State & state, | ||||||
|         (info.narSize, info.narSize != 0) |         (info.narSize, info.narSize != 0) | ||||||
|         (info.ultimate ? 1 : 0, info.ultimate) |         (info.ultimate ? 1 : 0, info.ultimate) | ||||||
|         (concatStringsSep(" ", info.sigs), !info.sigs.empty()) |         (concatStringsSep(" ", info.sigs), !info.sigs.empty()) | ||||||
|  |         (info.ca, !info.ca.empty()) | ||||||
|         .exec(); |         .exec(); | ||||||
|     uint64_t id = sqlite3_last_insert_rowid(state.db); |     uint64_t id = sqlite3_last_insert_rowid(state.db); | ||||||
| 
 | 
 | ||||||
|  | @ -609,6 +617,9 @@ std::shared_ptr<ValidPathInfo> LocalStore::queryPathInfoUncached(const Path & pa | ||||||
|         s = (const char *) sqlite3_column_text(state->stmtQueryPathInfo, 6); |         s = (const char *) sqlite3_column_text(state->stmtQueryPathInfo, 6); | ||||||
|         if (s) info->sigs = tokenizeString<StringSet>(s, " "); |         if (s) info->sigs = tokenizeString<StringSet>(s, " "); | ||||||
| 
 | 
 | ||||||
|  |         s = (const char *) sqlite3_column_text(state->stmtQueryPathInfo, 7); | ||||||
|  |         if (s) info->ca = s; | ||||||
|  | 
 | ||||||
|         /* Get the references. */ |         /* Get the references. */ | ||||||
|         auto useQueryReferences(state->stmtQueryReferences.use()(info->id)); |         auto useQueryReferences(state->stmtQueryReferences.use()(info->id)); | ||||||
| 
 | 
 | ||||||
|  | @ -628,6 +639,7 @@ void LocalStore::updatePathInfo(State & state, const ValidPathInfo & info) | ||||||
|         ("sha256:" + printHash(info.narHash)) |         ("sha256:" + printHash(info.narHash)) | ||||||
|         (info.ultimate ? 1 : 0, info.ultimate) |         (info.ultimate ? 1 : 0, info.ultimate) | ||||||
|         (concatStringsSep(" ", info.sigs), !info.sigs.empty()) |         (concatStringsSep(" ", info.sigs), !info.sigs.empty()) | ||||||
|  |         (info.ca, !info.ca.empty()) | ||||||
|         (info.path) |         (info.path) | ||||||
|         .exec(); |         .exec(); | ||||||
| } | } | ||||||
|  | @ -898,7 +910,7 @@ void LocalStore::addToStore(const ValidPathInfo & info, const std::string & nar, | ||||||
|         throw Error(format("hash mismatch importing path ‘%s’; expected hash ‘%s’, got ‘%s’") % |         throw Error(format("hash mismatch importing path ‘%s’; expected hash ‘%s’, got ‘%s’") % | ||||||
|             info.path % info.narHash.to_string() % h.to_string()); |             info.path % info.narHash.to_string() % h.to_string()); | ||||||
| 
 | 
 | ||||||
|     if (requireSigs && !dontCheckSigs && !info.checkSignatures(publicKeys)) |     if (requireSigs && !dontCheckSigs && !info.checkSignatures(*this, publicKeys)) | ||||||
|         throw Error(format("cannot import path ‘%s’ because it lacks a valid signature") % info.path); |         throw Error(format("cannot import path ‘%s’ because it lacks a valid signature") % info.path); | ||||||
| 
 | 
 | ||||||
|     addTempRoot(info.path); |     addTempRoot(info.path); | ||||||
|  | @ -983,6 +995,7 @@ Path LocalStore::addToStoreFromDump(const string & dump, const string & name, | ||||||
|             info.narHash = hash.first; |             info.narHash = hash.first; | ||||||
|             info.narSize = hash.second; |             info.narSize = hash.second; | ||||||
|             info.ultimate = true; |             info.ultimate = true; | ||||||
|  |             info.ca = "fixed:" + (recursive ? (std::string) "r:" : "") + h.to_string(); | ||||||
|             registerValidPath(info); |             registerValidPath(info); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  | @ -1014,7 +1027,8 @@ Path LocalStore::addToStore(const string & name, const Path & _srcPath, | ||||||
| Path LocalStore::addTextToStore(const string & name, const string & s, | Path LocalStore::addTextToStore(const string & name, const string & s, | ||||||
|     const PathSet & references, bool repair) |     const PathSet & references, bool repair) | ||||||
| { | { | ||||||
|     Path dstPath = computeStorePathForText(name, s, references); |     auto hash = hashString(htSHA256, s); | ||||||
|  |     auto dstPath = makeTextPath(name, hash, references); | ||||||
| 
 | 
 | ||||||
|     addTempRoot(dstPath); |     addTempRoot(dstPath); | ||||||
| 
 | 
 | ||||||
|  | @ -1034,16 +1048,17 @@ Path LocalStore::addTextToStore(const string & name, const string & s, | ||||||
| 
 | 
 | ||||||
|             StringSink sink; |             StringSink sink; | ||||||
|             dumpString(s, sink); |             dumpString(s, sink); | ||||||
|             auto hash = hashString(htSHA256, *sink.s); |             auto narHash = hashString(htSHA256, *sink.s); | ||||||
| 
 | 
 | ||||||
|             optimisePath(realPath); |             optimisePath(realPath); | ||||||
| 
 | 
 | ||||||
|             ValidPathInfo info; |             ValidPathInfo info; | ||||||
|             info.path = dstPath; |             info.path = dstPath; | ||||||
|             info.narHash = hash; |             info.narHash = narHash; | ||||||
|             info.narSize = sink.s->size(); |             info.narSize = sink.s->size(); | ||||||
|             info.references = references; |             info.references = references; | ||||||
|             info.ultimate = true; |             info.ultimate = true; | ||||||
|  |             info.ca = "text:" + hash.to_string(); | ||||||
|             registerValidPath(info); |             registerValidPath(info); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -17,8 +17,8 @@ namespace nix { | ||||||
| /* Nix store and database schema version.  Version 1 (or 0) was Nix <=
 | /* Nix store and database schema version.  Version 1 (or 0) was Nix <=
 | ||||||
|    0.7.  Version 2 was Nix 0.8 and 0.9.  Version 3 is Nix 0.10. |    0.7.  Version 2 was Nix 0.8 and 0.9.  Version 3 is Nix 0.10. | ||||||
|    Version 4 is Nix 0.11.  Version 5 is Nix 0.12-0.16.  Version 6 is |    Version 4 is Nix 0.11.  Version 5 is Nix 0.12-0.16.  Version 6 is | ||||||
|    Nix 1.0.  Version 7 is Nix 1.3. Version 9 is 1.12. */ |    Nix 1.0.  Version 7 is Nix 1.3. Version 10 is 1.12. */ | ||||||
| const int nixSchemaVersion = 9; | const int nixSchemaVersion = 10; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| extern string drvsLogDir; | extern string drvsLogDir; | ||||||
|  |  | ||||||
|  | @ -67,6 +67,10 @@ NarInfo::NarInfo(const Store & store, const std::string & s, const std::string & | ||||||
|             system = value; |             system = value; | ||||||
|         else if (name == "Sig") |         else if (name == "Sig") | ||||||
|             sigs.insert(value); |             sigs.insert(value); | ||||||
|  |         else if (name == "CA") { | ||||||
|  |             if (!ca.empty()) corrupt(); | ||||||
|  |             ca = value; | ||||||
|  |         } | ||||||
| 
 | 
 | ||||||
|         pos = eol + 1; |         pos = eol + 1; | ||||||
|     } |     } | ||||||
|  | @ -101,6 +105,9 @@ std::string NarInfo::to_string() const | ||||||
|     for (auto sig : sigs) |     for (auto sig : sigs) | ||||||
|         res += "Sig: " + sig + "\n"; |         res += "Sig: " + sig + "\n"; | ||||||
| 
 | 
 | ||||||
|  |     if (!ca.empty()) | ||||||
|  |         res += "CA: " + ca + "\n"; | ||||||
|  | 
 | ||||||
|     return res; |     return res; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -273,6 +273,7 @@ std::shared_ptr<ValidPathInfo> RemoteStore::queryPathInfoUncached(const Path & p | ||||||
|     if (GET_PROTOCOL_MINOR(conn->daemonVersion) >= 16) { |     if (GET_PROTOCOL_MINOR(conn->daemonVersion) >= 16) { | ||||||
|         info->ultimate = readInt(conn->from) != 0; |         info->ultimate = readInt(conn->from) != 0; | ||||||
|         info->sigs = readStrings<StringSet>(conn->from); |         info->sigs = readStrings<StringSet>(conn->from); | ||||||
|  |         info->ca = readString(conn->from); | ||||||
|     } |     } | ||||||
|     return info; |     return info; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -6,7 +6,8 @@ create table if not exists ValidPaths ( | ||||||
|     deriver          text, |     deriver          text, | ||||||
|     narSize          integer, |     narSize          integer, | ||||||
|     ultimate         integer, -- null implies "false" |     ultimate         integer, -- null implies "false" | ||||||
|     sigs             text -- space-separated |     sigs             text, -- space-separated | ||||||
|  |     ca               text -- if not null, an assertion that the path is content-addressed; see ValidPathInfo | ||||||
| ); | ); | ||||||
| 
 | 
 | ||||||
| create table if not exists Refs ( | create table if not exists Refs ( | ||||||
|  |  | ||||||
|  | @ -202,6 +202,22 @@ Path Store::makeFixedOutputPath(bool recursive, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | Path Store::makeTextPath(const string & name, const Hash & hash, | ||||||
|  |     const PathSet & references) const | ||||||
|  | { | ||||||
|  |     assert(hash.type == htSHA256); | ||||||
|  |     /* Stuff the references (if any) into the type.  This is a bit
 | ||||||
|  |        hacky, but we can't put them in `s' since that would be | ||||||
|  |        ambiguous. */ | ||||||
|  |     string type = "text"; | ||||||
|  |     for (auto & i : references) { | ||||||
|  |         type += ":"; | ||||||
|  |         type += i; | ||||||
|  |     } | ||||||
|  |     return makeStorePath(type, hash, name); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| std::pair<Path, Hash> Store::computeStorePathForPath(const Path & srcPath, | std::pair<Path, Hash> Store::computeStorePathForPath(const Path & srcPath, | ||||||
|     bool recursive, HashType hashAlgo, PathFilter & filter) const |     bool recursive, HashType hashAlgo, PathFilter & filter) const | ||||||
| { | { | ||||||
|  | @ -215,16 +231,7 @@ std::pair<Path, Hash> Store::computeStorePathForPath(const Path & srcPath, | ||||||
| Path Store::computeStorePathForText(const string & name, const string & s, | Path Store::computeStorePathForText(const string & name, const string & s, | ||||||
|     const PathSet & references) const |     const PathSet & references) const | ||||||
| { | { | ||||||
|     Hash hash = hashString(htSHA256, s); |     return makeTextPath(name, hashString(htSHA256, s), references); | ||||||
|     /* Stuff the references (if any) into the type.  This is a bit
 |  | ||||||
|        hacky, but we can't put them in `s' since that would be |  | ||||||
|        ambiguous. */ |  | ||||||
|     string type = "text"; |  | ||||||
|     for (auto & i : references) { |  | ||||||
|         type += ":"; |  | ||||||
|         type += i; |  | ||||||
|     } |  | ||||||
|     return makeStorePath(type, hash, name); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | @ -432,9 +439,38 @@ void ValidPathInfo::sign(const SecretKey & secretKey) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| unsigned int ValidPathInfo::checkSignatures(const PublicKeys & publicKeys) const | bool ValidPathInfo::isContentAddressed(const Store & store) const | ||||||
| { | { | ||||||
|     unsigned int good = 0; |     auto warn = [&]() { | ||||||
|  |         printMsg(lvlError, format("warning: path ‘%s’ claims to be content-addressed but isn't") % path); | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     if (hasPrefix(ca, "text:")) { | ||||||
|  |         auto hash = parseHash(std::string(ca, 5)); | ||||||
|  |         if (store.makeTextPath(storePathToName(path), hash, references) == path) | ||||||
|  |             return true; | ||||||
|  |         else | ||||||
|  |             warn(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     else if (hasPrefix(ca, "fixed:")) { | ||||||
|  |         bool recursive = ca.compare(6, 2, "r:") == 0; | ||||||
|  |         auto hash = parseHash(std::string(ca, recursive ? 8 : 6)); | ||||||
|  |         if (store.makeFixedOutputPath(recursive, hash, storePathToName(path)) == path) | ||||||
|  |             return true; | ||||||
|  |         else | ||||||
|  |             warn(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | size_t ValidPathInfo::checkSignatures(const Store & store, const PublicKeys & publicKeys) const | ||||||
|  | { | ||||||
|  |     if (isContentAddressed(store)) return maxSigs; | ||||||
|  | 
 | ||||||
|  |     size_t good = 0; | ||||||
|     for (auto & sig : sigs) |     for (auto & sig : sigs) | ||||||
|         if (checkSignature(publicKeys, sig)) |         if (checkSignature(publicKeys, sig)) | ||||||
|             good++; |             good++; | ||||||
|  |  | ||||||
|  | @ -16,6 +16,13 @@ | ||||||
| namespace nix { | namespace nix { | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | struct BasicDerivation; | ||||||
|  | struct Derivation; | ||||||
|  | class FSAccessor; | ||||||
|  | class NarInfoDiskCache; | ||||||
|  | class Store; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| /* Size of the hash part of store paths, in base-32 characters. */ | /* Size of the hash part of store paths, in base-32 characters. */ | ||||||
| const size_t storePathHashLen = 32; // i.e. 160 bits
 | const size_t storePathHashLen = 32; // i.e. 160 bits
 | ||||||
| 
 | 
 | ||||||
|  | @ -109,6 +116,34 @@ struct ValidPathInfo | ||||||
| 
 | 
 | ||||||
|     StringSet sigs; // note: not necessarily verified
 |     StringSet sigs; // note: not necessarily verified
 | ||||||
| 
 | 
 | ||||||
|  |     /* If non-empty, an assertion that the path is content-addressed,
 | ||||||
|  |        i.e., that the store path is computed from a cryptographic hash | ||||||
|  |        of the contents of the path, plus some other bits of data like | ||||||
|  |        the "name" part of the path. Such a path doesn't need | ||||||
|  |        signatures, since we don't have to trust anybody's claim that | ||||||
|  |        the path is the output of a particular derivation. (In the | ||||||
|  |        extensional store model, we have to trust that the *contents* | ||||||
|  |        of an output path of a derivation were actually produced by | ||||||
|  |        that derivation. In the intensional model, we have to trust | ||||||
|  |        that a particular output path was produced by a derivation; the | ||||||
|  |        path name then implies the contents.) | ||||||
|  | 
 | ||||||
|  |        Ideally, the content-addressability assertion would just be a | ||||||
|  |        Boolean, and the store path would be computed from | ||||||
|  |        ‘storePathToName(path)’, ‘narHash’ and ‘references’. However, | ||||||
|  |        1) we've accumulated several types of content-addressed paths | ||||||
|  |        over the years; and 2) fixed-output derivations support | ||||||
|  |        multiple hash algorithms and serialisation methods (flat file | ||||||
|  |        vs NAR). Thus, ‘ca’ has one of the following forms: | ||||||
|  | 
 | ||||||
|  |        * ‘text:sha256:<sha256 hash of file contents>’: For paths | ||||||
|  |          computed by makeTextPath() / addTextToStore(). | ||||||
|  | 
 | ||||||
|  |        * ‘fixed:<r?>:<ht>:<h>’: For paths computed by | ||||||
|  |          makeFixedOutputPath() / addToStore(). | ||||||
|  |     */ | ||||||
|  |     std::string ca; | ||||||
|  | 
 | ||||||
|     bool operator == (const ValidPathInfo & i) const |     bool operator == (const ValidPathInfo & i) const | ||||||
|     { |     { | ||||||
|         return |         return | ||||||
|  | @ -127,9 +162,15 @@ struct ValidPathInfo | ||||||
| 
 | 
 | ||||||
|     void sign(const SecretKey & secretKey); |     void sign(const SecretKey & secretKey); | ||||||
| 
 | 
 | ||||||
|  |     /* Return true iff the path is verifiably content-addressed. */ | ||||||
|  |     bool isContentAddressed(const Store & store) const; | ||||||
|  | 
 | ||||||
|  |     static const size_t maxSigs = std::numeric_limits<size_t>::max(); | ||||||
|  | 
 | ||||||
|     /* Return the number of signatures on this .narinfo that were
 |     /* Return the number of signatures on this .narinfo that were
 | ||||||
|        produced by one of the specified keys. */ |        produced by one of the specified keys, or maxSigs if the path | ||||||
|     unsigned int checkSignatures(const PublicKeys & publicKeys) const; |        is content-addressed. */ | ||||||
|  |     size_t checkSignatures(const Store & store, const PublicKeys & publicKeys) const; | ||||||
| 
 | 
 | ||||||
|     /* 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; | ||||||
|  | @ -169,12 +210,6 @@ struct BuildResult | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| struct BasicDerivation; |  | ||||||
| struct Derivation; |  | ||||||
| class FSAccessor; |  | ||||||
| class NarInfoDiskCache; |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| class Store : public std::enable_shared_from_this<Store> | class Store : public std::enable_shared_from_this<Store> | ||||||
| { | { | ||||||
| public: | public: | ||||||
|  | @ -234,10 +269,12 @@ public: | ||||||
|     Path makeFixedOutputPath(bool recursive, |     Path makeFixedOutputPath(bool recursive, | ||||||
|         const Hash & hash, const string & name) const; |         const Hash & hash, const string & name) const; | ||||||
| 
 | 
 | ||||||
|     /* This is the preparatory part of addToStore() and
 |     Path makeTextPath(const string & name, const Hash & hash, | ||||||
|        addToStoreFixed(); it computes the store path to which srcPath |         const PathSet & references) const; | ||||||
|        is to be copied.  Returns the store path and the cryptographic | 
 | ||||||
|        hash of the contents of srcPath. */ |     /* This is the preparatory part of addToStore(); it computes the
 | ||||||
|  |        store path to which srcPath is to be copied.  Returns the store | ||||||
|  |        path and the cryptographic hash of the contents of srcPath. */ | ||||||
|     std::pair<Path, Hash> computeStorePathForPath(const Path & srcPath, |     std::pair<Path, Hash> computeStorePathForPath(const Path & srcPath, | ||||||
|         bool recursive = true, HashType hashAlgo = htSHA256, |         bool recursive = true, HashType hashAlgo = htSHA256, | ||||||
|         PathFilter & filter = defaultPathFilter) const; |         PathFilter & filter = defaultPathFilter) const; | ||||||
|  |  | ||||||
|  | @ -515,7 +515,8 @@ static void performOp(ref<LocalStore> store, bool trusted, unsigned int clientVe | ||||||
|                << info->registrationTime << info->narSize; |                << info->registrationTime << info->narSize; | ||||||
|             if (GET_PROTOCOL_MINOR(clientVersion) >= 16) { |             if (GET_PROTOCOL_MINOR(clientVersion) >= 16) { | ||||||
|                 to << info->ultimate |                 to << info->ultimate | ||||||
|                    << info->sigs; |                    << info->sigs | ||||||
|  |                    << info->ca; | ||||||
|             } |             } | ||||||
|         } else { |         } else { | ||||||
|             assert(GET_PROTOCOL_MINOR(clientVersion) >= 17); |             assert(GET_PROTOCOL_MINOR(clientVersion) >= 17); | ||||||
|  |  | ||||||
|  | @ -73,6 +73,7 @@ struct CmdPathInfo : StorePathsCommand | ||||||
|                 std::cout << '\t'; |                 std::cout << '\t'; | ||||||
|                 Strings ss; |                 Strings ss; | ||||||
|                 if (info->ultimate) ss.push_back("ultimate"); |                 if (info->ultimate) ss.push_back("ultimate"); | ||||||
|  |                 if (info->ca != "") ss.push_back("ca:" + info->ca); | ||||||
|                 for (auto & sig : info->sigs) ss.push_back(sig); |                 for (auto & sig : info->sigs) ss.push_back(sig); | ||||||
|                 std::cout << concatStringsSep(" ", ss); |                 std::cout << concatStringsSep(" ", ss); | ||||||
|             } |             } | ||||||
|  |  | ||||||
|  | @ -116,12 +116,16 @@ struct CmdVerify : StorePathsCommand | ||||||
|                             } |                             } | ||||||
|                         }; |                         }; | ||||||
| 
 | 
 | ||||||
|  |                         if (info->isContentAddressed(*store)) validSigs = ValidPathInfo::maxSigs; | ||||||
|  | 
 | ||||||
|                         doSigs(info->sigs); |                         doSigs(info->sigs); | ||||||
| 
 | 
 | ||||||
|                         for (auto & store2 : substituters) { |                         for (auto & store2 : substituters) { | ||||||
|                             if (validSigs >= actualSigsNeeded) break; |                             if (validSigs >= actualSigsNeeded) break; | ||||||
|                             try { |                             try { | ||||||
|                                 doSigs(store2->queryPathInfo(info->path)->sigs); |                                 auto info2 = store2->queryPathInfo(info->path); | ||||||
|  |                                 if (info2->isContentAddressed(*store)) validSigs = ValidPathInfo::maxSigs; | ||||||
|  |                                 doSigs(info2->sigs); | ||||||
|                             } catch (InvalidPath &) { |                             } catch (InvalidPath &) { | ||||||
|                             } catch (Error & e) { |                             } catch (Error & e) { | ||||||
|                                 printMsg(lvlError, format(ANSI_RED "error:" ANSI_NORMAL " %s") % e.what()); |                                 printMsg(lvlError, format(ANSI_RED "error:" ANSI_NORMAL " %s") % e.what()); | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue