* 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
					
				
					 9 changed files with 58 additions and 16 deletions
				
			
		|  | @ -8,7 +8,7 @@ | |||
| #include "nixexpr.hh" | ||||
| 
 | ||||
| 
 | ||||
| typedef map<Path, PathSet> DrvPaths; | ||||
| typedef map<Path, PathSet> DrvRoots; | ||||
| typedef map<Path, Hash> DrvHashes; | ||||
| 
 | ||||
| struct EvalState; | ||||
|  | @ -22,7 +22,7 @@ struct EvalState | |||
| { | ||||
|     ATermMap normalForms; | ||||
|     ATermMap primOps; | ||||
|     DrvPaths drvPaths; | ||||
|     DrvRoots drvRoots; | ||||
|     DrvHashes drvHashes; /* normalised derivation hashes */ | ||||
|     Expr blackHole; | ||||
| 
 | ||||
|  |  | |||
|  | @ -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,7 +113,13 @@ static void processBinding(EvalState & state, Expr e, StoreExpr & ne, | |||
|             if (!a) throw Error("derivation hash missing"); | ||||
|             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.drvRoots[drvPath] = drvRoots; | ||||
| 
 | ||||
|             ss.push_back(addInput(state, drvPath, ne)); | ||||
|         } else | ||||
|  |  | |||
|  | @ -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); | ||||
|     } | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,6 +1,7 @@ | |||
| #include <sys/types.h> | ||||
| #include <sys/stat.h> | ||||
| #include <fcntl.h> | ||||
| #include <errno.h> | ||||
| 
 | ||||
| #include <memory> | ||||
| 
 | ||||
|  | @ -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<TableId, Db *>::iterator i = tables.find(table); | ||||
|     if (i == tables.end()) | ||||
|         throw Error("unknown table id"); | ||||
|  | @ -210,6 +215,10 @@ void Database::open(const string & path) | |||
|         string accessorsPath = path + "/accessor_count"; | ||||
|         fdAccessors = ::open(accessorsPath.c_str(), O_RDWR | O_CREAT, 0666); | ||||
|         if (fdAccessors == -1) | ||||
|             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. */ | ||||
|  |  | |||
|  | @ -87,4 +87,11 @@ public: | |||
| }; | ||||
| 
 | ||||
| 
 | ||||
| class DbNoPermission : public Error | ||||
| { | ||||
| public: | ||||
|     DbNoPermission(const format & f) : Error(f) { }; | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| #endif /* !__DB_H */ | ||||
|  |  | |||
|  | @ -15,3 +15,5 @@ bool tryFallback = false; | |||
| Verbosity buildVerbosity = lvlInfo; | ||||
| 
 | ||||
| unsigned int maxBuildJobs = 1; | ||||
| 
 | ||||
| bool readOnlyMode = false; | ||||
|  |  | |||
|  | @ -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 */ | ||||
|  |  | |||
|  | @ -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() | ||||
| { | ||||
|     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. */ | ||||
|  | @ -446,6 +453,9 @@ Path addToStore(const Path & _srcPath) | |||
| 
 | ||||
|             if (pathExists(dstPath)) deletePath(dstPath); | ||||
| 
 | ||||
|             /* !!! race: srcPath might change between hashPath() and
 | ||||
|                here! */ | ||||
|              | ||||
|             copyPath(srcPath, dstPath); | ||||
| 
 | ||||
|             makePathReadOnly(dstPath); | ||||
|  |  | |||
|  | @ -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)); | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue