2 #include "local-store.hh"
5 #include "pathlocks.hh"
6 #include "worker-protocol.hh"
7 #include "derivations.hh"
14 #include <sys/types.h>
25 #if HAVE_UNSHARE && HAVE_STATVFS && HAVE_SYS_MOUNT_H
27 #include <sys/statvfs.h>
28 #include <sys/mount.h>
31 #include <sys/ioctl.h>
40 void checkStoreNotSymlink()
42 if (getEnv("NIX_IGNORE_SYMLINK_STORE") == "1") return;
43 Path path
= settings
.nixStore
;
46 if (lstat(path
.c_str(), &st
))
47 throw SysError(format("getting status of `%1%'") % path
);
48 if (S_ISLNK(st
.st_mode
))
50 "the path `%1%' is a symlink; "
51 "this is not allowed for the store and its parent directories")
58 LocalStore::LocalStore(bool reserveSpace
)
59 : didSetSubstituterEnv(false)
61 schemaPath
= settings
.nixDBPath
+ "/schema";
63 if (settings
.readOnlyMode
) {
68 /* Create missing state directories if they don't already exist. */
69 createDirs(settings
.nixStore
);
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");
82 /* Optionally, create directories and set permissions for a
83 multi-user install. */
84 if (getuid() == 0 && settings
.buildUsersGroup
!= "") {
86 Path perUserDir
= profilesDir
+ "/per-user";
87 createDirs(perUserDir
);
88 if (chmod(perUserDir
.c_str(), 0755) == -1)
89 throw SysError(format("could not set permissions on '%1%' to 755")
94 struct group
* gr
= getgrnam(settings
.buildUsersGroup
.c_str());
96 throw Error(format("the group `%1%' specified in `build-users-group' does not exist")
97 % settings
.buildUsersGroup
);
100 if (stat(settings
.nixStore
.c_str(), &st
))
101 throw SysError(format("getting attributes of path '%1%'") % settings
.nixStore
);
103 if (st
.st_uid
!= 0 || st
.st_gid
!= gr
->gr_gid
|| (st
.st_mode
& ~S_IFMT
) != perm
) {
104 if (chown(settings
.nixStore
.c_str(), 0, gr
->gr_gid
) == -1)
105 throw SysError(format("changing ownership of path '%1%'") % settings
.nixStore
);
106 if (chmod(settings
.nixStore
.c_str(), perm
) == -1)
107 throw SysError(format("changing permissions on path '%1%'") % settings
.nixStore
);
112 checkStoreNotSymlink();
114 /* We can't open a SQLite database if the disk is full. Since
115 this prevents the garbage collector from running when it's most
116 needed, we reserve some dummy space that we can free just
117 before doing a garbage collection. */
119 Path reservedPath
= settings
.nixDBPath
+ "/reserved";
122 if (stat(reservedPath
.c_str(), &st
) == -1 ||
123 st
.st_size
!= settings
.reservedSize
)
125 AutoCloseFD fd
= open(reservedPath
.c_str(), O_WRONLY
| O_CREAT
, 0600);
127 #if HAVE_POSIX_FALLOCATE
128 res
= posix_fallocate(fd
, 0, settings
.reservedSize
);
131 writeFull(fd
, string(settings
.reservedSize
, 'X'));
132 ftruncate(fd
, settings
.reservedSize
);
137 deletePath(reservedPath
);
138 } catch (SysError
& e
) { /* don't care about errors */
141 /* Acquire the big fat lock in shared mode to make sure that no
142 schema upgrade is in progress. */
144 Path globalLockPath
= settings
.nixDBPath
+ "/big-lock";
145 globalLock
= openLockFile(globalLockPath
.c_str(), true);
146 } catch (SysError
& e
) {
147 if (e
.errNo
!= EACCES
) throw;
148 settings
.readOnlyMode
= true;
153 if (!lockFile(globalLock
, ltRead
, false)) {
154 printMsg(lvlError
, "waiting for the big store lock...");
155 lockFile(globalLock
, ltRead
, true);
158 /* Check the current database schema and if necessary do an
160 int curSchema
= getSchema();
161 if (curSchema
> nixSchemaVersion
)
162 throw Error(format("current store schema is version %1%, but I only support %2%")
163 % curSchema
% nixSchemaVersion
);
165 else if (curSchema
== 0) { /* new store */
166 curSchema
= nixSchemaVersion
;
168 writeFile(schemaPath
, (format("%1%") % nixSchemaVersion
).str());
171 else if (curSchema
< nixSchemaVersion
) {
172 /* Guix always used version 7 of the schema. */
174 format("Your store database uses an implausibly old schema, version %1%.")
182 LocalStore::~LocalStore()
185 if (runningSubstituter
) {
186 RunningSubstituter
&i
= *runningSubstituter
;
200 if (fdTempRoots
!= -1) {
202 unlink(fnTempRoots
.c_str());
210 int LocalStore::getSchema()
213 if (pathExists(schemaPath
)) {
214 string s
= readFile(schemaPath
);
215 if (!string2Int(s
, curSchema
))
216 throw Error(format("`%1%' is corrupt") % schemaPath
);
222 void LocalStore::openDB(bool create
)
224 if (access(settings
.nixDBPath
.c_str(), R_OK
| W_OK
))
225 throw SysError(format("store database directory `%1%' is not writable") % settings
.nixDBPath
);
227 /* Open the store database. */
228 string dbPath
= settings
.nixDBPath
+ "/db.sqlite";
229 if (sqlite3_open_v2(dbPath
.c_str(), &db
.db
,
230 SQLITE_OPEN_READWRITE
| (create
? SQLITE_OPEN_CREATE
: 0), 0) != SQLITE_OK
)
231 throw Error(format("cannot open store database `%1%'") % dbPath
);
233 if (sqlite3_busy_timeout(db
, 60 * 60 * 1000) != SQLITE_OK
)
234 throwSQLiteError(db
, "setting timeout");
236 if (sqlite3_exec(db
, "pragma foreign_keys = 1;", 0, 0, 0) != SQLITE_OK
)
237 throwSQLiteError(db
, "enabling foreign keys");
239 /* !!! check whether sqlite has been built with foreign key
242 /* Whether SQLite should fsync(). "Normal" synchronous mode
243 should be safe enough. If the user asks for it, don't sync at
244 all. This can cause database corruption if the system
246 string syncMode
= settings
.fsyncMetadata
? "normal" : "off";
247 if (sqlite3_exec(db
, ("pragma synchronous = " + syncMode
+ ";").c_str(), 0, 0, 0) != SQLITE_OK
)
248 throwSQLiteError(db
, "setting synchronous mode");
250 /* Set the SQLite journal mode. WAL mode is fastest, so it's the
252 string mode
= settings
.useSQLiteWAL
? "wal" : "truncate";
256 stmt
.create(db
, "pragma main.journal_mode;");
257 if (sqlite3_step(stmt
) != SQLITE_ROW
)
258 throwSQLiteError(db
, "querying journal mode");
259 prevMode
= string((const char *) sqlite3_column_text(stmt
, 0));
261 if (prevMode
!= mode
&&
262 sqlite3_exec(db
, ("pragma main.journal_mode = " + mode
+ ";").c_str(), 0, 0, 0) != SQLITE_OK
)
263 throwSQLiteError(db
, "setting journal mode");
265 /* Increase the auto-checkpoint interval to 40000 pages. This
266 seems enough to ensure that instantiating the NixOS system
267 derivation is done in a single fsync(). */
268 if (mode
== "wal" && sqlite3_exec(db
, "pragma wal_autocheckpoint = 40000;", 0, 0, 0) != SQLITE_OK
)
269 throwSQLiteError(db
, "setting autocheckpoint interval");
271 /* Initialise the database schema, if necessary. */
273 const char * schema
=
274 #include "schema.sql.hh"
276 if (sqlite3_exec(db
, (const char *) schema
, 0, 0, 0) != SQLITE_OK
)
277 throwSQLiteError(db
, "initialising database schema");
280 /* Prepare SQL statements. */
281 stmtRegisterValidPath
.create(db
,
282 "insert into ValidPaths (path, hash, registrationTime, deriver, narSize) values (?, ?, ?, ?, ?);");
283 stmtUpdatePathInfo
.create(db
,
284 "update ValidPaths set narSize = ?, hash = ? where path = ?;");
285 stmtAddReference
.create(db
,
286 "insert or replace into Refs (referrer, reference) values (?, ?);");
287 stmtQueryPathInfo
.create(db
,
288 "select id, hash, registrationTime, deriver, narSize from ValidPaths where path = ?;");
289 stmtQueryReferences
.create(db
,
290 "select path from Refs join ValidPaths on reference = id where referrer = ?;");
291 stmtQueryReferrers
.create(db
,
292 "select path from Refs join ValidPaths on referrer = id where reference = (select id from ValidPaths where path = ?);");
293 stmtInvalidatePath
.create(db
,
294 "delete from ValidPaths where path = ?;");
295 stmtRegisterFailedPath
.create(db
,
296 "insert or ignore into FailedPaths (path, time) values (?, ?);");
297 stmtHasPathFailed
.create(db
,
298 "select time from FailedPaths where path = ?;");
299 stmtQueryFailedPaths
.create(db
,
300 "select path from FailedPaths;");
301 // If the path is a derivation, then clear its outputs.
302 stmtClearFailedPath
.create(db
,
303 "delete from FailedPaths where ?1 = '*' or path = ?1 "
304 "or path in (select d.path from DerivationOutputs d join ValidPaths v on d.drv = v.id where v.path = ?1);");
305 stmtAddDerivationOutput
.create(db
,
306 "insert or replace into DerivationOutputs (drv, id, path) values (?, ?, ?);");
307 stmtQueryValidDerivers
.create(db
,
308 "select v.id, v.path from DerivationOutputs d join ValidPaths v on d.drv = v.id where d.path = ?;");
309 stmtQueryDerivationOutputs
.create(db
,
310 "select id, path from DerivationOutputs where drv = ?;");
311 // Use "path >= ?" with limit 1 rather than "path like '?%'" to
312 // ensure efficient lookup.
313 stmtQueryPathFromHashPart
.create(db
,
314 "select path from ValidPaths where path >= ? limit 1;");
315 stmtQueryValidPaths
.create(db
, "select path from ValidPaths");
319 /* To improve purity, users may want to make the store a read-only
320 bind mount. So make the store writable for this process. */
321 void LocalStore::makeStoreWritable()
323 #if HAVE_UNSHARE && HAVE_STATVFS && HAVE_SYS_MOUNT_H && defined(MS_BIND) && defined(MS_REMOUNT)
324 if (getuid() != 0) return;
325 /* Check if /nix/store is on a read-only mount. */
327 if (statvfs(settings
.nixStore
.c_str(), &stat
) != 0)
328 throw SysError("getting info about the store mount point");
330 if (stat
.f_flag
& ST_RDONLY
) {
331 if (unshare(CLONE_NEWNS
) == -1)
332 throw SysError("setting up a private mount namespace");
334 if (mount(0, settings
.nixStore
.c_str(), "none", MS_REMOUNT
| MS_BIND
, 0) == -1)
335 throw SysError(format("remounting %1% writable") % settings
.nixStore
);
341 const time_t mtimeStore
= 1; /* 1 second into the epoch */
344 static void canonicaliseTimestampAndPermissions(const Path
& path
, const struct stat
& st
)
346 if (!S_ISLNK(st
.st_mode
)) {
348 /* Mask out all type related bits. */
349 mode_t mode
= st
.st_mode
& ~S_IFMT
;
351 if (mode
!= 0444 && mode
!= 0555) {
352 mode
= (st
.st_mode
& S_IFMT
)
354 | (st
.st_mode
& S_IXUSR
? 0111 : 0);
355 if (chmod(path
.c_str(), mode
) == -1)
356 throw SysError(format("changing mode of `%1%' to %2$o") % path
% mode
);
361 if (st
.st_mtime
!= mtimeStore
) {
362 struct timeval times
[2];
363 times
[0].tv_sec
= st
.st_atime
;
364 times
[0].tv_usec
= 0;
365 times
[1].tv_sec
= mtimeStore
;
366 times
[1].tv_usec
= 0;
368 if (lutimes(path
.c_str(), times
) == -1)
369 if (errno
!= ENOSYS
||
370 (!S_ISLNK(st
.st_mode
) && utimes(path
.c_str(), times
) == -1))
372 if (!S_ISLNK(st
.st_mode
) && utimes(path
.c_str(), times
) == -1)
374 throw SysError(format("changing modification time of `%1%'") % path
);
379 void canonicaliseTimestampAndPermissions(const Path
& path
)
382 if (lstat(path
.c_str(), &st
))
383 throw SysError(format("getting attributes of path `%1%'") % path
);
384 canonicaliseTimestampAndPermissions(path
, st
);
388 static void canonicalisePathMetaData_(const Path
& path
, uid_t fromUid
, InodesSeen
& inodesSeen
)
393 if (lstat(path
.c_str(), &st
))
394 throw SysError(format("getting attributes of path `%1%'") % path
);
396 /* Really make sure that the path is of a supported type. */
397 if (!(S_ISREG(st
.st_mode
) || S_ISDIR(st
.st_mode
) || S_ISLNK(st
.st_mode
)))
398 throw Error(format("file ‘%1%’ has an unsupported type") % path
);
400 /* Fail if the file is not owned by the build user. This prevents
401 us from messing up the ownership/permissions of files
402 hard-linked into the output (e.g. "ln /etc/shadow $out/foo").
403 However, ignore files that we chown'ed ourselves previously to
404 ensure that we don't fail on hard links within the same build
405 (i.e. "touch $out/foo; ln $out/foo $out/bar"). */
406 if (fromUid
!= (uid_t
) -1 && st
.st_uid
!= fromUid
) {
407 assert(!S_ISDIR(st
.st_mode
));
408 if (inodesSeen
.find(Inode(st
.st_dev
, st
.st_ino
)) == inodesSeen
.end())
409 throw BuildError(format("invalid ownership on file `%1%'") % path
);
410 mode_t mode
= st
.st_mode
& ~S_IFMT
;
411 assert(S_ISLNK(st
.st_mode
) || (st
.st_uid
== geteuid() && (mode
== 0444 || mode
== 0555) && st
.st_mtime
== mtimeStore
));
415 inodesSeen
.insert(Inode(st
.st_dev
, st
.st_ino
));
417 canonicaliseTimestampAndPermissions(path
, st
);
419 /* Change ownership to the current uid. If it's a symlink, use
420 lchown if available, otherwise don't bother. Wrong ownership
421 of a symlink doesn't matter, since the owning user can't change
422 the symlink and can't delete it because the directory is not
423 writable. The only exception is top-level paths in the
424 store (since that directory is group-writable for the build
425 users group); we check for this case below. */
426 if (st
.st_uid
!= geteuid()) {
428 if (lchown(path
.c_str(), geteuid(), getegid()) == -1)
430 if (!S_ISLNK(st
.st_mode
) &&
431 chown(path
.c_str(), geteuid(), getegid()) == -1)
433 throw SysError(format("changing owner of `%1%' to %2%")
437 if (S_ISDIR(st
.st_mode
)) {
438 DirEntries entries
= readDirectory(path
);
439 for (auto & i
: entries
)
440 canonicalisePathMetaData_(path
+ "/" + i
.name
, fromUid
, inodesSeen
);
445 void canonicalisePathMetaData(const Path
& path
, uid_t fromUid
, InodesSeen
& inodesSeen
)
447 canonicalisePathMetaData_(path
, fromUid
, inodesSeen
);
449 /* On platforms that don't have lchown(), the top-level path can't
450 be a symlink, since we can't change its ownership. */
452 if (lstat(path
.c_str(), &st
))
453 throw SysError(format("getting attributes of path `%1%'") % path
);
455 if (st
.st_uid
!= geteuid()) {
456 assert(S_ISLNK(st
.st_mode
));
457 throw Error(format("wrong ownership of top-level store path `%1%'") % path
);
462 void canonicalisePathMetaData(const Path
& path
, uid_t fromUid
)
464 InodesSeen inodesSeen
;
465 canonicalisePathMetaData(path
, fromUid
, inodesSeen
);
469 void LocalStore::checkDerivationOutputs(const Path
& drvPath
, const Derivation
& drv
)
471 string drvName
= storePathToName(drvPath
);
472 assert(isDerivation(drvName
));
473 drvName
= string(drvName
, 0, drvName
.size() - drvExtension
.size());
475 if (isFixedOutputDrv(drv
)) {
476 DerivationOutputs::const_iterator out
= drv
.outputs
.find("out");
477 if (out
== drv
.outputs
.end())
478 throw Error(format("derivation `%1%' does not have an output named `out'") % drvPath
);
480 bool recursive
; HashType ht
; Hash h
;
481 out
->second
.parseHashInfo(recursive
, ht
, h
);
482 Path outPath
= makeFixedOutputPath(recursive
, ht
, h
, drvName
);
484 StringPairs::const_iterator j
= drv
.env
.find("out");
485 if (out
->second
.path
!= outPath
|| j
== drv
.env
.end() || j
->second
!= outPath
)
486 throw Error(format("derivation `%1%' has incorrect output `%2%', should be `%3%'")
487 % drvPath
% out
->second
.path
% outPath
);
491 Derivation
drvCopy(drv
);
492 foreach (DerivationOutputs::iterator
, i
, drvCopy
.outputs
) {
494 drvCopy
.env
[i
->first
] = "";
497 Hash h
= hashDerivationModulo(*this, drvCopy
);
499 foreach (DerivationOutputs::const_iterator
, i
, drv
.outputs
) {
500 Path outPath
= makeOutputPath(i
->first
, h
, drvName
);
501 StringPairs::const_iterator j
= drv
.env
.find(i
->first
);
502 if (i
->second
.path
!= outPath
|| j
== drv
.env
.end() || j
->second
!= outPath
)
503 throw Error(format("derivation `%1%' has incorrect output `%2%', should be `%3%'")
504 % drvPath
% i
->second
.path
% outPath
);
510 uint64_t LocalStore::addValidPath(const ValidPathInfo
& info
, bool checkOutputs
)
512 stmtRegisterValidPath
.use()
514 ("sha256:" + printHash(info
.hash
))
515 (info
.registrationTime
== 0 ? time(0) : info
.registrationTime
)
516 (info
.deriver
, info
.deriver
!= "")
517 (info
.narSize
, info
.narSize
!= 0)
519 uint64_t id
= sqlite3_last_insert_rowid(db
);
521 /* If this is a derivation, then store the derivation outputs in
522 the database. This is useful for the garbage collector: it can
523 efficiently query whether a path is an output of some
525 if (isDerivation(info
.path
)) {
526 Derivation drv
= readDerivation(info
.path
);
528 /* Verify that the output paths in the derivation are correct
529 (i.e., follow the scheme for computing output paths from
530 derivations). Note that if this throws an error, then the
531 DB transaction is rolled back, so the path validity
532 registration above is undone. */
533 if (checkOutputs
) checkDerivationOutputs(info
.path
, drv
);
535 for (auto & i
: drv
.outputs
) {
536 stmtAddDerivationOutput
.use()
548 void LocalStore::addReference(uint64_t referrer
, uint64_t reference
)
550 stmtAddReference
.use()(referrer
)(reference
).exec();
554 void LocalStore::registerFailedPath(const Path
& path
)
556 retrySQLite
<void>([&]() {
557 stmtRegisterFailedPath
.use()(path
)(time(0)).step();
562 bool LocalStore::hasPathFailed(const Path
& path
)
564 return retrySQLite
<bool>([&]() {
565 return stmtHasPathFailed
.use()(path
).next();
570 PathSet
LocalStore::queryFailedPaths()
572 return retrySQLite
<PathSet
>([&]() {
573 auto useQueryFailedPaths(stmtQueryFailedPaths
.use());
576 while (useQueryFailedPaths
.next())
577 res
.insert(useQueryFailedPaths
.getStr(0));
584 void LocalStore::clearFailedPaths(const PathSet
& paths
)
586 retrySQLite
<void>([&]() {
589 for (auto & path
: paths
)
590 stmtClearFailedPath
.use()(path
).exec();
597 Hash
parseHashField(const Path
& path
, const string
& s
)
599 string::size_type colon
= s
.find(':');
600 if (colon
== string::npos
)
601 throw Error(format("corrupt hash `%1%' in valid-path entry for `%2%'")
603 HashType ht
= parseHashType(string(s
, 0, colon
));
605 throw Error(format("unknown hash type `%1%' in valid-path entry for `%2%'")
606 % string(s
, 0, colon
) % path
);
607 return parseHash(ht
, string(s
, colon
+ 1));
611 ValidPathInfo
LocalStore::queryPathInfo(const Path
& path
)
616 assertStorePath(path
);
618 return retrySQLite
<ValidPathInfo
>([&]() {
620 /* Get the path info. */
621 auto useQueryPathInfo(stmtQueryPathInfo
.use()(path
));
623 if (!useQueryPathInfo
.next())
624 throw Error(format("path `%1%' is not valid") % path
);
626 info
.id
= useQueryPathInfo
.getInt(0);
628 info
.hash
= parseHashField(path
, useQueryPathInfo
.getStr(1));
630 info
.registrationTime
= useQueryPathInfo
.getInt(2);
632 auto s
= (const char *) sqlite3_column_text(stmtQueryPathInfo
, 3);
633 if (s
) info
.deriver
= s
;
635 /* Note that narSize = NULL yields 0. */
636 info
.narSize
= useQueryPathInfo
.getInt(4);
638 /* Get the references. */
639 auto useQueryReferences(stmtQueryReferences
.use()(info
.id
));
641 while (useQueryReferences
.next())
642 info
.references
.insert(useQueryReferences
.getStr(0));
649 /* Update path info in the database. Currently only updates the
651 void LocalStore::updatePathInfo(const ValidPathInfo
& info
)
653 stmtUpdatePathInfo
.use()
654 (info
.narSize
, info
.narSize
!= 0)
655 ("sha256:" + printHash(info
.hash
))
661 uint64_t LocalStore::queryValidPathId(const Path
& path
)
663 auto use(stmtQueryPathInfo
.use()(path
));
665 throw Error(format("path ‘%1%’ is not valid") % path
);
666 return use
.getInt(0);
670 bool LocalStore::isValidPath_(const Path
& path
)
672 return stmtQueryPathInfo
.use()(path
).next();
676 bool LocalStore::isValidPath(const Path
& path
)
678 return retrySQLite
<bool>([&]() {
679 return isValidPath_(path
);
684 PathSet
LocalStore::queryValidPaths(const PathSet
& paths
)
686 return retrySQLite
<PathSet
>([&]() {
688 foreach (PathSet::const_iterator
, i
, paths
)
689 if (isValidPath_(*i
)) res
.insert(*i
);
695 PathSet
LocalStore::queryAllValidPaths()
697 return retrySQLite
<PathSet
>([&]() {
698 auto use(stmtQueryValidPaths
.use());
700 while (use
.next()) res
.insert(use
.getStr(0));
706 void LocalStore::queryReferences(const Path
& path
,
707 PathSet
& references
)
709 ValidPathInfo info
= queryPathInfo(path
);
710 references
.insert(info
.references
.begin(), info
.references
.end());
714 void LocalStore::queryReferrers_(const Path
& path
, PathSet
& referrers
)
716 auto useQueryReferrers(stmtQueryReferrers
.use()(path
));
718 while (useQueryReferrers
.next())
719 referrers
.insert(useQueryReferrers
.getStr(0));
723 void LocalStore::queryReferrers(const Path
& path
, PathSet
& referrers
)
725 assertStorePath(path
);
726 return retrySQLite
<void>([&]() {
727 queryReferrers_(path
, referrers
);
732 Path
LocalStore::queryDeriver(const Path
& path
)
734 return queryPathInfo(path
).deriver
;
738 PathSet
LocalStore::queryValidDerivers(const Path
& path
)
740 assertStorePath(path
);
742 return retrySQLite
<PathSet
>([&]() {
743 auto useQueryValidDerivers(stmtQueryValidDerivers
.use()(path
));
746 while (useQueryValidDerivers
.next())
747 derivers
.insert(useQueryValidDerivers
.getStr(1));
754 PathSet
LocalStore::queryDerivationOutputs(const Path
& path
)
756 return retrySQLite
<PathSet
>([&]() {
757 auto useQueryDerivationOutputs(stmtQueryDerivationOutputs
.use()(queryValidPathId(path
)));
760 while (useQueryDerivationOutputs
.next())
761 outputs
.insert(useQueryDerivationOutputs
.getStr(1));
768 StringSet
LocalStore::queryDerivationOutputNames(const Path
& path
)
770 return retrySQLite
<StringSet
>([&]() {
771 auto useQueryDerivationOutputs(stmtQueryDerivationOutputs
.use()(queryValidPathId(path
)));
773 StringSet outputNames
;
774 while (useQueryDerivationOutputs
.next())
775 outputNames
.insert(useQueryDerivationOutputs
.getStr(0));
782 Path
LocalStore::queryPathFromHashPart(const string
& hashPart
)
784 if (hashPart
.size() != 32) throw Error("invalid hash part");
786 Path prefix
= settings
.nixStore
+ "/" + hashPart
;
788 return retrySQLite
<Path
>([&]() -> Path
{
789 auto useQueryPathFromHashPart(stmtQueryPathFromHashPart
.use()(prefix
));
791 if (!useQueryPathFromHashPart
.next()) return "";
793 const char * s
= (const char *) sqlite3_column_text(stmtQueryPathFromHashPart
, 0);
794 return s
&& prefix
.compare(0, prefix
.size(), s
, prefix
.size()) == 0 ? s
: "";
799 void LocalStore::setSubstituterEnv()
801 if (didSetSubstituterEnv
) return;
803 /* Pass configuration options (including those overridden with
804 --option) to substituters. */
805 setenv("_NIX_OPTIONS", settings
.pack().c_str(), 1);
807 didSetSubstituterEnv
= true;
811 void LocalStore::startSubstituter(RunningSubstituter
& run
)
813 if (run
.disabled
|| run
.pid
!= -1) return;
815 debug(format("starting substituter program `%1% substitute'")
816 % settings
.guixProgram
);
818 Pipe toPipe
, fromPipe
, errorPipe
;
826 run
.pid
= startProcess([&]() {
827 if (dup2(toPipe
.readSide
, STDIN_FILENO
) == -1)
828 throw SysError("dupping stdin");
829 if (dup2(fromPipe
.writeSide
, STDOUT_FILENO
) == -1)
830 throw SysError("dupping stdout");
831 if (dup2(errorPipe
.writeSide
, STDERR_FILENO
) == -1)
832 throw SysError("dupping stderr");
833 execl(settings
.guixProgram
.c_str(), "guix", "substitute", "--query", NULL
);
834 throw SysError(format("executing `%1%'") % settings
.guixProgram
);
837 run
.to
= toPipe
.writeSide
.borrow();
838 run
.from
= run
.fromBuf
.fd
= fromPipe
.readSide
.borrow();
839 run
.error
= errorPipe
.readSide
.borrow();
841 toPipe
.readSide
.close();
842 fromPipe
.writeSide
.close();
843 errorPipe
.writeSide
.close();
845 /* The substituter may exit right away if it's disabled in any way
846 (e.g. copy-from-other-stores.pl will exit if no other stores
849 getLineFromSubstituter(run
);
850 } catch (EndOfFile
& e
) {
855 if (run
.pid
.wait(true) != 0) throw;
860 /* Read a line from the substituter's stdout, while also processing
862 string
LocalStore::getLineFromSubstituter(RunningSubstituter
& run
)
866 /* We might have stdout data left over from the last time. */
867 if (run
.fromBuf
.hasData()) goto haveData
;
874 FD_SET(run
.from
, &fds
);
875 FD_SET(run
.error
, &fds
);
877 /* Wait for data to appear on the substituter's stdout or
879 if (select(run
.from
> run
.error
? run
.from
+ 1 : run
.error
+ 1, &fds
, 0, 0, 0) == -1) {
880 if (errno
== EINTR
) continue;
881 throw SysError("waiting for input from the substituter");
884 /* Completely drain stderr before dealing with stdout. */
885 if (FD_ISSET(run
.error
, &fds
)) {
887 ssize_t n
= read(run
.error
, (unsigned char *) buf
, sizeof(buf
));
889 if (errno
== EINTR
) continue;
890 throw SysError("reading from substituter's stderr");
892 if (n
== 0) throw EndOfFile(format("`%1% substitute' died unexpectedly")
893 % settings
.guixProgram
);
896 while (((p
= err
.find('\n')) != string::npos
)
897 || ((p
= err
.find('\r')) != string::npos
)) {
898 string
thing(err
, 0, p
+ 1);
899 writeToStderr("substitute: " + thing
);
900 err
= string(err
, p
+ 1);
904 /* Read from stdout until we get a newline or the buffer is empty. */
905 else if (run
.fromBuf
.hasData() || FD_ISSET(run
.from
, &fds
)) {
911 if (!err
.empty()) printMsg(lvlError
, "substitute: " + err
);
915 } while (run
.fromBuf
.hasData());
921 template<class T
> T
LocalStore::getIntLineFromSubstituter(RunningSubstituter
& run
)
923 string s
= getLineFromSubstituter(run
);
925 if (!string2Int(s
, res
)) throw Error("integer expected from stream");
930 PathSet
LocalStore::querySubstitutablePaths(const PathSet
& paths
)
934 if (!settings
.useSubstitutes
|| paths
.empty()) return res
;
936 if (!runningSubstituter
) {
937 std::unique_ptr
<RunningSubstituter
>fresh(new RunningSubstituter
);
938 runningSubstituter
.swap(fresh
);
941 RunningSubstituter
& run
= *runningSubstituter
;
942 startSubstituter(run
);
946 foreach (PathSet::const_iterator
, j
, paths
)
947 if (res
.find(*j
) == res
.end()) { s
+= *j
; s
+= " "; }
948 writeLine(run
.to
, s
);
950 /* FIXME: we only read stderr when an error occurs, so
951 substituters should only write (short) messages to
952 stderr when they fail. I.e. they shouldn't write debug
954 Path path
= getLineFromSubstituter(run
);
955 if (path
== "") break;
964 void LocalStore::querySubstitutablePathInfos(PathSet
& paths
, SubstitutablePathInfos
& infos
)
966 if (!settings
.useSubstitutes
) return;
968 if (!runningSubstituter
) {
969 std::unique_ptr
<RunningSubstituter
>fresh(new RunningSubstituter
);
970 runningSubstituter
.swap(fresh
);
973 RunningSubstituter
& run
= *runningSubstituter
;
974 startSubstituter(run
);
975 if (run
.disabled
) return;
978 foreach (PathSet::const_iterator
, i
, paths
)
979 if (infos
.find(*i
) == infos
.end()) { s
+= *i
; s
+= " "; }
980 writeLine(run
.to
, s
);
983 Path path
= getLineFromSubstituter(run
);
984 if (path
== "") break;
985 if (paths
.find(path
) == paths
.end())
986 throw Error(format("got unexpected path `%1%' from substituter") % path
);
988 SubstitutablePathInfo
& info(infos
[path
]);
989 info
.deriver
= getLineFromSubstituter(run
);
990 if (info
.deriver
!= "") assertStorePath(info
.deriver
);
991 int nrRefs
= getIntLineFromSubstituter
<int>(run
);
993 Path p
= getLineFromSubstituter(run
);
995 info
.references
.insert(p
);
997 info
.downloadSize
= getIntLineFromSubstituter
<long long>(run
);
998 info
.narSize
= getIntLineFromSubstituter
<long long>(run
);
1003 void LocalStore::querySubstitutablePathInfos(const PathSet
& paths
,
1004 SubstitutablePathInfos
& infos
)
1006 if (!paths
.empty()) {
1007 PathSet todo
= paths
;
1008 querySubstitutablePathInfos(todo
, infos
);
1013 Hash
LocalStore::queryPathHash(const Path
& path
)
1015 return queryPathInfo(path
).hash
;
1019 void LocalStore::registerValidPath(const ValidPathInfo
& info
)
1021 ValidPathInfos infos
;
1022 infos
.push_back(info
);
1023 registerValidPaths(infos
);
1027 void LocalStore::registerValidPaths(const ValidPathInfos
& infos
)
1029 /* SQLite will fsync by default, but the new valid paths may not be fsync-ed.
1030 * So some may want to fsync them before registering the validity, at the
1031 * expense of some speed of the path registering operation. */
1032 if (settings
.syncBeforeRegistering
) sync();
1034 return retrySQLite
<void>([&]() {
1038 foreach (ValidPathInfos::const_iterator
, i
, infos
) {
1039 assert(i
->hash
.type
== htSHA256
);
1040 if (isValidPath_(i
->path
))
1043 addValidPath(*i
, false);
1044 paths
.insert(i
->path
);
1047 for (auto & i
: infos
) {
1048 auto referrer
= queryValidPathId(i
.path
);
1049 for (auto & j
: i
.references
)
1050 addReference(referrer
, queryValidPathId(j
));
1053 /* Check that the derivation outputs are correct. We can't do
1054 this in addValidPath() above, because the references might
1055 not be valid yet. */
1056 foreach (ValidPathInfos::const_iterator
, i
, infos
)
1057 if (isDerivation(i
->path
)) {
1058 // FIXME: inefficient; we already loaded the
1059 // derivation in addValidPath().
1060 Derivation drv
= readDerivation(i
->path
);
1061 checkDerivationOutputs(i
->path
, drv
);
1064 /* Do a topological sort of the paths. This will throw an
1065 error if a cycle is detected and roll back the
1066 transaction. Cycles can only occur when a derivation
1067 has multiple outputs. */
1068 topoSortPaths(*this, paths
);
1075 /* Invalidate a path. The caller is responsible for checking that
1076 there are no referrers. */
1077 void LocalStore::invalidatePath(const Path
& path
)
1079 debug(format("invalidating path `%1%'") % path
);
1081 drvHashes
.erase(path
);
1083 stmtInvalidatePath
.use()(path
).exec();
1085 /* Note that the foreign key constraints on the Refs table take
1086 care of deleting the references entries for `path'. */
1090 Path
LocalStore::addToStoreFromDump(const string
& dump
, const string
& name
,
1091 bool recursive
, HashType hashAlgo
, bool repair
)
1093 Hash h
= hashString(hashAlgo
, dump
);
1095 Path dstPath
= makeFixedOutputPath(recursive
, hashAlgo
, h
, name
);
1097 addTempRoot(dstPath
);
1099 if (repair
|| !isValidPath(dstPath
)) {
1101 /* The first check above is an optimisation to prevent
1102 unnecessary lock acquisition. */
1104 PathLocks
outputLock(singleton
<PathSet
, Path
>(dstPath
));
1106 if (repair
|| !isValidPath(dstPath
)) {
1108 if (pathExists(dstPath
)) deletePath(dstPath
);
1111 StringSource
source(dump
);
1112 restorePath(dstPath
, source
);
1114 writeFile(dstPath
, dump
);
1116 canonicalisePathMetaData(dstPath
, -1);
1118 /* Register the SHA-256 hash of the NAR serialisation of
1119 the path in the database. We may just have computed it
1120 above (if called with recursive == true and hashAlgo ==
1121 sha256); otherwise, compute it here. */
1124 hash
.first
= hashAlgo
== htSHA256
? h
: hashString(htSHA256
, dump
);
1125 hash
.second
= dump
.size();
1127 hash
= hashPath(htSHA256
, dstPath
);
1129 optimisePath(dstPath
); // FIXME: combine with hashPath()
1132 info
.path
= dstPath
;
1133 info
.hash
= hash
.first
;
1134 info
.narSize
= hash
.second
;
1135 registerValidPath(info
);
1138 outputLock
.setDeletion(true);
1145 Path
LocalStore::addToStore(const string
& name
, const Path
& _srcPath
,
1146 bool recursive
, HashType hashAlgo
, PathFilter
& filter
, bool repair
)
1148 Path
srcPath(absPath(_srcPath
));
1149 debug(format("adding `%1%' to the store") % srcPath
);
1151 /* Read the whole path into memory. This is not a very scalable
1152 method for very large paths, but `copyPath' is mainly used for
1156 dumpPath(srcPath
, sink
, filter
);
1158 sink
.s
= readFile(srcPath
);
1160 return addToStoreFromDump(sink
.s
, name
, recursive
, hashAlgo
, repair
);
1164 Path
LocalStore::addTextToStore(const string
& name
, const string
& s
,
1165 const PathSet
& references
, bool repair
)
1167 Path dstPath
= computeStorePathForText(name
, s
, references
);
1169 addTempRoot(dstPath
);
1171 if (repair
|| !isValidPath(dstPath
)) {
1173 PathLocks
outputLock(singleton
<PathSet
, Path
>(dstPath
));
1175 if (repair
|| !isValidPath(dstPath
)) {
1177 if (pathExists(dstPath
)) deletePath(dstPath
);
1179 writeFile(dstPath
, s
);
1181 canonicalisePathMetaData(dstPath
, -1);
1183 HashResult hash
= hashPath(htSHA256
, dstPath
);
1185 optimisePath(dstPath
);
1188 info
.path
= dstPath
;
1189 info
.hash
= hash
.first
;
1190 info
.narSize
= hash
.second
;
1191 info
.references
= references
;
1192 registerValidPath(info
);
1195 outputLock
.setDeletion(true);
1202 struct HashAndWriteSink
: Sink
1206 HashAndWriteSink(Sink
& writeSink
) : writeSink(writeSink
), hashSink(htSHA256
)
1209 virtual void operator () (const unsigned char * data
, size_t len
)
1211 writeSink(data
, len
);
1212 hashSink(data
, len
);
1216 return hashSink
.currentHash().first
;
1221 #define EXPORT_MAGIC 0x4558494e
1224 static void checkSecrecy(const Path
& path
)
1227 if (stat(path
.c_str(), &st
))
1228 throw SysError(format("getting status of `%1%'") % path
);
1229 if ((st
.st_mode
& (S_IRWXG
| S_IRWXO
)) != 0)
1230 throw Error(format("file `%1%' should be secret (inaccessible to everybody else)!") % path
);
1234 static std::string
runAuthenticationProgram(const Strings
& args
)
1236 Strings fullArgs
= { "authenticate" };
1237 fullArgs
.insert(fullArgs
.end(), args
.begin(), args
.end()); // append
1238 return runProgram(settings
.guixProgram
, false, fullArgs
);
1241 /* Sign HASH with the key stored in file SECRETKEY. Return the signature as a
1242 string, or raise an exception upon error. */
1243 static std::string
signHash(const string
&secretKey
, const Hash
&hash
)
1246 args
.push_back("sign");
1247 args
.push_back(secretKey
);
1248 args
.push_back(printHash(hash
));
1250 return runAuthenticationProgram(args
);
1253 /* Verify SIGNATURE and return the base16-encoded hash over which it was
1255 static std::string
verifySignature(const string
&signature
)
1257 Path tmpDir
= createTempDir("", "guix", true, true, 0700);
1258 AutoDelete
delTmp(tmpDir
);
1260 Path sigFile
= tmpDir
+ "/sig";
1261 writeFile(sigFile
, signature
);
1264 args
.push_back("verify");
1265 args
.push_back(sigFile
);
1266 return runAuthenticationProgram(args
);
1269 void LocalStore::exportPath(const Path
& path
, bool sign
,
1272 assertStorePath(path
);
1274 printMsg(lvlInfo
, format("exporting path `%1%'") % path
);
1276 if (!isValidPath(path
))
1277 throw Error(format("path `%1%' is not valid") % path
);
1279 HashAndWriteSink
hashAndWriteSink(sink
);
1281 dumpPath(path
, hashAndWriteSink
);
1283 /* Refuse to export paths that have changed. This prevents
1284 filesystem corruption from spreading to other machines.
1285 Don't complain if the stored hash is zero (unknown). */
1286 Hash hash
= hashAndWriteSink
.currentHash();
1287 Hash storedHash
= queryPathHash(path
);
1288 if (hash
!= storedHash
&& storedHash
!= Hash(storedHash
.type
))
1289 throw Error(format("hash of path `%1%' has changed from `%2%' to `%3%'!") % path
1290 % printHash(storedHash
) % printHash(hash
));
1292 writeInt(EXPORT_MAGIC
, hashAndWriteSink
);
1294 writeString(path
, hashAndWriteSink
);
1297 queryReferences(path
, references
);
1298 writeStrings(references
, hashAndWriteSink
);
1300 Path deriver
= queryDeriver(path
);
1301 writeString(deriver
, hashAndWriteSink
);
1304 Hash hash
= hashAndWriteSink
.currentHash();
1306 writeInt(1, hashAndWriteSink
);
1308 Path secretKey
= settings
.nixConfDir
+ "/signing-key.sec";
1309 checkSecrecy(secretKey
);
1311 string signature
= signHash(secretKey
, hash
);
1313 writeString(signature
, hashAndWriteSink
);
1316 writeInt(0, hashAndWriteSink
);
1320 struct HashAndReadSource
: Source
1322 Source
& readSource
;
1325 HashAndReadSource(Source
& readSource
) : readSource(readSource
), hashSink(htSHA256
)
1329 size_t read(unsigned char * data
, size_t len
)
1331 size_t n
= readSource
.read(data
, len
);
1332 if (hashing
) hashSink(data
, n
);
1338 /* Create a temporary directory in the store that won't be
1339 garbage-collected. */
1340 Path
LocalStore::createTempDirInStore()
1344 /* There is a slight possibility that `tmpDir' gets deleted by
1345 the GC between createTempDir() and addTempRoot(), so repeat
1346 until `tmpDir' exists. */
1347 tmpDir
= createTempDir(settings
.nixStore
);
1348 addTempRoot(tmpDir
);
1349 } while (!pathExists(tmpDir
));
1354 Path
LocalStore::importPath(bool requireSignature
, Source
& source
)
1356 HashAndReadSource
hashAndReadSource(source
);
1358 /* We don't yet know what store path this archive contains (the
1359 store path follows the archive data proper), and besides, we
1360 don't know yet whether the signature is valid. */
1361 Path tmpDir
= createTempDirInStore();
1362 AutoDelete
delTmp(tmpDir
);
1363 Path unpacked
= tmpDir
+ "/unpacked";
1365 restorePath(unpacked
, hashAndReadSource
);
1367 unsigned int magic
= readInt(hashAndReadSource
);
1368 if (magic
!= EXPORT_MAGIC
)
1369 throw Error("normalized archive cannot be imported; wrong format");
1371 Path dstPath
= readStorePath(hashAndReadSource
);
1373 PathSet references
= readStorePaths
<PathSet
>(hashAndReadSource
);
1375 Path deriver
= readString(hashAndReadSource
);
1376 if (deriver
!= "") assertStorePath(deriver
);
1378 Hash hash
= hashAndReadSource
.hashSink
.finish().first
;
1379 hashAndReadSource
.hashing
= false;
1381 bool haveSignature
= readInt(hashAndReadSource
) == 1;
1383 if (requireSignature
&& !haveSignature
)
1384 throw Error(format("imported archive of `%1%' lacks a signature") % dstPath
);
1386 if (haveSignature
) {
1387 string signature
= readString(hashAndReadSource
);
1389 if (requireSignature
) {
1390 string hash2
= verifySignature(signature
);
1392 /* Note: runProgram() throws an exception if the signature
1395 if (printHash(hash
) != hash2
)
1397 "signed hash doesn't match actual contents of imported "
1398 "archive; archive could be corrupt, or someone is trying "
1399 "to import a Trojan horse");
1403 /* Do the actual import. */
1405 /* !!! way too much code duplication with addTextToStore() etc. */
1406 addTempRoot(dstPath
);
1408 if (!isValidPath(dstPath
)) {
1410 PathLocks outputLock
;
1412 /* Lock the output path. But don't lock if we're being called
1413 from a build hook (whose parent process already acquired a
1414 lock on this path). */
1415 Strings locksHeld
= tokenizeString
<Strings
>(getEnv("NIX_HELD_LOCKS"));
1416 if (find(locksHeld
.begin(), locksHeld
.end(), dstPath
) == locksHeld
.end())
1417 outputLock
.lockPaths(singleton
<PathSet
, Path
>(dstPath
));
1419 if (!isValidPath(dstPath
)) {
1421 if (pathExists(dstPath
)) deletePath(dstPath
);
1423 if (rename(unpacked
.c_str(), dstPath
.c_str()) == -1)
1424 throw SysError(format("cannot move `%1%' to `%2%'")
1425 % unpacked
% dstPath
);
1427 canonicalisePathMetaData(dstPath
, -1);
1429 /* !!! if we were clever, we could prevent the hashPath()
1431 HashResult hash
= hashPath(htSHA256
, dstPath
);
1433 optimisePath(dstPath
); // FIXME: combine with hashPath()
1436 info
.path
= dstPath
;
1437 info
.hash
= hash
.first
;
1438 info
.narSize
= hash
.second
;
1439 info
.references
= references
;
1440 info
.deriver
= deriver
!= "" && isValidPath(deriver
) ? deriver
: "";
1441 registerValidPath(info
);
1444 outputLock
.setDeletion(true);
1451 Paths
LocalStore::importPaths(bool requireSignature
, Source
& source
)
1455 unsigned long long n
= readLongLong(source
);
1457 if (n
!= 1) throw Error("input doesn't look like something created by `nix-store --export'");
1458 res
.push_back(importPath(requireSignature
, source
));
1464 void LocalStore::invalidatePathChecked(const Path
& path
)
1466 assertStorePath(path
);
1468 retrySQLite
<void>([&]() {
1471 if (isValidPath_(path
)) {
1472 PathSet referrers
; queryReferrers_(path
, referrers
);
1473 referrers
.erase(path
); /* ignore self-references */
1474 if (!referrers
.empty())
1475 throw PathInUse(format("cannot delete path `%1%' because it is in use by %2%")
1476 % path
% showPaths(referrers
));
1477 invalidatePath(path
);
1485 bool LocalStore::verifyStore(bool checkContents
, bool repair
)
1487 printMsg(lvlError
, format("reading the store..."));
1489 bool errors
= false;
1491 /* Acquire the global GC lock to prevent a garbage collection. */
1492 AutoCloseFD fdGCLock
= openGCLock(ltWrite
);
1495 for (auto & i
: readDirectory(settings
.nixStore
)) store
.insert(i
.name
);
1497 /* Check whether all valid paths actually exist. */
1498 printMsg(lvlInfo
, "checking path existence...");
1500 PathSet validPaths2
= queryAllValidPaths(), validPaths
, done
;
1502 foreach (PathSet::iterator
, i
, validPaths2
)
1503 verifyPath(*i
, store
, done
, validPaths
, repair
, errors
);
1505 /* Release the GC lock so that checking content hashes (which can
1506 take ages) doesn't block the GC or builds. */
1509 /* Optionally, check the content hashes (slow). */
1510 if (checkContents
) {
1511 printMsg(lvlInfo
, "checking hashes...");
1513 Hash
nullHash(htSHA256
);
1515 foreach (PathSet::iterator
, i
, validPaths
) {
1517 ValidPathInfo info
= queryPathInfo(*i
);
1519 /* Check the content hash (optionally - slow). */
1520 printMsg(lvlTalkative
, format("checking contents of `%1%'") % *i
);
1521 HashResult current
= hashPath(info
.hash
.type
, *i
);
1523 if (info
.hash
!= nullHash
&& info
.hash
!= current
.first
) {
1524 printMsg(lvlError
, format("path `%1%' was modified! "
1525 "expected hash `%2%', got `%3%'")
1526 % *i
% printHash(info
.hash
) % printHash(current
.first
));
1527 if (repair
) repairPath(*i
); else errors
= true;
1530 bool update
= false;
1532 /* Fill in missing hashes. */
1533 if (info
.hash
== nullHash
) {
1534 printMsg(lvlError
, format("fixing missing hash on `%1%'") % *i
);
1535 info
.hash
= current
.first
;
1539 /* Fill in missing narSize fields (from old stores). */
1540 if (info
.narSize
== 0) {
1541 printMsg(lvlError
, format("updating size field on `%1%' to %2%") % *i
% current
.second
);
1542 info
.narSize
= current
.second
;
1546 if (update
) updatePathInfo(info
);
1550 } catch (Error
& e
) {
1551 /* It's possible that the path got GC'ed, so ignore
1552 errors on invalid paths. */
1553 if (isValidPath(*i
))
1554 printMsg(lvlError
, format("error: %1%") % e
.msg());
1556 printMsg(lvlError
, format("warning: %1%") % e
.msg());
1566 void LocalStore::verifyPath(const Path
& path
, const PathSet
& store
,
1567 PathSet
& done
, PathSet
& validPaths
, bool repair
, bool & errors
)
1571 if (done
.find(path
) != done
.end()) return;
1574 if (!isStorePath(path
)) {
1575 printMsg(lvlError
, format("path `%1%' is not in the store") % path
);
1576 invalidatePath(path
);
1580 if (store
.find(baseNameOf(path
)) == store
.end()) {
1581 /* Check any referrers first. If we can invalidate them
1582 first, then we can invalidate this path as well. */
1583 bool canInvalidate
= true;
1584 PathSet referrers
; queryReferrers(path
, referrers
);
1585 foreach (PathSet::iterator
, i
, referrers
)
1587 verifyPath(*i
, store
, done
, validPaths
, repair
, errors
);
1588 if (validPaths
.find(*i
) != validPaths
.end())
1589 canInvalidate
= false;
1592 if (canInvalidate
) {
1593 printMsg(lvlError
, format("path `%1%' disappeared, removing from database...") % path
);
1594 invalidatePath(path
);
1596 printMsg(lvlError
, format("path `%1%' disappeared, but it still has valid referrers!") % path
);
1600 } catch (Error
& e
) {
1601 printMsg(lvlError
, format("warning: %1%") % e
.msg());
1610 validPaths
.insert(path
);
1614 bool LocalStore::pathContentsGood(const Path
& path
)
1616 std::map
<Path
, bool>::iterator i
= pathContentsGoodCache
.find(path
);
1617 if (i
!= pathContentsGoodCache
.end()) return i
->second
;
1618 printMsg(lvlInfo
, format("checking path `%1%'...") % path
);
1619 ValidPathInfo info
= queryPathInfo(path
);
1621 if (!pathExists(path
))
1624 HashResult current
= hashPath(info
.hash
.type
, path
);
1625 Hash
nullHash(htSHA256
);
1626 res
= info
.hash
== nullHash
|| info
.hash
== current
.first
;
1628 pathContentsGoodCache
[path
] = res
;
1629 if (!res
) printMsg(lvlError
, format("path `%1%' is corrupted or missing!") % path
);
1634 void LocalStore::markContentsGood(const Path
& path
)
1636 pathContentsGoodCache
[path
] = true;
1640 void LocalStore::vacuumDB()
1642 if (sqlite3_exec(db
, "vacuum;", 0, 0, 0) != SQLITE_OK
)
1643 throwSQLiteError(db
, "vacuuming SQLite database");
1647 void LocalStore::createUser(const std::string
& userName
, uid_t userId
)
1649 auto dir
= settings
.nixStateDir
+ "/profiles/per-user/" + userName
;
1652 if (chmod(dir
.c_str(), 0755) == -1)
1653 throw SysError(format("changing permissions of directory '%s'") % dir
);
1654 if (chown(dir
.c_str(), userId
, -1) == -1)
1655 throw SysError(format("changing owner of directory '%s'") % dir
);