From: Stefan Metzmacher Date: Tue, 14 Jul 2026 16:14:33 +0000 (+0200) Subject: s3:lib: add cluster_level related functions to cluster_support.[ch] X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=ea91c14c2064de25256f56587e51ec46eb2df185;p=thirdparty%2Fsamba.git s3:lib: add cluster_level related functions to cluster_support.[ch] This represents our global state related to cluster functional levels. This doesn't really depend on enabled cluserter support, but allows the core code to work without #ifdef's. Signed-off-by: Stefan Metzmacher Reviewed-by: Ralph Boehme --- diff --git a/source3/lib/cluster_support.c b/source3/lib/cluster_support.c index c11b1f76dbb..f8800144446 100644 --- a/source3/lib/cluster_support.c +++ b/source3/lib/cluster_support.c @@ -19,11 +19,14 @@ #include "includes.h" #include #include "cluster_support.h" +#include "librpc/gen_ndr/ndr_cluster_level.h" #ifdef CLUSTER_SUPPORT #include #endif +static void cluster_level_range_asserts(void); + bool cluster_support_available(void) { #ifdef CLUSTER_SUPPORT @@ -33,6 +36,52 @@ bool cluster_support_available(void) #endif } +/* + * Add the newer ranges at the beginning + * CFL_RANGE(__major, __minor_min, __minor_max) + */ +#define CFL_ALL_RANGES \ + CFL_RANGE(1, 0, 0) + +/* + * Cross check for mistakes in the CFL_ALL_RANGES definition + * + * For now we limit major and minor levels to UINT8_MAX, + * but that's not a hard limit as we use uint32_t. + * This should still last a long time with two + * major number changes a year. And the developers + * should think about raising the check... + * + * Also note that level 0.0 is not allowed, + * as it's reserved for unknown level. + * + * If we ever want to support upgrades from + * older versions, the older version needs to + * begin with level 0.1 + */ +#define CFL_RANGE(__major, __minor_min, __minor_max) \ + BUILD_ASSERT( \ + CLUSTER_LEVEL_MAJOR_ ## __major \ + == (__major)); \ + BUILD_ASSERT( \ + CLUSTER_LEVEL_MAJOR_ ## __major ## _MINOR_ ## __minor_min \ + == (__minor_min)); \ + BUILD_ASSERT( \ + CLUSTER_LEVEL_MAJOR_ ## __major ## _MINOR_MIN \ + == (__minor_min)); \ + BUILD_ASSERT( \ + CLUSTER_LEVEL_MAJOR_ ## __major ## _MINOR_ ## __minor_max \ + == (__minor_max)); \ + BUILD_ASSERT( \ + CLUSTER_LEVEL_MAJOR_ ## __major ## _MINOR_MAX \ + == (__minor_max)); \ + BUILD_ASSERT((__minor_min) <= __minor_max); \ + BUILD_ASSERT((__major) < UINT8_MAX); \ + BUILD_ASSERT((__minor_max) < UINT8_MAX); \ + BUILD_ASSERT((__major) != 0 || (__minor_max) != 0); + CFL_ALL_RANGES +#undef CFL_RANGE + const char *cluster_support_features(void) { #define _LINE_DEF(x) " " #x "\n" @@ -50,8 +99,25 @@ const char *cluster_support_features(void) #ifdef CTDB_PROTOCOL _LINE_INT(CTDB_PROTOCOL) #endif + +#ifdef CLUSTER_SUPPORT + " Cluster Functional Levels: \n" +#define CFL_RANGE(__major, __minor_min, __minor_max) \ + " " \ + __STRINGSTRING(__major) \ + "." \ + __STRINGSTRING(__minor_min) \ + " -> " \ + __STRINGSTRING(__major) \ + "." \ + __STRINGSTRING(__minor_max) \ + "\n" + CFL_ALL_RANGES +#undef CFL_RANGE +#endif /* CLUSTER_SUPPORT */ ""; + cluster_level_range_asserts(); return v; } @@ -70,3 +136,277 @@ const char *lp_ctdbd_socket(void) return ""; #endif } + +/* + * Basic checks of the level + */ +bool cluster_level_is_valid(const struct cluster_level_active *level) +{ + cluster_level_range_asserts(); + + if (level == NULL) { + return false; + } + + if (level->major == 0 && level->minor == 0) { + return false; + } + + return true; +} + +/* + * Verify that old and new cluster levels are compatible when updating + */ +bool cluster_level_is_valid_update(const struct cluster_level_active *oldl, + const struct cluster_level_active *newl) +{ + SMB_ASSERT(oldl != NULL); + + if (!cluster_level_is_valid(newl)) { + return false; + } + + if (newl->major < oldl->major) { + return false; + } + + if (newl->major > oldl->major) { + return true; + } + + if (newl->minor < oldl->minor) { + return false; + } + + return true; +} + +/* + * Checks the current active level is supported by the ranges of this node + */ +NTSTATUS cluster_level_compatible( + const struct cluster_level_ranges *ranges, + const struct cluster_level_active *level) +{ + NTSTATUS error = NT_STATUS_UNKNOWN_REVISION; + uint32_t i; + + /* + * level 0.0 will never be supported, + * it's just used to indicate the global + * db record is not there yet. + */ + if (!cluster_level_is_valid(level)) { + return NT_STATUS_INVALID_LEVEL; + } + + for (i = 0; i < ranges->num_ranges; i++) { + const struct cluster_level_range *range = + &ranges->ranges[i]; + + if (level->major != range->major) { + continue; + } + + /* + * At least the major number matched, + * turn any possible error from + * NT_STATUS_UNKNOWN_REVISION into + * NT_STATUS_REVISION_MISMATCH. + */ + error = NT_STATUS_REVISION_MISMATCH; + + if (level->minor < range->minor_min) { + continue; + } + + if (level->minor > range->minor_max) { + continue; + } + + return NT_STATUS_OK; + } + + return error; +} + +static const struct cluster_level_range supported_ranges[] = { +#ifdef CLUSTER_SUPPORT +#define CFL_RANGE(__major, __minor_min, __minor_max) \ + { \ + .major = \ + CLUSTER_LEVEL_MAJOR_ ## __major, \ + .minor_min = \ + CLUSTER_LEVEL_MAJOR_ ## __major ## _MINOR_MIN, \ + .minor_max = \ + CLUSTER_LEVEL_MAJOR_ ## __major ## _MINOR_MAX, \ + }, + CFL_ALL_RANGES +#undef CFL_RANGE +#else /* no CLUSTER_SUPPORT */ + { + /* + * Without cluster support we only support + * the latest level. + */ + .major = CLUSTER_LEVEL_MAJOR_LATEST, + .minor_min = CLUSTER_LEVEL_MAJOR_LATEST_MINOR_MAX, + .minor_max = CLUSTER_LEVEL_MAJOR_LATEST_MINOR_MAX, + }, + +#endif /* no CLUSTER_SUPPORT */ +}; +BUILD_ASSERT(ARRAY_SIZE(supported_ranges) >= 1); + +static const struct cluster_level_ranges supported_levels = { + .num_ranges = ARRAY_SIZE(supported_ranges), + .ranges = discard_const(supported_ranges), +}; + +static void cluster_level_range_asserts(void) +{ + static bool checked; + size_t i; + + if (checked) { + return; + } + + /* + * The following don't work as BUILD_ASSERT(), + * so we use SMB_ASSERT(), so that + * any make test fails immediately + * + * The ranges should be sorted with the + * latest level first. + */ + SMB_ASSERT(supported_ranges[0].major == + CLUSTER_LEVEL_MAJOR_LATEST); + SMB_ASSERT(supported_ranges[0].minor_max == + CLUSTER_LEVEL_MAJOR_LATEST_MINOR_MAX); + for (i = 1; i < ARRAY_SIZE(supported_ranges); i++) { + SMB_ASSERT(supported_ranges[i-1].major > + supported_ranges[i].major); + } + + SMB_ASSERT(supported_levels.num_ranges == ARRAY_SIZE(supported_ranges)); + SMB_ASSERT(supported_levels.ranges == supported_ranges); + + checked = true; +} + +/* + * Returns the cluster_level_ranges supported by this node + */ +const struct cluster_level_ranges *cluster_level_supported_ranges(void) +{ + cluster_level_range_asserts(); + + return &supported_levels; +} + +/* + * The global functional level after we've learned it from the cluster_level.db + */ +static struct cluster_level_active global_level_activated; + +/* + * Check the global functional level is valid + */ +bool cluster_level_global_is_valid(void) +{ + return cluster_level_is_valid(&global_level_activated); +} + +/* + * Activate the functional level + * + * This gets used by callers after they've learnt the functional level + * from cluster_level.db. Remembers the level in a local variable. + */ +void cluster_level_activate(const struct cluster_level_active *level) +{ + /* + * only upgrades are possible + */ + SMB_ASSERT(lp_clustering()); + SMB_ASSERT(cluster_level_is_valid(level)); + SMB_ASSERT(cluster_level_is_valid_update(&global_level_activated, level)); + + global_level_activated = *level; + + /* + * We should have at least the oldest version we support + * for the clustered case. + */ + SMB_ASSERT(CLUSTER_LEVEL_ACTIVE(1, 0)); +} + +/* + * Can be used to set the active level to the latest supported level + */ +void cluster_level_activate_latest(void) +{ + global_level_activated = (struct cluster_level_active) { + .major = supported_ranges[0].major, + .minor = supported_ranges[0].minor_max, + }; + + /* + * We should have the latest version we support + * in the non-clustered case. + */ + SMB_ASSERT(CLUSTER_LEVEL_ACTIVE(LATEST, MAX)); +} + +/* + * This will allow code like this: + * + * if (CLUSTER_LEVEL_ACTIVATED(5, 2)) { + * // handle 5.2 + * } else if (CLUSTER_LEVEL_ACTIVATED(3, 0)) { + * // handle 3.0 + * } else if (CLUSTER_LEVEL_ACTIVATED(1, 0)) { + * // handle 1.0 + * } else if (CLUSTER_LEVEL_ACTIVATED(0, 1)) { + * // handle 0.1 + * } else { + * // error!!! 0.0 should never be active + * } + */ +bool _cluster_level_activated(uint32_t major, uint32_t minor) +{ + /* + * cluster_level_activate_latest() or + * cluster_level_activate() should be called + * already! + */ + SMB_ASSERT(cluster_level_global_is_valid()); + + if (global_level_activated.major > major) { + /* + * 7.0 is active and the caller + * checks if 5.2 features are available. + */ + return true; + } + + if (global_level_activated.major < major) { + /* + * 3.0 is active and the caller + * checks if 5.2 features are available. + */ + return false; + } + + if (global_level_activated.minor >= minor) { + /* + * 5.3 is active and the caller + * checks if 5.2 features are available. + */ + return true; + } + + return false; +} diff --git a/source3/lib/cluster_support.h b/source3/lib/cluster_support.h index 5e040bb2d87..cd7c81e83a2 100644 --- a/source3/lib/cluster_support.h +++ b/source3/lib/cluster_support.h @@ -16,6 +16,36 @@ along with this program. If not, see . */ +struct cluster_level_active; +struct cluster_level_ranges; + bool cluster_support_available(void); const char *cluster_support_features(void); const char *lp_ctdbd_socket(void); + +bool cluster_level_is_valid(const struct cluster_level_active *level); +bool cluster_level_is_valid_update(const struct cluster_level_active *oldl, + const struct cluster_level_active *newl); +NTSTATUS cluster_level_compatible( + const struct cluster_level_ranges *ranges, + const struct cluster_level_active *level); + +const struct cluster_level_ranges *cluster_level_supported_ranges(void); + +bool cluster_level_global_is_valid(void); +void cluster_level_activate(const struct cluster_level_active *level); +void cluster_level_activate_latest(void); + +/* + * We use checks like 'if (CLUSTER_LEVEL_ACTIVE(1, 0))' in + * order to avoid long lines when using the + * define explicitly. + * + * But we use the defines implicitly in order + * to avoid missing defines in the idl file. + */ +#define CLUSTER_LEVEL_ACTIVE(__major, __minor) \ + _cluster_level_activated( \ + CLUSTER_LEVEL_MAJOR_ ## __major, \ + CLUSTER_LEVEL_MAJOR_ ## __major ## _MINOR_ ## __minor) +bool _cluster_level_activated(uint32_t major, uint32_t minor);