* 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:
Eelco Dolstra 2004-10-25 14:38:23 +00:00
parent 3ade3e7721
commit f4d44a0026
9 changed files with 58 additions and 16 deletions

View file

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

View file

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

View file

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

View file

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

View file

@ -87,4 +87,11 @@ public:
}; };
class DbNoPermission : public Error
{
public:
DbNoPermission(const format & f) : Error(f) { };
};
#endif /* !__DB_H */ #endif /* !__DB_H */

View file

@ -15,3 +15,5 @@ bool tryFallback = false;
Verbosity buildVerbosity = lvlInfo; Verbosity buildVerbosity = lvlInfo;
unsigned int maxBuildJobs = 1; unsigned int maxBuildJobs = 1;
bool readOnlyMode = false;

View file

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

View file

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

View file

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