LTP GCOV extension - code coverage report
Current view: directory - src/libstore - store.cc
Test: app.info
Date: 2004-12-21 Instrumented lines: 261
Code covered: 76.2 % Executed lines: 199

       1                 : #include <iostream>
       2                 : #include <algorithm>
       3                 : 
       4                 : #include <sys/wait.h>
       5                 : #include <unistd.h>
       6                 : 
       7                 : #include "store.hh"
       8                 : #include "globals.hh"
       9                 : #include "db.hh"
      10                 : #include "archive.hh"
      11                 : #include "pathlocks.hh"
      12                 : 
      13                 : 
      14                 : /* Nix database. */
      15              51 : static Database nixDB;
      16                 : 
      17                 : 
      18                 : /* Database tables. */
      19                 : 
      20                 : /* dbValidPaths :: Path -> ()
      21                 : 
      22                 :    The existence of a key $p$ indicates that path $p$ is valid (that
      23                 :    is, produced by a succesful build). */
      24                 : static TableId dbValidPaths = 0;
      25                 : 
      26                 : /* dbSuccessors :: Path -> Path
      27                 : 
      28                 :    Each pair $(p_1, p_2)$ in this mapping records the fact that the
      29                 :    Nix expression stored at path $p_1$ has a successor expression
      30                 :    stored at path $p_2$.
      31                 : 
      32                 :    Note that a term $y$ is a successor of $x$ iff there exists a
      33                 :    sequence of rewrite steps that rewrites $x$ into $y$.
      34                 : */
      35                 : static TableId dbSuccessors = 0;
      36                 : 
      37                 : /* dbSuccessorsRev :: Path -> [Path]
      38                 : 
      39                 :    The reverse mapping of dbSuccessors (i.e., it stores the
      40                 :    predecessors of a Nix expression).
      41                 : */
      42                 : static TableId dbSuccessorsRev = 0;
      43                 : 
      44                 : /* dbSubstitutes :: Path -> [(Path, Path, [string])]
      45                 : 
      46                 :    Each pair $(p, subs)$ tells Nix that it can use any of the
      47                 :    substitutes in $subs$ to build path $p$.  Each substitute is a
      48                 :    tuple $(storeExpr, program, args)$ (see the type `Substitute' in
      49                 :    `store.hh').
      50                 : 
      51                 :    The main purpose of this is for distributed caching of derivates.
      52                 :    One system can compute a derivate and put it on a website (as a Nix
      53                 :    archive), for instance, and then another system can register a
      54                 :    substitute for that derivate.  The substitute in this case might be
      55                 :    a Nix expression that fetches the Nix archive.
      56                 : */
      57                 : static TableId dbSubstitutes = 0;
      58                 : 
      59                 : /* dbSubstitutesRev :: Path -> [Path]
      60                 : 
      61                 :    The reverse mapping of dbSubstitutes; it maps store expressions
      62                 :    back to the paths for which they are substitutes.
      63                 : */
      64                 : static TableId dbSubstitutesRev = 0;
      65                 : 
      66                 : 
      67                 : bool Substitute::operator == (const Substitute & sub)
      68               2 : {
      69               2 :     return storeExpr == sub.storeExpr
      70                 :         && program == sub.program
      71                 :         && args == sub.args;
      72                 : }
      73                 : 
      74                 : 
      75                 : void openDB()
      76              51 : {
      77              51 :     if (readOnlyMode) return;
      78              39 :     try {
      79              39 :         nixDB.open(nixDBPath);
      80               0 :     } catch (DbNoPermission & e) {
      81               0 :         printMsg(lvlTalkative, "cannot access Nix database; continuing anyway");
      82               0 :         readOnlyMode = true;
      83               0 :         return;
      84                 :     }
      85              39 :     dbValidPaths = nixDB.openTable("validpaths");
      86              39 :     dbSuccessors = nixDB.openTable("successors");
      87              39 :     dbSuccessorsRev = nixDB.openTable("successors-rev");
      88              39 :     dbSubstitutes = nixDB.openTable("substitutes");
      89              39 :     dbSubstitutesRev = nixDB.openTable("substitutes-rev");
      90                 : }
      91                 : 
      92                 : 
      93                 : void initDB()
      94               1 : {
      95                 : }
      96                 : 
      97                 : 
      98                 : void createStoreTransaction(Transaction & txn)
      99              34 : {
     100              34 :     Transaction txn2(nixDB);
     101              34 :     txn2.moveTo(txn);
     102                 : }
     103                 : 
     104                 : 
     105                 : /* Path copying. */
     106                 : 
     107                 : struct CopySink : DumpSink
     108                 : {
     109                 :     int fd;
     110                 :     virtual void operator () (const unsigned char * data, unsigned int len)
     111             158 :     {
     112             158 :         writeFull(fd, data, len);
     113                 :     }
     114                 : };
     115                 : 
     116                 : 
     117                 : struct CopySource : RestoreSource
     118                 : {
     119                 :     int fd;
     120                 :     virtual void operator () (unsigned char * data, unsigned int len)
     121               0 :     {
     122               0 :         readFull(fd, data, len);
     123                 :     }
     124                 : };
     125                 : 
     126                 : 
     127                 : void copyPath(const Path & src, const Path & dst)
     128               8 : {
     129               8 :     debug(format("copying `%1%' to `%2%'") % src % dst);
     130                 : 
     131                 :     /* Unfortunately C++ doesn't support coprocedures, so we have no
     132                 :        nice way to chain CopySink and CopySource together.  Instead we
     133                 :        fork off a child to run the sink.  (Fork-less platforms should
     134                 :        use a thread). */
     135                 : 
     136                 :     /* Create a pipe. */
     137            1592 :     Pipe pipe;
     138               8 :     pipe.create();
     139                 : 
     140                 :     /* Fork. */
     141               8 :     Pid pid;
     142               8 :     pid = fork();
     143               8 :     switch (pid) {
     144                 : 
     145                 :     case -1:
     146               0 :         throw SysError("unable to fork");
     147                 : 
     148                 :     case 0: /* child */
     149               0 :         try {
     150               0 :             pipe.writeSide.close();
     151               0 :             CopySource source;
     152               0 :             source.fd = pipe.readSide;
     153               0 :             restorePath(dst, source);
     154               0 :             _exit(0);
     155               0 :         } catch (exception & e) {
     156               0 :             cerr << "error: " << e.what() << endl;
     157                 :         }
     158               0 :         _exit(1);        
     159                 :     }
     160                 : 
     161                 :     /* Parent. */
     162                 : 
     163               8 :     pipe.readSide.close();
     164                 :     
     165              86 :     CopySink sink;
     166               8 :     sink.fd = pipe.writeSide;
     167                 :     {
     168               8 :         SwitchToOriginalUser sw;
     169               8 :         dumpPath(src, sink);
     170                 :     }
     171                 : 
     172                 :     /* Wait for the child to finish. */
     173               8 :     int status = pid.wait(true);
     174               8 :     if (!statusOk(status))
     175               0 :         throw Error(format("cannot copy `%1% to `%2%': child %3%")
     176                 :             % src % dst % statusToString(status));
     177                 : }
     178                 : 
     179                 : 
     180                 : static bool isInStore(const Path & path)
     181             490 : {
     182             490 :     return path[0] == '/'
     183                 :         && path.compare(0, nixStore.size(), nixStore) == 0
     184                 :         && path.size() >= nixStore.size() + 2
     185                 :         && path[nixStore.size()] == '/'
     186                 :         && path.find('/', nixStore.size() + 1) == Path::npos;
     187                 : }
     188                 : 
     189                 : 
     190                 : void assertStorePath(const Path & path)
     191             408 : {
     192             408 :     if (!isInStore(path))
     193               0 :         throw Error(format("path `%1%' is not in the Nix store") % path);
     194                 : }
     195                 : 
     196                 : 
     197                 : static bool isValidPathTxn(const Path & path, const Transaction & txn)
     198             806 : {
     199             806 :     string s;
     200             806 :     return nixDB.queryString(txn, dbValidPaths, path, s);
     201                 : }
     202                 : 
     203                 : 
     204                 : bool isValidPath(const Path & path)
     205             783 : {
     206             783 :     return isValidPathTxn(path, noTxn);
     207                 : }
     208                 : 
     209                 : 
     210                 : static bool isUsablePathTxn(const Path & path, const Transaction & txn)
     211              23 : {
     212              23 :     if (isValidPathTxn(path, txn)) return true;
     213               3 :     Paths subs;
     214               3 :     nixDB.queryStrings(txn, dbSubstitutes, path, subs);
     215               3 :     return subs.size() > 0;
     216                 : }
     217                 : 
     218                 : 
     219                 : void registerSuccessor(const Transaction & txn,
     220                 :     const Path & srcPath, const Path & sucPath)
     221              23 : {
     222              23 :     assertStorePath(srcPath);
     223              23 :     assertStorePath(sucPath);
     224                 :     
     225              23 :     if (!isUsablePathTxn(sucPath, txn)) throw Error(
     226                 :         format("path `%1%' cannot be a successor, since it is not usable")
     227                 :         % sucPath);
     228                 : 
     229              23 :     Path known;
     230              23 :     if (nixDB.queryString(txn, dbSuccessors, srcPath, known) &&
     231                 :         known != sucPath)
     232                 :     {
     233               0 :         throw Error(format(
     234                 :             "the `impossible' happened: expression in path "
     235                 :             "`%1%' appears to have multiple successors "
     236                 :             "(known `%2%', new `%3%'")
     237                 :             % srcPath % known % sucPath);
     238                 :     }
     239                 : 
     240              23 :     Paths revs;
     241              23 :     nixDB.queryStrings(txn, dbSuccessorsRev, sucPath, revs);
     242              23 :     if (find(revs.begin(), revs.end(), srcPath) == revs.end())
     243              23 :         revs.push_back(srcPath);
     244                 : 
     245              23 :     nixDB.setString(txn, dbSuccessors, srcPath, sucPath);
     246              23 :     nixDB.setStrings(txn, dbSuccessorsRev, sucPath, revs);
     247                 : }
     248                 : 
     249                 : 
     250                 : void unregisterSuccessor(const Path & srcPath)
     251               1 : {
     252               1 :     assertStorePath(srcPath);
     253                 : 
     254               1 :     Transaction txn(nixDB);
     255                 : 
     256               1 :     Path sucPath;
     257               1 :     if (!nixDB.queryString(txn, dbSuccessors, srcPath, sucPath)) {
     258               0 :         txn.abort();
     259               0 :         return;
     260                 :     }
     261               1 :     nixDB.delPair(txn, dbSuccessors, srcPath);
     262                 : 
     263               1 :     Paths revs;
     264               1 :     nixDB.queryStrings(txn, dbSuccessorsRev, sucPath, revs);
     265               1 :     Paths::iterator i = find(revs.begin(), revs.end(), srcPath);
     266               1 :     assert(i != revs.end());
     267               1 :     revs.erase(i);
     268               1 :     nixDB.setStrings(txn, dbSuccessorsRev, sucPath, revs);
     269                 : 
     270               1 :     txn.commit();
     271                 : }
     272                 : 
     273                 : 
     274                 : bool querySuccessor(const Path & srcPath, Path & sucPath)
     275             299 : {
     276             299 :     return nixDB.queryString(noTxn, dbSuccessors, srcPath, sucPath);
     277                 : }
     278                 : 
     279                 : 
     280                 : Paths queryPredecessors(const Path & sucPath)
     281               0 : {
     282               0 :     Paths revs;
     283               0 :     nixDB.queryStrings(noTxn, dbSuccessorsRev, sucPath, revs);
     284               0 :     return revs;
     285                 : }
     286                 : 
     287                 : 
     288                 : static Substitutes readSubstitutes(const Transaction & txn,
     289                 :     const Path & srcPath)
     290              18 : {
     291              18 :     Strings ss;
     292              18 :     nixDB.queryStrings(txn, dbSubstitutes, srcPath, ss);
     293                 : 
     294             462 :     Substitutes subs;
     295                 :     
     296              34 :     for (Strings::iterator i = ss.begin(); i != ss.end(); ++i) {
     297              16 :         if (i->size() < 4 || (*i)[3] != 0) {
     298                 :             /* Old-style substitute.  !!! remove this code
     299                 :                eventually? */
     300              16 :             break;
     301                 :         }
     302              16 :         Strings ss2 = unpackStrings(*i);
     303              16 :         if (ss2.size() != 3) throw Error("malformed substitute");
     304              16 :         Strings::iterator j = ss2.begin();
     305              16 :         Substitute sub;
     306              16 :         sub.storeExpr = *j++;
     307              16 :         sub.program = *j++;
     308              16 :         sub.args = unpackStrings(*j++);
     309              16 :         subs.push_back(sub);
     310                 :     }
     311                 : 
     312              18 :     return subs;
     313                 : }
     314                 : 
     315                 : 
     316                 : static void writeSubstitutes(const Transaction & txn,
     317                 :     const Path & srcPath, const Substitutes & subs)
     318               8 : {
     319               8 :     Strings ss;
     320                 : 
     321              17 :     for (Substitutes::const_iterator i = subs.begin();
     322                 :          i != subs.end(); ++i)
     323                 :     {
     324               9 :         Strings ss2;
     325               9 :         ss2.push_back(i->storeExpr);
     326               9 :         ss2.push_back(i->program);
     327               9 :         ss2.push_back(packStrings(i->args));
     328               9 :         ss.push_back(packStrings(ss2));
     329                 :     }
     330                 : 
     331               8 :     nixDB.setStrings(txn, dbSubstitutes, srcPath, ss);
     332                 : }
     333                 : 
     334                 : 
     335                 : typedef map<Path, Paths> SubstitutesRev;
     336                 : 
     337                 : 
     338                 : void registerSubstitutes(const Transaction & txn,
     339                 :     const SubstitutePairs & subPairs)
     340               7 : {
     341              21 :     SubstitutesRev revMap;
     342                 :     
     343              14 :     for (SubstitutePairs::const_iterator i = subPairs.begin();
     344                 :          i != subPairs.end(); ++i)
     345                 :     {
     346               7 :         const Path & srcPath(i->first);
     347               7 :         const Substitute & sub(i->second);
     348                 : 
     349               7 :         assertStorePath(srcPath);
     350               7 :         assertStorePath(sub.storeExpr);
     351                 :     
     352               7 :         Substitutes subs = readSubstitutes(txn, srcPath);
     353                 : 
     354                 :         /* New substitutes take precedence over old ones.  If the
     355                 :            substitute is already present, it's moved to the front. */
     356               7 :         remove(subs.begin(), subs.end(), sub);
     357               7 :         subs.push_front(sub);
     358                 :         
     359               7 :         writeSubstitutes(txn, srcPath, subs);
     360                 : 
     361               7 :         Paths & revs = revMap[sub.storeExpr];
     362               7 :         if (revs.empty())
     363               7 :             nixDB.queryStrings(txn, dbSubstitutesRev, sub.storeExpr, revs);
     364               7 :         if (find(revs.begin(), revs.end(), srcPath) == revs.end())
     365               7 :             revs.push_back(srcPath);
     366                 :     }
     367                 : 
     368                 :     /* Re-write the reverse mapping in one go to prevent Theta(n^2)
     369                 :        performance.  (This would occur because the data fields of the
     370                 :        `substitutes-rev' table are lists). */
     371              14 :     for (SubstitutesRev::iterator i = revMap.begin(); i != revMap.end(); ++i)
     372               7 :         nixDB.setStrings(txn, dbSubstitutesRev, i->first, i->second);
     373                 : }
     374                 : 
     375                 : 
     376                 : Substitutes querySubstitutes(const Path & srcPath)
     377               6 : {
     378               6 :     return readSubstitutes(noTxn, srcPath);
     379                 : }
     380                 : 
     381                 : 
     382                 : void registerValidPath(const Transaction & txn, const Path & _path)
     383              82 : {
     384              82 :     Path path(canonPath(_path));
     385              82 :     assertStorePath(path);
     386              82 :     debug(format("registering path `%1%'") % path);
     387              82 :     nixDB.setString(txn, dbValidPaths, path, "");
     388                 : }
     389                 : 
     390                 : 
     391                 : static void invalidatePath(const Path & path, Transaction & txn)
     392               0 : {
     393               0 :     debug(format("unregistering path `%1%'") % path);
     394                 : 
     395               0 :     nixDB.delPair(txn, dbValidPaths, path);
     396                 : 
     397                 :     /* Remove any successor mappings to this path (but not *from*
     398                 :        it). */
     399               0 :     Paths revs;
     400               0 :     nixDB.queryStrings(txn, dbSuccessorsRev, path, revs);
     401               0 :     for (Paths::iterator i = revs.begin(); i != revs.end(); ++i)
     402               0 :         nixDB.delPair(txn, dbSuccessors, *i);
     403               0 :     nixDB.delPair(txn, dbSuccessorsRev, path);
     404                 : 
     405                 :     /* Remove any substitute mappings to this path. */
     406               0 :     revs.clear();
     407               0 :     nixDB.queryStrings(txn, dbSubstitutesRev, path, revs);
     408               0 :     for (Paths::iterator i = revs.begin(); i != revs.end(); ++i) {
     409               0 :         Substitutes subs = readSubstitutes(txn, *i), subs2;
     410               0 :         bool found = false;
     411               0 :         for (Substitutes::iterator j = subs.begin(); j != subs.end(); ++j)
     412               0 :             if (j->storeExpr != path)
     413               0 :                 subs2.push_back(*j);
     414                 :             else
     415               0 :                 found = true;
     416                 :         // !!!        if (!found) throw Error("integrity error in substitutes mapping");
     417               0 :         writeSubstitutes(txn, *i, subs);
     418                 : 
     419                 :         /* If path *i now has no substitutes left, and is not valid,
     420                 :            then it too should be invalidated.  This is because it may
     421                 :            be a substitute or successor. */
     422               0 :         if (subs.size() == 0 && !isValidPathTxn(*i, txn))
     423               0 :             invalidatePath(*i, txn);
     424                 :     }
     425               0 :     nixDB.delPair(txn, dbSubstitutesRev, path);
     426                 : }
     427                 : 
     428                 : 
     429                 : Path addToStore(const Path & _srcPath)
     430              23 : {
     431              23 :     Path srcPath(absPath(_srcPath));
     432              23 :     debug(format("adding `%1%' to the store") % srcPath);
     433                 : 
     434              23 :     Hash h;
     435                 :     {
     436              23 :         SwitchToOriginalUser sw;
     437              23 :         h = hashPath(srcPath);
     438                 :     }
     439                 : 
     440              23 :     string baseName = baseNameOf(srcPath);
     441              23 :     Path dstPath = canonPath(nixStore + "/" + (string) h + "-" + baseName);
     442                 : 
     443              23 :     if (!readOnlyMode && !isValidPath(dstPath)) { 
     444                 : 
     445                 :         /* The first check above is an optimisation to prevent
     446                 :            unnecessary lock acquisition. */
     447                 : 
     448               8 :         PathSet lockPaths;
     449               8 :         lockPaths.insert(dstPath);
     450               8 :         PathLocks outputLock(lockPaths);
     451                 : 
     452               8 :         if (!isValidPath(dstPath)) {
     453                 : 
     454               8 :             if (pathExists(dstPath)) deletePath(dstPath);
     455                 : 
     456                 :             /* !!! race: srcPath might change between hashPath() and
     457                 :                here! */
     458                 :             
     459               8 :             copyPath(srcPath, dstPath);
     460                 : 
     461               8 :             makePathReadOnly(dstPath);
     462                 :             
     463               8 :             Transaction txn(nixDB);
     464               8 :             registerValidPath(txn, dstPath);
     465               8 :             txn.commit();
     466                 :         }
     467                 : 
     468               8 :         outputLock.setDeletion(true);
     469                 :     }
     470                 : 
     471              23 :     return dstPath;
     472                 : }
     473                 : 
     474                 : 
     475                 : void addTextToStore(const Path & dstPath, const string & s)
     476              50 : {
     477              50 :     assertStorePath(dstPath);
     478                 :     
     479              50 :     if (!isValidPath(dstPath)) {
     480                 : 
     481              50 :         PathSet lockPaths;
     482              50 :         lockPaths.insert(dstPath);
     483              50 :         PathLocks outputLock(lockPaths);
     484                 : 
     485              50 :         if (!isValidPath(dstPath)) {
     486                 : 
     487              50 :             if (pathExists(dstPath)) deletePath(dstPath);
     488                 : 
     489              50 :             writeStringToFile(dstPath, s);
     490                 : 
     491              50 :             makePathReadOnly(dstPath);
     492                 :             
     493              50 :             Transaction txn(nixDB);
     494              50 :             registerValidPath(txn, dstPath);
     495              50 :             txn.commit();
     496                 :         }
     497                 : 
     498              50 :         outputLock.setDeletion(true);
     499                 :     }
     500                 : }
     501                 : 
     502                 : 
     503                 : void deleteFromStore(const Path & _path)
     504               0 : {
     505               0 :     Path path(canonPath(_path));
     506                 : 
     507               0 :     assertStorePath(path);
     508                 : 
     509               0 :     Transaction txn(nixDB);
     510               0 :     invalidatePath(path, txn);
     511               0 :     txn.commit();
     512                 : 
     513               0 :     deletePath(path);
     514                 : }
     515                 : 
     516                 : 
     517                 : void verifyStore()
     518               1 : {
     519               1 :     Transaction txn(nixDB);
     520                 : 
     521               1 :     Paths paths;
     522               1 :     PathSet validPaths;
     523               1 :     nixDB.enumTable(txn, dbValidPaths, paths);
     524                 : 
     525              83 :     for (Paths::iterator i = paths.begin(); i != paths.end(); ++i) {
     526              82 :         Path path = *i;
     527              82 :         if (!pathExists(path)) {
     528               0 :             printMsg(lvlError, format("path `%1%' disappeared") % path);
     529               0 :             invalidatePath(path, txn);
     530              82 :         } else if (!isInStore(path)) {
     531               0 :             printMsg(lvlError, format("path `%1%' is not in the Nix store") % path);
     532               0 :             invalidatePath(path, txn);
     533                 :         } else
     534              82 :             validPaths.insert(path);
     535                 :     }
     536                 : 
     537                 :     /* !!! the code below does not allow transitive substitutes.
     538                 :        I.e., if B is a substitute of A, then B must be a valid path.
     539                 :        B cannot itself be invalid but have a substitute. */
     540                 : 
     541                 :     /* "Usable" paths are those that are valid or have a substitute.
     542                 :        These are the paths that are allowed to appear in the
     543                 :        right-hand side of a sute mapping. */
     544               1 :     PathSet usablePaths(validPaths);
     545                 : 
     546                 :     /* Check that the values of the substitute mappings are valid
     547                 :        paths. */ 
     548               1 :     Paths subKeys;
     549               1 :     nixDB.enumTable(txn, dbSubstitutes, subKeys);
     550               6 :     for (Paths::iterator i = subKeys.begin(); i != subKeys.end(); ++i) {
     551               5 :         Substitutes subs = readSubstitutes(txn, *i), subs2;
     552              12 :         for (Substitutes::iterator j = subs.begin(); j != subs.end(); ++j)
     553               7 :             if (validPaths.find(j->storeExpr) == validPaths.end())
     554               1 :                 printMsg(lvlError,
     555                 :                     format("found substitute mapping to non-existent path `%1%'")
     556                 :                     % j->storeExpr);
     557                 :             else
     558               6 :                 subs2.push_back(*j);
     559               5 :         if (subs.size() != subs2.size())
     560               1 :             writeSubstitutes(txn, *i, subs2);
     561               5 :         if (subs2.size() > 0)
     562               4 :             usablePaths.insert(*i);
     563                 :     }
     564                 : 
     565                 :     /* Check that the keys of the reverse substitute mappings are
     566                 :        valid paths. */ 
     567               1 :     Paths rsubKeys;
     568               1 :     nixDB.enumTable(txn, dbSubstitutesRev, rsubKeys);
     569               4 :     for (Paths::iterator i = rsubKeys.begin(); i != rsubKeys.end(); ++i) {
     570               3 :         if (validPaths.find(*i) == validPaths.end()) {
     571               1 :             printMsg(lvlError,
     572                 :                 format("found reverse substitute mapping for non-existent path `%1%'") % *i);
     573               1 :             nixDB.delPair(txn, dbSubstitutesRev, *i);
     574                 :         }
     575                 :     }
     576                 : 
     577                 :     /* Check that the values of the successor mappings are usable
     578                 :        paths. */ 
     579               1 :     Paths sucKeys;
     580               1 :     nixDB.enumTable(txn, dbSuccessors, sucKeys);
     581              23 :     for (Paths::iterator i = sucKeys.begin(); i != sucKeys.end(); ++i) {
     582                 :         /* Note that *i itself does not have to be valid, just its
     583                 :            successor. */
     584              22 :         Path sucPath;
     585              22 :         if (nixDB.queryString(txn, dbSuccessors, *i, sucPath) &&
     586                 :             usablePaths.find(sucPath) == usablePaths.end())
     587                 :         {
     588               0 :             printMsg(lvlError,
     589                 :                 format("found successor mapping to non-existent path `%1%'") % sucPath);
     590               0 :             nixDB.delPair(txn, dbSuccessors, *i);
     591                 :         }
     592                 :     }
     593                 : 
     594                 :     /* Check that the keys of the reverse successor mappings are valid
     595                 :        paths. */ 
     596               1 :     Paths rsucKeys;
     597               1 :     nixDB.enumTable(txn, dbSuccessorsRev, rsucKeys);
     598              23 :     for (Paths::iterator i = rsucKeys.begin(); i != rsucKeys.end(); ++i) {
     599              22 :         if (usablePaths.find(*i) == usablePaths.end()) {
     600               0 :             printMsg(lvlError,
     601                 :                 format("found reverse successor mapping for non-existent path `%1%'") % *i);
     602               0 :             nixDB.delPair(txn, dbSuccessorsRev, *i);
     603                 :         }
     604                 :     }
     605                 : 
     606               1 :     txn.commit();
     607              51 : }

Generated by: LTP GCOV extension version 1.1