forked from lix-project/lix
* 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
|
@ -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)
|
||||||
|
|
|
@ -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…
Reference in a new issue