]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
sysupdate: Prevent a possible invalid partial+pending state on an instance
authorPhilip Withnall <pwithnall@gnome.org>
Wed, 22 Apr 2026 16:19:21 +0000 (17:19 +0100)
committerLuca Boccassi <luca.boccassi@gmail.com>
Fri, 22 May 2026 12:57:29 +0000 (13:57 +0100)
If the resource code is recursing, it’s possible for one iteration to
set a partial flag, and then a recursive iteration to set a pending flag
(or vice-versa). It doesn’t make sense to have both set at the same time
for a specific instance, so make sure to clear the other flag when
setting one of them.

Add some assertions to make this invariant clearer and easier to debug
if it fails.

Signed-off-by: Philip Withnall <pwithnall@gnome.org>
(cherry picked from commit 0d02d0bc51f2fd431f81daa39a970d5dea279f29)

src/sysupdate/sysupdate-resource.c
src/sysupdate/sysupdate.c

index 3be0943e4c0a3dd59fd1384b90330b22605fe2dc..c138707a868f70ad4e21971daf3534d795a4e749 100644 (file)
@@ -139,9 +139,11 @@ static int resource_load_from_directory_recursive(
                 if ((stripped = startswith(de->d_name, ".sysupdate.partial."))) {
                         de_d_name_stripped = stripped;
                         is_partial = true;
+                        is_pending = false;
                 } else if ((stripped = startswith(de->d_name, ".sysupdate.pending."))) {
                         de_d_name_stripped = stripped;
                         is_pending = true;
+                        is_partial = false;
                 } else
                         de_d_name_stripped = de->d_name;
 
@@ -192,6 +194,9 @@ static int resource_load_from_directory_recursive(
                 if (instance->metadata.mode == MODE_INVALID)
                         instance->metadata.mode = st.st_mode & 0775; /* mask out world-writability and suid and stuff, for safety */
 
+                /* Can’t be both partial and pending. */
+                assert(!(is_partial && is_pending));
+
                 instance->is_partial = is_partial;
                 instance->is_pending = is_pending;
         }
@@ -309,6 +314,9 @@ static int resource_load_from_blockdev(Resource *rr) {
                 if (instance->metadata.read_only < 0)
                         instance->metadata.read_only = instance->partition_info.read_only;
 
+                /* Can’t be both partial and pending. */
+                assert(!(is_partial && is_pending));
+
                 instance->is_partial = is_partial;
                 instance->is_pending = is_pending;
         }
index 69848c3fcb7ebdd4d59cfa436453ad643a1d2cc4..77e379cf2a4ea6107b3c62e31122fb8ddcfefa6b 100644 (file)
@@ -414,7 +414,10 @@ static int context_discover_update_sets_by_flag(Context *c, UpdateSetFlags flags
                                 extra_flags |= UPDATE_PROTECTED;
 
                         /* Partial or pending updates by definition are not incomplete, they’re
-                         * partial/pending instead */
+                         * partial/pending instead. While an individual Instance cannot be both partial and
+                         * pending, an UpdateSet as a whole can contain both partial and pending instances. */
+                        assert(!match || !(match->is_partial && match->is_pending));
+
                         if (match && match->is_partial)
                                 extra_flags = (extra_flags | UPDATE_PARTIAL) & ~UPDATE_INCOMPLETE;