* 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, | Path addPermRoot(const Path & _storePath, const Path & _gcRoot, | ||||||
|     bool indirect, bool allowOutsideRootsDir) |     bool indirect, bool allowOutsideRootsDir) | ||||||
| { | { | ||||||
|  | @ -122,16 +116,16 @@ Path addPermRoot(const Path & _storePath, const Path & _gcRoot, | ||||||
|              |              | ||||||
|         createSymlink(gcRoot, storePath, false); |         createSymlink(gcRoot, storePath, false); | ||||||
| 
 | 
 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     /* Check that the root can be found by the garbage collector. */ |     /* Check that the root can be found by the garbage collector. */ | ||||||
|         Roots roots; |     Roots roots = store->findRoots(); | ||||||
|         findRoots(roots, true); |  | ||||||
|     if (roots.find(gcRoot) == roots.end()) |     if (roots.find(gcRoot) == roots.end()) | ||||||
|         printMsg(lvlError,  |         printMsg(lvlError,  | ||||||
|             format( |             format( | ||||||
|                 "warning: the garbage collector does not find `%1%' as a root; " |                 "warning: the garbage collector does not find `%1%' as a root; " | ||||||
|                 "therefore, `%2%' might be removed by the garbage collector") |                 "therefore, `%2%' might be removed by the garbage collector") | ||||||
|             % gcRoot % storePath); |             % gcRoot % storePath); | ||||||
|     } |  | ||||||
|          |          | ||||||
|     /* Grab the global GC root, causing us to block while a GC is in
 |     /* Grab the global GC root, causing us to block while a GC is in
 | ||||||
|        progress.  This prevents the set of permanent roots from |        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, | static void findRoots(const Path & path, bool recurseSymlinks, | ||||||
|     bool ignoreUnreadable, Roots & roots) |     bool deleteStale, Roots & roots) | ||||||
| { | { | ||||||
|  |     try { | ||||||
|  |          | ||||||
|         struct stat st; |         struct stat st; | ||||||
|         if (lstat(path.c_str(), &st) == -1) |         if (lstat(path.c_str(), &st) == -1) | ||||||
|             throw SysError(format("statting `%1%'") % path); |             throw SysError(format("statting `%1%'") % path); | ||||||
|  | @ -319,7 +315,7 @@ static void findRoots(const Path & path, bool recurseSymlinks, | ||||||
|         if (S_ISDIR(st.st_mode)) { |         if (S_ISDIR(st.st_mode)) { | ||||||
|             Strings names = readDirectory(path); |             Strings names = readDirectory(path); | ||||||
|             for (Strings::iterator i = names.begin(); i != names.end(); ++i) |             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)) { |         else if (S_ISLNK(st.st_mode)) { | ||||||
|  | @ -337,29 +333,42 @@ static void findRoots(const Path & path, bool recurseSymlinks, | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             else if (recurseSymlinks) { |             else if (recurseSymlinks) { | ||||||
|             struct stat st2; |                 if (pathExists(target)) | ||||||
|             if (lstat(target.c_str(), &st2) == 0) |                     findRoots(target, false, deleteStale, roots); | ||||||
|                 findRoots(target, false, ignoreUnreadable, roots); |                 else if (deleteStale) { | ||||||
|             else if (ignoreUnreadable && errno == EACCES) |  | ||||||
|                 /* ignore */ ; |  | ||||||
|             else if (errno == ENOENT || errno == ENOTDIR) { |  | ||||||
|                     printMsg(lvlInfo, format("removing stale link from `%1%' to `%2%'") % path % target); |                     printMsg(lvlInfo, format("removing stale link from `%1%' to `%2%'") % path % target); | ||||||
|                 /* Note that we only delete when recursing, i.e., when
 |                     /* Note that we only delete when recursing, i.e.,
 | ||||||
|                    we are still in the `gcroots' tree.  We never |                        when we are still in the `gcroots' tree.  We | ||||||
|                    delete stuff outside that tree. */ |                        never delete stuff outside that tree. */ | ||||||
|                     unlink(path.c_str()); |                     unlink(path.c_str()); | ||||||
|                 } |                 } | ||||||
|             else  |  | ||||||
|                 throw SysError(format("statting `%1%'") % target); |  | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  | 
 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     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; | ||||||
|  |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| static void findRoots(Roots & roots, bool ignoreUnreadable) | static Roots findRoots(bool deleteStale) | ||||||
| { | { | ||||||
|  |     Roots roots; | ||||||
|     Path rootsDir = canonPath((format("%1%/%2%") % nixStateDir % gcRootsDir).str()); |     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
 |     /* Find the roots.  Since we've grabbed the GC lock, the set of
 | ||||||
|        permanent roots cannot increase now. */ |        permanent roots cannot increase now. */ | ||||||
|     Roots rootMap; |     Roots rootMap = ignoreLiveness ? Roots() : findRoots(true); | ||||||
|     findRoots(rootMap, false); |  | ||||||
| 
 | 
 | ||||||
|     PathSet roots; |     PathSet roots; | ||||||
|     for (Roots::iterator i = rootMap.begin(); i != rootMap.end(); ++i) |     for (Roots::iterator i = rootMap.begin(); i != rootMap.end(); ++i) | ||||||
|  |  | ||||||
|  | @ -64,6 +64,8 @@ public: | ||||||
|     void addIndirectRoot(const Path & path); |     void addIndirectRoot(const Path & path); | ||||||
|      |      | ||||||
|     void syncWithGC(); |     void syncWithGC(); | ||||||
|  | 
 | ||||||
|  |     Roots findRoots(); | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -18,6 +18,23 @@ | ||||||
| namespace nix { | 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() | RemoteStore::RemoteStore() | ||||||
| { | { | ||||||
|     string remoteMode = getEnv("NIX_REMOTE"); |     string remoteMode = getEnv("NIX_REMOTE"); | ||||||
|  | @ -179,7 +196,7 @@ void RemoteStore::queryReferences(const Path & path, | ||||||
|     writeInt(wopQueryReferences, to); |     writeInt(wopQueryReferences, to); | ||||||
|     writeString(path, to); |     writeString(path, to); | ||||||
|     processStderr(); |     processStderr(); | ||||||
|     PathSet references2 = readStringSet(from); |     PathSet references2 = readStorePaths(from); | ||||||
|     references.insert(references2.begin(), references2.end()); |     references.insert(references2.begin(), references2.end()); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -190,7 +207,7 @@ void RemoteStore::queryReferrers(const Path & path, | ||||||
|     writeInt(wopQueryReferrers, to); |     writeInt(wopQueryReferrers, to); | ||||||
|     writeString(path, to); |     writeString(path, to); | ||||||
|     processStderr(); |     processStderr(); | ||||||
|     PathSet referrers2 = readStringSet(from); |     PathSet referrers2 = readStorePaths(from); | ||||||
|     referrers.insert(referrers2.begin(), referrers2.end()); |     referrers.insert(referrers2.begin(), referrers2.end()); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -207,7 +224,7 @@ Path RemoteStore::addToStore(const Path & _srcPath, bool fixed, | ||||||
|     writeString(hashAlgo, to); |     writeString(hashAlgo, to); | ||||||
|     dumpPath(srcPath, to); |     dumpPath(srcPath, to); | ||||||
|     processStderr(); |     processStderr(); | ||||||
|     Path path = readString(from); |     Path path = readStorePath(from); | ||||||
|     return path; |     return path; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -221,7 +238,7 @@ Path RemoteStore::addTextToStore(const string & suffix, const string & s, | ||||||
|     writeStringSet(references, to); |     writeStringSet(references, to); | ||||||
|      |      | ||||||
|     processStderr(); |     processStderr(); | ||||||
|     Path path = readString(from); |     Path path = readStorePath(from); | ||||||
|     return path; |     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() | void RemoteStore::processStderr() | ||||||
| { | { | ||||||
|     unsigned int msg; |     unsigned int msg; | ||||||
|  |  | ||||||
|  | @ -53,6 +53,8 @@ public: | ||||||
|      |      | ||||||
|     void syncWithGC(); |     void syncWithGC(); | ||||||
|      |      | ||||||
|  |     Roots findRoots(); | ||||||
|  |      | ||||||
| private: | private: | ||||||
|     AutoCloseFD fdSocket; |     AutoCloseFD fdSocket; | ||||||
|     FdSink to; |     FdSink to; | ||||||
|  |  | ||||||
|  | @ -2,6 +2,7 @@ | ||||||
| #define __STOREAPI_H | #define __STOREAPI_H | ||||||
| 
 | 
 | ||||||
| #include <string> | #include <string> | ||||||
|  | #include <map> | ||||||
| 
 | 
 | ||||||
| #include <boost/shared_ptr.hpp> | #include <boost/shared_ptr.hpp> | ||||||
| 
 | 
 | ||||||
|  | @ -33,6 +34,9 @@ struct Substitute | ||||||
| typedef list<Substitute> Substitutes; | typedef list<Substitute> Substitutes; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | typedef std::map<Path, Path> Roots; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| class StoreAPI  | class StoreAPI  | ||||||
| { | { | ||||||
| public: | public: | ||||||
|  | @ -118,6 +122,11 @@ public: | ||||||
|        In either case the permanent root is seen by the collector. */ |        In either case the permanent root is seen by the collector. */ | ||||||
|     virtual void syncWithGC() = 0; |     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 | #define __WORKER_PROTOCOL_H | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | namespace nix { | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| #define WORKER_MAGIC_1 0x6e697864 | #define WORKER_MAGIC_1 0x6e697864 | ||||||
| #define WORKER_MAGIC_2 0x6478696e | #define WORKER_MAGIC_2 0x6478696e | ||||||
| 
 | 
 | ||||||
|  | @ -21,6 +24,7 @@ typedef enum { | ||||||
|     wopAddTempRoot, |     wopAddTempRoot, | ||||||
|     wopAddIndirectRoot, |     wopAddIndirectRoot, | ||||||
|     wopSyncWithGC, |     wopSyncWithGC, | ||||||
|  |     wopFindRoots, | ||||||
| } WorkerOp; | } WorkerOp; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | @ -34,4 +38,11 @@ typedef enum { | ||||||
| #define DEFAULT_SOCKET_PATH "/daemon.socket" | #define DEFAULT_SOCKET_PATH "/daemon.socket" | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | Path readStorePath(Source & from); | ||||||
|  | PathSet readStorePaths(Source & from); | ||||||
|  | 
 | ||||||
|  |      | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| #endif /* !__WORKER_PROTOCOL_H */ | #endif /* !__WORKER_PROTOCOL_H */ | ||||||
|  |  | ||||||
|  | @ -34,6 +34,7 @@ public: | ||||||
| class SysError : public Error | class SysError : public Error | ||||||
| { | { | ||||||
| public: | public: | ||||||
|  |     int errNo; | ||||||
|     SysError(const format & f); |     SysError(const format & f); | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -35,6 +35,7 @@ Error & Error::addPrefix(const format & f) | ||||||
| 
 | 
 | ||||||
| SysError::SysError(const format & f) | SysError::SysError(const format & f) | ||||||
|     : Error(format("%1%: %2%") % f.str() % strerror(errno)) |     : Error(format("%1%: %2%") % f.str() % strerror(errno)) | ||||||
|  |     , errNo(errno) | ||||||
| { | { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -23,23 +23,6 @@ using namespace nix; | ||||||
| #endif | #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 FdSource from(STDIN_FILENO); | ||||||
| static FdSink to(STDOUT_FILENO); | static FdSink to(STDOUT_FILENO); | ||||||
| 
 | 
 | ||||||
|  | @ -286,6 +269,18 @@ static void performOp(Source & from, Sink & to, unsigned int op) | ||||||
|         break; |         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: |     default: | ||||||
|         throw Error(format("invalid operation %1%") % op); |         throw Error(format("invalid operation %1%") % op); | ||||||
|     } |     } | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue