From 1df702d34733e69599a6ae21cb366348a2534b7d Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 1 Aug 2012 19:01:50 -0400 Subject: removeUnusedLinks(): Print stats on disk space saved by hard linking --- src/libstore/gc.cc | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) (limited to 'src/libstore') diff --git a/src/libstore/gc.cc b/src/libstore/gc.cc index 874efe4d32d9..a7547d079b31 100644 --- a/src/libstore/gc.cc +++ b/src/libstore/gc.cc @@ -581,6 +581,8 @@ void LocalStore::removeUnusedLinks() AutoCloseDir dir = opendir(linksDir.c_str()); if (!dir) throw SysError(format("opening directory `%1%'") % linksDir); + unsigned long long actualSize = 0, unsharedSize = 0; + struct dirent * dirent; while (errno = 0, dirent = readdir(dir)) { checkInterrupt(); @@ -592,13 +594,26 @@ void LocalStore::removeUnusedLinks() if (lstat(path.c_str(), &st) == -1) throw SysError(format("statting `%1%'") % path); - if (st.st_nlink != 1) continue; + if (st.st_nlink != 1) { + unsigned long long size = st.st_blocks * 512ULL; + actualSize += size; + unsharedSize += (st.st_nlink - 1) * size; + continue; + } printMsg(lvlTalkative, format("deleting unused link `%1%'") % path); if (unlink(path.c_str()) == -1) throw SysError(format("deleting `%1%'") % path); } + + struct stat st; + if (stat(linksDir.c_str(), &st) == -1) + throw SysError(format("statting `%1%'") % linksDir); + unsigned long long overhead = st.st_blocks * 512ULL; + + printMsg(lvlInfo, format("note: currently hard linking saves %.2f MiB") + % ((unsharedSize - actualSize - overhead) / (1024.0 * 1024.0))); } -- cgit 1.4.1 From 967d066d8e452e59507ebae7585d6f34a4edf687 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 1 Aug 2012 19:14:30 -0400 Subject: nix-store --gc: Make ‘--max-freed 0’ do the right thing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit That is, delete almost nothing (it will still remove unused links from /nix/store/.links). --- src/libstore/gc.cc | 4 ++-- src/libstore/store-api.cc | 4 ++-- src/libstore/store-api.hh | 3 +-- src/nix-store/nix-store.cc | 2 +- 4 files changed, 6 insertions(+), 7 deletions(-) (limited to 'src/libstore') diff --git a/src/libstore/gc.cc b/src/libstore/gc.cc index a7547d079b31..7753b3fe20c0 100644 --- a/src/libstore/gc.cc +++ b/src/libstore/gc.cc @@ -550,7 +550,7 @@ bool LocalStore::tryToDelete(GCState & state, const Path & path) } else deleteGarbage(state, path); - if (state.options.maxFreed && state.results.bytesFreed + state.bytesInvalidated > state.options.maxFreed) { + if (state.results.bytesFreed + state.bytesInvalidated > state.options.maxFreed) { printMsg(lvlInfo, format("deleted or invalidated more than %1% bytes; stopping") % state.options.maxFreed); throw GCLimitReached(); } @@ -675,7 +675,7 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results) throw Error(format("cannot delete path `%1%' since it is still alive") % *i); } - } else { + } else if (options.maxFreed > 0) { if (shouldDelete(state.options.action)) printMsg(lvlError, format("deleting garbage...")); diff --git a/src/libstore/store-api.cc b/src/libstore/store-api.cc index b64988268cdb..dac581e14e9e 100644 --- a/src/libstore/store-api.cc +++ b/src/libstore/store-api.cc @@ -2,7 +2,7 @@ #include "globals.hh" #include "util.hh" -#include +#include namespace nix { @@ -12,7 +12,7 @@ GCOptions::GCOptions() { action = gcDeleteDead; ignoreLiveness = false; - maxFreed = 0; + maxFreed = ULLONG_MAX; } diff --git a/src/libstore/store-api.hh b/src/libstore/store-api.hh index 9ba67852efec..c0fb50f23dd2 100644 --- a/src/libstore/store-api.hh +++ b/src/libstore/store-api.hh @@ -48,8 +48,7 @@ struct GCOptions /* For `gcDeleteSpecific', the paths to delete. */ PathSet pathsToDelete; - /* Stop after at least `maxFreed' bytes have been freed. 0 means - no limit. */ + /* Stop after at least `maxFreed' bytes have been freed. */ unsigned long long maxFreed; GCOptions(); diff --git a/src/nix-store/nix-store.cc b/src/nix-store/nix-store.cc index 82e08fecf22a..9a675fcd576a 100644 --- a/src/nix-store/nix-store.cc +++ b/src/nix-store/nix-store.cc @@ -583,7 +583,7 @@ static void opGC(Strings opFlags, Strings opArgs) else if (*i == "--delete") options.action = GCOptions::gcDeleteDead; else if (*i == "--max-freed") { long long maxFreed = getIntArg(*i, i, opFlags.end()); - options.maxFreed = maxFreed >= 1 ? maxFreed : 1; + options.maxFreed = maxFreed >= 0 ? maxFreed : 0; } else throw UsageError(format("bad sub-operation `%1%' in GC") % *i); -- cgit 1.4.1 From 01d56c1eeca497de247413a64a544605c53d9d41 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 1 Aug 2012 22:34:46 -0400 Subject: Drop the block count in the garbage collector --- src/libstore/build.cc | 11 +++++------ src/libstore/gc.cc | 5 ++--- src/libstore/local-store.hh | 3 +-- src/libstore/remote-store.cc | 2 +- src/libstore/store-api.hh | 4 ---- src/libutil/util.cc | 21 ++++++++------------- src/libutil/util.hh | 3 +-- src/nix-store/nix-store.cc | 9 ++++----- src/nix-worker/nix-worker.cc | 2 +- 9 files changed, 23 insertions(+), 37 deletions(-) (limited to 'src/libstore') diff --git a/src/libstore/build.cc b/src/libstore/build.cc index 290635695e05..91f235b7ab10 100644 --- a/src/libstore/build.cc +++ b/src/libstore/build.cc @@ -606,18 +606,17 @@ void getOwnership(const Path & path) } -void deletePathWrapped(const Path & path, - unsigned long long & bytesFreed, unsigned long long & blocksFreed) +void deletePathWrapped(const Path & path, unsigned long long & bytesFreed) { try { /* First try to delete it ourselves. */ - deletePath(path, bytesFreed, blocksFreed); + deletePath(path, bytesFreed); } catch (SysError & e) { /* If this failed due to a permission error, then try it with the setuid helper. */ if (haveBuildUsers() && !amPrivileged()) { getOwnership(path); - deletePath(path, bytesFreed, blocksFreed); + deletePath(path, bytesFreed); } else throw; } @@ -626,8 +625,8 @@ void deletePathWrapped(const Path & path, void deletePathWrapped(const Path & path) { - unsigned long long dummy1, dummy2; - deletePathWrapped(path, dummy1, dummy2); + unsigned long long dummy1; + deletePathWrapped(path, dummy1); } diff --git a/src/libstore/gc.cc b/src/libstore/gc.cc index 7753b3fe20c0..a1bb4051cd1b 100644 --- a/src/libstore/gc.cc +++ b/src/libstore/gc.cc @@ -425,10 +425,9 @@ bool LocalStore::isActiveTempFile(const GCState & state, void LocalStore::deleteGarbage(GCState & state, const Path & path) { printMsg(lvlInfo, format("deleting `%1%'") % path); - unsigned long long bytesFreed, blocksFreed; - deletePathWrapped(path, bytesFreed, blocksFreed); + unsigned long long bytesFreed; + deletePathWrapped(path, bytesFreed); state.results.bytesFreed += bytesFreed; - state.results.blocksFreed += blocksFreed; } diff --git a/src/libstore/local-store.hh b/src/libstore/local-store.hh index 50910f353ad1..721cc6afbed5 100644 --- a/src/libstore/local-store.hh +++ b/src/libstore/local-store.hh @@ -304,8 +304,7 @@ void getOwnership(const Path & path); /* Like deletePath(), but changes the ownership of `path' using the setuid wrapper if necessary (and possible). */ -void deletePathWrapped(const Path & path, - unsigned long long & bytesFreed, unsigned long long & blocksFreed); +void deletePathWrapped(const Path & path, unsigned long long & bytesFreed); void deletePathWrapped(const Path & path); diff --git a/src/libstore/remote-store.cc b/src/libstore/remote-store.cc index cbb70b2fd726..5e7f02749135 100644 --- a/src/libstore/remote-store.cc +++ b/src/libstore/remote-store.cc @@ -494,7 +494,7 @@ void RemoteStore::collectGarbage(const GCOptions & options, GCResults & results) results.paths = readStrings(from); results.bytesFreed = readLongLong(from); - results.blocksFreed = readLongLong(from); + readLongLong(from); // obsolete } diff --git a/src/libstore/store-api.hh b/src/libstore/store-api.hh index c0fb50f23dd2..79ae0170d700 100644 --- a/src/libstore/store-api.hh +++ b/src/libstore/store-api.hh @@ -65,13 +65,9 @@ struct GCResults number of bytes that would be or was freed. */ unsigned long long bytesFreed; - /* The number of file system blocks that would be or was freed. */ - unsigned long long blocksFreed; - GCResults() { bytesFreed = 0; - blocksFreed = 0; } }; diff --git a/src/libutil/util.cc b/src/libutil/util.cc index 689fc543af31..9d8e4afed37d 100644 --- a/src/libutil/util.cc +++ b/src/libutil/util.cc @@ -297,8 +297,7 @@ void computePathSize(const Path & path, } -static void _deletePath(const Path & path, unsigned long long & bytesFreed, - unsigned long long & blocksFreed) +static void _deletePath(const Path & path, unsigned long long & bytesFreed) { checkInterrupt(); @@ -308,10 +307,8 @@ static void _deletePath(const Path & path, unsigned long long & bytesFreed, if (S_ISDIR(st.st_mode) || S_ISREG(st.st_mode)) makeMutable(path); - if (!S_ISDIR(st.st_mode) && st.st_nlink == 1) { - bytesFreed += st.st_size; - blocksFreed += st.st_blocks; - } + if (!S_ISDIR(st.st_mode) && st.st_nlink == 1) + bytesFreed += st.st_blocks * 512; if (S_ISDIR(st.st_mode)) { Strings names = readDirectory(path); @@ -323,7 +320,7 @@ static void _deletePath(const Path & path, unsigned long long & bytesFreed, } for (Strings::iterator i = names.begin(); i != names.end(); ++i) - _deletePath(path + "/" + *i, bytesFreed, blocksFreed); + _deletePath(path + "/" + *i, bytesFreed); } if (remove(path.c_str()) == -1) @@ -333,19 +330,17 @@ static void _deletePath(const Path & path, unsigned long long & bytesFreed, void deletePath(const Path & path) { - unsigned long long dummy1, dummy2; - deletePath(path, dummy1, dummy2); + unsigned long long dummy; + deletePath(path, dummy); } -void deletePath(const Path & path, unsigned long long & bytesFreed, - unsigned long long & blocksFreed) +void deletePath(const Path & path, unsigned long long & bytesFreed) { startNest(nest, lvlDebug, format("recursively deleting path `%1%'") % path); bytesFreed = 0; - blocksFreed = 0; - _deletePath(path, bytesFreed, blocksFreed); + _deletePath(path, bytesFreed); } diff --git a/src/libutil/util.hh b/src/libutil/util.hh index 9b8656f70485..edb7c0fa6c60 100644 --- a/src/libutil/util.hh +++ b/src/libutil/util.hh @@ -80,8 +80,7 @@ void computePathSize(const Path & path, returns the number of bytes and blocks freed. */ void deletePath(const Path & path); -void deletePath(const Path & path, unsigned long long & bytesFreed, - unsigned long long & blocksFreed); +void deletePath(const Path & path, unsigned long long & bytesFreed); /* Make a path read-only recursively. */ void makePathReadOnly(const Path & path); diff --git a/src/nix-store/nix-store.cc b/src/nix-store/nix-store.cc index 9a675fcd576a..c182dbe49c7a 100644 --- a/src/nix-store/nix-store.cc +++ b/src/nix-store/nix-store.cc @@ -544,10 +544,9 @@ static void opCheckValidity(Strings opFlags, Strings opArgs) } -static string showBytes(unsigned long long bytes, unsigned long long blocks) +static string showBytes(unsigned long long bytes) { - return (format("%d bytes (%.2f MiB, %d blocks)") - % bytes % (bytes / (1024.0 * 1024.0)) % blocks).str(); + return (format("%.2f MiB") % (bytes / (1024.0 * 1024.0))).str(); } @@ -562,7 +561,7 @@ struct PrintFreed if (show) cout << format("%1% store paths deleted, %2% freed\n") % results.paths.size() - % showBytes(results.bytesFreed, results.blocksFreed); + % showBytes(results.bytesFreed); } }; @@ -735,7 +734,7 @@ static void showOptimiseStats(OptimiseStats & stats) { printMsg(lvlError, format("%1% freed by hard-linking %2% files; there are %3% files with equal contents out of %4% files in total") - % showBytes(stats.bytesFreed, stats.blocksFreed) + % showBytes(stats.bytesFreed) % stats.filesLinked % stats.sameContents % stats.totalFiles); diff --git a/src/nix-worker/nix-worker.cc b/src/nix-worker/nix-worker.cc index 74a619c71d0a..80c0d50609c7 100644 --- a/src/nix-worker/nix-worker.cc +++ b/src/nix-worker/nix-worker.cc @@ -503,7 +503,7 @@ static void performOp(unsigned int clientVersion, writeStrings(results.paths, to); writeLongLong(results.bytesFreed, to); - writeLongLong(results.blocksFreed, to); + writeLongLong(0, to); // obsolete break; } -- cgit 1.4.1 From 6763084ae53fc0228d50ab94bbbced89c1b14f1c Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 1 Aug 2012 22:43:03 -0400 Subject: Count bytes freed deleting unused links --- src/libstore/gc.cc | 6 ++++-- src/libstore/local-store.hh | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) (limited to 'src/libstore') diff --git a/src/libstore/gc.cc b/src/libstore/gc.cc index a1bb4051cd1b..255dc3aa099f 100644 --- a/src/libstore/gc.cc +++ b/src/libstore/gc.cc @@ -575,7 +575,7 @@ bool LocalStore::tryToDelete(GCState & state, const Path & path) safely deleted. FIXME: race condition with optimisePath(): we might see a link count of 1 just before optimisePath() increases the link count. */ -void LocalStore::removeUnusedLinks() +void LocalStore::removeUnusedLinks(const GCState & state) { AutoCloseDir dir = opendir(linksDir.c_str()); if (!dir) throw SysError(format("opening directory `%1%'") % linksDir); @@ -604,6 +604,8 @@ void LocalStore::removeUnusedLinks() if (unlink(path.c_str()) == -1) throw SysError(format("deleting `%1%'") % path); + + state.results.bytesFreed += st.st_blocks * 512; } struct stat st; @@ -732,7 +734,7 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results) /* Clean up the links directory. */ printMsg(lvlError, format("deleting unused links...")); - removeUnusedLinks(); + removeUnusedLinks(state); } diff --git a/src/libstore/local-store.hh b/src/libstore/local-store.hh index 721cc6afbed5..6a5e3641c4eb 100644 --- a/src/libstore/local-store.hh +++ b/src/libstore/local-store.hh @@ -264,7 +264,7 @@ private: int openGCLock(LockType lockType); - void removeUnusedLinks(); + void removeUnusedLinks(const GCState & state); void startSubstituter(const Path & substituter, RunningSubstituter & runningSubstituter); -- cgit 1.4.1 From 108e14bb189fd0fb291d3494f9f3915070a7052e Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Sun, 5 Aug 2012 18:17:55 -0400 Subject: Fix race condition when two processes create the same link in /nix/store/.links --- src/libstore/optimise-store.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src/libstore') diff --git a/src/libstore/optimise-store.cc b/src/libstore/optimise-store.cc index c05447f4a27f..b9b878d2a45b 100644 --- a/src/libstore/optimise-store.cc +++ b/src/libstore/optimise-store.cc @@ -102,11 +102,11 @@ void LocalStore::optimisePath_(OptimiseStats & stats, const Path & path) /* Nope, create a hard link in the links directory. */ makeMutable(path); MakeImmutable mk1(path); - - if (link(path.c_str(), linkPath.c_str()) == -1) + if (link(path.c_str(), linkPath.c_str()) == 0) return; + if (errno != EEXIST) throw SysError(format("cannot link `%1%' to `%2%'") % linkPath % path); - - return; + /* Fall through if another process created ‘linkPath’ before + we did. */ } /* Yes! We've seen a file with the same contents. Replace the -- cgit 1.4.1 From b6c989b80198badf5f694340c07abc282365aaec Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Sun, 5 Aug 2012 21:41:44 -0400 Subject: Fix race condition when two processes create a hard link to a file in .links This is a problem because one process may set the immutable bit before the second process has created its link. Addressed random Hydra failures such as: error: cannot rename `/nix/store/.tmp-link-17397-1804289383' to `/nix/store/rsvzm574rlfip3830ac7kmaa028bzl6h-nixos-0.1pre-git/upstart-interface-version': Operation not permitted --- src/libstore/optimise-store.cc | 64 ++++++++++++++++++++++++------------------ 1 file changed, 37 insertions(+), 27 deletions(-) (limited to 'src/libstore') diff --git a/src/libstore/optimise-store.cc b/src/libstore/optimise-store.cc index b9b878d2a45b..35c8dd48f2b6 100644 --- a/src/libstore/optimise-store.cc +++ b/src/libstore/optimise-store.cc @@ -123,9 +123,6 @@ void LocalStore::optimisePath_(OptimiseStats & stats, const Path & path) printMsg(lvlTalkative, format("linking `%1%' to `%2%'") % path % linkPath); - Path tempLink = (format("%1%/.tmp-link-%2%-%3%") - % nixStore % getpid() % rand()).str(); - /* Make the containing directory writable, but only if it's not the store itself (we don't want or need to mess with its permissions). */ @@ -140,40 +137,53 @@ void LocalStore::optimisePath_(OptimiseStats & stats, const Path & path) so make it mutable first (and make it immutable again when we're done). We also have to make ‘path’ mutable, otherwise rename() will fail to delete it. */ - makeMutable(linkPath); - MakeImmutable mk1(linkPath); - makeMutable(path); MakeImmutable mk2(path); - if (link(linkPath.c_str(), tempLink.c_str()) == -1) { - if (errno == EMLINK) { - /* Too many links to the same file (>= 32000 on most file - systems). This is likely to happen with empty files. - Just shrug and ignore. */ - printMsg(lvlInfo, format("`%1%' has maximum number of links") % linkPath); - return; + /* Another process might be doing the same thing (creating a new + link to ‘linkPath’) and make ‘linkPath’ immutable before we're + done. In that case, just retry. */ + unsigned int retries = 1024; + while (--retries > 0) { + makeMutable(linkPath); + MakeImmutable mk1(linkPath); + + Path tempLink = (format("%1%/.tmp-link-%2%-%3%") + % nixStore % getpid() % rand()).str(); + + if (link(linkPath.c_str(), tempLink.c_str()) == -1) { + if (errno == EMLINK) { + /* Too many links to the same file (>= 32000 on most + file systems). This is likely to happen with empty + files. Just shrug and ignore. */ + printMsg(lvlInfo, format("`%1%' has maximum number of links") % linkPath); + return; + } + if (errno == EPERM) continue; + throw SysError(format("cannot link `%1%' to `%2%'") % tempLink % linkPath); } - throw SysError(format("cannot link `%1%' to `%2%'") % tempLink % linkPath); - } - /* Atomically replace the old file with the new hard link. */ - if (rename(tempLink.c_str(), path.c_str()) == -1) { - if (errno == EMLINK) { - /* Some filesystems generate too many links on the rename, - rather than on the original link. (Probably it - temporarily increases the st_nlink field before - decreasing it again.) */ - printMsg(lvlInfo, format("`%1%' has maximum number of links") % linkPath); - - /* Unlink the temp link. */ + /* Atomically replace the old file with the new hard link. */ + if (rename(tempLink.c_str(), path.c_str()) == -1) { if (unlink(tempLink.c_str()) == -1) printMsg(lvlError, format("unable to unlink `%1%'") % tempLink); - return; + if (errno == EMLINK) { + /* Some filesystems generate too many links on the + rename, rather than on the original link. + (Probably it temporarily increases the st_nlink + field before decreasing it again.) */ + printMsg(lvlInfo, format("`%1%' has maximum number of links") % linkPath); + return; + } + if (errno == EPERM) continue; + throw SysError(format("cannot rename `%1%' to `%2%'") % tempLink % path); } - throw SysError(format("cannot rename `%1%' to `%2%'") % tempLink % path); + + break; } + if (retries == 0) throw Error(format("cannot link `%1%' to `%2%'") % path % linkPath); + stats.filesLinked++; stats.bytesFreed += st.st_size; stats.blocksFreed += st.st_blocks; -- cgit 1.4.1 From d025142f529731f05868f5397f5617011963c8b4 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Sun, 5 Aug 2012 21:45:27 -0400 Subject: Handle amount of disk space saved by hard linking being negative Fixes bogus messages like "currently hard linking saves 17592186044416.00 MiB". --- src/libstore/gc.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/libstore') diff --git a/src/libstore/gc.cc b/src/libstore/gc.cc index 255dc3aa099f..406fce672e3b 100644 --- a/src/libstore/gc.cc +++ b/src/libstore/gc.cc @@ -580,7 +580,7 @@ void LocalStore::removeUnusedLinks(const GCState & state) AutoCloseDir dir = opendir(linksDir.c_str()); if (!dir) throw SysError(format("opening directory `%1%'") % linksDir); - unsigned long long actualSize = 0, unsharedSize = 0; + long long actualSize = 0, unsharedSize = 0; struct dirent * dirent; while (errno = 0, dirent = readdir(dir)) { @@ -611,7 +611,7 @@ void LocalStore::removeUnusedLinks(const GCState & state) struct stat st; if (stat(linksDir.c_str(), &st) == -1) throw SysError(format("statting `%1%'") % linksDir); - unsigned long long overhead = st.st_blocks * 512ULL; + long long overhead = st.st_blocks * 512ULL; printMsg(lvlInfo, format("note: currently hard linking saves %.2f MiB") % ((unsharedSize - actualSize - overhead) / (1024.0 * 1024.0))); -- cgit 1.4.1 From 325d1cfebf6c8ad391dc318f984feb3e5731aa5a Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 7 Aug 2012 16:22:54 -0400 Subject: Don't warn about maximum link count exceeded on 0-byte files --- src/libstore/optimise-store.cc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'src/libstore') diff --git a/src/libstore/optimise-store.cc b/src/libstore/optimise-store.cc index 35c8dd48f2b6..137547a8aa79 100644 --- a/src/libstore/optimise-store.cc +++ b/src/libstore/optimise-store.cc @@ -156,7 +156,8 @@ void LocalStore::optimisePath_(OptimiseStats & stats, const Path & path) /* Too many links to the same file (>= 32000 on most file systems). This is likely to happen with empty files. Just shrug and ignore. */ - printMsg(lvlInfo, format("`%1%' has maximum number of links") % linkPath); + if (st.st_size) + printMsg(lvlInfo, format("`%1%' has maximum number of links") % linkPath); return; } if (errno == EPERM) continue; @@ -172,7 +173,8 @@ void LocalStore::optimisePath_(OptimiseStats & stats, const Path & path) rename, rather than on the original link. (Probably it temporarily increases the st_nlink field before decreasing it again.) */ - printMsg(lvlInfo, format("`%1%' has maximum number of links") % linkPath); + if (st.st_size) + printMsg(lvlInfo, format("`%1%' has maximum number of links") % linkPath); return; } if (errno == EPERM) continue; -- cgit 1.4.1 From 862c4c5ec509e05815d99fb4b80558974148b8c5 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Sun, 19 Aug 2012 16:32:42 -0400 Subject: Fix 1755 permission on temporary directories left behind by ‘-K’ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/libstore/build.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src/libstore') diff --git a/src/libstore/build.cc b/src/libstore/build.cc index 91f235b7ab10..a7aea164c0a4 100644 --- a/src/libstore/build.cc +++ b/src/libstore/build.cc @@ -1471,9 +1471,9 @@ HookReply DerivationGoal::tryBuildHook() } -void chmod(const Path & path, mode_t mode) +void chmod_(const Path & path, mode_t mode) { - if (::chmod(path.c_str(), 01777) == -1) + if (chmod(path.c_str(), mode) == -1) throw SysError(format("setting permissions on `%1%'") % path); } @@ -1675,7 +1675,7 @@ void DerivationGoal::startBuilder() instead.) */ Path chrootTmpDir = chrootRootDir + "/tmp"; createDirs(chrootTmpDir); - chmod(chrootTmpDir, 01777); + chmod_(chrootTmpDir, 01777); /* Create a /etc/passwd with entries for the build user and the nobody account. The latter is kind of a hack to support @@ -1719,7 +1719,7 @@ void DerivationGoal::startBuilder() precaution, make the fake Nix store only writable by the build user. */ createDirs(chrootRootDir + nixStore); - chmod(chrootRootDir + nixStore, 01777); + chmod_(chrootRootDir + nixStore, 01777); foreach (PathSet::iterator, i, inputPaths) { struct stat st; -- cgit 1.4.1 From f0eab0636b73a4f16b7639d30956d9072d5573cb Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 20 Aug 2012 15:27:00 -0400 Subject: Don't bind-mount /proc since we mount our own --- src/libstore/build.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/libstore') diff --git a/src/libstore/build.cc b/src/libstore/build.cc index a7aea164c0a4..9da8084bf5c7 100644 --- a/src/libstore/build.cc +++ b/src/libstore/build.cc @@ -1859,16 +1859,16 @@ void DerivationGoal::initChild() foreach (PathSet::iterator, i, dirsInChroot) { Path source = *i; Path target = chrootRootDir + source; + if (source == "/proc") continue; // backwards compatibility debug(format("bind mounting `%1%' to `%2%'") % source % target); - createDirs(target); - if (mount(source.c_str(), target.c_str(), "", MS_BIND, 0) == -1) throw SysError(format("bind mount from `%1%' to `%2%' failed") % source % target); } /* Bind a new instance of procfs on /proc to reflect our private PID namespace. */ + createDirs(chrootRootDir + "/proc"); if (mount("none", (chrootRootDir + "/proc").c_str(), "proc", 0, 0) == -1) throw SysError("mounting /proc"); -- cgit 1.4.1 From 56e30e161cd309addb5aa95ba02a8d3371846228 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 20 Aug 2012 15:27:30 -0400 Subject: In the chroot, make all mounted filesystems private This is required on systemd, which mounts filesystems as "shared" subtrees. Changes to shared trees in a private mount namespace are propagated to the outside world, which is bad. --- src/libstore/build.cc | 18 ++++++++++++++++++ src/libutil/util.cc | 4 ++-- src/libutil/util.hh | 2 +- 3 files changed, 21 insertions(+), 3 deletions(-) (limited to 'src/libstore') diff --git a/src/libstore/build.cc b/src/libstore/build.cc index 9da8084bf5c7..0ed69614b80b 100644 --- a/src/libstore/build.cc +++ b/src/libstore/build.cc @@ -1853,6 +1853,24 @@ void DerivationGoal::initChild() char domainname[] = "(none)"; // kernel default setdomainname(domainname, sizeof(domainname)); + /* Make all filesystems private. This is necessary + because subtrees may have been mounted as "shared" + (MS_SHARED). (Systemd does this, for instance.) Even + though we have a private mount namespace, mounting + filesystems on top of a shared subtree still propagates + outside of the namespace. Making a subtree private is + local to the namespace, though, so setting MS_PRIVATE + does not affect the outside world. */ + Strings mounts = tokenizeString(readFile("/proc/self/mountinfo", true), "\n"); + foreach (Strings::iterator, i, mounts) { + Strings fields = tokenizeString(*i, " "); + assert(fields.size() >= 5); + Strings::iterator j = fields.begin(); + std::advance(j, 4); + if (mount(0, j->c_str(), 0, MS_PRIVATE, 0) == -1) + throw SysError(format("unable to make filesystem `%1%' private") % *j); + } + /* Bind-mount all the directories from the "host" filesystem that we want in the chroot environment. */ diff --git a/src/libutil/util.cc b/src/libutil/util.cc index 9d8e4afed37d..fe4fedfa597d 100644 --- a/src/libutil/util.cc +++ b/src/libutil/util.cc @@ -224,12 +224,12 @@ string readFile(int fd) } -string readFile(const Path & path) +string readFile(const Path & path, bool drain) { AutoCloseFD fd = open(path.c_str(), O_RDONLY); if (fd == -1) throw SysError(format("opening file `%1%'") % path); - return readFile(fd); + return drain ? drainFD(fd) : readFile(fd); } diff --git a/src/libutil/util.hh b/src/libutil/util.hh index dc38a53ca2fe..22992bbafee7 100644 --- a/src/libutil/util.hh +++ b/src/libutil/util.hh @@ -63,7 +63,7 @@ Strings readDirectory(const Path & path); /* Read the contents of a file into a string. */ string readFile(int fd); -string readFile(const Path & path); +string readFile(const Path & path, bool drain = false); /* Write a string to a file. */ void writeFile(const Path & path, const string & s); -- cgit 1.4.1 From d950cfe70b2b70e938ece672dbccedfd4413c295 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 20 Aug 2012 15:55:49 -0400 Subject: Check if MS_PRIVATE is defined http://hydra.nixos.org/build/2955671 --- src/libstore/build.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/libstore') diff --git a/src/libstore/build.cc b/src/libstore/build.cc index 0ed69614b80b..a77644054883 100644 --- a/src/libstore/build.cc +++ b/src/libstore/build.cc @@ -45,7 +45,7 @@ #include #endif -#define CHROOT_ENABLED HAVE_CHROOT && HAVE_UNSHARE && HAVE_SYS_MOUNT_H && defined(MS_BIND) && defined(CLONE_NEWNS) +#define CHROOT_ENABLED HAVE_CHROOT && HAVE_UNSHARE && HAVE_SYS_MOUNT_H && defined(MS_BIND) && defined(MS_PRIVATE) && defined(CLONE_NEWNS) #if CHROOT_ENABLED #include -- cgit 1.4.1