forked from lix-project/lix
* Allow certain operations to succeed even if we don't have write
permission to the Nix store or database. E.g., `nix-env -qa' will work, but `nix-env -qas' won't (the latter needs DB access). The option `--readonly-mode' forces this mode; otherwise, it's only activated when the database cannot be opened.
This commit is contained in:
parent
3ade3e7721
commit
f4d44a0026
|
@ -8,7 +8,7 @@
|
||||||
#include "nixexpr.hh"
|
#include "nixexpr.hh"
|
||||||
|
|
||||||
|
|
||||||
typedef map<Path, PathSet> DrvPaths;
|
typedef map<Path, PathSet> DrvRoots;
|
||||||
typedef map<Path, Hash> DrvHashes;
|
typedef map<Path, Hash> DrvHashes;
|
||||||
|
|
||||||
struct EvalState;
|
struct EvalState;
|
||||||
|
@ -22,7 +22,7 @@ struct EvalState
|
||||||
{
|
{
|
||||||
ATermMap normalForms;
|
ATermMap normalForms;
|
||||||
ATermMap primOps;
|
ATermMap primOps;
|
||||||
DrvPaths drvPaths;
|
DrvRoots drvRoots;
|
||||||
DrvHashes drvHashes; /* normalised derivation hashes */
|
DrvHashes drvHashes; /* normalised derivation hashes */
|
||||||
Expr blackHole;
|
Expr blackHole;
|
||||||
|
|
||||||
|
|
|
@ -17,12 +17,12 @@ static Expr primImport(EvalState & state, const ATermVector & args)
|
||||||
|
|
||||||
static PathSet storeExprRootsCached(EvalState & state, const Path & nePath)
|
static PathSet storeExprRootsCached(EvalState & state, const Path & nePath)
|
||||||
{
|
{
|
||||||
DrvPaths::iterator i = state.drvPaths.find(nePath);
|
DrvRoots::iterator i = state.drvRoots.find(nePath);
|
||||||
if (i != state.drvPaths.end())
|
if (i != state.drvRoots.end())
|
||||||
return i->second;
|
return i->second;
|
||||||
else {
|
else {
|
||||||
PathSet paths = storeExprRoots(nePath);
|
PathSet paths = storeExprRoots(nePath);
|
||||||
state.drvPaths[nePath] = paths;
|
state.drvRoots[nePath] = paths;
|
||||||
return paths;
|
return paths;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -61,6 +61,8 @@ static Path copyAtom(EvalState & state, const Path & srcPath)
|
||||||
Path drvPath = writeTerm(unparseStoreExpr(ne), "");
|
Path drvPath = writeTerm(unparseStoreExpr(ne), "");
|
||||||
state.drvHashes[drvPath] = drvHash;
|
state.drvHashes[drvPath] = drvHash;
|
||||||
|
|
||||||
|
state.drvRoots[drvPath] = ne.closure.roots;
|
||||||
|
|
||||||
printMsg(lvlChatty, format("copied `%1%' -> closure `%2%'")
|
printMsg(lvlChatty, format("copied `%1%' -> closure `%2%'")
|
||||||
% srcPath % drvPath);
|
% srcPath % drvPath);
|
||||||
return drvPath;
|
return drvPath;
|
||||||
|
@ -111,7 +113,13 @@ static void processBinding(EvalState & state, Expr e, StoreExpr & ne,
|
||||||
if (!a) throw Error("derivation hash missing");
|
if (!a) throw Error("derivation hash missing");
|
||||||
Hash drvHash = parseHash(evalString(state, a));
|
Hash drvHash = parseHash(evalString(state, a));
|
||||||
|
|
||||||
|
a = queryAttr(e, "outPath");
|
||||||
|
if (!a) throw Error("output path missing");
|
||||||
|
PathSet drvRoots;
|
||||||
|
drvRoots.insert(evalPath(state, a));
|
||||||
|
|
||||||
state.drvHashes[drvPath] = drvHash;
|
state.drvHashes[drvPath] = drvHash;
|
||||||
|
state.drvRoots[drvPath] = drvRoots;
|
||||||
|
|
||||||
ss.push_back(addInput(state, drvPath, ne));
|
ss.push_back(addInput(state, drvPath, ne));
|
||||||
} else
|
} else
|
||||||
|
|
|
@ -160,6 +160,8 @@ static void initAndRun(int argc, char * * argv)
|
||||||
throw UsageError(format("`--max-jobs' requires a non-negative integer"));
|
throw UsageError(format("`--max-jobs' requires a non-negative integer"));
|
||||||
maxBuildJobs = n;
|
maxBuildJobs = n;
|
||||||
}
|
}
|
||||||
|
else if (arg == "--readonly-mode")
|
||||||
|
readOnlyMode = true;
|
||||||
else remaining.push_back(arg);
|
else remaining.push_back(arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
|
@ -81,12 +82,16 @@ void Transaction::moveTo(Transaction & t)
|
||||||
void Database::requireEnv()
|
void Database::requireEnv()
|
||||||
{
|
{
|
||||||
checkInterrupt();
|
checkInterrupt();
|
||||||
if (!env) throw Error("database environment not open");
|
if (!env)throw Error("database environment is not open "
|
||||||
|
"(maybe you don't have sufficient permission?)");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Db * Database::getDb(TableId table)
|
Db * Database::getDb(TableId table)
|
||||||
{
|
{
|
||||||
|
if (table == 0)
|
||||||
|
throw Error("database table is not open "
|
||||||
|
"(maybe you don't have sufficient permission?)");
|
||||||
map<TableId, Db *>::iterator i = tables.find(table);
|
map<TableId, Db *>::iterator i = tables.find(table);
|
||||||
if (i == tables.end())
|
if (i == tables.end())
|
||||||
throw Error("unknown table id");
|
throw Error("unknown table id");
|
||||||
|
@ -210,6 +215,10 @@ void Database::open(const string & path)
|
||||||
string accessorsPath = path + "/accessor_count";
|
string accessorsPath = path + "/accessor_count";
|
||||||
fdAccessors = ::open(accessorsPath.c_str(), O_RDWR | O_CREAT, 0666);
|
fdAccessors = ::open(accessorsPath.c_str(), O_RDWR | O_CREAT, 0666);
|
||||||
if (fdAccessors == -1)
|
if (fdAccessors == -1)
|
||||||
|
if (errno == EACCES)
|
||||||
|
throw DbNoPermission(
|
||||||
|
format("permission denied to database in `%1%'") % accessorsPath);
|
||||||
|
else
|
||||||
throw SysError(format("opening file `%1%'") % accessorsPath);
|
throw SysError(format("opening file `%1%'") % accessorsPath);
|
||||||
|
|
||||||
/* Open the lock file. */
|
/* Open the lock file. */
|
||||||
|
|
|
@ -87,4 +87,11 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class DbNoPermission : public Error
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
DbNoPermission(const format & f) : Error(f) { };
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
#endif /* !__DB_H */
|
#endif /* !__DB_H */
|
||||||
|
|
|
@ -15,3 +15,5 @@ bool tryFallback = false;
|
||||||
Verbosity buildVerbosity = lvlInfo;
|
Verbosity buildVerbosity = lvlInfo;
|
||||||
|
|
||||||
unsigned int maxBuildJobs = 1;
|
unsigned int maxBuildJobs = 1;
|
||||||
|
|
||||||
|
bool readOnlyMode = false;
|
||||||
|
|
|
@ -43,5 +43,9 @@ extern Verbosity buildVerbosity;
|
||||||
/* Maximum number of parallel build jobs. 0 means unlimited. */
|
/* Maximum number of parallel build jobs. 0 means unlimited. */
|
||||||
extern unsigned int maxBuildJobs;
|
extern unsigned int maxBuildJobs;
|
||||||
|
|
||||||
|
/* Read-only mode. Don't copy stuff to the store, don't change the
|
||||||
|
database. */
|
||||||
|
extern bool readOnlyMode;
|
||||||
|
|
||||||
|
|
||||||
#endif /* !__GLOBALS_H */
|
#endif /* !__GLOBALS_H */
|
||||||
|
|
|
@ -21,7 +21,7 @@ static Database nixDB;
|
||||||
|
|
||||||
The existence of a key $p$ indicates that path $p$ is valid (that
|
The existence of a key $p$ indicates that path $p$ is valid (that
|
||||||
is, produced by a succesful build). */
|
is, produced by a succesful build). */
|
||||||
static TableId dbValidPaths;
|
static TableId dbValidPaths = 0;
|
||||||
|
|
||||||
/* dbSuccessors :: Path -> Path
|
/* dbSuccessors :: Path -> Path
|
||||||
|
|
||||||
|
@ -32,14 +32,14 @@ static TableId dbValidPaths;
|
||||||
Note that a term $y$ is a successor of $x$ iff there exists a
|
Note that a term $y$ is a successor of $x$ iff there exists a
|
||||||
sequence of rewrite steps that rewrites $x$ into $y$.
|
sequence of rewrite steps that rewrites $x$ into $y$.
|
||||||
*/
|
*/
|
||||||
static TableId dbSuccessors;
|
static TableId dbSuccessors = 0;
|
||||||
|
|
||||||
/* dbSuccessorsRev :: Path -> [Path]
|
/* dbSuccessorsRev :: Path -> [Path]
|
||||||
|
|
||||||
The reverse mapping of dbSuccessors (i.e., it stores the
|
The reverse mapping of dbSuccessors (i.e., it stores the
|
||||||
predecessors of a Nix expression).
|
predecessors of a Nix expression).
|
||||||
*/
|
*/
|
||||||
static TableId dbSuccessorsRev;
|
static TableId dbSuccessorsRev = 0;
|
||||||
|
|
||||||
/* dbSubstitutes :: Path -> [(Path, Path, [string])]
|
/* dbSubstitutes :: Path -> [(Path, Path, [string])]
|
||||||
|
|
||||||
|
@ -54,14 +54,14 @@ static TableId dbSuccessorsRev;
|
||||||
substitute for that derivate. The substitute in this case might be
|
substitute for that derivate. The substitute in this case might be
|
||||||
a Nix expression that fetches the Nix archive.
|
a Nix expression that fetches the Nix archive.
|
||||||
*/
|
*/
|
||||||
static TableId dbSubstitutes;
|
static TableId dbSubstitutes = 0;
|
||||||
|
|
||||||
/* dbSubstitutesRev :: Path -> [Path]
|
/* dbSubstitutesRev :: Path -> [Path]
|
||||||
|
|
||||||
The reverse mapping of dbSubstitutes; it maps store expressions
|
The reverse mapping of dbSubstitutes; it maps store expressions
|
||||||
back to the paths for which they are substitutes.
|
back to the paths for which they are substitutes.
|
||||||
*/
|
*/
|
||||||
static TableId dbSubstitutesRev;
|
static TableId dbSubstitutesRev = 0;
|
||||||
|
|
||||||
|
|
||||||
bool Substitute::operator == (const Substitute & sub)
|
bool Substitute::operator == (const Substitute & sub)
|
||||||
|
@ -74,7 +74,14 @@ bool Substitute::operator == (const Substitute & sub)
|
||||||
|
|
||||||
void openDB()
|
void openDB()
|
||||||
{
|
{
|
||||||
|
if (readOnlyMode) return;
|
||||||
|
try {
|
||||||
nixDB.open(nixDBPath);
|
nixDB.open(nixDBPath);
|
||||||
|
} catch (DbNoPermission & e) {
|
||||||
|
printMsg(lvlTalkative, "cannot access Nix database; continuing anyway");
|
||||||
|
readOnlyMode = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
dbValidPaths = nixDB.openTable("validpaths");
|
dbValidPaths = nixDB.openTable("validpaths");
|
||||||
dbSuccessors = nixDB.openTable("successors");
|
dbSuccessors = nixDB.openTable("successors");
|
||||||
dbSuccessorsRev = nixDB.openTable("successors-rev");
|
dbSuccessorsRev = nixDB.openTable("successors-rev");
|
||||||
|
@ -433,7 +440,7 @@ Path addToStore(const Path & _srcPath)
|
||||||
string baseName = baseNameOf(srcPath);
|
string baseName = baseNameOf(srcPath);
|
||||||
Path dstPath = canonPath(nixStore + "/" + (string) h + "-" + baseName);
|
Path dstPath = canonPath(nixStore + "/" + (string) h + "-" + baseName);
|
||||||
|
|
||||||
if (!isValidPath(dstPath)) {
|
if (!readOnlyMode && !isValidPath(dstPath)) {
|
||||||
|
|
||||||
/* The first check above is an optimisation to prevent
|
/* The first check above is an optimisation to prevent
|
||||||
unnecessary lock acquisition. */
|
unnecessary lock acquisition. */
|
||||||
|
@ -446,6 +453,9 @@ Path addToStore(const Path & _srcPath)
|
||||||
|
|
||||||
if (pathExists(dstPath)) deletePath(dstPath);
|
if (pathExists(dstPath)) deletePath(dstPath);
|
||||||
|
|
||||||
|
/* !!! race: srcPath might change between hashPath() and
|
||||||
|
here! */
|
||||||
|
|
||||||
copyPath(srcPath, dstPath);
|
copyPath(srcPath, dstPath);
|
||||||
|
|
||||||
makePathReadOnly(dstPath);
|
makePathReadOnly(dstPath);
|
||||||
|
|
|
@ -17,7 +17,7 @@ Path writeTerm(ATerm t, const string & suffix)
|
||||||
Path path = canonPath(nixStore + "/" +
|
Path path = canonPath(nixStore + "/" +
|
||||||
(string) h + suffix + ".store");
|
(string) h + suffix + ".store");
|
||||||
|
|
||||||
if (!isValidPath(path)) {
|
if (!readOnlyMode && !isValidPath(path)) {
|
||||||
char * s = ATwriteToString(t);
|
char * s = ATwriteToString(t);
|
||||||
if (!s) throw Error(format("cannot write aterm to `%1%'") % path);
|
if (!s) throw Error(format("cannot write aterm to `%1%'") % path);
|
||||||
addTextToStore(path, string(s));
|
addTextToStore(path, string(s));
|
||||||
|
|
Loading…
Reference in a new issue