* Maintain the references graph again.

* Only build a derivation if there are no trusted output paths in the
  equivalence classes for that derivation's outputs.
* Set the trust ID to the current user name, or use the value of the
  NIX_USER_ID environment variable.
This commit is contained in:
Eelco Dolstra 2005-05-27 10:54:32 +00:00
parent 75454567f7
commit 89635e16ba
14 changed files with 179 additions and 162 deletions

View file

@ -1,6 +1,7 @@
#include "build.hh"
#include "eval.hh"
#include "globals.hh"
#include "misc.hh"
#include "nixexpr-ast.hh"

View file

@ -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;

View file

@ -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

View file

@ -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<OutputEqClass> 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<Path, PathSet> allReferences;
map<Path, Hash> 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<PathSet> 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
}

View file

@ -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 */

View file

@ -2,6 +2,7 @@
#include "gc.hh"
#include "build.hh"
#include "pathlocks.hh"
#include "misc.hh"
#include <boost/shared_ptr.hpp>

View file

@ -22,6 +22,8 @@ unsigned int maxBuildJobs = 1;
bool readOnlyMode = false;
string currentTrustId;
static bool settingsRead = false;

View file

@ -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);

View file

@ -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 "";
}

36
src/libstore/misc.hh Normal file
View file

@ -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 */

View file

@ -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<int> 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);
}

View file

@ -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

View file

@ -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());

View file

@ -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();