LTP GCOV extension - code coverage report
Current view: directory - src/libstore - optimise-store.cc
Test: app.info
Date: 2008-11-20 Instrumented lines: 61
Code covered: 0.0 % Executed lines: 0

       1                 : #include "util.hh"
       2                 : #include "local-store.hh"
       3                 : 
       4                 : #include <sys/types.h>
       5                 : #include <sys/stat.h>
       6                 : #include <unistd.h>
       7                 : #include <errno.h>
       8                 : 
       9                 : 
      10                 : namespace nix {
      11                 : 
      12                 : 
      13                 : typedef std::map<Hash, std::pair<Path, ino_t> > HashToPath;
      14                 : 
      15                 : 
      16               0 : static void makeWritable(const Path & path)
      17                 : {
      18                 :     struct stat st;
      19               0 :     if (lstat(path.c_str(), &st))
      20               0 :         throw SysError(format("getting attributes of path `%1%'") % path);
      21               0 :     if (chmod(path.c_str(), st.st_mode | S_IWUSR) == -1)
      22               0 :         throw SysError(format("changing writability of `%1%'") % path);
      23               0 : }
      24                 : 
      25                 : 
      26                 : struct MakeReadOnly
      27                 : {
      28                 :     Path path;
      29               0 :     MakeReadOnly(const Path & path) : path(path) { }
      30               0 :     ~MakeReadOnly()
      31                 :     {
      32                 :         try {
      33               0 :             if (path != "") canonicalisePathMetaData(path, false);
      34               0 :         } catch (...) {
      35               0 :             ignoreException();
      36                 :         }
      37               0 :     }
      38                 : };
      39                 : 
      40                 : 
      41                 : static void hashAndLink(bool dryRun, HashToPath & hashToPath,
      42               0 :     OptimiseStats & stats, const Path & path)
      43                 : {
      44                 :     struct stat st;
      45               0 :     if (lstat(path.c_str(), &st))
      46               0 :         throw SysError(format("getting attributes of path `%1%'") % path);
      47                 : 
      48                 :     /* Sometimes SNAFUs can cause files in the Nix store to be
      49                 :        modified, in particular when running programs as root under
      50                 :        NixOS (example: $fontconfig/var/cache being modified).  Skip
      51                 :        those files. */
      52               0 :     if (S_ISREG(st.st_mode) && (st.st_mode & S_IWUSR)) {
      53               0 :         printMsg(lvlError, format("skipping suspicious writable file `%1%'") % path);
      54               0 :         return;
      55                 :     }
      56                 : 
      57                 :     /* We can hard link regular files and symlinks. */
      58               0 :     if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)) {
      59                 : 
      60                 :         /* Hash the file.  Note that hashPath() returns the hash over
      61                 :            the NAR serialisation, which includes the execute bit on
      62                 :            the file.  Thus, executable and non-executable files with
      63                 :            the same contents *won't* be linked (which is good because
      64                 :            otherwise the permissions would be screwed up).
      65                 : 
      66                 :            Also note that if `path' is a symlink, then we're hashing
      67                 :            the contents of the symlink (i.e. the result of
      68                 :            readlink()), not the contents of the target (which may not
      69                 :            even exist). */
      70               0 :         Hash hash = hashPath(htSHA256, path);
      71               0 :         stats.totalFiles++;
      72               0 :         printMsg(lvlDebug, format("`%1%' has hash `%2%'") % path % printHash(hash));
      73                 : 
      74               0 :         std::pair<Path, ino_t> prevPath = hashToPath[hash];
      75                 :         
      76               0 :         if (prevPath.first == "") {
      77               0 :             hashToPath[hash] = std::pair<Path, ino_t>(path, st.st_ino);
      78               0 :             return;
      79                 :         }
      80                 :             
      81                 :         /* Yes!  We've seen a file with the same contents.  Replace
      82                 :            the current file with a hard link to that file. */
      83               0 :         stats.sameContents++;
      84               0 :         if (prevPath.second == st.st_ino) {
      85               0 :             printMsg(lvlDebug, format("`%1%' is already linked to `%2%'") % path % prevPath.first);
      86                 :             return;
      87                 :         }
      88                 :         
      89               0 :         if (!dryRun) {
      90                 :             
      91               0 :             printMsg(lvlTalkative, format("linking `%1%' to `%2%'") % path % prevPath.first);
      92                 : 
      93                 :             Path tempLink = (format("%1%.tmp-%2%-%3%")
      94               0 :                 % path % getpid() % rand()).str();
      95                 : 
      96                 :             /* Make the containing directory writable, but only if
      97                 :                it's not the store itself (we don't want or need to
      98                 :                mess with  its permissions). */
      99               0 :             bool mustToggle = !isStorePath(path);
     100               0 :             if (mustToggle) makeWritable(dirOf(path));
     101                 :             
     102                 :             /* When we're done, make the directory read-only again and
     103                 :                reset its timestamp back to 0. */
     104               0 :             MakeReadOnly makeReadOnly(mustToggle ? dirOf(path) : "");
     105                 :         
     106               0 :             if (link(prevPath.first.c_str(), tempLink.c_str()) == -1) {
     107               0 :                 if (errno == EMLINK) {
     108                 :                     /* Too many links to the same file (>= 32000 on
     109                 :                        most file systems).  This is likely to happen
     110                 :                        with empty files.  Just start over, creating
     111                 :                        links to the current file. */
     112               0 :                     printMsg(lvlInfo, format("`%1%' has maximum number of links") % prevPath.first);
     113               0 :                     hashToPath[hash] = std::pair<Path, ino_t>(path, st.st_ino);
     114                 :                     return;
     115                 :                 }
     116                 :                 throw SysError(format("cannot link `%1%' to `%2%'")
     117               0 :                     % tempLink % prevPath.first);
     118                 :             }
     119                 : 
     120                 :             /* Atomically replace the old file with the new hard link. */
     121               0 :             if (rename(tempLink.c_str(), path.c_str()) == -1)
     122                 :                 throw SysError(format("cannot rename `%1%' to `%2%'")
     123               0 :                     % tempLink % path);
     124                 :         } else
     125               0 :             printMsg(lvlTalkative, format("would link `%1%' to `%2%'") % path % prevPath.first);
     126                 :         
     127               0 :         stats.filesLinked++;
     128               0 :         stats.bytesFreed += st.st_size;
     129               0 :         stats.blocksFreed += st.st_blocks;
     130                 :     }
     131                 : 
     132               0 :     if (S_ISDIR(st.st_mode)) {
     133               0 :         Strings names = readDirectory(path);
     134               0 :         for (Strings::iterator i = names.begin(); i != names.end(); ++i)
     135               0 :             hashAndLink(dryRun, hashToPath, stats, path + "/" + *i);
     136                 :     }
     137                 : }
     138                 : 
     139                 : 
     140               0 : void LocalStore::optimiseStore(bool dryRun, OptimiseStats & stats)
     141                 : {
     142               0 :     HashToPath hashToPath;
     143                 : 
     144               0 :     PathSet paths = queryValidPaths();
     145                 : 
     146               0 :     for (PathSet::iterator i = paths.begin(); i != paths.end(); ++i) {
     147               0 :         addTempRoot(*i);
     148               0 :         if (!isValidPath(*i)) continue; /* path was GC'ed, probably */
     149               0 :         startNest(nest, lvlChatty, format("hashing files in `%1%'") % *i);
     150               0 :         hashAndLink(dryRun, hashToPath, stats, *i);
     151               0 :     }
     152               0 : }
     153                 : 
     154               0 : 
     155                 : }

Generated by: LTP GCOV extension version 1.6