Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
Ericson2314 committed Jul 20, 2024
1 parent d8856f4 commit 48aa3bb
Show file tree
Hide file tree
Showing 11 changed files with 181 additions and 138 deletions.
2 changes: 1 addition & 1 deletion src/libstore/local-fs-store.hh
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ struct LocalFSStoreConfig : LocalFSStoreConfigT<config::JustValue>
{
const Store::Config & storeConfig;

config::SettingDescriptionMap descriptions();
static config::SettingDescriptionMap descriptions();

LocalFSStoreConfig(
const Store::Config & storeConfig,
Expand Down
172 changes: 87 additions & 85 deletions src/libstore/local-overlay-store.cc
Original file line number Diff line number Diff line change
Expand Up @@ -5,111 +5,113 @@
#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<config::SettingInfo>{
.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<config::SettingInfo> 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<config::JustValue> localOverlayStoreConfigDefaults()
{
return {
.lowerStoreConfig = {make_ref<LocalStore::Config>(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::JustValue>{
CONFIG_ROW(lowerStoreUri, ""),
CONFIG_ROW(upperLayer, ""),
CONFIG_ROW(checkMount, true),
CONFIG_ROW(remountHook, ""),
}
: LocalStore::Config(scheme, authority, params)
, LocalOverlayStoreConfigT<config::JustValue>{localOverlayStoreConfigApplyParse(params)}
{
}


std::string LocalOverlayStoreConfig::doc()
std::string LocalOverlayStoreConfig::doc() const
{
return
#include "local-overlay-store.md"
;
}


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<const Store::Config &>(*this)}
, LocalFSStore{static_cast<const LocalFSStore::Config &>(*this)}
, LocalStore{static_cast<const LocalStore::Config &>(*this)}
, lowerStore(nix::openStore(lowerStoreUri.get()).dynamic_pointer_cast<LocalFSStore>())
LocalOverlayStore::LocalOverlayStore(ref<const Config> config)
: Store{*config}
, LocalFSStore{*config}
, LocalStore{static_cast<ref<const LocalStore::Config>>(config)}
, config{config}
, lowerStore(config->lowerStoreConfig.value->openStore().dynamic_pointer_cast<LocalFSStore>())
{
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.
Expand All @@ -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());
}
}
}
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand Down
28 changes: 12 additions & 16 deletions src/libstore/local-overlay-store.hh
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ namespace nix {
template<template<typename> class F>
struct LocalOverlayStoreConfigT
{
const F<std::string> lowerStoreUri;
const F<ref<const StoreConfig>> lowerStoreConfig;
const F<Path> upperLayer;
const F<bool> checkMount;
const F<Path> remountHook;
Expand All @@ -16,25 +16,17 @@ struct LocalOverlayStoreConfigT
* Configuration for `LocalOverlayStore`.
*/
struct LocalOverlayStoreConfig :
virtual LocalStoreConfig,
LocalStoreConfig,
LocalOverlayStoreConfigT<config::JustValue>
{
struct Descriptions :
virtual Store::Config::Descriptions,
virtual LocalStore::Config::Descriptions,
LocalOverlayStoreConfigT<config::SettingInfo>
{
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> experimentalFeature() const override
{
Expand All @@ -46,7 +38,7 @@ struct LocalOverlayStoreConfig :
return { "local-overlay" };
}

std::string doc() override;
std::string doc() const override;

protected:
/**
Expand All @@ -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;
};

/**
Expand All @@ -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<const Config> config;

LocalOverlayStore(ref<const Config>);

std::string getUri() override
{
Expand Down
2 changes: 1 addition & 1 deletion src/libstore/local-store.cc
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ LocalStore::Config::LocalStoreConfig(
{
}

std::string LocalStoreConfig::doc()
std::string LocalStoreConfig::doc() const
{
return
#include "local-store.md"
Expand Down
4 changes: 2 additions & 2 deletions src/libstore/local-store.hh
Original file line number Diff line number Diff line change
Expand Up @@ -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<std::string> uriSchemes()
{ return {"local"}; }

std::string doc() override;
std::string doc() const override;

ref<Store> openStore() const override;
};
Expand Down
20 changes: 6 additions & 14 deletions src/libstore/remote-store.hh
Original file line number Diff line number Diff line change
Expand Up @@ -21,22 +21,13 @@ template<typename T> class Pool;
template<template<typename> class F>
struct RemoteStoreConfigT
{
const F<int> maxConnections;
const F<unsigned int> maxConnectionAge;
F<int> maxConnections;
F<unsigned int> maxConnectionAge;
};

struct RemoteStoreConfig :
virtual Store::Config,
RemoteStoreConfigT<config::JustValue>
struct RemoteStoreConfig : RemoteStoreConfigT<config::JustValue>
{
struct Descriptions :
virtual Store::Config::Descriptions,
RemoteStoreConfigT<config::SettingInfo>
{
Descriptions();
};

static const Descriptions descriptions;
static config::SettingDescriptionMap descriptions();

RemoteStoreConfig(const StoreReference::Params &);
};
Expand All @@ -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. */
Expand Down
Loading

0 comments on commit 48aa3bb

Please sign in to comment.