about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/Makefile.am2
-rw-r--r--src/db.cc57
-rw-r--r--src/db.hh8
-rw-r--r--src/fix.cc2
-rw-r--r--src/fstate.cc8
-rw-r--r--src/shared.cc3
-rw-r--r--src/store.cc93
-rw-r--r--src/store.hh9
-rw-r--r--src/test.cc15
-rw-r--r--src/util.cc17
-rw-r--r--src/util.hh7
11 files changed, 172 insertions, 49 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 5488e133a142..b22a56e3a036 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 b33591c8b20d..89cee32ba4a9 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 0054dbec13e1..aee6ce9bff90 100644
--- a/src/db.hh
+++ b/src/db.hh
@@ -4,6 +4,8 @@
 #include <string>
 #include <list>
 
+#include "util.hh"
+
 using namespace std;
 
 typedef pair<string, string> 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 fdf12ffef5fe..eb77a494229c 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(<str>)", &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 8003a1b38105..2e3ffd639c56 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(<str>, Hash(<str>), <term>)",
diff --git a/src/shared.cc b/src/shared.cc
index 6d157766e4af..bd165ce9781b 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 68a1cd1e2879..73713201d7e2 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 b96fa30ba8e9..a83fa03045a3 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 fb7900ca96e1..b30a5b0e90b6 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 65ceea938352..2f9c43e55ce5 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 6242fcb112ae..a8f801b30c5d 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);