summaryrefslogtreecommitdiff
path: root/nix/libstore/optimise-store.cc
diff options
context:
space:
mode:
Diffstat (limited to 'nix/libstore/optimise-store.cc')
-rw-r--r--nix/libstore/optimise-store.cc76
1 files changed, 67 insertions, 9 deletions
diff --git a/nix/libstore/optimise-store.cc b/nix/libstore/optimise-store.cc
index d833f3aa05..67ee94a4bd 100644
--- a/nix/libstore/optimise-store.cc
+++ b/nix/libstore/optimise-store.cc
@@ -40,18 +40,66 @@ struct MakeReadOnly
};
-void LocalStore::optimisePath_(OptimiseStats & stats, const Path & path)
+LocalStore::InodeHash LocalStore::loadInodeHash()
+{
+ printMsg(lvlDebug, "loading hash inodes in memory");
+ InodeHash inodeHash;
+
+ AutoCloseDir dir = opendir(linksDir.c_str());
+ if (!dir) throw SysError(format("opening directory `%1%'") % linksDir);
+
+ struct dirent * dirent;
+ while (errno = 0, dirent = readdir(dir)) { /* sic */
+ checkInterrupt();
+ // We don't care if we hit non-hash files, anything goes
+ inodeHash.insert(dirent->d_ino);
+ }
+ if (errno) throw SysError(format("reading directory `%1%'") % linksDir);
+
+ printMsg(lvlTalkative, format("loaded %1% hash inodes") % inodeHash.size());
+
+ return inodeHash;
+}
+
+
+Strings LocalStore::readDirectoryIgnoringInodes(const Path & path, const InodeHash & inodeHash)
+{
+ Strings names;
+
+ AutoCloseDir dir = opendir(path.c_str());
+ if (!dir) throw SysError(format("opening directory `%1%'") % path);
+
+ struct dirent * dirent;
+ while (errno = 0, dirent = readdir(dir)) { /* sic */
+ checkInterrupt();
+
+ if (inodeHash.count(dirent->d_ino)) {
+ printMsg(lvlDebug, format("`%1%' is already linked") % dirent->d_name);
+ continue;
+ }
+
+ string name = dirent->d_name;
+ if (name == "." || name == "..") continue;
+ names.push_back(name);
+ }
+ if (errno) throw SysError(format("reading directory `%1%'") % path);
+
+ return names;
+}
+
+
+void LocalStore::optimisePath_(OptimiseStats & stats, const Path & path, InodeHash & inodeHash)
{
checkInterrupt();
-
+
struct stat st;
if (lstat(path.c_str(), &st))
throw SysError(format("getting attributes of path `%1%'") % path);
if (S_ISDIR(st.st_mode)) {
- Strings names = readDirectory(path);
+ Strings names = readDirectoryIgnoringInodes(path, inodeHash);
foreach (Strings::iterator, i, names)
- optimisePath_(stats, path + "/" + *i);
+ optimisePath_(stats, path + "/" + *i, inodeHash);
return;
}
@@ -71,6 +119,12 @@ void LocalStore::optimisePath_(OptimiseStats & stats, const Path & path)
return;
}
+ /* This can still happen on top-level files */
+ if (st.st_nlink > 1 && inodeHash.count(st.st_ino)) {
+ printMsg(lvlDebug, format("`%1%' is already linked, with %2% other file(s).") % path % (st.st_nlink - 2));
+ return;
+ }
+
/* Hash the file. Note that hashPath() returns the hash over the
NAR serialisation, which includes the execute bit on the file.
Thus, executable and non-executable files with the same
@@ -81,7 +135,6 @@ void LocalStore::optimisePath_(OptimiseStats & stats, const Path & path)
contents of the symlink (i.e. the result of readlink()), not
the contents of the target (which may not even exist). */
Hash hash = hashPath(htSHA256, path).first;
- stats.totalFiles++;
printMsg(lvlDebug, format("`%1%' has hash `%2%'") % path % printHash(hash));
/* Check if this is a known hash. */
@@ -89,7 +142,10 @@ void LocalStore::optimisePath_(OptimiseStats & stats, const Path & path)
if (!pathExists(linkPath)) {
/* Nope, create a hard link in the links directory. */
- if (link(path.c_str(), linkPath.c_str()) == 0) return;
+ if (link(path.c_str(), linkPath.c_str()) == 0) {
+ inodeHash.insert(st.st_ino);
+ return;
+ }
if (errno != EEXIST)
throw SysError(format("cannot link `%1%' to `%2%'") % linkPath % path);
/* Fall through if another process created ‘linkPath’ before
@@ -102,7 +158,6 @@ void LocalStore::optimisePath_(OptimiseStats & stats, const Path & path)
if (lstat(linkPath.c_str(), &stLink))
throw SysError(format("getting attributes of path `%1%'") % linkPath);
- stats.sameContents++;
if (st.st_ino == stLink.st_ino) {
printMsg(lvlDebug, format("`%1%' is already linked to `%2%'") % path % linkPath);
return;
@@ -160,12 +215,13 @@ void LocalStore::optimisePath_(OptimiseStats & stats, const Path & path)
void LocalStore::optimiseStore(OptimiseStats & stats)
{
PathSet paths = queryAllValidPaths();
+ InodeHash inodeHash = loadInodeHash();
foreach (PathSet::iterator, i, paths) {
addTempRoot(*i);
if (!isValidPath(*i)) continue; /* path was GC'ed, probably */
startNest(nest, lvlChatty, format("hashing files in `%1%'") % *i);
- optimisePath_(stats, *i);
+ optimisePath_(stats, *i, inodeHash);
}
}
@@ -173,7 +229,9 @@ void LocalStore::optimiseStore(OptimiseStats & stats)
void LocalStore::optimisePath(const Path & path)
{
OptimiseStats stats;
- if (settings.autoOptimiseStore) optimisePath_(stats, path);
+ InodeHash inodeHash;
+
+ if (settings.autoOptimiseStore) optimisePath_(stats, path, inodeHash);
}