* 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" | #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…
	
	Add table
		Add a link
		
	
		Reference in a new issue