From 0d02d0bc51f2fd431f81daa39a970d5dea279f29 Mon Sep 17 00:00:00 2001 From: Philip Withnall Date: Wed, 22 Apr 2026 17:19:21 +0100 Subject: [PATCH] sysupdate: Prevent a possible invalid partial+pending state on an instance MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- src/sysupdate/sysupdate-resource.c | 8 ++++++++ src/sysupdate/sysupdate.c | 5 ++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/sysupdate/sysupdate-resource.c b/src/sysupdate/sysupdate-resource.c index acd9604f29..bd1560371d 100644 --- a/src/sysupdate/sysupdate-resource.c +++ b/src/sysupdate/sysupdate-resource.c @@ -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; } @@ -313,6 +318,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; } diff --git a/src/sysupdate/sysupdate.c b/src/sysupdate/sysupdate.c index 76b6507f9a..2dd1bfdaac 100644 --- a/src/sysupdate/sysupdate.c +++ b/src/sysupdate/sysupdate.c @@ -410,7 +410,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;