aboutsummaryrefslogtreecommitdiff
path: root/src/libstore/store-api.cc
diff options
context:
space:
mode:
authorJohn Ericson <John.Ericson@Obsidian.Systems>2023-01-06 10:35:20 -0500
committerJohn Ericson <John.Ericson@Obsidian.Systems>2023-01-06 10:35:20 -0500
commite9fc1e4fdb0ab5adb6b163c3db361b86a4f5c69b (patch)
tree25522f96d7aa54f7c93ba3c5e187374d3a50dfe6 /src/libstore/store-api.cc
parent55caef36ed1cee2e924c82cf49b3ceb17bdde910 (diff)
parent3172c51baff5c81362fcdafa2e28773c2949c660 (diff)
Merge remote-tracking branch 'upstream/master' into path-info
Diffstat (limited to 'src/libstore/store-api.cc')
-rw-r--r--src/libstore/store-api.cc302
1 files changed, 177 insertions, 125 deletions
diff --git a/src/libstore/store-api.cc b/src/libstore/store-api.cc
index 4362177f9..592afebd8 100644
--- a/src/libstore/store-api.cc
+++ b/src/libstore/store-api.cc
@@ -6,33 +6,35 @@
#include "util.hh"
#include "nar-info-disk-cache.hh"
#include "thread-pool.hh"
-#include "json.hh"
#include "url.hh"
#include "references.hh"
#include "archive.hh"
#include "callback.hh"
#include "remote-store.hh"
+#include <nlohmann/json.hpp>
#include <regex>
+using json = nlohmann::json;
+
namespace nix {
-bool Store::isInStore(const Path & path) const
+bool Store::isInStore(PathView path) const
{
return isInDir(path, storeDir);
}
-std::pair<StorePath, Path> Store::toStorePath(const Path & path) const
+std::pair<StorePath, Path> Store::toStorePath(PathView 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);
+ auto slash = path.find('/', storeDir.size() + 1);
if (slash == Path::npos)
return {parseStorePath(path), ""};
else
- return {parseStorePath(std::string_view(path).substr(0, slash)), path.substr(slash)};
+ return {parseStorePath(path.substr(0, slash)), (Path) path.substr(slash)};
}
@@ -267,6 +269,84 @@ StorePath Store::addToStore(
return addToStoreFromDump(*source, name, method, hashAlgo, repair, references);
}
+void Store::addMultipleToStore(
+ PathsSource & pathsToCopy,
+ Activity & act,
+ RepairFlag repair,
+ CheckSigsFlag checkSigs)
+{
+ std::atomic<size_t> nrDone{0};
+ std::atomic<size_t> nrFailed{0};
+ std::atomic<uint64_t> bytesExpected{0};
+ std::atomic<uint64_t> nrRunning{0};
+
+ using PathWithInfo = std::pair<ValidPathInfo, std::unique_ptr<Source>>;
+
+ std::map<StorePath, PathWithInfo *> infosMap;
+ StorePathSet storePathsToAdd;
+ for (auto & thingToAdd : pathsToCopy) {
+ infosMap.insert_or_assign(thingToAdd.first.path, &thingToAdd);
+ storePathsToAdd.insert(thingToAdd.first.path);
+ }
+
+ auto showProgress = [&]() {
+ act.progress(nrDone, pathsToCopy.size(), nrRunning, nrFailed);
+ };
+
+ ThreadPool pool;
+
+ processGraph<StorePath>(pool,
+ storePathsToAdd,
+
+ [&](const StorePath & path) {
+
+ auto & [info, _] = *infosMap.at(path);
+
+ if (isValidPath(info.path)) {
+ nrDone++;
+ showProgress();
+ return StorePathSet();
+ }
+
+ bytesExpected += info.narSize;
+ act.setExpected(actCopyPath, bytesExpected);
+
+ return info.references;
+ },
+
+ [&](const StorePath & path) {
+ checkInterrupt();
+
+ auto & [info_, source_] = *infosMap.at(path);
+ auto info = info_;
+ info.ultimate = false;
+
+ /* Make sure that the Source object is destroyed when
+ we're done. In particular, a SinkToSource object must
+ be destroyed to ensure that the destructors on its
+ stack frame are run; this includes
+ LegacySSHStore::narFromPath()'s connection lock. */
+ auto source = std::move(source_);
+
+ if (!isValidPath(info.path)) {
+ MaintainCount<decltype(nrRunning)> mc(nrRunning);
+ showProgress();
+ try {
+ addToStore(info, *source, repair, checkSigs);
+ } catch (Error & e) {
+ nrFailed++;
+ if (!settings.keepGoing)
+ throw e;
+ printMsg(lvlError, "could not copy %s: %s", printStorePath(path), e.what());
+ showProgress();
+ return;
+ }
+ }
+
+ nrDone++;
+ showProgress();
+ });
+}
void Store::addMultipleToStore(
Source & source,
@@ -396,6 +476,7 @@ Store::Store(const Params & params)
: StoreConfig(params)
, state({(size_t) pathInfoCacheSize})
{
+ assertLibStoreInitialized();
}
@@ -778,56 +859,53 @@ StorePathSet Store::exportReferences(const StorePathSet & storePaths, const Stor
return paths;
}
-
-void Store::pathInfoToJSON(JSONPlaceholder & jsonOut, const StorePathSet & storePaths,
+json Store::pathInfoToJSON(const StorePathSet & storePaths,
bool includeImpureInfo, bool showClosureSize,
Base hashBase,
AllowInvalidFlag allowInvalid)
{
- auto jsonList = jsonOut.list();
+ json::array_t jsonList = json::array();
for (auto & storePath : storePaths) {
- auto jsonPath = jsonList.object();
+ auto& jsonPath = jsonList.emplace_back(json::object());
try {
auto info = queryPathInfo(storePath);
- jsonPath.attr("path", printStorePath(info->path));
- jsonPath
- .attr("narHash", info->narHash.to_string(hashBase, true))
- .attr("narSize", info->narSize);
+ jsonPath["path"] = printStorePath(info->path);
+ jsonPath["narHash"] = info->narHash.to_string(hashBase, true);
+ jsonPath["narSize"] = info->narSize;
{
- auto jsonRefs = jsonPath.list("references");
+ auto& jsonRefs = (jsonPath["references"] = json::array());
for (auto & ref : info->references)
- jsonRefs.elem(printStorePath(ref));
+ jsonRefs.emplace_back(printStorePath(ref));
}
if (info->ca)
- jsonPath.attr("ca", renderContentAddress(info->ca));
+ jsonPath["ca"] = renderContentAddress(info->ca);
std::pair<uint64_t, uint64_t> closureSizes;
if (showClosureSize) {
closureSizes = getClosureSize(info->path);
- jsonPath.attr("closureSize", closureSizes.first);
+ jsonPath["closureSize"] = closureSizes.first;
}
if (includeImpureInfo) {
if (info->deriver)
- jsonPath.attr("deriver", printStorePath(*info->deriver));
+ jsonPath["deriver"] = printStorePath(*info->deriver);
if (info->registrationTime)
- jsonPath.attr("registrationTime", info->registrationTime);
+ jsonPath["registrationTime"] = info->registrationTime;
if (info->ultimate)
- jsonPath.attr("ultimate", info->ultimate);
+ jsonPath["ultimate"] = info->ultimate;
if (!info->sigs.empty()) {
- auto jsonSigs = jsonPath.list("signatures");
for (auto & sig : info->sigs)
- jsonSigs.elem(sig);
+ jsonPath["signatures"].push_back(sig);
}
auto narInfo = std::dynamic_pointer_cast<const NarInfo>(
@@ -835,21 +913,22 @@ void Store::pathInfoToJSON(JSONPlaceholder & jsonOut, const StorePathSet & store
if (narInfo) {
if (!narInfo->url.empty())
- jsonPath.attr("url", narInfo->url);
+ jsonPath["url"] = narInfo->url;
if (narInfo->fileHash)
- jsonPath.attr("downloadHash", narInfo->fileHash->to_string(hashBase, true));
+ jsonPath["downloadHash"] = narInfo->fileHash->to_string(hashBase, true);
if (narInfo->fileSize)
- jsonPath.attr("downloadSize", narInfo->fileSize);
+ jsonPath["downloadSize"] = narInfo->fileSize;
if (showClosureSize)
- jsonPath.attr("closureDownloadSize", closureSizes.second);
+ jsonPath["closureDownloadSize"] = closureSizes.second;
}
}
} catch (InvalidPath &) {
- jsonPath.attr("path", printStorePath(storePath));
- jsonPath.attr("valid", false);
+ jsonPath["path"] = printStorePath(storePath);
+ jsonPath["valid"] = false;
}
}
+ return jsonList;
}
@@ -1011,115 +1090,62 @@ std::map<StorePath, StorePath> copyPaths(
for (auto & path : storePaths)
if (!valid.count(path)) missing.insert(path);
+ Activity act(*logger, lvlInfo, actCopyPaths, fmt("copying %d paths", missing.size()));
+
+ // In the general case, `addMultipleToStore` requires a sorted list of
+ // store paths to add, so sort them right now
+ auto sortedMissing = srcStore.topoSortPaths(missing);
+ std::reverse(sortedMissing.begin(), sortedMissing.end());
+
std::map<StorePath, StorePath> pathsMap;
for (auto & path : storePaths)
pathsMap.insert_or_assign(path, path);
- Activity act(*logger, lvlInfo, actCopyPaths, fmt("copying %d paths", missing.size()));
+ Store::PathsSource pathsToCopy;
+
+ auto computeStorePathForDst = [&](const ValidPathInfo & currentPathInfo) -> StorePath {
+ auto storePathForSrc = currentPathInfo.path;
+ auto storePathForDst = storePathForSrc;
+ if (currentPathInfo.ca && currentPathInfo.references.empty()) {
+ storePathForDst = dstStore.makeFixedOutputPathFromCA(
+ currentPathInfo.fullStorePathDescriptorOpt().value());
+ if (dstStore.storeDir == srcStore.storeDir)
+ assert(storePathForDst == storePathForSrc);
+ if (storePathForDst != storePathForSrc)
+ debug("replaced path '%s' to '%s' for substituter '%s'",
+ srcStore.printStorePath(storePathForSrc),
+ dstStore.printStorePath(storePathForDst),
+ dstStore.getUri());
+ }
+ return storePathForDst;
+ };
- auto sorted = srcStore.topoSortPaths(missing);
- std::reverse(sorted.begin(), sorted.end());
+ for (auto & missingPath : sortedMissing) {
+ auto info = srcStore.queryPathInfo(missingPath);
- auto source = sinkToSource([&](Sink & sink) {
- sink << sorted.size();
- for (auto & storePath : sorted) {
+ auto storePathForDst = computeStorePathForDst(*info);
+ pathsMap.insert_or_assign(missingPath, storePathForDst);
+
+ ValidPathInfo infoForDst = *info;
+ infoForDst.path = storePathForDst;
+
+ auto source = sinkToSource([&](Sink & sink) {
+ // We can reasonably assume that the copy will happen whenever we
+ // read the path, so log something about that at that point
auto srcUri = srcStore.getUri();
auto dstUri = dstStore.getUri();
- auto storePathS = srcStore.printStorePath(storePath);
+ auto storePathS = srcStore.printStorePath(missingPath);
Activity act(*logger, lvlInfo, actCopyPath,
makeCopyPathMessage(srcUri, dstUri, storePathS),
{storePathS, srcUri, dstUri});
PushActivity pact(act.id);
- auto info = srcStore.queryPathInfo(storePath);
- info->write(sink, srcStore, 16);
- srcStore.narFromPath(storePath, sink);
- }
- });
-
- dstStore.addMultipleToStore(*source, repair, checkSigs);
-
- #if 0
- std::atomic<size_t> nrDone{0};
- std::atomic<size_t> nrFailed{0};
- std::atomic<uint64_t> bytesExpected{0};
- std::atomic<uint64_t> nrRunning{0};
-
- auto showProgress = [&]() {
- act.progress(nrDone, missing.size(), nrRunning, nrFailed);
- };
-
- ThreadPool pool;
-
- processGraph<StorePath>(pool,
- StorePathSet(missing.begin(), missing.end()),
-
- [&](const StorePath & storePath) {
- auto info = srcStore.queryPathInfo(storePath);
- auto storePathForDst = storePath;
- if (info->ca && info->references.empty()) {
- storePathForDst = dstStore.makeFixedOutputPathFromCA(
- info->fullStorePathDescriptorOpt().value());
- if (dstStore.storeDir == srcStore.storeDir)
- assert(storePathForDst == storePath);
- if (storePathForDst != storePath)
- debug("replaced path '%s' to '%s' for substituter '%s'",
- srcStore.printStorePath(storePath),
- dstStore.printStorePath(storePathForDst),
- dstStore.getUri());
- }
- pathsMap.insert_or_assign(storePath, storePathForDst);
-
- if (dstStore.isValidPath(storePath)) {
- nrDone++;
- showProgress();
- return StorePathSet();
- }
-
- bytesExpected += info->narSize;
- act.setExpected(actCopyPath, bytesExpected);
-
- return info->references;
- },
-
- [&](const StorePath & storePath) {
- checkInterrupt();
-
- auto info = srcStore.queryPathInfo(storePath);
-
- auto storePathForDst = storePath;
- if (info->ca && info->references.empty()) {
- storePathForDst = dstStore.makeFixedOutputPathFromCA(
- info->fullStorePathDescriptorOpt().value());
- if (dstStore->storeDir == srcStore->storeDir)
- assert(storePathForDst == storePath);
- if (storePathForDst != storePath)
- debug("replaced path '%s' to '%s' for substituter '%s'",
- srcStore.printStorePath(storePath),
- dstStore.printStorePath(storePathForDst),
- dstStore.getUri());
- }
- pathsMap.insert_or_assign(storePath, storePathForDst);
-
- if (!dstStore.isValidPath(storePathForDst)) {
- MaintainCount<decltype(nrRunning)> mc(nrRunning);
- showProgress();
- try {
- copyStorePath(srcStore, dstStore, storePath, repair, checkSigs);
- } catch (Error &e) {
- nrFailed++;
- if (!settings.keepGoing)
- throw e;
- printMsg(lvlError, "could not copy %s: %s", dstStore.printStorePath(storePath), e.what());
- showProgress();
- return;
- }
- }
-
- nrDone++;
- showProgress();
+ srcStore.narFromPath(missingPath, sink);
});
- #endif
+ pathsToCopy.push_back(std::pair{infoForDst, std::move(source)});
+ }
+
+ dstStore.addMultipleToStore(pathsToCopy, act, repair, checkSigs);
return pathsMap;
}
@@ -1370,7 +1396,8 @@ std::pair<std::string, Store::Params> splitUriAndParams(const std::string & uri_
return {uri, params};
}
-static bool isNonUriPath(const std::string & spec) {
+static bool isNonUriPath(const std::string & spec)
+{
return
// is not a URL
spec.find("://") == std::string::npos
@@ -1382,11 +1409,36 @@ static bool isNonUriPath(const std::string & spec) {
std::shared_ptr<Store> openFromNonUri(const std::string & uri, const Store::Params & params)
{
if (uri == "" || uri == "auto") {
- auto stateDir = get(params, "state").value_or(settings.nixStateDir);
+ auto stateDir = getOr(params, "state", settings.nixStateDir);
if (access(stateDir.c_str(), R_OK | W_OK) == 0)
return std::make_shared<LocalStore>(params);
else if (pathExists(settings.nixDaemonSocketFile))
return std::make_shared<UDSRemoteStore>(params);
+ #if __linux__
+ else if (!pathExists(stateDir)
+ && params.empty()
+ && getuid() != 0
+ && !getEnv("NIX_STORE_DIR").has_value()
+ && !getEnv("NIX_STATE_DIR").has_value())
+ {
+ /* If /nix doesn't exist, there is no daemon socket, and
+ we're not root, then automatically set up a chroot
+ store in ~/.local/share/nix/root. */
+ auto chrootStore = getDataDir() + "/nix/root";
+ if (!pathExists(chrootStore)) {
+ try {
+ createDirs(chrootStore);
+ } catch (Error & e) {
+ return std::make_shared<LocalStore>(params);
+ }
+ warn("'%s' does not exist, so Nix will use '%s' as a chroot store", stateDir, chrootStore);
+ } else
+ debug("'%s' does not exist, so Nix will use '%s' as a chroot store", stateDir, chrootStore);
+ Store::Params params2;
+ params2["root"] = chrootStore;
+ return std::make_shared<LocalStore>(params2);
+ }
+ #endif
else
return std::make_shared<LocalStore>(params);
} else if (uri == "daemon") {