* Fix a huge gaping hole in nix-env w.r.t. the garbage collector.
Nix-env failed to call addPermRoot(), which is necessary to safely add a new root. So if nix-env started after and finished before the garbage collector, the user environment (plus all other new stuff) it built might be garbage collected, leading to a dangling symlink chain in ~/.nix-profile... * Be more explicit if we block on the GC lock ("waiting for the big garbage collector lock..."). * Don't loop trying to create a new generation. It's not necessary anymore since profiles are locked nowadays.
This commit is contained in:
parent
f00bc4c94c
commit
5c38c863bd
3 changed files with 26 additions and 22 deletions
|
@ -44,7 +44,10 @@ static int openGCLock(LockType lockType)
|
||||||
if (fdGCLock == -1)
|
if (fdGCLock == -1)
|
||||||
throw SysError(format("opening global GC lock `%1%'") % fnGCLock);
|
throw SysError(format("opening global GC lock `%1%'") % fnGCLock);
|
||||||
|
|
||||||
lockFile(fdGCLock, lockType, true);
|
if (!lockFile(fdGCLock, lockType, false)) {
|
||||||
|
printMsg(lvlError, format("waiting for the big garbage collector lock..."));
|
||||||
|
lockFile(fdGCLock, lockType, true);
|
||||||
|
}
|
||||||
|
|
||||||
/* !!! Restrict read permission on the GC root. Otherwise any
|
/* !!! Restrict read permission on the GC root. Otherwise any
|
||||||
process that can open the file for reading can DoS the
|
process that can open the file for reading can DoS the
|
||||||
|
@ -74,7 +77,7 @@ void createSymlink(const Path & link, const Path & target, bool careful)
|
||||||
|
|
||||||
|
|
||||||
Path addPermRoot(const Path & _storePath, const Path & _gcRoot,
|
Path addPermRoot(const Path & _storePath, const Path & _gcRoot,
|
||||||
bool indirect)
|
bool indirect, bool allowOutsideRootsDir)
|
||||||
{
|
{
|
||||||
Path storePath(canonPath(_storePath));
|
Path storePath(canonPath(_storePath));
|
||||||
Path gcRoot(canonPath(_gcRoot));
|
Path gcRoot(canonPath(_gcRoot));
|
||||||
|
@ -97,13 +100,15 @@ Path addPermRoot(const Path & _storePath, const Path & _gcRoot,
|
||||||
}
|
}
|
||||||
|
|
||||||
else {
|
else {
|
||||||
Path rootsDir = canonPath((format("%1%/%2%") % nixStateDir % gcRootsDir).str());
|
if (!allowOutsideRootsDir) {
|
||||||
|
Path rootsDir = canonPath((format("%1%/%2%") % nixStateDir % gcRootsDir).str());
|
||||||
|
|
||||||
if (string(gcRoot, 0, rootsDir.size() + 1) != rootsDir + "/")
|
if (string(gcRoot, 0, rootsDir.size() + 1) != rootsDir + "/")
|
||||||
throw Error(format(
|
throw Error(format(
|
||||||
"path `%1%' is not a valid garbage collector root; "
|
"path `%1%' is not a valid garbage collector root; "
|
||||||
"it's not in the directory `%2%'")
|
"it's not in the directory `%2%'")
|
||||||
% gcRoot % rootsDir);
|
% gcRoot % rootsDir);
|
||||||
|
}
|
||||||
|
|
||||||
createSymlink(gcRoot, storePath, false);
|
createSymlink(gcRoot, storePath, false);
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,7 +39,7 @@ void removeTempRoots();
|
||||||
|
|
||||||
/* Register a permanent GC root. */
|
/* Register a permanent GC root. */
|
||||||
Path addPermRoot(const Path & storePath, const Path & gcRoot,
|
Path addPermRoot(const Path & storePath, const Path & gcRoot,
|
||||||
bool indirect);
|
bool indirect, bool allowOutsideRootsDir = false);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#include "profiles.hh"
|
#include "profiles.hh"
|
||||||
#include "util.hh"
|
#include "util.hh"
|
||||||
|
#include "gc.hh"
|
||||||
|
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
@ -82,19 +83,17 @@ Path createGeneration(Path profile, Path outPath)
|
||||||
Generations gens = findGenerations(profile, dummy);
|
Generations gens = findGenerations(profile, dummy);
|
||||||
unsigned int num = gens.size() > 0 ? gens.front().number : 0;
|
unsigned int num = gens.size() > 0 ? gens.front().number : 0;
|
||||||
|
|
||||||
/* Create the new generation. */
|
/* Create the new generation. Note that addPermRoot() blocks if
|
||||||
Path outLink;
|
the garbage collector is running to prevent the stuff we've
|
||||||
|
build from moving from the temporary roots (which the GC knows)
|
||||||
|
to the permanent roots (of which the GC would have a stale
|
||||||
|
view). If we didn't do it this way, the GC might remove the
|
||||||
|
user environment etc. we've just built. */
|
||||||
|
Path generation;
|
||||||
|
makeName(profile, num + 1, generation);
|
||||||
|
addPermRoot(outPath, generation, false, true);
|
||||||
|
|
||||||
while (1) {
|
return generation;
|
||||||
makeName(profile, num, outLink);
|
|
||||||
if (symlink(outPath.c_str(), outLink.c_str()) == 0) break;
|
|
||||||
if (errno != EEXIST)
|
|
||||||
throw SysError(format("creating symlink `%1%'") % outLink);
|
|
||||||
/* Somebody beat us to it, retry with a higher number. */
|
|
||||||
num++;
|
|
||||||
}
|
|
||||||
|
|
||||||
return outLink;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue