diff options
Diffstat (limited to 'src/libstore')
25 files changed, 398 insertions, 147 deletions
diff --git a/src/libstore/binary-cache-store.cc b/src/libstore/binary-cache-store.cc index d1b278b8efbe..2e9a13e564ca 100644 --- a/src/libstore/binary-cache-store.cc +++ b/src/libstore/binary-cache-store.cc @@ -203,22 +203,18 @@ void BinaryCacheStore::narFromPath(const Path & storePath, Sink & sink) stats.narRead++; stats.narReadCompressedBytes += nar->size(); - /* Decompress the NAR. FIXME: would be nice to have the remote - side do this. */ - try { - nar = decompress(info->compression, *nar); - } catch (UnknownCompressionMethod &) { - throw Error(format("binary cache path '%s' uses unknown compression method '%s'") - % storePath % info->compression); - } + uint64_t narSize = 0; - stats.narReadBytes += nar->size(); + StringSource source(*nar); - printMsg(lvlTalkative, format("exporting path '%1%' (%2% bytes)") % storePath % nar->size()); + LambdaSink wrapperSink([&](const unsigned char * data, size_t len) { + sink(data, len); + narSize += len; + }); - assert(nar->size() % 8 == 0); + decompress(info->compression, source, wrapperSink); - sink((unsigned char *) nar->c_str(), nar->size()); + stats.narReadBytes += narSize; } void BinaryCacheStore::queryPathInfoUncached(const Path & storePath, diff --git a/src/libstore/build.cc b/src/libstore/build.cc index 2466f9bd6b06..6108785447a7 100644 --- a/src/libstore/build.cc +++ b/src/libstore/build.cc @@ -156,7 +156,7 @@ public: abort(); } - void trace(const format & f); + void trace(const FormatOrString & fs); string getName() { @@ -417,9 +417,9 @@ void Goal::amDone(ExitCode result) } -void Goal::trace(const format & f) +void Goal::trace(const FormatOrString & fs) { - debug(format("%1%: %2%") % name % f); + debug("%1%: %2%", name, fs.s); } @@ -652,6 +652,11 @@ HookInstance::HookInstance() if (dup2(builderOut.writeSide.get(), 4) == -1) throw SysError("dupping builder's stdout/stderr"); + /* Hack: pass the read side of that fd to allow build-remote + to read SSH error messages. */ + if (dup2(builderOut.readSide.get(), 5) == -1) + throw SysError("dupping builder's stdout/stderr"); + Strings args = { baseNameOf(settings.buildHook), std::to_string(verbosity), @@ -959,6 +964,8 @@ private: } void done(BuildResult::Status status, const string & msg = ""); + + PathSet exportReferences(PathSet storePaths); }; @@ -1189,7 +1196,7 @@ void DerivationGoal::outputsSubstituted() for (auto & i : drv->inputSrcs) { if (worker.store.isValidPath(i)) continue; if (!settings.useSubstitutes) - throw Error(format("dependency of '%1%' of '%2%' does not exist, and substitution is disabled") + throw Error(format("dependency '%1%' of '%2%' does not exist, and substitution is disabled") % i % drvPath); addWaitee(worker.makeSubstitutionGoal(i)); } @@ -1458,7 +1465,7 @@ void replaceValidPath(const Path & storePath, const Path tmpPath) tmpPath (the replacement), so we have to move it out of the way first. We'd better not be interrupted here, because if we're repairing (say) Glibc, we end up with a broken system. */ - Path oldPath = (format("%1%.old-%2%-%3%") % storePath % getpid() % rand()).str(); + Path oldPath = (format("%1%.old-%2%-%3%") % storePath % getpid() % random()).str(); if (pathExists(storePath)) rename(storePath.c_str(), oldPath.c_str()); if (rename(tmpPath.c_str(), storePath.c_str()) == -1) @@ -1725,22 +1732,23 @@ int childEntry(void * arg) } -PathSet exportReferences(Store & store, PathSet storePaths) +PathSet DerivationGoal::exportReferences(PathSet storePaths) { PathSet paths; for (auto storePath : storePaths) { /* Check that the store path is valid. */ - if (!store.isInStore(storePath)) + if (!worker.store.isInStore(storePath)) throw BuildError(format("'exportReferencesGraph' contains a non-store path '%1%'") % storePath); - storePath = store.toStorePath(storePath); - if (!store.isValidPath(storePath)) - throw BuildError(format("'exportReferencesGraph' contains an invalid path '%1%'") - % storePath); - store.computeFSClosure(storePath, paths); + storePath = worker.store.toStorePath(storePath); + + if (!inputPaths.count(storePath)) + throw BuildError("cannot export references of path '%s' because it is not in the input closure of the derivation", storePath); + + worker.store.computeFSClosure(storePath, paths); } /* If there are derivations in the graph, then include their @@ -1751,9 +1759,9 @@ PathSet exportReferences(Store & store, PathSet storePaths) for (auto & j : paths2) { if (isDerivation(j)) { - Derivation drv = store.derivationFromPath(j); + Derivation drv = worker.store.derivationFromPath(j); for (auto & k : drv.outputs) - store.computeFSClosure(k.second.path, paths); + worker.store.computeFSClosure(k.second.path, paths); } } @@ -1877,7 +1885,7 @@ void DerivationGoal::startBuilder() /* Write closure info to <fileName>. */ writeFile(tmpDir + "/" + fileName, worker.store.makeValidityRegistration( - exportReferences(worker.store, {storePath}), false, false)); + exportReferences({storePath}), false, false)); } } @@ -2379,7 +2387,7 @@ void DerivationGoal::writeStructuredAttrs() for (auto & p : *i) storePaths.insert(p.get<std::string>()); worker.store.pathInfoToJSON(jsonRoot, - exportReferences(worker.store, storePaths), false, true); + exportReferences(storePaths), false, true); } json[i.key()] = nlohmann::json::parse(str.str()); // urgh } @@ -2695,8 +2703,8 @@ void DerivationGoal::runChild() } else { if (errno != EINVAL) throw SysError("mounting /dev/pts"); - doBind("/dev/pts", "/dev/pts"); - doBind("/dev/ptmx", "/dev/ptmx"); + doBind("/dev/pts", chrootRootDir + "/dev/pts"); + doBind("/dev/ptmx", chrootRootDir + "/dev/ptmx"); } } @@ -2948,6 +2956,8 @@ void DerivationGoal::runChild() if (drv->builder == "builtin:fetchurl") builtinFetchurl(drv2, netrcData); + else if (drv->builder == "builtin:buildenv") + builtinBuildenv(drv2); else throw Error(format("unsupported builtin function '%1%'") % string(drv->builder, 8)); _exit(0); @@ -3684,7 +3694,7 @@ void SubstitutionGoal::tryNext() only after we've downloaded the path. */ if (worker.store.requireSigs && !sub->isTrusted - && !info->checkSignatures(worker.store, worker.store.publicKeys)) + && !info->checkSignatures(worker.store, worker.store.getPublicKeys())) { printError("warning: substituter '%s' does not have a valid signature for path '%s'", sub->getUri(), storePath); @@ -4158,10 +4168,10 @@ void Worker::waitForInput() assert(goal); set<int> fds2(j->fds); + std::vector<unsigned char> buffer(4096); for (auto & k : fds2) { if (FD_ISSET(k, &fds)) { - unsigned char buffer[4096]; - ssize_t rd = read(k, buffer, sizeof(buffer)); + ssize_t rd = read(k, buffer.data(), buffer.size()); if (rd == -1) { if (errno != EINTR) throw SysError(format("reading from %1%") @@ -4173,7 +4183,7 @@ void Worker::waitForInput() } else { printMsg(lvlVomit, format("%1%: read %2% bytes") % goal->getName() % rd); - string data((char *) buffer, rd); + string data((char *) buffer.data(), rd); j->lastOutput = after; goal->handleChildOutput(k, data); } diff --git a/src/libstore/builtins.hh b/src/libstore/builtins.hh index 0cc6ba31f658..0d2da873ece4 100644 --- a/src/libstore/builtins.hh +++ b/src/libstore/builtins.hh @@ -4,6 +4,8 @@ namespace nix { +// TODO: make pluggable. void builtinFetchurl(const BasicDerivation & drv, const std::string & netrcData); +void builtinBuildenv(const BasicDerivation & drv); } diff --git a/src/libstore/builtins/buildenv.cc b/src/libstore/builtins/buildenv.cc new file mode 100644 index 000000000000..74e706664694 --- /dev/null +++ b/src/libstore/builtins/buildenv.cc @@ -0,0 +1,204 @@ +#include "builtins.hh" + +#include <sys/stat.h> +#include <sys/types.h> +#include <fcntl.h> +#include <algorithm> + +namespace nix { + +typedef std::map<Path,int> Priorities; + +// FIXME: change into local variables. + +static Priorities priorities; + +static unsigned long symlinks; + +/* For each activated package, create symlinks */ +static void createLinks(const Path & srcDir, const Path & dstDir, int priority) +{ + DirEntries srcFiles; + + try { + srcFiles = readDirectory(srcDir); + } catch (SysError & e) { + if (e.errNo == ENOTDIR) { + printError("warning: not including '%s' in the user environment because it's not a directory", srcDir); + return; + } + throw; + } + + for (const auto & ent : srcFiles) { + if (ent.name[0] == '.') + /* not matched by glob */ + continue; + auto srcFile = srcDir + "/" + ent.name; + auto dstFile = dstDir + "/" + ent.name; + + struct stat srcSt; + try { + if (stat(srcFile.c_str(), &srcSt) == -1) + throw SysError("getting status of '%1%'", srcFile); + } catch (SysError & e) { + if (e.errNo == ENOENT || e.errNo == ENOTDIR) { + printError("warning: skipping dangling symlink '%s'", dstFile); + continue; + } + throw; + } + + /* The files below are special-cased to that they don't show up + * in user profiles, either because they are useless, or + * because they would cauase pointless collisions (e.g., each + * Python package brings its own + * `$out/lib/pythonX.Y/site-packages/easy-install.pth'.) + */ + if (hasSuffix(srcFile, "/propagated-build-inputs") || + hasSuffix(srcFile, "/nix-support") || + hasSuffix(srcFile, "/perllocal.pod") || + hasSuffix(srcFile, "/info/dir") || + hasSuffix(srcFile, "/log")) + continue; + + else if (S_ISDIR(srcSt.st_mode)) { + struct stat dstSt; + auto res = lstat(dstFile.c_str(), &dstSt); + if (res == 0) { + if (S_ISDIR(dstSt.st_mode)) { + createLinks(srcFile, dstFile, priority); + continue; + } else if (S_ISLNK(dstSt.st_mode)) { + auto target = canonPath(dstFile, true); + if (!S_ISDIR(lstat(target).st_mode)) + throw Error("collision between '%1%' and non-directory '%2%'", srcFile, target); + if (unlink(dstFile.c_str()) == -1) + throw SysError(format("unlinking '%1%'") % dstFile); + if (mkdir(dstFile.c_str(), 0755) == -1) + throw SysError(format("creating directory '%1%'")); + createLinks(target, dstFile, priorities[dstFile]); + createLinks(srcFile, dstFile, priority); + continue; + } + } else if (errno != ENOENT) + throw SysError(format("getting status of '%1%'") % dstFile); + } + + else { + struct stat dstSt; + auto res = lstat(dstFile.c_str(), &dstSt); + if (res == 0) { + if (S_ISLNK(dstSt.st_mode)) { + auto prevPriority = priorities[dstFile]; + if (prevPriority == priority) + throw Error( + "packages '%1%' and '%2%' have the same priority %3%; " + "use 'nix-env --set-flag priority NUMBER INSTALLED_PKGNAME' " + "to change the priority of one of the conflicting packages" + " (0 being the highest priority)", + srcFile, readLink(dstFile), priority); + if (prevPriority < priority) + continue; + if (unlink(dstFile.c_str()) == -1) + throw SysError(format("unlinking '%1%'") % dstFile); + } else if (S_ISDIR(dstSt.st_mode)) + throw Error("collision between non-directory '%1%' and directory '%2%'", srcFile, dstFile); + } else if (errno != ENOENT) + throw SysError(format("getting status of '%1%'") % dstFile); + } + + createSymlink(srcFile, dstFile); + priorities[dstFile] = priority; + symlinks++; + } +} + +typedef std::set<Path> FileProp; + +static FileProp done; +static FileProp postponed = FileProp{}; + +static Path out; + +static void addPkg(const Path & pkgDir, int priority) +{ + if (done.count(pkgDir)) return; + done.insert(pkgDir); + createLinks(pkgDir, out, priority); + + try { + for (const auto & p : tokenizeString<std::vector<string>>( + readFile(pkgDir + "/nix-support/propagated-user-env-packages"), " \n")) + if (!done.count(p)) + postponed.insert(p); + } catch (SysError & e) { + if (e.errNo != ENOENT && e.errNo != ENOTDIR) throw; + } +} + +struct Package { + Path path; + bool active; + int priority; + Package(Path path, bool active, int priority) : path{path}, active{active}, priority{priority} {} +}; + +typedef std::vector<Package> Packages; + +void builtinBuildenv(const BasicDerivation & drv) +{ + auto getAttr = [&](const string & name) { + auto i = drv.env.find(name); + if (i == drv.env.end()) throw Error("attribute '%s' missing", name); + return i->second; + }; + + out = getAttr("out"); + createDirs(out); + + /* Convert the stuff we get from the environment back into a + * coherent data type. */ + Packages pkgs; + auto derivations = tokenizeString<Strings>(getAttr("derivations")); + while (!derivations.empty()) { + /* !!! We're trusting the caller to structure derivations env var correctly */ + auto active = derivations.front(); derivations.pop_front(); + auto priority = stoi(derivations.front()); derivations.pop_front(); + auto outputs = stoi(derivations.front()); derivations.pop_front(); + for (auto n = 0; n < outputs; n++) { + auto path = derivations.front(); derivations.pop_front(); + pkgs.emplace_back(path, active != "false", priority); + } + } + + /* Symlink to the packages that have been installed explicitly by the + * user. Process in priority order to reduce unnecessary + * symlink/unlink steps. + */ + std::sort(pkgs.begin(), pkgs.end(), [](const Package & a, const Package & b) { + return a.priority < b.priority || (a.priority == b.priority && a.path < b.path); + }); + for (const auto & pkg : pkgs) + if (pkg.active) + addPkg(pkg.path, pkg.priority); + + /* Symlink to the packages that have been "propagated" by packages + * installed by the user (i.e., package X declares that it wants Y + * installed as well). We do these later because they have a lower + * priority in case of collisions. + */ + auto priorityCounter = 1000; + while (!postponed.empty()) { + auto pkgDirs = postponed; + postponed = FileProp{}; + for (const auto & pkgDir : pkgDirs) + addPkg(pkgDir, priorityCounter++); + } + + printError("created %d symlinks in user environment", symlinks); + + createSymlink(getAttr("manifest"), out + "/manifest.nix"); +} + +} diff --git a/src/libstore/builtins.cc b/src/libstore/builtins/fetchurl.cc index 4ca4a838e3c4..4ca4a838e3c4 100644 --- a/src/libstore/builtins.cc +++ b/src/libstore/builtins/fetchurl.cc diff --git a/src/libstore/download.cc b/src/libstore/download.cc index 5ab625f42288..18f9094f82e0 100644 --- a/src/libstore/download.cc +++ b/src/libstore/download.cc @@ -173,7 +173,11 @@ struct CurlDownloader : public Downloader int progressCallback(double dltotal, double dlnow) { - act.progress(dlnow, dltotal); + try { + act.progress(dlnow, dltotal); + } catch (nix::Interrupted &) { + assert(_isInterrupted); + } return _isInterrupted; } @@ -195,6 +199,7 @@ struct CurlDownloader : public Downloader if (readOffset == request.data->length()) return 0; auto count = std::min(size * nitems, request.data->length() - readOffset); + assert(count); memcpy(buffer, request.data->data() + readOffset, count); readOffset += count; return count; @@ -339,6 +344,7 @@ struct CurlDownloader : public Downloader case CURLE_BAD_FUNCTION_ARGUMENT: case CURLE_INTERFACE_FAILED: case CURLE_UNKNOWN_OPTION: + case CURLE_SSL_CACERT_BADFILE: err = Misc; break; default: // Shut up warnings @@ -630,7 +636,7 @@ Path Downloader::downloadCached(ref<Store> store, const string & url_, bool unpa if (expectedHash) { expectedStorePath = store->makeFixedOutputPath(unpack, expectedHash, name); if (store->isValidPath(expectedStorePath)) - return expectedStorePath; + return store->toRealPath(expectedStorePath); } Path cacheDir = getCacheDir() + "/nix/tarballs"; @@ -724,8 +730,13 @@ Path Downloader::downloadCached(ref<Store> store, const string & url_, bool unpa storePath = unpackedStorePath; } - if (expectedStorePath != "" && storePath != expectedStorePath) - throw nix::Error("store path mismatch in file downloaded from '%s'", url); + if (expectedStorePath != "" && storePath != expectedStorePath) { + Hash gotHash = unpack + ? hashPath(expectedHash.type, store->toRealPath(storePath)).first + : hashFile(expectedHash.type, store->toRealPath(storePath)); + throw nix::Error("hash mismatch in file downloaded from '%s': got hash '%s' instead of the expected hash '%s'", + url, gotHash.to_string(), expectedHash.to_string()); + } return store->toRealPath(storePath); } diff --git a/src/libstore/download.hh b/src/libstore/download.hh index d9d525d4e65f..0b8d29b21dfe 100644 --- a/src/libstore/download.hh +++ b/src/libstore/download.hh @@ -22,7 +22,7 @@ struct DownloadRequest std::string mimeType; DownloadRequest(const std::string & uri) - : uri(uri), parentAct(curActivity) { } + : uri(uri), parentAct(getCurActivity()) { } }; struct DownloadResult diff --git a/src/libstore/gc.cc b/src/libstore/gc.cc index 943b16c28fa3..ba49749d830a 100644 --- a/src/libstore/gc.cc +++ b/src/libstore/gc.cc @@ -59,7 +59,7 @@ static void makeSymlink(const Path & link, const Path & target) /* Create the new symlink. */ Path tempLink = (format("%1%.tmp-%2%-%3%") - % link % getpid() % rand()).str(); + % link % getpid() % random()).str(); createSymlink(target, tempLink); /* Atomically replace the old one. */ diff --git a/src/libstore/globals.cc b/src/libstore/globals.cc index f46e8326235f..544566e0b573 100644 --- a/src/libstore/globals.cc +++ b/src/libstore/globals.cc @@ -159,7 +159,7 @@ void initPlugins() void *handle = dlopen(file.c_str(), RTLD_LAZY | RTLD_LOCAL); if (!handle) - throw Error("could not dynamically open plugin file '%s%': %s%", file, dlerror()); + throw Error("could not dynamically open plugin file '%s': %s", file, dlerror()); } } /* We handle settings registrations here, since plugins can add settings */ diff --git a/src/libstore/globals.hh b/src/libstore/globals.hh index 897d53b7b94c..0ae69242a59c 100644 --- a/src/libstore/globals.hh +++ b/src/libstore/globals.hh @@ -29,7 +29,7 @@ struct CaseHackSetting : public BaseSetting<bool> void set(const std::string & str) override { BaseSetting<bool>::set(str); - nix::useCaseHack = true; + nix::useCaseHack = value; } }; @@ -320,6 +320,14 @@ public: Setting<Strings> trustedUsers{this, {"root"}, "trusted-users", "Which users or groups are trusted to ask the daemon to do unsafe things."}; + Setting<unsigned int> ttlNegativeNarInfoCache{this, 3600, "narinfo-cache-negative-ttl", + "The TTL in seconds for negative lookups in the disk cache i.e binary cache lookups that " + "return an invalid path result"}; + + Setting<unsigned int> ttlPositiveNarInfoCache{this, 30 * 24 * 3600, "narinfo-cache-positive-ttl", + "The TTL in seconds for positive lookups in the disk cache i.e binary cache lookups that " + "return a valid path result."}; + /* ?Who we trust to use the daemon in safe ways */ Setting<Strings> allowedUsers{this, {"*"}, "allowed-users", "Which users or groups are allowed to connect to the daemon."}; diff --git a/src/libstore/legacy-ssh-store.cc b/src/libstore/legacy-ssh-store.cc index dfefdb9bc874..5dee25308f7f 100644 --- a/src/libstore/legacy-ssh-store.cc +++ b/src/libstore/legacy-ssh-store.cc @@ -16,6 +16,7 @@ struct LegacySSHStore : public Store const Setting<int> maxConnections{this, 1, "max-connections", "maximum number of concurrent SSH connections"}; const Setting<Path> sshKey{this, "", "ssh-key", "path to an SSH private key"}; const Setting<bool> compress{this, false, "compress", "whether to compress the connection"}; + const Setting<Path> remoteProgram{this, "nix-store", "remote-program", "path to the nix-store executable on the remote system"}; // Hack for getting remote build log output. const Setting<int> logFD{this, -1, "log-fd", "file descriptor to which SSH's stderr is connected"}; @@ -55,7 +56,7 @@ struct LegacySSHStore : public Store ref<Connection> openConnection() { auto conn = make_ref<Connection>(); - conn->sshConn = master.startCommand("nix-store --serve --write"); + conn->sshConn = master.startCommand(fmt("%s --serve --write", remoteProgram)); conn->to = FdSink(conn->sshConn->in.get()); conn->from = FdSource(conn->sshConn->out.get()); @@ -119,7 +120,7 @@ struct LegacySSHStore : public Store }); } - void addToStore(const ValidPathInfo & info, const ref<std::string> & nar, + void addToStore(const ValidPathInfo & info, Source & source, RepairFlag repair, CheckSigsFlag checkSigs, std::shared_ptr<FSAccessor> accessor) override { @@ -130,7 +131,7 @@ struct LegacySSHStore : public Store conn->to << cmdImportPaths << 1; - conn->to(*nar); + copyNAR(source, conn->to); conn->to << exportMagic << info.path @@ -150,12 +151,7 @@ struct LegacySSHStore : public Store conn->to << cmdDumpStorePath << path; conn->to.flush(); - - /* FIXME: inefficient. */ - ParseSink parseSink; /* null sink; just parse the NAR */ - TeeSource savedNAR(conn->from); - parseDump(parseSink, savedNAR); - sink(*savedNAR.data); + copyNAR(conn->from, sink); } PathSet queryAllValidPaths() override { unsupported(); } diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc index 4afe51ea91ec..b63584f28a30 100644 --- a/src/libstore/local-store.cc +++ b/src/libstore/local-store.cc @@ -53,7 +53,6 @@ LocalStore::LocalStore(const Params & params) , trashDir(realStoreDir + "/trash") , tempRootsDir(stateDir + "/temproots") , fnTempRoots(fmt("%s/%d", tempRootsDir, getpid())) - , publicKeys(getDefaultPublicKeys()) { auto state(_state.lock()); @@ -964,21 +963,21 @@ void LocalStore::invalidatePath(State & state, const Path & path) } -void LocalStore::addToStore(const ValidPathInfo & info, const ref<std::string> & nar, - RepairFlag repair, CheckSigsFlag checkSigs, std::shared_ptr<FSAccessor> accessor) +const PublicKeys & LocalStore::getPublicKeys() { - assert(info.narHash); + auto state(_state.lock()); + if (!state->publicKeys) + state->publicKeys = std::make_unique<PublicKeys>(getDefaultPublicKeys()); + return *state->publicKeys; +} - Hash h = hashString(htSHA256, *nar); - if (h != info.narHash) - throw Error("hash mismatch importing path '%s'; expected hash '%s', got '%s'", - info.path, info.narHash.to_string(), h.to_string()); - if (nar->size() != info.narSize) - throw Error("size mismatch importing path '%s'; expected %s, got %s", - info.path, info.narSize, nar->size()); +void LocalStore::addToStore(const ValidPathInfo & info, Source & source, + RepairFlag repair, CheckSigsFlag checkSigs, std::shared_ptr<FSAccessor> accessor) +{ + assert(info.narHash); - if (requireSigs && checkSigs && !info.checkSignatures(*this, publicKeys)) + if (requireSigs && checkSigs && !info.checkSignatures(*this, getPublicKeys())) throw Error("cannot add path '%s' because it lacks a valid signature", info.path); addTempRoot(info.path); @@ -999,8 +998,27 @@ void LocalStore::addToStore(const ValidPathInfo & info, const ref<std::string> & deletePath(realPath); - StringSource source(*nar); - restorePath(realPath, source); + /* While restoring the path from the NAR, compute the hash + of the NAR. */ + HashSink hashSink(htSHA256); + + LambdaSource wrapperSource([&](unsigned char * data, size_t len) -> size_t { + size_t n = source.read(data, len); + hashSink(data, n); + return n; + }); + + restorePath(realPath, wrapperSource); + + auto hashResult = hashSink.finish(); + + if (hashResult.first != info.narHash) + throw Error("hash mismatch importing path '%s'; expected hash '%s', got '%s'", + info.path, info.narHash.to_string(), hashResult.first.to_string()); + + if (hashResult.second != info.narSize) + throw Error("size mismatch importing path '%s'; expected %s, got %s", + info.path, info.narSize, hashResult.second); autoGC(); @@ -1215,7 +1233,7 @@ bool LocalStore::verifyStore(bool checkContents, RepairFlag repair) /* Check the content hash (optionally - slow). */ printMsg(lvlTalkative, format("checking contents of '%1%'") % i); - HashResult current = hashPath(info->narHash.type, i); + HashResult current = hashPath(info->narHash.type, toRealPath(i)); if (info->narHash != nullHash && info->narHash != current.first) { printError(format("path '%1%' was modified! " diff --git a/src/libstore/local-store.hh b/src/libstore/local-store.hh index bbd50e1c1451..1209a06356f7 100644 --- a/src/libstore/local-store.hh +++ b/src/libstore/local-store.hh @@ -77,6 +77,8 @@ private: minFree but not much below availAfterGC, then there is no point in starting a new GC. */ uint64_t availAfterGC = std::numeric_limits<uint64_t>::max(); + + std::unique_ptr<PublicKeys> publicKeys; }; Sync<State, std::recursive_mutex> _state; @@ -100,7 +102,7 @@ private: settings.requireSigs, "require-sigs", "whether store paths should have a trusted signature on import"}; - PublicKeys publicKeys; + const PublicKeys & getPublicKeys(); public: @@ -143,7 +145,7 @@ public: void querySubstitutablePathInfos(const PathSet & paths, SubstitutablePathInfos & infos) override; - void addToStore(const ValidPathInfo & info, const ref<std::string> & nar, + void addToStore(const ValidPathInfo & info, Source & source, RepairFlag repair, CheckSigsFlag checkSigs, std::shared_ptr<FSAccessor> accessor) override; diff --git a/src/libstore/local.mk b/src/libstore/local.mk index e11efa5c2b54..a7279aa3939f 100644 --- a/src/libstore/local.mk +++ b/src/libstore/local.mk @@ -4,7 +4,7 @@ libstore_NAME = libnixstore libstore_DIR := $(d) -libstore_SOURCES := $(wildcard $(d)/*.cc) +libstore_SOURCES := $(wildcard $(d)/*.cc $(d)/builtins/*.cc) libstore_LIBS = libutil libformat diff --git a/src/libstore/nar-info-disk-cache.cc b/src/libstore/nar-info-disk-cache.cc index 6e155e877803..35403e5df56f 100644 --- a/src/libstore/nar-info-disk-cache.cc +++ b/src/libstore/nar-info-disk-cache.cc @@ -1,6 +1,7 @@ #include "nar-info-disk-cache.hh" #include "sync.hh" #include "sqlite.hh" +#include "globals.hh" #include <sqlite3.h> @@ -47,10 +48,6 @@ class NarInfoDiskCacheImpl : public NarInfoDiskCache { public: - /* How long negative and positive lookups are valid. */ - const int ttlNegative = 3600; - const int ttlPositive = 30 * 24 * 3600; - /* How often to purge expired entries from the cache. */ const int purgeInterval = 24 * 3600; @@ -116,8 +113,8 @@ public: SQLiteStmt(state->db, "delete from NARs where ((present = 0 and timestamp < ?) or (present = 1 and timestamp < ?))") .use() - (now - ttlNegative) - (now - ttlPositive) + (now - settings.ttlNegativeNarInfoCache) + (now - settings.ttlPositiveNarInfoCache) .exec(); debug("deleted %d entries from the NAR info disk cache", sqlite3_changes(state->db)); @@ -186,8 +183,8 @@ public: auto queryNAR(state->queryNAR.use() (cache.id) (hashPart) - (now - ttlNegative) - (now - ttlPositive)); + (now - settings.ttlNegativeNarInfoCache) + (now - settings.ttlPositiveNarInfoCache)); if (!queryNAR.next()) return {oUnknown, 0}; @@ -260,11 +257,8 @@ public: ref<NarInfoDiskCache> getNarInfoDiskCache() { - static Sync<std::shared_ptr<NarInfoDiskCache>> cache; - - auto cache_(cache.lock()); - if (!*cache_) *cache_ = std::make_shared<NarInfoDiskCacheImpl>(); - return ref<NarInfoDiskCache>(*cache_); + static ref<NarInfoDiskCache> cache = make_ref<NarInfoDiskCacheImpl>(); + return cache; } } diff --git a/src/libstore/nix-store.pc.in b/src/libstore/nix-store.pc.in index 3f1a2d83d2f2..5cf22faadcbe 100644 --- a/src/libstore/nix-store.pc.in +++ b/src/libstore/nix-store.pc.in @@ -5,5 +5,5 @@ includedir=@includedir@ Name: Nix Description: Nix Package Manager Version: @PACKAGE_VERSION@ -Libs: -L${libdir} -lnixstore -lnixutil -lnixformat -Cflags: -I${includedir}/nix +Libs: -L${libdir} -lnixstore -lnixutil +Cflags: -I${includedir}/nix -std=c++14 diff --git a/src/libstore/optimise-store.cc b/src/libstore/optimise-store.cc index 891540ae4c1d..7840167d7772 100644 --- a/src/libstore/optimise-store.cc +++ b/src/libstore/optimise-store.cc @@ -213,7 +213,7 @@ void LocalStore::optimisePath_(Activity * act, OptimiseStats & stats, MakeReadOnly makeReadOnly(mustToggle ? dirOf(path) : ""); Path tempLink = (format("%1%/.tmp-link-%2%-%3%") - % realStoreDir % getpid() % rand()).str(); + % realStoreDir % getpid() % random()).str(); if (link(linkPath.c_str(), tempLink.c_str()) == -1) { if (errno == EMLINK) { diff --git a/src/libstore/remote-store.cc b/src/libstore/remote-store.cc index 8f0b65557ac4..080cef93d214 100644 --- a/src/libstore/remote-store.cc +++ b/src/libstore/remote-store.cc @@ -377,7 +377,7 @@ Path RemoteStore::queryPathFromHashPart(const string & hashPart) } -void RemoteStore::addToStore(const ValidPathInfo & info, const ref<std::string> & nar, +void RemoteStore::addToStore(const ValidPathInfo & info, Source & source, RepairFlag repair, CheckSigsFlag checkSigs, std::shared_ptr<FSAccessor> accessor) { auto conn(connections->get()); @@ -385,22 +385,21 @@ void RemoteStore::addToStore(const ValidPathInfo & info, const ref<std::string> if (GET_PROTOCOL_MINOR(conn->daemonVersion) < 18) { conn->to << wopImportPaths; - StringSink sink; - sink << 1 // == path follows - ; - assert(nar->size() % 8 == 0); - sink((unsigned char *) nar->data(), nar->size()); - sink - << exportMagic - << info.path - << info.references - << info.deriver - << 0 // == no legacy signature - << 0 // == no path follows - ; - - StringSource source(*sink.s); - conn->processStderr(0, &source); + auto source2 = sinkToSource([&](Sink & sink) { + sink << 1 // == path follows + ; + copyNAR(source, sink); + sink + << exportMagic + << info.path + << info.references + << info.deriver + << 0 // == no legacy signature + << 0 // == no path follows + ; + }); + + conn->processStderr(0, source2.get()); auto importedPaths = readStorePaths<PathSet>(*this, conn->from); assert(importedPaths.size() <= 1); @@ -412,7 +411,7 @@ void RemoteStore::addToStore(const ValidPathInfo & info, const ref<std::string> << info.references << info.registrationTime << info.narSize << info.ultimate << info.sigs << info.ca << repair << !checkSigs; - conn->to(*nar); + copyNAR(source, conn->to); conn->processStderr(); } } diff --git a/src/libstore/remote-store.hh b/src/libstore/remote-store.hh index 7f36e206416b..95fa59a2069d 100644 --- a/src/libstore/remote-store.hh +++ b/src/libstore/remote-store.hh @@ -58,7 +58,7 @@ public: void querySubstitutablePathInfos(const PathSet & paths, SubstitutablePathInfos & infos) override; - void addToStore(const ValidPathInfo & info, const ref<std::string> & nar, + void addToStore(const ValidPathInfo & info, Source & nar, RepairFlag repair, CheckSigsFlag checkSigs, std::shared_ptr<FSAccessor> accessor) override; @@ -122,11 +122,12 @@ protected: ref<Pool<Connection>> connections; + virtual void setOptions(Connection & conn); + private: std::atomic_bool failed{false}; - void setOptions(Connection & conn); }; class UDSRemoteStore : public LocalFSStore, public RemoteStore diff --git a/src/libstore/sqlite.cc b/src/libstore/sqlite.cc index b13001b06d57..42d40e71d8be 100644 --- a/src/libstore/sqlite.cc +++ b/src/libstore/sqlite.cc @@ -7,7 +7,7 @@ namespace nix { -[[noreturn]] void throwSQLiteError(sqlite3 * db, const format & f) +[[noreturn]] void throwSQLiteError(sqlite3 * db, const FormatOrString & fs) { int err = sqlite3_errcode(db); @@ -21,7 +21,7 @@ namespace nix { : fmt("SQLite database '%s' is busy", path)); } else - throw SQLiteError("%s: %s (in '%s')", f.str(), sqlite3_errstr(err), path); + throw SQLiteError("%s: %s (in '%s')", fs.s, sqlite3_errstr(err), path); } SQLite::SQLite(const Path & path) diff --git a/src/libstore/sqlite.hh b/src/libstore/sqlite.hh index 14a7a0dd8996..115679b84159 100644 --- a/src/libstore/sqlite.hh +++ b/src/libstore/sqlite.hh @@ -93,7 +93,7 @@ struct SQLiteTxn MakeError(SQLiteError, Error); MakeError(SQLiteBusy, SQLiteError); -[[noreturn]] void throwSQLiteError(sqlite3 * db, const format & f); +[[noreturn]] void throwSQLiteError(sqlite3 * db, const FormatOrString & fs); void handleSQLiteBusy(const SQLiteBusy & e); diff --git a/src/libstore/ssh-store.cc b/src/libstore/ssh-store.cc index 107c6e1ecb4d..39205ae2ce12 100644 --- a/src/libstore/ssh-store.cc +++ b/src/libstore/ssh-store.cc @@ -51,21 +51,16 @@ private: std::string host; SSHMaster master; -}; - -class ForwardSource : public Source -{ - Source & readSource; - Sink & writeSink; -public: - ForwardSource(Source & readSource, Sink & writeSink) : readSource(readSource), writeSink(writeSink) {} - size_t read(unsigned char * data, size_t len) override + void setOptions(RemoteStore::Connection & conn) override { - auto n = readSource.read(data, len); - writeSink(data, n); - return n; - } + /* TODO Add a way to explicitly ask for some options to be + forwarded. One option: A way to query the daemon for its + settings, and then a series of params to SSHStore like + forward-cores or forward-overridden-cores that only + override the requested settings. + */ + }; }; void SSHStore::narFromPath(const Path & path, Sink & sink) @@ -73,9 +68,7 @@ void SSHStore::narFromPath(const Path & path, Sink & sink) auto conn(connections->get()); conn->to << wopNarFromPath << path; conn->processStderr(); - ParseSink ps; - auto fwd = ForwardSource(conn->from, sink); - parseDump(ps, fwd); + copyNAR(conn->from, sink); } ref<FSAccessor> SSHStore::getFSAccessor() diff --git a/src/libstore/ssh.cc b/src/libstore/ssh.cc index 7ff7a9bffc49..033c580936ad 100644 --- a/src/libstore/ssh.cc +++ b/src/libstore/ssh.cc @@ -49,6 +49,8 @@ std::unique_ptr<SSHMaster::Connection> SSHMaster::startCommand(const std::string addCommonSSHOpts(args); if (socketPath != "") args.insert(args.end(), {"-S", socketPath}); + if (verbosity >= lvlChatty) + args.push_back("-v"); args.push_back(command); execvp(args.begin()->c_str(), stringsToCharPtrs(args).data()); @@ -93,6 +95,8 @@ Path SSHMaster::startMaster() , "-o", "LocalCommand=echo started" , "-o", "PermitLocalCommand=yes" }; + if (verbosity >= lvlChatty) + args.push_back("-v"); addCommonSSHOpts(args); execvp(args.begin()->c_str(), stringsToCharPtrs(args).data()); diff --git a/src/libstore/store-api.cc b/src/libstore/store-api.cc index 8830edcc3449..1a0d12ca78c2 100644 --- a/src/libstore/store-api.cc +++ b/src/libstore/store-api.cc @@ -590,32 +590,15 @@ void copyStorePath(ref<Store> srcStore, ref<Store> dstStore, uint64_t total = 0; - auto progress = [&](size_t len) { - total += len; - act.progress(total, info->narSize); - }; - - struct MyStringSink : StringSink - { - typedef std::function<void(size_t)> Callback; - Callback callback; - MyStringSink(Callback callback) : callback(callback) { } - void operator () (const unsigned char * data, size_t len) override - { - StringSink::operator ()(data, len); - callback(len); - }; - }; - - MyStringSink sink(progress); - srcStore->narFromPath({storePath}, sink); - + // FIXME +#if 0 if (!info->narHash) { auto info2 = make_ref<ValidPathInfo>(*info); info2->narHash = hashString(htSHA256, *sink.s); if (!info->narSize) info2->narSize = sink.s->size(); info = info2; } +#endif if (info->ultimate) { auto info2 = make_ref<ValidPathInfo>(*info); @@ -623,7 +606,16 @@ void copyStorePath(ref<Store> srcStore, ref<Store> dstStore, info = info2; } - dstStore->addToStore(*info, sink.s, repair, checkSigs); + auto source = sinkToSource([&](Sink & sink) { + LambdaSink wrapperSink([&](const unsigned char * data, size_t len) { + sink(data, len); + total += len; + act.progress(total, info->narSize); + }); + srcStore->narFromPath({storePath}, wrapperSink); + }); + + dstStore->addToStore(*info, *source, repair, checkSigs); } @@ -765,7 +757,8 @@ bool ValidPathInfo::isContentAddressed(const Store & store) const else if (hasPrefix(ca, "fixed:")) { bool recursive = ca.compare(6, 2, "r:") == 0; Hash hash(std::string(ca, recursive ? 8 : 6)); - if (store.makeFixedOutputPath(recursive, hash, storePathToName(path)) == path) + if (references.empty() && + store.makeFixedOutputPath(recursive, hash, storePathToName(path)) == path) return true; else warn(); @@ -808,6 +801,21 @@ std::string makeFixedOutputCA(bool recursive, const Hash & hash) } +void Store::addToStore(const ValidPathInfo & info, Source & narSource, + RepairFlag repair, CheckSigsFlag checkSigs, + std::shared_ptr<FSAccessor> accessor) +{ + addToStore(info, make_ref<std::string>(narSource.drain()), repair, checkSigs, accessor); +} + +void Store::addToStore(const ValidPathInfo & info, const ref<std::string> & nar, + RepairFlag repair, CheckSigsFlag checkSigs, + std::shared_ptr<FSAccessor> accessor) +{ + StringSource source(*nar); + addToStore(info, source, repair, checkSigs, accessor); +} + } diff --git a/src/libstore/store-api.hh b/src/libstore/store-api.hh index 563aa566bd37..ea259f07e8ab 100644 --- a/src/libstore/store-api.hh +++ b/src/libstore/store-api.hh @@ -399,9 +399,14 @@ public: virtual bool wantMassQuery() { return false; } /* 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); + + // FIXME: remove virtual void addToStore(const ValidPathInfo & info, const ref<std::string> & nar, RepairFlag repair = NoRepair, CheckSigsFlag checkSigs = CheckSigs, - std::shared_ptr<FSAccessor> accessor = 0) = 0; + std::shared_ptr<FSAccessor> accessor = 0); /* Copy the contents of a path to the store and register the validity the resulting path. The resulting path is returned. |