* Revived the old "nix-store --delete" operation that deletes the
specified paths from the Nix store. However, this operation is safe: it refuses to delete anything that the garbage collector wouldn't delete.
This commit is contained in:
		
							parent
							
								
									3c5619c7e4
								
							
						
					
					
						commit
						4b9e7f59ca
					
				
					 4 changed files with 45 additions and 11 deletions
				
			
		|  | @ -303,8 +303,8 @@ static Paths topoSort(const PathSet & paths) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| void collectGarbage(GCAction action, PathSet & result, | void collectGarbage(GCAction action, const PathSet & pathsToDelete, | ||||||
|     unsigned long long & bytesFreed) |     PathSet & result, unsigned long long & bytesFreed) | ||||||
| { | { | ||||||
|     result.clear(); |     result.clear(); | ||||||
|     bytesFreed = 0; |     bytesFreed = 0; | ||||||
|  | @ -398,17 +398,26 @@ void collectGarbage(GCAction action, PathSet & result, | ||||||
|      |      | ||||||
|     /* Read the Nix store directory to find all currently existing
 |     /* Read the Nix store directory to find all currently existing
 | ||||||
|        paths. */ |        paths. */ | ||||||
|     Paths storePaths = readDirectory(nixStore); |     PathSet storePathSet; | ||||||
|     PathSet storePaths2; |     if (action != gcDeleteSpecific) { | ||||||
|     for (Paths::iterator i = storePaths.begin(); i != storePaths.end(); ++i) |         Paths entries = readDirectory(nixStore); | ||||||
|         storePaths2.insert(canonPath(nixStore + "/" + *i)); |         for (Paths::iterator i = entries.begin(); i != entries.end(); ++i) | ||||||
|  |             storePathSet.insert(canonPath(nixStore + "/" + *i)); | ||||||
|  |     } else { | ||||||
|  |         for (PathSet::iterator i = pathsToDelete.begin(); | ||||||
|  |              i != pathsToDelete.end(); ++i) | ||||||
|  |         { | ||||||
|  |             assertStorePath(*i); | ||||||
|  |             storePathSet.insert(*i); | ||||||
|  |         } | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     /* Topologically sort them under the `referrers' relation.  That
 |     /* Topologically sort them under the `referrers' relation.  That
 | ||||||
|        is, a < b iff a is in referrers(b).  This gives us the order in |        is, a < b iff a is in referrers(b).  This gives us the order in | ||||||
|        which things can be deleted safely. */ |        which things can be deleted safely. */ | ||||||
|     /* !!! when we have multiple output paths per derivation, this
 |     /* !!! when we have multiple output paths per derivation, this
 | ||||||
|        will not work anymore because we get cycles. */ |        will not work anymore because we get cycles. */ | ||||||
|     storePaths = topoSort(storePaths2); |     Paths storePaths = topoSort(storePathSet); | ||||||
| 
 | 
 | ||||||
|     /* Try to delete store paths in the topologically sorted order. */ |     /* Try to delete store paths in the topologically sorted order. */ | ||||||
|     for (Paths::iterator i = storePaths.begin(); i != storePaths.end(); ++i) { |     for (Paths::iterator i = storePaths.begin(); i != storePaths.end(); ++i) { | ||||||
|  | @ -416,6 +425,8 @@ void collectGarbage(GCAction action, PathSet & result, | ||||||
|         debug(format("considering deletion of `%1%'") % *i); |         debug(format("considering deletion of `%1%'") % *i); | ||||||
|          |          | ||||||
|         if (livePaths.find(*i) != livePaths.end()) { |         if (livePaths.find(*i) != livePaths.end()) { | ||||||
|  |             if (action == gcDeleteSpecific) | ||||||
|  |                 throw Error(format("cannot delete path `%1%' since it is still alive") % *i); | ||||||
|             debug(format("live path `%1%'") % *i); |             debug(format("live path `%1%'") % *i); | ||||||
|             continue; |             continue; | ||||||
|         } |         } | ||||||
|  | @ -430,7 +441,7 @@ void collectGarbage(GCAction action, PathSet & result, | ||||||
| 
 | 
 | ||||||
|         AutoCloseFD fdLock; |         AutoCloseFD fdLock; | ||||||
| 
 | 
 | ||||||
|         if (action == gcDeleteDead) { |         if (action == gcDeleteDead || action == gcDeleteSpecific) { | ||||||
| 
 | 
 | ||||||
|             /* Only delete a lock file if we can acquire a write lock
 |             /* Only delete a lock file if we can acquire a write lock
 | ||||||
|                on it.  That means that it's either stale, or the |                on it.  That means that it's either stale, or the | ||||||
|  |  | ||||||
|  | @ -10,6 +10,7 @@ typedef enum { | ||||||
|     gcReturnLive, |     gcReturnLive, | ||||||
|     gcReturnDead, |     gcReturnDead, | ||||||
|     gcDeleteDead, |     gcDeleteDead, | ||||||
|  |     gcDeleteSpecific, | ||||||
| } GCAction; | } GCAction; | ||||||
| 
 | 
 | ||||||
| /* If `action' is set to `gcReturnRoots', find and return the set of
 | /* If `action' is set to `gcReturnRoots', find and return the set of
 | ||||||
|  | @ -19,8 +20,8 @@ typedef enum { | ||||||
|    closure of) the roots.  If `action' is `gcReturnDead', return the |    closure of) the roots.  If `action' is `gcReturnDead', return the | ||||||
|    set of paths not reachable from the roots.  If `action' is |    set of paths not reachable from the roots.  If `action' is | ||||||
|    `gcDeleteDead', actually delete the latter set. */ |    `gcDeleteDead', actually delete the latter set. */ | ||||||
| void collectGarbage(GCAction action, PathSet & result, | void collectGarbage(GCAction action, const PathSet & pathsToDelete, | ||||||
|     unsigned long long & bytesFreed); |     PathSet & result, unsigned long long & bytesFreed); | ||||||
| 
 | 
 | ||||||
| /* Register a temporary GC root.  This root will automatically
 | /* Register a temporary GC root.  This root will automatically
 | ||||||
|    disappear when this process exits.  WARNING: this function should |    disappear when this process exits.  WARNING: this function should | ||||||
|  |  | ||||||
|  | @ -7,6 +7,7 @@ Operations: | ||||||
|   --realise / -r: ensure path validity; if a derivation, ensure that |   --realise / -r: ensure path validity; if a derivation, ensure that | ||||||
|       validity of the outputs |       validity of the outputs | ||||||
|   --add / -A: copy a path to the Nix store |   --add / -A: copy a path to the Nix store | ||||||
|  |   --delete: safely delete paths from the Nix store | ||||||
|   --query / -q: query information |   --query / -q: query information | ||||||
| 
 | 
 | ||||||
|   --register-substitutes: register a substitute expression (dangerous!) |   --register-substitutes: register a substitute expression (dangerous!) | ||||||
|  |  | ||||||
|  | @ -518,7 +518,7 @@ static void opGC(Strings opFlags, Strings opArgs) | ||||||
| 
 | 
 | ||||||
|     PathSet result; |     PathSet result; | ||||||
|     PrintFreed freed(action == gcDeleteDead); |     PrintFreed freed(action == gcDeleteDead); | ||||||
|     collectGarbage(action, result, freed.bytesFreed); |     collectGarbage(action, PathSet(), result, freed.bytesFreed); | ||||||
| 
 | 
 | ||||||
|     if (action != gcDeleteDead) { |     if (action != gcDeleteDead) { | ||||||
|         for (PathSet::iterator i = result.begin(); i != result.end(); ++i) |         for (PathSet::iterator i = result.begin(); i != result.end(); ++i) | ||||||
|  | @ -527,6 +527,25 @@ static void opGC(Strings opFlags, Strings opArgs) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | /* Remove paths from the Nix store if possible (i.e., if they do not
 | ||||||
|  |    have any remaining referrers and are not reachable from any GC | ||||||
|  |    roots). */ | ||||||
|  | static void opDelete(Strings opFlags, Strings opArgs) | ||||||
|  | { | ||||||
|  |     if (!opFlags.empty()) throw UsageError("unknown flag"); | ||||||
|  | 
 | ||||||
|  |     PathSet pathsToDelete; | ||||||
|  |     for (Strings::iterator i = opArgs.begin(); | ||||||
|  |          i != opArgs.end(); ++i) | ||||||
|  |         pathsToDelete.insert(fixPath(*i)); | ||||||
|  |      | ||||||
|  |     PathSet dummy; | ||||||
|  |     PrintFreed freed(true); | ||||||
|  |     collectGarbage(gcDeleteSpecific, pathsToDelete, | ||||||
|  |         dummy, freed.bytesFreed); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| /* A sink that writes dump output to stdout. */ | /* A sink that writes dump output to stdout. */ | ||||||
| struct StdoutSink : DumpSink | struct StdoutSink : DumpSink | ||||||
| { | { | ||||||
|  | @ -621,6 +640,8 @@ void run(Strings args) | ||||||
|             op = opAddFixed; |             op = opAddFixed; | ||||||
|         else if (arg == "--print-fixed-path") |         else if (arg == "--print-fixed-path") | ||||||
|             op = opPrintFixedPath; |             op = opPrintFixedPath; | ||||||
|  |         else if (arg == "--delete") | ||||||
|  |             op = opDelete; | ||||||
|         else if (arg == "--query" || arg == "-q") |         else if (arg == "--query" || arg == "-q") | ||||||
|             op = opQuery; |             op = opQuery; | ||||||
|         else if (arg == "--register-substitutes") |         else if (arg == "--register-substitutes") | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue