about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/libexpr/common-opts.cc11
-rw-r--r--src/libexpr/common-opts.hh2
-rw-r--r--src/libexpr/primops.cc17
-rw-r--r--src/libmain/Makefile.am7
-rw-r--r--src/libmain/shared.cc51
-rw-r--r--src/libstore/Makefile.am11
-rw-r--r--src/libstore/build.cc24
-rw-r--r--src/libstore/globals.cc30
-rw-r--r--src/libstore/globals.hh4
-rw-r--r--src/libstore/local-store.cc66
-rw-r--r--src/libstore/local-store.hh4
-rw-r--r--src/libstore/references.cc4
-rw-r--r--src/libstore/remote-store.cc51
-rw-r--r--src/libstore/remote-store.hh2
-rw-r--r--src/libstore/store-api.cc11
-rw-r--r--src/libstore/store-api.hh16
-rw-r--r--src/libstore/worker-protocol.hh8
-rw-r--r--src/libutil/hash.cc38
-rw-r--r--src/libutil/hash.hh8
-rw-r--r--src/libutil/serialise.cc144
-rw-r--r--src/libutil/serialise.hh106
-rw-r--r--src/nix-env/nix-env.cc2
-rw-r--r--src/nix-hash/nix-hash.cc6
-rw-r--r--src/nix-instantiate/nix-instantiate.cc3
-rw-r--r--src/nix-store/nix-store.cc24
-rw-r--r--src/nix-worker/nix-worker.cc86
26 files changed, 461 insertions, 275 deletions
diff --git a/src/libexpr/common-opts.cc b/src/libexpr/common-opts.cc
index d029d2ec35b9..e0865d9fcc25 100644
--- a/src/libexpr/common-opts.cc
+++ b/src/libexpr/common-opts.cc
@@ -44,4 +44,15 @@ bool parseSearchPathArg(const string & arg, Strings::iterator & i,
 }
 
 
+Path lookupFileArg(EvalState & state, string s)
+{
+    if (s.size() > 2 && s.at(0) == '<' && s.at(s.size() - 1) == '>') {
+        Path p = state.findFile(s.substr(1, s.size() - 2));
+        if (p == "") throw Error(format("file `%1%' was not found in the Nix search path (add it using $NIX_PATH or -I)") % p);
+        return p;
+    } else
+        return absPath(s);
+}
+
+
 }
diff --git a/src/libexpr/common-opts.hh b/src/libexpr/common-opts.hh
index 6b7247fc3d89..c28641e9015d 100644
--- a/src/libexpr/common-opts.hh
+++ b/src/libexpr/common-opts.hh
@@ -14,6 +14,8 @@ bool parseOptionArg(const string & arg, Strings::iterator & i,
 bool parseSearchPathArg(const string & arg, Strings::iterator & i,
     const Strings::iterator & argsEnd, EvalState & state);
 
+Path lookupFileArg(EvalState & state, string s);
+
 }
 
 
diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc
index b11562baa436..dc361c043f67 100644
--- a/src/libexpr/primops.cc
+++ b/src/libexpr/primops.cc
@@ -363,9 +363,8 @@ static void prim_derivationStrict(EvalState & state, Value * * args, Value & v)
             foreach (PathSet::iterator, j, refs) {
                 drv.inputSrcs.insert(*j);
                 if (isDerivation(*j))
-                    drv.inputDrvs[*j] = store -> queryDerivationOutputNames(*j);
+                    drv.inputDrvs[*j] = store->queryDerivationOutputNames(*j);
             }
-
             explicitlyPassed = true;
         } else if (path.at(0) == '!') {
             size_t index;
@@ -387,7 +386,7 @@ static void prim_derivationStrict(EvalState & state, Value * * args, Value & v)
         debug(format("derivation uses `%1%'") % path);
         if (!useDrvAsSrc && isDerivation(path))
             if (explicitlyPassed)
-                drv.inputDrvs[path] = store -> queryDerivationOutputNames(path);
+                drv.inputDrvs[path] = store->queryDerivationOutputNames(path);
             else if (drv.inputDrvs.find(path) == drv.inputDrvs.end())
                 drv.inputDrvs[path] = singleton<StringSet>(output);
             else
@@ -416,17 +415,7 @@ static void prim_derivationStrict(EvalState & state, Value * * args, Value & v)
         HashType ht = parseHashType(outputHashAlgo);
         if (ht == htUnknown)
             throw EvalError(format("unknown hash algorithm `%1%'") % outputHashAlgo);
-        Hash h(ht);
-        if (outputHash.size() == h.hashSize * 2)
-            /* hexadecimal representation */
-            h = parseHash(ht, outputHash);
-        else if (outputHash.size() == hashLength32(h))
-            /* base-32 representation */
-            h = parseHash32(ht, outputHash);
-        else
-            throw Error(format("hash `%1%' has wrong length for hash type `%2%'")
-                % outputHash % outputHashAlgo);
-        string s = outputHash;
+        Hash h = parseHash16or32(ht, outputHash);
         outputHash = printHash(h);
         if (outputHashRecursive) outputHashAlgo = "r:" + outputHashAlgo;
         
diff --git a/src/libmain/Makefile.am b/src/libmain/Makefile.am
index ababc3595968..404353c62922 100644
--- a/src/libmain/Makefile.am
+++ b/src/libmain/Makefile.am
@@ -7,13 +7,6 @@ libmain_la_LIBADD = ../libstore/libstore.la @BDW_GC_LIBS@
 pkginclude_HEADERS = shared.hh
 
 AM_CXXFLAGS = \
- -DNIX_STORE_DIR=\"$(storedir)\" \
- -DNIX_DATA_DIR=\"$(datadir)\" \
- -DNIX_STATE_DIR=\"$(localstatedir)/nix\" \
- -DNIX_LOG_DIR=\"$(localstatedir)/log/nix\" \
- -DNIX_CONF_DIR=\"$(sysconfdir)/nix\" \
- -DNIX_LIBEXEC_DIR=\"$(libexecdir)\" \
- -DNIX_BIN_DIR=\"$(bindir)\" \
  -DNIX_VERSION=\"$(VERSION)\" \
  -I$(srcdir)/.. -I$(srcdir)/../libutil \
  -I$(srcdir)/../libstore
diff --git a/src/libmain/shared.cc b/src/libmain/shared.cc
index 8532cdaadcab..d3b73f8fd5a3 100644
--- a/src/libmain/shared.cc
+++ b/src/libmain/shared.cc
@@ -65,7 +65,7 @@ void printMissing(StoreAPI & store, const PathSet & paths)
     }
 
     if (!willSubstitute.empty()) {
-        printMsg(lvlInfo, format("these paths will be downloaded/copied (%.2f MiB download, %.2f MiB unpacked):")
+        printMsg(lvlInfo, format("these paths will be fetched (%.2f MiB download, %.2f MiB unpacked):")
             % (downloadSize / (1024.0 * 1024.0))
             % (narSize / (1024.0 * 1024.0)));
         foreach (PathSet::iterator, i, willSubstitute)
@@ -90,23 +90,6 @@ static void setLogType(string lt)
 }
 
 
-static void closeStore()
-{
-    try {
-        throw;
-    } catch (std::exception & e) {
-        printMsg(lvlError,
-            format("FATAL: unexpected exception (closing store and aborting): %1%") % e.what());
-    }
-    try {
-        store.reset((StoreAPI *) 0);
-    } catch (...) {
-        ignoreException();
-    }
-    abort();
-}
-
-
 RemoveTempRoots::~RemoveTempRoots()
 {
     removeTempRoots();
@@ -120,30 +103,8 @@ static bool showTrace = false;
    processor. */
 static void initAndRun(int argc, char * * argv)
 {
-    /* Setup Nix paths. */
-    nixStore = canonPath(getEnv("NIX_STORE_DIR", getEnv("NIX_STORE", NIX_STORE_DIR)));
-    nixDataDir = canonPath(getEnv("NIX_DATA_DIR", NIX_DATA_DIR));
-    nixLogDir = canonPath(getEnv("NIX_LOG_DIR", NIX_LOG_DIR));
-    nixStateDir = canonPath(getEnv("NIX_STATE_DIR", NIX_STATE_DIR));
-    nixDBPath = getEnv("NIX_DB_DIR", nixStateDir + "/db");
-    nixConfDir = canonPath(getEnv("NIX_CONF_DIR", NIX_CONF_DIR));
-    nixLibexecDir = canonPath(getEnv("NIX_LIBEXEC_DIR", NIX_LIBEXEC_DIR));
-    nixBinDir = canonPath(getEnv("NIX_BIN_DIR", NIX_BIN_DIR));
-
-    string subs = getEnv("NIX_SUBSTITUTERS", "default");
-    if (subs == "default") {
-        substituters.push_back(nixLibexecDir + "/nix/substituters/copy-from-other-stores.pl");
-        substituters.push_back(nixLibexecDir + "/nix/substituters/download-using-manifests.pl");
-    } else
-        substituters = tokenizeString(subs, ":");
-
-    /* Get some settings from the configuration file. */
-    thisSystem = querySetting("system", SYSTEM);
-    maxBuildJobs = queryIntSetting("build-max-jobs", 1);
-    buildCores = queryIntSetting("build-cores", 1);
-    maxSilentTime = queryIntSetting("build-max-silent-time", 0);
-    buildTimeout = queryIntSetting("build-timeout", 0);
-
+    setDefaultsFromEnvironment();
+    
     /* Catch SIGINT. */
     struct sigaction act;
     act.sa_handler = sigintHandler;
@@ -260,12 +221,6 @@ static void initAndRun(int argc, char * * argv)
        exit. */
     RemoveTempRoots removeTempRoots __attribute__((unused));
 
-    /* Make sure that the database gets closed properly, even if
-       terminate() is called (which happens sometimes due to bugs in
-       destructor/exceptions interaction, but that needn't preclude a
-       clean shutdown of the database). */
-    std::set_terminate(closeStore);
-    
     run(remaining);
 
     /* Close the Nix database. */
diff --git a/src/libstore/Makefile.am b/src/libstore/Makefile.am
index e19256b925ea..39a61233b4a7 100644
--- a/src/libstore/Makefile.am
+++ b/src/libstore/Makefile.am
@@ -15,7 +15,16 @@ libstore_la_LIBADD = ../libutil/libutil.la ../boost/format/libformat.la ${aterm_
 EXTRA_DIST = schema.sql
 
 AM_CXXFLAGS = -Wall \
- ${sqlite_include} -I$(srcdir)/.. -I$(srcdir)/../libutil
+ ${sqlite_include} -I$(srcdir)/.. -I$(srcdir)/../libutil \
+ -DNIX_STORE_DIR=\"$(storedir)\" \
+ -DNIX_DATA_DIR=\"$(datadir)\" \
+ -DNIX_STATE_DIR=\"$(localstatedir)/nix\" \
+ -DNIX_LOG_DIR=\"$(localstatedir)/log/nix\" \
+ -DNIX_CONF_DIR=\"$(sysconfdir)/nix\" \
+ -DNIX_LIBEXEC_DIR=\"$(libexecdir)\" \
+ -DNIX_BIN_DIR=\"$(bindir)\" \
+ -I$(srcdir)/.. -I$(srcdir)/../libutil \
+ -I$(srcdir)/../libstore
 
 local-store.lo: schema.sql.hh
 
diff --git a/src/libstore/build.cc b/src/libstore/build.cc
index d12f41d66941..a8ef9b23efaf 100644
--- a/src/libstore/build.cc
+++ b/src/libstore/build.cc
@@ -1650,6 +1650,9 @@ void DerivationGoal::startBuilder()
             (format("nixbld:!:%1%:\n")
                 % (buildUser.enabled() ? buildUser.getGID() : getgid())).str());
 
+        /* Create /etc/hosts with localhost entry. */
+        writeFile(chrootRootDir + "/etc/hosts", "127.0.0.1 localhost\n");
+
         /* Bind-mount a user-configurable set of directories from the
            host file system.  The `/dev/pts' directory must be mounted
            separately so that newly-created pseudo-terminals show
@@ -2199,9 +2202,7 @@ void SubstitutionGoal::tryNext()
     if (subs.size() == 0) {
         /* None left.  Terminate this goal and let someone else deal
            with it. */
-        printMsg(lvlError,
-            format("path `%1%' is required, but there is no substituter that can build it")
-            % storePath);
+        debug(format("path `%1%' is required, but there is no substituter that can build it") % storePath);
         amDone(ecFailed);
         return;
     }
@@ -2232,8 +2233,7 @@ void SubstitutionGoal::referencesValid()
     trace("all references realised");
 
     if (nrFailed > 0) {
-        printMsg(lvlError,
-            format("some references of path `%1%' could not be realised") % storePath);
+        debug(format("some references of path `%1%' could not be realised") % storePath);
         amDone(ecFailed);
         return;
     }
@@ -2286,9 +2286,7 @@ void SubstitutionGoal::tryToRun()
         return;
     }
 
-    printMsg(lvlInfo,
-        format("substituting path `%1%' using substituter `%2%'")
-        % storePath % sub);
+    printMsg(lvlInfo, format("fetching path `%1%'...") % storePath);
     
     logPipe.create();
 
@@ -2364,19 +2362,15 @@ void SubstitutionGoal::finished()
     try {
         
         if (!statusOk(status))
-            throw SubstError(format("builder for `%1%' %2%")
+            throw SubstError(format("fetching path `%1%' %2%")
                 % storePath % statusToString(status));
 
         if (!pathExists(storePath))
-            throw SubstError(
-                format("substitute did not produce path `%1%'")
-                % storePath);
+            throw SubstError(format("substitute did not produce path `%1%'") % storePath);
         
     } catch (SubstError & e) {
 
-        printMsg(lvlInfo,
-            format("substitution of path `%1%' using substituter `%2%' failed: %3%")
-            % storePath % sub % e.msg());
+        printMsg(lvlInfo, e.msg());
         
         if (printBuildTrace) {
             printMsg(lvlError, format("@ substituter-failed %1% %2% %3%")
diff --git a/src/libstore/globals.cc b/src/libstore/globals.cc
index 2e9dc88237d1..5c22f1406649 100644
--- a/src/libstore/globals.cc
+++ b/src/libstore/globals.cc
@@ -1,3 +1,5 @@
+#include "config.h"
+
 #include "globals.hh"
 #include "util.hh"
 
@@ -138,5 +140,33 @@ void reloadSettings()
     settings.clear();
 }
 
+
+void setDefaultsFromEnvironment()
+{
+    /* Setup Nix paths. */
+    nixStore = canonPath(getEnv("NIX_STORE_DIR", getEnv("NIX_STORE", NIX_STORE_DIR)));
+    nixDataDir = canonPath(getEnv("NIX_DATA_DIR", NIX_DATA_DIR));
+    nixLogDir = canonPath(getEnv("NIX_LOG_DIR", NIX_LOG_DIR));
+    nixStateDir = canonPath(getEnv("NIX_STATE_DIR", NIX_STATE_DIR));
+    nixDBPath = getEnv("NIX_DB_DIR", nixStateDir + "/db");
+    nixConfDir = canonPath(getEnv("NIX_CONF_DIR", NIX_CONF_DIR));
+    nixLibexecDir = canonPath(getEnv("NIX_LIBEXEC_DIR", NIX_LIBEXEC_DIR));
+    nixBinDir = canonPath(getEnv("NIX_BIN_DIR", NIX_BIN_DIR));
+
+    string subs = getEnv("NIX_SUBSTITUTERS", "default");
+    if (subs == "default") {
+        substituters.push_back(nixLibexecDir + "/nix/substituters/copy-from-other-stores.pl");
+        substituters.push_back(nixLibexecDir + "/nix/substituters/download-using-manifests.pl");
+    } else
+        substituters = tokenizeString(subs, ":");
+
+    /* Get some settings from the configuration file. */
+    thisSystem = querySetting("system", SYSTEM);
+    maxBuildJobs = queryIntSetting("build-max-jobs", 1);
+    buildCores = queryIntSetting("build-cores", 1);
+    maxSilentTime = queryIntSetting("build-max-silent-time", 0);
+    buildTimeout = queryIntSetting("build-timeout", 0);
+}
+
  
 }
diff --git a/src/libstore/globals.hh b/src/libstore/globals.hh
index 231c1f8508ac..12a9b9ca15c0 100644
--- a/src/libstore/globals.hh
+++ b/src/libstore/globals.hh
@@ -114,7 +114,9 @@ void overrideSetting(const string & name, const Strings & value);
 
 void reloadSettings();
 
-    
+void setDefaultsFromEnvironment();
+
+
 }
 
 
diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc
index 8ca5daa9f171..29817df9d6e8 100644
--- a/src/libstore/local-store.cc
+++ b/src/libstore/local-store.cc
@@ -327,10 +327,9 @@ void LocalStore::openDB(bool create)
     if (sqlite3_exec(db, ("pragma synchronous = " + syncMode + ";").c_str(), 0, 0, 0) != SQLITE_OK)
         throwSQLiteError(db, "setting synchronous mode");
 
-    /* Set the SQLite journal mode.  WAL mode is fastest, but doesn't
-       seem entirely stable at the moment (Oct. 2010).  Thus, use
-       truncate mode by default. */
-    string mode = queryBoolSetting("use-sqlite-wal", false) ? "wal" : "truncate";
+    /* Set the SQLite journal mode.  WAL mode is fastest, so it's the
+       default. */
+    string mode = queryBoolSetting("use-sqlite-wal", true) ? "wal" : "truncate";
     string prevMode;
     {
         SQLiteStmt stmt;
@@ -367,7 +366,7 @@ void LocalStore::openDB(bool create)
     stmtRegisterValidPath.create(db,
         "insert into ValidPaths (path, hash, registrationTime, deriver, narSize) values (?, ?, ?, ?, ?);");
     stmtUpdatePathInfo.create(db,
-        "update ValidPaths set narSize = ? where path = ?;");
+        "update ValidPaths set narSize = ?, hash = ? where path = ?;");
     stmtAddReference.create(db,
         "insert or replace into Refs (referrer, reference) values (?, ?);");
     stmtQueryPathInfo.create(db,
@@ -684,7 +683,7 @@ ValidPathInfo LocalStore::queryPathInfo(const Path & path)
 }
 
 
-/* Update path info in the database.  Currently only updated the
+/* Update path info in the database.  Currently only updates the
    narSize field. */
 void LocalStore::updatePathInfo(const ValidPathInfo & info)
 {
@@ -693,6 +692,7 @@ void LocalStore::updatePathInfo(const ValidPathInfo & info)
         stmtUpdatePathInfo.bind64(info.narSize);
     else
         stmtUpdatePathInfo.bind(); // null
+    stmtUpdatePathInfo.bind("sha256:" + printHash(info.hash));
     stmtUpdatePathInfo.bind(info.path);
     if (sqlite3_step(stmtUpdatePathInfo) != SQLITE_DONE)
         throwSQLiteError(db, format("updating info of path `%1%' in database") % info.path);
@@ -1125,16 +1125,14 @@ struct HashAndWriteSink : Sink
     HashAndWriteSink(Sink & writeSink) : writeSink(writeSink), hashSink(htSHA256)
     {
     }
-    virtual void operator ()
-        (const unsigned char * data, unsigned int len)
+    virtual void operator () (const unsigned char * data, size_t len)
     {
         writeSink(data, len);
         hashSink(data, len);
     }
     Hash currentHash()
     {
-        HashSink hashSinkClone(hashSink);
-        return hashSinkClone.finish().first;
+        return hashSink.currentHash().first;
     }
 };
 
@@ -1180,7 +1178,7 @@ void LocalStore::exportPath(const Path & path, bool sign,
     
     PathSet references;
     queryReferences(path, references);
-    writeStringSet(references, hashAndWriteSink);
+    writeStrings(references, hashAndWriteSink);
 
     Path deriver = queryDeriver(path);
     writeString(deriver, hashAndWriteSink);
@@ -1223,11 +1221,11 @@ struct HashAndReadSource : Source
     {
         hashing = true;
     }
-    virtual void operator ()
-        (unsigned char * data, unsigned int len)
+    size_t read(unsigned char * data, size_t len)
     {
-        readSource(data, len);
-        if (hashing) hashSink(data, len);
+        size_t n = readSource.read(data, len);
+        if (hashing) hashSink(data, n);
+        return n;
     }
 };
 
@@ -1267,7 +1265,7 @@ Path LocalStore::importPath(bool requireSignature, Source & source)
 
     Path dstPath = readStorePath(hashAndReadSource);
 
-    PathSet references = readStorePaths(hashAndReadSource);
+    PathSet references = readStorePaths<PathSet>(hashAndReadSource);
 
     Path deriver = readString(hashAndReadSource);
     if (deriver != "") assertStorePath(deriver);
@@ -1278,7 +1276,7 @@ Path LocalStore::importPath(bool requireSignature, Source & source)
     bool haveSignature = readInt(hashAndReadSource) == 1;
 
     if (requireSignature && !haveSignature)
-        throw Error("imported archive lacks a signature");
+        throw Error(format("imported archive of `%1%' lacks a signature") % dstPath);
     
     if (haveSignature) {
         string signature = readString(hashAndReadSource);
@@ -1354,6 +1352,19 @@ Path LocalStore::importPath(bool requireSignature, Source & source)
 }
 
 
+Paths LocalStore::importPaths(bool requireSignature, Source & source)
+{
+    Paths res;
+    while (true) {
+        unsigned long long n = readLongLong(source);
+        if (n == 0) break;
+        if (n != 1) throw Error("input doesn't look like something created by `nix-store --export'");
+        res.push_back(importPath(requireSignature, source));
+    }
+    return res;
+}
+
+
 void LocalStore::deleteFromStore(const Path & path, unsigned long long & bytesFreed,
     unsigned long long & blocksFreed)
 {
@@ -1369,7 +1380,7 @@ void LocalStore::deleteFromStore(const Path & path, unsigned long long & bytesFr
                 PathSet referrers; queryReferrers(path, referrers);
                 referrers.erase(path); /* ignore self-references */
                 if (!referrers.empty())
-                    throw PathInUse(format("cannot delete path `%1%' because it is in use by `%2%'")
+                    throw PathInUse(format("cannot delete path `%1%' because it is in use by %2%")
                         % path % showPaths(referrers));
                 invalidatePath(path);
             }
@@ -1409,6 +1420,8 @@ void LocalStore::verifyStore(bool checkContents)
     if (checkContents) {
         printMsg(lvlInfo, "checking hashes...");
 
+        Hash nullHash(htSHA256);
+
         foreach (PathSet::iterator, i, validPaths) {
             try {
                 ValidPathInfo info = queryPathInfo(*i);
@@ -1417,17 +1430,30 @@ void LocalStore::verifyStore(bool checkContents)
                 printMsg(lvlTalkative, format("checking contents of `%1%'") % *i);
                 HashResult current = hashPath(info.hash.type, *i);
                 
-                if (current.first != info.hash) {
+                if (info.hash != nullHash && info.hash != current.first) {
                     printMsg(lvlError, format("path `%1%' was modified! "
                             "expected hash `%2%', got `%3%'")
                         % *i % printHash(info.hash) % printHash(current.first));
                 } else {
+
+                    bool update = false;
+
+                    /* Fill in missing hashes. */
+                    if (info.hash == nullHash) {
+                        printMsg(lvlError, format("fixing missing hash on `%1%'") % *i);
+                        info.hash = current.first;
+                        update = true;
+                    }
+                    
                     /* Fill in missing narSize fields (from old stores). */
                     if (info.narSize == 0) {
                         printMsg(lvlError, format("updating size field on `%1%' to %2%") % *i % current.second);
                         info.narSize = current.second;
-                        updatePathInfo(info);
+                        update = true;                        
                     }
+
+                    if (update) updatePathInfo(info);
+
                 }
             
             } catch (Error & e) {
diff --git a/src/libstore/local-store.hh b/src/libstore/local-store.hh
index b97e2f406190..2739c4eea69d 100644
--- a/src/libstore/local-store.hh
+++ b/src/libstore/local-store.hh
@@ -148,7 +148,7 @@ public:
     void exportPath(const Path & path, bool sign,
         Sink & sink);
 
-    Path importPath(bool requireSignature, Source & source);
+    Paths importPaths(bool requireSignature, Source & source);
     
     void buildDerivations(const PathSet & drvPaths);
 
@@ -261,6 +261,8 @@ private:
 
     Path createTempDirInStore();
 
+    Path importPath(bool requireSignature, Source & source);
+    
     void checkDerivationOutputs(const Path & drvPath, const Derivation & drv);
 };
 
diff --git a/src/libstore/references.cc b/src/libstore/references.cc
index ade9c9aa20e3..c1f9e3ba7711 100644
--- a/src/libstore/references.cc
+++ b/src/libstore/references.cc
@@ -57,11 +57,11 @@ struct RefScanSink : Sink
 
     RefScanSink() : hashSink(htSHA256) { }
     
-    void operator () (const unsigned char * data, unsigned int len);
+    void operator () (const unsigned char * data, size_t len);
 };
 
 
-void RefScanSink::operator () (const unsigned char * data, unsigned int len)
+void RefScanSink::operator () (const unsigned char * data, size_t len)
 {
     hashSink(data, len);
 
diff --git a/src/libstore/remote-store.cc b/src/libstore/remote-store.cc
index 84c87246f0f0..ce99c205e7d0 100644
--- a/src/libstore/remote-store.cc
+++ b/src/libstore/remote-store.cc
@@ -27,13 +27,15 @@ Path readStorePath(Source & from)
 }
 
 
-PathSet readStorePaths(Source & from)
+template<class T> T readStorePaths(Source & from)
 {
-    PathSet paths = readStringSet(from);
-    foreach (PathSet::iterator, i, paths) assertStorePath(*i);
+    T paths = readStrings<T>(from);
+    foreach (typename T::iterator, i, paths) assertStorePath(*i);
     return paths;
 }
 
+template PathSet readStorePaths(Source & from);
+
 
 RemoteStore::RemoteStore()
 {
@@ -65,6 +67,7 @@ void RemoteStore::openConnection()
     /* Send the magic greeting, check for the reply. */
     try {
         writeInt(WORKER_MAGIC_1, to);
+        to.flush();
         unsigned int magic = readInt(from);
         if (magic != WORKER_MAGIC_2) throw Error("protocol mismatch");
 
@@ -166,6 +169,7 @@ void RemoteStore::connectToDaemon()
 RemoteStore::~RemoteStore()
 {
     try {
+        to.flush();
         fdSocket.close();
         if (child != -1)
             child.wait(true);
@@ -213,7 +217,7 @@ PathSet RemoteStore::queryValidPaths()
     openConnection();
     writeInt(wopQueryValidPaths, to);
     processStderr();
-    return readStorePaths(from);
+    return readStorePaths<PathSet>(from);
 }
 
 
@@ -240,7 +244,7 @@ bool RemoteStore::querySubstitutablePathInfo(const Path & path,
     if (reply == 0) return false;
     info.deriver = readString(from);
     if (info.deriver != "") assertStorePath(info.deriver);
-    info.references = readStorePaths(from);
+    info.references = readStorePaths<PathSet>(from);
     info.downloadSize = readLongLong(from);
     info.narSize = GET_PROTOCOL_MINOR(daemonVersion) >= 7 ? readLongLong(from) : 0;
     return true;
@@ -258,7 +262,7 @@ ValidPathInfo RemoteStore::queryPathInfo(const Path & path)
     info.deriver = readString(from);
     if (info.deriver != "") assertStorePath(info.deriver);
     info.hash = parseHash(htSHA256, readString(from));
-    info.references = readStorePaths(from);
+    info.references = readStorePaths<PathSet>(from);
     info.registrationTime = readInt(from);
     info.narSize = readLongLong(from);
     return info;
@@ -283,7 +287,7 @@ void RemoteStore::queryReferences(const Path & path,
     writeInt(wopQueryReferences, to);
     writeString(path, to);
     processStderr();
-    PathSet references2 = readStorePaths(from);
+    PathSet references2 = readStorePaths<PathSet>(from);
     references.insert(references2.begin(), references2.end());
 }
 
@@ -295,7 +299,7 @@ void RemoteStore::queryReferrers(const Path & path,
     writeInt(wopQueryReferrers, to);
     writeString(path, to);
     processStderr();
-    PathSet referrers2 = readStorePaths(from);
+    PathSet referrers2 = readStorePaths<PathSet>(from);
     referrers.insert(referrers2.begin(), referrers2.end());
 }
 
@@ -318,7 +322,7 @@ PathSet RemoteStore::queryDerivationOutputs(const Path & path)
     writeInt(wopQueryDerivationOutputs, to);
     writeString(path, to);
     processStderr();
-    return readStorePaths(from);
+    return readStorePaths<PathSet>(from);
 }
 
 
@@ -338,7 +342,7 @@ Path RemoteStore::addToStore(const Path & _srcPath,
     openConnection();
     
     Path srcPath(absPath(_srcPath));
-    
+
     writeInt(wopAddToStore, to);
     writeString(baseNameOf(srcPath), to);
     /* backwards compatibility hack */
@@ -358,7 +362,7 @@ Path RemoteStore::addTextToStore(const string & name, const string & s,
     writeInt(wopAddTextToStore, to);
     writeString(name, to);
     writeString(s, to);
-    writeStringSet(references, to);
+    writeStrings(references, to);
     
     processStderr();
     return readStorePath(from);
@@ -377,14 +381,14 @@ void RemoteStore::exportPath(const Path & path, bool sign,
 }
 
 
-Path RemoteStore::importPath(bool requireSignature, Source & source)
+Paths RemoteStore::importPaths(bool requireSignature, Source & source)
 {
     openConnection();
-    writeInt(wopImportPath, to);
+    writeInt(wopImportPaths, to);
     /* We ignore requireSignature, since the worker forces it to true
-       anyway. */    
+       anyway. */
     processStderr(0, &source);
-    return readStorePath(from);
+    return readStorePaths<Paths>(from);
 }
 
 
@@ -392,7 +396,7 @@ void RemoteStore::buildDerivations(const PathSet & drvPaths)
 {
     openConnection();
     writeInt(wopBuildDerivations, to);
-    writeStringSet(drvPaths, to);
+    writeStrings(drvPaths, to);
     processStderr();
     readInt(from);
 }
@@ -459,7 +463,7 @@ void RemoteStore::collectGarbage(const GCOptions & options, GCResults & results)
     
     writeInt(wopCollectGarbage, to);
     writeInt(options.action, to);
-    writeStringSet(options.pathsToDelete, to);
+    writeStrings(options.pathsToDelete, to);
     writeInt(options.ignoreLiveness, to);
     writeLongLong(options.maxFreed, to);
     writeInt(options.maxLinks, to);
@@ -471,7 +475,7 @@ void RemoteStore::collectGarbage(const GCOptions & options, GCResults & results)
     
     processStderr();
     
-    results.paths = readStringSet(from);
+    results.paths = readStrings<PathSet>(from);
     results.bytesFreed = readLongLong(from);
     results.blocksFreed = readLongLong(from);
 }
@@ -482,7 +486,7 @@ PathSet RemoteStore::queryFailedPaths()
     openConnection();
     writeInt(wopQueryFailedPaths, to);
     processStderr();
-    return readStorePaths(from);
+    return readStorePaths<PathSet>(from);
 }
 
 
@@ -490,7 +494,7 @@ void RemoteStore::clearFailedPaths(const PathSet & paths)
 {
     openConnection();
     writeInt(wopClearFailedPaths, to);
-    writeStringSet(paths, to);
+    writeStrings(paths, to);
     processStderr();
     readInt(from);
 }
@@ -498,6 +502,7 @@ void RemoteStore::clearFailedPaths(const PathSet & paths)
 
 void RemoteStore::processStderr(Sink * sink, Source * source)
 {
+    to.flush();
     unsigned int msg;
     while ((msg = readInt(from)) == STDERR_NEXT
         || msg == STDERR_READ || msg == STDERR_WRITE) {
@@ -508,11 +513,11 @@ void RemoteStore::processStderr(Sink * sink, Source * source)
         }
         else if (msg == STDERR_READ) {
             if (!source) throw Error("no source");
-            unsigned int len = readInt(from);
+            size_t len = readInt(from);
             unsigned char * buf = new unsigned char[len];
             AutoDeleteArray<unsigned char> d(buf);
-            (*source)(buf, len);
-            writeString(string((const char *) buf, len), to);
+            writeString(buf, source->read(buf, len), to);
+            to.flush();
         }
         else {
             string s = readString(from);
diff --git a/src/libstore/remote-store.hh b/src/libstore/remote-store.hh
index 3be9e315a219..c5853ef53646 100644
--- a/src/libstore/remote-store.hh
+++ b/src/libstore/remote-store.hh
@@ -58,7 +58,7 @@ public:
     void exportPath(const Path & path, bool sign,
         Sink & sink);
 
-    Path importPath(bool requireSignature, Source & source);
+    Paths importPaths(bool requireSignature, Source & source);
     
     void buildDerivations(const PathSet & drvPaths);
 
diff --git a/src/libstore/store-api.cc b/src/libstore/store-api.cc
index d67ff2c77299..36ade2170876 100644
--- a/src/libstore/store-api.cc
+++ b/src/libstore/store-api.cc
@@ -298,6 +298,17 @@ string showPaths(const PathSet & paths)
 }
 
 
+void exportPaths(StoreAPI & store, const Paths & paths,
+    bool sign, Sink & sink)
+{
+    foreach (Paths::const_iterator, i, paths) {
+        writeInt(1, sink);
+        store.exportPath(*i, sign, sink);
+    }
+    writeInt(0, sink);
+}
+
+
 }
 
 
diff --git a/src/libstore/store-api.hh b/src/libstore/store-api.hh
index 0384657498b5..14890f5225ad 100644
--- a/src/libstore/store-api.hh
+++ b/src/libstore/store-api.hh
@@ -154,9 +154,7 @@ public:
     
     /* Copy the contents of a path to the store and register the
        validity the resulting path.  The resulting path is returned.
-       If `fixed' is true, then the output of a fixed-output
-       derivation is pre-loaded into the Nix store.  The function
-       object `filter' can be used to exclude files (see
+       The function object `filter' can be used to exclude files (see
        libutil/archive.hh). */
     virtual Path addToStore(const Path & srcPath,
         bool recursive = true, HashType hashAlgo = htSHA256,
@@ -174,9 +172,9 @@ public:
     virtual void exportPath(const Path & path, bool sign,
         Sink & sink) = 0;
 
-    /* Import a NAR dump created by exportPath() into the Nix
-       store. */
-    virtual Path importPath(bool requireSignature, Source & source) = 0;
+    /* Import a sequence of NAR dumps created by exportPaths() into
+       the Nix store. */
+    virtual Paths importPaths(bool requireSignature, Source & source) = 0;
 
     /* Ensure that the output paths of the derivation are valid.  If
        they are already valid, this is a no-op.  Otherwise, validity
@@ -345,6 +343,12 @@ ValidPathInfo decodeValidPathInfo(std::istream & str,
     bool hashGiven = false);
 
 
+/* Export multiple paths in the format expected by ‘nix-store
+   --import’. */
+void exportPaths(StoreAPI & store, const Paths & paths,
+    bool sign, Sink & sink);
+
+
 }
 
 
diff --git a/src/libstore/worker-protocol.hh b/src/libstore/worker-protocol.hh
index 576e754d2d7a..ef1e0993df28 100644
--- a/src/libstore/worker-protocol.hh
+++ b/src/libstore/worker-protocol.hh
@@ -8,7 +8,7 @@ namespace nix {
 #define WORKER_MAGIC_1 0x6e697863
 #define WORKER_MAGIC_2 0x6478696f
 
-#define PROTOCOL_VERSION 0x108
+#define PROTOCOL_VERSION 0x109
 #define GET_PROTOCOL_MAJOR(x) ((x) & 0xff00)
 #define GET_PROTOCOL_MINOR(x) ((x) & 0x00ff)
 
@@ -29,7 +29,6 @@ typedef enum {
     wopSyncWithGC = 13,
     wopFindRoots = 14,
     wopExportPath = 16,
-    wopImportPath = 17,
     wopQueryDeriver = 18,
     wopSetOptions = 19,
     wopCollectGarbage = 20,
@@ -39,7 +38,8 @@ typedef enum {
     wopQueryFailedPaths = 24,
     wopClearFailedPaths = 25,
     wopQueryPathInfo = 26,
-    wopQueryDerivationOutputNames = 27,
+    wopImportPaths = 27,
+    wopQueryDerivationOutputNames = 28,
 } WorkerOp;
 
 
@@ -59,7 +59,7 @@ typedef enum {
 
 
 Path readStorePath(Source & from);
-PathSet readStorePaths(Source & from);
+template<class T> T readStorePaths(Source & from);
 
     
 }
diff --git a/src/libutil/hash.cc b/src/libutil/hash.cc
index b9e7846992f6..bbfe7847fd8a 100644
--- a/src/libutil/hash.cc
+++ b/src/libutil/hash.cc
@@ -204,6 +204,22 @@ Hash parseHash32(HashType ht, const string & s)
 }
 
 
+Hash parseHash16or32(HashType ht, const string & s)
+{
+    Hash hash(ht);
+    if (s.size() == hash.hashSize * 2)
+        /* hexadecimal representation */
+        hash = parseHash(ht, s);
+    else if (s.size() == hashLength32(hash))
+        /* base-32 representation */
+        hash = parseHash32(ht, s);
+    else
+        throw Error(format("hash `%1%' has wrong length for hash type `%2%'")
+            % s % printHashType(ht));
+    return hash;
+}
+
+
 bool isHash(const string & s)
 {
     if (s.length() != 32) return false;
@@ -290,21 +306,13 @@ HashSink::HashSink(HashType ht) : ht(ht)
     start(ht, *ctx);
 }
     
-HashSink::HashSink(const HashSink & h)
-{
-    ht = h.ht;
-    bytes = h.bytes;
-    ctx = new Ctx;
-    *ctx = *h.ctx;
-}
-    
 HashSink::~HashSink()
 {
+    bufPos = 0;
     delete ctx;
 }
 
-void HashSink::operator ()
-    (const unsigned char * data, unsigned int len)
+void HashSink::write(const unsigned char * data, size_t len)
 {
     bytes += len;
     update(ht, *ctx, data, len);
@@ -312,11 +320,21 @@ void HashSink::operator ()
 
 HashResult HashSink::finish()
 {
+    flush();
     Hash hash(ht);
     nix::finish(ht, *ctx, hash.hash);
     return HashResult(hash, bytes);
 }
 
+HashResult HashSink::currentHash()
+{
+    flush();
+    Ctx ctx2 = *ctx;
+    Hash hash(ht);
+    nix::finish(ht, ctx2, hash.hash);
+    return HashResult(hash, bytes);
+}
+
 
 HashResult hashPath(
     HashType ht, const Path & path, PathFilter & filter)
diff --git a/src/libutil/hash.hh b/src/libutil/hash.hh
index 13740954d625..e0b6478cc418 100644
--- a/src/libutil/hash.hh
+++ b/src/libutil/hash.hh
@@ -58,6 +58,9 @@ string printHash32(const Hash & hash);
 /* Parse a base-32 representation of a hash code. */
 Hash parseHash32(HashType ht, const string & s);
 
+/* Parse a base-16 or base-32 representation of a hash code. */
+Hash parseHash16or32(HashType ht, const string & s);
+
 /* Verify that the given string is a valid hash code. */
 bool isHash(const string & s);
 
@@ -88,7 +91,7 @@ string printHashType(HashType ht);
 
 union Ctx;
 
-class HashSink : public Sink
+class HashSink : public BufferedSink
 {
 private:
     HashType ht;
@@ -99,8 +102,9 @@ public:
     HashSink(HashType ht);
     HashSink(const HashSink & h);
     ~HashSink();
-    virtual void operator () (const unsigned char * data, unsigned int len);
+    void write(const unsigned char * data, size_t len);
     HashResult finish();
+    HashResult currentHash();
 };
 
 
diff --git a/src/libutil/serialise.cc b/src/libutil/serialise.cc
index 9b422271323f..c4563ffd1212 100644
--- a/src/libutil/serialise.cc
+++ b/src/libutil/serialise.cc
@@ -2,24 +2,117 @@
 #include "util.hh"
 
 #include <cstring>
+#include <cerrno>
 
 
 namespace nix {
 
 
-void FdSink::operator () (const unsigned char * data, unsigned int len)
+BufferedSink::~BufferedSink()
+{
+    /* We can't call flush() here, because C++ for some insane reason
+       doesn't allow you to call virtual methods from a destructor. */
+    assert(!bufPos);
+    if (buffer) delete[] buffer;
+}
+
+    
+void BufferedSink::operator () (const unsigned char * data, size_t len)
+{
+    if (!buffer) buffer = new unsigned char[bufSize];
+    
+    while (len) {
+        /* Optimisation: bypass the buffer if the data exceeds the
+           buffer size. */
+        if (bufPos + len >= bufSize) {
+            flush();
+            write(data, len);
+            break;
+        }
+        /* Otherwise, copy the bytes to the buffer.  Flush the buffer
+           when it's full. */
+        size_t n = bufPos + len > bufSize ? bufSize - bufPos : len;
+        memcpy(buffer + bufPos, data, n);
+        data += n; bufPos += n; len -= n;
+        if (bufPos == bufSize) flush();
+    }
+}
+
+
+void BufferedSink::flush()
+{
+    if (bufPos == 0) return;
+    size_t n = bufPos;
+    bufPos = 0; // don't trigger the assert() in ~BufferedSink()
+    write(buffer, n);
+}
+
+
+FdSink::~FdSink()
+{
+    try { flush(); } catch (...) { ignoreException(); }
+}
+
+
+void FdSink::write(const unsigned char * data, size_t len)
 {
     writeFull(fd, data, len);
 }
 
 
-void FdSource::operator () (unsigned char * data, unsigned int len)
+void Source::operator () (unsigned char * data, size_t len)
+{
+    while (len) {
+        size_t n = read(data, len);
+        data += n; len -= n;
+    }
+}
+
+
+BufferedSource::~BufferedSource()
 {
-    readFull(fd, data, len);
+    if (buffer) delete[] buffer;
 }
 
 
-void writePadding(unsigned int len, Sink & sink)
+size_t BufferedSource::read(unsigned char * data, size_t len)
+{
+    if (!buffer) buffer = new unsigned char[bufSize];
+
+    if (!bufPosIn) bufPosIn = readUnbuffered(buffer, bufSize);
+            
+    /* Copy out the data in the buffer. */
+    size_t n = len > bufPosIn - bufPosOut ? bufPosIn - bufPosOut : len;
+    memcpy(data, buffer + bufPosOut, n);
+    bufPosOut += n;
+    if (bufPosIn == bufPosOut) bufPosIn = bufPosOut = 0;
+    return n;
+}
+
+
+size_t FdSource::readUnbuffered(unsigned char * data, size_t len)
+{
+    ssize_t n;
+    do {
+        checkInterrupt();
+        n = ::read(fd, (char *) data, bufSize);
+    } while (n == -1 && errno == EINTR);
+    if (n == -1) throw SysError("reading from file");
+    if (n == 0) throw EndOfFile("unexpected end-of-file");
+    return n;
+}
+
+
+size_t StringSource::read(unsigned char * data, size_t len)
+{
+    if (pos == s.size()) throw EndOfFile("end of string reached");
+    size_t n = s.copy((char *) data, len, pos);
+    pos += n;
+    return n;
+}
+
+
+void writePadding(size_t len, Sink & sink)
 {
     if (len % 8) {
         unsigned char zero[8];
@@ -56,28 +149,36 @@ void writeLongLong(unsigned long long n, Sink & sink)
 }
 
 
-void writeString(const string & s, Sink & sink)
+void writeString(const unsigned char * buf, size_t len, Sink & sink)
 {
-    unsigned int len = s.length();
     writeInt(len, sink);
-    sink((const unsigned char *) s.c_str(), len);
+    sink(buf, len);
     writePadding(len, sink);
 }
 
 
-void writeStringSet(const StringSet & ss, Sink & sink)
+void writeString(const string & s, Sink & sink)
+{
+    writeString((const unsigned char *) s.c_str(), s.size(), sink);
+}
+
+
+template<class T> void writeStrings(const T & ss, Sink & sink)
 {
     writeInt(ss.size(), sink);
-    for (StringSet::iterator i = ss.begin(); i != ss.end(); ++i)
+    foreach (typename T::const_iterator, i, ss)
         writeString(*i, sink);
 }
 
+template void writeStrings(const Paths & ss, Sink & sink);
+template void writeStrings(const PathSet & ss, Sink & sink);
 
-void readPadding(unsigned int len, Source & source)
+
+void readPadding(size_t len, Source & source)
 {
     if (len % 8) {
         unsigned char zero[8];
-        unsigned int n = 8 - (len % 8);
+        size_t n = 8 - (len % 8);
         source(zero, n);
         for (unsigned int i = 0; i < n; i++)
             if (zero[i]) throw SerialisationError("non-zero padding");
@@ -115,9 +216,19 @@ unsigned long long readLongLong(Source & source)
 }
 
 
+size_t readString(unsigned char * buf, size_t max, Source & source)
+{
+    size_t len = readInt(source);
+    if (len > max) throw Error("string is too long");
+    source(buf, len);
+    readPadding(len, source);
+    return len;
+}
+
+ 
 string readString(Source & source)
 {
-    unsigned int len = readInt(source);
+    size_t len = readInt(source);
     unsigned char * buf = new unsigned char[len];
     AutoDeleteArray<unsigned char> d(buf);
     source(buf, len);
@@ -126,14 +237,17 @@ string readString(Source & source)
 }
 
  
-StringSet readStringSet(Source & source)
+template<class T> T readStrings(Source & source)
 {
     unsigned int count = readInt(source);
-    StringSet ss;
+    T ss;
     while (count--)
-        ss.insert(readString(source));
+        ss.insert(ss.end(), readString(source));
     return ss;
 }
 
+template Paths readStrings(Source & source);
+template PathSet readStrings(Source & source);
+
 
 }
diff --git a/src/libutil/serialise.hh b/src/libutil/serialise.hh
index 0e797d63bca9..ded4b12a046e 100644
--- a/src/libutil/serialise.hh
+++ b/src/libutil/serialise.hh
@@ -11,7 +11,25 @@ namespace nix {
 struct Sink 
 {
     virtual ~Sink() { }
-    virtual void operator () (const unsigned char * data, unsigned int len) = 0;
+    virtual void operator () (const unsigned char * data, size_t len) = 0;
+};
+
+
+/* A buffered abstract sink. */
+struct BufferedSink : Sink
+{
+    size_t bufSize, bufPos;
+    unsigned char * buffer;
+
+    BufferedSink(size_t bufSize = 32 * 1024)
+        : bufSize(bufSize), bufPos(0), buffer(0) { }
+    ~BufferedSink();
+
+    void operator () (const unsigned char * data, size_t len);
+    
+    void flush();
+    
+    virtual void write(const unsigned char * data, size_t len) = 0;
 };
 
 
@@ -20,49 +38,55 @@ struct Source
 {
     virtual ~Source() { }
     
-    /* The callee should store exactly *len bytes in the buffer
-       pointed to by data.  It should block if that much data is not
-       yet available, or throw an error if it is not going to be
-       available. */
-    virtual void operator () (unsigned char * data, unsigned int len) = 0;
+    /* Store exactly ‘len’ bytes in the buffer pointed to by ‘data’.
+       It blocks until all the requested data is available, or throws
+       an error if it is not going to be available.   */
+    void operator () (unsigned char * data, size_t len);
+
+    /* Store up to ‘len’ in the buffer pointed to by ‘data’, and
+       return the number of bytes stored.  If blocks until at least
+       one byte is available. */
+    virtual size_t read(unsigned char * data, size_t len) = 0;
 };
 
 
-/* A sink that writes data to a file descriptor. */
-struct FdSink : Sink
+/* A buffered abstract source. */
+struct BufferedSource : Source
 {
-    int fd;
+    size_t bufSize, bufPosIn, bufPosOut;
+    unsigned char * buffer;
 
-    FdSink()
-    {
-        fd = -1;
-    }
+    BufferedSource(size_t bufSize = 32 * 1024)
+        : bufSize(bufSize), bufPosIn(0), bufPosOut(0), buffer(0) { }
+    ~BufferedSource();
     
-    FdSink(int fd) 
-    {
-        this->fd = fd;
-    }
+    size_t read(unsigned char * data, size_t len);
     
-    void operator () (const unsigned char * data, unsigned int len);
+    /* Underlying read call, to be overriden. */
+    virtual size_t readUnbuffered(unsigned char * data, size_t len) = 0;
 };
 
 
-/* A source that reads data from a file descriptor. */
-struct FdSource : Source
+/* A sink that writes data to a file descriptor. */
+struct FdSink : BufferedSink
 {
     int fd;
 
-    FdSource()
-    {
-        fd = -1;
-    }
+    FdSink() : fd(-1) { }
+    FdSink(int fd) : fd(fd) { }
+    ~FdSink();
     
-    FdSource(int fd) 
-    {
-        this->fd = fd;
-    }
-    
-    void operator () (unsigned char * data, unsigned int len);
+    void write(const unsigned char * data, size_t len);
+};
+
+
+/* A source that reads data from a file descriptor. */
+struct FdSource : BufferedSource
+{
+    int fd;
+    FdSource() : fd(-1) { }
+    FdSource(int fd) : fd(fd) { }
+    size_t readUnbuffered(unsigned char * data, size_t len);
 };
 
 
@@ -70,7 +94,7 @@ struct FdSource : Source
 struct StringSink : Sink
 {
     string s;
-    virtual void operator () (const unsigned char * data, unsigned int len)
+    void operator () (const unsigned char * data, size_t len)
     {
         s.append((const char *) data, len);
     }
@@ -81,29 +105,25 @@ struct StringSink : Sink
 struct StringSource : Source
 {
     const string & s;
-    unsigned int pos;
+    size_t pos;
     StringSource(const string & _s) : s(_s), pos(0) { }
-    virtual void operator () (unsigned char * data, unsigned int len)
-    {
-        s.copy((char *) data, len, pos);
-        pos += len;
-        if (pos > s.size())
-            throw Error("end of string reached");
-    }
+    size_t read(unsigned char * data, size_t len);    
 };
 
 
-void writePadding(unsigned int len, Sink & sink);
+void writePadding(size_t len, Sink & sink);
 void writeInt(unsigned int n, Sink & sink);
 void writeLongLong(unsigned long long n, Sink & sink);
+void writeString(const unsigned char * buf, size_t len, Sink & sink);
 void writeString(const string & s, Sink & sink);
-void writeStringSet(const StringSet & ss, Sink & sink);
+template<class T> void writeStrings(const T & ss, Sink & sink);
 
-void readPadding(unsigned int len, Source & source);
+void readPadding(size_t len, Source & source);
 unsigned int readInt(Source & source);
 unsigned long long readLongLong(Source & source);
+size_t readString(unsigned char * buf, size_t max, Source & source);
 string readString(Source & source);
-StringSet readStringSet(Source & source);
+template<class T> T readStrings(Source & source);
 
 
 MakeError(SerialisationError, Error)
diff --git a/src/nix-env/nix-env.cc b/src/nix-env/nix-env.cc
index 3dfecb2d7313..a8d9076cfb8d 100644
--- a/src/nix-env/nix-env.cc
+++ b/src/nix-env/nix-env.cc
@@ -1270,7 +1270,7 @@ void run(Strings args)
         else if (arg == "--profile" || arg == "-p")
             globals.profile = absPath(needArg(i, args, arg));
         else if (arg == "--file" || arg == "-f")
-            globals.instSource.nixExprPath = absPath(needArg(i, args, arg));
+            globals.instSource.nixExprPath = lookupFileArg(globals.state, needArg(i, args, arg));
         else if (arg == "--switch-profile" || arg == "-S")
             op = opSwitchProfile;
         else if (arg == "--switch-generation" || arg == "-G")
diff --git a/src/nix-hash/nix-hash.cc b/src/nix-hash/nix-hash.cc
index 4867234bffc1..5b35ccd9dae3 100644
--- a/src/nix-hash/nix-hash.cc
+++ b/src/nix-hash/nix-hash.cc
@@ -43,7 +43,7 @@ void run(Strings args)
     }
 
     if (op == opHash) {
-        for (Strings::iterator i = ss.begin(); i != ss.end(); ++i) {
+        foreach (Strings::iterator, i, ss) {
             Hash h = flat ? hashFile(ht, *i) : hashPath(ht, *i).first;
             if (truncate && h.hashSize > 20) h = compressHash(h, 20);
             std::cout << format("%1%\n") %
@@ -52,8 +52,8 @@ void run(Strings args)
     }
 
     else {
-        for (Strings::iterator i = ss.begin(); i != ss.end(); ++i) {
-            Hash h = op == opTo16 ? parseHash32(ht, *i) : parseHash(ht, *i);
+        foreach (Strings::iterator, i, ss) {
+            Hash h = parseHash16or32(ht, *i);
             std::cout << format("%1%\n") %
                 (op == opTo16 ? printHash(h) : printHash32(h));
         }
diff --git a/src/nix-instantiate/nix-instantiate.cc b/src/nix-instantiate/nix-instantiate.cc
index 98eadbd69580..93aa50943450 100644
--- a/src/nix-instantiate/nix-instantiate.cc
+++ b/src/nix-instantiate/nix-instantiate.cc
@@ -138,8 +138,7 @@ void run(Strings args)
     }
 
     foreach (Strings::iterator, i, files) {
-        Path path = absPath(*i);
-        Expr * e = state.parseExprFromFile(path);
+        Expr * e = state.parseExprFromFile(lookupFileArg(state, *i));
         processExpr(state, attrPaths, parseOnly, strict, autoArgs,
             evalOnly, xmlOutput, xmlOutputSourceLocation, e);
     }
diff --git a/src/nix-store/nix-store.cc b/src/nix-store/nix-store.cc
index 371ca54af07c..e92ccb153d12 100644
--- a/src/nix-store/nix-store.cc
+++ b/src/nix-store/nix-store.cc
@@ -133,14 +133,6 @@ static void opAddFixed(Strings opFlags, Strings opArgs)
 }
 
 
-static Hash parseHash16or32(HashType ht, const string & s)
-{
-    return s.size() == Hash(ht).hashSize * 2
-        ? parseHash(ht, s)
-        : parseHash32(ht, s);
-}
-
-
 /* Hack to support caching in `nix-prefetch-url'. */
 static void opPrintFixedPath(Strings opFlags, Strings opArgs)
 {
@@ -594,11 +586,7 @@ static void opExport(Strings opFlags, Strings opArgs)
         else throw UsageError(format("unknown flag `%1%'") % *i);
 
     FdSink sink(STDOUT_FILENO);
-    for (Strings::iterator i = opArgs.begin(); i != opArgs.end(); ++i) {
-        writeInt(1, sink);
-        store->exportPath(*i, sign, sink);
-    }
-    writeInt(0, sink);
+    exportPaths(*store, opArgs, sign, sink);
 }
 
 
@@ -612,12 +600,10 @@ static void opImport(Strings opFlags, Strings opArgs)
     if (!opArgs.empty()) throw UsageError("no arguments expected");
     
     FdSource source(STDIN_FILENO);
-    while (true) {
-        unsigned long long n = readLongLong(source);
-        if (n == 0) break;
-        if (n != 1) throw Error("input doesn't look like something created by `nix-store --export'");
-        cout << format("%1%\n") % store->importPath(requireSignature, source) << std::flush;
-    }
+    Paths paths = store->importPaths(requireSignature, source);
+
+    foreach (Paths::iterator, i, paths)
+        cout << format("%1%\n") % *i << std::flush;
 }
 
 
diff --git a/src/nix-worker/nix-worker.cc b/src/nix-worker/nix-worker.cc
index 3587bd7fde2e..68567f341e05 100644
--- a/src/nix-worker/nix-worker.cc
+++ b/src/nix-worker/nix-worker.cc
@@ -56,7 +56,8 @@ static void tunnelStderr(const unsigned char * buf, size_t count)
     if (canSendStderr && myPid == getpid()) {
         try {
             writeInt(STDERR_NEXT, to);
-            writeString(string((char *) buf, count), to);
+            writeString(buf, count, to);
+            to.flush();
         } catch (...) {
             /* Write failed; that means that the other side is
                gone. */
@@ -200,26 +201,20 @@ static void stopWork(bool success = true, const string & msg = "", unsigned int
 struct TunnelSink : Sink
 {
     Sink & to;
-    TunnelSink(Sink & to) : to(to)
-    {
-    }
-    virtual void operator ()
-        (const unsigned char * data, unsigned int len)
+    TunnelSink(Sink & to) : to(to) { }
+    virtual void operator () (const unsigned char * data, size_t len)
     {
         writeInt(STDERR_WRITE, to);
-        writeString(string((const char *) data, len), to);
+        writeString(data, len, to);
     }
 };
 
 
-struct TunnelSource : Source
+struct TunnelSource : BufferedSource
 {
     Source & from;
-    TunnelSource(Source & from) : from(from)
-    {
-    }
-    virtual void operator ()
-        (unsigned char * data, unsigned int len)
+    TunnelSource(Source & from) : from(from) { }
+    size_t readUnbuffered(unsigned char * data, size_t len)
     {
         /* Careful: we're going to receive data from the client now,
            so we have to disable the SIGPOLL handler. */
@@ -228,11 +223,12 @@ struct TunnelSource : Source
         
         writeInt(STDERR_READ, to);
         writeInt(len, to);
-        string s = readString(from);
-        if (s.size() != len) throw Error("not enough data");
-        memcpy(data, (const unsigned char *) s.c_str(), len);
+        to.flush();
+        size_t n = readString(data, len, from);
 
         startWork();
+        if (n == 0) throw EndOfFile("unexpected end-of-file");
+        return n;
     }
 };
 
@@ -241,11 +237,14 @@ struct TunnelSource : Source
    the contents of the file to `s'.  Otherwise barf. */
 struct RetrieveRegularNARSink : ParseSink
 {
+    bool regular;
     string s;
 
+    RetrieveRegularNARSink() : regular(true) { }
+
     void createDirectory(const Path & path)
     {
-        throw Error("regular file expected");
+        regular = false;
     }
 
     void receiveContents(unsigned char * data, unsigned int len)
@@ -255,7 +254,7 @@ struct RetrieveRegularNARSink : ParseSink
 
     void createSymlink(const Path & path, const string & target)
     {
-        throw Error("regular file expected");
+        regular = false;
     }
 };
 
@@ -266,10 +265,11 @@ struct SavingSourceAdapter : Source
     Source & orig;
     string s;
     SavingSourceAdapter(Source & orig) : orig(orig) { }
-    void operator () (unsigned char * data, unsigned int len)
+    size_t read(unsigned char * data, size_t len)
     {
-        orig(data, len);
-        s.append((const char *) data, len);
+        size_t n = orig.read(data, len);
+        s.append((const char *) data, n);
+        return n;
     }
 };
 
@@ -327,7 +327,7 @@ static void performOp(unsigned int clientVersion,
             store->queryReferrers(path, paths);
         else paths = store->queryDerivationOutputs(path);
         stopWork();
-        writeStringSet(paths, to);
+        writeStrings(paths, to);
         break;
     }
 
@@ -371,11 +371,11 @@ static void performOp(unsigned int clientVersion,
                addToStoreFromDump(). */
             ParseSink sink; /* null sink; just parse the NAR */
             parseDump(sink, savedNAR);
-        } else {
+        } else
             parseDump(savedRegular, from);
-        }
             
         startWork();
+        if (!savedRegular.regular) throw Error("regular file expected");
         Path path = dynamic_cast<LocalStore *>(store.get())
             ->addToStoreFromDump(recursive ? savedNAR.s : savedRegular.s, baseName, recursive, hashAlgo);
         stopWork();
@@ -387,7 +387,7 @@ static void performOp(unsigned int clientVersion,
     case wopAddTextToStore: {
         string suffix = readString(from);
         string s = readString(from);
-        PathSet refs = readStorePaths(from);
+        PathSet refs = readStorePaths<PathSet>(from);
         startWork();
         Path path = store->addTextToStore(suffix, s, refs);
         stopWork();
@@ -406,17 +406,17 @@ static void performOp(unsigned int clientVersion,
         break;
     }
 
-    case wopImportPath: {
+    case wopImportPaths: {
         startWork();
         TunnelSource source(from);
-        Path path = store->importPath(true, source);
+        Paths paths = store->importPaths(true, source);
         stopWork();
-        writeString(path, to);
+        writeStrings(paths, to);
         break;
     }
 
     case wopBuildDerivations: {
-        PathSet drvs = readStorePaths(from);
+        PathSet drvs = readStorePaths<PathSet>(from);
         startWork();
         store->buildDerivations(drvs);
         stopWork();
@@ -474,7 +474,7 @@ static void performOp(unsigned int clientVersion,
     case wopCollectGarbage: {
         GCOptions options;
         options.action = (GCOptions::GCAction) readInt(from);
-        options.pathsToDelete = readStorePaths(from);
+        options.pathsToDelete = readStorePaths<PathSet>(from);
         options.ignoreLiveness = readInt(from);
         options.maxFreed = readLongLong(from);
         options.maxLinks = readInt(from);
@@ -492,7 +492,7 @@ static void performOp(unsigned int clientVersion,
         store->collectGarbage(options, results);
         stopWork();
         
-        writeStringSet(results.paths, to);
+        writeStrings(results.paths, to);
         writeLongLong(results.bytesFreed, to);
         writeLongLong(results.blocksFreed, to);
         
@@ -530,7 +530,7 @@ static void performOp(unsigned int clientVersion,
         writeInt(res ? 1 : 0, to);
         if (res) {
             writeString(info.deriver, to);
-            writeStringSet(info.references, to);
+            writeStrings(info.references, to);
             writeLongLong(info.downloadSize, to);
             if (GET_PROTOCOL_MINOR(clientVersion) >= 7)
                 writeLongLong(info.narSize, to);
@@ -542,7 +542,7 @@ static void performOp(unsigned int clientVersion,
         startWork();
         PathSet paths = store->queryValidPaths();
         stopWork();
-        writeStringSet(paths, to);
+        writeStrings(paths, to);
         break;
     }
 
@@ -550,12 +550,12 @@ static void performOp(unsigned int clientVersion,
         startWork();
         PathSet paths = store->queryFailedPaths();
         stopWork();
-        writeStringSet(paths, to);
+        writeStrings(paths, to);
         break;
     }
 
     case wopClearFailedPaths: {
-        PathSet paths = readStringSet(from);
+        PathSet paths = readStrings<PathSet>(from);
         startWork();
         store->clearFailedPaths(paths);
         stopWork();
@@ -570,7 +570,7 @@ static void performOp(unsigned int clientVersion,
         stopWork();
         writeString(info.deriver, to);
         writeString(printHash(info.hash), to);
-        writeStringSet(info.references, to);
+        writeStrings(info.references, to);
         writeInt(info.registrationTime, to);
         writeLongLong(info.narSize, to);
         break;
@@ -603,8 +603,8 @@ static void processConnection()
     unsigned int magic = readInt(from);
     if (magic != WORKER_MAGIC_1) throw Error("protocol mismatch");
     writeInt(WORKER_MAGIC_2, to);
-
     writeInt(PROTOCOL_VERSION, to);
+    to.flush();
     unsigned int clientVersion = readInt(from);
 
     /* Send startup error messages to the client. */
@@ -626,9 +626,11 @@ static void processConnection()
         store = boost::shared_ptr<StoreAPI>(new LocalStore());
 
         stopWork();
+        to.flush();
         
     } catch (Error & e) {
         stopWork(false, e.msg());
+        to.flush();
         return;
     }
 
@@ -648,9 +650,19 @@ static void processConnection()
         try {
             performOp(clientVersion, from, to, op);
         } catch (Error & e) {
+            /* If we're not in a state were we can send replies, then
+               something went wrong processing the input of the
+               client.  This can happen especially if I/O errors occur
+               during addTextToStore() / importPath().  If that
+               happens, just send the error message and exit. */
+            bool errorAllowed = canSendStderr;
+            if (!errorAllowed) printMsg(lvlError, format("error processing client input: %1%") % e.msg());
             stopWork(false, e.msg(), GET_PROTOCOL_MINOR(clientVersion) >= 8 ? e.status : 0);
+            if (!errorAllowed) break;
         }
 
+        to.flush();
+
         assert(!canSendStderr);
     };