* 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:
Eelco Dolstra 2006-12-05 01:31:45 +00:00
parent 8623256f48
commit 29cf434a35
9 changed files with 140 additions and 79 deletions

View File

@ -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,17 +116,17 @@ 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. */
Roots roots;
findRoots(roots, true);
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);
} }
/* Check that the root can be found by the garbage collector. */
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 /* 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
increasing while a GC is in progress. */ increasing while a GC is in progress. */
@ -308,58 +302,73 @@ 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)
{ {
struct stat st; try {
if (lstat(path.c_str(), &st) == -1)
throw SysError(format("statting `%1%'") % path); struct stat st;
if (lstat(path.c_str(), &st) == -1)
throw SysError(format("statting `%1%'") % path);
printMsg(lvlVomit, format("looking at `%1%'") % path); printMsg(lvlVomit, format("looking at `%1%'") % path);
if (S_ISDIR(st.st_mode)) {
Strings names = readDirectory(path);
for (Strings::iterator i = names.begin(); i != names.end(); ++i)
findRoots(path + "/" + *i, recurseSymlinks, deleteStale, roots);
}
else if (S_ISLNK(st.st_mode)) {
Path target = absPath(readLink(path), dirOf(path));
if (isInStore(target)) {
debug(format("found root `%1%' in `%2%'")
% target % path);
Path storePath = toStorePath(target);
if (store->isValidPath(storePath))
roots[path] = storePath;
else
printMsg(lvlInfo, format("skipping invalid root from `%1%' to `%2%'")
% path % storePath);
}
else if (recurseSymlinks) {
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. */
unlink(path.c_str());
}
}
}
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);
} }
else if (S_ISLNK(st.st_mode)) { catch (SysError & e) {
Path target = absPath(readLink(path), dirOf(path)); /* We only ignore permanent failures. */
if (e.errNo == EACCES || e.errNo == ENOENT || e.errNo == ENOTDIR)
if (isInStore(target)) { printMsg(lvlInfo, format("cannot read potential root `%1%'") % path);
debug(format("found root `%1%' in `%2%'") else
% target % path); throw;
Path storePath = toStorePath(target);
if (store->isValidPath(storePath))
roots[path] = storePath;
else
printMsg(lvlInfo, format("skipping invalid root from `%1%' to `%2%'")
% path % storePath);
}
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) {
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());
}
else
throw SysError(format("statting `%1%'") % target);
}
} }
} }
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)

View File

@ -64,6 +64,8 @@ public:
void addIndirectRoot(const Path & path); void addIndirectRoot(const Path & path);
void syncWithGC(); void syncWithGC();
Roots findRoots();
}; };

View File

@ -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;

View File

@ -53,6 +53,8 @@ public:
void syncWithGC(); void syncWithGC();
Roots findRoots();
private: private:
AutoCloseFD fdSocket; AutoCloseFD fdSocket;
FdSink to; FdSink to;

View File

@ -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;
}; };

View File

@ -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 */

View File

@ -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);
}; };

View File

@ -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)
{ {
} }

View File

@ -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);
} }