diff --git a/src/sysupdate/sysupdate-instance.h b/src/sysupdate/sysupdate-instance.h index 42ba1f1ba9..05b42ac610 100644 --- a/src/sysupdate/sysupdate-instance.h +++ b/src/sysupdate/sysupdate-instance.h @@ -45,8 +45,11 @@ struct Instance { InstanceMetadata metadata; /* Where we found the instance */ - char *path; + char *path; /* includes the `.sysupdate.partial.` (etc.) prefix, if applicable */ PartitionInfo partition_info; + + bool is_partial; + bool is_pending; }; void instance_metadata_destroy(InstanceMetadata *m); diff --git a/src/sysupdate/sysupdate-resource.c b/src/sysupdate/sysupdate-resource.c index b268841899..821854cda8 100644 --- a/src/sysupdate/sysupdate-resource.c +++ b/src/sysupdate/sysupdate-resource.c @@ -80,15 +80,22 @@ static int resource_load_from_directory_recursive( Resource *rr, DIR* d, const char* relpath, - mode_t m) { + const char* relpath_for_matching, + mode_t m, + bool ancestor_is_partial, + bool ancestor_is_pending) { int r; for (;;) { _cleanup_(instance_metadata_destroy) InstanceMetadata extracted_fields = INSTANCE_METADATA_NULL; _cleanup_free_ char *joined = NULL, *rel_joined = NULL; + _cleanup_free_ char *rel_joined_for_matching = NULL; Instance *instance; struct dirent *de; + const char *de_d_name_stripped; struct stat st; + bool is_partial = ancestor_is_partial, is_pending = ancestor_is_pending; + const char *stripped; errno = 0; de = readdir_no_dot(d); @@ -128,11 +135,26 @@ static int resource_load_from_directory_recursive( if (!(S_ISDIR(st.st_mode) && S_ISREG(m)) && ((st.st_mode & S_IFMT) != m)) continue; + if ((stripped = startswith(de->d_name, ".sysupdate.partial."))) { + de_d_name_stripped = stripped; + is_partial = true; + } else if ((stripped = startswith(de->d_name, ".sysupdate.pending."))) { + de_d_name_stripped = stripped; + is_pending = true; + } else + de_d_name_stripped = de->d_name; + rel_joined = path_join(relpath, de->d_name); if (!rel_joined) return log_oom(); - r = pattern_match_many(rr->patterns, rel_joined, &extracted_fields); + /* Match against the filename with any `.sysupdate.partial.` (etc.) prefix stripped, so the + * user’s patterns still apply. But don’t use the stripped version in any paths or recursion. */ + rel_joined_for_matching = path_join(relpath_for_matching, de_d_name_stripped); + if (!rel_joined_for_matching) + return log_oom(); + + r = pattern_match_many(rr->patterns, rel_joined_for_matching, &extracted_fields); if (r == PATTERN_MATCH_RETRY) { _cleanup_closedir_ DIR *subdir = NULL; @@ -140,7 +162,7 @@ static int resource_load_from_directory_recursive( if (!subdir) continue; - r = resource_load_from_directory_recursive(rr, subdir, rel_joined, m); + r = resource_load_from_directory_recursive(rr, subdir, rel_joined, rel_joined_for_matching, m, is_partial, is_pending); if (r < 0) return r; if (r == 0) @@ -168,6 +190,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 */ + + instance->is_partial = is_partial; + instance->is_pending = is_pending; } return 0; @@ -192,7 +217,7 @@ static int resource_load_from_directory( return log_error_errno(errno, "Failed to open directory '%s': %m", rr->path); } - return resource_load_from_directory_recursive(rr, d, NULL, m); + return resource_load_from_directory_recursive(rr, d, NULL, NULL, m, false, false); } static int resource_load_from_blockdev(Resource *rr) { @@ -219,6 +244,9 @@ static int resource_load_from_blockdev(Resource *rr) { _cleanup_(instance_metadata_destroy) InstanceMetadata extracted_fields = INSTANCE_METADATA_NULL; _cleanup_(partition_info_destroy) PartitionInfo pinfo = PARTITION_INFO_NULL; Instance *instance; + const char *pinfo_label_stripped; + bool is_partial = false, is_pending = false; + const char *stripped; r = read_partition_info(c, t, i, &pinfo); if (r < 0) @@ -236,7 +264,18 @@ static int resource_load_from_blockdev(Resource *rr) { continue; } - r = pattern_match_many(rr->patterns, pinfo.label, &extracted_fields); + /* Match the label with any partial/pending prefix removed so the user’s existing patterns + * match regardless of the instance’s state. */ + if ((stripped = startswith(pinfo.label, "PRT#"))) { + pinfo_label_stripped = stripped; + is_partial = true; + } else if ((stripped = startswith(pinfo.label, "PND#"))) { + pinfo_label_stripped = stripped; + is_pending = true; + } else + pinfo_label_stripped = pinfo.label; + + r = pattern_match_many(rr->patterns, pinfo_label_stripped, &extracted_fields); if (r < 0) return log_error_errno(r, "Failed to match pattern: %m"); if (IN_SET(r, PATTERN_MATCH_NO, PATTERN_MATCH_RETRY)) @@ -262,6 +301,9 @@ static int resource_load_from_blockdev(Resource *rr) { if (instance->metadata.read_only < 0) instance->metadata.read_only = instance->partition_info.read_only; + + instance->is_partial = is_partial; + instance->is_pending = is_pending; } return 0; @@ -539,6 +581,11 @@ static int resource_load_from_web( memcpy(instance->metadata.sha256sum, h.iov_base, h.iov_len); instance->metadata.sha256sum_set = true; } + + /* Web resources can only be a source, not a target, so + * can never be partial or pending. */ + instance->is_partial = false; + instance->is_pending = false; } }