LTP GCOV extension - code coverage report
Current view: directory - src/libstore - local-store.cc
Test: app.info
Date: 2008-11-20 Instrumented lines: 536
Code covered: 79.7 % Executed lines: 427

       1                 : #include "config.h"
       2                 : #include "local-store.hh"
       3                 : #include "globals.hh"
       4                 : #include "archive.hh"
       5                 : #include "pathlocks.hh"
       6                 : #include "aterm.hh"
       7                 : #include "derivations-ast.hh"
       8                 : #include "worker-protocol.hh"
       9                 :     
      10                 : #include <iostream>
      11                 : #include <algorithm>
      12                 : 
      13                 : #include <sys/types.h>
      14                 : #include <sys/stat.h>
      15                 : #include <unistd.h>
      16                 : #include <utime.h>
      17                 : #include <fcntl.h>
      18                 : #include <errno.h>
      19                 : 
      20                 : 
      21                 : namespace nix {
      22                 : 
      23                 :     
      24             363 : void checkStoreNotSymlink()
      25                 : {
      26             363 :     if (getEnv("NIX_IGNORE_SYMLINK_STORE") == "1") return;
      27             362 :     Path path = nixStore;
      28                 :     struct stat st;
      29             362 :     while (path != "/") {
      30            2172 :         if (lstat(path.c_str(), &st))
      31               0 :             throw SysError(format("getting status of `%1%'") % path);
      32            2172 :         if (S_ISLNK(st.st_mode))
      33                 :             throw Error(format(
      34                 :                 "the path `%1%' is a symlink; "
      35                 :                 "this is not allowed for the Nix store and its parent directories")
      36               0 :                 % path);
      37            2172 :         path = dirOf(path);
      38             362 :     }
      39                 : }
      40                 : 
      41                 : 
      42             420 : LocalStore::LocalStore()
      43                 : {
      44             420 :     substitutablePathsLoaded = false;
      45                 :     
      46             420 :     schemaPath = nixDBPath + "/schema";
      47                 :     
      48             420 :     if (readOnlyMode) return;
      49                 : 
      50             363 :     checkStoreNotSymlink();
      51                 : 
      52                 :     try {
      53             363 :         Path globalLockPath = nixDBPath + "/big-lock";
      54             363 :         globalLock = openLockFile(globalLockPath.c_str(), true);
      55               0 :     } catch (SysError & e) {
      56               0 :         if (e.errNo != EACCES) throw;
      57               0 :         readOnlyMode = true;
      58               0 :         return;
      59                 :     }
      60                 :     
      61             726 :     if (!lockFile(globalLock, ltRead, false)) {
      62               0 :         printMsg(lvlError, "waiting for the big Nix store lock...");
      63               0 :         lockFile(globalLock, ltRead, true);
      64                 :     }
      65                 : 
      66             363 :     createDirs(nixDBPath + "/info");
      67             363 :     createDirs(nixDBPath + "/referrer");
      68                 : 
      69             363 :     int curSchema = getSchema();
      70             363 :     if (curSchema > nixSchemaVersion)
      71                 :         throw Error(format("current Nix store schema is version %1%, but I only support %2%")
      72               0 :             % curSchema % nixSchemaVersion);
      73             363 :     if (curSchema == 0) { /* new store */
      74              15 :         curSchema = nixSchemaVersion; 
      75              15 :         writeFile(schemaPath, (format("%1%") % nixSchemaVersion).str());
      76                 :     }
      77             363 :     if (curSchema == 1) throw Error("your Nix store is no longer supported");
      78             363 :     if (curSchema < nixSchemaVersion) upgradeStore12();
      79               0 : }
      80                 : 
      81                 : 
      82             420 : LocalStore::~LocalStore()
      83                 : {
      84                 :     try {
      85             420 :         flushDelayedUpdates();
      86                 : 
      87             557 :         foreach (RunningSubstituters::iterator, i, runningSubstituters) {
      88             137 :             i->second.toBuf.reset();
      89             137 :             i->second.to.reset();
      90             137 :             i->second.pid.wait(true);
      91                 :         }
      92                 :                 
      93               0 :     } catch (...) {
      94               0 :         ignoreException();
      95                 :     }
      96             420 : }
      97                 : 
      98                 : 
      99             363 : int LocalStore::getSchema()
     100                 : {
     101             363 :     int curSchema = 0;
     102             363 :     if (pathExists(schemaPath)) {
     103             348 :         string s = readFile(schemaPath);
     104             348 :         if (!string2Int(s, curSchema))
     105               0 :             throw Error(format("`%1%' is corrupt") % schemaPath);
     106                 :     }
     107             363 :     return curSchema;
     108                 : }
     109                 : 
     110                 : 
     111              72 : void copyPath(const Path & src, const Path & dst, PathFilter & filter)
     112                 : {
     113              72 :     debug(format("copying `%1%' to `%2%'") % src % dst);
     114                 : 
     115                 :     /* Dump an archive of the path `src' into a string buffer, then
     116                 :        restore the archive to `dst'.  This is not a very good method
     117                 :        for very large paths, but `copyPath' is mainly used for small
     118                 :        files. */ 
     119                 : 
     120              72 :     StringSink sink;
     121              72 :     dumpPath(src, sink, filter);
     122                 : 
     123              72 :     StringSource source(sink.s);
     124              72 :     restorePath(dst, source);
     125              72 : }
     126                 : 
     127                 : 
     128            5593 : void canonicalisePathMetaData(const Path & path, bool recurse)
     129                 : {
     130            5593 :     checkInterrupt();
     131                 : 
     132                 :     struct stat st;
     133            5593 :     if (lstat(path.c_str(), &st))
     134               0 :         throw SysError(format("getting attributes of path `%1%'") % path);
     135                 : 
     136                 :     /* Change ownership to the current uid.  If it's a symlink, use
     137                 :        lchown if available, otherwise don't bother.  Wrong ownership
     138                 :        of a symlink doesn't matter, since the owning user can't change
     139                 :        the symlink and can't delete it because the directory is not
     140                 :        writable.  The only exception is top-level paths in the Nix
     141                 :        store (since that directory is group-writable for the Nix build
     142                 :        users group); we check for this case below. */
     143            5593 :     if (st.st_uid != geteuid()) {
     144                 : #if HAVE_LCHOWN
     145               0 :         if (lchown(path.c_str(), geteuid(), (gid_t) -1) == -1)
     146                 : #else
     147                 :         if (!S_ISLNK(st.st_mode) &&
     148                 :             chown(path.c_str(), geteuid(), (gid_t) -1) == -1)
     149                 : #endif
     150                 :             throw SysError(format("changing owner of `%1%' to %2%")
     151               0 :                 % path % geteuid());
     152                 :     }
     153                 :     
     154            5593 :     if (!S_ISLNK(st.st_mode)) {
     155                 : 
     156                 :         /* Mask out all type related bits. */
     157            5490 :         mode_t mode = st.st_mode & ~S_IFMT;
     158                 :         
     159            5490 :         if (mode != 0444 && mode != 0555) {
     160                 :             mode = (st.st_mode & S_IFMT)
     161                 :                  | 0444
     162            2990 :                  | (st.st_mode & S_IXUSR ? 0111 : 0);
     163            2990 :             if (chmod(path.c_str(), mode) == -1)
     164               0 :                 throw SysError(format("changing mode of `%1%' to %2$o") % path % mode);
     165                 :         }
     166                 : 
     167            5490 :         if (st.st_mtime != 0) {
     168                 :             struct utimbuf utimbuf;
     169            2990 :             utimbuf.actime = st.st_atime;
     170            2990 :             utimbuf.modtime = 0;
     171            2990 :             if (utime(path.c_str(), &utimbuf) == -1) 
     172               0 :                 throw SysError(format("changing modification time of `%1%'") % path);
     173                 :         }
     174                 : 
     175                 :     }
     176                 : 
     177            5593 :     if (recurse && S_ISDIR(st.st_mode)) {
     178             138 :         Strings names = readDirectory(path);
     179             358 :         for (Strings::iterator i = names.begin(); i != names.end(); ++i)
     180             358 :             canonicalisePathMetaData(path + "/" + *i, true);
     181                 :     }
     182            5593 : }
     183                 : 
     184                 : 
     185            5373 : void canonicalisePathMetaData(const Path & path)
     186                 : {
     187            5373 :     canonicalisePathMetaData(path, true);
     188                 : 
     189                 :     /* On platforms that don't have lchown(), the top-level path can't
     190                 :        be a symlink, since we can't change its ownership. */
     191                 :     struct stat st;
     192            5373 :     if (lstat(path.c_str(), &st))
     193               0 :         throw SysError(format("getting attributes of path `%1%'") % path);
     194                 : 
     195            5373 :     if (st.st_uid != geteuid()) {
     196               0 :         assert(S_ISLNK(st.st_mode));
     197               0 :         throw Error(format("wrong ownership of top-level store path `%1%'") % path);
     198                 :     }
     199            5373 : }
     200                 : 
     201                 : 
     202           53529 : static Path infoFileFor(const Path & path)
     203                 : {
     204           53529 :     string baseName = baseNameOf(path);
     205           53529 :     return (format("%1%/info/%2%") % nixDBPath % baseName).str();
     206                 : }
     207                 : 
     208                 : 
     209           26432 : static Path referrersFileFor(const Path & path)
     210                 : {
     211           26432 :     string baseName = baseNameOf(path);
     212           26432 :     return (format("%1%/referrer/%2%") % nixDBPath % baseName).str();
     213                 : }
     214                 : 
     215                 : 
     216            5386 : static Path tmpFileForAtomicUpdate(const Path & path)
     217                 : {
     218            5386 :     return (format("%1%/.%2%.%3%") % dirOf(path) % getpid() % baseNameOf(path)).str();
     219                 : }
     220                 : 
     221                 : 
     222            5352 : static void appendReferrer(const Path & from, const Path & to, bool lock)
     223                 : {
     224            5352 :     Path referrersFile = referrersFileFor(from);
     225                 :     
     226            5352 :     PathLocks referrersLock;
     227            5352 :     if (lock) {
     228               0 :         referrersLock.lockPaths(singleton<PathSet, Path>(referrersFile));
     229               0 :         referrersLock.setDeletion(true);
     230                 :     }
     231                 : 
     232            5352 :     AutoCloseFD fd = open(referrersFile.c_str(), O_WRONLY | O_APPEND | O_CREAT, 0666);
     233            5352 :     if (fd == -1) throw SysError(format("opening file `%1%'") % referrersFile);
     234                 :     
     235            5352 :     string s = " " + to;
     236            5352 :     writeFull(fd, (const unsigned char *) s.c_str(), s.size());
     237            5352 : }
     238                 : 
     239                 : 
     240                 : /* Atomically update the referrers file.  If `purge' is true, the set
     241                 :    of referrers is set to `referrers'.  Otherwise, the current set of
     242                 :    referrers is purged of invalid paths. */
     243              16 : void LocalStore::rewriteReferrers(const Path & path, bool purge, PathSet referrers)
     244                 : {
     245              16 :     Path referrersFile = referrersFileFor(path);
     246                 :     
     247              16 :     PathLocks referrersLock(singleton<PathSet, Path>(referrersFile));
     248              16 :     referrersLock.setDeletion(true);
     249                 : 
     250              16 :     if (purge)
     251                 :         /* queryReferrers() purges invalid paths, so that's all we
     252                 :            need. */
     253              16 :         queryReferrers(path, referrers);
     254                 : 
     255              16 :     Path tmpFile = tmpFileForAtomicUpdate(referrersFile);
     256                 :     
     257              16 :     AutoCloseFD fd = open(tmpFile.c_str(), O_WRONLY | O_TRUNC | O_CREAT, 0666);
     258              16 :     if (fd == -1) throw SysError(format("opening file `%1%'") % referrersFile);
     259                 :     
     260              16 :     string s;
     261              37 :     foreach (PathSet::const_iterator, i, referrers) {
     262              21 :         s += " "; s += *i;
     263                 :     }
     264                 :     
     265              16 :     writeFull(fd, (const unsigned char *) s.c_str(), s.size());
     266                 : 
     267              16 :     fd.close(); /* for Windows; can't rename open file */
     268                 : 
     269              16 :     if (rename(tmpFile.c_str(), referrersFile.c_str()) == -1)
     270               0 :         throw SysError(format("cannot rename `%1%' to `%2%'") % tmpFile % referrersFile);
     271              16 : }
     272                 : 
     273                 : 
     274             420 : void LocalStore::flushDelayedUpdates()
     275                 : {
     276             872 :     foreach (PathSet::iterator, i, delayedUpdates) {
     277              16 :         rewriteReferrers(*i, true, PathSet());
     278                 :     }
     279             420 :     delayedUpdates.clear();
     280             420 : }
     281                 : 
     282                 : 
     283                 : void LocalStore::registerValidPath(const Path & path,
     284                 :     const Hash & hash, const PathSet & references,
     285             370 :     const Path & deriver)
     286                 : {
     287             370 :     ValidPathInfo info;
     288             370 :     info.path = path;
     289             370 :     info.hash = hash;
     290             370 :     info.references = references;
     291             370 :     info.deriver = deriver;
     292             371 :     registerValidPath(info);
     293             369 : }
     294                 : 
     295                 : 
     296            5371 : void LocalStore::registerValidPath(const ValidPathInfo & info, bool ignoreValidity)
     297                 : {
     298            5371 :     Path infoFile = infoFileFor(info.path);
     299                 : 
     300            5371 :     ValidPathInfo oldInfo;
     301            5371 :     if (pathExists(infoFile)) oldInfo = queryPathInfo(info.path);
     302                 : 
     303                 :     /* Note that it's possible for infoFile to already exist. */
     304                 : 
     305                 :     /* Acquire a lock on each referrer file.  This prevents those
     306                 :        paths from being invalidated.  (It would be a violation of the
     307                 :        store invariants if we registered info.path as valid while some
     308                 :        of its references are invalid.)  NB: there can be no deadlock
     309                 :        here since we're acquiring the locks in sorted order. */
     310            5371 :     PathSet lockNames;
     311           15724 :     foreach (PathSet::const_iterator, i, info.references)
     312           10353 :         if (*i != info.path) lockNames.insert(referrersFileFor(*i));
     313            5371 :     PathLocks referrerLocks(lockNames);
     314            5371 :     referrerLocks.setDeletion(true);
     315                 :         
     316            5371 :     string refs;
     317           15722 :     foreach (PathSet::const_iterator, i, info.references) {
     318           10352 :         if (!refs.empty()) refs += " ";
     319           10352 :         refs += *i;
     320                 : 
     321           10352 :         if (!ignoreValidity && *i != info.path && !isValidPath(*i))
     322                 :             throw Error(format("cannot register `%1%' as valid, because its reference `%2%' isn't valid")
     323               1 :                 % info.path % *i);
     324                 : 
     325                 :         /* Update the referrer mapping for *i.  This must be done
     326                 :            before the info file is written to maintain the invariant
     327                 :            that if `path' is a valid path, then all its references
     328                 :            have referrer mappings back to `path'.  A " " is prefixed
     329                 :            to separate it from the previous entry.  It's not suffixed
     330                 :            to deal with interrupted partial writes to this file. */
     331           10351 :         if (oldInfo.references.find(*i) == oldInfo.references.end())
     332            5352 :             appendReferrer(*i, info.path, false);
     333                 :     }
     334                 : 
     335                 :     string s = (format(
     336                 :         "Hash: sha256:%1%\n"
     337                 :         "References: %2%\n"
     338                 :         "Deriver: %3%\n"
     339                 :         "Registered-At: %4%\n")
     340                 :         % printHash(info.hash) % refs % info.deriver %
     341            5370 :         (oldInfo.registrationTime ? oldInfo.registrationTime : time(0))).str();
     342                 : 
     343                 :     /* Atomically rewrite the info file. */
     344            5370 :     Path tmpFile = tmpFileForAtomicUpdate(infoFile);
     345            5370 :     writeFile(tmpFile, s);
     346            5370 :     if (rename(tmpFile.c_str(), infoFile.c_str()) == -1)
     347               0 :         throw SysError(format("cannot rename `%1%' to `%2%'") % tmpFile % infoFile);
     348                 : 
     349            5371 :     pathInfoCache[info.path] = info;
     350            5370 : }
     351                 : 
     352                 : 
     353            5480 : Hash parseHashField(const Path & path, const string & s)
     354                 : {
     355            5480 :     string::size_type colon = s.find(':');
     356            5480 :     if (colon == string::npos)
     357                 :         throw Error(format("corrupt hash `%1%' in valid-path entry for `%2%'")
     358               0 :             % s % path);
     359            5480 :     HashType ht = parseHashType(string(s, 0, colon));
     360            5480 :     if (ht == htUnknown)
     361                 :         throw Error(format("unknown hash type `%1%' in valid-path entry for `%2%'")
     362               0 :             % string(s, 0, colon) % path);
     363            5480 :     return parseHash(ht, string(s, colon + 1));
     364                 : }
     365                 : 
     366                 : 
     367            5635 : ValidPathInfo LocalStore::queryPathInfo(const Path & path)
     368                 : {
     369            5635 :     ValidPathInfo res;
     370            5635 :     res.path = path;
     371                 : 
     372            5635 :     assertStorePath(path);
     373                 : 
     374            5635 :     std::map<Path, ValidPathInfo>::iterator lookup = pathInfoCache.find(path);
     375            5635 :     if (lookup != pathInfoCache.end()) return lookup->second;
     376                 :     
     377                 :     /* Read the info file. */
     378            5480 :     Path infoFile = infoFileFor(path);
     379            5480 :     if (!pathExists(infoFile))
     380               0 :         throw Error(format("path `%1%' is not valid") % path);
     381            5480 :     string info = readFile(infoFile);
     382                 : 
     383                 :     /* Parse it. */
     384            5480 :     Strings lines = tokenizeString(info, "\n");
     385                 : 
     386           32880 :     for (Strings::iterator i = lines.begin(); i != lines.end(); ++i) {
     387           21920 :         unsigned int p = i->find(':');
     388           21920 :         if (p == string::npos) continue; /* bad line */
     389           21920 :         string name(*i, 0, p);
     390           21920 :         string value(*i, p + 2);
     391           21920 :         if (name == "References") {
     392            5480 :             Strings refs = tokenizeString(value, " ");
     393           10960 :             res.references = PathSet(refs.begin(), refs.end());
     394           16440 :         } else if (name == "Deriver") {
     395            5480 :             res.deriver = value;
     396           10960 :         } else if (name == "Hash") {
     397            5480 :             res.hash = parseHashField(path, value);
     398            5480 :         } else if (name == "Registered-At") {
     399            5480 :             int n = 0;
     400            5480 :             string2Int(value, n);
     401            5480 :             res.registrationTime = n;
     402                 :         }
     403                 :     }
     404                 : 
     405            5480 :     return pathInfoCache[path] = res;
     406                 : }
     407                 : 
     408                 : 
     409           37332 : bool LocalStore::isValidPath(const Path & path)
     410                 : {
     411                 :     /* Files in the info directory starting with a `.' are temporary
     412                 :        files. */
     413           37332 :     if (baseNameOf(path).at(0) == '.') return false;
     414           37332 :     return pathExists(infoFileFor(path));
     415                 : }
     416                 : 
     417                 : 
     418               1 : PathSet LocalStore::queryValidPaths()
     419                 : {
     420               1 :     PathSet paths;
     421               1 :     Strings entries = readDirectory(nixDBPath + "/info");
     422              13 :     for (Strings::iterator i = entries.begin(); i != entries.end(); ++i)
     423              12 :         if (i->at(0) != '.') paths.insert(nixStore + "/" + *i);
     424               1 :     return paths;
     425                 : }
     426                 : 
     427                 : 
     428                 : void LocalStore::queryReferences(const Path & path,
     429             429 :     PathSet & references)
     430                 : {
     431             429 :     ValidPathInfo info = queryPathInfo(path);
     432             429 :     references.insert(info.references.begin(), info.references.end());
     433             429 : }
     434                 : 
     435                 : 
     436            5378 : bool LocalStore::queryReferrersInternal(const Path & path, PathSet & referrers)
     437                 : {
     438            5378 :     bool allValid = true;
     439                 :     
     440            5378 :     if (!isValidPath(path))
     441               0 :         throw Error(format("path `%1%' is not valid") % path);
     442                 : 
     443                 :     /* No locking is necessary here: updates are only done by
     444                 :        appending or by atomically replacing the file.  When appending,
     445                 :        there is a possibility that we see a partial entry, but it will
     446                 :        just be filtered out below (the partially written path will not
     447                 :        be valid, so it will be ignored). */
     448                 : 
     449            5378 :     Path referrersFile = referrersFileFor(path);
     450            5378 :     if (!pathExists(referrersFile)) return true;
     451                 :     
     452            5174 :     AutoCloseFD fd = open(referrersFile.c_str(), O_RDONLY);
     453            5174 :     if (fd == -1) throw SysError(format("opening file `%1%'") % referrersFile);
     454                 : 
     455            5174 :     Paths refs = tokenizeString(readFile(fd), " ");
     456                 : 
     457           18224 :     for (Paths::iterator i = refs.begin(); i != refs.end(); ++i)
     458                 :         /* Referrers can be invalid (see registerValidPath() for the
     459                 :            invariant), so we only return one if it is valid. */
     460            7876 :         if (isStorePath(*i) && isValidPath(*i)) referrers.insert(*i); else allValid = false;
     461                 : 
     462            5174 :     return allValid;
     463                 : }
     464                 : 
     465                 : 
     466            5371 : void LocalStore::queryReferrers(const Path & path, PathSet & referrers)
     467                 : {
     468            5371 :     queryReferrersInternal(path, referrers);
     469            5371 : }
     470                 : 
     471                 : 
     472              20 : Path LocalStore::queryDeriver(const Path & path)
     473                 : {
     474              20 :     return queryPathInfo(path).deriver;
     475                 : }
     476                 : 
     477                 : 
     478             505 : void LocalStore::startSubstituter(const Path & substituter, RunningSubstituter & run)
     479                 : {
     480             505 :     if (run.pid != -1) return;
     481                 :     
     482             137 :     debug(format("starting substituter program `%1%'") % substituter);
     483                 : 
     484             137 :     Pipe toPipe, fromPipe;
     485                 :             
     486             137 :     toPipe.create();
     487             137 :     fromPipe.create();
     488                 : 
     489             137 :     run.pid = fork();
     490                 :             
     491             274 :     switch (run.pid) {
     492                 : 
     493                 :     case -1:
     494               0 :         throw SysError("unable to fork");
     495                 : 
     496                 :     case 0: /* child */
     497                 :         try {
     498             137 :             fromPipe.readSide.close();
     499             137 :             toPipe.writeSide.close();
     500             137 :             if (dup2(toPipe.readSide, STDIN_FILENO) == -1)
     501               0 :                 throw SysError("dupping stdin");
     502             137 :             if (dup2(fromPipe.writeSide, STDOUT_FILENO) == -1)
     503               0 :                 throw SysError("dupping stdout");
     504             137 :             closeMostFDs(set<int>());
     505             137 :             execl(substituter.c_str(), substituter.c_str(), "--query", NULL);
     506               0 :             throw SysError(format("executing `%1%'") % substituter);
     507               0 :         } catch (std::exception & e) {
     508               0 :             std::cerr << "error: " << e.what() << std::endl;
     509                 :         }
     510               0 :         quickExit(1);
     511                 :     }
     512                 : 
     513                 :     /* Parent. */
     514                 :     
     515             137 :     toPipe.readSide.close();
     516             137 :     fromPipe.writeSide.close();
     517                 : 
     518             137 :     run.toBuf = boost::shared_ptr<stdio_filebuf>(new stdio_filebuf(toPipe.writeSide.borrow(), std::ios_base::out));
     519             137 :     run.to = boost::shared_ptr<std::ostream>(new std::ostream(&*run.toBuf));
     520                 : 
     521             137 :     run.fromBuf = boost::shared_ptr<stdio_filebuf>(new stdio_filebuf(fromPipe.readSide.borrow(), std::ios_base::in));
     522             137 :     run.from = boost::shared_ptr<std::istream>(new std::istream(&*run.fromBuf));
     523                 : }
     524                 : 
     525                 : 
     526             555 : template<class T> T getIntLine(std::istream & str)
     527                 : {
     528             555 :     string s;
     529                 :     T res;
     530             555 :     getline(str, s);
     531             555 :     if (!str || !string2Int(s, res)) throw Error("integer expected from stream");
     532             555 :     return res;
     533                 : }
     534                 : 
     535                 : 
     536             236 : bool LocalStore::hasSubstitutes(const Path & path)
     537                 : {
     538             691 :     foreach (Paths::iterator, i, substituters) {
     539             464 :         RunningSubstituter & run(runningSubstituters[*i]);
     540             464 :         startSubstituter(*i, run);
     541                 : 
     542             464 :         *run.to << "have\n" << path << "\n" << std::flush;
     543                 : 
     544             464 :         if (getIntLine<int>(*run.from)) return true;
     545                 :     }
     546                 : 
     547             227 :     return false;
     548                 : }
     549                 : 
     550                 : 
     551                 : bool LocalStore::querySubstitutablePathInfo(const Path & substituter,
     552              41 :     const Path & path, SubstitutablePathInfo & info)
     553                 : {
     554              41 :     RunningSubstituter & run(runningSubstituters[substituter]);
     555              41 :     startSubstituter(substituter, run);
     556                 : 
     557              41 :     *run.to << "info\n" << path << "\n" << std::flush;
     558                 :         
     559              41 :     if (!getIntLine<int>(*run.from)) return false;
     560                 :     
     561              25 :     getline(*run.from, info.deriver);
     562              25 :     if (info.deriver != "") assertStorePath(info.deriver);
     563              25 :     int nrRefs = getIntLine<int>(*run.from);
     564              40 :     while (nrRefs--) {
     565              15 :         Path p; getline(*run.from, p);
     566              15 :         assertStorePath(p);
     567              15 :         info.references.insert(p);
     568                 :     }
     569              25 :     info.downloadSize = getIntLine<long long>(*run.from);
     570                 :     
     571              25 :     return true;
     572                 : }
     573                 : 
     574                 : 
     575                 : bool LocalStore::querySubstitutablePathInfo(const Path & path,
     576               8 :     SubstitutablePathInfo & info)
     577                 : {
     578              12 :     foreach (Paths::iterator, i, substituters)
     579              12 :         if (querySubstitutablePathInfo(*i, path, info)) return true;
     580               0 :     return false;
     581                 : }
     582                 : 
     583                 : 
     584               1 : Hash LocalStore::queryPathHash(const Path & path)
     585                 : {
     586               1 :     return queryPathInfo(path).hash;
     587                 : }
     588                 : 
     589                 : 
     590                 : static void dfsVisit(std::map<Path, ValidPathInfo> & infos,
     591            9999 :     const Path & path, PathSet & visited, Paths & sorted)
     592                 : {
     593            9999 :     if (visited.find(path) != visited.end()) return;
     594            5001 :     visited.insert(path);
     595                 : 
     596            5001 :     ValidPathInfo & info(infos[path]);
     597                 :     
     598           14999 :     for (PathSet::iterator i = info.references.begin();
     599                 :          i != info.references.end(); ++i)
     600            9998 :         if (infos.find(*i) != infos.end())
     601            4998 :             dfsVisit(infos, *i, visited, sorted);
     602                 : 
     603            5001 :     sorted.push_back(path);
     604                 : }
     605                 : 
     606                 : 
     607               3 : void LocalStore::registerValidPaths(const ValidPathInfos & infos)
     608                 : {
     609               3 :     std::map<Path, ValidPathInfo> infosMap;
     610                 :     
     611                 :     /* Sort the paths topologically under the references relation, so
     612                 :        that if path A is referenced by B, then A is registered before
     613                 :        B. */
     614            5004 :     for (ValidPathInfos::const_iterator i = infos.begin(); i != infos.end(); ++i)
     615            5001 :         infosMap[i->path] = *i;
     616                 : 
     617               3 :     PathSet visited;
     618               3 :     Paths sorted;
     619            5007 :     for (ValidPathInfos::const_iterator i = infos.begin(); i != infos.end(); ++i)
     620            5001 :         dfsVisit(infosMap, i->path, visited, sorted);
     621                 : 
     622            5004 :     for (Paths::iterator i = sorted.begin(); i != sorted.end(); ++i)
     623            5004 :         registerValidPath(infosMap[*i]);
     624               3 : }
     625                 : 
     626                 : 
     627                 : /* Invalidate a path.  The caller is responsible for checking that
     628                 :    there are no referrers. */
     629            2673 : void LocalStore::invalidatePath(const Path & path)
     630                 : {
     631            2673 :     debug(format("invalidating path `%1%'") % path);
     632                 : 
     633            2673 :     ValidPathInfo info;
     634                 : 
     635            2673 :     if (pathExists(infoFileFor(path))) {
     636            2673 :         info = queryPathInfo(path);
     637                 : 
     638                 :         /* Remove the info file. */
     639            2673 :         Path p = infoFileFor(path);
     640            2673 :         if (unlink(p.c_str()) == -1)
     641               0 :             throw SysError(format("unlinking `%1%'") % p);
     642                 :     }
     643                 : 
     644                 :     /* Remove the referrers file for `path'. */
     645            2673 :     Path p = referrersFileFor(path);
     646            2673 :     if (pathExists(p) && unlink(p.c_str()) == -1)
     647               0 :         throw SysError(format("unlinking `%1%'") % p);
     648                 : 
     649                 :     /* Clear `path' from the info cache. */
     650            2673 :     pathInfoCache.erase(path);
     651            2673 :     delayedUpdates.erase(path);
     652                 : 
     653                 :     /* Cause the referrer files for each path referenced by this one
     654                 :        to be updated.  This has to happen after removing the info file
     655                 :        to preserve the invariant (see registerValidPath()).
     656                 : 
     657                 :        The referrer files are updated lazily in flushDelayedUpdates()
     658                 :        to prevent quadratic performance in the garbage collector
     659                 :        (i.e., when N referrers to some path X are deleted, we have to
     660                 :        rewrite the referrers file for X N times, causing O(N^2) I/O).
     661                 : 
     662                 :        What happens if we die before the referrer file can be updated?
     663                 :        That's not a problem, because stale (invalid) entries in the
     664                 :        referrer file are ignored by queryReferrers().  Thus a referrer
     665                 :        file is allowed to have stale entries; removing them is just an
     666                 :        optimisation.  verifyStore() gets rid of them eventually.
     667                 :     */
     668            7850 :     foreach (PathSet::iterator, i, info.references)
     669            7850 :         if (*i != path) delayedUpdates.insert(*i);
     670            2673 : }
     671                 : 
     672                 : 
     673                 : Path LocalStore::addToStore(const Path & _srcPath, bool fixed,
     674             163 :     bool recursive, string hashAlgo, PathFilter & filter)
     675                 : {
     676             163 :     Path srcPath(absPath(_srcPath));
     677             163 :     debug(format("adding `%1%' to the store") % srcPath);
     678                 : 
     679                 :     std::pair<Path, Hash> pr =
     680             163 :         computeStorePathForPath(srcPath, fixed, recursive, hashAlgo, filter);
     681             163 :     Path & dstPath(pr.first);
     682             163 :     Hash & h(pr.second);
     683                 : 
     684             163 :     addTempRoot(dstPath);
     685                 : 
     686             163 :     if (!isValidPath(dstPath)) {
     687                 : 
     688                 :         /* The first check above is an optimisation to prevent
     689                 :            unnecessary lock acquisition. */
     690                 : 
     691              72 :         PathLocks outputLock(singleton<PathSet, Path>(dstPath));
     692                 : 
     693             144 :         if (!isValidPath(dstPath)) {
     694                 : 
     695              72 :             if (pathExists(dstPath)) deletePathWrapped(dstPath);
     696                 : 
     697              72 :             copyPath(srcPath, dstPath, filter);
     698                 : 
     699              72 :             Hash h2 = hashPath(htSHA256, dstPath, filter);
     700              72 :             if (h != h2)
     701                 :                 throw Error(format("contents of `%1%' changed while copying it to `%2%' (%3% -> %4%)")
     702               0 :                     % srcPath % dstPath % printHash(h) % printHash(h2));
     703                 : 
     704              72 :             canonicalisePathMetaData(dstPath);
     705                 :             
     706              72 :             registerValidPath(dstPath, h, PathSet(), "");
     707                 :         }
     708                 : 
     709              72 :         outputLock.setDeletion(true);
     710                 :     }
     711                 : 
     712             163 :     return dstPath;
     713                 : }
     714                 : 
     715                 : 
     716                 : Path LocalStore::addTextToStore(const string & suffix, const string & s,
     717             233 :     const PathSet & references)
     718                 : {
     719             233 :     Path dstPath = computeStorePathForText(suffix, s, references);
     720                 :     
     721             233 :     addTempRoot(dstPath);
     722                 : 
     723             233 :     if (!isValidPath(dstPath)) {
     724                 : 
     725             172 :         PathLocks outputLock(singleton<PathSet, Path>(dstPath));
     726                 : 
     727             344 :         if (!isValidPath(dstPath)) {
     728                 : 
     729             172 :             if (pathExists(dstPath)) deletePathWrapped(dstPath);
     730                 : 
     731             172 :             writeStringToFile(dstPath, s);
     732                 : 
     733             172 :             canonicalisePathMetaData(dstPath);
     734                 :             
     735                 :             registerValidPath(dstPath,
     736             172 :                 hashPath(htSHA256, dstPath), references, "");
     737                 :         }
     738                 : 
     739             172 :         outputLock.setDeletion(true);
     740                 :     }
     741                 : 
     742               0 :     return dstPath;
     743                 : }
     744                 : 
     745                 : 
     746                 : struct HashAndWriteSink : Sink
     747               5 : {
     748                 :     Sink & writeSink;
     749                 :     HashSink hashSink;
     750                 :     bool hashing;
     751               5 :     HashAndWriteSink(Sink & writeSink) : writeSink(writeSink), hashSink(htSHA256)
     752                 :     {
     753               5 :         hashing = true;
     754               5 :     }
     755                 :     virtual void operator ()
     756             527 :         (const unsigned char * data, unsigned int len)
     757                 :     {
     758             527 :         writeSink(data, len);
     759             527 :         if (hashing) hashSink(data, len);
     760             527 :     }
     761                 : };
     762                 : 
     763                 : 
     764                 : #define EXPORT_MAGIC 0x4558494e
     765                 : 
     766                 : 
     767               0 : static void checkSecrecy(const Path & path)
     768                 : {
     769                 :     struct stat st;
     770               0 :     if (stat(path.c_str(), &st))
     771               0 :         throw SysError(format("getting status of `%1%'") % path);
     772               0 :     if ((st.st_mode & (S_IRWXG | S_IRWXO)) != 0)
     773               0 :         throw Error(format("file `%1%' should be secret (inaccessible to everybody else)!") % path);
     774               0 : }
     775                 : 
     776                 : 
     777                 : void LocalStore::exportPath(const Path & path, bool sign,
     778               5 :     Sink & sink)
     779                 : {
     780               5 :     assertStorePath(path);
     781                 : 
     782               5 :     addTempRoot(path);
     783               5 :     if (!isValidPath(path))
     784               0 :         throw Error(format("path `%1%' is not valid") % path);
     785                 : 
     786               5 :     HashAndWriteSink hashAndWriteSink(sink);
     787                 :     
     788               5 :     dumpPath(path, hashAndWriteSink);
     789                 : 
     790               5 :     writeInt(EXPORT_MAGIC, hashAndWriteSink);
     791                 : 
     792               5 :     writeString(path, hashAndWriteSink);
     793                 :     
     794               5 :     PathSet references;
     795               5 :     queryReferences(path, references);
     796               5 :     writeStringSet(references, hashAndWriteSink);
     797                 : 
     798               5 :     Path deriver = queryDeriver(path);
     799               5 :     writeString(deriver, hashAndWriteSink);
     800                 : 
     801               5 :     if (sign) {
     802               0 :         Hash hash = hashAndWriteSink.hashSink.finish();
     803               0 :         hashAndWriteSink.hashing = false;
     804                 : 
     805               0 :         writeInt(1, hashAndWriteSink);
     806                 :         
     807               0 :         Path tmpDir = createTempDir();
     808               0 :         AutoDelete delTmp(tmpDir);
     809               0 :         Path hashFile = tmpDir + "/hash";
     810               0 :         writeStringToFile(hashFile, printHash(hash));
     811                 : 
     812               0 :         Path secretKey = nixConfDir + "/signing-key.sec";
     813               0 :         checkSecrecy(secretKey);
     814                 : 
     815               0 :         Strings args;
     816               0 :         args.push_back("rsautl");
     817               0 :         args.push_back("-sign");
     818               0 :         args.push_back("-inkey");
     819               0 :         args.push_back(secretKey);
     820               0 :         args.push_back("-in");
     821               0 :         args.push_back(hashFile);
     822               0 :         string signature = runProgram(OPENSSL_PATH, true, args);
     823                 : 
     824               0 :         writeString(signature, hashAndWriteSink);
     825                 :         
     826                 :     } else
     827               5 :         writeInt(0, hashAndWriteSink);
     828               5 : }
     829                 : 
     830                 : 
     831                 : struct HashAndReadSource : Source
     832               5 : {
     833                 :     Source & readSource;
     834                 :     HashSink hashSink;
     835                 :     bool hashing;
     836               5 :     HashAndReadSource(Source & readSource) : readSource(readSource), hashSink(htSHA256)
     837                 :     {
     838               5 :         hashing = true;
     839               5 :     }
     840                 :     virtual void operator ()
     841             527 :         (unsigned char * data, unsigned int len)
     842                 :     {
     843             527 :         readSource(data, len);
     844             527 :         if (hashing) hashSink(data, len);
     845             527 :     }
     846                 : };
     847                 : 
     848                 : 
     849               5 : Path LocalStore::importPath(bool requireSignature, Source & source)
     850                 : {
     851               5 :     HashAndReadSource hashAndReadSource(source);
     852                 :     
     853                 :     /* We don't yet know what store path this archive contains (the
     854                 :        store path follows the archive data proper), and besides, we
     855                 :        don't know yet whether the signature is valid. */
     856               5 :     Path tmpDir = createTempDir(nixStore);
     857               5 :     AutoDelete delTmp(tmpDir);
     858               5 :     Path unpacked = tmpDir + "/unpacked";
     859                 : 
     860               5 :     restorePath(unpacked, hashAndReadSource);
     861                 : 
     862               5 :     unsigned int magic = readInt(hashAndReadSource);
     863               5 :     if (magic != EXPORT_MAGIC)
     864               0 :         throw Error("Nix archive cannot be imported; wrong format");
     865                 : 
     866               5 :     Path dstPath = readStorePath(hashAndReadSource);
     867                 : 
     868               5 :     PathSet references = readStorePaths(hashAndReadSource);
     869                 : 
     870               5 :     Path deriver = readString(hashAndReadSource);
     871               5 :     if (deriver != "") assertStorePath(deriver);
     872                 : 
     873               5 :     Hash hash = hashAndReadSource.hashSink.finish();
     874               5 :     hashAndReadSource.hashing = false;
     875                 : 
     876               5 :     bool haveSignature = readInt(hashAndReadSource) == 1;
     877                 : 
     878               5 :     if (requireSignature && !haveSignature)
     879               0 :         throw Error("imported archive lacks a signature");
     880                 :     
     881               5 :     if (haveSignature) {
     882               0 :         string signature = readString(hashAndReadSource);
     883                 : 
     884               0 :         if (requireSignature) {
     885               0 :             Path sigFile = tmpDir + "/sig";
     886               0 :             writeStringToFile(sigFile, signature);
     887                 : 
     888               0 :             Strings args;
     889               0 :             args.push_back("rsautl");
     890               0 :             args.push_back("-verify");
     891               0 :             args.push_back("-inkey");
     892               0 :             args.push_back(nixConfDir + "/signing-key.pub");
     893               0 :             args.push_back("-pubin");
     894               0 :             args.push_back("-in");
     895               0 :             args.push_back(sigFile);
     896               0 :             string hash2 = runProgram(OPENSSL_PATH, true, args);
     897                 : 
     898                 :             /* Note: runProgram() throws an exception if the signature
     899                 :                is invalid. */
     900                 : 
     901               0 :             if (printHash(hash) != hash2)
     902                 :                 throw Error(
     903                 :                     "signed hash doesn't match actual contents of imported "
     904                 :                     "archive; archive could be corrupt, or someone is trying "
     905               0 :                     "to import a Trojan horse");
     906               0 :         }
     907                 :     }
     908                 : 
     909                 :     /* Do the actual import. */
     910                 : 
     911                 :     /* !!! way too much code duplication with addTextToStore() etc. */
     912               5 :     addTempRoot(dstPath);
     913                 : 
     914               5 :     if (!isValidPath(dstPath)) {
     915                 : 
     916               5 :         PathLocks outputLock(singleton<PathSet, Path>(dstPath));
     917                 : 
     918              10 :         if (!isValidPath(dstPath)) {
     919                 : 
     920               5 :             if (pathExists(dstPath)) deletePathWrapped(dstPath);
     921                 : 
     922               5 :             if (rename(unpacked.c_str(), dstPath.c_str()) == -1)
     923                 :                 throw SysError(format("cannot move `%1%' to `%2%'")
     924               0 :                     % unpacked % dstPath);
     925                 : 
     926               5 :             canonicalisePathMetaData(dstPath);
     927                 :             
     928                 :             /* !!! if we were clever, we could prevent the hashPath()
     929                 :                here. */
     930               5 :             if (deriver != "" && !isValidPath(deriver)) deriver = "";
     931                 :             registerValidPath(dstPath,
     932               5 :                 hashPath(htSHA256, dstPath), references, deriver);
     933                 :         }
     934                 :         
     935               4 :         outputLock.setDeletion(true);
     936                 :     }
     937                 :     
     938               4 :     return dstPath;
     939                 : }
     940                 : 
     941                 : 
     942                 : void LocalStore::deleteFromStore(const Path & path, unsigned long long & bytesFreed,
     943            2675 :     unsigned long long & blocksFreed)
     944                 : {
     945            2675 :     bytesFreed = 0;
     946                 : 
     947            2675 :     assertStorePath(path);
     948                 : 
     949            2675 :     if (isValidPath(path)) {
     950                 :         /* Acquire a lock on the referrers file to prevent new
     951                 :            referrers to this path from appearing while we're deleting
     952                 :            it. */
     953            2673 :         PathLocks referrersLock(singleton<PathSet, Path>(referrersFileFor(path)));
     954            2673 :         referrersLock.setDeletion(true);
     955            2673 :         PathSet referrers; queryReferrers(path, referrers);
     956            2673 :         referrers.erase(path); /* ignore self-references */
     957            2673 :         if (!referrers.empty())
     958                 :             throw PathInUse(format("cannot delete path `%1%' because it is in use by `%2%'")
     959               0 :                 % path % showPaths(referrers));
     960            2673 :         invalidatePath(path);
     961                 :     }
     962                 : 
     963            2675 :     deletePathWrapped(path, bytesFreed, blocksFreed);
     964            2675 : }
     965                 : 
     966                 : 
     967               1 : void LocalStore::verifyStore(bool checkContents)
     968                 : {
     969                 :     /* Check whether all valid paths actually exist. */
     970               1 :     printMsg(lvlInfo, "checking path existence");
     971                 : 
     972               1 :     PathSet validPaths2 = queryValidPaths(), validPaths;
     973                 :     
     974              13 :     for (PathSet::iterator i = validPaths2.begin(); i != validPaths2.end(); ++i) {
     975              12 :         checkInterrupt();
     976              12 :         if (!isStorePath(*i)) {
     977               0 :             printMsg(lvlError, format("path `%1%' is not in the Nix store") % *i);
     978               0 :             invalidatePath(*i);
     979              12 :         } else if (!pathExists(*i)) {
     980               0 :             printMsg(lvlError, format("path `%1%' disappeared") % *i);
     981               0 :             invalidatePath(*i);
     982                 :         } else
     983              12 :             validPaths.insert(*i);
     984                 :     }
     985                 : 
     986                 : 
     987                 :     /* Check the store path meta-information. */
     988               1 :     printMsg(lvlInfo, "checking path meta-information");
     989                 : 
     990               1 :     std::map<Path, PathSet> referrersCache;
     991                 :     
     992              13 :     for (PathSet::iterator i = validPaths.begin(); i != validPaths.end(); ++i) {
     993              12 :         bool update = false;
     994              12 :         ValidPathInfo info = queryPathInfo(*i);
     995                 : 
     996                 :         /* Check the references: each reference should be valid, and
     997                 :            it should have a matching referrer. */
     998              21 :         for (PathSet::iterator j = info.references.begin();
     999                 :              j != info.references.end(); ++j)
    1000                 :         {
    1001               9 :             if (referrersCache.find(*j) == referrersCache.end())
    1002               7 :                 queryReferrers(*j, referrersCache[*j]);
    1003               9 :             if (referrersCache[*j].find(*i) == referrersCache[*j].end()) {
    1004               0 :                 printMsg(lvlError, format("adding missing referrer mapping from `%1%' to `%2%'")
    1005                 :                     % *j % *i);
    1006               0 :                 appendReferrer(*j, *i, true);
    1007                 :             }
    1008               9 :             if (validPaths.find(*j) == validPaths.end()) {
    1009               0 :                 printMsg(lvlError, format("incomplete closure: `%1%' needs missing `%2%'")
    1010                 :                     % *i % *j);
    1011                 :                 /* nothing we can do about it... */
    1012                 :             }
    1013                 :         }
    1014                 : 
    1015                 :         /* Check the deriver.  (Note that the deriver doesn't have to
    1016                 :            be a valid path.) */
    1017              12 :         if (!info.deriver.empty() && !isStorePath(info.deriver)) {
    1018               0 :             info.deriver = "";
    1019               0 :             update = true;
    1020                 :         }
    1021                 : 
    1022                 :         /* Check the content hash (optionally - slow). */
    1023              12 :         if (checkContents) {
    1024               0 :             debug(format("checking contents of `%1%'") % *i);
    1025               0 :             Hash current = hashPath(info.hash.type, *i);
    1026               0 :             if (current != info.hash) {
    1027               0 :                 printMsg(lvlError, format("path `%1%' was modified! "
    1028                 :                         "expected hash `%2%', got `%3%'")
    1029                 :                     % *i % printHash(info.hash) % printHash(current));
    1030                 :             }
    1031                 :         }
    1032                 : 
    1033              12 :         if (update) registerValidPath(info);
    1034                 :     }
    1035                 : 
    1036               1 :     referrersCache.clear();
    1037                 :     
    1038                 : 
    1039                 :     /* Check the referrers. */
    1040               1 :     printMsg(lvlInfo, "checking referrers");
    1041                 : 
    1042               1 :     std::map<Path, PathSet> referencesCache;
    1043                 :     
    1044               1 :     Strings entries = readDirectory(nixDBPath + "/referrer");
    1045              15 :     for (Strings::iterator i = entries.begin(); i != entries.end(); ++i) {
    1046               7 :         Path from = nixStore + "/" + *i;
    1047                 :         
    1048               7 :         if (validPaths.find(from) == validPaths.end()) {
    1049               0 :             printMsg(lvlError, format("removing referrers file for invalid `%1%'") % from);
    1050               0 :             Path p = referrersFileFor(from);
    1051               0 :             if (unlink(p.c_str()) == -1)
    1052               0 :                 throw SysError(format("unlinking `%1%'") % p);
    1053               0 :             continue;
    1054                 :         }
    1055                 : 
    1056               7 :         PathSet referrers;
    1057               7 :         bool allValid = queryReferrersInternal(from, referrers);
    1058               7 :         bool update = false;
    1059                 : 
    1060               7 :         if (!allValid) {
    1061               0 :             printMsg(lvlError, format("removing some stale referrers for `%1%'") % from);
    1062               0 :             update = true;
    1063                 :         }
    1064                 : 
    1065                 :         /* Each referrer should have a matching reference. */
    1066               7 :         PathSet referrersNew;
    1067              16 :         for (PathSet::iterator j = referrers.begin(); j != referrers.end(); ++j) {
    1068               9 :             if (referencesCache.find(*j) == referencesCache.end())
    1069               5 :                 queryReferences(*j, referencesCache[*j]);
    1070               9 :             if (referencesCache[*j].find(from) == referencesCache[*j].end()) {
    1071               0 :                 printMsg(lvlError, format("removing unexpected referrer mapping from `%1%' to `%2%'")
    1072                 :                     % from % *j);
    1073               0 :                 update = true;
    1074               9 :             } else referrersNew.insert(*j);
    1075                 :         }
    1076                 : 
    1077               7 :         if (update) rewriteReferrers(from, false, referrersNew);
    1078               1 :     }
    1079               1 : }
    1080                 : 
    1081               0 : 
    1082            1106 : }

Generated by: LTP GCOV extension version 1.6