2023-05-08 17:20:06 +03:00
|
|
|
#include "local-store.hh"
|
|
|
|
|
|
|
|
namespace nix {
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Configuration for `LocalOverlayStore`.
|
|
|
|
*/
|
|
|
|
struct LocalOverlayStoreConfig : virtual LocalStoreConfig
|
|
|
|
{
|
|
|
|
LocalOverlayStoreConfig(const StringMap & params)
|
|
|
|
: StoreConfig(params)
|
|
|
|
, LocalFSStoreConfig(params)
|
|
|
|
, LocalStoreConfig(params)
|
|
|
|
{ }
|
|
|
|
|
|
|
|
const Setting<std::string> lowerStoreUri{(StoreConfig*) this, "", "lower-store",
|
|
|
|
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.
|
2023-05-09 01:50:16 +03:00
|
|
|
Must be used as OverlayFS lower layer for this store's store dir.
|
|
|
|
)"};
|
|
|
|
|
2023-07-10 04:53:06 +03:00
|
|
|
const PathSetting upperLayer{(StoreConfig*) this, "", "upper-layer",
|
2023-05-09 01:50:16 +03:00
|
|
|
R"(
|
2023-12-11 20:18:34 +02:00
|
|
|
Directory containing the OverlayFS upper layer for this store's store dir.
|
2023-05-08 17:20:06 +03:00
|
|
|
)"};
|
|
|
|
|
2023-05-15 12:20:16 +03:00
|
|
|
Setting<bool> checkMount{(StoreConfig*) this, true, "check-mount",
|
2023-05-16 15:10:25 +03:00
|
|
|
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.
|
|
|
|
)"};
|
2023-05-15 12:20:16 +03:00
|
|
|
|
2023-07-26 13:31:26 +03:00
|
|
|
const PathSetting remountHook{(StoreConfig*) this, "", "remount-hook",
|
|
|
|
R"(
|
2023-08-01 13:55:55 +03:00
|
|
|
Script or other executable to run when overlay filesystem needs remounting.
|
2023-07-26 13:31:26 +03:00
|
|
|
|
2023-08-01 13:55:55 +03:00
|
|
|
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.
|
2023-07-26 13:31:26 +03:00
|
|
|
)"};
|
|
|
|
|
2023-05-08 17:20:06 +03:00
|
|
|
const std::string name() override { return "Experimental Local Overlay Store"; }
|
|
|
|
|
2023-08-03 03:30:42 +03:00
|
|
|
std::optional<ExperimentalFeature> experimentalFeature() const override
|
|
|
|
{
|
|
|
|
return ExperimentalFeature::LocalOverlayStore;
|
|
|
|
}
|
|
|
|
|
2023-08-03 18:59:04 +03:00
|
|
|
std::string doc() override;
|
2023-05-09 01:50:16 +03:00
|
|
|
|
2023-12-11 20:18:34 +02:00
|
|
|
protected:
|
2023-05-09 01:50:16 +03:00
|
|
|
/**
|
2023-12-11 20:43:17 +02:00
|
|
|
* @return The host OS path corresponding to the store path for the
|
|
|
|
* upper layer.
|
|
|
|
*
|
|
|
|
* @note The there is no guarantee a store object is actually stored
|
|
|
|
* at that file path. It might be stored in the lower layer instead,
|
|
|
|
* or it might not be part of this store at all.
|
2023-05-09 01:50:16 +03:00
|
|
|
*/
|
|
|
|
Path toUpperPath(const StorePath & path);
|
2023-05-08 17:20:06 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
2023-08-04 00:28:18 +03:00
|
|
|
* Variation of local store using OverlayFS for the store directory.
|
|
|
|
*
|
|
|
|
* Documentation on overridden methods states how they differ from their
|
|
|
|
* `LocalStore` counterparts.
|
2023-05-08 17:20:06 +03:00
|
|
|
*/
|
|
|
|
class LocalOverlayStore : public virtual LocalOverlayStoreConfig, public virtual LocalStore
|
|
|
|
{
|
|
|
|
/**
|
|
|
|
* The store beneath us.
|
|
|
|
*
|
|
|
|
* Our store dir should be an overlay fs where the lower layer
|
|
|
|
* is that store's store dir, and the upper layer is some
|
|
|
|
* scratch storage just for us.
|
|
|
|
*/
|
|
|
|
ref<LocalFSStore> lowerStore;
|
|
|
|
|
|
|
|
public:
|
|
|
|
LocalOverlayStore(const Params & params);
|
|
|
|
|
2024-01-25 17:31:52 +02:00
|
|
|
LocalOverlayStore(std::string_view scheme, PathView path, const Params & params)
|
2023-05-08 17:20:06 +03:00
|
|
|
: LocalOverlayStore(params)
|
|
|
|
{
|
2023-10-25 22:29:11 +03:00
|
|
|
if (!path.empty())
|
|
|
|
throw UsageError("local-overlay:// store url doesn't support path part, only scheme and query params");
|
2023-05-08 17:20:06 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static std::set<std::string> uriSchemes()
|
2023-10-25 22:29:11 +03:00
|
|
|
{
|
|
|
|
return { "local-overlay" };
|
|
|
|
}
|
2023-05-08 17:20:06 +03:00
|
|
|
|
|
|
|
std::string getUri() override
|
|
|
|
{
|
2023-10-25 22:29:11 +03:00
|
|
|
return "local-overlay://";
|
2023-05-08 17:20:06 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
2023-08-04 00:28:18 +03:00
|
|
|
/**
|
|
|
|
* First copy up any lower store realisation with the same key, so we
|
|
|
|
* merge rather than mask it.
|
|
|
|
*/
|
2023-05-08 23:48:55 +03:00
|
|
|
void registerDrvOutput(const Realisation & info) override;
|
2023-05-09 00:30:17 +03:00
|
|
|
|
2023-08-04 00:28:18 +03:00
|
|
|
/**
|
|
|
|
* Check lower store if upper DB does not have.
|
|
|
|
*/
|
2023-05-09 00:30:17 +03:00
|
|
|
void queryPathInfoUncached(const StorePath & path,
|
|
|
|
Callback<std::shared_ptr<const ValidPathInfo>> callback) noexcept override;
|
2023-05-09 01:50:16 +03:00
|
|
|
|
2023-08-04 00:28:18 +03:00
|
|
|
/**
|
|
|
|
* Check lower store if upper DB does not have.
|
|
|
|
*
|
|
|
|
* In addition, copy up metadata for lower store objects (and their
|
|
|
|
* closure). (I.e. Optimistically cache in the upper DB.)
|
|
|
|
*/
|
2023-05-09 01:50:16 +03:00
|
|
|
bool isValidPathUncached(const StorePath & path) override;
|
|
|
|
|
2023-08-04 00:28:18 +03:00
|
|
|
/**
|
|
|
|
* Check the lower store and upper DB.
|
|
|
|
*/
|
2023-05-10 00:20:58 +03:00
|
|
|
void queryReferrers(const StorePath & path, StorePathSet & referrers) override;
|
|
|
|
|
2023-08-04 00:28:18 +03:00
|
|
|
/**
|
|
|
|
* Check the lower store and upper DB.
|
|
|
|
*/
|
2023-05-10 00:20:58 +03:00
|
|
|
StorePathSet queryValidDerivers(const StorePath & path) override;
|
|
|
|
|
2023-08-04 00:28:18 +03:00
|
|
|
/**
|
|
|
|
* Check lower store if upper DB does not have.
|
|
|
|
*/
|
2023-05-09 17:40:10 +03:00
|
|
|
std::optional<StorePath> queryPathFromHashPart(const std::string & hashPart) override;
|
|
|
|
|
2023-08-04 00:28:18 +03:00
|
|
|
/**
|
|
|
|
* First copy up any lower store realisation with the same key, so we
|
|
|
|
* merge rather than mask it.
|
|
|
|
*/
|
2023-05-09 17:22:38 +03:00
|
|
|
void registerValidPaths(const ValidPathInfos & infos) override;
|
|
|
|
|
2023-08-04 00:28:18 +03:00
|
|
|
/**
|
|
|
|
* Check lower store if upper DB does not have.
|
|
|
|
*/
|
2023-05-09 01:50:16 +03:00
|
|
|
void queryRealisationUncached(const DrvOutput&,
|
|
|
|
Callback<std::shared_ptr<const Realisation>> callback) noexcept override;
|
2023-05-24 13:26:33 +03:00
|
|
|
|
2023-08-04 00:28:18 +03:00
|
|
|
/**
|
|
|
|
* Call `remountIfNecessary` after collecting garbage normally.
|
|
|
|
*/
|
2023-07-26 16:05:54 +03:00
|
|
|
void collectGarbage(const GCOptions & options, GCResults & results) override;
|
|
|
|
|
2023-08-04 00:28:18 +03:00
|
|
|
/**
|
|
|
|
* Check which layers the store object exists in to try to avoid
|
|
|
|
* needing to remount.
|
|
|
|
*/
|
2023-07-26 15:29:31 +03:00
|
|
|
void deleteStorePath(const Path & path, uint64_t & bytesFreed) override;
|
2023-07-18 15:49:13 +03:00
|
|
|
|
2023-08-04 00:28:18 +03:00
|
|
|
/**
|
|
|
|
* Deduplicate by removing store objects from the upper layer that
|
|
|
|
* are now in the lower layer.
|
|
|
|
*
|
2023-12-11 20:18:34 +02:00
|
|
|
* Operations on a layered store will not cause duplications, but addition of
|
2023-08-04 00:28:18 +03:00
|
|
|
* new store objects to the lower layer can instill induce them
|
|
|
|
* (there is no way to prevent that). This cleans up those
|
|
|
|
* duplications.
|
|
|
|
*
|
|
|
|
* @note We do not yet optomise the upper layer in the normal way
|
|
|
|
* (hardlink) yet. We would like to, but it requires more
|
|
|
|
* refactoring of existing code to support this sustainably.
|
|
|
|
*/
|
2023-07-18 15:59:22 +03:00
|
|
|
void optimiseStore() override;
|
2023-07-25 17:28:11 +03:00
|
|
|
|
2023-07-28 14:23:42 +03:00
|
|
|
/**
|
|
|
|
* Check all paths registered in the upper DB.
|
|
|
|
*
|
2023-08-01 13:11:16 +03:00
|
|
|
* Note that this includes store objects that reside in either overlayfs layer;
|
|
|
|
* just enumerating the contents of the upper layer would skip them.
|
2023-08-02 21:38:22 +03:00
|
|
|
*
|
2023-08-01 13:11:16 +03:00
|
|
|
* We don't verify the contents of both layers on the assumption that the lower layer is far bigger,
|
|
|
|
* and also the observation that anything not in the upper db the overlayfs doesn't yet care about.
|
2023-07-28 14:23:42 +03:00
|
|
|
*/
|
2023-12-11 20:28:40 +02:00
|
|
|
VerificationResult verifyAllValidPaths(RepairFlag repair) override;
|
2023-07-28 12:59:16 +03:00
|
|
|
|
2023-07-26 00:09:23 +03:00
|
|
|
/**
|
|
|
|
* Deletion only effects the upper layer, so we ignore lower-layer referrers.
|
|
|
|
*/
|
|
|
|
void queryGCReferrers(const StorePath & path, StorePathSet & referrers) override;
|
2023-07-26 19:01:08 +03:00
|
|
|
|
2023-08-04 00:28:18 +03:00
|
|
|
/**
|
|
|
|
* Call the `remountHook` if we have done something such that the
|
|
|
|
* OverlayFS needed to be remounted. See that hook's user-facing
|
|
|
|
* documentation for further details.
|
|
|
|
*/
|
2023-07-26 16:05:54 +03:00
|
|
|
void remountIfNecessary();
|
|
|
|
|
2023-08-04 00:28:18 +03:00
|
|
|
/**
|
|
|
|
* State for `remountIfNecessary`
|
|
|
|
*/
|
2023-07-26 16:44:14 +03:00
|
|
|
std::atomic_bool _remountRequired = false;
|
2023-05-08 17:20:06 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
}
|