From f4d44a002688262d33093494a7fea1bb11b97ac9 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 25 Oct 2004 14:38:23 +0000 Subject: [PATCH] * 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. --- src/libexpr/eval.hh | 4 ++-- src/libexpr/primops.cc | 16 ++++++++++++---- src/libmain/shared.cc | 2 ++ src/libstore/db.cc | 13 +++++++++++-- src/libstore/db.hh | 7 +++++++ src/libstore/globals.cc | 2 ++ src/libstore/globals.hh | 4 ++++ src/libstore/store.cc | 24 +++++++++++++++++------- src/libstore/storeexpr.cc | 2 +- 9 files changed, 58 insertions(+), 16 deletions(-) diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index b51a5b0798..8ea0aec06c 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -8,7 +8,7 @@ #include "nixexpr.hh" -typedef map DrvPaths; +typedef map DrvRoots; typedef map DrvHashes; struct EvalState; @@ -22,7 +22,7 @@ struct EvalState { ATermMap normalForms; ATermMap primOps; - DrvPaths drvPaths; + DrvRoots drvRoots; DrvHashes drvHashes; /* normalised derivation hashes */ Expr blackHole; diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 6588922c26..070ed1b54a 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -17,12 +17,12 @@ static Expr primImport(EvalState & state, const ATermVector & args) static PathSet storeExprRootsCached(EvalState & state, const Path & nePath) { - DrvPaths::iterator i = state.drvPaths.find(nePath); - if (i != state.drvPaths.end()) + DrvRoots::iterator i = state.drvRoots.find(nePath); + if (i != state.drvRoots.end()) return i->second; else { PathSet paths = storeExprRoots(nePath); - state.drvPaths[nePath] = paths; + state.drvRoots[nePath] = paths; return paths; } } @@ -61,6 +61,8 @@ static Path copyAtom(EvalState & state, const Path & srcPath) Path drvPath = writeTerm(unparseStoreExpr(ne), ""); state.drvHashes[drvPath] = drvHash; + state.drvRoots[drvPath] = ne.closure.roots; + printMsg(lvlChatty, format("copied `%1%' -> closure `%2%'") % srcPath % drvPath); return drvPath; @@ -111,8 +113,14 @@ static void processBinding(EvalState & state, Expr e, StoreExpr & ne, if (!a) throw Error("derivation hash missing"); Hash drvHash = parseHash(evalString(state, a)); - state.drvHashes[drvPath] = drvHash; + a = queryAttr(e, "outPath"); + if (!a) throw Error("output path missing"); + PathSet drvRoots; + drvRoots.insert(evalPath(state, a)); + state.drvHashes[drvPath] = drvHash; + state.drvRoots[drvPath] = drvRoots; + ss.push_back(addInput(state, drvPath, ne)); } else throw Error("invalid derivation attribute"); diff --git a/src/libmain/shared.cc b/src/libmain/shared.cc index 24f6ec4df9..73388c96e8 100644 --- a/src/libmain/shared.cc +++ b/src/libmain/shared.cc @@ -160,6 +160,8 @@ static void initAndRun(int argc, char * * argv) throw UsageError(format("`--max-jobs' requires a non-negative integer")); maxBuildJobs = n; } + else if (arg == "--readonly-mode") + readOnlyMode = true; else remaining.push_back(arg); } diff --git a/src/libstore/db.cc b/src/libstore/db.cc index f01cefd799..3b7bddaa26 100644 --- a/src/libstore/db.cc +++ b/src/libstore/db.cc @@ -1,6 +1,7 @@ #include #include #include +#include #include @@ -81,12 +82,16 @@ void Transaction::moveTo(Transaction & t) void Database::requireEnv() { 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) { + if (table == 0) + throw Error("database table is not open " + "(maybe you don't have sufficient permission?)"); map::iterator i = tables.find(table); if (i == tables.end()) throw Error("unknown table id"); @@ -210,7 +215,11 @@ void Database::open(const string & path) string accessorsPath = path + "/accessor_count"; fdAccessors = ::open(accessorsPath.c_str(), O_RDWR | O_CREAT, 0666); if (fdAccessors == -1) - throw SysError(format("opening file `%1%'") % accessorsPath); + if (errno == EACCES) + throw DbNoPermission( + format("permission denied to database in `%1%'") % accessorsPath); + else + throw SysError(format("opening file `%1%'") % accessorsPath); /* Open the lock file. */ string lockPath = path + "/access_lock"; diff --git a/src/libstore/db.hh b/src/libstore/db.hh index bbeabfc7df..d566fdad1e 100644 --- a/src/libstore/db.hh +++ b/src/libstore/db.hh @@ -87,4 +87,11 @@ public: }; +class DbNoPermission : public Error +{ +public: + DbNoPermission(const format & f) : Error(f) { }; +}; + + #endif /* !__DB_H */ diff --git a/src/libstore/globals.cc b/src/libstore/globals.cc index 44794d147a..52f2a0a0bf 100644 --- a/src/libstore/globals.cc +++ b/src/libstore/globals.cc @@ -15,3 +15,5 @@ bool tryFallback = false; Verbosity buildVerbosity = lvlInfo; unsigned int maxBuildJobs = 1; + +bool readOnlyMode = false; diff --git a/src/libstore/globals.hh b/src/libstore/globals.hh index 7f88d5c53b..beaa0acc9f 100644 --- a/src/libstore/globals.hh +++ b/src/libstore/globals.hh @@ -43,5 +43,9 @@ extern Verbosity buildVerbosity; /* Maximum number of parallel build jobs. 0 means unlimited. */ 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 */ diff --git a/src/libstore/store.cc b/src/libstore/store.cc index 1f05b63a6c..7dbf520d22 100644 --- a/src/libstore/store.cc +++ b/src/libstore/store.cc @@ -21,7 +21,7 @@ static Database nixDB; The existence of a key $p$ indicates that path $p$ is valid (that is, produced by a succesful build). */ -static TableId dbValidPaths; +static TableId dbValidPaths = 0; /* dbSuccessors :: Path -> Path @@ -32,14 +32,14 @@ static TableId dbValidPaths; Note that a term $y$ is a successor of $x$ iff there exists a sequence of rewrite steps that rewrites $x$ into $y$. */ -static TableId dbSuccessors; +static TableId dbSuccessors = 0; /* dbSuccessorsRev :: Path -> [Path] The reverse mapping of dbSuccessors (i.e., it stores the predecessors of a Nix expression). */ -static TableId dbSuccessorsRev; +static TableId dbSuccessorsRev = 0; /* dbSubstitutes :: Path -> [(Path, Path, [string])] @@ -54,14 +54,14 @@ static TableId dbSuccessorsRev; substitute for that derivate. The substitute in this case might be a Nix expression that fetches the Nix archive. */ -static TableId dbSubstitutes; +static TableId dbSubstitutes = 0; /* dbSubstitutesRev :: Path -> [Path] The reverse mapping of dbSubstitutes; it maps store expressions back to the paths for which they are substitutes. */ -static TableId dbSubstitutesRev; +static TableId dbSubstitutesRev = 0; bool Substitute::operator == (const Substitute & sub) @@ -74,7 +74,14 @@ bool Substitute::operator == (const Substitute & sub) void openDB() { - nixDB.open(nixDBPath); + if (readOnlyMode) return; + try { + nixDB.open(nixDBPath); + } catch (DbNoPermission & e) { + printMsg(lvlTalkative, "cannot access Nix database; continuing anyway"); + readOnlyMode = true; + return; + } dbValidPaths = nixDB.openTable("validpaths"); dbSuccessors = nixDB.openTable("successors"); dbSuccessorsRev = nixDB.openTable("successors-rev"); @@ -433,7 +440,7 @@ Path addToStore(const Path & _srcPath) string baseName = baseNameOf(srcPath); Path dstPath = canonPath(nixStore + "/" + (string) h + "-" + baseName); - if (!isValidPath(dstPath)) { + if (!readOnlyMode && !isValidPath(dstPath)) { /* The first check above is an optimisation to prevent unnecessary lock acquisition. */ @@ -445,6 +452,9 @@ Path addToStore(const Path & _srcPath) if (!isValidPath(dstPath)) { if (pathExists(dstPath)) deletePath(dstPath); + + /* !!! race: srcPath might change between hashPath() and + here! */ copyPath(srcPath, dstPath); diff --git a/src/libstore/storeexpr.cc b/src/libstore/storeexpr.cc index 45b5af055a..29f271de88 100644 --- a/src/libstore/storeexpr.cc +++ b/src/libstore/storeexpr.cc @@ -17,7 +17,7 @@ Path writeTerm(ATerm t, const string & suffix) Path path = canonPath(nixStore + "/" + (string) h + suffix + ".store"); - if (!isValidPath(path)) { + if (!readOnlyMode && !isValidPath(path)) { char * s = ATwriteToString(t); if (!s) throw Error(format("cannot write aterm to `%1%'") % path); addTextToStore(path, string(s));