From a3ab9d2c1a604201b65789427ec914af3e0f92a2 Mon Sep 17 00:00:00 2001 From: dongshengyuan <545258830@qq.com> Date: Wed, 5 Aug 2026 12:15:19 +0800 Subject: [PATCH 1/6] sysupdate: honor default MatchPartitionType= for partitions sysupdate.d(5) documents linux-generic as the default MatchPartitionType= for partition targets. After 10eaca4159, omitting the option left partition_type_set false and update failed before acquiring the image. Reproducer: create a partition target transfer without MatchPartitionType= and run systemd-sysupdate update. Before: update failed with: Partition type must be set for partition targets. After: update succeeds on a linux-generic slot and installs the version, leaving the GPT label as part-v1. Follow-up for: 10eaca4159a93485054c4e7c7c35ce16114591d7 --- src/sysupdate/sysupdate-transfer.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/sysupdate/sysupdate-transfer.c b/src/sysupdate/sysupdate-transfer.c index e80dcaf39a..36678cdccd 100644 --- a/src/sysupdate/sysupdate-transfer.c +++ b/src/sysupdate/sysupdate-transfer.c @@ -608,6 +608,11 @@ int transfer_read_definition(Transfer *t, const char *path, const char **dirs, H return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL), "Target Type= must be one of partition, regular-file, directory, subvolume."); + if (t->target.type == RESOURCE_PARTITION && !t->target.partition_type_set) { + t->target.partition_type = gpt_partition_type_from_uuid(SD_GPT_LINUX_GENERIC); + t->target.partition_type_set = true; + } + if ((IN_SET(t->source.type, RESOURCE_URL_FILE, RESOURCE_PARTITION, RESOURCE_REGULAR_FILE) && !IN_SET(t->target.type, RESOURCE_PARTITION, RESOURCE_REGULAR_FILE)) || (IN_SET(t->source.type, RESOURCE_URL_TAR, RESOURCE_TAR, RESOURCE_DIRECTORY, RESOURCE_SUBVOLUME) && @@ -1269,8 +1274,7 @@ int transfer_compute_temporary_paths(Transfer *t, Instance *i, InstanceMetadata if (!r) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Formatted pattern is not suitable as GPT partition label, refusing: %s", formatted_pattern); - if (!t->target.partition_type_set) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Partition type must be set for partition targets."); + assert(t->target.partition_type_set); /* Derive temporary partition type UUIDs for partial/pending states from the configured * partition type. This avoids the need for label prefixes. */ From 09f8b59ac3037962cb6f9d9d9bff5c61e1218e81 Mon Sep 17 00:00:00 2001 From: dongshengyuan <545258830@qq.com> Date: Wed, 5 Aug 2026 16:01:46 +0800 Subject: [PATCH 2/6] sysupdate: remove current partial instance names 48fff6a27a switched filesystem transfers to predictable .sysupdate.partial.* names, but transfer_remove_temporary() still only scanned the legacy .# prefix. Remove the current partial prefix there too. Pending instances are deliberately left for transfer_vacuum(), so ProtectVersion= can still spare protected pending versions. Follow-up for: 48fff6a27aab434035d5ead65d48a6f860f5b656 --- src/sysupdate/sysupdate-transfer.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/sysupdate/sysupdate-transfer.c b/src/sysupdate/sysupdate-transfer.c index 36678cdccd..3f5f717962 100644 --- a/src/sysupdate/sysupdate-transfer.c +++ b/src/sysupdate/sysupdate-transfer.c @@ -735,7 +735,8 @@ static void transfer_remove_temporary(Transfer *t) { if (!IN_SET(t->target.type, RESOURCE_REGULAR_FILE, RESOURCE_DIRECTORY, RESOURCE_SUBVOLUME)) return; - /* Removes all temporary files/dirs from previous runs in the target directory, i.e. all those starting with '.#' */ + /* Removes all incomplete files/dirs from previous runs in the target directory. Clean both the + * current partial sysupdate prefix and the legacy '.#' prefix. */ d = opendir(t->target.path); if (!d) { @@ -757,7 +758,7 @@ static void transfer_remove_temporary(Transfer *t) { break; } - if (!startswith(de->d_name, ".#")) + if (!STARTSWITH_SET(de->d_name, ".#", ".sysupdate.partial.")) continue; r = rm_rf_child(dirfd(d), de->d_name, REMOVE_PHYSICAL|REMOVE_SUBVOLUME|REMOVE_CHMOD); From 073965ab2f1785ac7b37988a94ffd34b5ac738e6 Mon Sep 17 00:00:00 2001 From: dongshengyuan <545258830@qq.com> Date: Wed, 5 Aug 2026 12:16:38 +0800 Subject: [PATCH 3/6] sysupdate: parse PartitionFlags= as hexadecimal sysupdate.d(5) documents PartitionFlags= as a hexadecimal integer, and sysupdate patterns format partition flags with PRIx64. The parser used safe_atou64(), which rejected values such as abcdef. Reproducer: set PartitionFlags=abcdef in a partition target transfer and run systemd-sysupdate list with debug logging. Before: sysupdate logged: Failed to parse partition flags, ignoring: abcdef After: the transfer parses cleanly and list emits no partition-flags parse warning. Follow-up for: 43cc7a3ef4f6a89946e7ffd6a3112a0c1740b1ef --- src/sysupdate/sysupdate-transfer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sysupdate/sysupdate-transfer.c b/src/sysupdate/sysupdate-transfer.c index 3f5f717962..b824d29471 100644 --- a/src/sysupdate/sysupdate-transfer.c +++ b/src/sysupdate/sysupdate-transfer.c @@ -471,7 +471,7 @@ static int config_parse_partition_flags( assert(rvalue); - r = safe_atou64(rvalue, &t->partition_flags); + r = safe_atoux64(rvalue, &t->partition_flags); if (r < 0) { log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse partition flags, ignoring: %s", rvalue); From 753b0a6e240303711c6eaf525286c81b9c61b1ae Mon Sep 17 00:00:00 2001 From: dongshengyuan <545258830@qq.com> Date: Wed, 5 Aug 2026 12:17:10 +0800 Subject: [PATCH 4/6] sysupdate: allow resetting version filters systemd drop-ins use an empty assignment to reset list and scalar settings. MinVersion= and ProtectVersion= treated an empty assignment as an invalid version and kept the earlier value active. Reproducer: define MinVersion=v2 and ProtectVersion=v1, then add a drop-in with empty MinVersion= and ProtectVersion= assignments. Before: sysupdate logged invalid-version warnings for the empty values. After: list logs no invalid-version warnings and shows both v1 and v2. Follow-up for: 43cc7a3ef4f6a89946e7ffd6a3112a0c1740b1ef --- src/sysupdate/sysupdate-transfer.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/sysupdate/sysupdate-transfer.c b/src/sysupdate/sysupdate-transfer.c index b824d29471..a03bbbb63f 100644 --- a/src/sysupdate/sysupdate-transfer.c +++ b/src/sysupdate/sysupdate-transfer.c @@ -134,6 +134,11 @@ static int config_parse_protect_version( assert(rvalue); + if (isempty(rvalue)) { + *protected_versions = strv_free(*protected_versions); + return 0; + } + r = specifier_printf(rvalue, NAME_MAX, system_and_tmp_specifier_table, t->context->root, NULL, &resolved); if (r < 0) { log_syntax(unit, LOG_WARNING, filename, line, r, @@ -173,6 +178,11 @@ static int config_parse_min_version( assert(rvalue); + if (isempty(rvalue)) { + *version = mfree(*version); + return 0; + } + r = specifier_printf(rvalue, NAME_MAX, system_and_tmp_specifier_table, t->context->root, NULL, &resolved); if (r < 0) { log_syntax(unit, LOG_WARNING, filename, line, r, @@ -415,6 +425,12 @@ static int config_parse_resource_ptype( assert(rvalue); + if (isempty(rvalue)) { + rr->partition_type = (GptPartitionType) {}; + rr->partition_type_set = false; + return 0; + } + r = gpt_partition_type_from_string(rvalue, &rr->partition_type); if (r < 0) { log_syntax(unit, LOG_WARNING, filename, line, r, @@ -443,6 +459,12 @@ static int config_parse_partition_uuid( assert(rvalue); + if (isempty(rvalue)) { + t->partition_uuid = SD_ID128_NULL; + t->partition_uuid_set = false; + return 0; + } + r = sd_id128_from_string(rvalue, &t->partition_uuid); if (r < 0) { log_syntax(unit, LOG_WARNING, filename, line, r, @@ -471,6 +493,12 @@ static int config_parse_partition_flags( assert(rvalue); + if (isempty(rvalue)) { + t->partition_flags = 0; + t->partition_flags_set = false; + return 0; + } + r = safe_atoux64(rvalue, &t->partition_flags); if (r < 0) { log_syntax(unit, LOG_WARNING, filename, line, r, From 215c4383724f38b1566ee29c0a2e6f65282dca68 Mon Sep 17 00:00:00 2001 From: dongshengyuan <545258830@qq.com> Date: Wed, 5 Aug 2026 12:17:40 +0800 Subject: [PATCH 5/6] sysupdate: allow long metadata URLs 138829b784 moved Documentation=, AppStream=, and related URL parsing into sysupdate-config.c, but kept NAME_MAX as the specifier expansion limit. These settings are URLs, not filenames. Reproducer: configure a feature Documentation= URL longer than NAME_MAX and run systemd-sysupdate features FEATURE. Before: the URL was ignored after ENAMETOOLONG during specifier expansion. After: features FEATURE prints the long Documentation URL. Follow-up for: 138829b7843c6f744764d72d2c995005bfe6dc00 --- src/sysupdate/sysupdate-config.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sysupdate/sysupdate-config.c b/src/sysupdate/sysupdate-config.c index 45794b5a21..1b4a025228 100644 --- a/src/sysupdate/sysupdate-config.c +++ b/src/sysupdate/sysupdate-config.c @@ -34,7 +34,7 @@ int config_parse_url_specifiers( } _cleanup_free_ char *resolved = NULL; - r = specifier_printf(rvalue, NAME_MAX, system_and_tmp_specifier_table, root, NULL, &resolved); + r = specifier_printf(rvalue, SIZE_MAX, system_and_tmp_specifier_table, root, NULL, &resolved); if (r < 0) { log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to expand specifiers in %s=, ignoring: %s", lvalue, rvalue); @@ -90,7 +90,7 @@ int config_parse_url_specifiers_many( } _cleanup_free_ char *resolved = NULL; - r = specifier_printf(rvalue, NAME_MAX, system_and_tmp_specifier_table, root, NULL, &resolved); + r = specifier_printf(rvalue, SIZE_MAX, system_and_tmp_specifier_table, root, NULL, &resolved); if (r < 0) { log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to expand specifiers in %s=, ignoring: %s", lvalue, rvalue); From a3839ade4ea27ca7a8c5c5a02d10b74dc9e54d96 Mon Sep 17 00:00:00 2001 From: dongshengyuan <545258830@qq.com> Date: Wed, 5 Aug 2026 12:18:03 +0800 Subject: [PATCH 6/6] sysupdate: allow long CurrentSymlink= paths CurrentSymlink= accepts a symlink path, but the parser used NAME_MAX for specifier expansion. Absolute and target-relative paths can be longer than one filename component while still staying below PATH_MAX. Reproducer: configure CurrentSymlink= to a path longer than NAME_MAX and run systemd-sysupdate list with debug logging. Before: sysupdate logged that CurrentSymlink= specifier expansion failed and ignored the setting. After: the transfer parses cleanly and list emits no CurrentSymlink= specifier expansion warning. Follow-up for: 43cc7a3ef4f6a89946e7ffd6a3112a0c1740b1ef --- src/sysupdate/sysupdate-transfer.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/sysupdate/sysupdate-transfer.c b/src/sysupdate/sysupdate-transfer.c index a03bbbb63f..226dbd93ae 100644 --- a/src/sysupdate/sysupdate-transfer.c +++ b/src/sysupdate/sysupdate-transfer.c @@ -238,7 +238,12 @@ static int config_parse_current_symlink( assert(rvalue); - r = specifier_printf(rvalue, NAME_MAX, system_and_tmp_specifier_table, t->context->root, NULL, &resolved); + if (isempty(rvalue)) { + *current_symlink = mfree(*current_symlink); + return 0; + } + + r = specifier_printf(rvalue, PATH_MAX-1, system_and_tmp_specifier_table, t->context->root, NULL, &resolved); if (r < 0) { log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to expand specifiers in CurrentSymlink=, ignoring: %s", rvalue);