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:
		
							parent
							
								
									25a00cae5b
								
							
						
					
					
						commit
						aeb810b01e
					
				
					 5 changed files with 54 additions and 48 deletions
				
			
		|  | @ -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 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -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); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -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()] == '/'; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -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; | ||||||
|  |  | ||||||
|  | @ -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); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue