diff --git a/src/Makefile.am b/src/Makefile.am index 5488e133a1..b22a56e3a0 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -14,7 +14,7 @@ fix_LDADD = libnix.a -ldb_cxx-4 -lATerm TESTS = test -test_SOURCES = test.cc +test_SOURCES = test.cc shared.cc test_LDADD = libnix.a -ldb_cxx-4 -lATerm noinst_LIBRARIES = libnix.a diff --git a/src/db.cc b/src/db.cc index b33591c8b2..89cee32ba4 100644 --- a/src/db.cc +++ b/src/db.cc @@ -73,6 +73,40 @@ bool queryDB(const string & filename, const string & dbname, } +bool queryListDB(const string & filename, const string & dbname, + const string & key, Strings & data) +{ + string d; + + if (!queryDB(filename, dbname, key, d)) + return false; + + string::iterator it = d.begin(); + + while (it != d.end()) { + + if (it + 4 > d.end()) + throw Error(format("short db entry: `%1%'") % d); + + unsigned int len; + len = (unsigned char) *it++; + len |= ((unsigned char) *it++) << 8; + len |= ((unsigned char) *it++) << 16; + len |= ((unsigned char) *it++) << 24; + + if (it + len > d.end()) + throw Error(format("short db entry: `%1%'") % d); + + string s; + while (len--) s += *it++; + + data.push_back(s); + } + + return true; +} + + void setDB(const string & filename, const string & dbname, const string & key, const string & data) { @@ -85,6 +119,29 @@ void setDB(const string & filename, const string & dbname, } +void setListDB(const string & filename, const string & dbname, + const string & key, const Strings & data) +{ + string d; + + for (Strings::const_iterator it = data.begin(); + it != data.end(); it++) + { + string s = *it; + unsigned int len = s.size(); + + d += len & 0xff; + d += (len >> 8) & 0xff; + d += (len >> 16) & 0xff; + d += (len >> 24) & 0xff; + + d += s; + } + + setDB(filename, dbname, key, d); +} + + void delDB(const string & filename, const string & dbname, const string & key) { diff --git a/src/db.hh b/src/db.hh index 0054dbec13..aee6ce9bff 100644 --- a/src/db.hh +++ b/src/db.hh @@ -4,6 +4,8 @@ #include #include +#include "util.hh" + using namespace std; typedef pair DBPair; @@ -14,9 +16,15 @@ void createDB(const string & filename, const string & dbname); bool queryDB(const string & filename, const string & dbname, const string & key, string & data); +bool queryListDB(const string & filename, const string & dbname, + const string & key, Strings & data); + void setDB(const string & filename, const string & dbname, const string & key, const string & data); +void setListDB(const string & filename, const string & dbname, + const string & key, const Strings & data); + void delDB(const string & filename, const string & dbname, const string & key); diff --git a/src/fix.cc b/src/fix.cc index fdf12ffef5..eb77a49422 100644 --- a/src/fix.cc +++ b/src/fix.cc @@ -29,7 +29,7 @@ static bool isFState(Expr e, string & path) } else if (ATmatch(e, "Include()", &s1)) { - string fn = queryFromStore(parseHash(s1)); + string fn = queryPathByHash(parseHash(s1)); return isFState(evalFile(fn), path); } else return false; diff --git a/src/fstate.cc b/src/fstate.cc index 8003a1b381..2e3ffd639c 100644 --- a/src/fstate.cc +++ b/src/fstate.cc @@ -167,7 +167,7 @@ struct RStatus static ATerm termFromHash(const Hash & hash) { - string path = queryFromStore(hash); + string path = queryPathByHash(hash); ATerm t = ATreadFromNamedFile(path.c_str()); if (!t) throw Error(format("cannot read aterm %1%") % path); return t; @@ -183,7 +183,7 @@ Hash writeTerm(ATerm t) string path2 = nixStore + "/" + (string) hash + ".nix"; if (rename(path.c_str(), path2.c_str()) == -1) throw SysError(format("renaming %1% to %2%") % path % path2); - setDB(nixDB, dbRefs, hash, path2); + registerPath(path2, hash); return hash; } @@ -259,7 +259,7 @@ static FState realise(RStatus & status, FState fs) } /* Do we know a path with that hash? If so, copy it. */ - string path2 = queryFromStore(hash); + string path2 = queryPathByHash(hash); copyPath(path2, path); return nf; @@ -318,7 +318,7 @@ static FState realise(RStatus & status, FState fs) /* Register targetHash -> targetPath. !!! this should be in values.cc. */ - setDB(nixDB, dbRefs, outHash, outPath); + registerPath(outPath, outHash); /* Register the normal form of fs. */ FState nf = ATmake("Path(, Hash(), )", diff --git a/src/shared.cc b/src/shared.cc index 6d157766e4..bd165ce978 100644 --- a/src/shared.cc +++ b/src/shared.cc @@ -54,6 +54,9 @@ int main(int argc, char * * argv) "Try `%2% --help' for more information.\n") % e.what() % programId; return 1; + } catch (Error & e) { + cerr << format("error: %1%\n") % e.msg(); + return 1; } catch (exception & e) { cerr << format("error: %1%\n") % e.what(); return 1; diff --git a/src/store.cc b/src/store.cc index 68a1cd1e28..73713201d7 100644 --- a/src/store.cc +++ b/src/store.cc @@ -83,25 +83,89 @@ void copyPath(string src, string dst) } +Hash registerPath(const string & _path, Hash hash) +{ + string path(canonPath(_path)); + + if (hash == Hash()) hash = hashPath(path); + + Strings paths; + queryListDB(nixDB, dbRefs, hash, paths); /* non-existence = ok */ + + for (Strings::iterator it = paths.begin(); + it != paths.end(); it++) + if (*it == path) goto exists; + + paths.push_back(path); + + setListDB(nixDB, dbRefs, hash, paths); + + exists: + return hash; +} + + +bool isInPrefix(const string & path, const string & _prefix) +{ + string prefix = canonPath(_prefix + "/"); + return string(path, 0, prefix.size()) == prefix; +} + + +static string queryPathByHashPrefix(Hash hash, const string & prefix) +{ + Strings paths; + + if (!queryListDB(nixDB, dbRefs, hash, paths)) + throw Error(format("no paths known with hash `%1%'") % (string) hash); + + /* Arbitrarily pick the first one that exists and still hash the + right hash. */ + + for (Strings::iterator it = paths.begin(); + it != paths.end(); it++) + { + debug(*it); + string path = *it; + try { + debug(path); + debug(prefix); + if (isInPrefix(path, prefix) && hashPath(path) == hash) + return path; + } catch (Error & e) { + debug(format("checking hash: %1%") % e.msg()); + /* try next one */ + } + } + + throw Error(format("all paths with hash `%1%' are stale") % (string) hash); +} + + +string queryPathByHash(Hash hash) +{ + return queryPathByHashPrefix(hash, "/"); +} + + void addToStore(string srcPath, string & dstPath, Hash & hash) { srcPath = absPath(srcPath); hash = hashPath(srcPath); - string path; - if (queryDB(nixDB, dbRefs, hash, path)) { - debug((string) hash + " already known"); - dstPath = path; + try { + dstPath = queryPathByHashPrefix(hash, nixStore); return; + } catch (...) { } - + string baseName = baseNameOf(srcPath); dstPath = nixStore + "/" + (string) hash + "-" + baseName; copyPath(srcPath, dstPath); - setDB(nixDB, dbRefs, hash, dstPath); + registerPath(dstPath, hash); } @@ -113,20 +177,3 @@ void deleteFromStore(const string & path) deletePath(path); // delDB(nixDB, dbRefs, hash); } - - -string queryFromStore(Hash hash) -{ - string fn, url; - - if (queryDB(nixDB, dbRefs, hash, fn)) { - - /* Verify that the file hasn't changed. !!! race !!! slow */ - if (hashPath(fn) != hash) - throw Error("file " + fn + " is stale"); - - return fn; - } - - throw Error(format("don't know a path with hash `%1%'") % (string) hash); -} diff --git a/src/store.hh b/src/store.hh index b96fa30ba8..a83fa03045 100644 --- a/src/store.hh +++ b/src/store.hh @@ -10,6 +10,12 @@ using namespace std; void copyPath(string src, string dst); +/* Register a path keyed on its hash. */ +Hash registerPath(const string & path, Hash hash = Hash()); + +/* Query a path (any path) through its hash. */ +string queryPathByHash(Hash hash); + /* Copy a file to the nixStore directory and register it in dbRefs. Return the hash code of the value. */ void addToStore(string srcPath, string & dstPath, Hash & hash); @@ -17,8 +23,5 @@ void addToStore(string srcPath, string & dstPath, Hash & hash); /* Delete a value from the nixStore directory. */ void deleteFromStore(const string & path); -/* !!! */ -string queryFromStore(Hash hash); - #endif /* !__VALUES_H */ diff --git a/src/test.cc b/src/test.cc index fb7900ca96..b30a5b0e90 100644 --- a/src/test.cc +++ b/src/test.cc @@ -191,15 +191,10 @@ void runTests() } -int main(int argc, char * * argv) +void run(Strings args) { - ATerm bottomOfStack; - ATinit(argc, argv, &bottomOfStack); - - try { - runTests(); - } catch (exception & e) { - cerr << "error: " << e.what() << endl; - return 1; - } + runTests(); } + + +string programId = "test"; diff --git a/src/util.cc b/src/util.cc index 65ceea9383..2f9c43e55c 100644 --- a/src/util.cc +++ b/src/util.cc @@ -33,13 +33,18 @@ string absPath(string path, string dir) dir = buf; } path = dir + "/" + path; - /* !!! canonicalise */ - char resolved[PATH_MAX]; - if (!realpath(path.c_str(), resolved)) - throw SysError(format("cannot canonicalise path %1%") % path); - path = resolved; } - return path; + return canonPath(path); +} + + +string canonPath(const string & path) +{ + char resolved[PATH_MAX]; + if (!realpath(path.c_str(), resolved)) + throw SysError(format("cannot canonicalise path `%1%'") % path); + /* !!! check that this removes trailing slashes */ + return resolved; } diff --git a/src/util.hh b/src/util.hh index 6242fcb112..a8f801b30c 100644 --- a/src/util.hh +++ b/src/util.hh @@ -21,6 +21,7 @@ public: Error(const format & f); ~Error() throw () { }; const char * what() const throw () { return err.c_str(); } + const string & msg() const throw () { return err; } }; class SysError : public Error @@ -44,9 +45,13 @@ extern string thisSystem; /* Return an absolutized path, resolving paths relative to the - specified directory, or the current directory otherwise. */ + specified directory, or the current directory otherwise. The path + is also canonicalised. */ string absPath(string path, string dir = ""); +/* Canonicalise a path (as in realpath(3)). */ +string canonPath(const string & path); + /* Return the directory part of the given path, i.e., everything before the final `/'. */ string dirOf(string path);