projects/guix·blob python-team · refs

1655 lines · 50.8 KiB

1#include "config.h"
2#include "local-store.hh"
3#include "globals.hh"
4#include "archive.hh"
5#include "pathlocks.hh"
6#include "worker-protocol.hh"
7#include "derivations.hh"
8
9#include <algorithm>
10#include <format>
11#include <cstring>
12#include <cassert>
13
14#include <sys/types.h>
15#include <sys/stat.h>
16#include <sys/time.h>
17#include <unistd.h>
18#include <utime.h>
19#include <fcntl.h>
20#include <errno.h>
21#include <stdio.h>
22#include <time.h>
23#include <grp.h>
24#include <ctype.h>
25
26#if HAVE_UNSHARE && HAVE_STATVFS && HAVE_SYS_MOUNT_H
27#include <sched.h>
28#include <sys/statvfs.h>
29#include <sys/mount.h>
30#endif
31
32#include <sys/ioctl.h>
33#include <errno.h>
34
35#include <sqlite3.h>
36
37
38namespace nix {
39
40
41void checkStoreNotSymlink()
42{
43 if (getEnv("NIX_IGNORE_SYMLINK_STORE") == "1") return;
44 Path path = settings.nixStore;
45 struct stat st;
46 while (path != "/") {
47 if (lstat(path.c_str(), &st))
48 throw SysError(std::format("getting status of `{}'", path));
49 if (S_ISLNK(st.st_mode))
50 throw Error(std::format(
51 "the path `{}' is a symlink; "
52 "this is not allowed for the store and its parent directories",
53 path));
54 path = dirOf(path);
55 }
56}
57
58
59LocalStore::LocalStore(bool reserveSpace)
60{
61 schemaPath = settings.nixDBPath + "/schema";
62
63 if (settings.readOnlyMode) {
64 openDB(false);
65 return;
66 }
67
68 /* Create missing state directories if they don't already exist. */
69 createDirs(settings.nixStore);
70 ensureStoreWritable();
71 createDirs(linksDir = settings.nixStore + "/.links");
72 Path profilesDir = settings.nixStateDir + "/profiles";
73 createDirs(profilesDir);
74 createDirs(settings.nixStateDir + "/temproots");
75 createDirs(settings.nixDBPath);
76 Path gcRootsDir = settings.nixStateDir + "/gcroots";
77 if (!pathExists(gcRootsDir)) {
78 createDirs(gcRootsDir);
79 createSymlink(profilesDir, gcRootsDir + "/profiles");
80 }
81
82 Path perUserDir = profilesDir + "/per-user";
83 createDirs(perUserDir);
84
85 /* Optionally, set permissions for a multi-user install. */
86 if (getuid() == 0 && settings.buildUsersGroup != "") {
87
88 if (chmod(perUserDir.c_str(), 0755) == -1)
89 throw SysError(std::format("could not set permissions on '{}' to 755", perUserDir));
90
91 mode_t perm = 01775;
92
93 struct group * gr = getgrnam(settings.buildUsersGroup.c_str());
94 if (!gr)
95 throw Error(std::format("the group `{}' specified in `build-users-group' does not exist",
96 settings.buildUsersGroup));
97 else {
98 struct stat st;
99 if (stat(settings.nixStore.c_str(), &st))
100 throw SysError(std::format("getting attributes of path '{}'", settings.nixStore));
101
102 if (st.st_uid != 0 || st.st_gid != gr->gr_gid || (st.st_mode & ~S_IFMT) != perm) {
103 if (chown(settings.nixStore.c_str(), 0, gr->gr_gid) == -1)
104 throw SysError(std::format("changing ownership of path '{}'", settings.nixStore));
105 if (chmod(settings.nixStore.c_str(), perm) == -1)
106 throw SysError(std::format("changing permissions on path '{}'", settings.nixStore));
107 }
108 }
109 }
110
111 checkStoreNotSymlink();
112
113 /* We can't open a SQLite database if the disk is full. Since
114 this prevents the garbage collector from running when it's most
115 needed, we reserve some dummy space that we can free just
116 before doing a garbage collection. */
117 try {
118 Path reservedPath = settings.nixDBPath + "/reserved";
119 if (reserveSpace) {
120 struct stat st;
121 if (stat(reservedPath.c_str(), &st) == -1 ||
122 st.st_size != settings.reservedSize)
123 {
124 AutoCloseFD fd = open(reservedPath.c_str(), O_WRONLY | O_CREAT, 0600);
125 int res = -1;
126#if HAVE_POSIX_FALLOCATE
127 res = posix_fallocate(fd, 0, settings.reservedSize);
128#endif
129 if (res == -1) {
130 writeFull(fd, string(settings.reservedSize, 'X'));
131 ftruncate(fd, settings.reservedSize);
132 }
133 }
134 }
135 else
136 deletePath(reservedPath);
137 } catch (SysError & e) { /* don't care about errors */
138 }
139
140 /* Acquire the big fat lock in shared mode to make sure that no
141 schema upgrade is in progress. */
142 try {
143 Path globalLockPath = settings.nixDBPath + "/big-lock";
144 globalLock = openLockFile(globalLockPath.c_str(), true);
145 } catch (SysError & e) {
146 if (e.errNo != EACCES) throw;
147 settings.readOnlyMode = true;
148 openDB(false);
149 return;
150 }
151
152 if (!lockFile(globalLock, ltRead, false)) {
153 printMsg(lvlError, "waiting for the big store lock...");
154 lockFile(globalLock, ltRead, true);
155 }
156
157 /* Check the current database schema and if necessary do an
158 upgrade. */
159 int curSchema = getSchema();
160 if (curSchema > nixSchemaVersion)
161 throw Error(std::format("current store schema is version {}, but I only support {}",
162 curSchema, nixSchemaVersion));
163
164 else if (curSchema == 0) { /* new store */
165 curSchema = nixSchemaVersion;
166 openDB(true);
167 writeFile(schemaPath, std::format("{}", nixSchemaVersion));
168 }
169
170 else if (curSchema < nixSchemaVersion) {
171 /* Guix always used version 7 of the schema. */
172 throw Error(
173 std::format("Your store database uses an implausibly old schema, version {}.",
174 curSchema));
175 }
176
177 else openDB(false);
178}
179
180
181LocalStore::~LocalStore()
182{
183 try {
184 if (fdTempRoots != -1) {
185 fdTempRoots.close();
186 unlink(fnTempRoots.c_str());
187 }
188 } catch (...) {
189 ignoreException();
190 }
191}
192
193
194int LocalStore::getSchema()
195{
196 int curSchema = 0;
197 if (pathExists(schemaPath)) {
198 string s = readFile(schemaPath);
199 if (!string2Int(s, curSchema))
200 throw Error(std::format("`{}' is corrupt", schemaPath));
201 }
202 return curSchema;
203}
204
205
206void LocalStore::openDB(bool create)
207{
208 if (access(settings.nixDBPath.c_str(), R_OK | W_OK))
209 throw SysError(std::format("store database directory `{}' is not writable", settings.nixDBPath));
210
211 /* Open the store database. */
212 string dbPath = settings.nixDBPath + "/db.sqlite";
213 if (sqlite3_open_v2(dbPath.c_str(), &db.db,
214 SQLITE_OPEN_READWRITE | (create ? SQLITE_OPEN_CREATE : 0), 0) != SQLITE_OK)
215 throw Error(std::format("cannot open store database `{}'", dbPath));
216
217 if (sqlite3_busy_timeout(db, 60 * 60 * 1000) != SQLITE_OK)
218 throwSQLiteError(db, "setting timeout");
219
220 if (sqlite3_exec(db, "pragma foreign_keys = 1;", 0, 0, 0) != SQLITE_OK)
221 throwSQLiteError(db, "enabling foreign keys");
222
223 /* !!! check whether sqlite has been built with foreign key
224 support */
225
226 /* Whether SQLite should fsync(). "Normal" synchronous mode
227 should be safe enough. If the user asks for it, don't sync at
228 all. This can cause database corruption if the system
229 crashes. */
230 string syncMode = settings.fsyncMetadata ? "normal" : "off";
231 if (sqlite3_exec(db, ("pragma synchronous = " + syncMode + ";").c_str(), 0, 0, 0) != SQLITE_OK)
232 throwSQLiteError(db, "setting synchronous mode");
233
234 /* Set the SQLite journal mode. WAL mode is fastest, so it's the
235 default. */
236 string mode = settings.useSQLiteWAL ? "wal" : "truncate";
237 string prevMode;
238 {
239 SQLiteStmt stmt;
240 stmt.create(db, "pragma main.journal_mode;");
241 if (sqlite3_step(stmt) != SQLITE_ROW)
242 throwSQLiteError(db, "querying journal mode");
243 prevMode = string((const char *) sqlite3_column_text(stmt, 0));
244 }
245 if (prevMode != mode &&
246 sqlite3_exec(db, ("pragma main.journal_mode = " + mode + ";").c_str(), 0, 0, 0) != SQLITE_OK)
247 throwSQLiteError(db, "setting journal mode");
248
249 /* Increase the auto-checkpoint interval to 40000 pages. This
250 seems enough to ensure that instantiating the NixOS system
251 derivation is done in a single fsync(). */
252 if (mode == "wal" && sqlite3_exec(db, "pragma wal_autocheckpoint = 40000;", 0, 0, 0) != SQLITE_OK)
253 throwSQLiteError(db, "setting autocheckpoint interval");
254
255 /* Initialise the database schema, if necessary. */
256 if (create) {
257 const char * schema =
258#include "schema.sql.hh"
259 ;
260 if (sqlite3_exec(db, (const char *) schema, 0, 0, 0) != SQLITE_OK)
261 throwSQLiteError(db, "initialising database schema");
262 }
263
264 /* Prepare SQL statements. */
265 stmtRegisterValidPath.create(db,
266 "insert into ValidPaths (path, hash, registrationTime, deriver, narSize) values (?, ?, ?, ?, ?);");
267 stmtUpdatePathInfo.create(db,
268 "update ValidPaths set narSize = ?, hash = ? where path = ?;");
269 stmtAddReference.create(db,
270 "insert or replace into Refs (referrer, reference) values (?, ?);");
271 stmtQueryPathInfo.create(db,
272 "select id, hash, registrationTime, deriver, narSize from ValidPaths where path = ?;");
273 stmtQueryReferences.create(db,
274 "select path from Refs join ValidPaths on reference = id where referrer = ?;");
275 stmtQueryReferrers.create(db,
276 "select path from Refs join ValidPaths on referrer = id where reference = (select id from ValidPaths where path = ?);");
277 stmtInvalidatePath.create(db,
278 "delete from ValidPaths where path = ?;");
279 stmtRegisterFailedPath.create(db,
280 "insert or ignore into FailedPaths (path, time) values (?, ?);");
281 stmtHasPathFailed.create(db,
282 "select time from FailedPaths where path = ?;");
283 stmtQueryFailedPaths.create(db,
284 "select path from FailedPaths;");
285 // If the path is a derivation, then clear its outputs.
286 stmtClearFailedPath.create(db,
287 "delete from FailedPaths where ?1 = '*' or path = ?1 "
288 "or path in (select d.path from DerivationOutputs d join ValidPaths v on d.drv = v.id where v.path = ?1);");
289 stmtAddDerivationOutput.create(db,
290 "insert or replace into DerivationOutputs (drv, id, path) values (?, ?, ?);");
291 stmtQueryValidDerivers.create(db,
292 "select v.id, v.path from DerivationOutputs d join ValidPaths v on d.drv = v.id where d.path = ?;");
293 stmtQueryDerivationOutputs.create(db,
294 "select id, path from DerivationOutputs where drv = ?;");
295 // Use "path >= ?" with limit 1 rather than "path like '?%'" to
296 // ensure efficient lookup.
297 stmtQueryPathFromHashPart.create(db,
298 "select path from ValidPaths where path >= ? limit 1;");
299 stmtQueryValidPaths.create(db, "select path from ValidPaths");
300}
301
302
303/* To improve purity, users may want to make the store a read-only bind mount.
304 So make the store writable for this process. In case the store is read-only
305 and cannot be made writable, throw an error. */
306void LocalStore::ensureStoreWritable()
307{
308#if HAVE_UNSHARE && HAVE_STATVFS && HAVE_SYS_MOUNT_H && defined(MS_BIND) && defined(MS_REMOUNT)
309 /* Check if /nix/store is on a read-only mount. */
310 struct statvfs stat;
311 if (statvfs(settings.nixStore.c_str(), &stat) != 0)
312 throw SysError("getting info about the store mount point");
313
314 if (stat.f_flag & ST_RDONLY) {
315 if (getuid() != 0) {
316 throw Error(
317 std::format(
318 "'{}' is read-only; make sure to mount it read-write "
319 "for proper guix-daemon operation",
320 settings.nixStore));
321 }
322
323 if (unshare(CLONE_NEWNS) == -1)
324 throw SysError("setting up a private mount namespace");
325
326 if (mount(0, settings.nixStore.c_str(), "none", MS_REMOUNT | MS_BIND, 0) == -1)
327 throw SysError(std::format("remounting {} writable", settings.nixStore));
328 }
329#endif
330}
331
332
333const time_t mtimeStore = 1; /* 1 second into the epoch */
334
335
336static void canonicaliseTimestampAndPermissions(const Path & path, const struct stat & st)
337{
338 if (!S_ISLNK(st.st_mode)) {
339
340 /* Mask out all type related bits. */
341 mode_t mode = st.st_mode & ~S_IFMT;
342
343 if (mode != 0444 && mode != 0555) {
344 mode = (st.st_mode & S_IFMT)
345 | 0444
346 | (st.st_mode & S_IXUSR ? 0111 : 0);
347 if (chmod(path.c_str(), mode) == -1)
348 throw SysError(std::format("changing mode of `{}' to {:o}", path, mode));
349 }
350
351 }
352
353 if (st.st_mtime != mtimeStore) {
354 struct timeval times[2];
355 times[0].tv_sec = st.st_atime;
356 times[0].tv_usec = 0;
357 times[1].tv_sec = mtimeStore;
358 times[1].tv_usec = 0;
359#if HAVE_LUTIMES
360 if (lutimes(path.c_str(), times) == -1)
361 if (errno != ENOSYS ||
362 (!S_ISLNK(st.st_mode) && utimes(path.c_str(), times) == -1))
363#else
364 if (!S_ISLNK(st.st_mode) && utimes(path.c_str(), times) == -1)
365#endif
366 throw SysError(std::format("changing modification time of `{}'", path));
367 }
368}
369
370
371void canonicaliseTimestampAndPermissions(const Path & path)
372{
373 struct stat st;
374 if (lstat(path.c_str(), &st))
375 throw SysError(std::format("getting attributes of path `{}'", path));
376 canonicaliseTimestampAndPermissions(path, st);
377}
378
379
380static void canonicalisePathMetaData_(const Path & path, uid_t fromUid, InodesSeen & inodesSeen)
381{
382 checkInterrupt();
383
384 struct stat st;
385 if (lstat(path.c_str(), &st))
386 throw SysError(std::format("getting attributes of path `{}'", path));
387
388 /* Really make sure that the path is of a supported type. */
389 if (!(S_ISREG(st.st_mode) || S_ISDIR(st.st_mode) || S_ISLNK(st.st_mode)))
390 throw Error(std::format("file `{}' has an unsupported type", path));
391
392 /* Fail if the file is not owned by the build user. This prevents
393 us from messing up the ownership/permissions of files
394 hard-linked into the output (e.g. "ln /etc/shadow $out/foo").
395 However, ignore files that we chown'ed ourselves previously to
396 ensure that we don't fail on hard links within the same build
397 (i.e. "touch $out/foo; ln $out/foo $out/bar"). */
398 if (fromUid != (uid_t) -1 && st.st_uid != fromUid) {
399 assert(!S_ISDIR(st.st_mode));
400 if (inodesSeen.find(Inode(st.st_dev, st.st_ino)) == inodesSeen.end())
401 throw BuildError(std::format("invalid ownership on file `{}'", path));
402 mode_t mode = st.st_mode & ~S_IFMT;
403 assert(S_ISLNK(st.st_mode) || (st.st_uid == geteuid() && (mode == 0444 || mode == 0555) && st.st_mtime == mtimeStore));
404 return;
405 }
406
407 inodesSeen.insert(Inode(st.st_dev, st.st_ino));
408
409 canonicaliseTimestampAndPermissions(path, st);
410
411 /* Change ownership to the current uid. If it's a symlink, use
412 lchown if available, otherwise don't bother. Wrong ownership
413 of a symlink doesn't matter, since the owning user can't change
414 the symlink and can't delete it because the directory is not
415 writable. The only exception is top-level paths in the
416 store (since that directory is group-writable for the build
417 users group); we check for this case below. */
418 if (st.st_uid != geteuid()) {
419#if HAVE_LCHOWN
420 if (lchown(path.c_str(), geteuid(), getegid()) == -1)
421#else
422 if (!S_ISLNK(st.st_mode) &&
423 chown(path.c_str(), geteuid(), getegid()) == -1)
424#endif
425 throw SysError(std::format("changing owner of `{}' to {}",
426 path, geteuid()));
427 }
428
429 if (S_ISDIR(st.st_mode)) {
430 DirEntries entries = readDirectory(path);
431 for (auto & i : entries)
432 canonicalisePathMetaData_(path + "/" + i.name, fromUid, inodesSeen);
433 }
434}
435
436
437void canonicalisePathMetaData(const Path & path, uid_t fromUid, InodesSeen & inodesSeen)
438{
439 canonicalisePathMetaData_(path, fromUid, inodesSeen);
440
441 /* On platforms that don't have lchown(), the top-level path can't
442 be a symlink, since we can't change its ownership. */
443 struct stat st;
444 if (lstat(path.c_str(), &st))
445 throw SysError(std::format("getting attributes of path `{}'", path));
446
447 if (st.st_uid != geteuid()) {
448 assert(S_ISLNK(st.st_mode));
449 throw Error(std::format("wrong ownership of top-level store path `{}'", path));
450 }
451}
452
453
454void canonicalisePathMetaData(const Path & path, uid_t fromUid)
455{
456 InodesSeen inodesSeen;
457 canonicalisePathMetaData(path, fromUid, inodesSeen);
458}
459
460
461void LocalStore::checkDerivationOutputs(const Path & drvPath, const Derivation & drv)
462{
463 string drvName = storePathToName(drvPath);
464 assert(isDerivation(drvName));
465 drvName = string(drvName, 0, drvName.size() - drvExtension.size());
466
467 if (isFixedOutputDrv(drv)) {
468 DerivationOutputs::const_iterator out = drv.outputs.find("out");
469 if (out == drv.outputs.end())
470 throw Error(std::format("derivation `{}' does not have an output named `out'", drvPath));
471
472 bool recursive; HashType ht; Hash h;
473 out->second.parseHashInfo(recursive, ht, h);
474 Path outPath = makeFixedOutputPath(recursive, ht, h, drvName);
475
476 StringPairs::const_iterator j = drv.env.find("out");
477 if (out->second.path != outPath || j == drv.env.end() || j->second != outPath)
478 throw Error(std::format("derivation `{}' has incorrect output `{}', should be `{}'",
479 drvPath, out->second.path, outPath));
480 }
481
482 else {
483 Derivation drvCopy(drv);
484 for (auto& i : drvCopy.outputs) {
485 i.second.path = "";
486 drvCopy.env[i.first] = "";
487 }
488
489 Hash h = hashDerivationModulo(*this, drvCopy);
490
491 for (const auto& i : drv.outputs) {
492 Path outPath = makeOutputPath(i.first, h, drvName);
493 StringPairs::const_iterator j = drv.env.find(i.first);
494 if (i.second.path != outPath || j == drv.env.end() || j->second != outPath)
495 throw Error(std::format("derivation `{}' has incorrect output `{}', should be `{}'",
496 drvPath, i.second.path, outPath));
497 }
498 }
499}
500
501
502uint64_t LocalStore::addValidPath(const ValidPathInfo & info, bool checkOutputs)
503{
504 stmtRegisterValidPath.use()
505 (info.path)
506 ("sha256:" + printHash(info.hash))
507 (info.registrationTime == 0 ? time(0) : info.registrationTime)
508 (info.deriver, info.deriver != "")
509 (info.narSize, info.narSize != 0)
510 .exec();
511 uint64_t id = sqlite3_last_insert_rowid(db);
512
513 /* If this is a derivation, then store the derivation outputs in
514 the database. This is useful for the garbage collector: it can
515 efficiently query whether a path is an output of some
516 derivation. */
517 if (isDerivation(info.path)) {
518 Derivation drv = readDerivation(info.path);
519
520 /* Verify that the output paths in the derivation are correct
521 (i.e., follow the scheme for computing output paths from
522 derivations). Note that if this throws an error, then the
523 DB transaction is rolled back, so the path validity
524 registration above is undone. */
525 if (checkOutputs) checkDerivationOutputs(info.path, drv);
526
527 for (auto & i : drv.outputs) {
528 stmtAddDerivationOutput.use()
529 (id)
530 (i.first)
531 (i.second.path)
532 .exec();
533 }
534 }
535
536 return id;
537}
538
539
540void LocalStore::addReference(uint64_t referrer, uint64_t reference)
541{
542 stmtAddReference.use()(referrer)(reference).exec();
543}
544
545
546void LocalStore::registerFailedPath(const Path & path)
547{
548 retrySQLite<void>([&]() {
549 stmtRegisterFailedPath.use()(path)(time(0)).step();
550 });
551}
552
553
554bool LocalStore::hasPathFailed(const Path & path)
555{
556 return retrySQLite<bool>([&]() {
557 return stmtHasPathFailed.use()(path).next();
558 });
559}
560
561
562PathSet LocalStore::queryFailedPaths()
563{
564 return retrySQLite<PathSet>([&]() {
565 auto useQueryFailedPaths(stmtQueryFailedPaths.use());
566
567 PathSet res;
568 while (useQueryFailedPaths.next())
569 res.insert(useQueryFailedPaths.getStr(0));
570
571 return res;
572 });
573}
574
575
576void LocalStore::clearFailedPaths(const PathSet & paths)
577{
578 retrySQLite<void>([&]() {
579 SQLiteTxn txn(db);
580
581 for (auto & path : paths)
582 stmtClearFailedPath.use()(path).exec();
583
584 txn.commit();
585 });
586}
587
588
589Hash parseHashField(const Path & path, const string & s)
590{
591 string::size_type colon = s.find(':');
592 if (colon == string::npos)
593 throw Error(std::format("corrupt hash `{}' in valid-path entry for `{}'",
594 s, path));
595 HashType ht = parseHashType(string(s, 0, colon));
596 if (ht == htUnknown)
597 throw Error(std::format("unknown hash type `{}' in valid-path entry for `{}'",
598 string(s, 0, colon), path));
599 return parseHash(ht, string(s, colon + 1));
600}
601
602
603ValidPathInfo LocalStore::queryPathInfo(const Path & path)
604{
605 ValidPathInfo info;
606 info.path = path;
607
608 assertStorePath(path);
609
610 return retrySQLite<ValidPathInfo>([&]() {
611
612 /* Get the path info. */
613 auto useQueryPathInfo(stmtQueryPathInfo.use()(path));
614
615 if (!useQueryPathInfo.next())
616 throw Error(std::format("path `{}' is not valid", path));
617
618 info.id = useQueryPathInfo.getInt(0);
619
620 info.hash = parseHashField(path, useQueryPathInfo.getStr(1));
621
622 info.registrationTime = useQueryPathInfo.getInt(2);
623
624 auto s = (const char *) sqlite3_column_text(stmtQueryPathInfo, 3);
625 if (s) info.deriver = s;
626
627 /* Note that narSize = NULL yields 0. */
628 info.narSize = useQueryPathInfo.getInt(4);
629
630 /* Get the references. */
631 auto useQueryReferences(stmtQueryReferences.use()(info.id));
632
633 while (useQueryReferences.next())
634 info.references.insert(useQueryReferences.getStr(0));
635
636 return info;
637 });
638}
639
640
641/* Update path info in the database. Currently only updates the
642 narSize field. */
643void LocalStore::updatePathInfo(const ValidPathInfo & info)
644{
645 stmtUpdatePathInfo.use()
646 (info.narSize, info.narSize != 0)
647 ("sha256:" + printHash(info.hash))
648 (info.path)
649 .exec();
650}
651
652
653uint64_t LocalStore::queryValidPathId(const Path & path)
654{
655 auto use(stmtQueryPathInfo.use()(path));
656 if (!use.next())
657 throw Error(std::format("path `{}' is not valid", path));
658 return use.getInt(0);
659}
660
661
662bool LocalStore::isValidPath_(const Path & path)
663{
664 return stmtQueryPathInfo.use()(path).next();
665}
666
667
668bool LocalStore::isValidPath(const Path & path)
669{
670 return retrySQLite<bool>([&]() {
671 return isValidPath_(path);
672 });
673}
674
675
676PathSet LocalStore::queryValidPaths(const PathSet & paths)
677{
678 return retrySQLite<PathSet>([&]() {
679 PathSet res;
680 for (const auto& i : paths)
681 if (isValidPath_(i)) res.insert(i);
682 return res;
683 });
684}
685
686
687PathSet LocalStore::queryAllValidPaths()
688{
689 return retrySQLite<PathSet>([&]() {
690 auto use(stmtQueryValidPaths.use());
691 PathSet res;
692 while (use.next()) res.insert(use.getStr(0));
693 return res;
694 });
695}
696
697
698void LocalStore::queryReferences(const Path & path,
699 PathSet & references)
700{
701 ValidPathInfo info = queryPathInfo(path);
702 references.insert(info.references.begin(), info.references.end());
703}
704
705
706void LocalStore::queryReferrers_(const Path & path, PathSet & referrers)
707{
708 auto useQueryReferrers(stmtQueryReferrers.use()(path));
709
710 while (useQueryReferrers.next())
711 referrers.insert(useQueryReferrers.getStr(0));
712}
713
714
715void LocalStore::queryReferrers(const Path & path, PathSet & referrers)
716{
717 assertStorePath(path);
718 return retrySQLite<void>([&]() {
719 queryReferrers_(path, referrers);
720 });
721}
722
723
724Path LocalStore::queryDeriver(const Path & path)
725{
726 return queryPathInfo(path).deriver;
727}
728
729
730PathSet LocalStore::queryValidDerivers(const Path & path)
731{
732 assertStorePath(path);
733
734 return retrySQLite<PathSet>([&]() {
735 auto useQueryValidDerivers(stmtQueryValidDerivers.use()(path));
736
737 PathSet derivers;
738 while (useQueryValidDerivers.next())
739 derivers.insert(useQueryValidDerivers.getStr(1));
740
741 return derivers;
742 });
743}
744
745
746PathSet LocalStore::queryDerivationOutputs(const Path & path)
747{
748 return retrySQLite<PathSet>([&]() {
749 auto useQueryDerivationOutputs(stmtQueryDerivationOutputs.use()(queryValidPathId(path)));
750
751 PathSet outputs;
752 while (useQueryDerivationOutputs.next())
753 outputs.insert(useQueryDerivationOutputs.getStr(1));
754
755 return outputs;
756 });
757}
758
759
760StringSet LocalStore::queryDerivationOutputNames(const Path & path)
761{
762 return retrySQLite<StringSet>([&]() {
763 auto useQueryDerivationOutputs(stmtQueryDerivationOutputs.use()(queryValidPathId(path)));
764
765 StringSet outputNames;
766 while (useQueryDerivationOutputs.next())
767 outputNames.insert(useQueryDerivationOutputs.getStr(0));
768
769 return outputNames;
770 });
771}
772
773
774Path LocalStore::queryPathFromHashPart(const string & hashPart)
775{
776 if (hashPart.size() != 32) throw Error("invalid hash part");
777
778 Path prefix = settings.nixStore + "/" + hashPart;
779
780 return retrySQLite<Path>([&]() -> Path {
781 auto useQueryPathFromHashPart(stmtQueryPathFromHashPart.use()(prefix));
782
783 if (!useQueryPathFromHashPart.next()) return "";
784
785 const char * s = (const char *) sqlite3_column_text(stmtQueryPathFromHashPart, 0);
786 return s && prefix.compare(0, prefix.size(), s, prefix.size()) == 0 ? s : "";
787 });
788}
789
790/* Read a line from the substituter's reply file descriptor, while also
791 processing its stderr. */
792string LocalStore::getLineFromSubstituter(Agent & run)
793{
794 string res, err;
795
796 while (1) {
797 checkInterrupt();
798
799 fd_set fds;
800 FD_ZERO(&fds);
801 FD_SET(run.fromAgent.readSide, &fds);
802 FD_SET(run.builderOut.readSide, &fds);
803
804 /* Wait for data to appear on the substituter's stdout or
805 stderr. */
806 if (select(std::max(run.fromAgent.readSide, run.builderOut.readSide) + 1, &fds, 0, 0, 0) == -1) {
807 if (errno == EINTR) continue;
808 throw SysError("waiting for input from the substituter");
809 }
810
811 /* Completely drain stderr before dealing with stdout. */
812 if (FD_ISSET(run.fromAgent.readSide, &fds)) {
813 char buf[4096];
814 ssize_t n = read(run.fromAgent.readSide, (unsigned char *) buf, sizeof(buf));
815 if (n == -1) {
816 if (errno == EINTR) continue;
817 throw SysError("reading from substituter's stderr");
818 }
819 if (n == 0) throw EndOfFile(std::format("`{} substitute' died unexpectedly",
820 settings.guixProgram));
821 err.append(buf, n);
822 string::size_type p;
823 while (((p = err.find('\n')) != string::npos)
824 || ((p = err.find('\r')) != string::npos)) {
825 string thing(err, 0, p + 1);
826 writeToStderr("substitute: " + thing);
827 err = string(err, p + 1);
828 }
829 }
830
831 /* Read from stdout until we get a newline or the buffer is empty. */
832 else if (FD_ISSET(run.builderOut.readSide, &fds)) {
833 unsigned char c;
834 readFull(run.builderOut.readSide, (unsigned char *) &c, 1);
835 if (c == '\n') {
836 if (!err.empty()) printMsg(lvlError, "substitute: " + err);
837 return res;
838 }
839 res += c;
840 }
841 }
842}
843
844
845template<class T> T LocalStore::getIntLineFromSubstituter(Agent & run)
846{
847 string s = getLineFromSubstituter(run);
848 T res;
849 if (!string2Int(s, res))
850 throw Error(std::format("integer expected from stream: {}", s));
851 return res;
852}
853
854
855PathSet LocalStore::querySubstitutablePaths(const PathSet & paths)
856{
857 PathSet res;
858
859 if (!settings.useSubstitutes || paths.empty()) return res;
860
861 Agent & run = *substituter();
862
863 string s = "have ";
864 for (const auto& j : paths)
865 if (res.find(j) == res.end()) { s += j; s += " "; }
866 writeLine(run.toAgent.writeSide, s);
867 while (true) {
868 /* FIXME: we only read stderr when an error occurs, so
869 substituters should only write (short) messages to
870 stderr when they fail. I.e. they shouldn't write debug
871 output. */
872 Path path = getLineFromSubstituter(run);
873 if (path == "") break;
874 res.insert(path);
875 }
876
877 return res;
878}
879
880
881std::shared_ptr<Agent> LocalStore::substituter()
882{
883 if (!runningSubstituter) {
884 const Strings args = { "substitute", "--query" };
885 const std::map<string, string> env = { { "_NIX_OPTIONS", settings.pack() } };
886 runningSubstituter = std::make_shared<Agent>(settings.guixProgram, args, env);
887 }
888
889 return runningSubstituter;
890}
891
892void LocalStore::querySubstitutablePathInfos(PathSet & paths, SubstitutablePathInfos & infos)
893{
894 if (!settings.useSubstitutes) return;
895
896 Agent & run = *substituter();
897
898 string s = "info ";
899 for (const auto& i : paths)
900 if (infos.find(i) == infos.end()) { s += i; s += " "; }
901 writeLine(run.toAgent.writeSide, s);
902
903 while (true) {
904 Path path = getLineFromSubstituter(run);
905 if (path == "") break;
906 if (paths.find(path) == paths.end())
907 throw Error(std::format("got unexpected path `{}' from substituter", path));
908 paths.erase(path);
909 SubstitutablePathInfo & info(infos[path]);
910 info.deriver = getLineFromSubstituter(run);
911 if (info.deriver != "") assertStorePath(info.deriver);
912 int nrRefs = getIntLineFromSubstituter<int>(run);
913 while (nrRefs--) {
914 Path p = getLineFromSubstituter(run);
915 assertStorePath(p);
916 info.references.insert(p);
917 }
918 info.downloadSize = getIntLineFromSubstituter<unsigned long long>(run);
919 info.narSize = getIntLineFromSubstituter<unsigned long long>(run);
920 }
921}
922
923
924void LocalStore::querySubstitutablePathInfos(const PathSet & paths,
925 SubstitutablePathInfos & infos)
926{
927 if (!paths.empty()) {
928 PathSet todo = paths;
929 querySubstitutablePathInfos(todo, infos);
930 }
931}
932
933
934Hash LocalStore::queryPathHash(const Path & path)
935{
936 return queryPathInfo(path).hash;
937}
938
939
940void LocalStore::registerValidPath(const ValidPathInfo & info)
941{
942 ValidPathInfos infos;
943 infos.push_back(info);
944 registerValidPaths(infos);
945}
946
947
948void LocalStore::registerValidPaths(const ValidPathInfos & infos)
949{
950 /* SQLite will fsync by default, but the new valid paths may not be fsync-ed.
951 * So some may want to fsync them before registering the validity, at the
952 * expense of some speed of the path registering operation. */
953 if (settings.syncBeforeRegistering) sync();
954
955 return retrySQLite<void>([&]() {
956 SQLiteTxn txn(db);
957 PathSet paths;
958
959 for (const auto& i : infos) {
960 assert(i.hash.type == htSHA256);
961 if (isValidPath_(i.path))
962 updatePathInfo(i);
963 else
964 addValidPath(i, false);
965 paths.insert(i.path);
966 }
967
968 for (auto & i : infos) {
969 auto referrer = queryValidPathId(i.path);
970 for (auto & j : i.references)
971 addReference(referrer, queryValidPathId(j));
972 }
973
974 /* Check that the derivation outputs are correct. We can't do
975 this in addValidPath() above, because the references might
976 not be valid yet. */
977 for (const auto& i : infos)
978 if (isDerivation(i.path)) {
979 // FIXME: inefficient; we already loaded the
980 // derivation in addValidPath().
981 Derivation drv = readDerivation(i.path);
982 checkDerivationOutputs(i.path, drv);
983 }
984
985 /* Do a topological sort of the paths. This will throw an
986 error if a cycle is detected and roll back the
987 transaction. Cycles can only occur when a derivation
988 has multiple outputs. */
989 topoSortPaths(*this, paths);
990
991 txn.commit();
992 });
993}
994
995
996/* Invalidate a path. The caller is responsible for checking that
997 there are no referrers. */
998void LocalStore::invalidatePath(const Path & path)
999{
1000 debug(std::format("invalidating path `{}'", path));
1001
1002 drvHashes.erase(path);
1003
1004 stmtInvalidatePath.use()(path).exec();
1005
1006 /* Note that the foreign key constraints on the Refs table take
1007 care of deleting the references entries for `path'. */
1008}
1009
1010
1011Path LocalStore::addToStoreFromDump(const string & dump, const string & name,
1012 bool recursive, HashType hashAlgo, bool repair)
1013{
1014 Hash h = hashString(hashAlgo, dump);
1015
1016 Path dstPath = makeFixedOutputPath(recursive, hashAlgo, h, name);
1017
1018 addTempRoot(dstPath);
1019
1020 if (repair || !isValidPath(dstPath)) {
1021
1022 /* The first check above is an optimisation to prevent
1023 unnecessary lock acquisition. */
1024
1025 PathLocks outputLock{ PathSet{dstPath} };
1026
1027 if (repair || !isValidPath(dstPath)) {
1028
1029 if (pathExists(dstPath)) deletePath(dstPath);
1030
1031 if (recursive) {
1032 StringSource source(dump);
1033 restorePath(dstPath, source);
1034 } else
1035 writeFile(dstPath, dump);
1036
1037 canonicalisePathMetaData(dstPath, -1);
1038
1039 /* Register the SHA-256 hash of the NAR serialisation of
1040 the path in the database. We may just have computed it
1041 above (if called with recursive == true and hashAlgo ==
1042 sha256); otherwise, compute it here. */
1043 HashResult hash;
1044 if (recursive) {
1045 hash.first = hashAlgo == htSHA256 ? h : hashString(htSHA256, dump);
1046 hash.second = dump.size();
1047 } else
1048 hash = hashPath(htSHA256, dstPath);
1049
1050 optimisePath(dstPath); // FIXME: combine with hashPath()
1051
1052 ValidPathInfo info;
1053 info.path = dstPath;
1054 info.hash = hash.first;
1055 info.narSize = hash.second;
1056 registerValidPath(info);
1057 }
1058
1059 outputLock.setDeletion(true);
1060 }
1061
1062 return dstPath;
1063}
1064
1065
1066Path LocalStore::addToStore(const string & name, const Path & _srcPath,
1067 bool recursive, HashType hashAlgo, PathFilter & filter, bool repair)
1068{
1069 Path srcPath(absPath(_srcPath));
1070 debug(std::format("adding `{}' to the store", srcPath));
1071
1072 /* Read the whole path into memory. This is not a very scalable
1073 method for very large paths, but `copyPath' is mainly used for
1074 small files. */
1075 StringSink sink;
1076 if (recursive)
1077 dumpPath(srcPath, sink, filter);
1078 else
1079 sink.s = readFile(srcPath);
1080
1081 return addToStoreFromDump(sink.s, name, recursive, hashAlgo, repair);
1082}
1083
1084
1085Path LocalStore::addTextToStore(const string & name, const string & s,
1086 const PathSet & references, bool repair)
1087{
1088 Path dstPath = computeStorePathForText(name, s, references);
1089
1090 addTempRoot(dstPath);
1091
1092 if (repair || !isValidPath(dstPath)) {
1093
1094 PathLocks outputLock{ PathSet{dstPath} };
1095
1096 if (repair || !isValidPath(dstPath)) {
1097
1098 if (pathExists(dstPath)) deletePath(dstPath);
1099
1100 writeFile(dstPath, s);
1101
1102 canonicalisePathMetaData(dstPath, -1);
1103
1104 HashResult hash = hashPath(htSHA256, dstPath);
1105
1106 optimisePath(dstPath);
1107
1108 ValidPathInfo info;
1109 info.path = dstPath;
1110 info.hash = hash.first;
1111 info.narSize = hash.second;
1112 info.references = references;
1113 registerValidPath(info);
1114 }
1115
1116 outputLock.setDeletion(true);
1117 }
1118
1119 return dstPath;
1120}
1121
1122
1123struct HashAndWriteSink : Sink
1124{
1125 Sink & writeSink;
1126 HashSink hashSink;
1127 HashAndWriteSink(Sink & writeSink) : writeSink(writeSink), hashSink(htSHA256)
1128 {
1129 }
1130 virtual void operator () (const unsigned char * data, size_t len)
1131 {
1132 writeSink(data, len);
1133 hashSink(data, len);
1134 }
1135 Hash currentHash()
1136 {
1137 return hashSink.currentHash().first;
1138 }
1139};
1140
1141
1142#define EXPORT_MAGIC 0x4558494e
1143
1144
1145static void checkSecrecy(const Path & path)
1146{
1147 struct stat st;
1148 if (stat(path.c_str(), &st))
1149 throw SysError(std::format("getting status of `{}'", path));
1150 if ((st.st_mode & (S_IRWXG | S_IRWXO)) != 0)
1151 throw Error(std::format("file `{}' should be secret (inaccessible to everybody else)!", path));
1152}
1153
1154
1155/* Return the authentication agent, a "guix authenticate" process started
1156 lazily. */
1157static std::shared_ptr<Agent> authenticationAgent()
1158{
1159 static std::shared_ptr<Agent> agent;
1160
1161 if (!agent) {
1162 Strings args = { "authenticate" };
1163 agent = std::make_shared<Agent>(settings.guixProgram, args);
1164 }
1165
1166 return agent;
1167}
1168
1169/* Read an integer and the byte that immediately follows it from FD. Return
1170 the integer. */
1171static int readInteger(int fd)
1172{
1173 string str;
1174
1175 while (1) {
1176 char ch;
1177 ssize_t rd = read(fd, &ch, 1);
1178 if (rd == -1) {
1179 if (errno != EINTR)
1180 throw SysError("reading an integer");
1181 } else if (rd == 0)
1182 throw EndOfFile("unexpected EOF reading an integer");
1183 else {
1184 if (isdigit(ch)) {
1185 str += ch;
1186 } else {
1187 break;
1188 }
1189 }
1190 }
1191
1192 return stoi(str);
1193}
1194
1195/* Read from FD a reply coming from 'guix authenticate'. The reply has the
1196 form "CODE LEN:STR". CODE is an integer, where zero indicates success.
1197 LEN specifies the length in bytes of the string that immediately
1198 follows. */
1199static std::string readAuthenticateReply(int fd)
1200{
1201 int code = readInteger(fd);
1202 int len = readInteger(fd);
1203
1204 string str;
1205 str.resize(len);
1206 readFull(fd, (unsigned char *) &str[0], len);
1207
1208 if (code == 0)
1209 return str;
1210 else
1211 throw AuthenticationError(str);
1212}
1213
1214/* Sign HASH with the key stored in file SECRETKEY. Return the signature as a
1215 string, or raise an exception upon error. */
1216static std::string signHash(const string &secretKey, const Hash &hash)
1217{
1218 auto agent = authenticationAgent();
1219 auto hexHash = printHash(hash);
1220
1221 writeLine(agent->toAgent.writeSide,
1222 std::format("sign {}:{} {}:{}",
1223 secretKey.size(), secretKey,
1224 hexHash.size(), hexHash));
1225
1226 return readAuthenticateReply(agent->fromAgent.readSide);
1227}
1228
1229/* Verify SIGNATURE and return the base16-encoded hash over which it was
1230 computed. */
1231static std::string verifySignature(const string &signature)
1232{
1233 auto agent = authenticationAgent();
1234
1235 writeLine(agent->toAgent.writeSide,
1236 std::format("verify {}:{}", signature.size(), signature));
1237
1238 return readAuthenticateReply(agent->fromAgent.readSide);
1239}
1240
1241void LocalStore::exportPath(const Path & path, bool sign,
1242 Sink & sink)
1243{
1244 assertStorePath(path);
1245
1246 printMsg(lvlInfo, std::format("exporting path `{}'", path));
1247
1248 if (!isValidPath(path))
1249 throw Error(std::format("path `{}' is not valid", path));
1250
1251 HashAndWriteSink hashAndWriteSink(sink);
1252
1253 dumpPath(path, hashAndWriteSink);
1254
1255 /* Refuse to export paths that have changed. This prevents
1256 filesystem corruption from spreading to other machines.
1257 Don't complain if the stored hash is zero (unknown). */
1258 Hash hash = hashAndWriteSink.currentHash();
1259 Hash storedHash = queryPathHash(path);
1260 if (hash != storedHash && storedHash != Hash(storedHash.type))
1261 throw Error(std::format("hash of path `{}' has changed from `{}' to `{}'!",
1262 path, printHash(storedHash), printHash(hash)));
1263
1264 writeInt(EXPORT_MAGIC, hashAndWriteSink);
1265
1266 writeString(path, hashAndWriteSink);
1267
1268 PathSet references;
1269 queryReferences(path, references);
1270 writeStrings(references, hashAndWriteSink);
1271
1272 Path deriver = queryDeriver(path);
1273 writeString(deriver, hashAndWriteSink);
1274
1275 if (sign) {
1276 Hash hash = hashAndWriteSink.currentHash();
1277
1278 writeInt(1, hashAndWriteSink);
1279
1280 Path secretKey = settings.nixConfDir + "/signing-key.sec";
1281 checkSecrecy(secretKey);
1282
1283 string signature = signHash(secretKey, hash);
1284
1285 writeString(signature, hashAndWriteSink);
1286
1287 } else
1288 writeInt(0, hashAndWriteSink);
1289}
1290
1291
1292struct HashAndReadSource : Source
1293{
1294 Source & readSource;
1295 HashSink hashSink;
1296 bool hashing;
1297 HashAndReadSource(Source & readSource) : readSource(readSource), hashSink(htSHA256)
1298 {
1299 hashing = true;
1300 }
1301 size_t read(unsigned char * data, size_t len)
1302 {
1303 size_t n = readSource.read(data, len);
1304 if (hashing) hashSink(data, n);
1305 return n;
1306 }
1307};
1308
1309
1310/* Create a temporary directory in the store that won't be
1311 garbage-collected. */
1312Path LocalStore::createTempDirInStore()
1313{
1314 Path tmpDir;
1315 do {
1316 /* There is a slight possibility that `tmpDir' gets deleted by
1317 the GC between createTempDir() and addTempRoot(), so repeat
1318 until `tmpDir' exists. */
1319 tmpDir = createTempDir(settings.nixStore);
1320 addTempRoot(tmpDir);
1321 } while (!pathExists(tmpDir));
1322 return tmpDir;
1323}
1324
1325
1326Path LocalStore::importPath(Source & source)
1327{
1328 HashAndReadSource hashAndReadSource(source);
1329
1330 /* We don't yet know what store path this archive contains (the
1331 store path follows the archive data proper), and besides, we
1332 don't know yet whether the signature is valid. */
1333 Path tmpDir = createTempDirInStore();
1334 AutoDelete delTmp(tmpDir);
1335 Path unpacked = tmpDir + "/unpacked";
1336
1337 restorePath(unpacked, hashAndReadSource);
1338
1339 Hash narHash = hashAndReadSource.hashSink.currentHash().first;
1340
1341 unsigned int magic = readInt(hashAndReadSource);
1342 if (magic != EXPORT_MAGIC)
1343 throw Error("normalized archive cannot be imported; wrong format");
1344
1345 /* The path being imported must at least be syntactically valid. This
1346 * doesn't guarantee that it can be constructed by some existing method,
1347 * but it at least rules out paths like "/gnu/store/nix-12982-1" or
1348 * "/gnu/store/." or "/gnu/store/..". */
1349 Path dstPath = readStorePathStrict(hashAndReadSource);
1350
1351 PathSet references = readStorePathsStrict<PathSet>(hashAndReadSource);
1352
1353 Path deriver = readString(hashAndReadSource);
1354 if (deriver != "") assertStorePathStrict(deriver);
1355
1356 Hash hash = hashAndReadSource.hashSink.finish().first;
1357 hashAndReadSource.hashing = false;
1358
1359 bool haveSignature = readInt(hashAndReadSource) == 1;
1360 string signature;
1361 if (haveSignature)
1362 signature = readString(hashAndReadSource);
1363
1364 try {
1365 /* First, check whether there is a valid, authorized, matching
1366 signature. */
1367 if (haveSignature) {
1368 string hash2 = verifySignature(signature);
1369 if (printHash(hash) != hash2)
1370 throw AuthenticationError(
1371 "signed hash doesn't match actual contents of imported "
1372 "archive; archive could be corrupt, or someone is trying "
1373 "to import a Trojan horse");
1374 }
1375 else
1376 throw AuthenticationError(std::format("imported archive of `{}' lacks a signature", dstPath));
1377 }
1378 catch (AuthenticationError &e) {
1379 /* Second, check whether 'dstPath' is content-addressed--e.g., a store
1380 item created by 'addToStore' or 'addTextToStore'. XXX: This can
1381 yield an extra 'hashFile' call and is limited to SHA256. */
1382 if (!(deriver == ""
1383 && (isContentAddressedPath(dstPath, narHash, references, true)
1384 || (isPlainFile(unpacked)
1385 && isContentAddressedPath(dstPath, hashFile(htSHA256, unpacked),
1386 references, false)))))
1387 /* Rethrow. */
1388 throw;
1389 }
1390
1391 /* Do the actual import. */
1392
1393 /* !!! way too much code duplication with addTextToStore() etc. */
1394 addTempRoot(dstPath);
1395
1396 if (!isValidPath(dstPath)) {
1397
1398 PathLocks outputLock;
1399
1400 /* Lock the output path. But don't lock if we're being called
1401 from a build hook (whose parent process already acquired a
1402 lock on this path). */
1403 Strings locksHeld = tokenizeString<Strings>(getEnv("NIX_HELD_LOCKS"));
1404 if (find(locksHeld.begin(), locksHeld.end(), dstPath) == locksHeld.end())
1405 outputLock.lockPaths(PathSet{dstPath});
1406
1407 if (!isValidPath(dstPath)) {
1408
1409 if (pathExists(dstPath)) deletePath(dstPath);
1410
1411 if (rename(unpacked.c_str(), dstPath.c_str()) == -1)
1412 throw SysError(std::format("cannot move `{}' to `{}'",
1413 unpacked, dstPath));
1414
1415 canonicalisePathMetaData(dstPath, -1);
1416
1417 /* !!! if we were clever, we could prevent the hashPath()
1418 here. */
1419 HashResult hash = hashPath(htSHA256, dstPath);
1420
1421 optimisePath(dstPath); // FIXME: combine with hashPath()
1422
1423 ValidPathInfo info;
1424 info.path = dstPath;
1425 info.hash = hash.first;
1426 info.narSize = hash.second;
1427 info.references = references;
1428 info.deriver = deriver != "" && isValidPath(deriver) ? deriver : "";
1429 registerValidPath(info);
1430 }
1431
1432 outputLock.setDeletion(true);
1433 }
1434
1435 return dstPath;
1436}
1437
1438
1439Paths LocalStore::importPaths(Source & source)
1440{
1441 Paths res;
1442 while (true) {
1443 unsigned long long n = readLongLong(source);
1444 if (n == 0) break;
1445 if (n != 1) throw Error("input doesn't look like something created by `nix-store --export'");
1446 res.push_back(importPath(source));
1447 }
1448 return res;
1449}
1450
1451
1452void LocalStore::invalidatePathChecked(const Path & path)
1453{
1454 assertStorePath(path);
1455
1456 retrySQLite<void>([&]() {
1457 SQLiteTxn txn(db);
1458
1459 if (isValidPath_(path)) {
1460 PathSet referrers; queryReferrers_(path, referrers);
1461 referrers.erase(path); /* ignore self-references */
1462 if (!referrers.empty())
1463 throw PathInUse(std::format("cannot delete path `{}' because it is in use by {}",
1464 path, showPaths(referrers)));
1465 invalidatePath(path);
1466 }
1467
1468 txn.commit();
1469 });
1470}
1471
1472
1473bool LocalStore::verifyStore(bool checkContents, bool repair)
1474{
1475 printMsg(lvlError, "reading the store...");
1476
1477 bool errors = false;
1478
1479 /* Acquire the global GC lock to prevent a garbage collection. */
1480 AutoCloseFD fdGCLock = openGCLock(ltWrite);
1481
1482 PathSet store;
1483 for (auto & i : readDirectory(settings.nixStore)) store.insert(i.name);
1484
1485 /* Check whether all valid paths actually exist. */
1486 printMsg(lvlInfo, "checking path existence...");
1487
1488 PathSet validPaths2 = queryAllValidPaths(), validPaths, done;
1489
1490 for (auto& i : validPaths2)
1491 verifyPath(i, store, done, validPaths, repair, errors);
1492
1493 /* Release the GC lock so that checking content hashes (which can
1494 take ages) doesn't block the GC or builds. */
1495 fdGCLock.close();
1496
1497 /* Optionally, check the content hashes (slow). */
1498 if (checkContents) {
1499 printMsg(lvlInfo, "checking hashes...");
1500
1501 Hash nullHash(htSHA256);
1502
1503 for (auto& i : validPaths) {
1504 try {
1505 ValidPathInfo info = queryPathInfo(i);
1506
1507 /* Check the content hash (optionally - slow). */
1508 printMsg(lvlTalkative, std::format("checking contents of `{}'", i));
1509 HashResult current = hashPath(info.hash.type, i);
1510
1511 if (info.hash != nullHash && info.hash != current.first) {
1512 printMsg(lvlError, std::format("path `{}' was modified! "
1513 "expected hash `{}', got `{}'",
1514 i, printHash(info.hash), printHash(current.first)));
1515 if (repair) repairPath(i); else errors = true;
1516 } else {
1517
1518 bool update = false;
1519
1520 /* Fill in missing hashes. */
1521 if (info.hash == nullHash) {
1522 printMsg(lvlError, std::format("fixing missing hash on `{}'", i));
1523 info.hash = current.first;
1524 update = true;
1525 }
1526
1527 /* Fill in missing narSize fields (from old stores). */
1528 if (info.narSize == 0) {
1529 printMsg(lvlError, std::format("updating size field on `{}' to {}", i, current.second));
1530 info.narSize = current.second;
1531 update = true;
1532 }
1533
1534 if (update) updatePathInfo(info);
1535
1536 }
1537
1538 } catch (Error & e) {
1539 /* It's possible that the path got GC'ed, so ignore
1540 errors on invalid paths. */
1541 if (isValidPath(i))
1542 printMsg(lvlError, std::format("error: {}", e.msg()));
1543 else
1544 printMsg(lvlError, std::format("warning: {}", e.msg()));
1545 errors = true;
1546 }
1547 }
1548 }
1549
1550 return errors;
1551}
1552
1553
1554void LocalStore::verifyPath(const Path & path, const PathSet & store,
1555 PathSet & done, PathSet & validPaths, bool repair, bool & errors)
1556{
1557 checkInterrupt();
1558
1559 if (done.find(path) != done.end()) return;
1560 done.insert(path);
1561
1562 if (!isStorePath(path)) {
1563 printMsg(lvlError, std::format("path `{}' is not in the store", path));
1564 invalidatePath(path);
1565 return;
1566 }
1567
1568 if (store.find(baseNameOf(path)) == store.end()) {
1569 /* Check any referrers first. If we can invalidate them
1570 first, then we can invalidate this path as well. */
1571 bool canInvalidate = true;
1572 PathSet referrers; queryReferrers(path, referrers);
1573 for (auto& i : referrers)
1574 if (i != path) {
1575 verifyPath(i, store, done, validPaths, repair, errors);
1576 if (validPaths.find(i) != validPaths.end())
1577 canInvalidate = false;
1578 }
1579
1580 if (canInvalidate) {
1581 printMsg(lvlError, std::format("path `{}' disappeared, removing from database...", path));
1582 invalidatePath(path);
1583 } else {
1584 printMsg(lvlError, std::format("path `{}' disappeared, but it still has valid referrers!", path));
1585 if (repair)
1586 try {
1587 repairPath(path);
1588 } catch (Error & e) {
1589 printMsg(lvlError, std::format("warning: {}", e.msg()));
1590 errors = true;
1591 }
1592 else errors = true;
1593 }
1594
1595 return;
1596 }
1597
1598 validPaths.insert(path);
1599}
1600
1601
1602bool LocalStore::pathContentsGood(const Path & path)
1603{
1604 std::map<Path, bool>::iterator i = pathContentsGoodCache.find(path);
1605 if (i != pathContentsGoodCache.end()) return i->second;
1606 printMsg(lvlInfo, std::format("checking path `{}'...", path));
1607 ValidPathInfo info = queryPathInfo(path);
1608 bool res;
1609 if (!pathExists(path))
1610 res = false;
1611 else {
1612 HashResult current = hashPath(info.hash.type, path);
1613 Hash nullHash(htSHA256);
1614 res = info.hash == nullHash || info.hash == current.first;
1615 }
1616 pathContentsGoodCache[path] = res;
1617 if (!res) printMsg(lvlError, std::format("path `{}' is corrupted or missing!", path));
1618 return res;
1619}
1620
1621
1622void LocalStore::markContentsGood(const Path & path)
1623{
1624 pathContentsGoodCache[path] = true;
1625}
1626
1627
1628void LocalStore::vacuumDB()
1629{
1630 if (sqlite3_exec(db, "vacuum;", 0, 0, 0) != SQLITE_OK)
1631 throwSQLiteError(db, "vacuuming SQLite database");
1632}
1633
1634
1635void LocalStore::createUser(const std::string & userName, uid_t userId)
1636{
1637 auto dir = settings.nixStateDir + "/profiles/per-user/" + userName;
1638
1639 auto created = createDirs(dir);
1640 if (!created.empty()) {
1641 if (chmod(dir.c_str(), 0755) == -1)
1642 throw SysError(std::format("changing permissions of directory '{}'", dir));
1643
1644 /* The following operation requires CAP_CHOWN or can be handled
1645 manually by a user with CAP_CHOWN. */
1646 if (chown(dir.c_str(), userId, -1) == -1) {
1647 rmdir(dir.c_str());
1648 string message = strerror(errno);
1649 printMsg(lvlInfo, std::format("failed to change owner of directory '{}' to {}: {}", dir, userId, message));
1650 }
1651 }
1652}
1653
1654
1655}