* The determination of the root set should be made by the privileged
process, so forward the operation. * Spam the user about GC misconfigurations (NIX-71). * findRoots: skip all roots that are unreadable - the warnings with which we spam the user should be enough.
This commit is contained in:
		
							parent
							
								
									8623256f48
								
							
						
					
					
						commit
						29cf434a35
					
				
					 9 changed files with 140 additions and 79 deletions
				
			
		|  | @ -91,12 +91,6 @@ void LocalStore::addIndirectRoot(const Path & path) | |||
| } | ||||
| 
 | ||||
| 
 | ||||
| typedef std::map<Path, Path> Roots; | ||||
| 
 | ||||
| 
 | ||||
| static void findRoots(Roots & roots, bool ignoreUnreadable); | ||||
| 
 | ||||
| 
 | ||||
| Path addPermRoot(const Path & _storePath, const Path & _gcRoot, | ||||
|     bool indirect, bool allowOutsideRootsDir) | ||||
| { | ||||
|  | @ -122,16 +116,16 @@ Path addPermRoot(const Path & _storePath, const Path & _gcRoot, | |||
|              | ||||
|         createSymlink(gcRoot, storePath, false); | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     /* Check that the root can be found by the garbage collector. */ | ||||
|         Roots roots; | ||||
|         findRoots(roots, true); | ||||
|     Roots roots = store->findRoots(); | ||||
|     if (roots.find(gcRoot) == roots.end()) | ||||
|         printMsg(lvlError,  | ||||
|             format( | ||||
|                 "warning: the garbage collector does not find `%1%' as a root; " | ||||
|                 "therefore, `%2%' might be removed by the garbage collector") | ||||
|             % gcRoot % storePath); | ||||
|     } | ||||
|          | ||||
|     /* Grab the global GC root, causing us to block while a GC is in
 | ||||
|        progress.  This prevents the set of permanent roots from | ||||
|  | @ -308,8 +302,10 @@ static void readTempRoots(PathSet & tempRoots, FDs & fds) | |||
| 
 | ||||
| 
 | ||||
| static void findRoots(const Path & path, bool recurseSymlinks, | ||||
|     bool ignoreUnreadable, Roots & roots) | ||||
|     bool deleteStale, Roots & roots) | ||||
| { | ||||
|     try { | ||||
|          | ||||
|         struct stat st; | ||||
|         if (lstat(path.c_str(), &st) == -1) | ||||
|             throw SysError(format("statting `%1%'") % path); | ||||
|  | @ -319,7 +315,7 @@ static void findRoots(const Path & path, bool recurseSymlinks, | |||
|         if (S_ISDIR(st.st_mode)) { | ||||
|             Strings names = readDirectory(path); | ||||
|             for (Strings::iterator i = names.begin(); i != names.end(); ++i) | ||||
|             findRoots(path + "/" + *i, recurseSymlinks, ignoreUnreadable, roots); | ||||
|                 findRoots(path + "/" + *i, recurseSymlinks, deleteStale, roots); | ||||
|         } | ||||
| 
 | ||||
|         else if (S_ISLNK(st.st_mode)) { | ||||
|  | @ -337,29 +333,42 @@ static void findRoots(const Path & path, bool recurseSymlinks, | |||
|             } | ||||
| 
 | ||||
|             else if (recurseSymlinks) { | ||||
|             struct stat st2; | ||||
|             if (lstat(target.c_str(), &st2) == 0) | ||||
|                 findRoots(target, false, ignoreUnreadable, roots); | ||||
|             else if (ignoreUnreadable && errno == EACCES) | ||||
|                 /* ignore */ ; | ||||
|             else if (errno == ENOENT || errno == ENOTDIR) { | ||||
|                 if (pathExists(target)) | ||||
|                     findRoots(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. */ | ||||
|                     /* 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()); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     catch (SysError & e) { | ||||
|         /* We only ignore permanent failures. */ | ||||
|         if (e.errNo == EACCES || e.errNo == ENOENT || e.errNo == ENOTDIR) | ||||
|             printMsg(lvlInfo, format("cannot read potential root `%1%'") % path); | ||||
|         else | ||||
|                 throw SysError(format("statting `%1%'") % target); | ||||
|         } | ||||
|             throw; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| static void findRoots(Roots & roots, bool ignoreUnreadable) | ||||
| static Roots findRoots(bool deleteStale) | ||||
| { | ||||
|     Roots roots; | ||||
|     Path rootsDir = canonPath((format("%1%/%2%") % nixStateDir % gcRootsDir).str()); | ||||
|     findRoots(rootsDir, true, ignoreUnreadable, roots); | ||||
|     findRoots(rootsDir, true, deleteStale, roots); | ||||
|     return roots; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| Roots LocalStore::findRoots() | ||||
| { | ||||
|     return nix::findRoots(false); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
|  | @ -437,8 +446,7 @@ void collectGarbage(GCAction action, const PathSet & pathsToDelete, | |||
| 
 | ||||
|     /* Find the roots.  Since we've grabbed the GC lock, the set of
 | ||||
|        permanent roots cannot increase now. */ | ||||
|     Roots rootMap; | ||||
|     findRoots(rootMap, false); | ||||
|     Roots rootMap = ignoreLiveness ? Roots() : findRoots(true); | ||||
| 
 | ||||
|     PathSet roots; | ||||
|     for (Roots::iterator i = rootMap.begin(); i != rootMap.end(); ++i) | ||||
|  |  | |||
|  | @ -64,6 +64,8 @@ public: | |||
|     void addIndirectRoot(const Path & path); | ||||
|      | ||||
|     void syncWithGC(); | ||||
| 
 | ||||
|     Roots findRoots(); | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -18,6 +18,23 @@ | |||
| namespace nix { | ||||
| 
 | ||||
| 
 | ||||
| Path readStorePath(Source & from) | ||||
| { | ||||
|     Path path = readString(from); | ||||
|     assertStorePath(path); | ||||
|     return path; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| PathSet readStorePaths(Source & from) | ||||
| { | ||||
|     PathSet paths = readStringSet(from); | ||||
|     for (PathSet::iterator i = paths.begin(); i != paths.end(); ++i) | ||||
|         assertStorePath(*i); | ||||
|     return paths; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| RemoteStore::RemoteStore() | ||||
| { | ||||
|     string remoteMode = getEnv("NIX_REMOTE"); | ||||
|  | @ -179,7 +196,7 @@ void RemoteStore::queryReferences(const Path & path, | |||
|     writeInt(wopQueryReferences, to); | ||||
|     writeString(path, to); | ||||
|     processStderr(); | ||||
|     PathSet references2 = readStringSet(from); | ||||
|     PathSet references2 = readStorePaths(from); | ||||
|     references.insert(references2.begin(), references2.end()); | ||||
| } | ||||
| 
 | ||||
|  | @ -190,7 +207,7 @@ void RemoteStore::queryReferrers(const Path & path, | |||
|     writeInt(wopQueryReferrers, to); | ||||
|     writeString(path, to); | ||||
|     processStderr(); | ||||
|     PathSet referrers2 = readStringSet(from); | ||||
|     PathSet referrers2 = readStorePaths(from); | ||||
|     referrers.insert(referrers2.begin(), referrers2.end()); | ||||
| } | ||||
| 
 | ||||
|  | @ -207,7 +224,7 @@ Path RemoteStore::addToStore(const Path & _srcPath, bool fixed, | |||
|     writeString(hashAlgo, to); | ||||
|     dumpPath(srcPath, to); | ||||
|     processStderr(); | ||||
|     Path path = readString(from); | ||||
|     Path path = readStorePath(from); | ||||
|     return path; | ||||
| } | ||||
| 
 | ||||
|  | @ -221,7 +238,7 @@ Path RemoteStore::addTextToStore(const string & suffix, const string & s, | |||
|     writeStringSet(references, to); | ||||
|      | ||||
|     processStderr(); | ||||
|     Path path = readString(from); | ||||
|     Path path = readStorePath(from); | ||||
|     return path; | ||||
| } | ||||
| 
 | ||||
|  | @ -270,6 +287,21 @@ void RemoteStore::syncWithGC() | |||
| } | ||||
| 
 | ||||
| 
 | ||||
| Roots RemoteStore::findRoots() | ||||
| { | ||||
|     writeInt(wopFindRoots, to); | ||||
|     processStderr(); | ||||
|     unsigned int count = readInt(from); | ||||
|     Roots result; | ||||
|     while (count--) { | ||||
|         Path link = readString(from); | ||||
|         Path target = readStorePath(from); | ||||
|         result[link] = target; | ||||
|     } | ||||
|     return result; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| void RemoteStore::processStderr() | ||||
| { | ||||
|     unsigned int msg; | ||||
|  |  | |||
|  | @ -53,6 +53,8 @@ public: | |||
|      | ||||
|     void syncWithGC(); | ||||
|      | ||||
|     Roots findRoots(); | ||||
|      | ||||
| private: | ||||
|     AutoCloseFD fdSocket; | ||||
|     FdSink to; | ||||
|  |  | |||
|  | @ -2,6 +2,7 @@ | |||
| #define __STOREAPI_H | ||||
| 
 | ||||
| #include <string> | ||||
| #include <map> | ||||
| 
 | ||||
| #include <boost/shared_ptr.hpp> | ||||
| 
 | ||||
|  | @ -33,6 +34,9 @@ struct Substitute | |||
| typedef list<Substitute> Substitutes; | ||||
| 
 | ||||
| 
 | ||||
| typedef std::map<Path, Path> Roots; | ||||
| 
 | ||||
| 
 | ||||
| class StoreAPI  | ||||
| { | ||||
| public: | ||||
|  | @ -118,6 +122,11 @@ public: | |||
|        In either case the permanent root is seen by the collector. */ | ||||
|     virtual void syncWithGC() = 0; | ||||
| 
 | ||||
|     /* Find the roots of the garbage collector.  Each root is a pair
 | ||||
|        (link, storepath) where `link' is the path of the symlink | ||||
|        outside of the Nix store that point to `storePath'.  */ | ||||
|     virtual Roots findRoots() = 0; | ||||
| 
 | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -2,6 +2,9 @@ | |||
| #define __WORKER_PROTOCOL_H | ||||
| 
 | ||||
| 
 | ||||
| namespace nix { | ||||
| 
 | ||||
| 
 | ||||
| #define WORKER_MAGIC_1 0x6e697864 | ||||
| #define WORKER_MAGIC_2 0x6478696e | ||||
| 
 | ||||
|  | @ -21,6 +24,7 @@ typedef enum { | |||
|     wopAddTempRoot, | ||||
|     wopAddIndirectRoot, | ||||
|     wopSyncWithGC, | ||||
|     wopFindRoots, | ||||
| } WorkerOp; | ||||
| 
 | ||||
| 
 | ||||
|  | @ -34,4 +38,11 @@ typedef enum { | |||
| #define DEFAULT_SOCKET_PATH "/daemon.socket" | ||||
| 
 | ||||
| 
 | ||||
| Path readStorePath(Source & from); | ||||
| PathSet readStorePaths(Source & from); | ||||
| 
 | ||||
|      | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| #endif /* !__WORKER_PROTOCOL_H */ | ||||
|  |  | |||
|  | @ -34,6 +34,7 @@ public: | |||
| class SysError : public Error | ||||
| { | ||||
| public: | ||||
|     int errNo; | ||||
|     SysError(const format & f); | ||||
| }; | ||||
| 
 | ||||
|  |  | |||
|  | @ -35,6 +35,7 @@ Error & Error::addPrefix(const format & f) | |||
| 
 | ||||
| SysError::SysError(const format & f) | ||||
|     : Error(format("%1%: %2%") % f.str() % strerror(errno)) | ||||
|     , errNo(errno) | ||||
| { | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -23,23 +23,6 @@ using namespace nix; | |||
| #endif | ||||
| 
 | ||||
| 
 | ||||
| static Path readStorePath(Source & from) | ||||
| { | ||||
|     Path path = readString(from); | ||||
|     assertStorePath(path); | ||||
|     return path; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| static PathSet readStorePaths(Source & from) | ||||
| { | ||||
|     PathSet paths = readStringSet(from); | ||||
|     for (PathSet::iterator i = paths.begin(); i != paths.end(); ++i) | ||||
|         assertStorePath(*i); | ||||
|     return paths; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| static FdSource from(STDIN_FILENO); | ||||
| static FdSink to(STDOUT_FILENO); | ||||
| 
 | ||||
|  | @ -286,6 +269,18 @@ static void performOp(Source & from, Sink & to, unsigned int op) | |||
|         break; | ||||
|     } | ||||
| 
 | ||||
|     case wopFindRoots: { | ||||
|         startWork(); | ||||
|         Roots roots = store->findRoots(); | ||||
|         stopWork(); | ||||
|         writeInt(roots.size(), to); | ||||
|         for (Roots::iterator i = roots.begin(); i != roots.end(); ++i) { | ||||
|             writeString(i->first, to); | ||||
|             writeString(i->second, to); | ||||
|         } | ||||
|         break; | ||||
|     } | ||||
| 
 | ||||
|     default: | ||||
|         throw Error(format("invalid operation %1%") % op); | ||||
|     } | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue