* Merged the no-bdb branch (-r10900:HEAD
https://svn.nixos.org/repos/nix/nix/branches/no-bdb).
This commit is contained in:
		
							parent
							
								
									4ed01ed791
								
							
						
					
					
						commit
						b0e92f6d47
					
				
					 24 changed files with 923 additions and 739 deletions
				
			
		
							
								
								
									
										21
									
								
								configure.ac
									
										
									
									
									
								
							
							
						
						
									
										21
									
								
								configure.ac
									
										
									
									
									
								
							|  | @ -16,7 +16,7 @@ if test "$STABLE" != "1"; then | ||||||
|     fi |     fi | ||||||
| fi | fi | ||||||
| 
 | 
 | ||||||
| AC_DEFINE_UNQUOTED(NIX_VERSION, ["$VERSION"], [version]) | AC_DEFINE_UNQUOTED(NIX_VERSION, ["$VERSION"], [Nix version.]) | ||||||
| 
 | 
 | ||||||
| AC_PREFIX_DEFAULT(/nix) | AC_PREFIX_DEFAULT(/nix) | ||||||
| 
 | 
 | ||||||
|  | @ -54,7 +54,7 @@ case $sys_name in | ||||||
| esac | esac | ||||||
| 
 | 
 | ||||||
| AC_ARG_WITH(system, AC_HELP_STRING([--with-system=SYSTEM], | AC_ARG_WITH(system, AC_HELP_STRING([--with-system=SYSTEM], | ||||||
|   [platform identifier (e.g., `i686-linux')]), |   [Platform identifier (e.g., `i686-linux').]), | ||||||
|   system=$withval, system="${machine_name}-${sys_name}") |   system=$withval, system="${machine_name}-${sys_name}") | ||||||
| AC_MSG_RESULT($system) | AC_MSG_RESULT($system) | ||||||
| AC_SUBST(system) | AC_SUBST(system) | ||||||
|  | @ -94,7 +94,7 @@ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <iostream> | ||||||
| using namespace std; | using namespace std; | ||||||
| static char buf[1024];]], | static char buf[1024];]], | ||||||
|     [[cerr.rdbuf()->pubsetbuf(buf, sizeof(buf));]])], |     [[cerr.rdbuf()->pubsetbuf(buf, sizeof(buf));]])], | ||||||
|     [AC_MSG_RESULT(yes) AC_DEFINE(HAVE_PUBSETBUF, 1, [whether pubsetbuf is available])], |     [AC_MSG_RESULT(yes) AC_DEFINE(HAVE_PUBSETBUF, 1, [Whether pubsetbuf is available.])], | ||||||
|     AC_MSG_RESULT(no)) |     AC_MSG_RESULT(no)) | ||||||
| AC_LANG_POP(C++) | AC_LANG_POP(C++) | ||||||
| 
 | 
 | ||||||
|  | @ -177,8 +177,13 @@ AC_ARG_WITH(store-dir, AC_HELP_STRING([--with-store-dir=PATH], | ||||||
|   storedir=$withval, storedir='${prefix}/store') |   storedir=$withval, storedir='${prefix}/store') | ||||||
| AC_SUBST(storedir) | AC_SUBST(storedir) | ||||||
| 
 | 
 | ||||||
|  | AC_ARG_ENABLE(old-db-compat, AC_HELP_STRING([--disable-old-db-compat], | ||||||
|  |   [disable support for converting from old Berkeley DB-based Nix stores]), | ||||||
|  |   old_db_compat=$enableval, old_db_compat=yes) | ||||||
|  | AM_CONDITIONAL(OLD_DB_COMPAT, test "$old_db_compat" = "yes") | ||||||
|  | 
 | ||||||
| AC_ARG_WITH(bdb, AC_HELP_STRING([--with-bdb=PATH], | AC_ARG_WITH(bdb, AC_HELP_STRING([--with-bdb=PATH], | ||||||
|   [prefix of Berkeley DB]), |   [prefix of Berkeley DB (for Nix <= 0.11 compatibility)]), | ||||||
|   bdb=$withval, bdb=) |   bdb=$withval, bdb=) | ||||||
| AM_CONDITIONAL(HAVE_BDB, test -n "$bdb") | AM_CONDITIONAL(HAVE_BDB, test -n "$bdb") | ||||||
| if test -z "$bdb"; then | if test -z "$bdb"; then | ||||||
|  | @ -188,6 +193,12 @@ else | ||||||
|   bdb_lib="-L$bdb/lib -ldb_cxx" |   bdb_lib="-L$bdb/lib -ldb_cxx" | ||||||
|   bdb_include="-I$bdb/include" |   bdb_include="-I$bdb/include" | ||||||
| fi | fi | ||||||
|  | if test "$old_db_compat" = "no"; then | ||||||
|  |   bdb_lib= | ||||||
|  |   bdb_include= | ||||||
|  | else | ||||||
|  |   AC_DEFINE(OLD_DB_COMPAT, 1, [Whether to support converting from old Berkeley DB-based Nix stores.]) | ||||||
|  | fi | ||||||
| AC_SUBST(bdb_lib) | AC_SUBST(bdb_lib) | ||||||
| AC_SUBST(bdb_include) | AC_SUBST(bdb_include) | ||||||
| 
 | 
 | ||||||
|  | @ -216,7 +227,7 @@ if test -n "$openssl"; then | ||||||
|   LDFLAGS="-L$openssl/lib -lcrypto $LDFLAGS" |   LDFLAGS="-L$openssl/lib -lcrypto $LDFLAGS" | ||||||
|   CFLAGS="-I$openssl/include $CFLAGS" |   CFLAGS="-I$openssl/include $CFLAGS" | ||||||
|   CXXFLAGS="-I$openssl/include $CXXFLAGS" |   CXXFLAGS="-I$openssl/include $CXXFLAGS" | ||||||
|   AC_DEFINE(HAVE_OPENSSL, 1, [whether to use OpenSSL]) |   AC_DEFINE(HAVE_OPENSSL, 1, [Whether to use OpenSSL.]) | ||||||
| fi | fi | ||||||
| 
 | 
 | ||||||
| AC_ARG_WITH(bzip2, AC_HELP_STRING([--with-bzip2=PATH], | AC_ARG_WITH(bzip2, AC_HELP_STRING([--with-bzip2=PATH], | ||||||
|  |  | ||||||
							
								
								
									
										8
									
								
								externals/Makefile.am
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										8
									
								
								externals/Makefile.am
									
										
									
									
										vendored
									
									
								
							|  | @ -2,6 +2,8 @@ | ||||||
| 
 | 
 | ||||||
| DB = db-4.5.20 | DB = db-4.5.20 | ||||||
| 
 | 
 | ||||||
|  | if OLD_DB_COMPAT | ||||||
|  | 
 | ||||||
| $(DB).tar.gz: | $(DB).tar.gz: | ||||||
| 	@echo "Nix requires Berkeley DB to build." | 	@echo "Nix requires Berkeley DB to build." | ||||||
| 	@echo "Please download version 4.5.20 from" | 	@echo "Please download version 4.5.20 from" | ||||||
|  | @ -32,6 +34,12 @@ build-db: have-db | ||||||
| 	touch build-db | 	touch build-db | ||||||
| endif | endif | ||||||
| 
 | 
 | ||||||
|  | else | ||||||
|  | 
 | ||||||
|  | build-db: | ||||||
|  | 
 | ||||||
|  | endif | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| # CWI ATerm | # CWI ATerm | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -2,7 +2,8 @@ pkglib_LTLIBRARIES = libstore.la | ||||||
| 
 | 
 | ||||||
| libstore_la_SOURCES = \ | libstore_la_SOURCES = \ | ||||||
|   store-api.cc local-store.cc remote-store.cc derivations.cc build.cc misc.cc \ |   store-api.cc local-store.cc remote-store.cc derivations.cc build.cc misc.cc \ | ||||||
|  globals.cc db.cc references.cc pathlocks.cc gc.cc  |   globals.cc db.cc references.cc pathlocks.cc gc.cc upgrade-schema.cc \ | ||||||
|  |   optimise-store.cc | ||||||
| 
 | 
 | ||||||
| pkginclude_HEADERS = \ | pkginclude_HEADERS = \ | ||||||
|   store-api.hh local-store.hh remote-store.hh derivations.hh misc.hh \ |   store-api.hh local-store.hh remote-store.hh derivations.hh misc.hh \ | ||||||
|  |  | ||||||
|  | @ -3,7 +3,6 @@ | ||||||
| #include "misc.hh" | #include "misc.hh" | ||||||
| #include "globals.hh" | #include "globals.hh" | ||||||
| #include "local-store.hh" | #include "local-store.hh" | ||||||
| #include "db.hh" |  | ||||||
| #include "util.hh" | #include "util.hh" | ||||||
| 
 | 
 | ||||||
| #include <map> | #include <map> | ||||||
|  | @ -207,7 +206,9 @@ private: | ||||||
|      |      | ||||||
| public: | public: | ||||||
| 
 | 
 | ||||||
|     Worker(); |     LocalStore & store; | ||||||
|  | 
 | ||||||
|  |     Worker(LocalStore & store); | ||||||
|     ~Worker(); |     ~Worker(); | ||||||
| 
 | 
 | ||||||
|     /* Make a goal (with caching). */ |     /* Make a goal (with caching). */ | ||||||
|  | @ -897,14 +898,14 @@ void DerivationGoal::haveDerivation() | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     assert(store->isValidPath(drvPath)); |     assert(worker.store.isValidPath(drvPath)); | ||||||
| 
 | 
 | ||||||
|     /* Get the derivation. */ |     /* Get the derivation. */ | ||||||
|     drv = derivationFromPath(drvPath); |     drv = derivationFromPath(drvPath); | ||||||
| 
 | 
 | ||||||
|     for (DerivationOutputs::iterator i = drv.outputs.begin(); |     for (DerivationOutputs::iterator i = drv.outputs.begin(); | ||||||
|          i != drv.outputs.end(); ++i) |          i != drv.outputs.end(); ++i) | ||||||
|         store->addTempRoot(i->second.path); |         worker.store.addTempRoot(i->second.path); | ||||||
| 
 | 
 | ||||||
|     /* Check what outputs paths are not already valid. */ |     /* Check what outputs paths are not already valid. */ | ||||||
|     PathSet invalidOutputs = checkPathValidity(false); |     PathSet invalidOutputs = checkPathValidity(false); | ||||||
|  | @ -938,7 +939,7 @@ void DerivationGoal::haveDerivation() | ||||||
|          i != invalidOutputs.end(); ++i) |          i != invalidOutputs.end(); ++i) | ||||||
|         /* Don't bother creating a substitution goal if there are no
 |         /* Don't bother creating a substitution goal if there are no
 | ||||||
|            substitutes. */ |            substitutes. */ | ||||||
|         if (store->hasSubstitutes(*i)) |         if (worker.store.hasSubstitutes(*i)) | ||||||
|             addWaitee(worker.makeSubstitutionGoal(*i)); |             addWaitee(worker.makeSubstitutionGoal(*i)); | ||||||
|      |      | ||||||
|     if (waitees.empty()) /* to prevent hang (no wake-up event) */ |     if (waitees.empty()) /* to prevent hang (no wake-up event) */ | ||||||
|  | @ -993,7 +994,7 @@ void DerivationGoal::outputsSubstituted() | ||||||
|             throw BuildError(format("`exportBuildReferencesGraph' contains a non-store path `%1%'") |             throw BuildError(format("`exportBuildReferencesGraph' contains a non-store path `%1%'") | ||||||
|                 % storePath); |                 % storePath); | ||||||
|         storePath = toStorePath(storePath); |         storePath = toStorePath(storePath); | ||||||
|         if (!store->isValidPath(storePath)) |         if (!worker.store.isValidPath(storePath)) | ||||||
|             throw BuildError(format("`exportBuildReferencesGraph' contains an invalid path `%1%'") |             throw BuildError(format("`exportBuildReferencesGraph' contains an invalid path `%1%'") | ||||||
|                 % storePath); |                 % storePath); | ||||||
|          |          | ||||||
|  | @ -1250,19 +1251,6 @@ PathSet outputPaths(const DerivationOutputs & outputs) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| string showPaths(const PathSet & paths) |  | ||||||
| { |  | ||||||
|     string s; |  | ||||||
|     for (PathSet::const_iterator i = paths.begin(); |  | ||||||
|          i != paths.end(); ++i) |  | ||||||
|     { |  | ||||||
|         if (s.size() != 0) s += ", "; |  | ||||||
|         s += "`" + *i + "'"; |  | ||||||
|     } |  | ||||||
|     return s; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| DerivationGoal::HookReply DerivationGoal::tryBuildHook() | DerivationGoal::HookReply DerivationGoal::tryBuildHook() | ||||||
| { | { | ||||||
|     if (!useBuildHook) return rpDecline; |     if (!useBuildHook) return rpDecline; | ||||||
|  | @ -1474,7 +1462,7 @@ DerivationGoal::PrepareBuildReply DerivationGoal::prepareBuild() | ||||||
|          i != drv.outputs.end(); ++i) |          i != drv.outputs.end(); ++i) | ||||||
|     { |     { | ||||||
|         Path path = i->second.path; |         Path path = i->second.path; | ||||||
|         if (store->isValidPath(path)) |         if (worker.store.isValidPath(path)) | ||||||
|             throw BuildError(format("obstructed build: path `%1%' exists") % path); |             throw BuildError(format("obstructed build: path `%1%' exists") % path); | ||||||
|         if (pathExists(path)) { |         if (pathExists(path)) { | ||||||
|             debug(format("removing unregistered path `%1%'") % path); |             debug(format("removing unregistered path `%1%'") % path); | ||||||
|  | @ -1502,7 +1490,7 @@ DerivationGoal::PrepareBuildReply DerivationGoal::prepareBuild() | ||||||
|         /* Add the relevant output closures of the input derivation
 |         /* Add the relevant output closures of the input derivation
 | ||||||
|            `*i' as input paths.  Only add the closures of output paths |            `*i' as input paths.  Only add the closures of output paths | ||||||
|            that are specified as inputs. */ |            that are specified as inputs. */ | ||||||
|         assert(store->isValidPath(i->first)); |         assert(worker.store.isValidPath(i->first)); | ||||||
|         Derivation inDrv = derivationFromPath(i->first); |         Derivation inDrv = derivationFromPath(i->first); | ||||||
|         for (StringSet::iterator j = i->second.begin(); |         for (StringSet::iterator j = i->second.begin(); | ||||||
|              j != i->second.end(); ++j) |              j != i->second.end(); ++j) | ||||||
|  | @ -1624,7 +1612,7 @@ void DerivationGoal::startBuilder() | ||||||
|             throw BuildError(format("`exportReferencesGraph' contains a non-store path `%1%'") |             throw BuildError(format("`exportReferencesGraph' contains a non-store path `%1%'") | ||||||
|                 % storePath); |                 % storePath); | ||||||
|         storePath = toStorePath(storePath); |         storePath = toStorePath(storePath); | ||||||
|         if (!store->isValidPath(storePath)) |         if (!worker.store.isValidPath(storePath)) | ||||||
|             throw BuildError(format("`exportReferencesGraph' contains an invalid path `%1%'") |             throw BuildError(format("`exportReferencesGraph' contains an invalid path `%1%'") | ||||||
|                 % storePath); |                 % storePath); | ||||||
| 
 | 
 | ||||||
|  | @ -1652,7 +1640,7 @@ void DerivationGoal::startBuilder() | ||||||
|             throw BuildError(format("`exportBuildReferencesGraph' contains a non-store path `%1%'") |             throw BuildError(format("`exportBuildReferencesGraph' contains a non-store path `%1%'") | ||||||
|                 % storePath); |                 % storePath); | ||||||
|         storePath = toStorePath(storePath); |         storePath = toStorePath(storePath); | ||||||
|         if (!store->isValidPath(storePath)) |         if (!worker.store.isValidPath(storePath)) | ||||||
|             throw BuildError(format("`exportBuildReferencesGraph' contains an invalid path `%1%'") |             throw BuildError(format("`exportBuildReferencesGraph' contains an invalid path `%1%'") | ||||||
|                 % storePath); |                 % storePath); | ||||||
| 
 | 
 | ||||||
|  | @ -1994,27 +1982,17 @@ void DerivationGoal::computeClosure() | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /* Register each output path as valid, and register the sets of
 |     /* Register each output path as valid, and register the sets of
 | ||||||
|        paths referenced by each of them.  This is wrapped in one |        paths referenced by each of them.  !!! this should be | ||||||
|        database transaction to ensure that if we crash, either |        atomic so that either all paths are registered as valid, or | ||||||
|        everything is registered or nothing is.  This is for |        none are. */ | ||||||
|        recoverability: unregistered paths in the store can be deleted |  | ||||||
|        arbitrarily, while registered paths can only be deleted by |  | ||||||
|        running the garbage collector. |  | ||||||
| 
 |  | ||||||
|        The reason that we do the transaction here and not on the fly |  | ||||||
|        while we are scanning (above) is so that we don't hold database |  | ||||||
|        locks for too long. */ |  | ||||||
|     Transaction txn; |  | ||||||
|     createStoreTransaction(txn); |  | ||||||
|     for (DerivationOutputs::iterator i = drv.outputs.begin();  |     for (DerivationOutputs::iterator i = drv.outputs.begin();  | ||||||
|          i != drv.outputs.end(); ++i) |          i != drv.outputs.end(); ++i) | ||||||
|     { |     { | ||||||
|         registerValidPath(txn, i->second.path, |         worker.store.registerValidPath(i->second.path, | ||||||
|             contentHashes[i->second.path], |             contentHashes[i->second.path], | ||||||
|             allReferences[i->second.path], |             allReferences[i->second.path], | ||||||
|             drvPath); |             drvPath); | ||||||
|     } |     } | ||||||
|     txn.commit(); |  | ||||||
| 
 | 
 | ||||||
|     /* It is now safe to delete the lock files, since all future
 |     /* It is now safe to delete the lock files, since all future
 | ||||||
|        lockers will see that the output paths are valid; they will not |        lockers will see that the output paths are valid; they will not | ||||||
|  | @ -2113,7 +2091,7 @@ PathSet DerivationGoal::checkPathValidity(bool returnValid) | ||||||
|     PathSet result; |     PathSet result; | ||||||
|     for (DerivationOutputs::iterator i = drv.outputs.begin(); |     for (DerivationOutputs::iterator i = drv.outputs.begin(); | ||||||
|          i != drv.outputs.end(); ++i) |          i != drv.outputs.end(); ++i) | ||||||
|         if (store->isValidPath(i->second.path)) { |         if (worker.store.isValidPath(i->second.path)) { | ||||||
|             if (returnValid) result.insert(i->second.path); |             if (returnValid) result.insert(i->second.path); | ||||||
|         } else { |         } else { | ||||||
|             if (!returnValid) result.insert(i->second.path); |             if (!returnValid) result.insert(i->second.path); | ||||||
|  | @ -2219,10 +2197,10 @@ void SubstitutionGoal::init() | ||||||
| { | { | ||||||
|     trace("init"); |     trace("init"); | ||||||
| 
 | 
 | ||||||
|     store->addTempRoot(storePath); |     worker.store.addTempRoot(storePath); | ||||||
|      |      | ||||||
|     /* If the path already exists we're done. */ |     /* If the path already exists we're done. */ | ||||||
|     if (store->isValidPath(storePath)) { |     if (worker.store.isValidPath(storePath)) { | ||||||
|         amDone(ecSuccess); |         amDone(ecSuccess); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  | @ -2293,7 +2271,7 @@ void SubstitutionGoal::referencesValid() | ||||||
|     for (PathSet::iterator i = references.begin(); |     for (PathSet::iterator i = references.begin(); | ||||||
|          i != references.end(); ++i) |          i != references.end(); ++i) | ||||||
|         if (*i != storePath) /* ignore self-references */ |         if (*i != storePath) /* ignore self-references */ | ||||||
|             assert(store->isValidPath(*i)); |             assert(worker.store.isValidPath(*i)); | ||||||
| 
 | 
 | ||||||
|     state = &SubstitutionGoal::tryToRun; |     state = &SubstitutionGoal::tryToRun; | ||||||
|     worker.waitForBuildSlot(shared_from_this()); |     worker.waitForBuildSlot(shared_from_this()); | ||||||
|  | @ -2327,7 +2305,7 @@ void SubstitutionGoal::tryToRun() | ||||||
|         (format("waiting for lock on `%1%'") % storePath).str()); |         (format("waiting for lock on `%1%'") % storePath).str()); | ||||||
| 
 | 
 | ||||||
|     /* Check again whether the path is invalid. */ |     /* Check again whether the path is invalid. */ | ||||||
|     if (store->isValidPath(storePath)) { |     if (worker.store.isValidPath(storePath)) { | ||||||
|         debug(format("store path `%1%' has become valid") % storePath); |         debug(format("store path `%1%' has become valid") % storePath); | ||||||
|         outputLock->setDeletion(true); |         outputLock->setDeletion(true); | ||||||
|         amDone(ecSuccess); |         amDone(ecSuccess); | ||||||
|  | @ -2434,11 +2412,8 @@ void SubstitutionGoal::finished() | ||||||
| 
 | 
 | ||||||
|     Hash contentHash = hashPath(htSHA256, storePath); |     Hash contentHash = hashPath(htSHA256, storePath); | ||||||
| 
 | 
 | ||||||
|     Transaction txn; |     worker.store.registerValidPath(storePath, contentHash, | ||||||
|     createStoreTransaction(txn); |  | ||||||
|     registerValidPath(txn, storePath, contentHash, |  | ||||||
|         references, deriver); |         references, deriver); | ||||||
|     txn.commit(); |  | ||||||
| 
 | 
 | ||||||
|     outputLock->setDeletion(true); |     outputLock->setDeletion(true); | ||||||
|      |      | ||||||
|  | @ -2472,7 +2447,8 @@ void SubstitutionGoal::handleEOF(int fd) | ||||||
| static bool working = false; | static bool working = false; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| Worker::Worker() | Worker::Worker(LocalStore & store) | ||||||
|  |     : store(store) | ||||||
| { | { | ||||||
|     /* Debugging: prevent recursive workers. */  |     /* Debugging: prevent recursive workers. */  | ||||||
|     if (working) abort(); |     if (working) abort(); | ||||||
|  | @ -2870,7 +2846,7 @@ void LocalStore::buildDerivations(const PathSet & drvPaths) | ||||||
|     startNest(nest, lvlDebug, |     startNest(nest, lvlDebug, | ||||||
|         format("building %1%") % showPaths(drvPaths)); |         format("building %1%") % showPaths(drvPaths)); | ||||||
| 
 | 
 | ||||||
|     Worker worker; |     Worker worker(*this); | ||||||
| 
 | 
 | ||||||
|     Goals goals; |     Goals goals; | ||||||
|     for (PathSet::const_iterator i = drvPaths.begin(); |     for (PathSet::const_iterator i = drvPaths.begin(); | ||||||
|  | @ -2895,9 +2871,9 @@ void LocalStore::buildDerivations(const PathSet & drvPaths) | ||||||
| void LocalStore::ensurePath(const Path & path) | void LocalStore::ensurePath(const Path & path) | ||||||
| { | { | ||||||
|     /* If the path is already valid, we're done. */ |     /* If the path is already valid, we're done. */ | ||||||
|     if (store->isValidPath(path)) return; |     if (isValidPath(path)) return; | ||||||
| 
 | 
 | ||||||
|     Worker worker; |     Worker worker(*this); | ||||||
|     GoalPtr goal = worker.makeSubstitutionGoal(path); |     GoalPtr goal = worker.makeSubstitutionGoal(path); | ||||||
|     Goals goals = singleton<Goals>(goal); |     Goals goals = singleton<Goals>(goal); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1,3 +1,7 @@ | ||||||
|  | #include "config.h" | ||||||
|  | 
 | ||||||
|  | #ifdef OLD_DB_COMPAT | ||||||
|  | 
 | ||||||
| #include "db.hh" | #include "db.hh" | ||||||
| #include "util.hh" | #include "util.hh" | ||||||
| #include "pathlocks.hh" | #include "pathlocks.hh" | ||||||
|  | @ -466,3 +470,5 @@ void Database::clearTable(const Transaction & txn, TableId table) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | @ -2,8 +2,6 @@ | ||||||
| #include "misc.hh" | #include "misc.hh" | ||||||
| #include "pathlocks.hh" | #include "pathlocks.hh" | ||||||
| #include "local-store.hh" | #include "local-store.hh" | ||||||
| #include "db.hh" |  | ||||||
| #include "util.hh" |  | ||||||
| 
 | 
 | ||||||
| #include <boost/shared_ptr.hpp> | #include <boost/shared_ptr.hpp> | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							|  | @ -4,18 +4,16 @@ | ||||||
| #include <string> | #include <string> | ||||||
| 
 | 
 | ||||||
| #include "store-api.hh" | #include "store-api.hh" | ||||||
|  | #include "util.hh" | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| namespace nix { | namespace nix { | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class Transaction; |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| /* Nix store and database schema version.  Version 1 (or 0) was Nix <=
 | /* Nix store and database schema version.  Version 1 (or 0) was Nix <=
 | ||||||
|    0.7.  Version 2 was Nix 0.8 and 0.9.  Version 3 is Nix 0.10. |    0.7.  Version 2 was Nix 0.8 and 0.9.  Version 3 is Nix 0.10. | ||||||
|    Version 4 is Nix 0.11. */ |    Version 4 is Nix 0.11.  Version 5 is Nix 0.12*/ | ||||||
| const int nixSchemaVersion = 4; | const int nixSchemaVersion = 5; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| extern string drvsLogDir; | extern string drvsLogDir; | ||||||
|  | @ -43,15 +41,9 @@ private: | ||||||
|      |      | ||||||
| public: | public: | ||||||
| 
 | 
 | ||||||
|     /* Open the database environment.  If `reserveSpace' is true, make
 |     /* Initialise the local store, upgrading the schema if
 | ||||||
|        sure that a big empty file exists in /nix/var/nix/db/reserved. |        necessary. */ | ||||||
|        If `reserveSpace' is false, delete this file if it exists.  The |     LocalStore(); | ||||||
|        idea is that on normal operation, the file exists; but when we |  | ||||||
|        run the garbage collector, it is deleted.  This is to ensure |  | ||||||
|        that the garbage collector has a small amount of disk space |  | ||||||
|        available, which is required to open the Berkeley DB |  | ||||||
|        environment. */ |  | ||||||
|     LocalStore(bool reserveSpace); |  | ||||||
| 
 | 
 | ||||||
|     ~LocalStore(); |     ~LocalStore(); | ||||||
|      |      | ||||||
|  | @ -100,33 +92,63 @@ public: | ||||||
|     void collectGarbage(GCAction action, const PathSet & pathsToDelete, |     void collectGarbage(GCAction action, const PathSet & pathsToDelete, | ||||||
|         bool ignoreLiveness, PathSet & result, unsigned long long & bytesFreed); |         bool ignoreLiveness, PathSet & result, unsigned long long & bytesFreed); | ||||||
| 
 | 
 | ||||||
|  |     /* Delete a path from the Nix store. */ | ||||||
|  |     void deleteFromStore(const Path & path, unsigned long long & bytesFreed); | ||||||
|  |      | ||||||
|     /* Optimise the disk space usage of the Nix store by hard-linking
 |     /* Optimise the disk space usage of the Nix store by hard-linking
 | ||||||
|        files with the same contents. */ |        files with the same contents. */ | ||||||
|     void optimiseStore(bool dryRun, OptimiseStats & stats); |     void optimiseStore(bool dryRun, OptimiseStats & stats); | ||||||
|  | 
 | ||||||
|  |     /* Check the integrity of the Nix store. */ | ||||||
|  |     void verifyStore(bool checkContents); | ||||||
|  | 
 | ||||||
|  |     /* Register the validity of a path, i.e., that `path' exists, that
 | ||||||
|  |        the paths referenced by it exists, and in the case of an output | ||||||
|  |        path of a derivation, that it has been produced by a succesful | ||||||
|  |        execution of the derivation (or something equivalent).  Also | ||||||
|  |        register the hash of the file system contents of the path.  The | ||||||
|  |        hash must be a SHA-256 hash. */ | ||||||
|  |     void registerValidPath(const Path & path, | ||||||
|  |         const Hash & hash, const PathSet & references, const Path & deriver); | ||||||
|  | 
 | ||||||
|  |     void registerValidPaths(const ValidPathInfos & infos); | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  | 
 | ||||||
|  |     Path schemaPath; | ||||||
|  | 
 | ||||||
|  |     /* Lock file used for upgrading. */ | ||||||
|  |     AutoCloseFD globalLock; | ||||||
|  | 
 | ||||||
|  |     /* !!! The cache can grow very big.  Maybe it should be pruned
 | ||||||
|  |        every once in a while. */ | ||||||
|  |     std::map<Path, ValidPathInfo> pathInfoCache; | ||||||
|  | 
 | ||||||
|  |     /* Store paths for which the referrers file must be purged. */ | ||||||
|  |     PathSet delayedUpdates; | ||||||
|  | 
 | ||||||
|  |     int getSchema(); | ||||||
|  | 
 | ||||||
|  |     void registerValidPath(const ValidPathInfo & info, bool ignoreValidity = false); | ||||||
|  | 
 | ||||||
|  |     ValidPathInfo queryPathInfo(const Path & path); | ||||||
|  | 
 | ||||||
|  |     void rewriteReferrers(const Path & path, bool purge, PathSet referrers); | ||||||
|  | 
 | ||||||
|  |     void flushDelayedUpdates(); | ||||||
|  |      | ||||||
|  |     bool queryReferrersInternal(const Path & path, PathSet & referrers); | ||||||
|  |      | ||||||
|  |     void invalidatePath(const Path & path); | ||||||
|  |      | ||||||
|  |     void upgradeStore12(); | ||||||
|  | 
 | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| /* Get a transaction object. */ |  | ||||||
| void createStoreTransaction(Transaction & txn); |  | ||||||
| 
 |  | ||||||
| /* Copy a path recursively. */ | /* Copy a path recursively. */ | ||||||
| void copyPath(const Path & src, const Path & dst); | void copyPath(const Path & src, const Path & dst); | ||||||
| 
 | 
 | ||||||
| /* Register the validity of a path, i.e., that `path' exists, that the
 |  | ||||||
|    paths referenced by it exists, and in the case of an output path of |  | ||||||
|    a derivation, that it has been produced by a succesful execution of |  | ||||||
|    the derivation (or something equivalent).  Also register the hash |  | ||||||
|    of the file system contents of the path.  The hash must be a |  | ||||||
|    SHA-256 hash. */ |  | ||||||
| void registerValidPath(const Transaction & txn, |  | ||||||
|     const Path & path, const Hash & hash, const PathSet & references, |  | ||||||
|     const Path & deriver); |  | ||||||
| 
 |  | ||||||
| typedef list<ValidPathInfo> ValidPathInfos; |  | ||||||
| 
 |  | ||||||
| void registerValidPaths(const Transaction & txn, |  | ||||||
|     const ValidPathInfos & infos); |  | ||||||
| 
 |  | ||||||
| /* "Fix", or canonicalise, the meta-data of the files in a store path
 | /* "Fix", or canonicalise, the meta-data of the files in a store path
 | ||||||
|    after it has been built.  In particular: |    after it has been built.  In particular: | ||||||
|    - the last modification date on each file is set to 0 (i.e., |    - the last modification date on each file is set to 0 (i.e., | ||||||
|  | @ -137,25 +159,10 @@ void registerValidPaths(const Transaction & txn, | ||||||
|      in a setuid Nix installation. */ |      in a setuid Nix installation. */ | ||||||
| void canonicalisePathMetaData(const Path & path); | void canonicalisePathMetaData(const Path & path); | ||||||
| 
 | 
 | ||||||
| /* Checks whether a path is valid. */  | void canonicalisePathMetaData(const Path & path, bool recurse); | ||||||
| bool isValidPathTxn(const Transaction & txn, const Path & path); |  | ||||||
| 
 |  | ||||||
| /* Sets the set of outgoing FS references for a store path.  Use with
 |  | ||||||
|    care! */ |  | ||||||
| void setReferences(const Transaction & txn, const Path & path, |  | ||||||
|     const PathSet & references); |  | ||||||
| 
 |  | ||||||
| /* Sets the deriver of a store path.  Use with care! */ |  | ||||||
| void setDeriver(const Transaction & txn, const Path & path, |  | ||||||
|     const Path & deriver); |  | ||||||
| 
 |  | ||||||
| /* Delete a value from the nixStore directory. */ |  | ||||||
| void deleteFromStore(const Path & path, unsigned long long & bytesFreed); |  | ||||||
| 
 | 
 | ||||||
| MakeError(PathInUse, Error); | MakeError(PathInUse, Error); | ||||||
| 
 | 
 | ||||||
| void verifyStore(bool checkContents); |  | ||||||
| 
 |  | ||||||
| /* Whether we are in build users mode. */ | /* Whether we are in build users mode. */ | ||||||
| bool haveBuildUsers(); | bool haveBuildUsers(); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1,6 +1,5 @@ | ||||||
| #include "misc.hh" | #include "misc.hh" | ||||||
| #include "store-api.hh" | #include "store-api.hh" | ||||||
| #include "db.hh" |  | ||||||
| 
 | 
 | ||||||
| #include <aterm2.h> | #include <aterm2.h> | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
							
								
								
									
										129
									
								
								src/libstore/optimise-store.cc
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										129
									
								
								src/libstore/optimise-store.cc
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,129 @@ | ||||||
|  | #include "util.hh" | ||||||
|  | #include "local-store.hh" | ||||||
|  | 
 | ||||||
|  | #include <sys/types.h> | ||||||
|  | #include <sys/stat.h> | ||||||
|  | #include <unistd.h> | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | namespace nix { | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | typedef std::map<Hash, std::pair<Path, ino_t> > HashToPath; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | static void makeWritable(const Path & path) | ||||||
|  | { | ||||||
|  |     struct stat st; | ||||||
|  |     if (lstat(path.c_str(), &st)) | ||||||
|  | 	throw SysError(format("getting attributes of path `%1%'") % path); | ||||||
|  |     if (chmod(path.c_str(), st.st_mode | S_IWUSR) == -1) | ||||||
|  |         throw SysError(format("changing writability of `%1%'") % path); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | static void hashAndLink(bool dryRun, HashToPath & hashToPath, | ||||||
|  |     OptimiseStats & stats, const Path & path) | ||||||
|  | { | ||||||
|  |     struct stat st; | ||||||
|  |     if (lstat(path.c_str(), &st)) | ||||||
|  | 	throw SysError(format("getting attributes of path `%1%'") % path); | ||||||
|  | 
 | ||||||
|  |     /* Sometimes SNAFUs can cause files in the Nix store to be
 | ||||||
|  |        modified, in particular when running programs as root under | ||||||
|  |        NixOS (example: $fontconfig/var/cache being modified).  Skip | ||||||
|  |        those files. */ | ||||||
|  |     if (S_ISREG(st.st_mode) && (st.st_mode & S_IWUSR)) { | ||||||
|  |         printMsg(lvlError, format("skipping suspicious writable file `%1%'") % path); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /* We can hard link regular files and symlinks. */ | ||||||
|  |     if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)) { | ||||||
|  | 
 | ||||||
|  |         /* Hash the file.  Note that hashPath() returns the hash over
 | ||||||
|  |            the NAR serialisation, which includes the execute bit on | ||||||
|  |            the file.  Thus, executable and non-executable files with | ||||||
|  |            the same contents *won't* be linked (which is good because | ||||||
|  |            otherwise the permissions would be screwed up). | ||||||
|  | 
 | ||||||
|  |            Also note that if `path' is a symlink, then we're hashing | ||||||
|  |            the contents of the symlink (i.e. the result of | ||||||
|  |            readlink()), not the contents of the target (which may not | ||||||
|  |            even exist). */ | ||||||
|  |         Hash hash = hashPath(htSHA256, path); | ||||||
|  |         stats.totalFiles++; | ||||||
|  |         printMsg(lvlDebug, format("`%1%' has hash `%2%'") % path % printHash(hash)); | ||||||
|  | 
 | ||||||
|  |         std::pair<Path, ino_t> prevPath = hashToPath[hash]; | ||||||
|  |          | ||||||
|  |         if (prevPath.first == "") { | ||||||
|  |             hashToPath[hash] = std::pair<Path, ino_t>(path, st.st_ino); | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |              | ||||||
|  |         /* Yes!  We've seen a file with the same contents.  Replace
 | ||||||
|  |            the current file with a hard link to that file. */ | ||||||
|  |         stats.sameContents++; | ||||||
|  |         if (prevPath.second == st.st_ino) { | ||||||
|  |             printMsg(lvlDebug, format("`%1%' is already linked to `%2%'") % path % prevPath.first); | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         if (!dryRun) { | ||||||
|  |              | ||||||
|  |             printMsg(lvlTalkative, format("linking `%1%' to `%2%'") % path % prevPath.first); | ||||||
|  | 
 | ||||||
|  |             Path tempLink = (format("%1%.tmp-%2%-%3%") | ||||||
|  |                 % path % getpid() % rand()).str(); | ||||||
|  | 
 | ||||||
|  |             /* Make the containing directory writable, but only if
 | ||||||
|  |                it's not the store itself (we don't want or need to | ||||||
|  |                mess with  its permissions). */ | ||||||
|  |             bool mustToggle = !isStorePath(path); | ||||||
|  |             if (mustToggle) makeWritable(dirOf(path)); | ||||||
|  |          | ||||||
|  |             if (link(prevPath.first.c_str(), tempLink.c_str()) == -1) | ||||||
|  |                 throw SysError(format("cannot link `%1%' to `%2%'") | ||||||
|  |                     % tempLink % prevPath.first); | ||||||
|  | 
 | ||||||
|  |             /* Atomically replace the old file with the new hard link. */ | ||||||
|  |             if (rename(tempLink.c_str(), path.c_str()) == -1) | ||||||
|  |                 throw SysError(format("cannot rename `%1%' to `%2%'") | ||||||
|  |                     % tempLink % path); | ||||||
|  | 
 | ||||||
|  |             /* Make the directory read-only again and reset its
 | ||||||
|  |                timestamp back to 0. */ | ||||||
|  |             if (mustToggle) canonicalisePathMetaData(dirOf(path), false); | ||||||
|  |              | ||||||
|  |         } else | ||||||
|  |             printMsg(lvlTalkative, format("would link `%1%' to `%2%'") % path % prevPath.first); | ||||||
|  |          | ||||||
|  |         stats.filesLinked++; | ||||||
|  |         stats.bytesFreed += st.st_size; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (S_ISDIR(st.st_mode)) { | ||||||
|  |         Strings names = readDirectory(path); | ||||||
|  | 	for (Strings::iterator i = names.begin(); i != names.end(); ++i) | ||||||
|  | 	    hashAndLink(dryRun, hashToPath, stats, path + "/" + *i); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | void LocalStore::optimiseStore(bool dryRun, OptimiseStats & stats) | ||||||
|  | { | ||||||
|  |     HashToPath hashToPath; | ||||||
|  | 
 | ||||||
|  |     PathSet paths = queryValidPaths(); | ||||||
|  | 
 | ||||||
|  |     for (PathSet::iterator i = paths.begin(); i != paths.end(); ++i) { | ||||||
|  |         addTempRoot(*i); | ||||||
|  |         if (!isValidPath(*i)) continue; /* path was GC'ed, probably */ | ||||||
|  |         startNest(nest, lvlChatty, format("hashing files in `%1%'") % *i); | ||||||
|  |         hashAndLink(dryRun, hashToPath, stats, *i); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | @ -205,6 +205,19 @@ ValidPathInfo decodeValidPathInfo(std::istream & str, bool hashGiven) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | string showPaths(const PathSet & paths) | ||||||
|  | { | ||||||
|  |     string s; | ||||||
|  |     for (PathSet::const_iterator i = paths.begin(); | ||||||
|  |          i != paths.end(); ++i) | ||||||
|  |     { | ||||||
|  |         if (s.size() != 0) s += ", "; | ||||||
|  |         s += "`" + *i + "'"; | ||||||
|  |     } | ||||||
|  |     return s; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | @ -219,10 +232,10 @@ namespace nix { | ||||||
| boost::shared_ptr<StoreAPI> store; | boost::shared_ptr<StoreAPI> store; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| boost::shared_ptr<StoreAPI> openStore(bool reserveSpace) | boost::shared_ptr<StoreAPI> openStore() | ||||||
| { | { | ||||||
|     if (getEnv("NIX_REMOTE") == "") |     if (getEnv("NIX_REMOTE") == "") | ||||||
|         return boost::shared_ptr<StoreAPI>(new LocalStore(reserveSpace)); |         return boost::shared_ptr<StoreAPI>(new LocalStore()); | ||||||
|     else |     else | ||||||
|         return boost::shared_ptr<StoreAPI>(new RemoteStore()); |         return boost::shared_ptr<StoreAPI>(new RemoteStore()); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -249,7 +249,12 @@ extern boost::shared_ptr<StoreAPI> store; | ||||||
| 
 | 
 | ||||||
| /* Factory method: open the Nix database, either through the local or
 | /* Factory method: open the Nix database, either through the local or
 | ||||||
|    remote implementation. */ |    remote implementation. */ | ||||||
| boost::shared_ptr<StoreAPI> openStore(bool reserveSpace = true); | boost::shared_ptr<StoreAPI> openStore(); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | /* Display a set of paths in human-readable form (i.e., between quotes
 | ||||||
|  |    and separated by commas). */ | ||||||
|  | string showPaths(const PathSet & paths); | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| string makeValidityRegistration(const PathSet & paths, | string makeValidityRegistration(const PathSet & paths, | ||||||
|  | @ -261,8 +266,12 @@ struct ValidPathInfo | ||||||
|     Path deriver; |     Path deriver; | ||||||
|     Hash hash; |     Hash hash; | ||||||
|     PathSet references; |     PathSet references; | ||||||
|  |     time_t registrationTime; | ||||||
|  |     ValidPathInfo() : registrationTime(0) { } | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | typedef list<ValidPathInfo> ValidPathInfos; | ||||||
|  | 
 | ||||||
| ValidPathInfo decodeValidPathInfo(std::istream & str, | ValidPathInfo decodeValidPathInfo(std::istream & str, | ||||||
|     bool hashGiven = false); |     bool hashGiven = false); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
							
								
								
									
										108
									
								
								src/libstore/upgrade-schema.cc
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										108
									
								
								src/libstore/upgrade-schema.cc
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,108 @@ | ||||||
|  | #include "db.hh" | ||||||
|  | #include "hash.hh" | ||||||
|  | #include "util.hh" | ||||||
|  | #include "local-store.hh" | ||||||
|  | #include "globals.hh" | ||||||
|  | #include "pathlocks.hh" | ||||||
|  | #include "config.h" | ||||||
|  | 
 | ||||||
|  | #include <iostream> | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | namespace nix { | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | Hash parseHashField(const Path & path, const string & s); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | /* Upgrade from schema 4 (Nix 0.11) to schema 5 (Nix >= 0.12).  The
 | ||||||
|  |    old schema uses Berkeley DB, the new one stores store path | ||||||
|  |    meta-information in files. */ | ||||||
|  | void LocalStore::upgradeStore12() | ||||||
|  | { | ||||||
|  | #if OLD_DB_COMPAT | ||||||
|  |      | ||||||
|  | #ifdef __CYGWIN__ | ||||||
|  |     /* Cygwin can't upgrade a read lock to a write lock... */ | ||||||
|  |     lockFile(globalLock, ltNone, true); | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  |     if (!lockFile(globalLock, ltWrite, false)) { | ||||||
|  |         printMsg(lvlError, "waiting for exclusive access to the Nix store..."); | ||||||
|  |         lockFile(globalLock, ltWrite, true); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     printMsg(lvlError, "upgrading Nix store to new schema (this may take a while)..."); | ||||||
|  | 
 | ||||||
|  |     if (getSchema() >= nixSchemaVersion) return; /* somebody else beat us to it */ | ||||||
|  | 
 | ||||||
|  |     /* Open the old Nix database and tables. */ | ||||||
|  |     Database nixDB; | ||||||
|  |     nixDB.open(nixDBPath); | ||||||
|  |      | ||||||
|  |     /* dbValidPaths :: Path -> ()
 | ||||||
|  | 
 | ||||||
|  |        The existence of a key $p$ indicates that path $p$ is valid | ||||||
|  |        (that is, produced by a succesful build). */ | ||||||
|  |     TableId dbValidPaths = nixDB.openTable("validpaths"); | ||||||
|  | 
 | ||||||
|  |     /* dbReferences :: Path -> [Path]
 | ||||||
|  | 
 | ||||||
|  |        This table lists the outgoing file system references for each | ||||||
|  |        output path that has been built by a Nix derivation.  These are | ||||||
|  |        found by scanning the path for the hash components of input | ||||||
|  |        paths. */ | ||||||
|  |     TableId dbReferences = nixDB.openTable("references"); | ||||||
|  | 
 | ||||||
|  |     /* dbReferrers :: Path -> Path
 | ||||||
|  | 
 | ||||||
|  |        This table is just the reverse mapping of dbReferences.  This | ||||||
|  |        table can have duplicate keys, each corresponding value | ||||||
|  |        denoting a single referrer. */ | ||||||
|  |     // Not needed for conversion: it's just the inverse of
 | ||||||
|  |     // references.
 | ||||||
|  |     // TableId dbReferrers = nixDB.openTable("referrers");
 | ||||||
|  | 
 | ||||||
|  |     /* dbDerivers :: Path -> [Path]
 | ||||||
|  | 
 | ||||||
|  |        This table lists the derivation used to build a path.  There | ||||||
|  |        can only be multiple such paths for fixed-output derivations | ||||||
|  |        (i.e., derivations specifying an expected hash). */ | ||||||
|  |     TableId dbDerivers = nixDB.openTable("derivers"); | ||||||
|  | 
 | ||||||
|  |     Paths paths; | ||||||
|  |     nixDB.enumTable(noTxn, dbValidPaths, paths); | ||||||
|  |      | ||||||
|  |     for (Paths::iterator i = paths.begin(); i != paths.end(); ++i) { | ||||||
|  |         ValidPathInfo info; | ||||||
|  |         info.path = *i; | ||||||
|  |          | ||||||
|  |         Paths references; | ||||||
|  |         nixDB.queryStrings(noTxn, dbReferences, *i, references); | ||||||
|  |         info.references.insert(references.begin(), references.end()); | ||||||
|  |          | ||||||
|  |         string s; | ||||||
|  |         nixDB.queryString(noTxn, dbValidPaths, *i, s); | ||||||
|  |         info.hash = parseHashField(*i, s); | ||||||
|  |          | ||||||
|  |         nixDB.queryString(noTxn, dbDerivers, *i, info.deriver); | ||||||
|  |          | ||||||
|  |         registerValidPath(info, true); | ||||||
|  |         std::cerr << "."; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     std::cerr << std::endl; | ||||||
|  | 
 | ||||||
|  |     writeFile(schemaPath, (format("%1%") % nixSchemaVersion).str()); | ||||||
|  | 
 | ||||||
|  |     lockFile(globalLock, ltRead, true); | ||||||
|  | 
 | ||||||
|  | #else | ||||||
|  |     throw Error( | ||||||
|  |         "Your Nix store has a database in Berkeley DB format. To convert\n" | ||||||
|  |         "to the new format, please compile Nix with Berkeley DB support."); | ||||||
|  | #endif | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | @ -361,9 +361,10 @@ Path createTempDir(const Path & tmpRoot, const Path & prefix, | ||||||
| 
 | 
 | ||||||
| Paths createDirs(const Path & path) | Paths createDirs(const Path & path) | ||||||
| { | { | ||||||
|     if (path == "/") return Paths(); |     Paths created; | ||||||
|     Paths created = createDirs(dirOf(path)); |     if (path == "/") return created; | ||||||
|     if (!pathExists(path)) { |     if (!pathExists(path)) { | ||||||
|  |         created = createDirs(dirOf(path)); | ||||||
|         if (mkdir(path.c_str(), 0777) == -1) |         if (mkdir(path.c_str(), 0777) == -1) | ||||||
|             throw SysError(format("creating directory `%1%'") % path); |             throw SysError(format("creating directory `%1%'") % path); | ||||||
|         created.push_back(path); |         created.push_back(path); | ||||||
|  |  | ||||||
|  | @ -12,6 +12,10 @@ | ||||||
| namespace nix { | namespace nix { | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | #define foreach(it_type, it, collection)                                \ | ||||||
|  |     for (it_type it = collection.begin(); it != collection.end(); ++it) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| /* Return an environment variable. */ | /* Return an environment variable. */ | ||||||
| string getEnv(const string & key, const string & def = ""); | string getEnv(const string & key, const string & def = ""); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -13,7 +13,6 @@ | ||||||
| #include "common-opts.hh" | #include "common-opts.hh" | ||||||
| #include "xml-writer.hh" | #include "xml-writer.hh" | ||||||
| #include "store-api.hh" | #include "store-api.hh" | ||||||
| #include "db.hh" |  | ||||||
| #include "util.hh" | #include "util.hh" | ||||||
| 
 | 
 | ||||||
| #include <cerrno> | #include <cerrno> | ||||||
|  |  | ||||||
|  | @ -1,7 +1,6 @@ | ||||||
| #include "dotgraph.hh" | #include "dotgraph.hh" | ||||||
| #include "util.hh" | #include "util.hh" | ||||||
| #include "store-api.hh" | #include "store-api.hh" | ||||||
| #include "db.hh" |  | ||||||
| 
 | 
 | ||||||
| #include <iostream> | #include <iostream> | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -7,7 +7,6 @@ | ||||||
| #include "shared.hh" | #include "shared.hh" | ||||||
| #include "dotgraph.hh" | #include "dotgraph.hh" | ||||||
| #include "local-store.hh" | #include "local-store.hh" | ||||||
| #include "db.hh" |  | ||||||
| #include "util.hh" | #include "util.hh" | ||||||
| #include "help.txt.hh" | #include "help.txt.hh" | ||||||
| 
 | 
 | ||||||
|  | @ -31,6 +30,14 @@ static int rootNr = 0; | ||||||
| static bool indirectRoot = false; | static bool indirectRoot = false; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | LocalStore & ensureLocalStore() | ||||||
|  | { | ||||||
|  |     LocalStore * store2(dynamic_cast<LocalStore *>(store.get())); | ||||||
|  |     if (!store2) throw Error("you don't have sufficient rights to use --verify"); | ||||||
|  |     return *store2; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| static Path useDeriver(Path path) | static Path useDeriver(Path path) | ||||||
| {        | {        | ||||||
|     if (!isDerivation(path)) { |     if (!isDerivation(path)) { | ||||||
|  | @ -430,10 +437,7 @@ static void registerValidity(bool reregister, bool hashGiven, bool canonicalise) | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     Transaction txn; |     ensureLocalStore().registerValidPaths(infos); | ||||||
|     createStoreTransaction(txn); |  | ||||||
|     registerValidPaths(txn, infos); |  | ||||||
|     txn.commit(); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | @ -641,11 +645,10 @@ static void opVerify(Strings opFlags, Strings opArgs) | ||||||
|         if (*i == "--check-contents") checkContents = true; |         if (*i == "--check-contents") checkContents = true; | ||||||
|         else throw UsageError(format("unknown flag `%1%'") % *i); |         else throw UsageError(format("unknown flag `%1%'") % *i); | ||||||
|      |      | ||||||
|     verifyStore(checkContents); |     ensureLocalStore().verifyStore(checkContents); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| static void showOptimiseStats(OptimiseStats & stats) | static void showOptimiseStats(OptimiseStats & stats) | ||||||
| { | { | ||||||
|     printMsg(lvlError, |     printMsg(lvlError, | ||||||
|  | @ -671,12 +674,9 @@ static void opOptimise(Strings opFlags, Strings opArgs) | ||||||
|         if (*i == "--dry-run") dryRun = true; |         if (*i == "--dry-run") dryRun = true; | ||||||
|         else throw UsageError(format("unknown flag `%1%'") % *i); |         else throw UsageError(format("unknown flag `%1%'") % *i); | ||||||
| 
 | 
 | ||||||
|     LocalStore * store2(dynamic_cast<LocalStore *>(store.get())); |  | ||||||
|     if (!store2) throw Error("you don't have sufficient rights to use --optimise"); |  | ||||||
| 
 |  | ||||||
|     OptimiseStats stats; |     OptimiseStats stats; | ||||||
|     try { |     try { | ||||||
|         store2->optimiseStore(dryRun, stats); |         ensureLocalStore().optimiseStore(dryRun, stats); | ||||||
|     } catch (...) { |     } catch (...) { | ||||||
|         showOptimiseStats(stats); |         showOptimiseStats(stats); | ||||||
|         throw; |         throw; | ||||||
|  | @ -755,7 +755,7 @@ void run(Strings args) | ||||||
|     if (!op) throw UsageError("no operation specified"); |     if (!op) throw UsageError("no operation specified"); | ||||||
| 
 | 
 | ||||||
|     if (op != opDump && op != opRestore) /* !!! hack */ |     if (op != opDump && op != opRestore) /* !!! hack */ | ||||||
|         store = openStore(op != opGC); |         store = openStore(); | ||||||
| 
 | 
 | ||||||
|     op(opFlags, opArgs); |     op(opFlags, opArgs); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -474,7 +474,7 @@ static void processConnection() | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
|         /* Open the store. */ |         /* Open the store. */ | ||||||
|         store = boost::shared_ptr<StoreAPI>(new LocalStore(true)); |         store = boost::shared_ptr<StoreAPI>(new LocalStore()); | ||||||
| 
 | 
 | ||||||
|         stopWork(); |         stopWork(); | ||||||
|          |          | ||||||
|  |  | ||||||
|  | @ -19,7 +19,7 @@ TESTS = init.sh hash.sh lang.sh add.sh simple.sh dependencies.sh \ | ||||||
|   fallback.sh nix-push.sh gc.sh gc-concurrent.sh verify.sh nix-pull.sh \ |   fallback.sh nix-push.sh gc.sh gc-concurrent.sh verify.sh nix-pull.sh \ | ||||||
|   referrers.sh user-envs.sh logging.sh nix-build.sh misc.sh fixed.sh \ |   referrers.sh user-envs.sh logging.sh nix-build.sh misc.sh fixed.sh \ | ||||||
|   gc-runtime.sh install-package.sh check-refs.sh filter-source.sh \ |   gc-runtime.sh install-package.sh check-refs.sh filter-source.sh \ | ||||||
|   remote-store.sh |   remote-store.sh export.sh | ||||||
| 
 | 
 | ||||||
| XFAIL_TESTS = | XFAIL_TESTS = | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
							
								
								
									
										31
									
								
								tests/export.sh
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								tests/export.sh
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,31 @@ | ||||||
|  | source common.sh | ||||||
|  | 
 | ||||||
|  | clearStore | ||||||
|  | 
 | ||||||
|  | outPath=$($nixstore -r $($nixinstantiate dependencies.nix)) | ||||||
|  | 
 | ||||||
|  | $nixstore --export $outPath > $TEST_ROOT/exp | ||||||
|  | 
 | ||||||
|  | $nixstore --export $($nixstore -qR $outPath) > $TEST_ROOT/exp_all | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | clearStore | ||||||
|  | 
 | ||||||
|  | if $nixstore --import < $TEST_ROOT/exp; then | ||||||
|  |     echo "importing a non-closure should fail" | ||||||
|  |     exit 1 | ||||||
|  | fi | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | clearStore | ||||||
|  | 
 | ||||||
|  | $nixstore --import < $TEST_ROOT/exp_all | ||||||
|  | 
 | ||||||
|  | $nixstore --export $($nixstore -qR $outPath) > $TEST_ROOT/exp_all2 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | clearStore | ||||||
|  | 
 | ||||||
|  | # Regression test: the derivers in exp_all2 are empty, which shouldn't | ||||||
|  | # cause a failure. | ||||||
|  | $nixstore --import < $TEST_ROOT/exp_all2 | ||||||
|  | @ -95,4 +95,5 @@ chmod +x $NIX_BIN_DIR/nix/download-using-manifests.pl | ||||||
| $nixstore --init | $nixstore --init | ||||||
| 
 | 
 | ||||||
| # Did anything happen? | # Did anything happen? | ||||||
| test -e "$NIX_DB_DIR"/validpaths | test -e "$NIX_DB_DIR"/info | ||||||
|  | test -e "$NIX_DB_DIR"/referrer | ||||||
|  |  | ||||||
|  | @ -9,12 +9,50 @@ reference=$NIX_STORE_DIR/abcdef | ||||||
| touch $reference | touch $reference | ||||||
| (echo $reference && echo && echo 0) | $nixstore --register-validity  | (echo $reference && echo && echo 0) | $nixstore --register-validity  | ||||||
| 
 | 
 | ||||||
| echo "registering..." | echo "making registration..." | ||||||
| time for ((n = 0; n < $max; n++)); do | 
 | ||||||
|  | for ((n = 0; n < $max; n++)); do | ||||||
|     storePath=$NIX_STORE_DIR/$n |     storePath=$NIX_STORE_DIR/$n | ||||||
|     touch $storePath |     touch $storePath | ||||||
|     (echo $storePath && echo && echo 1 && echo $reference) |     ref2=$NIX_STORE_DIR/$((n+1)) | ||||||
| done | $nixstore --register-validity  |     if test $((n+1)) = $max; then | ||||||
|  |         ref2=$reference | ||||||
|  |     fi | ||||||
|  |     (echo $storePath && echo && echo 2 && echo $reference && echo $ref2) | ||||||
|  | done > $TEST_ROOT/reg_info | ||||||
|  | 
 | ||||||
|  | echo "registering..." | ||||||
|  | 
 | ||||||
|  | time $nixstore --register-validity < $TEST_ROOT/reg_info | ||||||
|  | 
 | ||||||
|  | oldTime=$(cat test-tmp/db/info/1 | grep Registered-At) | ||||||
|  | 
 | ||||||
|  | echo "sleeping..." | ||||||
|  | 
 | ||||||
|  | sleep 2 | ||||||
|  | 
 | ||||||
|  | echo "reregistering..." | ||||||
|  | 
 | ||||||
|  | time $nixstore --register-validity --reregister < $TEST_ROOT/reg_info | ||||||
|  | 
 | ||||||
|  | newTime=$(cat test-tmp/db/info/1 | grep Registered-At) | ||||||
|  | 
 | ||||||
|  | if test "$newTime" != "$oldTime"; then | ||||||
|  |     echo "reregistration changed original registration time" | ||||||
|  |     exit 1 | ||||||
|  | fi | ||||||
|  | 
 | ||||||
|  | if test "$(cat test-tmp/db/referrer/1 | wc -w)" != 1; then | ||||||
|  |     echo "reregistration duplicated referrers" | ||||||
|  |     exit 1 | ||||||
|  | fi | ||||||
| 
 | 
 | ||||||
| echo "collecting garbage..." | echo "collecting garbage..." | ||||||
| time $nixstore --gc 2> /dev/null | ln -sfn $reference "$NIX_STATE_DIR"/gcroots/ref | ||||||
|  | time $nixstore --gc | ||||||
|  | 
 | ||||||
|  | if test "$(cat test-tmp/db/referrer/abcdef | wc -w)" != 0; then | ||||||
|  |     echo "referrers not cleaned up" | ||||||
|  |     exit 1 | ||||||
|  | fi | ||||||
|  | 
 | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue