aboutsummaryrefslogtreecommitdiff
path: root/src/libstore
diff options
context:
space:
mode:
authorJohn Ericson <John.Ericson@Obsidian.Systems>2020-07-09 19:07:06 +0000
committerJohn Ericson <John.Ericson@Obsidian.Systems>2020-07-09 19:07:06 +0000
commit323d51a7a05d81c717d8e8673a78b07500a591fe (patch)
tree7f211f4b87ac11e129b865cd4d187608df1f7a1f /src/libstore
parent0f05a36e205a1d04a0ed6d6cdcd0e3e225f73f05 (diff)
parenta2c27022e9afc394e08d34d349587c8903fc1a97 (diff)
Merge branch 'master' of github.com:NixOS/nix into better-ca-parse-errors
Diffstat (limited to 'src/libstore')
-rw-r--r--src/libstore/build.cc5
-rw-r--r--src/libstore/daemon.cc2
-rw-r--r--src/libstore/globals.hh3
-rw-r--r--src/libstore/local-store.cc135
4 files changed, 129 insertions, 16 deletions
diff --git a/src/libstore/build.cc b/src/libstore/build.cc
index a57055168..6970363b0 100644
--- a/src/libstore/build.cc
+++ b/src/libstore/build.cc
@@ -2041,7 +2041,10 @@ void DerivationGoal::startBuilder()
if (!std::regex_match(fileName, regex))
throw Error("invalid file name '%s' in 'exportReferencesGraph'", fileName);
- auto storePath = worker.store.parseStorePath(*i++);
+ auto storePathS = *i++;
+ if (!worker.store.isInStore(storePathS))
+ throw BuildError("'exportReferencesGraph' contains a non-store path '%1%'", storePathS);
+ auto storePath = worker.store.parseStorePath(worker.store.toStorePath(storePathS));
/* Write closure info to <fileName>. */
writeFile(tmpDir + "/" + fileName,
diff --git a/src/libstore/daemon.cc b/src/libstore/daemon.cc
index c844583e2..e476a25d3 100644
--- a/src/libstore/daemon.cc
+++ b/src/libstore/daemon.cc
@@ -81,7 +81,7 @@ struct TunnelLogger : public Logger
showErrorInfo(oss, ei, false);
StringSink buf;
- buf << STDERR_NEXT << oss.str() << "\n";
+ buf << STDERR_NEXT << oss.str();
enqueueMsg(*buf.s);
}
diff --git a/src/libstore/globals.hh b/src/libstore/globals.hh
index 58cf08763..4d5eec7bf 100644
--- a/src/libstore/globals.hh
+++ b/src/libstore/globals.hh
@@ -365,6 +365,9 @@ public:
Setting<bool> warnDirty{this, true, "warn-dirty",
"Whether to warn about dirty Git/Mercurial trees."};
+
+ Setting<size_t> narBufferSize{this, 32 * 1024 * 1024, "nar-buffer-size",
+ "Maximum size of NARs before spilling them to disk."};
};
diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc
index 749429355..33003580c 100644
--- a/src/libstore/local-store.cc
+++ b/src/libstore/local-store.cc
@@ -976,7 +976,7 @@ void LocalStore::addToStore(const ValidPathInfo & info, Source & source,
PathLocks outputLock;
- Path realPath = realStoreDir + "/" + std::string(info.path.to_string());
+ auto realPath = Store::toRealPath(info.path);
/* Lock the output path. But don't lock if we're being called
from a build hook (whose parent process already acquired a
@@ -1047,8 +1047,7 @@ StorePath LocalStore::addToStoreFromDump(const string & dump, const string & nam
/* The first check above is an optimisation to prevent
unnecessary lock acquisition. */
- Path realPath = realStoreDir + "/";
- realPath += dstPath.to_string();
+ auto realPath = Store::toRealPath(dstPath);
PathLocks outputLock({realPath});
@@ -1098,16 +1097,125 @@ StorePath LocalStore::addToStore(const string & name, const Path & _srcPath,
{
Path srcPath(absPath(_srcPath));
- /* Read the whole path into memory. This is not a very scalable
- method for very large paths, but `copyPath' is mainly used for
- small files. */
- StringSink sink;
- if (method == FileIngestionMethod::Recursive)
- dumpPath(srcPath, sink, filter);
- else
- sink.s = make_ref<std::string>(readFile(srcPath));
+ /* For computing the NAR hash. */
+ auto sha256Sink = std::make_unique<HashSink>(htSHA256);
+
+ /* For computing the store path. In recursive SHA-256 mode, this
+ is the same as the NAR hash, so no need to do it again. */
+ std::unique_ptr<HashSink> hashSink =
+ method == FileIngestionMethod::Recursive && hashAlgo == htSHA256
+ ? nullptr
+ : std::make_unique<HashSink>(hashAlgo);
+
+ /* Read the source path into memory, but only if it's up to
+ narBufferSize bytes. If it's larger, write it to a temporary
+ location in the Nix store. If the subsequently computed
+ destination store path is already valid, we just delete the
+ temporary path. Otherwise, we move it to the destination store
+ path. */
+ bool inMemory = true;
+ std::string nar;
+
+ auto source = sinkToSource([&](Sink & sink) {
+
+ LambdaSink sink2([&](const unsigned char * buf, size_t len) {
+ (*sha256Sink)(buf, len);
+ if (hashSink) (*hashSink)(buf, len);
+
+ if (inMemory) {
+ if (nar.size() + len > settings.narBufferSize) {
+ inMemory = false;
+ sink << 1;
+ sink((const unsigned char *) nar.data(), nar.size());
+ nar.clear();
+ } else {
+ nar.append((const char *) buf, len);
+ }
+ }
+
+ if (!inMemory) sink(buf, len);
+ });
+
+ if (method == FileIngestionMethod::Recursive)
+ dumpPath(srcPath, sink2, filter);
+ else
+ readFile(srcPath, sink2);
+ });
+
+ std::unique_ptr<AutoDelete> delTempDir;
+ Path tempPath;
+
+ try {
+ /* Wait for the source coroutine to give us some dummy
+ data. This is so that we don't create the temporary
+ directory if the NAR fits in memory. */
+ readInt(*source);
+
+ auto tempDir = createTempDir(realStoreDir, "add");
+ delTempDir = std::make_unique<AutoDelete>(tempDir);
+ tempPath = tempDir + "/x";
+
+ if (method == FileIngestionMethod::Recursive)
+ restorePath(tempPath, *source);
+ else
+ writeFile(tempPath, *source);
+
+ } catch (EndOfFile &) {
+ if (!inMemory) throw;
+ /* The NAR fits in memory, so we didn't do restorePath(). */
+ }
+
+ auto sha256 = sha256Sink->finish();
+
+ Hash hash = hashSink ? hashSink->finish().first : sha256.first;
+
+ auto dstPath = makeFixedOutputPath(method, hash, name);
+
+ addTempRoot(dstPath);
+
+ if (repair || !isValidPath(dstPath)) {
+
+ /* The first check above is an optimisation to prevent
+ unnecessary lock acquisition. */
+
+ auto realPath = Store::toRealPath(dstPath);
- return addToStoreFromDump(*sink.s, name, method, hashAlgo, repair);
+ PathLocks outputLock({realPath});
+
+ if (repair || !isValidPath(dstPath)) {
+
+ deletePath(realPath);
+
+ autoGC();
+
+ if (inMemory) {
+ /* Restore from the NAR in memory. */
+ StringSource source(nar);
+ if (method == FileIngestionMethod::Recursive)
+ restorePath(realPath, source);
+ else
+ writeFile(realPath, source);
+ } else {
+ /* Move the temporary path we restored above. */
+ if (rename(tempPath.c_str(), realPath.c_str()))
+ throw Error("renaming '%s' to '%s'", tempPath, realPath);
+ }
+
+ canonicalisePathMetaData(realPath, -1); // FIXME: merge into restorePath
+
+ optimisePath(realPath);
+
+ ValidPathInfo info(dstPath);
+ info.narHash = sha256.first;
+ info.narSize = sha256.second;
+ info.ca = FixedOutputHash { .method = method, .hash = hash };
+ registerValidPath(info);
+ }
+
+ outputLock.setDeletion(true);
+ }
+
+ return dstPath;
}
@@ -1121,8 +1229,7 @@ StorePath LocalStore::addTextToStore(const string & name, const string & s,
if (repair || !isValidPath(dstPath)) {
- Path realPath = realStoreDir + "/";
- realPath += dstPath.to_string();
+ auto realPath = Store::toRealPath(dstPath);
PathLocks outputLock({realPath});