diff --git a/src/libstore/local-fs-store.hh b/src/libstore/local-fs-store.hh index 27ef32ee94c..8fbd98db99d 100644 --- a/src/libstore/local-fs-store.hh +++ b/src/libstore/local-fs-store.hh @@ -20,7 +20,7 @@ struct LocalFSStoreConfig : LocalFSStoreConfigT { const Store::Config & storeConfig; - config::SettingDescriptionMap descriptions(); + static config::SettingDescriptionMap descriptions(); LocalFSStoreConfig( const Store::Config & storeConfig, diff --git a/src/libstore/local-overlay-store.cc b/src/libstore/local-overlay-store.cc index a1bf594cdb4..5f3d9281814 100644 --- a/src/libstore/local-overlay-store.cc +++ b/src/libstore/local-overlay-store.cc @@ -5,84 +5,88 @@ #include "realisation.hh" #include "processes.hh" #include "url.hh" -#include "store-open.hh" +#include "store-api.hh" #include "store-registration.hh" #include "config-parse-impl.hh" namespace nix { -LocalOverlayStore::Config::Descriptions::Descriptions() - : Store::Config::Descriptions{Store::Config::descriptions} - , LocalFSStore::Config::Descriptions{LocalFSStore::Config::descriptions} - , LocalStore::Config::Descriptions{LocalStore::Config::descriptions} - , LocalOverlayStoreConfigT{ - .lowerStoreUri{ - .name = "lower-store", - .description = R"( - [Store URL](@docroot@/command-ref/new-cli/nix3-help-stores.md#store-url-format) - for the lower store. The default is `auto` (i.e. use the Nix daemon or `/nix/store` directly). - - Must be a store with a store dir on the file system. - Must be used as OverlayFS lower layer for this store's store dir. - )", - }, - .upperLayer{ - .name = "upper-layer", - .description = R"( - Directory containing the OverlayFS upper layer for this store's store dir. - )", - }, - .checkMount{ - .name = "check-mount", - .description = R"( - Check that the overlay filesystem is correctly mounted. - - Nix does not manage the overlayfs mount point itself, but the correct - functioning of the overlay store does depend on this mount point being set up - correctly. Rather than just assume this is the case, check that the lowerdir - and upperdir options are what we expect them to be. This check is on by - default, but can be disabled if needed. - )", - }, - .remountHook{ - .name = "remount-hook", - .description = R"( - Script or other executable to run when overlay filesystem needs remounting. - - This is occasionally necessary when deleting a store path that exists in both upper and lower layers. - In such a situation, bypassing OverlayFS and deleting the path in the upper layer directly - is the only way to perform the deletion without creating a "whiteout". - However this causes the OverlayFS kernel data structures to get out-of-sync, - and can lead to 'stale file handle' errors; remounting solves the problem. - - The store directory is passed as an argument to the invoked executable. - )", - }, - } -{} - - -const LocalOverlayStore::Config::Descriptions LocalOverlayStore::Config::descriptions{}; +static LocalOverlayStoreConfigT localOverlayStoreConfigDescriptions = { + .lowerStoreConfig{ + .name = "lower-store", + .description = R"( + [Store URL](@docroot@/command-ref/new-cli/nix3-help-stores.md#store-url-format) + for the lower store. The default is `auto` (i.e. use the Nix daemon or `/nix/store` directly). + + Must be a store with a store dir on the file system. + Must be used as OverlayFS lower layer for this store's store dir. + )", + }, + .upperLayer{ + .name = "upper-layer", + .description = R"( + Directory containing the OverlayFS upper layer for this store's store dir. + )", + }, + .checkMount{ + .name = "check-mount", + .description = R"( + Check that the overlay filesystem is correctly mounted. + + Nix does not manage the overlayfs mount point itself, but the correct + functioning of the overlay store does depend on this mount point being set up + correctly. Rather than just assume this is the case, check that the lowerdir + and upperdir options are what we expect them to be. This check is on by + default, but can be disabled if needed. + )", + }, + .remountHook{ + .name = "remount-hook", + .description = R"( + Script or other executable to run when overlay filesystem needs remounting. + + This is occasionally necessary when deleting a store path that exists in both upper and lower layers. + In such a situation, bypassing OverlayFS and deleting the path in the upper layer directly + is the only way to perform the deletion without creating a "whiteout". + However this causes the OverlayFS kernel data structures to get out-of-sync, + and can lead to 'stale file handle' errors; remounting solves the problem. + + The store directory is passed as an argument to the invoked executable. + )", + }, +}; + +#define LOCAL_OVERLAY_STORE_CONFIG_FIELDS(X) \ + X(lowerStoreConfig), \ + X(upperLayer), \ + X(checkMount), \ + X(remountHook), + +MAKE_PARSE(LocalOverlayStoreConfig, localOverlayStoreConfig, LOCAL_OVERLAY_STORE_CONFIG_FIELDS) + +static LocalOverlayStoreConfigT localOverlayStoreConfigDefaults() +{ + return { + .lowerStoreConfig = {make_ref(StoreReference::Params{})}, + .upperLayer = {""}, + .checkMount = {true}, + .remountHook = {""}, + }; +} +MAKE_APPLY_PARSE(LocalOverlayStoreConfig, localOverlayStoreConfig, LOCAL_OVERLAY_STORE_CONFIG_FIELDS) LocalOverlayStore::Config::LocalOverlayStoreConfig( std::string_view scheme, std::string_view authority, const StoreReference::Params & params) - : Store::Config(params) - , LocalFSStore::Config(authority, params) - , LocalStore::Config(scheme, authority, params) - , LocalOverlayStoreConfigT{ - CONFIG_ROW(lowerStoreUri, ""), - CONFIG_ROW(upperLayer, ""), - CONFIG_ROW(checkMount, true), - CONFIG_ROW(remountHook, ""), - } + : LocalStore::Config(scheme, authority, params) + , LocalOverlayStoreConfigT{localOverlayStoreConfigApplyParse(params)} { } -std::string LocalOverlayStoreConfig::doc() +std::string LocalOverlayStoreConfig::doc() const { return #include "local-overlay-store.md" @@ -90,26 +94,24 @@ std::string LocalOverlayStoreConfig::doc() } -Path LocalOverlayStoreConfig::toUpperPath(const StorePath & path) { +Path LocalOverlayStoreConfig::toUpperPath(const StorePath & path) const +{ return upperLayer + "/" + path.to_string(); } -LocalOverlayStore::LocalOverlayStore(const Config & config) - : Store::Config{config} - , LocalFSStore::Config{config} - , LocalStore::Config{config} - , LocalOverlayStore::Config{config} - , Store{static_cast(*this)} - , LocalFSStore{static_cast(*this)} - , LocalStore{static_cast(*this)} - , lowerStore(nix::openStore(lowerStoreUri.get()).dynamic_pointer_cast()) +LocalOverlayStore::LocalOverlayStore(ref config) + : Store{*config} + , LocalFSStore{*config} + , LocalStore{static_cast>(config)} + , config{config} + , lowerStore(config->lowerStoreConfig.value->openStore().dynamic_pointer_cast()) { - if (checkMount.get()) { + if (config->checkMount.get()) { std::smatch match; std::string mountInfo; auto mounts = readFile("/proc/self/mounts"); - auto regex = std::regex(R"((^|\n)overlay )" + realStoreDir.get() + R"( .*(\n|$))"); + auto regex = std::regex(R"((^|\n)overlay )" + config->realStoreDir.get() + R"( .*(\n|$))"); // Mount points can be stacked, so there might be multiple matching entries. // Loop until the last match, which will be the current state of the mount point. @@ -122,13 +124,13 @@ LocalOverlayStore::LocalOverlayStore(const Config & config) return std::regex_search(mountInfo, std::regex("\\b" + option + "=" + value + "( |,)")); }; - auto expectedLowerDir = lowerStore->realStoreDir.get(); - if (!checkOption("lowerdir", expectedLowerDir) || !checkOption("upperdir", upperLayer)) { + auto expectedLowerDir = lowerStore->config.realStoreDir.get(); + if (!checkOption("lowerdir", expectedLowerDir) || !checkOption("upperdir", config->upperLayer)) { debug("expected lowerdir: %s", expectedLowerDir); - debug("expected upperdir: %s", upperLayer); + debug("expected upperdir: %s", config->upperLayer); debug("actual mount: %s", mountInfo); throw Error("overlay filesystem '%s' mounted incorrectly", - realStoreDir.get()); + config->realStoreDir.get()); } } } @@ -278,14 +280,14 @@ void LocalOverlayStore::collectGarbage(const GCOptions & options, GCResults & re void LocalOverlayStore::deleteStorePath(const Path & path, uint64_t & bytesFreed) { - auto mergedDir = realStoreDir.get() + "/"; + auto mergedDir = config->realStoreDir.get() + "/"; if (path.substr(0, mergedDir.length()) != mergedDir) { warn("local-overlay: unexpected gc path '%s' ", path); return; } StorePath storePath = {path.substr(mergedDir.length())}; - auto upperPath = toUpperPath(storePath); + auto upperPath = config->toUpperPath(storePath); if (pathExists(upperPath)) { debug("upper exists: %s", path); @@ -334,7 +336,7 @@ LocalStore::VerificationResult LocalOverlayStore::verifyAllValidPaths(RepairFlag StorePathSet done; auto existsInStoreDir = [&](const StorePath & storePath) { - return pathExists(realStoreDir.get() + "/" + storePath.to_string()); + return pathExists(config->realStoreDir.get() + "/" + storePath.to_string()); }; bool errors = false; @@ -354,10 +356,10 @@ void LocalOverlayStore::remountIfNecessary() { if (!_remountRequired) return; - if (remountHook.get().empty()) { - warn("'%s' needs remounting, set remount-hook to do this automatically", realStoreDir.get()); + if (config->remountHook.get().empty()) { + warn("'%s' needs remounting, set remount-hook to do this automatically", config->realStoreDir.get()); } else { - runProgram(remountHook, false, {realStoreDir}); + runProgram(config->remountHook, false, {config->realStoreDir}); } _remountRequired = false; diff --git a/src/libstore/local-overlay-store.hh b/src/libstore/local-overlay-store.hh index 3d16c0c4695..112abd6be6b 100644 --- a/src/libstore/local-overlay-store.hh +++ b/src/libstore/local-overlay-store.hh @@ -6,7 +6,7 @@ namespace nix { template class F> struct LocalOverlayStoreConfigT { - const F lowerStoreUri; + const F> lowerStoreConfig; const F upperLayer; const F checkMount; const F remountHook; @@ -16,25 +16,17 @@ struct LocalOverlayStoreConfigT * Configuration for `LocalOverlayStore`. */ struct LocalOverlayStoreConfig : - virtual LocalStoreConfig, + LocalStoreConfig, LocalOverlayStoreConfigT { - struct Descriptions : - virtual Store::Config::Descriptions, - virtual LocalStore::Config::Descriptions, - LocalOverlayStoreConfigT - { - Descriptions(); - }; - - static const Descriptions descriptions; + static config::SettingDescriptionMap descriptions(); LocalOverlayStoreConfig( std::string_view scheme, PathView path, const StoreReference::Params & params); - const std::string name() override { return "Experimental Local Overlay Store"; } + const std::string name() const override { return "Experimental Local Overlay Store"; } std::optional experimentalFeature() const override { @@ -46,7 +38,7 @@ struct LocalOverlayStoreConfig : return { "local-overlay" }; } - std::string doc() override; + std::string doc() const override; protected: /** @@ -57,7 +49,9 @@ protected: * at that file path. It might be stored in the lower layer instead, * or it might not be part of this store at all. */ - Path toUpperPath(const StorePath & path); + Path toUpperPath(const StorePath & path) const; + + friend struct LocalOverlayStore; }; /** @@ -66,11 +60,13 @@ protected: * Documentation on overridden methods states how they differ from their * `LocalStore` counterparts. */ -struct LocalOverlayStore : virtual LocalOverlayStoreConfig, virtual LocalStore +struct LocalOverlayStore : virtual LocalStore { using Config = LocalOverlayStoreConfig; - LocalOverlayStore(const Config &); + ref config; + + LocalOverlayStore(ref); std::string getUri() override { diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc index b16d56ebb3d..2572ed9e75a 100644 --- a/src/libstore/local-store.cc +++ b/src/libstore/local-store.cc @@ -108,7 +108,7 @@ LocalStore::Config::LocalStoreConfig( { } -std::string LocalStoreConfig::doc() +std::string LocalStoreConfig::doc() const { return #include "local-store.md" diff --git a/src/libstore/local-store.hh b/src/libstore/local-store.hh index e05d3cc3b85..fd72249abe0 100644 --- a/src/libstore/local-store.hh +++ b/src/libstore/local-store.hh @@ -58,12 +58,12 @@ struct LocalStoreConfig : std::string_view authority, const StoreReference::Params & params); - const std::string name() override { return "Local Store"; } + const std::string name() const override { return "Local Store"; } static std::set uriSchemes() { return {"local"}; } - std::string doc() override; + std::string doc() const override; ref openStore() const override; }; diff --git a/src/libstore/remote-store.hh b/src/libstore/remote-store.hh index d4a63de1f13..b79f68b3c53 100644 --- a/src/libstore/remote-store.hh +++ b/src/libstore/remote-store.hh @@ -21,22 +21,13 @@ template class Pool; template class F> struct RemoteStoreConfigT { - const F maxConnections; - const F maxConnectionAge; + F maxConnections; + F maxConnectionAge; }; -struct RemoteStoreConfig : - virtual Store::Config, - RemoteStoreConfigT +struct RemoteStoreConfig : RemoteStoreConfigT { - struct Descriptions : - virtual Store::Config::Descriptions, - RemoteStoreConfigT - { - Descriptions(); - }; - - static const Descriptions descriptions; + static config::SettingDescriptionMap descriptions(); RemoteStoreConfig(const StoreReference::Params &); }; @@ -46,13 +37,14 @@ struct RemoteStoreConfig : * DaemonStore. */ struct RemoteStore : - public virtual RemoteStoreConfig, public virtual Store, public virtual GcStore, public virtual LogStore { using Config = RemoteStoreConfig; + const Config & config; + RemoteStore(const Config & config); /* Implementations of abstract store API methods. */ diff --git a/src/libstore/store-api.cc b/src/libstore/store-api.cc index 675f09a1231..1629135b751 100644 --- a/src/libstore/store-api.cc +++ b/src/libstore/store-api.cc @@ -1307,7 +1307,7 @@ std::optional decodeValidPathInfo(const Store & store, std::istre } -std::string MixStoreDirMethods::showPaths(const StorePathSet & paths) +std::string MixStoreDirMethods::showPaths(const StorePathSet & paths) const { std::string s; for (auto & i : paths) { diff --git a/src/libstore/store-api.hh b/src/libstore/store-api.hh index 206a35373db..87b8335430a 100644 --- a/src/libstore/store-api.hh +++ b/src/libstore/store-api.hh @@ -133,12 +133,12 @@ struct StoreConfig : /** * The name of this type of store. */ - virtual const std::string name() = 0; + virtual const std::string name() const = 0; /** * Documentation for this type of store. */ - virtual std::string doc() + virtual std::string doc() const { return ""; } @@ -897,3 +897,6 @@ std::map drvOutputReferences( Store * evalStore = nullptr); } + +// Parses a Store URL, uses global state not pure so think about this +JSON_IMPL(ref) diff --git a/src/libstore/store-dir-config.hh b/src/libstore/store-dir-config.hh index f247f917a50..dae372147c9 100644 --- a/src/libstore/store-dir-config.hh +++ b/src/libstore/store-dir-config.hh @@ -64,7 +64,7 @@ struct MixStoreDirMethods * Display a set of paths in human-readable form (i.e., between quotes * and separated by commas). */ - std::string showPaths(const StorePathSet & paths); + std::string showPaths(const StorePathSet & paths) const; /** * @return true if ‘path’ is in the Nix store (but not the Nix diff --git a/src/libstore/store-registration.cc b/src/libstore/store-registration.cc index 1f3edfc71c1..306416b2681 100644 --- a/src/libstore/store-registration.cc +++ b/src/libstore/store-registration.cc @@ -15,7 +15,6 @@ ref openStore(StoreReference && storeURI) { auto store = resolveStoreConfig(std::move(storeURI))->openStore(); - experimentalFeatureSettings.require(store->experimentalFeature()); #if 0 // FIXME store->warnUnknownSettings(); store->init(); @@ -69,6 +68,8 @@ ref resolveStoreConfig(StoreReference && storeURI) }, storeURI.variant); + experimentalFeatureSettings.require(storeConfig->experimentalFeature()); + return storeConfig; } @@ -94,7 +95,9 @@ std::list> getDefaultSubstituters() for (auto uri : settings.substituters.get()) addStore(uri); - stores.sort([](ref & a, ref & b) { return a->priority < b->priority; }); + stores.sort([](ref & a, ref & b) { + return a->resolvedSubstConfig.priority < b->resolvedSubstConfig.priority; + }); return stores; }()); @@ -103,3 +106,54 @@ std::list> getDefaultSubstituters() } } + +namespace nlohmann { + +using namespace nix::config; + +ref adl_serializer>::from_json(const json & json) +{ + StoreReference ref; + switch (json.type()) { + + case json::value_t::string: { + ref = StoreReference::parse(json.get_ref()); + break; + } + + case json::value_t::object: { + auto & obj = json.get_ref(); + ref = StoreReference { + .variant = StoreReference::Specified{ + .scheme = getString(valueAt(obj, "scheme")), + .authority = getString(valueAt(obj, "authority")), + }, + .params = obj, + }; + break; + } + + case json::value_t::null: + case json::value_t::number_unsigned: + case json::value_t::number_integer: + case json::value_t::number_float: + case json::value_t::boolean: + case json::value_t::array: + case json::value_t::binary: + case json::value_t::discarded: + default: + throw UsageError( + "Invalid JSON for Store configuration: is type '%s' but must be string or object", + json.type_name()); + }; + + return resolveStoreConfig(std::move(ref)); +} + +void adl_serializer>::to_json(json & obj, ref s) +{ + // TODO, for tests maybe + assert(false); +} + +} diff --git a/src/libstore/uds-remote-store.hh b/src/libstore/uds-remote-store.hh index 4af4eac624e..a584bb90385 100644 --- a/src/libstore/uds-remote-store.hh +++ b/src/libstore/uds-remote-store.hh @@ -8,17 +8,12 @@ namespace nix { struct UDSRemoteStoreConfig : - virtual LocalFSStore::Config, - virtual RemoteStore::Config + std::enable_shared_from_this, + Store::Config, + LocalFSStore::Config, + RemoteStore::Config { - struct Descriptions : - virtual LocalFSStore::Config::Descriptions, - virtual RemoteStore::Config::Descriptions - { - Descriptions(); - }; - - static const Descriptions descriptions; + static config::SettingDescriptionMap descriptions(); UDSRemoteStoreConfig(const StoreReference::Params & params) : UDSRemoteStoreConfig{"unix", "", params} @@ -32,9 +27,9 @@ struct UDSRemoteStoreConfig : std::string_view authority, const StoreReference::Params & params); - const std::string name() override { return "Local Daemon Store"; } + const std::string name() const override { return "Local Daemon Store"; } - std::string doc() override; + std::string doc() const override; /** * The path to the unix domain socket. @@ -51,12 +46,13 @@ struct UDSRemoteStoreConfig : }; struct UDSRemoteStore : - virtual UDSRemoteStoreConfig, virtual IndirectRootStore, virtual RemoteStore { using Config = UDSRemoteStoreConfig; + ref config; + UDSRemoteStore(const Config &); std::string getUri() override;