diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 2a5019fd9a..04712e74b5 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -2,7 +2,7 @@ #include "parser.hh" #include "hash.hh" #include "util.hh" -#include "store.hh" +#include "store-api.hh" #include "derivations.hh" #include "nixexpr-ast.hh" @@ -251,7 +251,7 @@ string coerceToString(EvalState & state, Expr e, PathSet & context, if (state.srcToStore[path] != "") dstPath = state.srcToStore[path]; else { - dstPath = addToStore(path); + dstPath = store->addToStore(path); state.srcToStore[path] = dstPath; printMsg(lvlChatty, format("copied source `%1%' -> `%2%'") % path % dstPath); diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 7776a38d65..d4de6027e4 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -2,7 +2,7 @@ #include "misc.hh" #include "eval.hh" #include "globals.hh" -#include "store.hh" +#include "store-api.hh" #include "util.hh" #include "expr-to-xml.hh" #include "nixexpr-ast.hh" @@ -46,7 +46,7 @@ static Expr primImport(EvalState & state, const ATermVector & args) for (PathSet::iterator i = context.begin(); i != context.end(); ++i) { assert(isStorePath(*i)); - if (!isValidPath(*i)) + if (!store->isValidPath(*i)) throw EvalError(format("cannot import `%1%', since path `%2%' is not valid") % path % *i); if (isDerivation(*i)) @@ -390,7 +390,7 @@ static Expr primToFile(EvalState & state, const ATermVector & args) refs.insert(*i); } - Path storePath = addTextToStore(name, contents, refs); + Path storePath = store->addTextToStore(name, contents, refs); /* Note: we don't need to add `context' to the context of the result, since `storePath' itself has references to the paths diff --git a/src/libmain/shared.cc b/src/libmain/shared.cc index d135691a1a..33576d69c2 100644 --- a/src/libmain/shared.cc +++ b/src/libmain/shared.cc @@ -1,7 +1,7 @@ #include "shared.hh" #include "globals.hh" #include "gc.hh" -#include "store.hh" +#include "store-api.hh" #include "util.hh" #include "config.h" @@ -199,7 +199,8 @@ static void initAndRun(int argc, char * * argv) run(remaining); - closeDB(); /* it's fine if the DB isn't actually open */ + /* Close the Nix database. */ + store.reset((StoreAPI *) 0); } diff --git a/src/libstore/Makefile.am b/src/libstore/Makefile.am index 515311efac..209a1e6929 100644 --- a/src/libstore/Makefile.am +++ b/src/libstore/Makefile.am @@ -1,12 +1,12 @@ pkglib_LTLIBRARIES = libstore.la libstore_la_SOURCES = \ - store.cc derivations.cc build.cc misc.cc globals.cc db.cc \ - references.cc pathlocks.cc gc.cc + store-api.cc local-store.cc derivations.cc build.cc misc.cc globals.cc \ + db.cc references.cc pathlocks.cc gc.cc pkginclude_HEADERS = \ - store.hh derivations.hh build.hh misc.hh globals.hh db.hh \ - references.hh pathlocks.hh gc.hh + store-api.hh local-store.cc derivations.hh build.hh misc.hh globals.hh \ + db.hh references.hh pathlocks.hh gc.hh libstore_la_LIBADD = ../libutil/libutil.la ../boost/format/libformat.la diff --git a/src/libstore/build.cc b/src/libstore/build.cc index 7058bd12b3..a78d5010cb 100644 --- a/src/libstore/build.cc +++ b/src/libstore/build.cc @@ -4,7 +4,7 @@ #include "misc.hh" #include "globals.hh" #include "gc.hh" -#include "store.hh" +#include "local-store.hh" #include "db.hh" #include "util.hh" @@ -636,7 +636,7 @@ void DerivationGoal::haveStoreExpr() return; } - assert(isValidPath(drvPath)); + assert(store->isValidPath(drvPath)); /* Get the derivation. */ drv = derivationFromPath(drvPath); @@ -661,7 +661,7 @@ void DerivationGoal::haveStoreExpr() i != invalidOutputs.end(); ++i) /* Don't bother creating a substitution goal if there are no substitutes. */ - if (querySubstitutes(noTxn, *i).size() > 0) + if (store->querySubstitutes(*i).size() > 0) addWaitee(worker.makeSubstitutionGoal(*i)); if (waitees.empty()) /* to prevent hang (no wake-up event) */ @@ -916,7 +916,7 @@ static string makeValidityRegistration(const PathSet & paths, s += deriver + "\n"; PathSet references; - queryReferences(noTxn, *i, references); + store->queryReferences(*i, references); s += (format("%1%\n") % references.size()).str(); @@ -1130,7 +1130,7 @@ bool DerivationGoal::prepareBuild() /* Add the relevant output closures of the input derivation `*i' as input paths. Only add the closures of output paths that are specified as inputs. */ - assert(isValidPath(i->first)); + assert(store->isValidPath(i->first)); Derivation inDrv = derivationFromPath(i->first); for (StringSet::iterator j = i->second.begin(); j != i->second.end(); ++j) @@ -1172,7 +1172,7 @@ void DerivationGoal::startBuilder() i != drv.outputs.end(); ++i) { Path path = i->second.path; - if (isValidPath(path)) + if (store->isValidPath(path)) throw Error(format("obstructed build: path `%1%' exists") % path); if (pathExists(path)) { debug(format("removing unregistered path `%1%'") % path); @@ -1259,7 +1259,7 @@ void DerivationGoal::startBuilder() for (Strings::iterator i = ss.begin(); i != ss.end(); ) { string fileName = *i++; Path storePath = *i++; - if (!isValidPath(storePath)) + if (!store->isValidPath(storePath)) throw Error(format("`exportReferencesGraph' refers to an invalid path `%1%'") % storePath); checkStoreName(fileName); /* !!! abuse of this function */ @@ -1630,7 +1630,7 @@ PathSet DerivationGoal::checkPathValidity(bool returnValid) PathSet result; for (DerivationOutputs::iterator i = drv.outputs.begin(); i != drv.outputs.end(); ++i) - if (isValidPath(i->second.path)) { + if (store->isValidPath(i->second.path)) { if (returnValid) result.insert(i->second.path); } else { if (!returnValid) result.insert(i->second.path); @@ -1718,17 +1718,21 @@ void SubstitutionGoal::init() addTempRoot(storePath); /* If the path already exists we're done. */ - if (isValidPath(storePath)) { + if (store->isValidPath(storePath)) { amDone(); return; } + /* !!! race condition; should get the substitutes and the + references in a transaction (in case a clearSubstitutes() is + done simultaneously). */ + /* Read the substitutes. */ - subs = querySubstitutes(noTxn, storePath); + subs = store->querySubstitutes(storePath); /* To maintain the closure invariant, we first have to realise the paths referenced by this one. */ - queryReferences(noTxn, storePath, references); + store->queryReferences(storePath, references); for (PathSet::iterator i = references.begin(); i != references.end(); ++i) @@ -1752,7 +1756,7 @@ void SubstitutionGoal::referencesValid() for (PathSet::iterator i = references.begin(); i != references.end(); ++i) if (*i != storePath) /* ignore self-references */ - assert(isValidPath(*i)); + assert(store->isValidPath(*i)); tryNext(); } @@ -1796,7 +1800,7 @@ void SubstitutionGoal::tryToRun() (format("waiting for lock on `%1%'") % storePath).str()); /* Check again whether the path is invalid. */ - if (isValidPath(storePath)) { + if (store->isValidPath(storePath)) { debug(format("store path `%1%' has become valid") % storePath); outputLock->setDeletion(true); amDone(); @@ -2221,7 +2225,7 @@ void buildDerivations(const PathSet & drvPaths) void ensurePath(const Path & path) { /* If the path is already valid, we're done. */ - if (isValidPath(path)) return; + if (store->isValidPath(path)) return; Worker worker; GoalPtr goal = worker.makeSubstitutionGoal(path); diff --git a/src/libstore/derivations.cc b/src/libstore/derivations.cc index aeab675b23..d159d47a5e 100644 --- a/src/libstore/derivations.cc +++ b/src/libstore/derivations.cc @@ -1,5 +1,5 @@ #include "derivations.hh" -#include "store.hh" +#include "store-api.hh" #include "aterm.hh" #include "derivations-ast.hh" @@ -25,7 +25,7 @@ Path writeDerivation(const Derivation & drv, const string & name) /* Note that the outputs of a derivation are *not* references (that can be missing (of course) and should not necessarily be held during a garbage collection). */ - return addTextToStore(name + drvExtension, + return store->addTextToStore(name + drvExtension, atPrint(unparseDerivation(drv)), references); } diff --git a/src/libstore/gc.cc b/src/libstore/gc.cc index 3a626dedae..05966ad4b0 100644 --- a/src/libstore/gc.cc +++ b/src/libstore/gc.cc @@ -2,7 +2,7 @@ #include "globals.hh" #include "misc.hh" #include "pathlocks.hh" -#include "store.hh" +#include "local-store.hh" #include "db.hh" #include "util.hh" @@ -305,7 +305,7 @@ static void findRoots(const Path & path, bool recurseSymlinks, debug(format("found root `%1%' in `%2%'") % target2 % path); Path target3 = toStorePath(target2); - if (isValidPath(target3)) + if (store->isValidPath(target3)) roots.insert(target3); else printMsg(lvlInfo, format("skipping invalid root from `%1%' to `%2%'") @@ -343,7 +343,7 @@ static void addAdditionalRoots(PathSet & roots) for (Strings::iterator i = paths.begin(); i != paths.end(); ++i) { if (isInStore(*i)) { Path path = toStorePath(*i); - if (roots.find(path) == roots.end() && isValidPath(path)) { + if (roots.find(path) == roots.end() && store->isValidPath(path)) { debug(format("found additional root `%1%'") % path); roots.insert(path); } @@ -359,8 +359,8 @@ static void dfsVisit(const PathSet & paths, const Path & path, visited.insert(path); PathSet references; - if (isValidPath(path)) - queryReferences(noTxn, path, references); + if (store->isValidPath(path)) + store->queryReferences(path, references); for (PathSet::iterator i = references.begin(); i != references.end(); ++i) @@ -431,7 +431,7 @@ void collectGarbage(GCAction action, const PathSet & pathsToDelete, previously ran the collector with `gcKeepDerivations' turned off). */ Path deriver = queryDeriver(noTxn, *i); - if (deriver != "" && isValidPath(deriver)) + if (deriver != "" && store->isValidPath(deriver)) computeFSClosure(deriver, livePaths); } } @@ -444,7 +444,7 @@ void collectGarbage(GCAction action, const PathSet & pathsToDelete, Derivation drv = derivationFromPath(*i); for (DerivationOutputs::iterator j = drv.outputs.begin(); j != drv.outputs.end(); ++j) - if (isValidPath(j->second.path)) + if (store->isValidPath(j->second.path)) computeFSClosure(j->second.path, livePaths); } } @@ -469,7 +469,7 @@ void collectGarbage(GCAction action, const PathSet & pathsToDelete, means that it has already been closed). */ PathSet tempRootsClosed; for (PathSet::iterator i = tempRoots.begin(); i != tempRoots.end(); ++i) - if (isValidPath(*i)) + if (store->isValidPath(*i)) computeFSClosure(*i, tempRootsClosed); else tempRootsClosed.insert(*i); diff --git a/src/libstore/store.cc b/src/libstore/local-store.cc similarity index 92% rename from src/libstore/store.cc rename to src/libstore/local-store.cc index e073d64ada..c8bb60bce6 100644 --- a/src/libstore/store.cc +++ b/src/libstore/local-store.cc @@ -1,4 +1,4 @@ -#include "store.hh" +#include "local-store.hh" #include "util.hh" #include "globals.hh" #include "db.hh" @@ -99,7 +99,7 @@ void checkStoreNotSymlink() } -void openDB(bool reserveSpace) +LocalStore::LocalStore(bool reserveSpace) { if (readOnlyMode) return; @@ -156,12 +156,7 @@ void openDB(bool reserveSpace) } -void initDB() -{ -} - - -void closeDB() +LocalStore::~LocalStore() { /* If the database isn't open, this is a NOP. */ nixDB.close(); @@ -221,60 +216,6 @@ void copyPath(const Path & src, const Path & dst) } -bool isInStore(const Path & path) -{ - return path[0] == '/' - && string(path, 0, nixStore.size()) == nixStore - && path.size() >= nixStore.size() + 2 - && path[nixStore.size()] == '/'; -} - - -bool isStorePath(const Path & path) -{ - return isInStore(path) - && path.find('/', nixStore.size() + 1) == Path::npos; -} - - -void assertStorePath(const Path & path) -{ - if (!isStorePath(path)) - throw Error(format("path `%1%' is not in the Nix store") % path); -} - - -Path toStorePath(const Path & path) -{ - if (!isInStore(path)) - throw Error(format("path `%1%' is not in the Nix store") % path); - Path::size_type slash = path.find('/', nixStore.size() + 1); - if (slash == Path::npos) - return path; - else - return Path(path, 0, slash); -} - - -void checkStoreName(const string & name) -{ - string validChars = "+-._?="; - /* Disallow names starting with a dot for possible security - reasons (e.g., "." and ".."). */ - if (string(name, 0, 1) == ".") - throw Error(format("illegal name: `%1%'") % name); - for (string::const_iterator i = name.begin(); i != name.end(); ++i) - if (!((*i >= 'A' && *i <= 'Z') || - (*i >= 'a' && *i <= 'z') || - (*i >= '0' && *i <= '9') || - validChars.find(*i) != string::npos)) - { - throw Error(format("invalid character `%1%' in name `%2%'") - % *i % name); - } -} - - void canonicalisePathMetaData(const Path & path) { checkInterrupt(); @@ -327,7 +268,7 @@ bool isValidPathTxn(const Transaction & txn, const Path & path) } -bool isValidPath(const Path & path) +bool LocalStore::isValidPath(const Path & path) { return isValidPathTxn(noTxn, path); } @@ -416,6 +357,13 @@ void queryReferences(const Transaction & txn, } +void LocalStore::queryReferences(const Path & storePath, + PathSet & references) +{ + nix::queryReferences(noTxn, storePath, references); +} + + void queryReferrers(const Transaction & txn, const Path & storePath, PathSet & referrers) { @@ -426,6 +374,13 @@ void queryReferrers(const Transaction & txn, } +void LocalStore::queryReferrers(const Path & storePath, + PathSet & referrers) +{ + nix::queryReferrers(noTxn, storePath, referrers); +} + + void setDeriver(const Transaction & txn, const Path & storePath, const Path & deriver) { @@ -531,6 +486,12 @@ Substitutes querySubstitutes(const Transaction & txn, const Path & srcPath) } +Substitutes LocalStore::querySubstitutes(const Path & srcPath) +{ + return nix::querySubstitutes(noTxn, srcPath); +} + + static void invalidatePath(Transaction & txn, const Path & path); @@ -584,7 +545,7 @@ static Hash queryHash(const Transaction & txn, const Path & storePath) } -Hash queryPathHash(const Path & path) +Hash LocalStore::queryPathHash(const Path & path) { if (!isValidPath(path)) throw Error(format("path `%1%' is not valid") % path); @@ -657,34 +618,7 @@ static void invalidatePath(Transaction & txn, const Path & path) } -Path makeStorePath(const string & type, - const Hash & hash, const string & suffix) -{ - /* e.g., "source:sha256:1abc...:/nix/store:foo.tar.gz" */ - string s = type + ":sha256:" + printHash(hash) + ":" - + nixStore + ":" + suffix; - - checkStoreName(suffix); - - return nixStore + "/" - + printHash32(compressHash(hashString(htSHA256, s), 20)) - + "-" + suffix; -} - - -Path makeFixedOutputPath(bool recursive, - string hashAlgo, Hash hash, string name) -{ - /* !!! copy/paste from primops.cc */ - Hash h = hashString(htSHA256, "fixed:out:" - + (recursive ? (string) "r:" : "") + hashAlgo + ":" - + printHash(hash) + ":" - + ""); - return makeStorePath("output:out", h, name); -} - - -static Path _addToStore(bool fixed, bool recursive, +Path LocalStore::_addToStore(bool fixed, bool recursive, string hashAlgo, const Path & _srcPath) { Path srcPath(absPath(_srcPath)); @@ -748,19 +682,19 @@ static Path _addToStore(bool fixed, bool recursive, } -Path addToStore(const Path & srcPath) +Path LocalStore::addToStore(const Path & srcPath) { return _addToStore(false, false, "", srcPath); } -Path addToStoreFixed(bool recursive, string hashAlgo, const Path & srcPath) +Path LocalStore::addToStoreFixed(bool recursive, string hashAlgo, const Path & srcPath) { return _addToStore(true, recursive, hashAlgo, srcPath); } -Path addTextToStore(const string & suffix, const string & s, +Path LocalStore::addTextToStore(const string & suffix, const string & s, const PathSet & references) { Hash hash = hashString(htSHA256, s); diff --git a/src/libstore/local-store.hh b/src/libstore/local-store.hh new file mode 100644 index 0000000000..2fd45cd7a5 --- /dev/null +++ b/src/libstore/local-store.hh @@ -0,0 +1,136 @@ +#ifndef __LOCAL_STORE_H +#define __LOCAL_STORE_H + +#include + +#include "store-api.hh" + + +namespace nix { + + +class Transaction; + + +/* Nix store and database schema version. Version 1 (or 0) was Nix <= + 0.7. Version 2 was Nix 0.8 and 0.8. Version 3 is Nix 0.10 and + up. */ +const int nixSchemaVersion = 3; + + +class LocalStore : public StoreAPI +{ +public: + + /* Open the database environment. If `reserveSpace' is true, make + sure that a big empty file exists in /nix/var/nix/db/reserved. + If `reserveSpace' is false, delete this file if it exists. The + 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(); + + /* Implementations of abstract store API methods. */ + + bool isValidPath(const Path & path); + + Substitutes querySubstitutes(const Path & srcPath); + + Hash queryPathHash(const Path & path); + + void queryReferences(const Path & storePath, + PathSet & references); + + void queryReferrers(const Path & storePath, + PathSet & referrers); + + Path addToStore(const Path & srcPath); + + Path addToStoreFixed(bool recursive, string hashAlgo, + const Path & srcPath); + + Path addTextToStore(const string & suffix, const string & s, + const PathSet & references); + +private: + Path _addToStore(bool fixed, bool recursive, + string hashAlgo, const Path & _srcPath); +}; + + +/* Get a transaction object. */ +void createStoreTransaction(Transaction & txn); + +/* Copy a path recursively. */ +void copyPath(const Path & src, const Path & dst); + +/* Register a substitute. */ +void registerSubstitute(const Transaction & txn, + const Path & srcPath, const Substitute & sub); + +/* Deregister all substitutes. */ +void clearSubstitutes(); + +/* 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); + +struct ValidPathInfo +{ + Path path; + Path deriver; + Hash hash; + PathSet references; +}; + +typedef list ValidPathInfos; + +void registerValidPaths(const Transaction & txn, + const ValidPathInfos & infos); + +/* "Fix", or canonicalise, the meta-data of the files in a store path + after it has been built. In particular: + - the last modification date on each file is set to 0 (i.e., + 00:00:00 1/1/1970 UTC) + - the permissions are set of 444 or 555 (i.e., read-only with or + without execute permission; setuid bits etc. are cleared) + - the owner and group are set to the Nix user and group, if we're + in a setuid Nix installation. */ +void canonicalisePathMetaData(const Path & path); + +/* Checks whether a path is valid. */ +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 & storePath, + const PathSet & references); + +/* Sets the deriver of a store path. Use with care! */ +void setDeriver(const Transaction & txn, const Path & storePath, + const Path & deriver); + +/* Query the deriver of a store path. Return the empty string if no + deriver has been set. */ +Path queryDeriver(const Transaction & txn, const Path & storePath); + +/* Delete a value from the nixStore directory. */ +void deleteFromStore(const Path & path, unsigned long long & bytesFreed); + +void verifyStore(bool checkContents); + + +} + + +#endif /* !__LOCAL_STORE_H */ diff --git a/src/libstore/misc.cc b/src/libstore/misc.cc index bcede901c5..b442bd4c21 100644 --- a/src/libstore/misc.cc +++ b/src/libstore/misc.cc @@ -1,5 +1,5 @@ #include "misc.hh" -#include "store.hh" +#include "store-api.hh" #include "build.hh" #include "db.hh" @@ -27,9 +27,9 @@ void computeFSClosure(const Path & storePath, PathSet references; if (flipDirection) - queryReferrers(noTxn, storePath, references); + store->queryReferrers(storePath, references); else - queryReferences(noTxn, storePath, references); + store->queryReferences(storePath, references); for (PathSet::iterator i = references.begin(); i != references.end(); ++i) @@ -58,14 +58,14 @@ void queryMissing(const PathSet & targets, done.insert(p); if (isDerivation(p)) { - if (!isValidPath(p)) continue; + if (!store->isValidPath(p)) continue; Derivation drv = derivationFromPath(p); bool mustBuild = false; for (DerivationOutputs::iterator i = drv.outputs.begin(); i != drv.outputs.end(); ++i) - if (!isValidPath(i->second.path) && - querySubstitutes(noTxn, i->second.path).size() == 0) + if (!store->isValidPath(i->second.path) && + store->querySubstitutes(i->second.path).size() == 0) mustBuild = true; if (mustBuild) { @@ -81,11 +81,11 @@ void queryMissing(const PathSet & targets, } else { - if (isValidPath(p)) continue; - if (querySubstitutes(noTxn, p).size() > 0) + if (store->isValidPath(p)) continue; + if (store->querySubstitutes(p).size() > 0) willSubstitute.insert(p); PathSet refs; - queryReferences(noTxn, p, todo); + store->queryReferences(p, todo); } } } diff --git a/src/libstore/store-api.cc b/src/libstore/store-api.cc new file mode 100644 index 0000000000..3a07d1c73c --- /dev/null +++ b/src/libstore/store-api.cc @@ -0,0 +1,107 @@ +#include "store-api.hh" +#include "globals.hh" + + +namespace nix { + + +bool isInStore(const Path & path) +{ + return path[0] == '/' + && string(path, 0, nixStore.size()) == nixStore + && path.size() >= nixStore.size() + 2 + && path[nixStore.size()] == '/'; +} + + +bool isStorePath(const Path & path) +{ + return isInStore(path) + && path.find('/', nixStore.size() + 1) == Path::npos; +} + + +void assertStorePath(const Path & path) +{ + if (!isStorePath(path)) + throw Error(format("path `%1%' is not in the Nix store") % path); +} + + +Path toStorePath(const Path & path) +{ + if (!isInStore(path)) + throw Error(format("path `%1%' is not in the Nix store") % path); + Path::size_type slash = path.find('/', nixStore.size() + 1); + if (slash == Path::npos) + return path; + else + return Path(path, 0, slash); +} + + +void checkStoreName(const string & name) +{ + string validChars = "+-._?="; + /* Disallow names starting with a dot for possible security + reasons (e.g., "." and ".."). */ + if (string(name, 0, 1) == ".") + throw Error(format("illegal name: `%1%'") % name); + for (string::const_iterator i = name.begin(); i != name.end(); ++i) + if (!((*i >= 'A' && *i <= 'Z') || + (*i >= 'a' && *i <= 'z') || + (*i >= '0' && *i <= '9') || + validChars.find(*i) != string::npos)) + { + throw Error(format("invalid character `%1%' in name `%2%'") + % *i % name); + } +} + + +Path makeStorePath(const string & type, + const Hash & hash, const string & suffix) +{ + /* e.g., "source:sha256:1abc...:/nix/store:foo.tar.gz" */ + string s = type + ":sha256:" + printHash(hash) + ":" + + nixStore + ":" + suffix; + + checkStoreName(suffix); + + return nixStore + "/" + + printHash32(compressHash(hashString(htSHA256, s), 20)) + + "-" + suffix; +} + + +Path makeFixedOutputPath(bool recursive, + string hashAlgo, Hash hash, string name) +{ + /* !!! copy/paste from primops.cc */ + Hash h = hashString(htSHA256, "fixed:out:" + + (recursive ? (string) "r:" : "") + hashAlgo + ":" + + printHash(hash) + ":" + + ""); + return makeStorePath("output:out", h, name); +} + + +} + + +#include "local-store.hh" + + +namespace nix { + + +boost::shared_ptr store; + + +boost::shared_ptr openStore(bool reserveSpace) +{ + return boost::shared_ptr(new LocalStore(reserveSpace)); +} + + +} diff --git a/src/libstore/store-api.hh b/src/libstore/store-api.hh new file mode 100644 index 0000000000..91beba50f7 --- /dev/null +++ b/src/libstore/store-api.hh @@ -0,0 +1,115 @@ +#ifndef __STOREAPI_H +#define __STOREAPI_H + +#include + +#include + +#include "hash.hh" + + +namespace nix { + + +/* A substitute is a program invocation that constructs some store + path (typically by fetching it from somewhere, e.g., from the + network). */ +struct Substitute +{ + /* The derivation that built this store path (empty if none). */ + Path deriver; + + /* Program to be executed to create the store path. Must be in + the output path of `storeExpr'. */ + Path program; + + /* Extra arguments to be passed to the program (the first argument + is the store path to be substituted). */ + Strings args; + + bool operator == (const Substitute & sub) const; +}; + +typedef list Substitutes; + + +class StoreAPI +{ +public: + + virtual ~StoreAPI() { } + + /* Checks whether a path is valid. */ + virtual bool isValidPath(const Path & path) = 0; + + /* Return the substitutes for the given path. */ + virtual Substitutes querySubstitutes(const Path & srcPath) = 0; + + /* Queries the hash of a valid path. */ + virtual Hash queryPathHash(const Path & path) = 0; + + /* Queries the set of outgoing FS references for a store path. + The result is not cleared. */ + virtual void queryReferences(const Path & storePath, + PathSet & references) = 0; + + /* Queries the set of incoming FS references for a store path. + The result is not cleared. */ + virtual void queryReferrers(const Path & storePath, + PathSet & referrers) = 0; + + /* Copy the contents of a path to the store and register the + validity the resulting path. The resulting path is + returned. */ + virtual Path addToStore(const Path & srcPath) = 0; + + /* Like addToStore(), but for pre-adding the outputs of + fixed-output derivations. */ + virtual Path addToStoreFixed(bool recursive, string hashAlgo, + const Path & srcPath) = 0; + + /* Like addToStore, but the contents written to the output path is + a regular file containing the given string. */ + virtual Path addTextToStore(const string & suffix, const string & s, + const PathSet & references) = 0; +}; + + +/* !!! These should be part of the store API, I guess. */ + +/* Throw an exception if `path' is not directly in the Nix store. */ +void assertStorePath(const Path & path); + +bool isInStore(const Path & path); +bool isStorePath(const Path & path); + +void checkStoreName(const string & name); + +/* Chop off the parts after the top-level store name, e.g., + /nix/store/abcd-foo/bar => /nix/store/abcd-foo. */ +Path toStorePath(const Path & path); + + +/* Constructs a unique store path name. */ +Path makeStorePath(const string & type, + const Hash & hash, const string & suffix); + +Path makeFixedOutputPath(bool recursive, + string hashAlgo, Hash hash, string name); + + +/* For now, there is a single global store API object, but we'll + purify that in the future. */ +extern boost::shared_ptr store; + + +/* Factory method: open the Nix database, either through the local or + remote implementation. */ +boost::shared_ptr openStore(bool reserveSpace = true); + + + +} + + +#endif /* !__STOREAPI_H */ diff --git a/src/libstore/store.hh b/src/libstore/store.hh deleted file mode 100644 index 7b18871e4f..0000000000 --- a/src/libstore/store.hh +++ /dev/null @@ -1,178 +0,0 @@ -#ifndef __STORE_H -#define __STORE_H - -#include - -#include "hash.hh" - - -namespace nix { - - -class Transaction; - - -/* Nix store and database schema version. Version 1 (or 0) was Nix <= - 0.7. Version 2 was Nix 0.8 and 0.8. Version 3 is Nix 0.10 and - up. */ -const int nixSchemaVersion = 3; - - -/* A substitute is a program invocation that constructs some store - path (typically by fetching it from somewhere, e.g., from the - network). */ -struct Substitute -{ - /* The derivation that built this store path (empty if none). */ - Path deriver; - - /* Program to be executed to create the store path. Must be in - the output path of `storeExpr'. */ - Path program; - - /* Extra arguments to be passed to the program (the first argument - is the store path to be substituted). */ - Strings args; - - bool operator == (const Substitute & sub) const; -}; - -typedef list Substitutes; - - -/* Open the database environment. If `reserveSpace' is true, make - sure that a big empty file exists in /nix/var/nix/db/reserved. If - `reserveSpace' is false, delete this file if it exists. The 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. */ -void openDB(bool reserveSpace = true); - -/* Create the required database tables. */ -void initDB(); - -/* Close the database. */ -void closeDB(); - -/* Get a transaction object. */ -void createStoreTransaction(Transaction & txn); - -/* Copy a path recursively. */ -void copyPath(const Path & src, const Path & dst); - -/* Register a substitute. */ -void registerSubstitute(const Transaction & txn, - const Path & srcPath, const Substitute & sub); - -/* Return the substitutes for the given path. */ -Substitutes querySubstitutes(const Transaction & txn, const Path & srcPath); - -/* Deregister all substitutes. */ -void clearSubstitutes(); - -/* 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); - -struct ValidPathInfo -{ - Path path; - Path deriver; - Hash hash; - PathSet references; -}; - -typedef list ValidPathInfos; - -void registerValidPaths(const Transaction & txn, - const ValidPathInfos & infos); - -/* Throw an exception if `path' is not directly in the Nix store. */ -void assertStorePath(const Path & path); - -bool isInStore(const Path & path); -bool isStorePath(const Path & path); - -void checkStoreName(const string & name); - -/* Chop off the parts after the top-level store name, e.g., - /nix/store/abcd-foo/bar => /nix/store/abcd-foo. */ -Path toStorePath(const Path & path); - -/* "Fix", or canonicalise, the meta-data of the files in a store path - after it has been built. In particular: - - the last modification date on each file is set to 0 (i.e., - 00:00:00 1/1/1970 UTC) - - the permissions are set of 444 or 555 (i.e., read-only with or - without execute permission; setuid bits etc. are cleared) - - the owner and group are set to the Nix user and group, if we're - in a setuid Nix installation. */ -void canonicalisePathMetaData(const Path & path); - -/* Checks whether a path is valid. */ -bool isValidPathTxn(const Transaction & txn, const Path & path); -bool isValidPath(const Path & path); - -/* Queries the hash of a valid path. */ -Hash queryPathHash(const Path & path); - -/* Sets the set of outgoing FS references for a store path. Use with - care! */ -void setReferences(const Transaction & txn, const Path & storePath, - const PathSet & references); - -/* Queries the set of outgoing FS references for a store path. The - result is not cleared. */ -void queryReferences(const Transaction & txn, - const Path & storePath, PathSet & references); - -/* Queries the set of incoming FS references for a store path. The - result is not cleared. */ -void queryReferrers(const Transaction & txn, - const Path & storePath, PathSet & referrers); - -/* Sets the deriver of a store path. Use with care! */ -void setDeriver(const Transaction & txn, const Path & storePath, - const Path & deriver); - -/* Query the deriver of a store path. Return the empty string if no - deriver has been set. */ -Path queryDeriver(const Transaction & txn, const Path & storePath); - -/* Constructs a unique store path name. */ -Path makeStorePath(const string & type, - const Hash & hash, const string & suffix); - -/* Copy the contents of a path to the store and register the validity - the resulting path. The resulting path is returned. */ -Path addToStore(const Path & srcPath); - -/* Like addToStore(), but for pre-adding the outputs of fixed-output - derivations. */ -Path addToStoreFixed(bool recursive, string hashAlgo, const Path & srcPath); - -Path makeFixedOutputPath(bool recursive, - string hashAlgo, Hash hash, string name); - -/* Like addToStore, but the contents written to the output path is a - regular file containing the given string. */ -Path addTextToStore(const string & suffix, const string & s, - const PathSet & references); - -/* Delete a value from the nixStore directory. */ -void deleteFromStore(const Path & path, unsigned long long & bytesFreed); - -void verifyStore(bool checkContents); - - -} - - -#endif /* !__STORE_H */ diff --git a/src/nix-env/main.cc b/src/nix-env/main.cc index f2a9957306..997c45da30 100644 --- a/src/nix-env/main.cc +++ b/src/nix-env/main.cc @@ -13,7 +13,7 @@ #include "attr-path.hh" #include "pathlocks.hh" #include "xml-writer.hh" -#include "store.hh" +#include "store-api.hh" #include "db.hh" #include "util.hh" @@ -192,7 +192,7 @@ static void createUserEnv(EvalState & state, const DrvInfos & elems, /* Also write a copy of the list of inputs to the store; we need it for future modifications of the environment. */ - Path manifestFile = addTextToStore("env-manifest", + Path manifestFile = store->addTextToStore("env-manifest", atPrint(makeList(ATreverse(manifest))), references); Expr topLevel = makeCall(envBuilder, makeAttrs(ATmakeList3( @@ -833,9 +833,9 @@ static void opQuery(Globals & globals, XMLAttrs attrs; if (printStatus) { - Substitutes subs = querySubstitutes(noTxn, i->queryOutPath(globals.state)); + Substitutes subs = store->querySubstitutes(i->queryOutPath(globals.state)); bool isInstalled = installed.find(i->queryOutPath(globals.state)) != installed.end(); - bool isValid = isValidPath(i->queryOutPath(globals.state)); + bool isValid = store->isValidPath(i->queryOutPath(globals.state)); if (xmlOutput) { attrs["installed"] = isInstalled ? "1" : "0"; attrs["valid"] = isValid ? "1" : "0"; @@ -1204,7 +1204,7 @@ void run(Strings args) : canonPath(nixStateDir + "/profiles/default"); } - openDB(); + store = openStore(); op(globals, opFlags, opArgs); diff --git a/src/nix-instantiate/main.cc b/src/nix-instantiate/main.cc index 3eb6d30319..144b13d2bc 100644 --- a/src/nix-instantiate/main.cc +++ b/src/nix-instantiate/main.cc @@ -11,7 +11,7 @@ #include "attr-path.hh" #include "expr-to-xml.hh" #include "util.hh" -#include "store.hh" +#include "store-api.hh" #include "help.txt.hh" @@ -137,7 +137,7 @@ void run(Strings args) files.push_back(arg); } - openDB(); + store = openStore(); if (readStdin) { Expr e = parseStdin(state); diff --git a/src/nix-store/dotgraph.cc b/src/nix-store/dotgraph.cc index 4b068514e4..989945600f 100644 --- a/src/nix-store/dotgraph.cc +++ b/src/nix-store/dotgraph.cc @@ -1,6 +1,6 @@ #include "dotgraph.hh" #include "util.hh" -#include "store.hh" +#include "store-api.hh" #include "db.hh" #include @@ -112,7 +112,7 @@ void printDotGraph(const PathSet & roots) cout << makeNode(path, symbolicName(path), "#ff0000"); PathSet references; - queryReferences(noTxn, path, references); + store->queryReferences(path, references); for (PathSet::iterator i = references.begin(); i != references.end(); ++i) diff --git a/src/nix-store/main.cc b/src/nix-store/main.cc index 8c2ff22799..273fa3e745 100644 --- a/src/nix-store/main.cc +++ b/src/nix-store/main.cc @@ -8,7 +8,7 @@ #include "archive.hh" #include "shared.hh" #include "dotgraph.hh" -#include "store.hh" +#include "local-store.hh" #include "db.hh" #include "util.hh" #include "help.txt.hh" @@ -112,7 +112,7 @@ static void opAdd(Strings opFlags, Strings opArgs) if (!opFlags.empty()) throw UsageError("unknown flag"); for (Strings::iterator i = opArgs.begin(); i != opArgs.end(); ++i) - cout << format("%1%\n") % addToStore(*i); + cout << format("%1%\n") % store->addToStore(*i); } @@ -134,7 +134,7 @@ static void opAddFixed(Strings opFlags, Strings opArgs) opArgs.pop_front(); for (Strings::iterator i = opArgs.begin(); i != opArgs.end(); ++i) - cout << format("%1%\n") % addToStoreFixed(recursive, hashAlgo, *i); + cout << format("%1%\n") % store->addToStoreFixed(recursive, hashAlgo, *i); } @@ -195,7 +195,7 @@ static void storePathRequisites(const Path & storePath, Derivation drv = derivationFromPath(*i); for (DerivationOutputs::iterator j = drv.outputs.begin(); j != drv.outputs.end(); ++j) - if (isValidPath(j->second.path)) + if (store->isValidPath(j->second.path)) computeFSClosure(j->second.path, paths); } } @@ -270,7 +270,7 @@ static void printTree(const Path & path, cout << format("%1%%2%\n") % firstPad % path; PathSet references; - queryReferences(noTxn, path, references); + store->queryReferences(path, references); #if 0 for (PathSet::iterator i = drv.inputSrcs.begin(); @@ -353,8 +353,8 @@ static void opQuery(Strings opFlags, Strings opArgs) Path path = maybeUseOutput(fixPath(*i), useOutput, forceRealise); if (query == qRequisites) storePathRequisites(path, includeOutputs, paths); - else if (query == qReferences) queryReferences(noTxn, path, paths); - else if (query == qReferrers) queryReferrers(noTxn, path, paths); + else if (query == qReferences) store->queryReferences(path, paths); + else if (query == qReferrers) store->queryReferrers(path, paths); else if (query == qReferrersClosure) computeFSClosure(path, paths, true); } printPathSet(paths); @@ -390,7 +390,7 @@ static void opQuery(Strings opFlags, Strings opArgs) i != opArgs.end(); ++i) { Path path = maybeUseOutput(fixPath(*i), useOutput, forceRealise); - Hash hash = queryPathHash(path); + Hash hash = store->queryPathHash(path); assert(hash.type == htSHA256); cout << format("sha256:%1%\n") % printHash32(hash); } @@ -515,7 +515,7 @@ static void opRegisterValidity(Strings opFlags, Strings opArgs) info.references.insert(s); } if (!cin || cin.eof()) throw Error("missing input"); - if (!isValidPath(info.path) || reregister) { + if (!store->isValidPath(info.path) || reregister) { /* !!! races */ canonicalisePathMetaData(info.path); info.hash = hashPath(htSHA256, info.path); @@ -536,7 +536,7 @@ static void opCheckValidity(Strings opFlags, Strings opArgs) for (Strings::iterator i = opArgs.begin(); i != opArgs.end(); ++i) - if (!isValidPath(*i)) + if (!store->isValidPath(*i)) throw Error(format("path `%1%' is not valid") % *i); } @@ -660,7 +660,8 @@ static void opInit(Strings opFlags, Strings opArgs) if (!opFlags.empty()) throw UsageError("unknown flag"); if (!opArgs.empty()) throw UsageError("no arguments expected"); - initDB(); + /* Doesn't do anything right now; database tables are initialised + automatically. */ } @@ -745,7 +746,7 @@ void run(Strings args) if (!op) throw UsageError("no operation specified"); if (op != opDump && op != opRestore) /* !!! hack */ - openDB(op != opGC); + store = openStore(op != opGC); op(opFlags, opArgs); }