1 #include "crypto.hh"
2 #include "globals.hh"
3 #include "store-api.hh"
4 #include "util.hh"
5 #include "nar-info-disk-cache.hh"
6 #include "thread-pool.hh"
7 #include "json.hh"
8 #include "derivations.hh"
9 
10 #include <future>
11 
12 
13 namespace nix {
14 
15 
isInStore(const Path & path) const16 bool Store::isInStore(const Path & path) const
17 {
18     return isInDir(path, storeDir);
19 }
20 
21 
isStorePath(const Path & path) const22 bool Store::isStorePath(const Path & path) const
23 {
24     return isInStore(path)
25         && path.size() >= storeDir.size() + 1 + storePathHashLen
26         && path.find('/', storeDir.size() + 1) == Path::npos;
27 }
28 
29 
assertStorePath(const Path & path) const30 void Store::assertStorePath(const Path & path) const
31 {
32     if (!isStorePath(path))
33         throw Error(format("path '%1%' is not in the Nix store") % path);
34 }
35 
36 
toStorePath(const Path & path) const37 Path Store::toStorePath(const Path & path) const
38 {
39     if (!isInStore(path))
40         throw Error(format("path '%1%' is not in the Nix store") % path);
41     Path::size_type slash = path.find('/', storeDir.size() + 1);
42     if (slash == Path::npos)
43         return path;
44     else
45         return Path(path, 0, slash);
46 }
47 
48 
followLinksToStore(const Path & _path) const49 Path Store::followLinksToStore(const Path & _path) const
50 {
51     Path path = absPath(_path);
52     while (!isInStore(path)) {
53         if (!isLink(path)) break;
54         string target = readLink(path);
55         path = absPath(target, dirOf(path));
56     }
57     if (!isInStore(path))
58         throw Error(format("path '%1%' is not in the Nix store") % path);
59     return path;
60 }
61 
62 
followLinksToStorePath(const Path & path) const63 Path Store::followLinksToStorePath(const Path & path) const
64 {
65     return toStorePath(followLinksToStore(path));
66 }
67 
68 
storePathToName(const Path & path)69 string storePathToName(const Path & path)
70 {
71     auto base = baseNameOf(path);
72     assert(base.size() == storePathHashLen || (base.size() > storePathHashLen && base[storePathHashLen] == '-'));
73     return base.size() == storePathHashLen ? "" : string(base, storePathHashLen + 1);
74 }
75 
76 
storePathToHash(const Path & path)77 string storePathToHash(const Path & path)
78 {
79     auto base = baseNameOf(path);
80     assert(base.size() >= storePathHashLen);
81     return string(base, 0, storePathHashLen);
82 }
83 
84 
checkStoreName(const string & name)85 void checkStoreName(const string & name)
86 {
87     string validChars = "+-._?=";
88 
89     auto baseError = format("The path name '%2%' is invalid: %3%. "
90         "Path names are alphanumeric and can include the symbols %1% "
91         "and must not begin with a period. "
92         "Note: If '%2%' is a source file and you cannot rename it on "
93         "disk, builtins.path { name = ... } can be used to give it an "
94         "alternative name.") % validChars % name;
95 
96     /* Disallow names starting with a dot for possible security
97        reasons (e.g., "." and ".."). */
98     if (string(name, 0, 1) == ".")
99         throw Error(baseError % "it is illegal to start the name with a period");
100     /* Disallow names longer than 211 characters. ext4’s max is 256,
101        but we need extra space for the hash and .chroot extensions. */
102     if (name.length() > 211)
103         throw Error(baseError % "name must be less than 212 characters");
104     for (auto & i : name)
105         if (!((i >= 'A' && i <= 'Z') ||
106               (i >= 'a' && i <= 'z') ||
107               (i >= '0' && i <= '9') ||
108               validChars.find(i) != string::npos))
109         {
110             throw Error(baseError % (format("the '%1%' character is invalid") % i));
111         }
112 }
113 
114 
115 /* Store paths have the following form:
116 
117    <store>/<h>-<name>
118 
119    where
120 
121    <store> = the location of the Nix store, usually /nix/store
122 
123    <name> = a human readable name for the path, typically obtained
124      from the name attribute of the derivation, or the name of the
125      source file from which the store path is created.  For derivation
126      outputs other than the default "out" output, the string "-<id>"
127      is suffixed to <name>.
128 
129    <h> = base-32 representation of the first 160 bits of a SHA-256
130      hash of <s>; the hash part of the store name
131 
132    <s> = the string "<type>:sha256:<h2>:<store>:<name>";
133      note that it includes the location of the store as well as the
134      name to make sure that changes to either of those are reflected
135      in the hash (e.g. you won't get /nix/store/<h>-name1 and
136      /nix/store/<h>-name2 with equal hash parts).
137 
138    <type> = one of:
139      "text:<r1>:<r2>:...<rN>"
140        for plain text files written to the store using
141        addTextToStore(); <r1> ... <rN> are the references of the
142        path.
143      "source"
144        for paths copied to the store using addToStore() when recursive
145        = true and hashAlgo = "sha256"
146      "output:<id>"
147        for either the outputs created by derivations, OR paths copied
148        to the store using addToStore() with recursive != true or
149        hashAlgo != "sha256" (in that case "source" is used; it's
150        silly, but it's done that way for compatibility).  <id> is the
151        name of the output (usually, "out").
152 
153    <h2> = base-16 representation of a SHA-256 hash of:
154      if <type> = "text:...":
155        the string written to the resulting store path
156      if <type> = "source":
157        the serialisation of the path from which this store path is
158        copied, as returned by hashPath()
159      if <type> = "output:<id>":
160        for non-fixed derivation outputs:
161          the derivation (see hashDerivationModulo() in
162          primops.cc)
163        for paths copied by addToStore() or produced by fixed-output
164        derivations:
165          the string "fixed:out:<rec><algo>:<hash>:", where
166            <rec> = "r:" for recursive (path) hashes, or "" for flat
167              (file) hashes
168            <algo> = "md5", "sha1" or "sha256"
169            <hash> = base-16 representation of the path or flat hash of
170              the contents of the path (or expected contents of the
171              path for fixed-output derivations)
172 
173    It would have been nicer to handle fixed-output derivations under
174    "source", e.g. have something like "source:<rec><algo>", but we're
175    stuck with this for now...
176 
177    The main reason for this way of computing names is to prevent name
178    collisions (for security).  For instance, it shouldn't be feasible
179    to come up with a derivation whose output path collides with the
180    path for a copied source.  The former would have a <s> starting with
181    "output:out:", while the latter would have a <s> starting with
182    "source:".
183 */
184 
185 
makeStorePath(const string & type,const Hash & hash,const string & name) const186 Path Store::makeStorePath(const string & type,
187     const Hash & hash, const string & name) const
188 {
189     /* e.g., "source:sha256:1abc...:/nix/store:foo.tar.gz" */
190     string s = type + ":" + hash.to_string(Base16) + ":" + storeDir + ":" + name;
191 
192     checkStoreName(name);
193 
194     return storeDir + "/"
195         + compressHash(hashString(htSHA256, s), 20).to_string(Base32, false)
196         + "-" + name;
197 }
198 
199 
makeOutputPath(const string & id,const Hash & hash,const string & name) const200 Path Store::makeOutputPath(const string & id,
201     const Hash & hash, const string & name) const
202 {
203     return makeStorePath("output:" + id, hash,
204         name + (id == "out" ? "" : "-" + id));
205 }
206 
207 
makeFixedOutputPath(bool recursive,const Hash & hash,const string & name) const208 Path Store::makeFixedOutputPath(bool recursive,
209     const Hash & hash, const string & name) const
210 {
211     return hash.type == htSHA256 && recursive
212         ? makeStorePath("source", hash, name)
213         : makeStorePath("output:out", hashString(htSHA256,
214                 "fixed:out:" + (recursive ? (string) "r:" : "") +
215                 hash.to_string(Base16) + ":"),
216             name);
217 }
218 
219 
makeTextPath(const string & name,const Hash & hash,const PathSet & references) const220 Path Store::makeTextPath(const string & name, const Hash & hash,
221     const PathSet & references) const
222 {
223     assert(hash.type == htSHA256);
224     /* Stuff the references (if any) into the type.  This is a bit
225        hacky, but we can't put them in `s' since that would be
226        ambiguous. */
227     string type = "text";
228     for (auto & i : references) {
229         type += ":";
230         type += i;
231     }
232     return makeStorePath(type, hash, name);
233 }
234 
235 
computeStorePathForPath(const string & name,const Path & srcPath,bool recursive,HashType hashAlgo,PathFilter & filter) const236 std::pair<Path, Hash> Store::computeStorePathForPath(const string & name,
237     const Path & srcPath, bool recursive, HashType hashAlgo, PathFilter & filter) const
238 {
239     Hash h = recursive ? hashPath(hashAlgo, srcPath, filter).first : hashFile(hashAlgo, srcPath);
240     Path dstPath = makeFixedOutputPath(recursive, h, name);
241     return std::pair<Path, Hash>(dstPath, h);
242 }
243 
244 
computeStorePathForText(const string & name,const string & s,const PathSet & references) const245 Path Store::computeStorePathForText(const string & name, const string & s,
246     const PathSet & references) const
247 {
248     return makeTextPath(name, hashString(htSHA256, s), references);
249 }
250 
251 
Store(const Params & params)252 Store::Store(const Params & params)
253     : Config(params)
254     , state({(size_t) pathInfoCacheSize})
255 {
256 }
257 
258 
getUri()259 std::string Store::getUri()
260 {
261     return "";
262 }
263 
isKnownNow()264 bool Store::PathInfoCacheValue::isKnownNow()
265 {
266     std::chrono::duration ttl = didExist()
267         ? std::chrono::seconds(settings.ttlPositiveNarInfoCache)
268         : std::chrono::seconds(settings.ttlNegativeNarInfoCache);
269 
270     return std::chrono::steady_clock::now() < time_point + ttl;
271 }
272 
273 
isValidPath(const Path & storePath)274 bool Store::isValidPath(const Path & storePath)
275 {
276     assertStorePath(storePath);
277 
278     auto hashPart = storePathToHash(storePath);
279 
280     {
281         auto state_(state.lock());
282         auto res = state_->pathInfoCache.get(hashPart);
283         if (res && res->isKnownNow()) {
284             stats.narInfoReadAverted++;
285             return res->didExist();
286         }
287     }
288 
289     if (diskCache) {
290         auto res = diskCache->lookupNarInfo(getUri(), hashPart);
291         if (res.first != NarInfoDiskCache::oUnknown) {
292             stats.narInfoReadAverted++;
293             auto state_(state.lock());
294             state_->pathInfoCache.upsert(hashPart,
295                 res.first == NarInfoDiskCache::oInvalid ? 0 : PathInfoCacheValue(res.second));
296             return res.first == NarInfoDiskCache::oValid;
297         }
298     }
299 
300     bool valid = isValidPathUncached(storePath);
301 
302     if (diskCache && !valid)
303         // FIXME: handle valid = true case.
304         diskCache->upsertNarInfo(getUri(), hashPart, 0);
305 
306     return valid;
307 }
308 
309 
310 /* Default implementation for stores that only implement
311    queryPathInfoUncached(). */
isValidPathUncached(const Path & path)312 bool Store::isValidPathUncached(const Path & path)
313 {
314     try {
315         queryPathInfo(path);
316         return true;
317     } catch (InvalidPath &) {
318         return false;
319     }
320 }
321 
322 
queryPathInfo(const Path & storePath)323 ref<const ValidPathInfo> Store::queryPathInfo(const Path & storePath)
324 {
325     std::promise<ref<ValidPathInfo>> promise;
326 
327     queryPathInfo(storePath,
328         {[&](std::future<ref<ValidPathInfo>> result) {
329             try {
330                 promise.set_value(result.get());
331             } catch (...) {
332                 promise.set_exception(std::current_exception());
333             }
334         }});
335 
336     return promise.get_future().get();
337 }
338 
339 
queryPathInfo(const Path & storePath,Callback<ref<ValidPathInfo>> callback)340 void Store::queryPathInfo(const Path & storePath,
341     Callback<ref<ValidPathInfo>> callback) noexcept
342 {
343     std::string hashPart;
344 
345     try {
346         assertStorePath(storePath);
347 
348         hashPart = storePathToHash(storePath);
349 
350         {
351             auto res = state.lock()->pathInfoCache.get(hashPart);
352             if (res && res->isKnownNow()) {
353                 stats.narInfoReadAverted++;
354                 if (!res->didExist())
355                     throw InvalidPath(format("path '%s' is not valid") % storePath);
356                 return callback(ref<ValidPathInfo>(res->value));
357             }
358         }
359 
360         if (diskCache) {
361             auto res = diskCache->lookupNarInfo(getUri(), hashPart);
362             if (res.first != NarInfoDiskCache::oUnknown) {
363                 stats.narInfoReadAverted++;
364                 {
365                     auto state_(state.lock());
366                     state_->pathInfoCache.upsert(hashPart,
367                         res.first == NarInfoDiskCache::oInvalid ? 0 : PathInfoCacheValue(res.second));
368                     if (res.first == NarInfoDiskCache::oInvalid ||
369                         (res.second->path != storePath && storePathToName(storePath) != ""))
370                         throw InvalidPath(format("path '%s' is not valid") % storePath);
371                 }
372                 return callback(ref<ValidPathInfo>(res.second));
373             }
374         }
375 
376     } catch (...) { return callback.rethrow(); }
377 
378     auto callbackPtr = std::make_shared<decltype(callback)>(std::move(callback));
379 
380     queryPathInfoUncached(storePath,
381         {[this, storePath, hashPart, callbackPtr](std::future<std::shared_ptr<ValidPathInfo>> fut) {
382 
383             try {
384                 auto info = fut.get();
385 
386                 if (diskCache)
387                     diskCache->upsertNarInfo(getUri(), hashPart, info);
388 
389                 {
390                     auto state_(state.lock());
391                     state_->pathInfoCache.upsert(hashPart, info);
392                 }
393 
394                 if (!info
395                     || (info->path != storePath && storePathToName(storePath) != ""))
396                 {
397                     stats.narInfoMissing++;
398                     throw InvalidPath("path '%s' is not valid", storePath);
399                 }
400 
401                 (*callbackPtr)(ref<ValidPathInfo>(info));
402             } catch (...) { callbackPtr->rethrow(); }
403         }});
404 }
405 
406 
queryValidPaths(const PathSet & paths,SubstituteFlag maybeSubstitute)407 PathSet Store::queryValidPaths(const PathSet & paths, SubstituteFlag maybeSubstitute)
408 {
409     struct State
410     {
411         size_t left;
412         PathSet valid;
413         std::exception_ptr exc;
414     };
415 
416     Sync<State> state_(State{paths.size(), PathSet()});
417 
418     std::condition_variable wakeup;
419     ThreadPool pool;
420 
421     auto doQuery = [&](const Path & path ) {
422         checkInterrupt();
423         queryPathInfo(path, {[path, &state_, &wakeup](std::future<ref<ValidPathInfo>> fut) {
424             auto state(state_.lock());
425             try {
426                 auto info = fut.get();
427                 state->valid.insert(path);
428             } catch (InvalidPath &) {
429             } catch (...) {
430                 state->exc = std::current_exception();
431             }
432             assert(state->left);
433             if (!--state->left)
434                 wakeup.notify_one();
435         }});
436     };
437 
438     for (auto & path : paths)
439         pool.enqueue(std::bind(doQuery, path));
440 
441     pool.process();
442 
443     while (true) {
444         auto state(state_.lock());
445         if (!state->left) {
446             if (state->exc) std::rethrow_exception(state->exc);
447             return state->valid;
448         }
449         state.wait(wakeup);
450     }
451 }
452 
453 
454 /* Return a string accepted by decodeValidPathInfo() that
455    registers the specified paths as valid.  Note: it's the
456    responsibility of the caller to provide a closure. */
makeValidityRegistration(const PathSet & paths,bool showDerivers,bool showHash)457 string Store::makeValidityRegistration(const PathSet & paths,
458     bool showDerivers, bool showHash)
459 {
460     string s = "";
461 
462     for (auto & i : paths) {
463         s += i + "\n";
464 
465         auto info = queryPathInfo(i);
466 
467         if (showHash) {
468             s += info->narHash.to_string(Base16, false) + "\n";
469             s += (format("%1%\n") % info->narSize).str();
470         }
471 
472         Path deriver = showDerivers ? info->deriver : "";
473         s += deriver + "\n";
474 
475         s += (format("%1%\n") % info->references.size()).str();
476 
477         for (auto & j : info->references)
478             s += j + "\n";
479     }
480 
481     return s;
482 }
483 
484 
pathInfoToJSON(JSONPlaceholder & jsonOut,const PathSet & storePaths,bool includeImpureInfo,bool showClosureSize,AllowInvalidFlag allowInvalid)485 void Store::pathInfoToJSON(JSONPlaceholder & jsonOut, const PathSet & storePaths,
486     bool includeImpureInfo, bool showClosureSize, AllowInvalidFlag allowInvalid)
487 {
488     auto jsonList = jsonOut.list();
489 
490     for (auto storePath : storePaths) {
491         auto jsonPath = jsonList.object();
492         jsonPath.attr("path", storePath);
493 
494         try {
495             auto info = queryPathInfo(storePath);
496             storePath = info->path;
497 
498             jsonPath
499                 .attr("narHash", info->narHash.to_string())
500                 .attr("narSize", info->narSize);
501 
502             {
503                 auto jsonRefs = jsonPath.list("references");
504                 for (auto & ref : info->references)
505                     jsonRefs.elem(ref);
506             }
507 
508             if (info->ca != "")
509                 jsonPath.attr("ca", info->ca);
510 
511             std::pair<uint64_t, uint64_t> closureSizes;
512 
513             if (showClosureSize) {
514                 closureSizes = getClosureSize(storePath);
515                 jsonPath.attr("closureSize", closureSizes.first);
516             }
517 
518             if (includeImpureInfo) {
519 
520                 if (info->deriver != "")
521                     jsonPath.attr("deriver", info->deriver);
522 
523                 if (info->registrationTime)
524                     jsonPath.attr("registrationTime", info->registrationTime);
525 
526                 if (info->ultimate)
527                     jsonPath.attr("ultimate", info->ultimate);
528 
529                 if (!info->sigs.empty()) {
530                     auto jsonSigs = jsonPath.list("signatures");
531                     for (auto & sig : info->sigs)
532                         jsonSigs.elem(sig);
533                 }
534 
535                 auto narInfo = std::dynamic_pointer_cast<const NarInfo>(
536                     std::shared_ptr<const ValidPathInfo>(info));
537 
538                 if (narInfo) {
539                     if (!narInfo->url.empty())
540                         jsonPath.attr("url", narInfo->url);
541                     if (narInfo->fileHash)
542                         jsonPath.attr("downloadHash", narInfo->fileHash.to_string());
543                     if (narInfo->fileSize)
544                         jsonPath.attr("downloadSize", narInfo->fileSize);
545                     if (showClosureSize)
546                         jsonPath.attr("closureDownloadSize", closureSizes.second);
547                 }
548             }
549 
550         } catch (InvalidPath &) {
551             jsonPath.attr("valid", false);
552         }
553     }
554 }
555 
556 
getClosureSize(const Path & storePath)557 std::pair<uint64_t, uint64_t> Store::getClosureSize(const Path & storePath)
558 {
559     uint64_t totalNarSize = 0, totalDownloadSize = 0;
560     PathSet closure;
561     computeFSClosure(storePath, closure, false, false);
562     for (auto & p : closure) {
563         auto info = queryPathInfo(p);
564         totalNarSize += info->narSize;
565         auto narInfo = std::dynamic_pointer_cast<const NarInfo>(
566             std::shared_ptr<const ValidPathInfo>(info));
567         if (narInfo)
568             totalDownloadSize += narInfo->fileSize;
569     }
570     return {totalNarSize, totalDownloadSize};
571 }
572 
573 
getStats()574 const Store::Stats & Store::getStats()
575 {
576     {
577         auto state_(state.lock());
578         stats.pathInfoCacheSize = state_->pathInfoCache.size();
579     }
580     return stats;
581 }
582 
583 
buildPaths(const PathSet & paths,BuildMode buildMode)584 void Store::buildPaths(const PathSet & paths, BuildMode buildMode)
585 {
586     for (auto & path : paths)
587         if (isDerivation(path))
588             unsupported("buildPaths");
589 
590     if (queryValidPaths(paths).size() != paths.size())
591         unsupported("buildPaths");
592 }
593 
594 
copyStorePath(ref<Store> srcStore,ref<Store> dstStore,const Path & storePath,RepairFlag repair,CheckSigsFlag checkSigs)595 void copyStorePath(ref<Store> srcStore, ref<Store> dstStore,
596     const Path & storePath, RepairFlag repair, CheckSigsFlag checkSigs)
597 {
598     auto srcUri = srcStore->getUri();
599     auto dstUri = dstStore->getUri();
600 
601     Activity act(*logger, lvlInfo, actCopyPath,
602         srcUri == "local" || srcUri == "daemon"
603           ? fmt("copying path '%s' to '%s'", storePath, dstUri)
604           : dstUri == "local" || dstUri == "daemon"
605             ? fmt("copying path '%s' from '%s'", storePath, srcUri)
606             : fmt("copying path '%s' from '%s' to '%s'", storePath, srcUri, dstUri),
607         {storePath, srcUri, dstUri});
608     PushActivity pact(act.id);
609 
610     auto info = srcStore->queryPathInfo(storePath);
611 
612     uint64_t total = 0;
613 
614     if (!info->narHash) {
615         StringSink sink;
616         srcStore->narFromPath({storePath}, sink);
617         auto info2 = make_ref<ValidPathInfo>(*info);
618         info2->narHash = hashString(htSHA256, *sink.s);
619         if (!info->narSize) info2->narSize = sink.s->size();
620         if (info->ultimate) info2->ultimate = false;
621         info = info2;
622 
623         StringSource source(*sink.s);
624         dstStore->addToStore(*info, source, repair, checkSigs);
625         return;
626     }
627 
628     if (info->ultimate) {
629         auto info2 = make_ref<ValidPathInfo>(*info);
630         info2->ultimate = false;
631         info = info2;
632     }
633 
634     auto source = sinkToSource([&](Sink & sink) {
635         LambdaSink wrapperSink([&](const unsigned char * data, size_t len) {
636             sink(data, len);
637             total += len;
638             act.progress(total, info->narSize);
639         });
640         srcStore->narFromPath({storePath}, wrapperSink);
641     }, [&]() {
642         throw EndOfFile("NAR for '%s' fetched from '%s' is incomplete", storePath, srcStore->getUri());
643     });
644 
645     dstStore->addToStore(*info, *source, repair, checkSigs);
646 }
647 
648 
copyPaths(ref<Store> srcStore,ref<Store> dstStore,const PathSet & storePaths,RepairFlag repair,CheckSigsFlag checkSigs,SubstituteFlag substitute)649 void copyPaths(ref<Store> srcStore, ref<Store> dstStore, const PathSet & storePaths,
650     RepairFlag repair, CheckSigsFlag checkSigs, SubstituteFlag substitute)
651 {
652     PathSet valid = dstStore->queryValidPaths(storePaths, substitute);
653 
654     PathSet missing;
655     for (auto & path : storePaths)
656         if (!valid.count(path)) missing.insert(path);
657 
658     if (missing.empty()) return;
659 
660     Activity act(*logger, lvlInfo, actCopyPaths, fmt("copying %d paths", missing.size()));
661 
662     std::atomic<size_t> nrDone{0};
663     std::atomic<size_t> nrFailed{0};
664     std::atomic<uint64_t> bytesExpected{0};
665     std::atomic<uint64_t> nrRunning{0};
666 
667     auto showProgress = [&]() {
668         act.progress(nrDone, missing.size(), nrRunning, nrFailed);
669     };
670 
671     ThreadPool pool;
672 
673     processGraph<Path>(pool,
674         PathSet(missing.begin(), missing.end()),
675 
676         [&](const Path & storePath) {
677             if (dstStore->isValidPath(storePath)) {
678                 nrDone++;
679                 showProgress();
680                 return PathSet();
681             }
682 
683             auto info = srcStore->queryPathInfo(storePath);
684 
685             bytesExpected += info->narSize;
686             act.setExpected(actCopyPath, bytesExpected);
687 
688             return info->references;
689         },
690 
691         [&](const Path & storePath) {
692             checkInterrupt();
693 
694             if (!dstStore->isValidPath(storePath)) {
695                 MaintainCount<decltype(nrRunning)> mc(nrRunning);
696                 showProgress();
697                 try {
698                     copyStorePath(srcStore, dstStore, storePath, repair, checkSigs);
699                 } catch (Error &e) {
700                     nrFailed++;
701                     if (!settings.keepGoing)
702                         throw e;
703                     logger->log(lvlError, format("could not copy %s: %s") % storePath % e.what());
704                     showProgress();
705                     return;
706                 }
707             }
708 
709             nrDone++;
710             showProgress();
711         });
712 }
713 
714 
copyClosure(ref<Store> srcStore,ref<Store> dstStore,const PathSet & storePaths,RepairFlag repair,CheckSigsFlag checkSigs,SubstituteFlag substitute)715 void copyClosure(ref<Store> srcStore, ref<Store> dstStore,
716     const PathSet & storePaths, RepairFlag repair, CheckSigsFlag checkSigs,
717     SubstituteFlag substitute)
718 {
719     PathSet closure;
720     srcStore->computeFSClosure({storePaths}, closure);
721     copyPaths(srcStore, dstStore, closure, repair, checkSigs, substitute);
722 }
723 
724 
decodeValidPathInfo(std::istream & str,bool hashGiven)725 ValidPathInfo decodeValidPathInfo(std::istream & str, bool hashGiven)
726 {
727     ValidPathInfo info;
728     getline(str, info.path);
729     if (str.eof()) { info.path = ""; return info; }
730     if (hashGiven) {
731         string s;
732         getline(str, s);
733         info.narHash = Hash(s, htSHA256);
734         getline(str, s);
735         if (!string2Int(s, info.narSize)) throw Error("number expected");
736     }
737     getline(str, info.deriver);
738     string s; int n;
739     getline(str, s);
740     if (!string2Int(s, n)) throw Error("number expected");
741     while (n--) {
742         getline(str, s);
743         info.references.insert(s);
744     }
745     if (!str || str.eof()) throw Error("missing input");
746     return info;
747 }
748 
749 
showPaths(const PathSet & paths)750 string showPaths(const PathSet & paths)
751 {
752     string s;
753     for (auto & i : paths) {
754         if (s.size() != 0) s += ", ";
755         s += "'" + i + "'";
756     }
757     return s;
758 }
759 
760 
fingerprint() const761 std::string ValidPathInfo::fingerprint() const
762 {
763     if (narSize == 0 || !narHash)
764         throw Error(format("cannot calculate fingerprint of path '%s' because its size/hash is not known")
765             % path);
766     return
767         "1;" + path + ";"
768         + narHash.to_string(Base32) + ";"
769         + std::to_string(narSize) + ";"
770         + concatStringsSep(",", references);
771 }
772 
773 
sign(const SecretKey & secretKey)774 void ValidPathInfo::sign(const SecretKey & secretKey)
775 {
776     sigs.insert(secretKey.signDetached(fingerprint()));
777 }
778 
779 
isContentAddressed(const Store & store) const780 bool ValidPathInfo::isContentAddressed(const Store & store) const
781 {
782     auto warn = [&]() {
783         printError(format("warning: path '%s' claims to be content-addressed but isn't") % path);
784     };
785 
786     if (hasPrefix(ca, "text:")) {
787         Hash hash(std::string(ca, 5));
788         if (store.makeTextPath(storePathToName(path), hash, references) == path)
789             return true;
790         else
791             warn();
792     }
793 
794     else if (hasPrefix(ca, "fixed:")) {
795         bool recursive = ca.compare(6, 2, "r:") == 0;
796         Hash hash(std::string(ca, recursive ? 8 : 6));
797         if (references.empty() &&
798             store.makeFixedOutputPath(recursive, hash, storePathToName(path)) == path)
799             return true;
800         else
801             warn();
802     }
803 
804     return false;
805 }
806 
807 
checkSignatures(const Store & store,const PublicKeys & publicKeys) const808 size_t ValidPathInfo::checkSignatures(const Store & store, const PublicKeys & publicKeys) const
809 {
810     if (isContentAddressed(store)) return maxSigs;
811 
812     size_t good = 0;
813     for (auto & sig : sigs)
814         if (checkSignature(publicKeys, sig))
815             good++;
816     return good;
817 }
818 
819 
checkSignature(const PublicKeys & publicKeys,const std::string & sig) const820 bool ValidPathInfo::checkSignature(const PublicKeys & publicKeys, const std::string & sig) const
821 {
822     return verifyDetached(fingerprint(), sig, publicKeys);
823 }
824 
825 
shortRefs() const826 Strings ValidPathInfo::shortRefs() const
827 {
828     Strings refs;
829     for (auto & r : references)
830         refs.push_back(baseNameOf(r));
831     return refs;
832 }
833 
834 
makeFixedOutputCA(bool recursive,const Hash & hash)835 std::string makeFixedOutputCA(bool recursive, const Hash & hash)
836 {
837     return "fixed:" + (recursive ? (std::string) "r:" : "") + hash.to_string();
838 }
839 
840 
addToStore(const ValidPathInfo & info,Source & narSource,RepairFlag repair,CheckSigsFlag checkSigs,std::shared_ptr<FSAccessor> accessor)841 void Store::addToStore(const ValidPathInfo & info, Source & narSource,
842     RepairFlag repair, CheckSigsFlag checkSigs,
843     std::shared_ptr<FSAccessor> accessor)
844 {
845     addToStore(info, make_ref<std::string>(narSource.drain()), repair, checkSigs, accessor);
846 }
847 
addToStore(const ValidPathInfo & info,const ref<std::string> & nar,RepairFlag repair,CheckSigsFlag checkSigs,std::shared_ptr<FSAccessor> accessor)848 void Store::addToStore(const ValidPathInfo & info, const ref<std::string> & nar,
849     RepairFlag repair, CheckSigsFlag checkSigs,
850     std::shared_ptr<FSAccessor> accessor)
851 {
852     StringSource source(*nar);
853     addToStore(info, source, repair, checkSigs, accessor);
854 }
855 
856 }
857 
858 
859 #include "local-store.hh"
860 #include "remote-store.hh"
861 
862 
863 namespace nix {
864 
865 
866 RegisterStoreImplementation::Implementations * RegisterStoreImplementation::implementations = 0;
867 
868 /* Split URI into protocol+hierarchy part and its parameter set. */
splitUriAndParams(const std::string & uri_)869 std::pair<std::string, Store::Params> splitUriAndParams(const std::string & uri_)
870 {
871     auto uri(uri_);
872     Store::Params params;
873     auto q = uri.find('?');
874     if (q != std::string::npos) {
875         for (auto s : tokenizeString<Strings>(uri.substr(q + 1), "&")) {
876             auto e = s.find('=');
877             if (e != std::string::npos) {
878                 auto value = s.substr(e + 1);
879                 std::string decoded;
880                 for (size_t i = 0; i < value.size(); ) {
881                     if (value[i] == '%') {
882                         if (i + 2 >= value.size())
883                             throw Error("invalid URI parameter '%s'", value);
884                         try {
885                             decoded += std::stoul(std::string(value, i + 1, 2), 0, 16);
886                             i += 3;
887                         } catch (...) {
888                             throw Error("invalid URI parameter '%s'", value);
889                         }
890                     } else
891                         decoded += value[i++];
892                 }
893                 params[s.substr(0, e)] = decoded;
894             }
895         }
896         uri = uri_.substr(0, q);
897     }
898     return {uri, params};
899 }
900 
openStore(const std::string & uri_,const Store::Params & extraParams)901 ref<Store> openStore(const std::string & uri_,
902     const Store::Params & extraParams)
903 {
904     auto [uri, uriParams] = splitUriAndParams(uri_);
905     auto params = extraParams;
906     params.insert(uriParams.begin(), uriParams.end());
907 
908     for (auto fun : *RegisterStoreImplementation::implementations) {
909         auto store = fun(uri, params);
910         if (store) {
911             store->warnUnknownSettings();
912             return ref<Store>(store);
913         }
914     }
915 
916     throw Error("don't know how to open Nix store '%s'", uri);
917 }
918 
919 
getStoreType(const std::string & uri,const std::string & stateDir)920 StoreType getStoreType(const std::string & uri, const std::string & stateDir)
921 {
922     if (uri == "daemon") {
923         return tDaemon;
924     } else if (uri == "local" || hasPrefix(uri, "/")) {
925         return tLocal;
926     } else if (uri == "" || uri == "auto") {
927         if (access(stateDir.c_str(), R_OK | W_OK) == 0)
928             return tLocal;
929         else if (pathExists(settings.nixDaemonSocketFile))
930             return tDaemon;
931         else
932             return tLocal;
933     } else {
934         return tOther;
935     }
936 }
937 
938 
939 static RegisterStoreImplementation regStore([](
940     const std::string & uri, const Store::Params & params)
941     -> std::shared_ptr<Store>
__anon86f7a2e60c02( const std::string & uri, const Store::Params & params) 942 {
943     switch (getStoreType(uri, get(params, "state", settings.nixStateDir))) {
944         case tDaemon:
945             return std::shared_ptr<Store>(std::make_shared<UDSRemoteStore>(params));
946         case tLocal: {
947             Store::Params params2 = params;
948             if (hasPrefix(uri, "/"))
949                 params2["root"] = uri;
950             return std::shared_ptr<Store>(std::make_shared<LocalStore>(params2));
951         }
952         default:
953             return nullptr;
954     }
955 });
956 
957 
getDefaultSubstituters()958 std::list<ref<Store>> getDefaultSubstituters()
959 {
960     static auto stores([]() {
961         std::list<ref<Store>> stores;
962 
963         StringSet done;
964 
965         auto addStore = [&](const std::string & uri) {
966             if (done.count(uri)) return;
967             done.insert(uri);
968             try {
969                 stores.push_back(openStore(uri));
970             } catch (Error & e) {
971                 printError("warning: %s", e.what());
972             }
973         };
974 
975         for (auto uri : settings.substituters.get())
976             addStore(uri);
977 
978         for (auto uri : settings.extraSubstituters.get())
979             addStore(uri);
980 
981         stores.sort([](ref<Store> & a, ref<Store> & b) {
982             return a->getPriority() < b->getPriority();
983         });
984 
985         return stores;
986     } ());
987 
988     return stores;
989 }
990 
991 
992 }
993