diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index c3bd4d9cd..e6579aaca 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -1,6 +1,7 @@ #include "build.hh" #include "eval.hh" #include "globals.hh" +#include "misc.hh" #include "nixexpr-ast.hh" diff --git a/src/libmain/shared.cc b/src/libmain/shared.cc index eb0b53628..0d0f2773c 100644 --- a/src/libmain/shared.cc +++ b/src/libmain/shared.cc @@ -143,6 +143,17 @@ static void initAndRun(int argc, char * * argv) /* Random number generator needed by makeRandomStorePath(); !!! improve. */ srand(time(0)); + + /* Set the trust ID to the user name. */ + currentTrustId = getEnv("NIX_USER_ID"); /* !!! dangerous? */ + if (currentTrustId == "") { + SwitchToOriginalUser sw; + uid_t uid = geteuid(); + struct passwd * pw = getpwuid(uid); + if (!pw) throw Error(format("unknown user ID %1%, go away") % uid); + currentTrustId = pw->pw_name; + } + printMsg(lvlError, format("trust ID is `%1%'") % currentTrustId); /* Put the arguments in a vector. */ Strings args, remaining; diff --git a/src/libstore/Makefile.am b/src/libstore/Makefile.am index 07e278232..55cce6faf 100644 --- a/src/libstore/Makefile.am +++ b/src/libstore/Makefile.am @@ -2,7 +2,7 @@ noinst_LIBRARIES = libstore.a libstore_a_SOURCES = \ store.cc store.hh derivations.cc derivations.hh \ - build.cc misc.cc build.hh \ + build.cc build.hh misc.cc misc.hh \ globals.cc globals.hh db.cc db.hh \ references.cc references.hh pathlocks.cc pathlocks.hh \ gc.cc gc.hh derivations-ast.hh diff --git a/src/libstore/build.cc b/src/libstore/build.cc index f08e4a4d7..8e5ca8951 100644 --- a/src/libstore/build.cc +++ b/src/libstore/build.cc @@ -14,6 +14,7 @@ #include "pathlocks.hh" #include "globals.hh" #include "gc.hh" +#include "misc.hh" /* !!! TODO derivationFromPath shouldn't be used here */ @@ -409,7 +410,8 @@ private: void writeLog(int fd, const unsigned char * buf, size_t count); /* Return the set of (in)valid paths. */ - PathSet checkPathValidity(bool returnValid); + typedef set OutputEqClasses; + OutputEqClasses checkOutputValidity(bool returnValid); }; @@ -477,9 +479,11 @@ void DerivationGoal::haveStoreExpr() for (DerivationOutputs::iterator i = drv.outputs.begin(); i != drv.outputs.end(); ++i) addTempRoot(i->second.path); +#endif - /* Check what outputs paths are not already valid. */ - PathSet invalidOutputs = checkPathValidity(false); + /* Check for what output path equivalence classes we do not + already have valid, trusted output paths. */ + OutputEqClasses invalidOutputs = checkOutputValidity(false); /* If they are all valid, then we're done. */ if (invalidOutputs.size() == 0) { @@ -487,6 +491,7 @@ void DerivationGoal::haveStoreExpr() return; } +#if 0 /* We are first going to try to create the invalid output paths through substitutes. If that doesn't work, we'll build them. */ @@ -514,12 +519,10 @@ void DerivationGoal::outputsSubstituted() nrFailed = 0; -#if 0 - if (checkPathValidity(false).size() == 0) { + if (checkOutputValidity(false).size() == 0) { amDone(true); return; } -#endif /* Otherwise, at least one of the output paths could not be produced using a substitute. So we have to build instead. */ @@ -904,6 +907,9 @@ void DerivationGoal::terminateBuildHook() bool DerivationGoal::prepareBuild() { + /* We direct each output of the derivation to a temporary location + in the Nix store. Afterwards, we move the outputs to their + final, content-addressed location. */ for (DerivationOutputs::iterator i = drv.outputs.begin(); i != drv.outputs.end(); ++i) { @@ -911,8 +917,20 @@ bool DerivationGoal::prepareBuild() printMsg(lvlError, format("mapping output id `%1%', class `%2%' to `%3%'") % i->first % i->second.eqClass % tmpPath); assert(i->second.eqClass.size() == tmpPath.size()); - rewrites[hashPartOf(i->second.eqClass)] = hashPartOf(tmpPath); + + debug(format("building path `%1%'") % tmpPath); + tmpOutputs[i->second.eqClass] = tmpPath; + + /* This is a referenceable path. Make a note of that for when + we are scanning for references in the output. */ + allPaths.insert(tmpPath); + + /* The environment variables and command-line arguments of the + builder refer to the output path equivalence class. Cause + those references to be rewritten to the temporary + locations. */ + rewrites[hashPartOf(i->second.eqClass)] = hashPartOf(tmpPath); } /* Obtain locks on all output paths. The locks are automatically @@ -948,19 +966,6 @@ bool DerivationGoal::prepareBuild() } #endif - /* Gather information necessary for computing the closure and/or - running the build hook. */ - -#if 0 - /* The outputs are referenceable paths. */ - for (DerivationOutputs::iterator i = drv.outputs.begin(); - i != drv.outputs.end(); ++i) - { - debug(format("building path `%1%'") % i->second.path); - allPaths.insert(i->second.path); - } -#endif - /* Determine the full set of input paths. */ /* First, the input derivations. */ @@ -972,30 +977,19 @@ bool DerivationGoal::prepareBuild() that are specified as inputs. */ assert(isValidPath(i->first)); Derivation inDrv = derivationFromPath(i->first); + for (StringSet::iterator j = i->second.begin(); j != i->second.end(); ++j) - - if (inDrv.outputs.find(*j) != inDrv.outputs.end()) { - - OutputEqClass eqClass = inDrv.outputs[*j].eqClass; - OutputEqMembers members; - queryOutputEqMembers(noTxn, eqClass, members); + { + OutputEqClass eqClass = findOutputEqClass(inDrv, *j); + Path input = findTrustedEqClassMember(eqClass, currentTrustId); + if (input == "") + throw Error(format("output `%1%' of derivation `%2%' is missing!") + % *j % i->first); + rewrites[hashPartOf(eqClass)] = hashPartOf(input); - if (members.size() == 0) - throw Error(format("output equivalence class `%1%' has no members!") - % eqClass); - - Path input = members.front().path; - - rewrites[hashPartOf(eqClass)] = hashPartOf(input); - -#if 0 - computeFSClosure(inDrv.outputs[*j].path, inputPaths); -#endif - } else - throw Error( - format("derivation `%1%' requires non-existent output `%2%' from input derivation `%3%'") - % drvPath % *j % i->first); + computeFSClosure(input, inputPaths); + } } /* Second, the input sources. */ @@ -1003,7 +997,7 @@ bool DerivationGoal::prepareBuild() i != drv.inputSrcs.end(); ++i) computeFSClosure(*i, inputPaths); - debug(format("added input paths %1%") % showPaths(inputPaths)); + printMsg(lvlError, format("added input paths %1%") % showPaths(inputPaths)); /* !!! */ allPaths.insert(inputPaths.begin(), inputPaths.end()); @@ -1014,7 +1008,7 @@ bool DerivationGoal::prepareBuild() void DerivationGoal::startBuilder() { startNest(nest, lvlInfo, - format("building path(s) XXX") /* % showPaths(outputPaths(drv.outputs)) */) + format("building derivation `%1%'") % drvPath) /* Right platform? */ if (drv.platform != thisSystem) @@ -1151,43 +1145,16 @@ void DerivationGoal::computeClosure() map allReferences; map contentHashes; - for (OutputMap::iterator i = tmpOutputs.begin(); - i != tmpOutputs.end(); ++i) - { - /* Rewrite each output to a name matching its content hash. - I.e., enforce the hash invariant: the hash part of a store - path matches the contents at that path. */ - Path finalPath = addToStore(i->second, hashPartOf(i->second), - namePartOf(i->second)); - printMsg(lvlError, format("produced final path `%1%'") % finalPath); - - /* Register the fact that this output path is a member of some - output path equivalence class (for a certain user, at - least). This is how subsequent derivations will be able to - find it. */ - Transaction txn; - createStoreTransaction(txn); - addOutputEqMember(txn, i->first, "root", finalPath); - txn.commit(); - } - -#if 0 - /* Check whether the output paths were created, and grep each - output path to determine what other paths it references. Also make all - output paths read-only. */ for (DerivationOutputs::iterator i = drv.outputs.begin(); i != drv.outputs.end(); ++i) { - Path path = i->second.path; + Path path = tmpOutputs[i->second.eqClass]; if (!pathExists(path)) { throw BuildError( format("builder for `%1%' failed to produce output path `%2%'") % drvPath % path); } - - startNest(nest, lvlTalkative, - format("scanning for references inside `%1%'") % path); - + /* Check that fixed-output derivations produced the right outputs (i.e., the content hash should match the specified hash). */ @@ -1225,64 +1192,50 @@ void DerivationGoal::computeClosure() % path % algo % printHash(h) % printHash(h2)); } - canonicalisePathMetaData(path); - - /* For this output path, find the references to other paths contained - in it. */ - PathSet references; + /* For this output path, find the references to other paths + contained in it. */ + PathSet referenced; if (!pathExists(path + "/nix-support/no-scan")) { - Paths references2; - references2 = filterReferences(path, + Paths referenced2 = filterReferences(path, Paths(allPaths.begin(), allPaths.end())); - references = PathSet(references2.begin(), references2.end()); + referenced = PathSet(referenced2.begin(), referenced2.end()); /* For debugging, print out the referenced and unreferenced paths. */ - for (PathSet::iterator i = inputPaths.begin(); - i != inputPaths.end(); ++i) - { - PathSet::iterator j = references.find(*i); - if (j == references.end()) - debug(format("unreferenced input: `%1%'") % *i); - else - debug(format("referenced input: `%1%'") % *i); - } + PathSet unreferenced; + insert_iterator ins(unreferenced, unreferenced.begin()); + set_difference( + inputPaths.begin(), inputPaths.end(), + referenced.begin(), referenced.end(), ins); + printMsg(lvlError, format("unreferenced inputs: %1%") % showPaths(unreferenced)); + printMsg(lvlError, format("referenced inputs: %1%") % showPaths(referenced)); } - allReferences[path] = references; + /* Rewrite each output to a name matching its content hash. + I.e., enforce the hash invariant: the hash part of a store + path matches the contents at that path. - /* Hash the contents of the path. The hash is stored in the - database so that we can verify later on whether nobody has - messed with the store. !!! inefficient: it would be nice - if we could combine this with filterReferences(). */ - contentHashes[path] = hashPath(htSHA256, path); + This also registers the final output path as valid, and + sets it references. */ + Path finalPath = addToStore(path, + hashPartOf(path), namePartOf(path), + referenced); + printMsg(lvlError, format("produced final path `%1%'") % finalPath); + + /* Register the fact that this output path is a member of some + output path equivalence class (for a certain user, at + least). This is how subsequent derivations will be able to + find it. */ + Transaction txn; + createStoreTransaction(txn); + addOutputEqMember(txn, i->second.eqClass, currentTrustId, finalPath); + txn.commit(); + + /* Get rid of the temporary output. !!! optimise all this by + *moving* the temporary output to the new location and + applying rewrites in situ. */ + deletePath(path); } -#endif - -#if 0 - /* Register each output path as valid, and register the sets of - paths referenced by each of them. This is wrapped in one - database transaction to ensure that if we crash, either - everything is registered or nothing is. This is for - 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(); - i != drv.outputs.end(); ++i) - { - registerValidPath(txn, i->second.path, - contentHashes[i->second.path], - allReferences[i->second.path], - drvPath); - } - txn.commit(); -#endif /* It is now safe to delete the lock files, since all future lockers will see that the output paths are valid; they will not @@ -1363,19 +1316,21 @@ void DerivationGoal::writeLog(int fd, } -PathSet DerivationGoal::checkPathValidity(bool returnValid) +DerivationGoal::OutputEqClasses DerivationGoal::checkOutputValidity(bool returnValid) { -#if 0 - PathSet result; + OutputEqClasses result; for (DerivationOutputs::iterator i = drv.outputs.begin(); i != drv.outputs.end(); ++i) - if (isValidPath(i->second.path)) { - if (returnValid) result.insert(i->second.path); + { + Path path = findTrustedEqClassMember(i->second.eqClass, currentTrustId); + if (path != "") { + assert(isValidPath(path)); + if (returnValid) result.insert(i->second.eqClass); } else { - if (!returnValid) result.insert(i->second.path); + if (!returnValid) result.insert(i->second.eqClass); } + } return result; -#endif } diff --git a/src/libstore/build.hh b/src/libstore/build.hh index 45997ebb2..96a8f8771 100644 --- a/src/libstore/build.hh +++ b/src/libstore/build.hh @@ -3,6 +3,7 @@ #include "derivations.hh" + /* Ensure that the output paths of the derivation are valid. If they are already valid, this is a no-op. Otherwise, validity can be reached in two ways. First, if the output paths have @@ -11,26 +12,10 @@ sub-derivations. */ void buildDerivations(const PathSet & drvPaths); + /* Ensure that a path is valid. If it is not currently valid, it may be made valid by running a substitute (if defined for the path). */ void ensurePath(const Path & storePath); -/* Read a derivation, after ensuring its existence through - ensurePath(). */ -Derivation derivationFromPath(const Path & drvPath); - -/* Place in `paths' the set of all store paths in the file system - closure of `storePath'; that is, all paths than can be directly or - indirectly reached from it. `paths' is not cleared. If - `flipDirection' is true, the set of paths that can reach - `storePath' is returned; that is, the closures under the `referers' - relation instead of the `references' relation is returned. */ -void computeFSClosure(const Path & storePath, - PathSet & paths, bool flipDirection = false); - -/* Return the path corresponding to the output identifier `id' in the - given derivation. */ -Path findOutput(const Derivation & drv, string id); - #endif /* !__BUILD_H */ diff --git a/src/libstore/gc.cc b/src/libstore/gc.cc index 5e2b60713..e2ac4d39b 100644 --- a/src/libstore/gc.cc +++ b/src/libstore/gc.cc @@ -2,6 +2,7 @@ #include "gc.hh" #include "build.hh" #include "pathlocks.hh" +#include "misc.hh" #include diff --git a/src/libstore/globals.cc b/src/libstore/globals.cc index 4387c8acc..f54c27aa1 100644 --- a/src/libstore/globals.cc +++ b/src/libstore/globals.cc @@ -22,6 +22,8 @@ unsigned int maxBuildJobs = 1; bool readOnlyMode = false; +string currentTrustId; + static bool settingsRead = false; diff --git a/src/libstore/globals.hh b/src/libstore/globals.hh index e2ae2ed65..55741dc07 100644 --- a/src/libstore/globals.hh +++ b/src/libstore/globals.hh @@ -52,6 +52,10 @@ extern unsigned int maxBuildJobs; database. */ extern bool readOnlyMode; +/* Current trust ID. !!! Of course, this shouldn't be a global + variable. */ +extern string currentTrustId; + string querySetting(const string & name, const string & def); diff --git a/src/libstore/misc.cc b/src/libstore/misc.cc index c729c3a33..4af1c914c 100644 --- a/src/libstore/misc.cc +++ b/src/libstore/misc.cc @@ -29,13 +29,23 @@ void computeFSClosure(const Path & storePath, } -Path findOutput(const Derivation & drv, string id) +OutputEqClass findOutputEqClass(const Derivation & drv, const string & id) { - assert(0); -#if 0 - for (DerivationOutputs::const_iterator i = drv.outputs.begin(); - i != drv.outputs.end(); ++i) - if (i->first == id) return i->second.path; - throw Error(format("derivation has no output `%1%'") % id); -#endif + DerivationOutputs::const_iterator i = drv.outputs.find(id); + if (i == drv.outputs.end()) + throw Error(format("derivation has no output `%1%'") % id); + return i->second.eqClass; +} + + +Path findTrustedEqClassMember(const OutputEqClass & eqClass, + const TrustId & trustId) +{ + OutputEqMembers members; + queryOutputEqMembers(noTxn, eqClass, members); + + for (OutputEqMembers::iterator j = members.begin(); j != members.end(); ++j) + if (j->trustId == trustId || j->trustId == "root") return j->path; + + return ""; } diff --git a/src/libstore/misc.hh b/src/libstore/misc.hh new file mode 100644 index 000000000..7aa55f212 --- /dev/null +++ b/src/libstore/misc.hh @@ -0,0 +1,36 @@ +#ifndef __MISC_H +#define __MISC_H + +#include "derivations.hh" +#include "store.hh" + + +/* Read a derivation, after ensuring its existence through + ensurePath(). */ +Derivation derivationFromPath(const Path & drvPath); + + +/* Place in `paths' the set of all store paths in the file system + closure of `storePath'; that is, all paths than can be directly or + indirectly reached from it. `paths' is not cleared. If + `flipDirection' is true, the set of paths that can reach + `storePath' is returned; that is, the closures under the `referers' + relation instead of the `references' relation is returned. */ +void computeFSClosure(const Path & storePath, + PathSet & paths, bool flipDirection = false); + + +/* Return the output equivalence class denoted by `id' in the + derivation `drv'. */ +OutputEqClass findOutputEqClass(const Derivation & drv, + const string & id); + + +/* Return any trusted path (wrt to the given trust ID) in the given + output path equivalence class, or "" if no such path currently + exists. */ +Path findTrustedEqClassMember(const OutputEqClass & eqClass, + const TrustId & trustId); + + +#endif /* !__MISC_H */ diff --git a/src/libstore/store.cc b/src/libstore/store.cc index fb9196db8..64409bfe3 100644 --- a/src/libstore/store.cc +++ b/src/libstore/store.cc @@ -842,8 +842,8 @@ static Path _addToStore(const string & suffix, string dump, /* If the contents had a previous hash reference, rewrite those references to the new hash. */ + HashRewrites rewrites; if (!selfHash.isNull()) { - HashRewrites rewrites; rewrites[selfHash] = pathHash; vector positions; dump = rewriteHashes(dump, rewrites, positions); @@ -871,9 +871,16 @@ static Path _addToStore(const string & suffix, string dump, restorePath(dstPath, source); canonicalisePathMetaData(dstPath); + + /* Set the references for the new path. Of course, any + hash rewrites have to be applied to the references, + too. */ + PathSet references2; + for (PathSet::iterator i = references.begin(); i != references.end(); ++i) + references2.insert(rewriteHashes(*i, rewrites)); Transaction txn(nixDB); - registerValidPath(txn, dstPath, contentHash, references, ""); + registerValidPath(txn, dstPath, contentHash, references2, ""); txn.commit(); } @@ -885,7 +892,7 @@ static Path _addToStore(const string & suffix, string dump, Path addToStore(const Path & _srcPath, const PathHash & selfHash, - const string & suffix) + const string & suffix, const PathSet & references) { Path srcPath(absPath(_srcPath)); debug(format("adding `%1%' to the store") % srcPath); @@ -897,7 +904,7 @@ Path addToStore(const Path & _srcPath, const PathHash & selfHash, } return _addToStore(suffix == "" ? baseNameOf(srcPath) : suffix, - sink.s, selfHash, PathSet()); + sink.s, selfHash, references); } diff --git a/src/libstore/store.hh b/src/libstore/store.hh index 6d14e6ea9..7239a7f23 100644 --- a/src/libstore/store.hh +++ b/src/libstore/store.hh @@ -226,7 +226,7 @@ string rewriteHashes(const string & s, const HashRewrites & rewrites); /* 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, const PathHash & selfHash = PathHash(), - const string & suffix = ""); + const string & suffix = "", const PathSet & references = PathSet()); #if 0 /* Like addToStore(), but for pre-adding the outputs of fixed-output diff --git a/src/nix-env/main.cc b/src/nix-env/main.cc index 0a6bda707..413cb9d11 100644 --- a/src/nix-env/main.cc +++ b/src/nix-env/main.cc @@ -3,6 +3,7 @@ #include "globals.hh" #include "build.hh" #include "gc.hh" +#include "misc.hh" #include "shared.hh" #include "parser.hh" #include "eval.hh" @@ -383,7 +384,6 @@ static void queryInstSources(EvalState & state, (import ./foo.nix)' = `(import ./foo.nix).bar'. */ case srcNixExprs: { - Expr e1 = parseExprFromFile(state, absPath(instSource.nixExprPath)); @@ -416,7 +416,10 @@ static void queryInstSources(EvalState & state, if (isDerivation(*i)) { elem.setDrvPath(*i); - elem.setOutPath(findOutput(derivationFromPath(*i), "out")); + elem.setOutPath( + /* XXX check this; may not give a result */ + findTrustedEqClassMember( + findOutputEqClass(derivationFromPath(*i), "out"), currentTrustId)); if (name.size() >= drvExtension.size() && string(name, name.size() - drvExtension.size()) == drvExtension) name = string(name, 0, name.size() - drvExtension.size()); diff --git a/src/nix-store/main.cc b/src/nix-store/main.cc index e511ed7cd..99acca858 100644 --- a/src/nix-store/main.cc +++ b/src/nix-store/main.cc @@ -4,6 +4,7 @@ #include "globals.hh" #include "build.hh" #include "gc.hh" +#include "misc.hh" #include "archive.hh" #include "shared.hh" #include "dotgraph.hh" @@ -45,7 +46,8 @@ static Path realisePath(const Path & path) PathSet paths; paths.insert(path); buildDerivations(paths); - Path outPath = findOutput(derivationFromPath(path), "out"); + Path outPath = findTrustedEqClassMember( + findOutputEqClass(derivationFromPath(path), "out"), currentTrustId); if (gcRoot == "") printGCWarning();