diff --git a/src/shared/image-policy.c b/src/shared/image-policy.c index b5f9e699d6..813dc04f95 100644 --- a/src/shared/image-policy.c +++ b/src/shared/image-policy.c @@ -785,21 +785,35 @@ static bool partition_policy_flags_has_unspecified(PartitionPolicyFlags flags) { return false; } -int image_policy_intersect(const ImagePolicy *a, const ImagePolicy *b, ImagePolicy **ret) { +static PartitionPolicyFlags policy_flags_or(PartitionPolicyFlags a, PartitionPolicyFlags b) { + return a | b; +} + +static PartitionPolicyFlags policy_flags_and(PartitionPolicyFlags a, PartitionPolicyFlags b) { + return a & b; +} + +static int policy_intersect_or_union( + const ImagePolicy *a, + const ImagePolicy *b, + PartitionPolicyFlags (*op)(PartitionPolicyFlags a, PartitionPolicyFlags b), + ImagePolicy **ret) { + _cleanup_(image_policy_freep) ImagePolicy *p = NULL; - /* Calculates the intersection of the specified policies, i.e. only what is permitted in both. This - * might fail with -ENAVAIL if the intersection is an "impossible policy". For example, if a root - * partition my neither be used, nor be absent, nor be unused then this is considered - * "impossible". */ + assert(op); + + /* Calculates the intersection or union of the specified policies, i.e. only what is permitted in + * both or either. This might fail with -ENAVAIL if the intersection is an "impossible policy". For + * example, if a root partition my neither be used, nor be absent, nor be unused then this is + * considered "impossible". */ p = image_policy_new(_PARTITION_DESIGNATOR_MAX); if (!p) return -ENOMEM; - p->default_flags = - partition_policy_flags_extend(image_policy_default(a)) & - partition_policy_flags_extend(image_policy_default(b)); + p->default_flags = op(partition_policy_flags_extend(image_policy_default(a)), + partition_policy_flags_extend(image_policy_default(b))); if (partition_policy_flags_has_unspecified(p->default_flags)) /* Intersection empty? */ return -ENAVAIL; @@ -824,10 +838,12 @@ int image_policy_intersect(const ImagePolicy *a, const ImagePolicy *b, ImagePoli return y; /* Mask it */ - z = x & y; + z = op(x, y); - /* Check if the intersection is empty for this partition. If so, generate a clear error */ - if (partition_policy_flags_has_unspecified(z)) + /* Check if the intersection is empty for this partition. If so, generate a clear error. + * If the partition has to be absent, then it won't have read-only/growfs flags, as + * image_policy_get_exhaustively() intentionally strips them, so skip the check. */ + if (z != PARTITION_POLICY_ABSENT && partition_policy_flags_has_unspecified(z)) return -ENAVAIL; df = partition_policy_normalized_flags( @@ -857,6 +873,14 @@ int image_policy_intersect(const ImagePolicy *a, const ImagePolicy *b, ImagePoli return 0; } +int image_policy_intersect(const ImagePolicy *a, const ImagePolicy *b, ImagePolicy **ret) { + return policy_intersect_or_union(a, b, policy_flags_and, ret); +} + +int image_policy_union(const ImagePolicy *a, const ImagePolicy *b, ImagePolicy **ret) { + return policy_intersect_or_union(a, b, policy_flags_or, ret); +} + ImagePolicy* image_policy_free(ImagePolicy *p) { return mfree(p); } diff --git a/src/shared/image-policy.h b/src/shared/image-policy.h index 218d2c5beb..c40192931b 100644 --- a/src/shared/image-policy.h +++ b/src/shared/image-policy.h @@ -111,6 +111,7 @@ bool image_policy_equal(const ImagePolicy *a, const ImagePolicy *b); /* ch int image_policy_equivalent(const ImagePolicy *a, const ImagePolicy *b); /* checks if the outcome is the same, i.e. for all partitions results in the same decisions. */ int image_policy_intersect(const ImagePolicy *a, const ImagePolicy *b, ImagePolicy **ret); +int image_policy_union(const ImagePolicy *a, const ImagePolicy *b, ImagePolicy **ret); ImagePolicy* image_policy_free(ImagePolicy *p); diff --git a/src/test/test-image-policy.c b/src/test/test-image-policy.c index f285d31d9e..12f7fc51e0 100644 --- a/src/test/test-image-policy.c +++ b/src/test/test-image-policy.c @@ -142,14 +142,17 @@ TEST(extend) { assert_se(partition_policy_flags_extend(PARTITION_POLICY_GROWFS_ON) == (PARTITION_POLICY_GROWFS_ON|_PARTITION_POLICY_USE_MASK|_PARTITION_POLICY_READ_ONLY_MASK)); } -static void test_policy_intersect_one(const char *a, const char *b, const char *c) { +static void test_policy_intersect_one(const char *a, const char *b, const char *c, bool intersect) { _cleanup_(image_policy_freep) ImagePolicy *x = NULL, *y = NULL, *z = NULL, *t = NULL; assert_se(image_policy_from_string(a, /* graceful= */ false, &x) >= 0); assert_se(image_policy_from_string(b, /* graceful= */ false, &y) >= 0); assert_se(image_policy_from_string(c, /* graceful= */ false, &z) >= 0); - assert_se(image_policy_intersect(x, y, &t) >= 0); + if (intersect) + assert_se(image_policy_intersect(x, y, &t) >= 0); + else + assert_se(image_policy_union(x, y, &t) >= 0); _cleanup_free_ char *s1 = NULL, *s2 = NULL, *s3 = NULL, *s4 = NULL; assert_se(image_policy_to_string(x, false, &s1) >= 0); @@ -157,21 +160,33 @@ static void test_policy_intersect_one(const char *a, const char *b, const char * assert_se(image_policy_to_string(z, false, &s3) >= 0); assert_se(image_policy_to_string(t, false, &s4) >= 0); - log_info("%s ^ %s → %s vs. %s", s1, s2, s3, s4); + log_info("%s %s %s → %s vs. %s", s1, intersect ? "^" : "U", s2, s3, s4); assert_se(image_policy_equivalent(z, t) > 0); } TEST(image_policy_intersect) { - test_policy_intersect_one("", "", ""); - test_policy_intersect_one("-", "-", "-"); - test_policy_intersect_one("*", "*", "*"); - test_policy_intersect_one("~", "~", "~"); - test_policy_intersect_one("root=verity+signed", "root=signed+verity", "root=verity+signed"); - test_policy_intersect_one("root=verity+signed", "root=signed", "root=signed"); - test_policy_intersect_one("root=verity+signed", "root=verity", "root=verity"); - test_policy_intersect_one("root=open", "root=verity", "root=verity"); - test_policy_intersect_one("root=open", "=verity+ignore", "root=verity+ignore:=ignore"); + test_policy_intersect_one("", "", "", /* intersect= */ true); + test_policy_intersect_one("-", "-", "-", /* intersect= */ true); + test_policy_intersect_one("*", "*", "*", /* intersect= */ true); + test_policy_intersect_one("~", "~", "~", /* intersect= */ true); + test_policy_intersect_one("root=verity+signed", "root=signed+verity", "root=verity+signed", /* intersect= */ true); + test_policy_intersect_one("root=verity+signed", "root=signed", "root=signed", /* intersect= */ true); + test_policy_intersect_one("root=verity+signed", "root=verity", "root=verity", /* intersect= */ true); + test_policy_intersect_one("root=open", "root=verity", "root=verity", /* intersect= */ true); + test_policy_intersect_one("root=open", "=verity+ignore", "root=verity+ignore:=ignore", /* intersect= */ true); +} + +TEST(image_policy_union) { + test_policy_intersect_one("root=verity+signed", "root=signed+verity", "root=verity+signed", /* intersect= */ false); + test_policy_intersect_one("root=verity+signed", "root=signed", "root=verity+signed", /* intersect= */ false); + test_policy_intersect_one("root=verity+signed", "root=verity", "root=verity+signed", /* intersect= */ false); + test_policy_intersect_one("root=signed", "root=verity", "root=verity+signed", /* intersect= */ false); + test_policy_intersect_one("root=signed:=absent", "root=verity:=unused", "root=verity+signed", /* intersect= */ false); + test_policy_intersect_one("root=open", "root=verity", "root=open", /* intersect= */ false); + test_policy_intersect_one("root=open", "=verity+ignore", "root=open:=verity+ignore", /* intersect= */ false); + test_policy_intersect_one("root=open:usr=absent", "root=open:usr=absent", "root=open:usr=absent", /* intersect= */ false); + } static void test_policy_ignore_designators_one(const char *a, const PartitionDesignator array[], size_t n, const char *b) {