forked from lix-project/lix
Merge remote-tracking branch 'origin/master' into non-ca-depending-on-ca
This commit is contained in:
commit
67cc356bb3
16 changed files with 483 additions and 268 deletions
2
Makefile
2
Makefile
|
@ -26,7 +26,7 @@ OPTIMIZE = 1
|
||||||
ifeq ($(OPTIMIZE), 1)
|
ifeq ($(OPTIMIZE), 1)
|
||||||
GLOBAL_CXXFLAGS += -O3
|
GLOBAL_CXXFLAGS += -O3
|
||||||
else
|
else
|
||||||
GLOBAL_CXXFLAGS += -O0
|
GLOBAL_CXXFLAGS += -O0 -U_FORTIFY_SOURCE
|
||||||
endif
|
endif
|
||||||
|
|
||||||
include mk/lib.mk
|
include mk/lib.mk
|
||||||
|
|
|
@ -115,6 +115,14 @@ public:
|
||||||
{
|
{
|
||||||
return handle_value<void(Value&, const char*)>(mkString, val.c_str());
|
return handle_value<void(Value&, const char*)>(mkString, val.c_str());
|
||||||
}
|
}
|
||||||
|
#if NLOHMANN_JSON_VERSION_MAJOR >= 3 && NLOHMANN_JSON_VERSION_MINOR >= 8
|
||||||
|
bool binary(binary_t&)
|
||||||
|
{
|
||||||
|
// This function ought to be unreachable
|
||||||
|
assert(false);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
bool start_object(std::size_t len)
|
bool start_object(std::size_t len)
|
||||||
{
|
{
|
||||||
|
|
|
@ -2988,14 +2988,6 @@ struct RestrictedStore : public LocalFSStore, public virtual RestrictedStoreConf
|
||||||
goal.addDependency(info.path);
|
goal.addDependency(info.path);
|
||||||
}
|
}
|
||||||
|
|
||||||
StorePath addToStoreFromDump(Source & dump, const string & name,
|
|
||||||
FileIngestionMethod method = FileIngestionMethod::Recursive, HashType hashAlgo = htSHA256, RepairFlag repair = NoRepair) override
|
|
||||||
{
|
|
||||||
auto path = next->addToStoreFromDump(dump, name, method, hashAlgo, repair);
|
|
||||||
goal.addDependency(path);
|
|
||||||
return path;
|
|
||||||
}
|
|
||||||
|
|
||||||
StorePath addTextToStore(const string & name, const string & s,
|
StorePath addTextToStore(const string & name, const string & s,
|
||||||
const StorePathSet & references, RepairFlag repair = NoRepair) override
|
const StorePathSet & references, RepairFlag repair = NoRepair) override
|
||||||
{
|
{
|
||||||
|
|
|
@ -4,11 +4,13 @@
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
std::string FixedOutputHash::printMethodAlgo() const {
|
std::string FixedOutputHash::printMethodAlgo() const
|
||||||
|
{
|
||||||
return makeFileIngestionPrefix(method) + printHashType(hash.type);
|
return makeFileIngestionPrefix(method) + printHashType(hash.type);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string makeFileIngestionPrefix(const FileIngestionMethod m) {
|
std::string makeFileIngestionPrefix(const FileIngestionMethod m)
|
||||||
|
{
|
||||||
switch (m) {
|
switch (m) {
|
||||||
case FileIngestionMethod::Flat:
|
case FileIngestionMethod::Flat:
|
||||||
return "";
|
return "";
|
||||||
|
@ -26,7 +28,8 @@ std::string makeFixedOutputCA(FileIngestionMethod method, const Hash & hash)
|
||||||
+ hash.to_string(Base32, true);
|
+ hash.to_string(Base32, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string renderContentAddress(ContentAddress ca) {
|
std::string renderContentAddress(ContentAddress ca)
|
||||||
|
{
|
||||||
return std::visit(overloaded {
|
return std::visit(overloaded {
|
||||||
[](TextHash th) {
|
[](TextHash th) {
|
||||||
return "text:" + th.hash.to_string(Base32, true);
|
return "text:" + th.hash.to_string(Base32, true);
|
||||||
|
@ -37,54 +40,97 @@ std::string renderContentAddress(ContentAddress ca) {
|
||||||
}, ca);
|
}, ca);
|
||||||
}
|
}
|
||||||
|
|
||||||
ContentAddress parseContentAddress(std::string_view rawCa) {
|
std::string renderContentAddressMethod(ContentAddressMethod cam)
|
||||||
auto rest = rawCa;
|
{
|
||||||
|
return std::visit(overloaded {
|
||||||
|
[](TextHashMethod &th) {
|
||||||
|
return std::string{"text:"} + printHashType(htSHA256);
|
||||||
|
},
|
||||||
|
[](FixedOutputHashMethod &fshm) {
|
||||||
|
return "fixed:" + makeFileIngestionPrefix(fshm.fileIngestionMethod) + printHashType(fshm.hashType);
|
||||||
|
}
|
||||||
|
}, cam);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Parses content address strings up to the hash.
|
||||||
|
*/
|
||||||
|
static ContentAddressMethod parseContentAddressMethodPrefix(std::string_view & rest)
|
||||||
|
{
|
||||||
|
std::string_view wholeInput { rest };
|
||||||
|
|
||||||
std::string_view prefix;
|
std::string_view prefix;
|
||||||
{
|
{
|
||||||
auto optPrefix = splitPrefixTo(rest, ':');
|
auto optPrefix = splitPrefixTo(rest, ':');
|
||||||
if (!optPrefix)
|
if (!optPrefix)
|
||||||
throw UsageError("not a content address because it is not in the form '<prefix>:<rest>': %s", rawCa);
|
throw UsageError("not a content address because it is not in the form '<prefix>:<rest>': %s", wholeInput);
|
||||||
prefix = *optPrefix;
|
prefix = *optPrefix;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto parseHashType_ = [&](){
|
auto parseHashType_ = [&](){
|
||||||
auto hashTypeRaw = splitPrefixTo(rest, ':');
|
auto hashTypeRaw = splitPrefixTo(rest, ':');
|
||||||
if (!hashTypeRaw)
|
if (!hashTypeRaw)
|
||||||
throw UsageError("content address hash must be in form '<algo>:<hash>', but found: %s", rawCa);
|
throw UsageError("content address hash must be in form '<algo>:<hash>', but found: %s", wholeInput);
|
||||||
HashType hashType = parseHashType(*hashTypeRaw);
|
HashType hashType = parseHashType(*hashTypeRaw);
|
||||||
return std::move(hashType);
|
return std::move(hashType);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Switch on prefix
|
// Switch on prefix
|
||||||
if (prefix == "text") {
|
if (prefix == "text") {
|
||||||
// No parsing of the method, "text" only support flat.
|
// No parsing of the ingestion method, "text" only support flat.
|
||||||
HashType hashType = parseHashType_();
|
HashType hashType = parseHashType_();
|
||||||
if (hashType != htSHA256)
|
if (hashType != htSHA256)
|
||||||
throw Error("text content address hash should use %s, but instead uses %s",
|
throw Error("text content address hash should use %s, but instead uses %s",
|
||||||
printHashType(htSHA256), printHashType(hashType));
|
printHashType(htSHA256), printHashType(hashType));
|
||||||
return TextHash {
|
return TextHashMethod {};
|
||||||
.hash = Hash::parseNonSRIUnprefixed(rest, std::move(hashType)),
|
|
||||||
};
|
|
||||||
} else if (prefix == "fixed") {
|
} else if (prefix == "fixed") {
|
||||||
// Parse method
|
// Parse method
|
||||||
auto method = FileIngestionMethod::Flat;
|
auto method = FileIngestionMethod::Flat;
|
||||||
if (splitPrefix(rest, "r:"))
|
if (splitPrefix(rest, "r:"))
|
||||||
method = FileIngestionMethod::Recursive;
|
method = FileIngestionMethod::Recursive;
|
||||||
HashType hashType = parseHashType_();
|
HashType hashType = parseHashType_();
|
||||||
return FixedOutputHash {
|
return FixedOutputHashMethod {
|
||||||
.method = method,
|
.fileIngestionMethod = method,
|
||||||
.hash = Hash::parseNonSRIUnprefixed(rest, std::move(hashType)),
|
.hashType = std::move(hashType),
|
||||||
};
|
};
|
||||||
} else
|
} else
|
||||||
throw UsageError("content address prefix '%s' is unrecognized. Recogonized prefixes are 'text' or 'fixed'", prefix);
|
throw UsageError("content address prefix '%s' is unrecognized. Recogonized prefixes are 'text' or 'fixed'", prefix);
|
||||||
|
}
|
||||||
|
|
||||||
|
ContentAddress parseContentAddress(std::string_view rawCa) {
|
||||||
|
auto rest = rawCa;
|
||||||
|
|
||||||
|
ContentAddressMethod caMethod = parseContentAddressMethodPrefix(rest);
|
||||||
|
|
||||||
|
return std::visit(
|
||||||
|
overloaded {
|
||||||
|
[&](TextHashMethod thm) {
|
||||||
|
return ContentAddress(TextHash {
|
||||||
|
.hash = Hash::parseNonSRIUnprefixed(rest, htSHA256)
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[&](FixedOutputHashMethod fohMethod) {
|
||||||
|
return ContentAddress(FixedOutputHash {
|
||||||
|
.method = fohMethod.fileIngestionMethod,
|
||||||
|
.hash = Hash::parseNonSRIUnprefixed(rest, std::move(fohMethod.hashType)),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
}, caMethod);
|
||||||
|
}
|
||||||
|
|
||||||
|
ContentAddressMethod parseContentAddressMethod(std::string_view caMethod)
|
||||||
|
{
|
||||||
|
std::string_view asPrefix {std::string{caMethod} + ":"};
|
||||||
|
return parseContentAddressMethodPrefix(asPrefix);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<ContentAddress> parseContentAddressOpt(std::string_view rawCaOpt)
|
||||||
|
{
|
||||||
|
return rawCaOpt == "" ? std::optional<ContentAddress>() : parseContentAddress(rawCaOpt);
|
||||||
};
|
};
|
||||||
|
|
||||||
std::optional<ContentAddress> parseContentAddressOpt(std::string_view rawCaOpt) {
|
std::string renderContentAddress(std::optional<ContentAddress> ca)
|
||||||
return rawCaOpt == "" ? std::optional<ContentAddress> {} : parseContentAddress(rawCaOpt);
|
{
|
||||||
};
|
|
||||||
|
|
||||||
std::string renderContentAddress(std::optional<ContentAddress> ca) {
|
|
||||||
return ca ? renderContentAddress(*ca) : "";
|
return ca ? renderContentAddress(*ca) : "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -55,4 +55,23 @@ std::optional<ContentAddress> parseContentAddressOpt(std::string_view rawCaOpt);
|
||||||
|
|
||||||
Hash getContentAddressHash(const ContentAddress & ca);
|
Hash getContentAddressHash(const ContentAddress & ca);
|
||||||
|
|
||||||
|
/*
|
||||||
|
We only have one way to hash text with references, so this is single-value
|
||||||
|
type is only useful in std::variant.
|
||||||
|
*/
|
||||||
|
struct TextHashMethod { };
|
||||||
|
struct FixedOutputHashMethod {
|
||||||
|
FileIngestionMethod fileIngestionMethod;
|
||||||
|
HashType hashType;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef std::variant<
|
||||||
|
TextHashMethod,
|
||||||
|
FixedOutputHashMethod
|
||||||
|
> ContentAddressMethod;
|
||||||
|
|
||||||
|
ContentAddressMethod parseContentAddressMethod(std::string_view rawCaMethod);
|
||||||
|
|
||||||
|
std::string renderContentAddressMethod(ContentAddressMethod caMethod);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
#include "monitor-fd.hh"
|
#include "monitor-fd.hh"
|
||||||
#include "worker-protocol.hh"
|
#include "worker-protocol.hh"
|
||||||
#include "store-api.hh"
|
#include "store-api.hh"
|
||||||
#include "local-store.hh"
|
|
||||||
#include "finally.hh"
|
#include "finally.hh"
|
||||||
#include "affinity.hh"
|
#include "affinity.hh"
|
||||||
#include "archive.hh"
|
#include "archive.hh"
|
||||||
|
@ -240,6 +239,23 @@ struct ClientSettings
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static void writeValidPathInfo(
|
||||||
|
ref<Store> store,
|
||||||
|
unsigned int clientVersion,
|
||||||
|
Sink & to,
|
||||||
|
std::shared_ptr<const ValidPathInfo> info)
|
||||||
|
{
|
||||||
|
to << (info->deriver ? store->printStorePath(*info->deriver) : "")
|
||||||
|
<< info->narHash.to_string(Base16, false);
|
||||||
|
writeStorePaths(*store, to, info->references);
|
||||||
|
to << info->registrationTime << info->narSize;
|
||||||
|
if (GET_PROTOCOL_MINOR(clientVersion) >= 16) {
|
||||||
|
to << info->ultimate
|
||||||
|
<< info->sigs
|
||||||
|
<< renderContentAddress(info->ca);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void performOp(TunnelLogger * logger, ref<Store> store,
|
static void performOp(TunnelLogger * logger, ref<Store> store,
|
||||||
TrustedFlag trusted, RecursiveFlag recursive, unsigned int clientVersion,
|
TrustedFlag trusted, RecursiveFlag recursive, unsigned int clientVersion,
|
||||||
Source & from, BufferedSink & to, unsigned int op)
|
Source & from, BufferedSink & to, unsigned int op)
|
||||||
|
@ -350,6 +366,41 @@ static void performOp(TunnelLogger * logger, ref<Store> store,
|
||||||
}
|
}
|
||||||
|
|
||||||
case wopAddToStore: {
|
case wopAddToStore: {
|
||||||
|
if (GET_PROTOCOL_MINOR(clientVersion) >= 25) {
|
||||||
|
auto name = readString(from);
|
||||||
|
auto camStr = readString(from);
|
||||||
|
auto refs = readStorePaths<StorePathSet>(*store, from);
|
||||||
|
bool repairBool;
|
||||||
|
from >> repairBool;
|
||||||
|
auto repair = RepairFlag{repairBool};
|
||||||
|
|
||||||
|
logger->startWork();
|
||||||
|
auto pathInfo = [&]() {
|
||||||
|
// NB: FramedSource must be out of scope before logger->stopWork();
|
||||||
|
ContentAddressMethod contentAddressMethod = parseContentAddressMethod(camStr);
|
||||||
|
FramedSource source(from);
|
||||||
|
// TODO this is essentially RemoteStore::addCAToStore. Move it up to Store.
|
||||||
|
return std::visit(overloaded {
|
||||||
|
[&](TextHashMethod &_) {
|
||||||
|
// We could stream this by changing Store
|
||||||
|
std::string contents = source.drain();
|
||||||
|
auto path = store->addTextToStore(name, contents, refs, repair);
|
||||||
|
return store->queryPathInfo(path);
|
||||||
|
},
|
||||||
|
[&](FixedOutputHashMethod &fohm) {
|
||||||
|
if (!refs.empty())
|
||||||
|
throw UnimplementedError("cannot yet have refs with flat or nar-hashed data");
|
||||||
|
auto path = store->addToStoreFromDump(source, name, fohm.fileIngestionMethod, fohm.hashType, repair);
|
||||||
|
return store->queryPathInfo(path);
|
||||||
|
},
|
||||||
|
}, contentAddressMethod);
|
||||||
|
}();
|
||||||
|
logger->stopWork();
|
||||||
|
|
||||||
|
to << store->printStorePath(pathInfo->path);
|
||||||
|
writeValidPathInfo(store, clientVersion, to, pathInfo);
|
||||||
|
|
||||||
|
} else {
|
||||||
HashType hashAlgo;
|
HashType hashAlgo;
|
||||||
std::string baseName;
|
std::string baseName;
|
||||||
FileIngestionMethod method;
|
FileIngestionMethod method;
|
||||||
|
@ -391,6 +442,7 @@ static void performOp(TunnelLogger * logger, ref<Store> store,
|
||||||
logger->stopWork();
|
logger->stopWork();
|
||||||
|
|
||||||
to << store->printStorePath(path);
|
to << store->printStorePath(path);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -675,15 +727,7 @@ static void performOp(TunnelLogger * logger, ref<Store> store,
|
||||||
if (info) {
|
if (info) {
|
||||||
if (GET_PROTOCOL_MINOR(clientVersion) >= 17)
|
if (GET_PROTOCOL_MINOR(clientVersion) >= 17)
|
||||||
to << 1;
|
to << 1;
|
||||||
to << (info->deriver ? store->printStorePath(*info->deriver) : "")
|
writeValidPathInfo(store, clientVersion, to, info);
|
||||||
<< info->narHash.to_string(Base16, false);
|
|
||||||
writeStorePaths(*store, to, info->references);
|
|
||||||
to << info->registrationTime << info->narSize;
|
|
||||||
if (GET_PROTOCOL_MINOR(clientVersion) >= 16) {
|
|
||||||
to << info->ultimate
|
|
||||||
<< info->sigs
|
|
||||||
<< renderContentAddress(info->ca);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
assert(GET_PROTOCOL_MINOR(clientVersion) >= 17);
|
assert(GET_PROTOCOL_MINOR(clientVersion) >= 17);
|
||||||
to << 0;
|
to << 0;
|
||||||
|
@ -749,59 +793,12 @@ static void performOp(TunnelLogger * logger, ref<Store> store,
|
||||||
info.ultimate = false;
|
info.ultimate = false;
|
||||||
|
|
||||||
if (GET_PROTOCOL_MINOR(clientVersion) >= 23) {
|
if (GET_PROTOCOL_MINOR(clientVersion) >= 23) {
|
||||||
|
|
||||||
struct FramedSource : Source
|
|
||||||
{
|
|
||||||
Source & from;
|
|
||||||
bool eof = false;
|
|
||||||
std::vector<unsigned char> pending;
|
|
||||||
size_t pos = 0;
|
|
||||||
|
|
||||||
FramedSource(Source & from) : from(from)
|
|
||||||
{ }
|
|
||||||
|
|
||||||
~FramedSource()
|
|
||||||
{
|
|
||||||
if (!eof) {
|
|
||||||
while (true) {
|
|
||||||
auto n = readInt(from);
|
|
||||||
if (!n) break;
|
|
||||||
std::vector<unsigned char> data(n);
|
|
||||||
from(data.data(), n);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t read(unsigned char * data, size_t len) override
|
|
||||||
{
|
|
||||||
if (eof) throw EndOfFile("reached end of FramedSource");
|
|
||||||
|
|
||||||
if (pos >= pending.size()) {
|
|
||||||
size_t len = readInt(from);
|
|
||||||
if (!len) {
|
|
||||||
eof = true;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
pending = std::vector<unsigned char>(len);
|
|
||||||
pos = 0;
|
|
||||||
from(pending.data(), len);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto n = std::min(len, pending.size() - pos);
|
|
||||||
memcpy(data, pending.data() + pos, n);
|
|
||||||
pos += n;
|
|
||||||
return n;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
logger->startWork();
|
logger->startWork();
|
||||||
|
|
||||||
{
|
{
|
||||||
FramedSource source(from);
|
FramedSource source(from);
|
||||||
store->addToStore(info, source, (RepairFlag) repair,
|
store->addToStore(info, source, (RepairFlag) repair,
|
||||||
dontCheckSigs ? NoCheckSigs : CheckSigs);
|
dontCheckSigs ? NoCheckSigs : CheckSigs);
|
||||||
}
|
}
|
||||||
|
|
||||||
logger->stopWork();
|
logger->stopWork();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -42,6 +42,7 @@ Settings::Settings()
|
||||||
{
|
{
|
||||||
buildUsersGroup = getuid() == 0 ? "nixbld" : "";
|
buildUsersGroup = getuid() == 0 ? "nixbld" : "";
|
||||||
lockCPU = getEnv("NIX_AFFINITY_HACK") == "1";
|
lockCPU = getEnv("NIX_AFFINITY_HACK") == "1";
|
||||||
|
allowSymlinkedStore = getEnv("NIX_IGNORE_SYMLINK_STORE") == "1";
|
||||||
|
|
||||||
caFile = getEnv("NIX_SSL_CERT_FILE").value_or(getEnv("SSL_CERT_FILE").value_or(""));
|
caFile = getEnv("NIX_SSL_CERT_FILE").value_or(getEnv("SSL_CERT_FILE").value_or(""));
|
||||||
if (caFile == "") {
|
if (caFile == "") {
|
||||||
|
@ -147,6 +148,12 @@ bool Settings::isWSL1()
|
||||||
|
|
||||||
const string nixVersion = PACKAGE_VERSION;
|
const string nixVersion = PACKAGE_VERSION;
|
||||||
|
|
||||||
|
NLOHMANN_JSON_SERIALIZE_ENUM(SandboxMode, {
|
||||||
|
{SandboxMode::smEnabled, true},
|
||||||
|
{SandboxMode::smRelaxed, "relaxed"},
|
||||||
|
{SandboxMode::smDisabled, false},
|
||||||
|
});
|
||||||
|
|
||||||
template<> void BaseSetting<SandboxMode>::set(const std::string & str)
|
template<> void BaseSetting<SandboxMode>::set(const std::string & str)
|
||||||
{
|
{
|
||||||
if (str == "true") value = smEnabled;
|
if (str == "true") value = smEnabled;
|
||||||
|
|
|
@ -880,6 +880,19 @@ public:
|
||||||
|
|
||||||
Setting<std::string> flakeRegistry{this, "https://github.com/NixOS/flake-registry/raw/master/flake-registry.json", "flake-registry",
|
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."};
|
"Path or URI of the global flake registry."};
|
||||||
|
|
||||||
|
Setting<bool> allowSymlinkedStore{
|
||||||
|
this, false, "allow-symlinked-store",
|
||||||
|
R"(
|
||||||
|
If set to `true`, Nix will stop complaining if the store directory
|
||||||
|
(typically /nix/store) contains symlink components.
|
||||||
|
|
||||||
|
This risks making some builds "impure" because builders sometimes
|
||||||
|
"canonicalise" paths by resolving all symlink components. Problems
|
||||||
|
occur if those builds are then deployed to machines where /nix/store
|
||||||
|
resolves to a different location from that of the build machine. You
|
||||||
|
can enable this setting if you are sure you're not going to do that.
|
||||||
|
)"};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -110,7 +110,7 @@ LocalStore::LocalStore(const Params & params)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Ensure that the store and its parents are not symlinks. */
|
/* Ensure that the store and its parents are not symlinks. */
|
||||||
if (getEnv("NIX_IGNORE_SYMLINK_STORE") != "1") {
|
if (!settings.allowSymlinkedStore) {
|
||||||
Path path = realStoreDir;
|
Path path = realStoreDir;
|
||||||
struct stat st;
|
struct stat st;
|
||||||
while (path != "/") {
|
while (path != "/") {
|
||||||
|
|
|
@ -307,6 +307,8 @@ struct ConnectionHandle
|
||||||
std::rethrow_exception(ex);
|
std::rethrow_exception(ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void withFramedSink(std::function<void(Sink & sink)> fun);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -420,11 +422,28 @@ void RemoteStore::querySubstitutablePathInfos(const StorePathCAMap & pathsMap, S
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ref<const ValidPathInfo> RemoteStore::readValidPathInfo(ConnectionHandle & conn, const StorePath & path)
|
||||||
|
{
|
||||||
|
auto deriver = readString(conn->from);
|
||||||
|
auto narHash = Hash::parseAny(readString(conn->from), htSHA256);
|
||||||
|
auto info = make_ref<ValidPathInfo>(path, narHash);
|
||||||
|
if (deriver != "") info->deriver = parseStorePath(deriver);
|
||||||
|
info->references = readStorePaths<StorePathSet>(*this, conn->from);
|
||||||
|
conn->from >> info->registrationTime >> info->narSize;
|
||||||
|
if (GET_PROTOCOL_MINOR(conn->daemonVersion) >= 16) {
|
||||||
|
conn->from >> info->ultimate;
|
||||||
|
info->sigs = readStrings<StringSet>(conn->from);
|
||||||
|
info->ca = parseContentAddressOpt(readString(conn->from));
|
||||||
|
}
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void RemoteStore::queryPathInfoUncached(const StorePath & path,
|
void RemoteStore::queryPathInfoUncached(const StorePath & path,
|
||||||
Callback<std::shared_ptr<const ValidPathInfo>> callback) noexcept
|
Callback<std::shared_ptr<const ValidPathInfo>> callback) noexcept
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
std::shared_ptr<ValidPathInfo> info;
|
std::shared_ptr<const ValidPathInfo> info;
|
||||||
{
|
{
|
||||||
auto conn(getConnection());
|
auto conn(getConnection());
|
||||||
conn->to << wopQueryPathInfo << printStorePath(path);
|
conn->to << wopQueryPathInfo << printStorePath(path);
|
||||||
|
@ -440,17 +459,7 @@ void RemoteStore::queryPathInfoUncached(const StorePath & path,
|
||||||
bool valid; conn->from >> valid;
|
bool valid; conn->from >> valid;
|
||||||
if (!valid) throw InvalidPath("path '%s' is not valid", printStorePath(path));
|
if (!valid) throw InvalidPath("path '%s' is not valid", printStorePath(path));
|
||||||
}
|
}
|
||||||
auto deriver = readString(conn->from);
|
info = readValidPathInfo(conn, path);
|
||||||
auto narHash = Hash::parseAny(readString(conn->from), htSHA256);
|
|
||||||
info = std::make_shared<ValidPathInfo>(path, narHash);
|
|
||||||
if (deriver != "") info->deriver = parseStorePath(deriver);
|
|
||||||
info->references = readStorePaths<StorePathSet>(*this, conn->from);
|
|
||||||
conn->from >> info->registrationTime >> info->narSize;
|
|
||||||
if (GET_PROTOCOL_MINOR(conn->daemonVersion) >= 16) {
|
|
||||||
conn->from >> info->ultimate;
|
|
||||||
info->sigs = readStrings<StringSet>(conn->from);
|
|
||||||
info->ca = parseContentAddressOpt(readString(conn->from));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
callback(std::move(info));
|
callback(std::move(info));
|
||||||
} catch (...) { callback.rethrow(); }
|
} catch (...) { callback.rethrow(); }
|
||||||
|
@ -525,6 +534,93 @@ std::optional<StorePath> RemoteStore::queryPathFromHashPart(const std::string &
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ref<const ValidPathInfo> RemoteStore::addCAToStore(
|
||||||
|
Source & dump,
|
||||||
|
const string & name,
|
||||||
|
ContentAddressMethod caMethod,
|
||||||
|
const StorePathSet & references,
|
||||||
|
RepairFlag repair)
|
||||||
|
{
|
||||||
|
std::optional<ConnectionHandle> conn_(getConnection());
|
||||||
|
auto & conn = *conn_;
|
||||||
|
|
||||||
|
if (GET_PROTOCOL_MINOR(conn->daemonVersion) >= 25) {
|
||||||
|
|
||||||
|
conn->to
|
||||||
|
<< wopAddToStore
|
||||||
|
<< name
|
||||||
|
<< renderContentAddressMethod(caMethod);
|
||||||
|
writeStorePaths(*this, conn->to, references);
|
||||||
|
conn->to << repair;
|
||||||
|
|
||||||
|
conn.withFramedSink([&](Sink & sink) {
|
||||||
|
dump.drainInto(sink);
|
||||||
|
});
|
||||||
|
|
||||||
|
auto path = parseStorePath(readString(conn->from));
|
||||||
|
return readValidPathInfo(conn, path);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (repair) throw Error("repairing is not supported when building through the Nix daemon protocol < 1.25");
|
||||||
|
|
||||||
|
std::visit(overloaded {
|
||||||
|
[&](TextHashMethod thm) -> void {
|
||||||
|
std::string s = dump.drain();
|
||||||
|
conn->to << wopAddTextToStore << name << s;
|
||||||
|
writeStorePaths(*this, conn->to, references);
|
||||||
|
conn.processStderr();
|
||||||
|
},
|
||||||
|
[&](FixedOutputHashMethod fohm) -> void {
|
||||||
|
conn->to
|
||||||
|
<< wopAddToStore
|
||||||
|
<< name
|
||||||
|
<< ((fohm.hashType == htSHA256 && fohm.fileIngestionMethod == FileIngestionMethod::Recursive) ? 0 : 1) /* backwards compatibility hack */
|
||||||
|
<< (fohm.fileIngestionMethod == FileIngestionMethod::Recursive ? 1 : 0)
|
||||||
|
<< printHashType(fohm.hashType);
|
||||||
|
|
||||||
|
try {
|
||||||
|
conn->to.written = 0;
|
||||||
|
conn->to.warn = true;
|
||||||
|
connections->incCapacity();
|
||||||
|
{
|
||||||
|
Finally cleanup([&]() { connections->decCapacity(); });
|
||||||
|
if (fohm.fileIngestionMethod == FileIngestionMethod::Recursive) {
|
||||||
|
dump.drainInto(conn->to);
|
||||||
|
} else {
|
||||||
|
std::string contents = dump.drain();
|
||||||
|
dumpString(contents, conn->to);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
conn->to.warn = false;
|
||||||
|
conn.processStderr();
|
||||||
|
} catch (SysError & e) {
|
||||||
|
/* Daemon closed while we were sending the path. Probably OOM
|
||||||
|
or I/O error. */
|
||||||
|
if (e.errNo == EPIPE)
|
||||||
|
try {
|
||||||
|
conn.processStderr();
|
||||||
|
} catch (EndOfFile & e) { }
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}, caMethod);
|
||||||
|
auto path = parseStorePath(readString(conn->from));
|
||||||
|
// Release our connection to prevent a deadlock in queryPathInfo().
|
||||||
|
conn_.reset();
|
||||||
|
return queryPathInfo(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
StorePath RemoteStore::addToStoreFromDump(Source & dump, const string & name,
|
||||||
|
FileIngestionMethod method, HashType hashType, RepairFlag repair)
|
||||||
|
{
|
||||||
|
StorePathSet references;
|
||||||
|
return addCAToStore(dump, name, FixedOutputHashMethod{ .fileIngestionMethod = method, .hashType = hashType }, references, repair)->path;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void RemoteStore::addToStore(const ValidPathInfo & info, Source & source,
|
void RemoteStore::addToStore(const ValidPathInfo & info, Source & source,
|
||||||
RepairFlag repair, CheckSigsFlag checkSigs)
|
RepairFlag repair, CheckSigsFlag checkSigs)
|
||||||
{
|
{
|
||||||
|
@ -565,78 +661,9 @@ void RemoteStore::addToStore(const ValidPathInfo & info, Source & source,
|
||||||
<< repair << !checkSigs;
|
<< repair << !checkSigs;
|
||||||
|
|
||||||
if (GET_PROTOCOL_MINOR(conn->daemonVersion) >= 23) {
|
if (GET_PROTOCOL_MINOR(conn->daemonVersion) >= 23) {
|
||||||
|
conn.withFramedSink([&](Sink & sink) {
|
||||||
conn->to.flush();
|
|
||||||
|
|
||||||
std::exception_ptr ex;
|
|
||||||
|
|
||||||
struct FramedSink : BufferedSink
|
|
||||||
{
|
|
||||||
ConnectionHandle & conn;
|
|
||||||
std::exception_ptr & ex;
|
|
||||||
|
|
||||||
FramedSink(ConnectionHandle & conn, std::exception_ptr & ex) : conn(conn), ex(ex)
|
|
||||||
{ }
|
|
||||||
|
|
||||||
~FramedSink()
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
conn->to << 0;
|
|
||||||
conn->to.flush();
|
|
||||||
} catch (...) {
|
|
||||||
ignoreException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void write(const unsigned char * data, size_t len) override
|
|
||||||
{
|
|
||||||
/* Don't send more data if the remote has
|
|
||||||
encountered an error. */
|
|
||||||
if (ex) {
|
|
||||||
auto ex2 = ex;
|
|
||||||
ex = nullptr;
|
|
||||||
std::rethrow_exception(ex2);
|
|
||||||
}
|
|
||||||
conn->to << len;
|
|
||||||
conn->to(data, len);
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Handle log messages / exceptions from the remote on a
|
|
||||||
separate thread. */
|
|
||||||
std::thread stderrThread([&]()
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
conn.processStderr(nullptr, nullptr, false);
|
|
||||||
} catch (...) {
|
|
||||||
ex = std::current_exception();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Finally joinStderrThread([&]()
|
|
||||||
{
|
|
||||||
if (stderrThread.joinable()) {
|
|
||||||
stderrThread.join();
|
|
||||||
if (ex) {
|
|
||||||
try {
|
|
||||||
std::rethrow_exception(ex);
|
|
||||||
} catch (...) {
|
|
||||||
ignoreException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
{
|
|
||||||
FramedSink sink(conn, ex);
|
|
||||||
copyNAR(source, sink);
|
copyNAR(source, sink);
|
||||||
sink.flush();
|
});
|
||||||
}
|
|
||||||
|
|
||||||
stderrThread.join();
|
|
||||||
if (ex)
|
|
||||||
std::rethrow_exception(ex);
|
|
||||||
|
|
||||||
} else if (GET_PROTOCOL_MINOR(conn->daemonVersion) >= 21) {
|
} else if (GET_PROTOCOL_MINOR(conn->daemonVersion) >= 21) {
|
||||||
conn.processStderr(0, &source);
|
conn.processStderr(0, &source);
|
||||||
} else {
|
} else {
|
||||||
|
@ -647,57 +674,11 @@ void RemoteStore::addToStore(const ValidPathInfo & info, Source & source,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
StorePath RemoteStore::addToStore(const string & name, const Path & _srcPath,
|
|
||||||
FileIngestionMethod method, HashType hashAlgo, PathFilter & filter, RepairFlag repair)
|
|
||||||
{
|
|
||||||
if (repair) throw Error("repairing is not supported when building through the Nix daemon");
|
|
||||||
|
|
||||||
auto conn(getConnection());
|
|
||||||
|
|
||||||
Path srcPath(absPath(_srcPath));
|
|
||||||
|
|
||||||
conn->to
|
|
||||||
<< wopAddToStore
|
|
||||||
<< name
|
|
||||||
<< ((hashAlgo == htSHA256 && method == FileIngestionMethod::Recursive) ? 0 : 1) /* backwards compatibility hack */
|
|
||||||
<< (method == FileIngestionMethod::Recursive ? 1 : 0)
|
|
||||||
<< printHashType(hashAlgo);
|
|
||||||
|
|
||||||
try {
|
|
||||||
conn->to.written = 0;
|
|
||||||
conn->to.warn = true;
|
|
||||||
connections->incCapacity();
|
|
||||||
{
|
|
||||||
Finally cleanup([&]() { connections->decCapacity(); });
|
|
||||||
dumpPath(srcPath, conn->to, filter);
|
|
||||||
}
|
|
||||||
conn->to.warn = false;
|
|
||||||
conn.processStderr();
|
|
||||||
} catch (SysError & e) {
|
|
||||||
/* Daemon closed while we were sending the path. Probably OOM
|
|
||||||
or I/O error. */
|
|
||||||
if (e.errNo == EPIPE)
|
|
||||||
try {
|
|
||||||
conn.processStderr();
|
|
||||||
} catch (EndOfFile & e) { }
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
|
|
||||||
return parseStorePath(readString(conn->from));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
StorePath RemoteStore::addTextToStore(const string & name, const string & s,
|
StorePath RemoteStore::addTextToStore(const string & name, const string & s,
|
||||||
const StorePathSet & references, RepairFlag repair)
|
const StorePathSet & references, RepairFlag repair)
|
||||||
{
|
{
|
||||||
if (repair) throw Error("repairing is not supported when building through the Nix daemon");
|
StringSource source(s);
|
||||||
|
return addCAToStore(source, name, TextHashMethod{}, references, repair)->path;
|
||||||
auto conn(getConnection());
|
|
||||||
conn->to << wopAddTextToStore << name << s;
|
|
||||||
writeStorePaths(*this, conn->to, references);
|
|
||||||
|
|
||||||
conn.processStderr();
|
|
||||||
return parseStorePath(readString(conn->from));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -993,6 +974,49 @@ std::exception_ptr RemoteStore::Connection::processStderr(Sink * sink, Source *
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ConnectionHandle::withFramedSink(std::function<void(Sink &sink)> fun)
|
||||||
|
{
|
||||||
|
(*this)->to.flush();
|
||||||
|
|
||||||
|
std::exception_ptr ex;
|
||||||
|
|
||||||
|
/* Handle log messages / exceptions from the remote on a
|
||||||
|
separate thread. */
|
||||||
|
std::thread stderrThread([&]()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
processStderr(nullptr, nullptr, false);
|
||||||
|
} catch (...) {
|
||||||
|
ex = std::current_exception();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Finally joinStderrThread([&]()
|
||||||
|
{
|
||||||
|
if (stderrThread.joinable()) {
|
||||||
|
stderrThread.join();
|
||||||
|
if (ex) {
|
||||||
|
try {
|
||||||
|
std::rethrow_exception(ex);
|
||||||
|
} catch (...) {
|
||||||
|
ignoreException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
{
|
||||||
|
FramedSink sink((*this)->to, ex);
|
||||||
|
fun(sink);
|
||||||
|
sink.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
stderrThread.join();
|
||||||
|
if (ex)
|
||||||
|
std::rethrow_exception(ex);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
static RegisterStoreImplementation<UDSRemoteStore, UDSRemoteStoreConfig> regStore;
|
static RegisterStoreImplementation<UDSRemoteStore, UDSRemoteStoreConfig> regStore;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -63,13 +63,21 @@ public:
|
||||||
void querySubstitutablePathInfos(const StorePathCAMap & paths,
|
void querySubstitutablePathInfos(const StorePathCAMap & paths,
|
||||||
SubstitutablePathInfos & infos) override;
|
SubstitutablePathInfos & infos) override;
|
||||||
|
|
||||||
|
/* Add a content-addressable store path. `dump` will be drained. */
|
||||||
|
ref<const ValidPathInfo> addCAToStore(
|
||||||
|
Source & dump,
|
||||||
|
const string & name,
|
||||||
|
ContentAddressMethod caMethod,
|
||||||
|
const StorePathSet & references,
|
||||||
|
RepairFlag repair);
|
||||||
|
|
||||||
|
/* Add a content-addressable store path. Does not support references. `dump` will be drained. */
|
||||||
|
StorePath addToStoreFromDump(Source & dump, const string & name,
|
||||||
|
FileIngestionMethod method = FileIngestionMethod::Recursive, HashType hashAlgo = htSHA256, RepairFlag repair = NoRepair) override;
|
||||||
|
|
||||||
void addToStore(const ValidPathInfo & info, Source & nar,
|
void addToStore(const ValidPathInfo & info, Source & nar,
|
||||||
RepairFlag repair, CheckSigsFlag checkSigs) override;
|
RepairFlag repair, CheckSigsFlag checkSigs) override;
|
||||||
|
|
||||||
StorePath addToStore(const string & name, const Path & srcPath,
|
|
||||||
FileIngestionMethod method = FileIngestionMethod::Recursive, HashType hashAlgo = htSHA256,
|
|
||||||
PathFilter & filter = defaultPathFilter, RepairFlag repair = NoRepair) override;
|
|
||||||
|
|
||||||
StorePath addTextToStore(const string & name, const string & s,
|
StorePath addTextToStore(const string & name, const string & s,
|
||||||
const StorePathSet & references, RepairFlag repair) override;
|
const StorePathSet & references, RepairFlag repair) override;
|
||||||
|
|
||||||
|
@ -139,6 +147,8 @@ protected:
|
||||||
|
|
||||||
virtual void narFromPath(const StorePath & path, Sink & sink) override;
|
virtual void narFromPath(const StorePath & path, Sink & sink) override;
|
||||||
|
|
||||||
|
ref<const ValidPathInfo> readValidPathInfo(ConnectionHandle & conn, const StorePath & path);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
std::atomic_bool failed{false};
|
std::atomic_bool failed{false};
|
||||||
|
|
|
@ -449,7 +449,8 @@ public:
|
||||||
/* Like addToStore(), but the contents of the path are contained
|
/* Like addToStore(), but the contents of the path are contained
|
||||||
in `dump', which is either a NAR serialisation (if recursive ==
|
in `dump', which is either a NAR serialisation (if recursive ==
|
||||||
true) or simply the contents of a regular file (if recursive ==
|
true) or simply the contents of a regular file (if recursive ==
|
||||||
false). */
|
false).
|
||||||
|
`dump` may be drained */
|
||||||
// FIXME: remove?
|
// FIXME: remove?
|
||||||
virtual StorePath addToStoreFromDump(Source & dump, const string & name,
|
virtual StorePath addToStoreFromDump(Source & dump, const string & name,
|
||||||
FileIngestionMethod method = FileIngestionMethod::Recursive, HashType hashAlgo = htSHA256, RepairFlag repair = NoRepair)
|
FileIngestionMethod method = FileIngestionMethod::Recursive, HashType hashAlgo = htSHA256, RepairFlag repair = NoRepair)
|
||||||
|
|
|
@ -6,7 +6,7 @@ namespace nix {
|
||||||
#define WORKER_MAGIC_1 0x6e697863
|
#define WORKER_MAGIC_1 0x6e697863
|
||||||
#define WORKER_MAGIC_2 0x6478696f
|
#define WORKER_MAGIC_2 0x6478696f
|
||||||
|
|
||||||
#define PROTOCOL_VERSION 0x118
|
#define PROTOCOL_VERSION 0x119
|
||||||
#define GET_PROTOCOL_MAJOR(x) ((x) & 0xff00)
|
#define GET_PROTOCOL_MAJOR(x) ((x) & 0xff00)
|
||||||
#define GET_PROTOCOL_MINOR(x) ((x) & 0x00ff)
|
#define GET_PROTOCOL_MINOR(x) ((x) & 0x00ff)
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@ typedef enum {
|
||||||
wopQueryReferences = 5, // obsolete
|
wopQueryReferences = 5, // obsolete
|
||||||
wopQueryReferrers = 6,
|
wopQueryReferrers = 6,
|
||||||
wopAddToStore = 7,
|
wopAddToStore = 7,
|
||||||
wopAddTextToStore = 8,
|
wopAddTextToStore = 8, // obsolete since 1.25, Nix 3.0. Use wopAddToStore
|
||||||
wopBuildPaths = 9,
|
wopBuildPaths = 9,
|
||||||
wopEnsurePath = 10,
|
wopEnsurePath = 10,
|
||||||
wopAddTempRoot = 11,
|
wopAddTempRoot = 11,
|
||||||
|
|
|
@ -93,7 +93,7 @@ void Source::operator () (unsigned char * data, size_t len)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
std::string Source::drain()
|
void Source::drainInto(Sink & sink)
|
||||||
{
|
{
|
||||||
std::string s;
|
std::string s;
|
||||||
std::vector<unsigned char> buf(8192);
|
std::vector<unsigned char> buf(8192);
|
||||||
|
@ -101,12 +101,19 @@ std::string Source::drain()
|
||||||
size_t n;
|
size_t n;
|
||||||
try {
|
try {
|
||||||
n = read(buf.data(), buf.size());
|
n = read(buf.data(), buf.size());
|
||||||
s.append((char *) buf.data(), n);
|
sink(buf.data(), n);
|
||||||
} catch (EndOfFile &) {
|
} catch (EndOfFile &) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return s;
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::string Source::drain()
|
||||||
|
{
|
||||||
|
StringSink s;
|
||||||
|
drainInto(s);
|
||||||
|
return *s.s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -69,6 +69,8 @@ struct Source
|
||||||
|
|
||||||
virtual bool good() { return true; }
|
virtual bool good() { return true; }
|
||||||
|
|
||||||
|
void drainInto(Sink & sink);
|
||||||
|
|
||||||
std::string drain();
|
std::string drain();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -340,7 +342,7 @@ T readNum(Source & source)
|
||||||
((uint64_t) buf[6] << 48) |
|
((uint64_t) buf[6] << 48) |
|
||||||
((uint64_t) buf[7] << 56);
|
((uint64_t) buf[7] << 56);
|
||||||
|
|
||||||
if (n > std::numeric_limits<T>::max())
|
if (n > (uint64_t)std::numeric_limits<T>::max())
|
||||||
throw SerialisationError("serialised integer %d is too large for type '%s'", n, typeid(T).name());
|
throw SerialisationError("serialised integer %d is too large for type '%s'", n, typeid(T).name());
|
||||||
|
|
||||||
return (T) n;
|
return (T) n;
|
||||||
|
@ -404,4 +406,93 @@ struct StreamToSourceAdapter : Source
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/* A source that reads a distinct format of concatenated chunks back into its
|
||||||
|
logical form, in order to guarantee a known state to the original stream,
|
||||||
|
even in the event of errors.
|
||||||
|
|
||||||
|
Use with FramedSink, which also allows the logical stream to be terminated
|
||||||
|
in the event of an exception.
|
||||||
|
*/
|
||||||
|
struct FramedSource : Source
|
||||||
|
{
|
||||||
|
Source & from;
|
||||||
|
bool eof = false;
|
||||||
|
std::vector<unsigned char> pending;
|
||||||
|
size_t pos = 0;
|
||||||
|
|
||||||
|
FramedSource(Source & from) : from(from)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
~FramedSource()
|
||||||
|
{
|
||||||
|
if (!eof) {
|
||||||
|
while (true) {
|
||||||
|
auto n = readInt(from);
|
||||||
|
if (!n) break;
|
||||||
|
std::vector<unsigned char> data(n);
|
||||||
|
from(data.data(), n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t read(unsigned char * data, size_t len) override
|
||||||
|
{
|
||||||
|
if (eof) throw EndOfFile("reached end of FramedSource");
|
||||||
|
|
||||||
|
if (pos >= pending.size()) {
|
||||||
|
size_t len = readInt(from);
|
||||||
|
if (!len) {
|
||||||
|
eof = true;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
pending = std::vector<unsigned char>(len);
|
||||||
|
pos = 0;
|
||||||
|
from(pending.data(), len);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto n = std::min(len, pending.size() - pos);
|
||||||
|
memcpy(data, pending.data() + pos, n);
|
||||||
|
pos += n;
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Write as chunks in the format expected by FramedSource.
|
||||||
|
|
||||||
|
The exception_ptr reference can be used to terminate the stream when you
|
||||||
|
detect that an error has occurred on the remote end.
|
||||||
|
*/
|
||||||
|
struct FramedSink : nix::BufferedSink
|
||||||
|
{
|
||||||
|
BufferedSink & to;
|
||||||
|
std::exception_ptr & ex;
|
||||||
|
|
||||||
|
FramedSink(BufferedSink & to, std::exception_ptr & ex) : to(to), ex(ex)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
~FramedSink()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
to << 0;
|
||||||
|
to.flush();
|
||||||
|
} catch (...) {
|
||||||
|
ignoreException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void write(const unsigned char * data, size_t len) override
|
||||||
|
{
|
||||||
|
/* Don't send more data if the remote has
|
||||||
|
encountered an error. */
|
||||||
|
if (ex) {
|
||||||
|
auto ex2 = ex;
|
||||||
|
ex = nullptr;
|
||||||
|
std::rethrow_exception(ex2);
|
||||||
|
}
|
||||||
|
to << len;
|
||||||
|
to(data, len);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@ namespace nix {
|
||||||
|
|
||||||
// If `separator` is found, we return the portion of the string before the
|
// If `separator` is found, we return the portion of the string before the
|
||||||
// separator, and modify the string argument to contain only the part after the
|
// separator, and modify the string argument to contain only the part after the
|
||||||
// separator. Otherwise, wer return `std::nullopt`, and we leave the argument
|
// separator. Otherwise, we return `std::nullopt`, and we leave the argument
|
||||||
// string alone.
|
// string alone.
|
||||||
static inline std::optional<std::string_view> splitPrefixTo(std::string_view & string, char separator) {
|
static inline std::optional<std::string_view> splitPrefixTo(std::string_view & string, char separator) {
|
||||||
auto sepInstance = string.find(separator);
|
auto sepInstance = string.find(separator);
|
||||||
|
|
Loading…
Reference in a new issue