Garbage collector: Don't follow symlinks arbitrarily

Only indirect roots (symlinks to symlinks to the Nix store) are now
supported.
This commit is contained in:
Eelco Dolstra 2013-07-12 14:01:25 +02:00
parent 25a00cae5b
commit aeb810b01e
5 changed files with 54 additions and 48 deletions

View file

@ -27,11 +27,9 @@ init-state:
$(INSTALL) $(INIT_FLAGS) -d $(DESTDIR)$(localstatedir)/nix/profiles $(INSTALL) $(INIT_FLAGS) -d $(DESTDIR)$(localstatedir)/nix/profiles
$(INSTALL) $(INIT_FLAGS) -d $(DESTDIR)$(localstatedir)/nix/gcroots $(INSTALL) $(INIT_FLAGS) -d $(DESTDIR)$(localstatedir)/nix/gcroots
$(INSTALL) $(INIT_FLAGS) -d $(DESTDIR)$(localstatedir)/nix/temproots $(INSTALL) $(INIT_FLAGS) -d $(DESTDIR)$(localstatedir)/nix/temproots
ln -sfn $(localstatedir)/nix/profiles $(DESTDIR)$(localstatedir)/nix/gcroots/profiles
$(INSTALL) $(INIT_FLAGS) -d $(DESTDIR)$(localstatedir)/nix/userpool $(INSTALL) $(INIT_FLAGS) -d $(DESTDIR)$(localstatedir)/nix/userpool
-$(INSTALL) $(INIT_FLAGS) -d $(DESTDIR)$(storedir) -$(INSTALL) $(INIT_FLAGS) -d $(DESTDIR)$(storedir)
$(INSTALL) $(INIT_FLAGS) $(GROUP_WRITABLE) -d $(DESTDIR)$(localstatedir)/nix/manifests $(INSTALL) $(INIT_FLAGS) $(GROUP_WRITABLE) -d $(DESTDIR)$(localstatedir)/nix/manifests
ln -sfn $(localstatedir)/nix/manifests $(DESTDIR)$(localstatedir)/nix/gcroots/manifests
else else

View file

@ -258,8 +258,7 @@ static void readTempRoots(PathSet & tempRoots, FDs & fds)
only succeed if the owning process has died. In that case only succeed if the owning process has died. In that case
we don't care about its temporary roots. */ we don't care about its temporary roots. */
if (lockFile(*fd, ltWrite, false)) { if (lockFile(*fd, ltWrite, false)) {
printMsg(lvlError, format("removing stale temporary roots file `%1%'") printMsg(lvlError, format("removing stale temporary roots file `%1%'") % path);
% path);
unlink(path.c_str()); unlink(path.c_str());
writeFull(*fd, (const unsigned char *) "d", 1); writeFull(*fd, (const unsigned char *) "d", 1);
continue; continue;
@ -290,46 +289,47 @@ static void readTempRoots(PathSet & tempRoots, FDs & fds)
} }
static void findRoots(StoreAPI & store, const Path & path, static void foundRoot(StoreAPI & store,
bool recurseSymlinks, bool deleteStale, Roots & roots) const Path & path, const Path & target, Roots & roots)
{
Path storePath = toStorePath(target);
if (store.isValidPath(storePath))
roots[path] = storePath;
else
printMsg(lvlInfo, format("skipping invalid root from `%1%' to `%2%'") % path % storePath);
}
static void findRoots(StoreAPI & store, const Path & path, Roots & roots)
{ {
try { try {
struct stat st; struct stat st = lstat(path);
if (lstat(path.c_str(), &st) == -1)
throw SysError(format("statting `%1%'") % path);
printMsg(lvlVomit, format("looking at `%1%'") % path);
if (S_ISDIR(st.st_mode)) { if (S_ISDIR(st.st_mode)) {
Strings names = readDirectory(path); Strings names = readDirectory(path);
foreach (Strings::iterator, i, names) foreach (Strings::iterator, i, names)
findRoots(store, path + "/" + *i, recurseSymlinks, deleteStale, roots); findRoots(store, path + "/" + *i, roots);
} }
else if (S_ISLNK(st.st_mode)) { else if (S_ISLNK(st.st_mode)) {
Path target = absPath(readLink(path), dirOf(path)); Path target = readLink(path);
if (isInStore(target))
foundRoot(store, path, target, roots);
if (isInStore(target)) { /* Handle indirect roots. */
debug(format("found root `%1%' in `%2%'") else {
% target % path); target = absPath(target, dirOf(path));
Path storePath = toStorePath(target); if (!pathExists(target)) {
if (store.isValidPath(storePath)) if (isInDir(path, settings.nixStateDir + "/" + gcRootsDir + "/auto")) {
roots[path] = storePath; printMsg(lvlInfo, format("removing stale link from `%1%' to `%2%'") % path % target);
else unlink(path.c_str());
printMsg(lvlInfo, format("skipping invalid root from `%1%' to `%2%'") }
% path % storePath); } else {
} struct stat st2 = lstat(target);
if (!S_ISLNK(st2.st_mode)) return;
else if (recurseSymlinks) { Path target2 = readLink(target);
if (pathExists(target)) if (isInStore(target2)) foundRoot(store, path, target2, roots);
findRoots(store, target, false, deleteStale, roots);
else if (deleteStale) {
printMsg(lvlInfo, format("removing stale link from `%1%' to `%2%'") % path % target);
/* Note that we only delete when recursing, i.e.,
when we are still in the `gcroots' tree. We
never delete stuff outside that tree. */
unlink(path.c_str());
} }
} }
} }
@ -346,18 +346,16 @@ static void findRoots(StoreAPI & store, const Path & path,
} }
static Roots findRoots(StoreAPI & store, bool deleteStale)
{
Roots roots;
Path rootsDir = canonPath((format("%1%/%2%") % settings.nixStateDir % gcRootsDir).str());
findRoots(store, rootsDir, true, deleteStale, roots);
return roots;
}
Roots LocalStore::findRoots() Roots LocalStore::findRoots()
{ {
return nix::findRoots(*this, false); Roots roots;
/* Process direct roots in {gcroots,manifests,profiles}. */
nix::findRoots(*this, settings.nixStateDir + "/" + gcRootsDir, roots);
nix::findRoots(*this, settings.nixStateDir + "/manifests", roots);
nix::findRoots(*this, settings.nixStateDir + "/profiles", roots);
return roots;
} }
@ -637,7 +635,7 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
/* Find the roots. Since we've grabbed the GC lock, the set of /* Find the roots. Since we've grabbed the GC lock, the set of
permanent roots cannot increase now. */ permanent roots cannot increase now. */
printMsg(lvlError, format("finding garbage collector roots...")); printMsg(lvlError, format("finding garbage collector roots..."));
Roots rootMap = options.ignoreLiveness ? Roots() : nix::findRoots(*this, true); Roots rootMap = options.ignoreLiveness ? Roots() : findRoots();
foreach (Roots::iterator, i, rootMap) state.roots.insert(i->second); foreach (Roots::iterator, i, rootMap) state.roots.insert(i->second);

View file

@ -18,10 +18,7 @@ GCOptions::GCOptions()
bool isInStore(const Path & path) bool isInStore(const Path & path)
{ {
return path[0] == '/' return isInDir(path, settings.nixStore);
&& string(path, 0, settings.nixStore.size()) == settings.nixStore
&& path.size() >= settings.nixStore.size() + 2
&& path[settings.nixStore.size()] == '/';
} }

View file

@ -148,6 +148,15 @@ string baseNameOf(const Path & path)
} }
bool isInDir(const Path & path, const Path & dir)
{
return path[0] == '/'
&& string(path, 0, dir.size()) == dir
&& path.size() >= dir.size() + 2
&& path[dir.size()] == '/';
}
struct stat lstat(const Path & path) struct stat lstat(const Path & path)
{ {
struct stat st; struct stat st;

View file

@ -45,6 +45,10 @@ Path dirOf(const Path & path);
following the final `/'. */ following the final `/'. */
string baseNameOf(const Path & path); string baseNameOf(const Path & path);
/* Check whether a given path is a descendant of the given
directory. */
bool isInDir(const Path & path, const Path & dir);
/* Get status of `path'. */ /* Get status of `path'. */
struct stat lstat(const Path & path); struct stat lstat(const Path & path);