aboutsummaryrefslogtreecommitdiff
path: root/src/libstore
diff options
context:
space:
mode:
Diffstat (limited to 'src/libstore')
-rw-r--r--src/libstore/binary-cache-store.cc128
-rw-r--r--src/libstore/binary-cache-store.hh9
-rw-r--r--src/libstore/build.cc9
-rw-r--r--src/libstore/builtins/buildenv.hh2
-rw-r--r--src/libstore/daemon.cc15
-rw-r--r--src/libstore/derivations.cc7
-rw-r--r--src/libstore/export-import.cc43
-rw-r--r--src/libstore/filetransfer.cc18
-rw-r--r--src/libstore/gc.cc30
-rw-r--r--src/libstore/globals.hh3
-rw-r--r--src/libstore/http-binary-cache-store.cc4
-rw-r--r--src/libstore/legacy-ssh-store.cc3
-rw-r--r--src/libstore/local-binary-cache-store.cc35
-rw-r--r--src/libstore/local-fs-store.cc6
-rw-r--r--src/libstore/local-store.cc24
-rw-r--r--src/libstore/local-store.hh3
-rw-r--r--src/libstore/local.mk3
-rw-r--r--src/libstore/nar-accessor.cc46
-rw-r--r--src/libstore/nar-accessor.hh4
-rw-r--r--src/libstore/path.cc2
-rw-r--r--src/libstore/profiles.cc81
-rw-r--r--src/libstore/profiles.hh25
-rw-r--r--src/libstore/remote-fs-accessor.cc33
-rw-r--r--src/libstore/remote-fs-accessor.hh6
-rw-r--r--src/libstore/remote-store.cc2
-rw-r--r--src/libstore/remote-store.hh3
-rw-r--r--src/libstore/s3-binary-cache-store.cc39
-rw-r--r--src/libstore/sqlite.cc9
-rw-r--r--src/libstore/sqlite.hh4
-rw-r--r--src/libstore/store-api.cc30
-rw-r--r--src/libstore/store-api.hh23
31 files changed, 346 insertions, 303 deletions
diff --git a/src/libstore/binary-cache-store.cc b/src/libstore/binary-cache-store.cc
index 9f52ddafa..b791c125b 100644
--- a/src/libstore/binary-cache-store.cc
+++ b/src/libstore/binary-cache-store.cc
@@ -15,6 +15,7 @@
#include <chrono>
#include <future>
#include <regex>
+#include <fstream>
#include <nlohmann/json.hpp>
@@ -57,6 +58,13 @@ void BinaryCacheStore::init()
}
}
+void BinaryCacheStore::upsertFile(const std::string & path,
+ std::string && data,
+ const std::string & mimeType)
+{
+ upsertFile(path, std::make_shared<std::stringstream>(std::move(data)), mimeType);
+}
+
void BinaryCacheStore::getFile(const std::string & path,
Callback<std::shared_ptr<std::string>> callback) noexcept
{
@@ -113,13 +121,74 @@ void BinaryCacheStore::writeNarInfo(ref<NarInfo> narInfo)
diskCache->upsertNarInfo(getUri(), hashPart, std::shared_ptr<NarInfo>(narInfo));
}
+AutoCloseFD openFile(const Path & path)
+{
+ auto fd = open(path.c_str(), O_RDONLY | O_CLOEXEC);
+ if (!fd)
+ throw SysError("opening file '%1%'", path);
+ return fd;
+}
+
+struct FileSource : FdSource
+{
+ AutoCloseFD fd2;
+
+ FileSource(const Path & path)
+ : fd2(openFile(path))
+ {
+ fd = fd2.get();
+ }
+};
+
void BinaryCacheStore::addToStore(const ValidPathInfo & info, Source & narSource,
- RepairFlag repair, CheckSigsFlag checkSigs, std::shared_ptr<FSAccessor> accessor)
+ RepairFlag repair, CheckSigsFlag checkSigs)
{
- // FIXME: See if we can use the original source to reduce memory usage.
- auto nar = make_ref<std::string>(narSource.drain());
+ assert(info.narHash && info.narSize);
+
+ if (!repair && isValidPath(info.path)) {
+ // FIXME: copyNAR -> null sink
+ narSource.drain();
+ return;
+ }
+
+ auto [fdTemp, fnTemp] = createTempFile();
+
+ auto now1 = std::chrono::steady_clock::now();
+
+ /* Read the NAR simultaneously into a CompressionSink+FileSink (to
+ write the compressed NAR to disk), into a HashSink (to get the
+ NAR hash), and into a NarAccessor (to get the NAR listing). */
+ HashSink fileHashSink(htSHA256);
+ std::shared_ptr<FSAccessor> narAccessor;
+ {
+ FdSink fileSink(fdTemp.get());
+ TeeSink teeSink(fileSink, fileHashSink);
+ auto compressionSink = makeCompressionSink(compression, teeSink);
+ TeeSource teeSource(narSource, *compressionSink);
+ narAccessor = makeNarAccessor(teeSource);
+ compressionSink->finish();
+ }
+
+ auto now2 = std::chrono::steady_clock::now();
+
+ auto narInfo = make_ref<NarInfo>(info);
+ narInfo->narSize = info.narSize;
+ narInfo->narHash = info.narHash;
+ narInfo->compression = compression;
+ auto [fileHash, fileSize] = fileHashSink.finish();
+ narInfo->fileHash = fileHash;
+ narInfo->fileSize = fileSize;
+ narInfo->url = "nar/" + narInfo->fileHash.to_string(Base32, false) + ".nar"
+ + (compression == "xz" ? ".xz" :
+ compression == "bzip2" ? ".bz2" :
+ compression == "br" ? ".br" :
+ "");
- if (!repair && isValidPath(info.path)) return;
+ auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(now2 - now1).count();
+ printMsg(lvlTalkative, "copying path '%1%' (%2% bytes, compressed %3$.1f%% in %4% ms) to binary cache",
+ printStorePath(narInfo->path), info.narSize,
+ ((1.0 - (double) fileSize / info.narSize) * 100.0),
+ duration);
/* Verify that all references are valid. This may do some .narinfo
reads, but typically they'll already be cached. */
@@ -132,23 +201,6 @@ void BinaryCacheStore::addToStore(const ValidPathInfo & info, Source & narSource
printStorePath(info.path), printStorePath(ref));
}
- assert(nar->compare(0, narMagic.size(), narMagic) == 0);
-
- auto narInfo = make_ref<NarInfo>(info);
-
- narInfo->narSize = nar->size();
- narInfo->narHash = hashString(htSHA256, *nar);
-
- if (info.narHash && info.narHash != narInfo->narHash)
- throw Error("refusing to copy corrupted path '%1%' to binary cache", printStorePath(info.path));
-
- auto accessor_ = std::dynamic_pointer_cast<RemoteFSAccessor>(accessor);
-
- auto narAccessor = makeNarAccessor(nar);
-
- if (accessor_)
- accessor_->addToCache(printStorePath(info.path), *nar, narAccessor);
-
/* Optionally write a JSON file containing a listing of the
contents of the NAR. */
if (writeNARListing) {
@@ -160,33 +212,13 @@ void BinaryCacheStore::addToStore(const ValidPathInfo & info, Source & narSource
{
auto res = jsonRoot.placeholder("root");
- listNar(res, narAccessor, "", true);
+ listNar(res, ref<FSAccessor>(narAccessor), "", true);
}
}
upsertFile(std::string(info.path.to_string()) + ".ls", jsonOut.str(), "application/json");
}
- /* Compress the NAR. */
- narInfo->compression = compression;
- auto now1 = std::chrono::steady_clock::now();
- auto narCompressed = compress(compression, *nar, parallelCompression);
- auto now2 = std::chrono::steady_clock::now();
- narInfo->fileHash = hashString(htSHA256, *narCompressed);
- narInfo->fileSize = narCompressed->size();
-
- auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(now2 - now1).count();
- printMsg(lvlTalkative, "copying path '%1%' (%2% bytes, compressed %3$.1f%% in %4% ms) to binary cache",
- printStorePath(narInfo->path), narInfo->narSize,
- ((1.0 - (double) narCompressed->size() / nar->size()) * 100.0),
- duration);
-
- narInfo->url = "nar/" + narInfo->fileHash.to_string(Base32, false) + ".nar"
- + (compression == "xz" ? ".xz" :
- compression == "bzip2" ? ".bz2" :
- compression == "br" ? ".br" :
- "");
-
/* Optionally maintain an index of DWARF debug info files
consisting of JSON files named 'debuginfo/<build-id>' that
specify the NAR file and member containing the debug info. */
@@ -247,12 +279,14 @@ void BinaryCacheStore::addToStore(const ValidPathInfo & info, Source & narSource
/* Atomically write the NAR file. */
if (repair || !fileExists(narInfo->url)) {
stats.narWrite++;
- upsertFile(narInfo->url, *narCompressed, "application/x-nix-nar");
+ upsertFile(narInfo->url,
+ std::make_shared<std::fstream>(fnTemp, std::ios_base::in),
+ "application/x-nix-nar");
} else
stats.narWriteAverted++;
- stats.narWriteBytes += nar->size();
- stats.narWriteCompressedBytes += narCompressed->size();
+ stats.narWriteBytes += info.narSize;
+ stats.narWriteCompressedBytes += fileSize;
stats.narWriteCompressionTimeMs += duration;
/* Atomically write the NAR info file.*/
@@ -351,7 +385,7 @@ StorePath BinaryCacheStore::addToStore(const string & name, const Path & srcPath
ValidPathInfo info(makeFixedOutputPath(method, h, name));
auto source = StringSource { *sink.s };
- addToStore(info, source, repair, CheckSigs, nullptr);
+ addToStore(info, source, repair, CheckSigs);
return std::move(info.path);
}
@@ -366,7 +400,7 @@ StorePath BinaryCacheStore::addTextToStore(const string & name, const string & s
StringSink sink;
dumpString(s, sink);
auto source = StringSource { *sink.s };
- addToStore(info, source, repair, CheckSigs, nullptr);
+ addToStore(info, source, repair, CheckSigs);
}
return std::move(info.path);
diff --git a/src/libstore/binary-cache-store.hh b/src/libstore/binary-cache-store.hh
index 52ef8aa7a..9bcdf5901 100644
--- a/src/libstore/binary-cache-store.hh
+++ b/src/libstore/binary-cache-store.hh
@@ -36,9 +36,13 @@ public:
virtual bool fileExists(const std::string & path) = 0;
virtual void upsertFile(const std::string & path,
- const std::string & data,
+ std::shared_ptr<std::basic_iostream<char>> istream,
const std::string & mimeType) = 0;
+ void upsertFile(const std::string & path,
+ std::string && data,
+ const std::string & mimeType);
+
/* Note: subclasses must implement at least one of the two
following getFile() methods. */
@@ -75,8 +79,7 @@ public:
{ unsupported("queryPathFromHashPart"); }
void addToStore(const ValidPathInfo & info, Source & narSource,
- RepairFlag repair, CheckSigsFlag checkSigs,
- std::shared_ptr<FSAccessor> accessor) override;
+ RepairFlag repair, CheckSigsFlag checkSigs) override;
StorePath addToStore(const string & name, const Path & srcPath,
FileIngestionMethod method, HashType hashAlgo,
diff --git a/src/libstore/build.cc b/src/libstore/build.cc
index 9a53d9df7..1c88d91bc 100644
--- a/src/libstore/build.cc
+++ b/src/libstore/build.cc
@@ -2044,7 +2044,7 @@ void DerivationGoal::startBuilder()
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));
+ auto storePath = worker.store.toStorePath(storePathS).first;
/* Write closure info to <fileName>. */
writeFile(tmpDir + "/" + fileName,
@@ -2083,7 +2083,7 @@ void DerivationGoal::startBuilder()
for (auto & i : dirsInChroot)
try {
if (worker.store.isInStore(i.second.source))
- worker.store.computeFSClosure(worker.store.parseStorePath(worker.store.toStorePath(i.second.source)), closure);
+ worker.store.computeFSClosure(worker.store.toStorePath(i.second.source).first, closure);
} catch (InvalidPath & e) {
} catch (Error & e) {
throw Error("while processing 'sandbox-paths': %s", e.what());
@@ -2768,10 +2768,9 @@ struct RestrictedStore : public LocalFSStore
{ throw Error("addToStore"); }
void addToStore(const ValidPathInfo & info, Source & narSource,
- RepairFlag repair = NoRepair, CheckSigsFlag checkSigs = CheckSigs,
- std::shared_ptr<FSAccessor> accessor = 0) override
+ RepairFlag repair = NoRepair, CheckSigsFlag checkSigs = CheckSigs) override
{
- next->addToStore(info, narSource, repair, checkSigs, accessor);
+ next->addToStore(info, narSource, repair, checkSigs);
goal.addDependency(info.path);
}
diff --git a/src/libstore/builtins/buildenv.hh b/src/libstore/builtins/buildenv.hh
index 0a37459b0..73c0f5f7f 100644
--- a/src/libstore/builtins/buildenv.hh
+++ b/src/libstore/builtins/buildenv.hh
@@ -9,7 +9,7 @@ struct Package {
Path path;
bool active;
int priority;
- Package(Path path, bool active, int priority) : path{path}, active{active}, priority{priority} {}
+ Package(const Path & path, bool active, int priority) : path{path}, active{active}, priority{priority} {}
};
typedef std::vector<Package> Packages;
diff --git a/src/libstore/daemon.cc b/src/libstore/daemon.cc
index 2ff53c964..e574ea1a7 100644
--- a/src/libstore/daemon.cc
+++ b/src/libstore/daemon.cc
@@ -391,7 +391,8 @@ static void performOp(TunnelLogger * logger, ref<Store> store,
}
HashType hashAlgo = parseHashType(s);
- TeeSource savedNAR(from);
+ StringSink savedNAR;
+ TeeSource savedNARSource(from, savedNAR);
RetrieveRegularNARSink savedRegular;
if (method == FileIngestionMethod::Recursive) {
@@ -399,7 +400,7 @@ static void performOp(TunnelLogger * logger, ref<Store> store,
a string so that we can pass it to
addToStoreFromDump(). */
ParseSink sink; /* null sink; just parse the NAR */
- parseDump(sink, savedNAR);
+ parseDump(sink, savedNARSource);
} else
parseDump(savedRegular, from);
@@ -407,7 +408,7 @@ static void performOp(TunnelLogger * logger, ref<Store> store,
if (!savedRegular.regular) throw Error("regular file expected");
auto path = store->addToStoreFromDump(
- method == FileIngestionMethod::Recursive ? *savedNAR.data : savedRegular.s,
+ method == FileIngestionMethod::Recursive ? *savedNAR.s : savedRegular.s,
baseName,
method,
hashAlgo);
@@ -442,7 +443,7 @@ static void performOp(TunnelLogger * logger, ref<Store> store,
case wopImportPaths: {
logger->startWork();
TunnelSource source(from, to);
- auto paths = store->importPaths(source, nullptr,
+ auto paths = store->importPaths(source,
trusted ? NoCheckSigs : CheckSigs);
logger->stopWork();
Strings paths2;
@@ -731,9 +732,9 @@ static void performOp(TunnelLogger * logger, ref<Store> store,
if (GET_PROTOCOL_MINOR(clientVersion) >= 21)
source = std::make_unique<TunnelSource>(from, to);
else {
- TeeSink tee(from);
+ TeeParseSink tee(from);
parseDump(tee, tee.source);
- saved = std::move(*tee.source.data);
+ saved = std::move(*tee.saved.s);
source = std::make_unique<StringSource>(saved);
}
@@ -741,7 +742,7 @@ static void performOp(TunnelLogger * logger, ref<Store> store,
// FIXME: race if addToStore doesn't read source?
store->addToStore(info, *source, (RepairFlag) repair,
- dontCheckSigs ? NoCheckSigs : CheckSigs, nullptr);
+ dontCheckSigs ? NoCheckSigs : CheckSigs);
logger->stopWork();
break;
diff --git a/src/libstore/derivations.cc b/src/libstore/derivations.cc
index 375d089ec..24e66f1db 100644
--- a/src/libstore/derivations.cc
+++ b/src/libstore/derivations.cc
@@ -4,7 +4,6 @@
#include "util.hh"
#include "worker-protocol.hh"
#include "fs-accessor.hh"
-#include "istringstream_nocopy.hh"
namespace nix {
@@ -118,7 +117,7 @@ static StringSet parseStrings(std::istream & str, bool arePaths)
}
-static DerivationOutput parseDerivationOutput(const Store & store, istringstream_nocopy & str)
+static DerivationOutput parseDerivationOutput(const Store & store, std::istringstream & str)
{
expect(str, ","); auto path = store.parseStorePath(parsePath(str));
expect(str, ","); auto hashAlgo = parseString(str);
@@ -157,12 +156,12 @@ static DerivationOutput parseDerivationOutput(const Store & store, istringstream
}
-static Derivation parseDerivation(const Store & store, const string & s, std::string_view name)
+static Derivation parseDerivation(const Store & store, std::string && s, std::string_view name)
{
Derivation drv;
drv.name = name;
- istringstream_nocopy str(s);
+ std::istringstream str(std::move(s));
expect(str, "Derive([");
/* Parse the list of outputs. */
diff --git a/src/libstore/export-import.cc b/src/libstore/export-import.cc
index 57b7e9590..082d0f1d1 100644
--- a/src/libstore/export-import.cc
+++ b/src/libstore/export-import.cc
@@ -7,24 +7,6 @@
namespace nix {
-struct HashAndWriteSink : Sink
-{
- Sink & writeSink;
- HashSink hashSink;
- HashAndWriteSink(Sink & writeSink) : writeSink(writeSink), hashSink(htSHA256)
- {
- }
- virtual void operator () (const unsigned char * data, size_t len)
- {
- writeSink(data, len);
- hashSink(data, len);
- }
- Hash currentHash()
- {
- return hashSink.currentHash().first;
- }
-};
-
void Store::exportPaths(const StorePathSet & paths, Sink & sink)
{
auto sorted = topoSortPaths(paths);
@@ -47,28 +29,29 @@ void Store::exportPath(const StorePath & path, Sink & sink)
{
auto info = queryPathInfo(path);
- HashAndWriteSink hashAndWriteSink(sink);
+ HashSink hashSink(htSHA256);
+ TeeSink teeSink(sink, hashSink);
- narFromPath(path, hashAndWriteSink);
+ narFromPath(path, teeSink);
/* Refuse to export paths that have changed. This prevents
filesystem corruption from spreading to other machines.
Don't complain if the stored hash is zero (unknown). */
- Hash hash = hashAndWriteSink.currentHash();
+ Hash hash = hashSink.currentHash().first;
if (hash != info->narHash && info->narHash != Hash(*info->narHash.type))
throw Error("hash of path '%s' has changed from '%s' to '%s'!",
printStorePath(path), info->narHash.to_string(Base32, true), hash.to_string(Base32, true));
- hashAndWriteSink
+ teeSink
<< exportMagic
<< printStorePath(path);
- writeStorePaths(*this, hashAndWriteSink, info->references);
- hashAndWriteSink
+ writeStorePaths(*this, teeSink, info->references);
+ teeSink
<< (info->deriver ? printStorePath(*info->deriver) : "")
<< 0;
}
-StorePaths Store::importPaths(Source & source, std::shared_ptr<FSAccessor> accessor, CheckSigsFlag checkSigs)
+StorePaths Store::importPaths(Source & source, CheckSigsFlag checkSigs)
{
StorePaths res;
while (true) {
@@ -77,7 +60,7 @@ StorePaths Store::importPaths(Source & source, std::shared_ptr<FSAccessor> acces
if (n != 1) throw Error("input doesn't look like something created by 'nix-store --export'");
/* Extract the NAR from the source. */
- TeeSink tee(source);
+ TeeParseSink tee(source);
parseDump(tee, tee.source);
uint32_t magic = readInt(source);
@@ -94,16 +77,16 @@ StorePaths Store::importPaths(Source & source, std::shared_ptr<FSAccessor> acces
if (deriver != "")
info.deriver = parseStorePath(deriver);
- info.narHash = hashString(htSHA256, *tee.source.data);
- info.narSize = tee.source.data->size();
+ info.narHash = hashString(htSHA256, *tee.saved.s);
+ info.narSize = tee.saved.s->size();
// Ignore optional legacy signature.
if (readInt(source) == 1)
readString(source);
// Can't use underlying source, which would have been exhausted
- auto source = StringSource { *tee.source.data };
- addToStore(info, source, NoRepair, checkSigs, accessor);
+ auto source = StringSource { *tee.saved.s };
+ addToStore(info, source, NoRepair, checkSigs);
res.push_back(info.path);
}
diff --git a/src/libstore/filetransfer.cc b/src/libstore/filetransfer.cc
index 531b85af8..beb508e67 100644
--- a/src/libstore/filetransfer.cc
+++ b/src/libstore/filetransfer.cc
@@ -22,6 +22,7 @@
#include <queue>
#include <random>
#include <thread>
+#include <regex>
using namespace std::string_literals;
@@ -56,7 +57,7 @@ struct curlFileTransfer : public FileTransfer
Callback<FileTransferResult> callback;
CURL * req = 0;
bool active = false; // whether the handle has been added to the multi object
- std::string status;
+ std::string statusMsg;
unsigned int attempt = 0;
@@ -175,12 +176,13 @@ struct curlFileTransfer : public FileTransfer
size_t realSize = size * nmemb;
std::string line((char *) contents, realSize);
printMsg(lvlVomit, format("got header for '%s': %s") % request.uri % trim(line));
- if (line.compare(0, 5, "HTTP/") == 0) { // new response starts
+ static std::regex statusLine("HTTP/[^ ]+ +[0-9]+(.*)", std::regex::extended | std::regex::icase);
+ std::smatch match;
+ if (std::regex_match(line, match, statusLine)) {
result.etag = "";
- auto ss = tokenizeString<vector<string>>(line, " ");
- status = ss.size() >= 2 ? ss[1] : "";
result.data = std::make_shared<std::string>();
result.bodySize = 0;
+ statusMsg = trim(match[1]);
acceptRanges = false;
encoding = "";
} else {
@@ -194,7 +196,9 @@ struct curlFileTransfer : public FileTransfer
the expected ETag on a 200 response, then shut
down the connection because we already have the
data. */
- if (result.etag == request.expectedETag && status == "200") {
+ long httpStatus = 0;
+ curl_easy_getinfo(req, CURLINFO_RESPONSE_CODE, &httpStatus);
+ if (result.etag == request.expectedETag && httpStatus == 200) {
debug(format("shutting down on 200 HTTP response with expected ETag"));
return 0;
}
@@ -413,8 +417,8 @@ struct curlFileTransfer : public FileTransfer
? FileTransferError(Interrupted, fmt("%s of '%s' was interrupted", request.verb(), request.uri))
: httpStatus != 0
? FileTransferError(err,
- fmt("unable to %s '%s': HTTP error %d",
- request.verb(), request.uri, httpStatus)
+ fmt("unable to %s '%s': HTTP error %d ('%s')",
+ request.verb(), request.uri, httpStatus, statusMsg)
+ (code == CURLE_OK ? "" : fmt(" (curl error: %s)", curl_easy_strerror(code)))
)
: FileTransferError(err,
diff --git a/src/libstore/gc.cc b/src/libstore/gc.cc
index 57fb20845..aaed5c218 100644
--- a/src/libstore/gc.cc
+++ b/src/libstore/gc.cc
@@ -262,11 +262,13 @@ void LocalStore::findTempRoots(FDs & fds, Roots & tempRoots, bool censor)
void LocalStore::findRoots(const Path & path, unsigned char type, Roots & roots)
{
auto foundRoot = [&](const Path & path, const Path & target) {
- auto storePath = maybeParseStorePath(toStorePath(target));
- if (storePath && isValidPath(*storePath))
- roots[std::move(*storePath)].emplace(path);
- else
- printInfo("skipping invalid root from '%1%' to '%2%'", path, target);
+ try {
+ auto storePath = toStorePath(target).first;
+ if (isValidPath(storePath))
+ roots[std::move(storePath)].emplace(path);
+ else
+ printInfo("skipping invalid root from '%1%' to '%2%'", path, target);
+ } catch (BadStorePath &) { }
};
try {
@@ -472,15 +474,15 @@ void LocalStore::findRuntimeRoots(Roots & roots, bool censor)
for (auto & [target, links] : unchecked) {
if (!isInStore(target)) continue;
- Path pathS = toStorePath(target);
- if (!isStorePath(pathS)) continue;
- auto path = parseStorePath(pathS);
- if (!isValidPath(path)) continue;
- debug("got additional root '%1%'", pathS);
- if (censor)
- roots[path].insert(censored);
- else
- roots[path].insert(links.begin(), links.end());
+ try {
+ auto path = toStorePath(target).first;
+ if (!isValidPath(path)) continue;
+ debug("got additional root '%1%'", printStorePath(path));
+ if (censor)
+ roots[path].insert(censored);
+ else
+ roots[path].insert(links.begin(), links.end());
+ } catch (BadStorePath &) { }
}
}
diff --git a/src/libstore/globals.hh b/src/libstore/globals.hh
index 4d5eec7bf..d47e0b6b5 100644
--- a/src/libstore/globals.hh
+++ b/src/libstore/globals.hh
@@ -368,6 +368,9 @@ public:
Setting<size_t> narBufferSize{this, 32 * 1024 * 1024, "nar-buffer-size",
"Maximum size of NARs before spilling them to disk."};
+
+ Setting<std::string> flakeRegistry{this, "https://github.com/NixOS/flake-registry/raw/master/flake-registry.json", "flake-registry",
+ "Path or URI of the global flake registry."};
};
diff --git a/src/libstore/http-binary-cache-store.cc b/src/libstore/http-binary-cache-store.cc
index 451a64785..c1ceb08cf 100644
--- a/src/libstore/http-binary-cache-store.cc
+++ b/src/libstore/http-binary-cache-store.cc
@@ -100,11 +100,11 @@ protected:
}
void upsertFile(const std::string & path,
- const std::string & data,
+ std::shared_ptr<std::basic_iostream<char>> istream,
const std::string & mimeType) override
{
auto req = FileTransferRequest(cacheUri + "/" + path);
- req.data = std::make_shared<string>(data); // FIXME: inefficient
+ req.data = std::make_shared<string>(StreamToSourceAdapter(istream).drain());
req.mimeType = mimeType;
try {
getFileTransfer()->upload(req);
diff --git a/src/libstore/legacy-ssh-store.cc b/src/libstore/legacy-ssh-store.cc
index 5657aa593..a8bd8a972 100644
--- a/src/libstore/legacy-ssh-store.cc
+++ b/src/libstore/legacy-ssh-store.cc
@@ -126,8 +126,7 @@ struct LegacySSHStore : public Store
}
void addToStore(const ValidPathInfo & info, Source & source,
- RepairFlag repair, CheckSigsFlag checkSigs,
- std::shared_ptr<FSAccessor> accessor) override
+ RepairFlag repair, CheckSigsFlag checkSigs) override
{
debug("adding path '%s' to remote host '%s'", printStorePath(info.path), host);
diff --git a/src/libstore/local-binary-cache-store.cc b/src/libstore/local-binary-cache-store.cc
index 48aca478c..87d8334d7 100644
--- a/src/libstore/local-binary-cache-store.cc
+++ b/src/libstore/local-binary-cache-store.cc
@@ -31,8 +31,18 @@ protected:
bool fileExists(const std::string & path) override;
void upsertFile(const std::string & path,
- const std::string & data,
- const std::string & mimeType) override;
+ std::shared_ptr<std::basic_iostream<char>> istream,
+ const std::string & mimeType) override
+ {
+ auto path2 = binaryCacheDir + "/" + path;
+ Path tmp = path2 + ".tmp." + std::to_string(getpid());
+ AutoDelete del(tmp, false);
+ StreamToSourceAdapter source(istream);
+ writeFile(tmp, source);
+ if (rename(tmp.c_str(), path2.c_str()))
+ throw SysError("renaming '%1%' to '%2%'", tmp, path2);
+ del.cancel();
+ }
void getFile(const std::string & path, Sink & sink) override
{
@@ -52,7 +62,9 @@ protected:
if (entry.name.size() != 40 ||
!hasSuffix(entry.name, ".narinfo"))
continue;
- paths.insert(parseStorePath(storeDir + "/" + entry.name.substr(0, entry.name.size() - 8)));
+ paths.insert(parseStorePath(
+ storeDir + "/" + entry.name.substr(0, entry.name.size() - 8)
+ + "-" + MissingName));
}
return paths;
@@ -68,28 +80,11 @@ void LocalBinaryCacheStore::init()
BinaryCacheStore::init();
}
-static void atomicWrite(const Path & path, const std::string & s)
-{
- Path tmp = path + ".tmp." + std::to_string(getpid());
- AutoDelete del(tmp, false);
- writeFile(tmp, s);
- if (rename(tmp.c_str(), path.c_str()))
- throw SysError("renaming '%1%' to '%2%'", tmp, path);
- del.cancel();
-}
-
bool LocalBinaryCacheStore::fileExists(const std::string & path)
{
return pathExists(binaryCacheDir + "/" + path);
}
-void LocalBinaryCacheStore::upsertFile(const std::string & path,
- const std::string & data,
- const std::string & mimeType)
-{
- atomicWrite(binaryCacheDir + "/" + path, data);
-}
-
static RegisterStoreImplementation regStore([](
const std::string & uri, const Store::Params & params)
-> std::shared_ptr<Store>
diff --git a/src/libstore/local-fs-store.cc b/src/libstore/local-fs-store.cc
index dd96d2578..2f1d9663a 100644
--- a/src/libstore/local-fs-store.cc
+++ b/src/libstore/local-fs-store.cc
@@ -20,9 +20,9 @@ struct LocalStoreAccessor : public FSAccessor
Path toRealPath(const Path & path)
{
- Path storePath = store->toStorePath(path);
- if (!store->isValidPath(store->parseStorePath(storePath)))
- throw InvalidPath("path '%1%' is not a valid store path", storePath);
+ auto storePath = store->toStorePath(path).first;
+ if (!store->isValidPath(storePath))
+ throw InvalidPath("path '%1%' is not a valid store path", store->printStorePath(storePath));
return store->getRealStoreDir() + std::string(path, store->storeDir.size());
}
diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc
index d1aca9790..d1a3b95c1 100644
--- a/src/libstore/local-store.cc
+++ b/src/libstore/local-store.cc
@@ -587,7 +587,7 @@ uint64_t LocalStore::addValidPath(State & state,
(concatStringsSep(" ", info.sigs), !info.sigs.empty())
(renderContentAddress(info.ca), (bool) info.ca)
.exec();
- uint64_t id = sqlite3_last_insert_rowid(state.db);
+ uint64_t id = state.db.getLastInsertedRowId();
/* If this is a derivation, then store the derivation outputs in
the database. This is useful for the garbage collector: it can
@@ -955,7 +955,7 @@ const PublicKeys & LocalStore::getPublicKeys()
void LocalStore::addToStore(const ValidPathInfo & info, Source & source,
- RepairFlag repair, CheckSigsFlag checkSigs, std::shared_ptr<FSAccessor> accessor)
+ RepairFlag repair, CheckSigsFlag checkSigs)
{
if (!info.narHash)
throw Error("cannot add path '%s' because it lacks a hash", printStorePath(info.path));
@@ -1090,13 +1090,16 @@ StorePath LocalStore::addToStore(const string & name, const Path & _srcPath,
{
Path srcPath(absPath(_srcPath));
+ if (method != FileIngestionMethod::Recursive)
+ return addToStoreFromDump(readFile(srcPath), name, method, hashAlgo, repair);
+
/* 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
+ hashAlgo == htSHA256
? nullptr
: std::make_unique<HashSink>(hashAlgo);
@@ -1129,10 +1132,7 @@ StorePath LocalStore::addToStore(const string & name, const Path & _srcPath,
if (!inMemory) sink(buf, len);
});
- if (method == FileIngestionMethod::Recursive)
- dumpPath(srcPath, sink2, filter);
- else
- readFile(srcPath, sink2);
+ dumpPath(srcPath, sink2, filter);
});
std::unique_ptr<AutoDelete> delTempDir;
@@ -1148,10 +1148,7 @@ StorePath LocalStore::addToStore(const string & name, const Path & _srcPath,
delTempDir = std::make_unique<AutoDelete>(tempDir);
tempPath = tempDir + "/x";
- if (method == FileIngestionMethod::Recursive)
- restorePath(tempPath, *source);
- else
- writeFile(tempPath, *source);
+ restorePath(tempPath, *source);
} catch (EndOfFile &) {
if (!inMemory) throw;
@@ -1184,10 +1181,7 @@ StorePath LocalStore::addToStore(const string & name, const Path & _srcPath,
if (inMemory) {
/* Restore from the NAR in memory. */
StringSource source(nar);
- if (method == FileIngestionMethod::Recursive)
- restorePath(realPath, source);
- else
- writeFile(realPath, source);
+ restorePath(realPath, source);
} else {
/* Move the temporary path we restored above. */
if (rename(tempPath.c_str(), realPath.c_str()))
diff --git a/src/libstore/local-store.hh b/src/libstore/local-store.hh
index ff36cb00e..c0e5d0286 100644
--- a/src/libstore/local-store.hh
+++ b/src/libstore/local-store.hh
@@ -143,8 +143,7 @@ public:
SubstitutablePathInfos & infos) override;
void addToStore(const ValidPathInfo & info, Source & source,
- RepairFlag repair, CheckSigsFlag checkSigs,
- std::shared_ptr<FSAccessor> accessor) override;
+ RepairFlag repair, CheckSigsFlag checkSigs) override;
StorePath addToStore(const string & name, const Path & srcPath,
FileIngestionMethod method, HashType hashAlgo,
diff --git a/src/libstore/local.mk b/src/libstore/local.mk
index aec4ed493..d266c8efe 100644
--- a/src/libstore/local.mk
+++ b/src/libstore/local.mk
@@ -61,3 +61,6 @@ $(d)/build.cc:
clean-files += $(d)/schema.sql.gen.hh
$(eval $(call install-file-in, $(d)/nix-store.pc, $(prefix)/lib/pkgconfig, 0644))
+
+$(foreach i, $(wildcard src/libstore/builtins/*.hh), \
+ $(eval $(call install-file-in, $(i), $(includedir)/nix/builtins, 0644)))
diff --git a/src/libstore/nar-accessor.cc b/src/libstore/nar-accessor.cc
index ca663d837..d884a131e 100644
--- a/src/libstore/nar-accessor.cc
+++ b/src/libstore/nar-accessor.cc
@@ -18,7 +18,7 @@ struct NarMember
/* If this is a regular file, position of the contents of this
file in the NAR. */
- size_t start = 0, size = 0;
+ uint64_t start = 0, size = 0;
std::string target;
@@ -34,17 +34,19 @@ struct NarAccessor : public FSAccessor
NarMember root;
- struct NarIndexer : ParseSink, StringSource
+ struct NarIndexer : ParseSink, Source
{
NarAccessor & acc;
+ Source & source;
std::stack<NarMember *> parents;
- std::string currentStart;
bool isExec = false;
- NarIndexer(NarAccessor & acc, const std::string & nar)
- : StringSource(nar), acc(acc)
+ uint64_t pos = 0;
+
+ NarIndexer(NarAccessor & acc, Source & source)
+ : acc(acc), source(source)
{ }
void createMember(const Path & path, NarMember member) {
@@ -79,31 +81,38 @@ struct NarAccessor : public FSAccessor
void preallocateContents(unsigned long long size) override
{
- currentStart = string(s, pos, 16);
- assert(size <= std::numeric_limits<size_t>::max());
- parents.top()->size = (size_t)size;
+ assert(size <= std::numeric_limits<uint64_t>::max());
+ parents.top()->size = (uint64_t) size;
parents.top()->start = pos;
}
void receiveContents(unsigned char * data, unsigned int len) override
- {
- // Sanity check
- if (!currentStart.empty()) {
- assert(len < 16 || currentStart == string((char *) data, 16));
- currentStart.clear();
- }
- }
+ { }
void createSymlink(const Path & path, const string & target) override
{
createMember(path,
NarMember{FSAccessor::Type::tSymlink, false, 0, 0, target});
}
+
+ size_t read(unsigned char * data, size_t len) override
+ {
+ auto n = source.read(data, len);
+ pos += n;
+ return n;
+ }
};
NarAccessor(ref<const std::string> nar) : nar(nar)
{
- NarIndexer indexer(*this, *nar);
+ StringSource source(*nar);
+ NarIndexer indexer(*this, source);
+ parseDump(indexer, indexer);
+ }
+
+ NarAccessor(Source & source)
+ {
+ NarIndexer indexer(*this, source);
parseDump(indexer, indexer);
}
@@ -219,6 +228,11 @@ ref<FSAccessor> makeNarAccessor(ref<const std::string> nar)
return make_ref<NarAccessor>(nar);
}
+ref<FSAccessor> makeNarAccessor(Source & source)
+{
+ return make_ref<NarAccessor>(source);
+}
+
ref<FSAccessor> makeLazyNarAccessor(const std::string & listing,
GetNarBytes getNarBytes)
{
diff --git a/src/libstore/nar-accessor.hh b/src/libstore/nar-accessor.hh
index 2871199de..8af1272f6 100644
--- a/src/libstore/nar-accessor.hh
+++ b/src/libstore/nar-accessor.hh
@@ -6,10 +6,14 @@
namespace nix {
+struct Source;
+
/* Return an object that provides access to the contents of a NAR
file. */
ref<FSAccessor> makeNarAccessor(ref<const std::string> nar);
+ref<FSAccessor> makeNarAccessor(Source & source);
+
/* Create a NAR accessor from a NAR listing (in the format produced by
listNar()). The callback getNarBytes(offset, length) is used by the
readFile() method of the accessor to get the contents of files
diff --git a/src/libstore/path.cc b/src/libstore/path.cc
index b3d8ce95c..dc9dc3897 100644
--- a/src/libstore/path.cc
+++ b/src/libstore/path.cc
@@ -2,8 +2,6 @@
namespace nix {
-MakeError(BadStorePath, Error);
-
static void checkName(std::string_view path, std::string_view name)
{
if (name.empty())
diff --git a/src/libstore/profiles.cc b/src/libstore/profiles.cc
index 6cfe393a4..6862b42f0 100644
--- a/src/libstore/profiles.cc
+++ b/src/libstore/profiles.cc
@@ -12,30 +12,24 @@
namespace nix {
-static bool cmpGensByNumber(const Generation & a, const Generation & b)
-{
- return a.number < b.number;
-}
-
-
/* Parse a generation name of the format
`<profilename>-<number>-link'. */
-static int parseName(const string & profileName, const string & name)
+static std::optional<GenerationNumber> parseName(const string & profileName, const string & name)
{
- if (string(name, 0, profileName.size() + 1) != profileName + "-") return -1;
+ if (string(name, 0, profileName.size() + 1) != profileName + "-") return {};
string s = string(name, profileName.size() + 1);
string::size_type p = s.find("-link");
- if (p == string::npos) return -1;
- int n;
+ if (p == string::npos) return {};
+ unsigned int n;
if (string2Int(string(s, 0, p), n) && n >= 0)
return n;
else
- return -1;
+ return {};
}
-Generations findGenerations(Path profile, int & curGen)
+std::pair<Generations, std::optional<GenerationNumber>> findGenerations(Path profile)
{
Generations gens;
@@ -43,30 +37,34 @@ Generations findGenerations(Path profile, int & curGen)
auto profileName = std::string(baseNameOf(profile));
for (auto & i : readDirectory(profileDir)) {
- int n;
- if ((n = parseName(profileName, i.name)) != -1) {
- Generation gen;
- gen.path = profileDir + "/" + i.name;
- gen.number = n;
+ if (auto n = parseName(profileName, i.name)) {
+ auto path = profileDir + "/" + i.name;
struct stat st;
- if (lstat(gen.path.c_str(), &st) != 0)
- throw SysError("statting '%1%'", gen.path);
- gen.creationTime = st.st_mtime;
- gens.push_back(gen);
+ if (lstat(path.c_str(), &st) != 0)
+ throw SysError("statting '%1%'", path);
+ gens.push_back({
+ .number = *n,
+ .path = path,
+ .creationTime = st.st_mtime
+ });
}
}
- gens.sort(cmpGensByNumber);
+ gens.sort([](const Generation & a, const Generation & b)
+ {
+ return a.number < b.number;
+ });
- curGen = pathExists(profile)
+ return {
+ gens,
+ pathExists(profile)
? parseName(profileName, readLink(profile))
- : -1;
-
- return gens;
+ : std::nullopt
+ };
}
-static void makeName(const Path & profile, unsigned int num,
+static void makeName(const Path & profile, GenerationNumber num,
Path & outLink)
{
Path prefix = (format("%1%-%2%") % profile % num).str();
@@ -78,10 +76,9 @@ Path createGeneration(ref<LocalFSStore> store, Path profile, Path outPath)
{
/* The new generation number should be higher than old the
previous ones. */
- int dummy;
- Generations gens = findGenerations(profile, dummy);
+ auto [gens, dummy] = findGenerations(profile);
- unsigned int num;
+ GenerationNumber num;
if (gens.size() > 0) {
Generation last = gens.back();
@@ -121,7 +118,7 @@ static void removeFile(const Path & path)
}
-void deleteGeneration(const Path & profile, unsigned int gen)
+void deleteGeneration(const Path & profile, GenerationNumber gen)
{
Path generation;
makeName(profile, gen, generation);
@@ -129,7 +126,7 @@ void deleteGeneration(const Path & profile, unsigned int gen)
}
-static void deleteGeneration2(const Path & profile, unsigned int gen, bool dryRun)
+static void deleteGeneration2(const Path & profile, GenerationNumber gen, bool dryRun)
{
if (dryRun)
printInfo(format("would remove generation %1%") % gen);
@@ -140,31 +137,29 @@ static void deleteGeneration2(const Path & profile, unsigned int gen, bool dryRu
}
-void deleteGenerations(const Path & profile, const std::set<unsigned int> & gensToDelete, bool dryRun)
+void deleteGenerations(const Path & profile, const std::set<GenerationNumber> & gensToDelete, bool dryRun)
{
PathLocks lock;
lockProfile(lock, profile);
- int curGen;
- Generations gens = findGenerations(profile, curGen);
+ auto [gens, curGen] = findGenerations(profile);
- if (gensToDelete.find(curGen) != gensToDelete.end())
+ if (gensToDelete.count(*curGen))
throw Error("cannot delete current generation of profile %1%'", profile);
for (auto & i : gens) {
- if (gensToDelete.find(i.number) == gensToDelete.end()) continue;
+ if (!gensToDelete.count(i.number)) continue;
deleteGeneration2(profile, i.number, dryRun);
}
}
-void deleteGenerationsGreaterThan(const Path & profile, int max, bool dryRun)
+void deleteGenerationsGreaterThan(const Path & profile, GenerationNumber max, bool dryRun)
{
PathLocks lock;
lockProfile(lock, profile);
- int curGen;
bool fromCurGen = false;
- Generations gens = findGenerations(profile, curGen);
+ auto [gens, curGen] = findGenerations(profile);
for (auto i = gens.rbegin(); i != gens.rend(); ++i) {
if (i->number == curGen) {
fromCurGen = true;
@@ -186,8 +181,7 @@ void deleteOldGenerations(const Path & profile, bool dryRun)
PathLocks lock;
lockProfile(lock, profile);
- int curGen;
- Generations gens = findGenerations(profile, curGen);
+ auto [gens, curGen] = findGenerations(profile);
for (auto & i : gens)
if (i.number != curGen)
@@ -200,8 +194,7 @@ void deleteGenerationsOlderThan(const Path & profile, time_t t, bool dryRun)
PathLocks lock;
lockProfile(lock, profile);
- int curGen;
- Generations gens = findGenerations(profile, curGen);
+ auto [gens, curGen] = findGenerations(profile);
bool canDelete = false;
for (auto i = gens.rbegin(); i != gens.rend(); ++i)
diff --git a/src/libstore/profiles.hh b/src/libstore/profiles.hh
index 78645d8b6..abe507f0e 100644
--- a/src/libstore/profiles.hh
+++ b/src/libstore/profiles.hh
@@ -9,37 +9,32 @@
namespace nix {
+typedef unsigned int GenerationNumber;
+
struct Generation
{
- int number;
+ GenerationNumber number;
Path path;
time_t creationTime;
- Generation()
- {
- number = -1;
- }
- operator bool() const
- {
- return number != -1;
- }
};
-typedef list<Generation> Generations;
+typedef std::list<Generation> Generations;
/* Returns the list of currently present generations for the specified
- profile, sorted by generation number. */
-Generations findGenerations(Path profile, int & curGen);
+ profile, sorted by generation number. Also returns the number of
+ the current generation. */
+std::pair<Generations, std::optional<GenerationNumber>> findGenerations(Path profile);
class LocalFSStore;
Path createGeneration(ref<LocalFSStore> store, Path profile, Path outPath);
-void deleteGeneration(const Path & profile, unsigned int gen);
+void deleteGeneration(const Path & profile, GenerationNumber gen);
-void deleteGenerations(const Path & profile, const std::set<unsigned int> & gensToDelete, bool dryRun);
+void deleteGenerations(const Path & profile, const std::set<GenerationNumber> & gensToDelete, bool dryRun);
-void deleteGenerationsGreaterThan(const Path & profile, const int max, bool dryRun);
+void deleteGenerationsGreaterThan(const Path & profile, GenerationNumber max, bool dryRun);
void deleteOldGenerations(const Path & profile, bool dryRun);
diff --git a/src/libstore/remote-fs-accessor.cc b/src/libstore/remote-fs-accessor.cc
index bd698d781..2d02a181b 100644
--- a/src/libstore/remote-fs-accessor.cc
+++ b/src/libstore/remote-fs-accessor.cc
@@ -16,26 +16,26 @@ RemoteFSAccessor::RemoteFSAccessor(ref<Store> store, const Path & cacheDir)
createDirs(cacheDir);
}
-Path RemoteFSAccessor::makeCacheFile(const Path & storePath, const std::string & ext)
+Path RemoteFSAccessor::makeCacheFile(std::string_view hashPart, const std::string & ext)
{
assert(cacheDir != "");
- return fmt("%s/%s.%s", cacheDir, store->parseStorePath(storePath).hashPart(), ext);
+ return fmt("%s/%s.%s", cacheDir, hashPart, ext);
}
-void RemoteFSAccessor::addToCache(const Path & storePath, const std::string & nar,
+void RemoteFSAccessor::addToCache(std::string_view hashPart, const std::string & nar,
ref<FSAccessor> narAccessor)
{
- nars.emplace(storePath, narAccessor);
+ nars.emplace(hashPart, narAccessor);
if (cacheDir != "") {
try {
std::ostringstream str;
JSONPlaceholder jsonRoot(str);
listNar(jsonRoot, narAccessor, "", true);
- writeFile(makeCacheFile(storePath, "ls"), str.str());
+ writeFile(makeCacheFile(hashPart, "ls"), str.str());
/* FIXME: do this asynchronously. */
- writeFile(makeCacheFile(storePath, "nar"), nar);
+ writeFile(makeCacheFile(hashPart, "nar"), nar);
} catch (...) {
ignoreException();
@@ -47,23 +47,22 @@ std::pair<ref<FSAccessor>, Path> RemoteFSAccessor::fetch(const Path & path_)
{
auto path = canonPath(path_);
- auto storePath = store->toStorePath(path);
- std::string restPath = std::string(path, storePath.size());
+ auto [storePath, restPath] = store->toStorePath(path);
- if (!store->isValidPath(store->parseStorePath(storePath)))
- throw InvalidPath("path '%1%' is not a valid store path", storePath);
+ if (!store->isValidPath(storePath))
+ throw InvalidPath("path '%1%' is not a valid store path", store->printStorePath(storePath));
- auto i = nars.find(storePath);
+ auto i = nars.find(std::string(storePath.hashPart()));
if (i != nars.end()) return {i->second, restPath};
StringSink sink;
std::string listing;
Path cacheFile;
- if (cacheDir != "" && pathExists(cacheFile = makeCacheFile(storePath, "nar"))) {
+ if (cacheDir != "" && pathExists(cacheFile = makeCacheFile(storePath.hashPart(), "nar"))) {
try {
- listing = nix::readFile(makeCacheFile(storePath, "ls"));
+ listing = nix::readFile(makeCacheFile(storePath.hashPart(), "ls"));
auto narAccessor = makeLazyNarAccessor(listing,
[cacheFile](uint64_t offset, uint64_t length) {
@@ -81,7 +80,7 @@ std::pair<ref<FSAccessor>, Path> RemoteFSAccessor::fetch(const Path & path_)
return buf;
});
- nars.emplace(storePath, narAccessor);
+ nars.emplace(storePath.hashPart(), narAccessor);
return {narAccessor, restPath};
} catch (SysError &) { }
@@ -90,15 +89,15 @@ std::pair<ref<FSAccessor>, Path> RemoteFSAccessor::fetch(const Path & path_)
*sink.s = nix::readFile(cacheFile);
auto narAccessor = makeNarAccessor(sink.s);
- nars.emplace(storePath, narAccessor);
+ nars.emplace(storePath.hashPart(), narAccessor);
return {narAccessor, restPath};
} catch (SysError &) { }
}
- store->narFromPath(store->parseStorePath(storePath), sink);
+ store->narFromPath(storePath, sink);
auto narAccessor = makeNarAccessor(sink.s);
- addToCache(storePath, *sink.s, narAccessor);
+ addToCache(storePath.hashPart(), *sink.s, narAccessor);
return {narAccessor, restPath};
}
diff --git a/src/libstore/remote-fs-accessor.hh b/src/libstore/remote-fs-accessor.hh
index 4afb3be95..347cf5764 100644
--- a/src/libstore/remote-fs-accessor.hh
+++ b/src/libstore/remote-fs-accessor.hh
@@ -10,7 +10,7 @@ class RemoteFSAccessor : public FSAccessor
{
ref<Store> store;
- std::map<Path, ref<FSAccessor>> nars;
+ std::map<std::string, ref<FSAccessor>> nars;
Path cacheDir;
@@ -18,9 +18,9 @@ class RemoteFSAccessor : public FSAccessor
friend class BinaryCacheStore;
- Path makeCacheFile(const Path & storePath, const std::string & ext);
+ Path makeCacheFile(std::string_view hashPart, const std::string & ext);
- void addToCache(const Path & storePath, const std::string & nar,
+ void addToCache(std::string_view hashPart, const std::string & nar,
ref<FSAccessor> narAccessor);
public:
diff --git a/src/libstore/remote-store.cc b/src/libstore/remote-store.cc
index 118aadf7e..9af4364b7 100644
--- a/src/libstore/remote-store.cc
+++ b/src/libstore/remote-store.cc
@@ -466,7 +466,7 @@ std::optional<StorePath> RemoteStore::queryPathFromHashPart(const std::string &
void RemoteStore::addToStore(const ValidPathInfo & info, Source & source,
- RepairFlag repair, CheckSigsFlag checkSigs, std::shared_ptr<FSAccessor> accessor)
+ RepairFlag repair, CheckSigsFlag checkSigs)
{
auto conn(getConnection());
diff --git a/src/libstore/remote-store.hh b/src/libstore/remote-store.hh
index fb2052752..3c1b78b6a 100644
--- a/src/libstore/remote-store.hh
+++ b/src/libstore/remote-store.hh
@@ -60,8 +60,7 @@ public:
SubstitutablePathInfos & infos) override;
void addToStore(const ValidPathInfo & info, Source & nar,
- RepairFlag repair, CheckSigsFlag checkSigs,
- std::shared_ptr<FSAccessor> accessor) override;
+ RepairFlag repair, CheckSigsFlag checkSigs) override;
StorePath addToStore(const string & name, const Path & srcPath,
FileIngestionMethod method = FileIngestionMethod::Recursive, HashType hashAlgo = htSHA256,
diff --git a/src/libstore/s3-binary-cache-store.cc b/src/libstore/s3-binary-cache-store.cc
index 427dd48ce..1b7dff085 100644
--- a/src/libstore/s3-binary-cache-store.cc
+++ b/src/libstore/s3-binary-cache-store.cc
@@ -7,7 +7,6 @@
#include "globals.hh"
#include "compression.hh"
#include "filetransfer.hh"
-#include "istringstream_nocopy.hh"
#include <aws/core/Aws.h>
#include <aws/core/VersionConfig.h>
@@ -262,12 +261,11 @@ struct S3BinaryCacheStoreImpl : public S3BinaryCacheStore
std::shared_ptr<TransferManager> transferManager;
std::once_flag transferManagerCreated;
- void uploadFile(const std::string & path, const std::string & data,
+ void uploadFile(const std::string & path,
+ std::shared_ptr<std::basic_iostream<char>> istream,
const std::string & mimeType,
const std::string & contentEncoding)
{
- auto stream = std::make_shared<istringstream_nocopy>(data);
-
auto maxThreads = std::thread::hardware_concurrency();
static std::shared_ptr<Aws::Utils::Threading::PooledThreadExecutor>
@@ -307,7 +305,7 @@ struct S3BinaryCacheStoreImpl : public S3BinaryCacheStore
std::shared_ptr<TransferHandle> transferHandle =
transferManager->UploadFile(
- stream, bucketName, path, mimeType,
+ istream, bucketName, path, mimeType,
Aws::Map<Aws::String, Aws::String>(),
nullptr /*, contentEncoding */);
@@ -333,9 +331,7 @@ struct S3BinaryCacheStoreImpl : public S3BinaryCacheStore
if (contentEncoding != "")
request.SetContentEncoding(contentEncoding);
- auto stream = std::make_shared<istringstream_nocopy>(data);
-
- request.SetBody(stream);
+ request.SetBody(istream);
auto result = checkAws(fmt("AWS error uploading '%s'", path),
s3Helper.client->PutObject(request));
@@ -347,25 +343,34 @@ struct S3BinaryCacheStoreImpl : public S3BinaryCacheStore
std::chrono::duration_cast<std::chrono::milliseconds>(now2 - now1)
.count();
- printInfo(format("uploaded 's3://%1%/%2%' (%3% bytes) in %4% ms") %
- bucketName % path % data.size() % duration);
+ auto size = istream->tellg();
+
+ printInfo("uploaded 's3://%s/%s' (%d bytes) in %d ms",
+ bucketName, path, size, duration);
stats.putTimeMs += duration;
- stats.putBytes += data.size();
+ stats.putBytes += size;
stats.put++;
}
- void upsertFile(const std::string & path, const std::string & data,
+ void upsertFile(const std::string & path,
+ std::shared_ptr<std::basic_iostream<char>> istream,
const std::string & mimeType) override
{
+ auto compress = [&](std::string compression)
+ {
+ auto compressed = nix::compress(compression, StreamToSourceAdapter(istream).drain());
+ return std::make_shared<std::stringstream>(std::move(*compressed));
+ };
+
if (narinfoCompression != "" && hasSuffix(path, ".narinfo"))
- uploadFile(path, *compress(narinfoCompression, data), mimeType, narinfoCompression);
+ uploadFile(path, compress(narinfoCompression), mimeType, narinfoCompression);
else if (lsCompression != "" && hasSuffix(path, ".ls"))
- uploadFile(path, *compress(lsCompression, data), mimeType, lsCompression);
+ uploadFile(path, compress(lsCompression), mimeType, lsCompression);
else if (logCompression != "" && hasPrefix(path, "log/"))
- uploadFile(path, *compress(logCompression, data), mimeType, logCompression);
+ uploadFile(path, compress(logCompression), mimeType, logCompression);
else
- uploadFile(path, data, mimeType, "");
+ uploadFile(path, istream, mimeType, "");
}
void getFile(const std::string & path, Sink & sink) override
@@ -410,7 +415,7 @@ struct S3BinaryCacheStoreImpl : public S3BinaryCacheStore
for (auto object : contents) {
auto & key = object.GetKey();
if (key.size() != 40 || !hasSuffix(key, ".narinfo")) continue;
- paths.insert(parseStorePath(storeDir + "/" + key.substr(0, key.size() - 8) + "-unknown"));
+ paths.insert(parseStorePath(storeDir + "/" + key.substr(0, key.size() - 8) + "-" + MissingName));
}
marker = res.GetNextMarker();
diff --git a/src/libstore/sqlite.cc b/src/libstore/sqlite.cc
index 76c822c4e..31a1f0cac 100644
--- a/src/libstore/sqlite.cc
+++ b/src/libstore/sqlite.cc
@@ -61,6 +61,11 @@ void SQLite::exec(const std::string & stmt)
});
}
+uint64_t SQLite::getLastInsertedRowId()
+{
+ return sqlite3_last_insert_rowid(db);
+}
+
void SQLiteStmt::create(sqlite3 * db, const string & sql)
{
checkInterrupt();
@@ -95,10 +100,10 @@ SQLiteStmt::Use::~Use()
sqlite3_reset(stmt);
}
-SQLiteStmt::Use & SQLiteStmt::Use::operator () (const std::string & value, bool notNull)
+SQLiteStmt::Use & SQLiteStmt::Use::operator () (std::string_view value, bool notNull)
{
if (notNull) {
- if (sqlite3_bind_text(stmt, curArg++, value.c_str(), -1, SQLITE_TRANSIENT) != SQLITE_OK)
+ if (sqlite3_bind_text(stmt, curArg++, value.data(), -1, SQLITE_TRANSIENT) != SQLITE_OK)
throwSQLiteError(stmt.db, "binding argument");
} else
bind();
diff --git a/src/libstore/sqlite.hh b/src/libstore/sqlite.hh
index dd81ab051..99f0d56ce 100644
--- a/src/libstore/sqlite.hh
+++ b/src/libstore/sqlite.hh
@@ -26,6 +26,8 @@ struct SQLite
void isCache();
void exec(const std::string & stmt);
+
+ uint64_t getLastInsertedRowId();
};
/* RAII wrapper to create and destroy SQLite prepared statements. */
@@ -54,7 +56,7 @@ struct SQLiteStmt
~Use();
/* Bind the next parameter. */
- Use & operator () (const std::string & value, bool notNull = true);
+ Use & operator () (std::string_view value, bool notNull = true);
Use & operator () (const unsigned char * data, size_t len, bool notNull = true);
Use & operator () (int64_t value, bool notNull = true);
Use & bind(); // null
diff --git a/src/libstore/store-api.cc b/src/libstore/store-api.cc
index 62514d3be..411dcc521 100644
--- a/src/libstore/store-api.cc
+++ b/src/libstore/store-api.cc
@@ -21,15 +21,15 @@ bool Store::isInStore(const Path & path) const
}
-Path Store::toStorePath(const Path & path) const
+std::pair<StorePath, Path> Store::toStorePath(const Path & path) const
{
if (!isInStore(path))
throw Error("path '%1%' is not in the Nix store", path);
Path::size_type slash = path.find('/', storeDir.size() + 1);
if (slash == Path::npos)
- return path;
+ return {parseStorePath(path), ""};
else
- return Path(path, 0, slash);
+ return {parseStorePath(std::string_view(path).substr(0, slash)), path.substr(slash)};
}
@@ -42,14 +42,14 @@ Path Store::followLinksToStore(std::string_view _path) const
path = absPath(target, dirOf(path));
}
if (!isInStore(path))
- throw NotInStore("path '%1%' is not in the Nix store", path);
+ throw BadStorePath("path '%1%' is not in the Nix store", path);
return path;
}
StorePath Store::followLinksToStorePath(std::string_view path) const
{
- return parseStorePath(toStorePath(followLinksToStore(path)));
+ return toStorePath(followLinksToStore(path)).first;
}
@@ -351,6 +351,14 @@ ref<const ValidPathInfo> Store::queryPathInfo(const StorePath & storePath)
}
+static bool goodStorePath(const StorePath & expected, const StorePath & actual)
+{
+ return
+ expected.hashPart() == actual.hashPart()
+ && (expected.name() == Store::MissingName || expected.name() == actual.name());
+}
+
+
void Store::queryPathInfo(const StorePath & storePath,
Callback<ref<const ValidPathInfo>> callback) noexcept
{
@@ -378,7 +386,7 @@ void Store::queryPathInfo(const StorePath & storePath,
state_->pathInfoCache.upsert(hashPart,
res.first == NarInfoDiskCache::oInvalid ? PathInfoCacheValue{} : PathInfoCacheValue{ .value = res.second });
if (res.first == NarInfoDiskCache::oInvalid ||
- res.second->path != storePath)
+ !goodStorePath(storePath, res.second->path))
throw InvalidPath("path '%s' is not valid", printStorePath(storePath));
}
return callback(ref<const ValidPathInfo>(res.second));
@@ -390,7 +398,7 @@ void Store::queryPathInfo(const StorePath & storePath,
auto callbackPtr = std::make_shared<decltype(callback)>(std::move(callback));
queryPathInfoUncached(storePath,
- {[this, storePath{printStorePath(storePath)}, hashPart, callbackPtr](std::future<std::shared_ptr<const ValidPathInfo>> fut) {
+ {[this, storePathS{printStorePath(storePath)}, hashPart, callbackPtr](std::future<std::shared_ptr<const ValidPathInfo>> fut) {
try {
auto info = fut.get();
@@ -403,9 +411,11 @@ void Store::queryPathInfo(const StorePath & storePath,
state_->pathInfoCache.upsert(hashPart, PathInfoCacheValue { .value = info });
}
- if (!info || info->path != parseStorePath(storePath)) {
+ auto storePath = parseStorePath(storePathS);
+
+ if (!info || !goodStorePath(storePath, info->path)) {
stats.narInfoMissing++;
- throw InvalidPath("path '%s' is not valid", storePath);
+ throw InvalidPath("path '%s' is not valid", storePathS);
}
(*callbackPtr)(ref<const ValidPathInfo>(info));
@@ -550,7 +560,7 @@ void Store::pathInfoToJSON(JSONPlaceholder & jsonOut, const StorePathSet & store
if (!narInfo->url.empty())
jsonPath.attr("url", narInfo->url);
if (narInfo->fileHash)
- jsonPath.attr("downloadHash", narInfo->fileHash.to_string(Base32, true));
+ jsonPath.attr("downloadHash", narInfo->fileHash.to_string(hashBase, true));
if (narInfo->fileSize)
jsonPath.attr("downloadSize", narInfo->fileSize);
if (showClosureSize)
diff --git a/src/libstore/store-api.hh b/src/libstore/store-api.hh
index b1dd1f478..a4be0411e 100644
--- a/src/libstore/store-api.hh
+++ b/src/libstore/store-api.hh
@@ -31,7 +31,7 @@ MakeError(InvalidPath, Error);
MakeError(Unsupported, Error);
MakeError(SubstituteGone, Error);
MakeError(SubstituterDisabled, Error);
-MakeError(NotInStore, Error);
+MakeError(BadStorePath, Error);
class FSAccessor;
@@ -317,9 +317,9 @@ public:
the Nix store. */
bool isStorePath(std::string_view path) const;
- /* Chop off the parts after the top-level store name, e.g.,
- /nix/store/abcd-foo/bar => /nix/store/abcd-foo. */
- Path toStorePath(const Path & path) const;
+ /* Split a path like /nix/store/<hash>-<name>/<bla> into
+ /nix/store/<hash>-<name> and /<bla>. */
+ std::pair<StorePath, Path> toStorePath(const Path & path) const;
/* Follow symlinks until we end up with a path in the Nix store. */
Path followLinksToStore(std::string_view path) const;
@@ -384,13 +384,16 @@ public:
SubstituteFlag maybeSubstitute = NoSubstitute);
/* Query the set of all valid paths. Note that for some store
- backends, the name part of store paths may be omitted
- (i.e. you'll get /nix/store/<hash> rather than
+ backends, the name part of store paths may be replaced by 'x'
+ (i.e. you'll get /nix/store/<hash>-x rather than
/nix/store/<hash>-<name>). Use queryPathInfo() to obtain the
- full store path. */
+ full store path. FIXME: should return a set of
+ std::variant<StorePath, HashPart> to get rid of this hack. */
virtual StorePathSet queryAllValidPaths()
{ unsupported("queryAllValidPaths"); }
+ constexpr static const char * MissingName = "x";
+
/* Query information about a valid path. It is permitted to omit
the name part of the store path. */
ref<const ValidPathInfo> queryPathInfo(const StorePath & path);
@@ -439,8 +442,7 @@ public:
/* Import a path into the store. */
virtual void addToStore(const ValidPathInfo & info, Source & narSource,
- RepairFlag repair = NoRepair, CheckSigsFlag checkSigs = CheckSigs,
- std::shared_ptr<FSAccessor> accessor = 0) = 0;
+ RepairFlag repair = NoRepair, CheckSigsFlag checkSigs = CheckSigs) = 0;
/* Copy the contents of a path to the store and register the
validity the resulting path. The resulting path is returned.
@@ -623,8 +625,7 @@ public:
the Nix store. Optionally, the contents of the NARs are
preloaded into the specified FS accessor to speed up subsequent
access. */
- StorePaths importPaths(Source & source, std::shared_ptr<FSAccessor> accessor,
- CheckSigsFlag checkSigs = CheckSigs);
+ StorePaths importPaths(Source & source, CheckSigsFlag checkSigs = CheckSigs);
struct Stats
{