mirror of
https://github.com/systemd/systemd.git
synced 2024-12-22 17:35:35 +03:00
sysupdate: Repair incomplete versions in-place
A previous commit made sysupdate recognize installed versions where some transfers are missing. This commit teaches sysupdate how to correctly repair these incomplete versions. Previously, if you had a incomplete installation of the OS booted, and ran sysupdate in an attempt to repair it, sysupdate would make things worse by creating copies of the currently-booted partitions in the inactive slots. Then at boot you have two identical partitions, with identical labels an UUIDs, and end up with a mess. With this commit, sysupdate is able to recognize situations where it can simply download the missing transfers and leave the rest of the system undistrubed. Partial fix for https://github.com/systemd/systemd/issues/33339
This commit is contained in:
parent
57ada07e7a
commit
631803cccd
@ -226,7 +226,8 @@ node /org/freedesktop/sysupdate1/target/host {
|
||||
<term><literal>incomplete</literal></term>
|
||||
<listitem><para>A boolean indicating whether this version is incomplete, which means that it is
|
||||
missing some file. Note that only installed incomplete versions will be offered by the service;
|
||||
versions that are incomplete on the server-side are completely ignored.</para></listitem>
|
||||
versions that are incomplete on the server-side are completely ignored. Incomplete versions can
|
||||
be repaired in-place by calling <function>Update()</function> on that version.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
|
@ -1075,8 +1075,7 @@ int transfer_acquire_instance(Transfer *t, Instance *i, TransferProgress cb, voi
|
||||
|
||||
assert(t);
|
||||
assert(i);
|
||||
assert(i->resource);
|
||||
assert(t == container_of(i->resource, Transfer, source));
|
||||
assert(i->resource == &t->source);
|
||||
assert(cb);
|
||||
|
||||
/* Does this instance already exist in the target? Then we don't need to acquire anything */
|
||||
|
@ -325,11 +325,36 @@ static int context_discover_update_sets_by_flag(Context *c, UpdateSetFlags flags
|
||||
/* See if we already have this update set in our table */
|
||||
FOREACH_ARRAY(update_set, c->update_sets, c->n_update_sets) {
|
||||
UpdateSet *u = *update_set;
|
||||
|
||||
if (strverscmp_improved(u->version, cursor) != 0)
|
||||
continue;
|
||||
|
||||
/* We only store the instances we found first, but we remember we also found it again */
|
||||
/* Merge in what we've learned and continue onto the next version */
|
||||
|
||||
if (FLAGS_SET(u->flags, UPDATE_INCOMPLETE)) {
|
||||
assert(u->n_instances == c->n_transfers);
|
||||
|
||||
/* Incomplete updates will have picked NULL instances for the transfers that
|
||||
* are missing. Now we have more information, so let's try to fill them in. */
|
||||
|
||||
for (size_t j = 0; j < u->n_instances; j++) {
|
||||
if (!u->instances[j])
|
||||
u->instances[j] = cursor_instances[j];
|
||||
|
||||
/* Make sure that the list is full if the update is AVAILABLE */
|
||||
assert(flags != UPDATE_AVAILABLE || u->instances[j]);
|
||||
}
|
||||
}
|
||||
|
||||
u->flags |= flags | extra_flags;
|
||||
|
||||
/* If this is the newest installed version, that is incomplete and just became marked
|
||||
* as available, and if there is no other candidate available, we promote this to be
|
||||
* the candidate. */
|
||||
if (FLAGS_SET(u->flags, UPDATE_NEWEST|UPDATE_INSTALLED|UPDATE_INCOMPLETE|UPDATE_AVAILABLE) &&
|
||||
!c->candidate && !FLAGS_SET(u->flags, UPDATE_OBSOLETE))
|
||||
c->candidate = u;
|
||||
|
||||
skip = true;
|
||||
newest_found = true;
|
||||
break;
|
||||
@ -367,7 +392,8 @@ static int context_discover_update_sets_by_flag(Context *c, UpdateSetFlags flags
|
||||
}
|
||||
|
||||
/* Newest installed is newer than or equal to candidate? Then suppress the candidate */
|
||||
if (c->newest_installed && c->candidate && strverscmp_improved(c->newest_installed->version, c->candidate->version) >= 0)
|
||||
if (c->newest_installed && !FLAGS_SET(c->newest_installed->flags, UPDATE_INCOMPLETE) &&
|
||||
c->candidate && strverscmp_improved(c->newest_installed->version, c->candidate->version) >= 0)
|
||||
c->candidate = NULL;
|
||||
|
||||
return 0;
|
||||
@ -736,6 +762,10 @@ static int context_vacuum(
|
||||
FOREACH_ARRAY(tr, c->transfers, c->n_transfers) {
|
||||
Transfer *t = *tr;
|
||||
|
||||
/* Don't bother clearing out space if we're not going to be downloading anything */
|
||||
if (extra_protected_version && resource_find_instance(&t->target, extra_protected_version))
|
||||
continue;
|
||||
|
||||
r = transfer_vacuum(t, space, extra_protected_version);
|
||||
if (r < 0)
|
||||
return r;
|
||||
@ -866,7 +896,9 @@ static int context_apply(
|
||||
us = c->candidate;
|
||||
}
|
||||
|
||||
if (FLAGS_SET(us->flags, UPDATE_INSTALLED)) {
|
||||
if (FLAGS_SET(us->flags, UPDATE_INCOMPLETE))
|
||||
log_info("Selected update '%s' is already installed, but incomplete. Repairing.", us->version);
|
||||
else if (FLAGS_SET(us->flags, UPDATE_INSTALLED)) {
|
||||
log_info("Selected update '%s' is already installed. Skipping update.", us->version);
|
||||
|
||||
if (ret_applied)
|
||||
@ -874,13 +906,12 @@ static int context_apply(
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!FLAGS_SET(us->flags, UPDATE_AVAILABLE))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Selected update '%s' is not available, refusing.", us->version);
|
||||
if (FLAGS_SET(us->flags, UPDATE_OBSOLETE))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Selected update '%s' is obsolete, refusing.", us->version);
|
||||
|
||||
assert((us->flags & (UPDATE_AVAILABLE|UPDATE_INSTALLED|UPDATE_OBSOLETE)) == UPDATE_AVAILABLE);
|
||||
|
||||
if (!FLAGS_SET(us->flags, UPDATE_NEWEST))
|
||||
log_notice("Selected update '%s' is not the newest, proceeding anyway.", us->version);
|
||||
if (c->newest_installed && strverscmp_improved(c->newest_installed->version, us->version) > 0)
|
||||
@ -913,8 +944,17 @@ static int context_apply(
|
||||
assert(us->n_instances == c->n_transfers);
|
||||
|
||||
for (size_t i = 0; i < c->n_transfers; i++) {
|
||||
r = transfer_acquire_instance(c->transfers[i], us->instances[i],
|
||||
context_on_acquire_progress, c);
|
||||
Instance *inst = us->instances[i];
|
||||
Transfer *t = c->transfers[i];
|
||||
|
||||
assert(inst); /* ditto */
|
||||
|
||||
if (inst->resource == &t->target) { /* a present transfer in an incomplete installation */
|
||||
assert(FLAGS_SET(us->flags, UPDATE_INCOMPLETE));
|
||||
continue;
|
||||
}
|
||||
|
||||
r = transfer_acquire_instance(t, inst, context_on_acquire_progress, c);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
@ -926,7 +966,13 @@ static int context_apply(
|
||||
"STATUS=Installing '%s'.", us->version);
|
||||
|
||||
for (size_t i = 0; i < c->n_transfers; i++) {
|
||||
r = transfer_install_instance(c->transfers[i], us->instances[i], arg_root);
|
||||
Instance *inst = us->instances[i];
|
||||
Transfer *t = c->transfers[i];
|
||||
|
||||
if (inst->resource == &t->target)
|
||||
continue;
|
||||
|
||||
r = transfer_install_instance(t, inst, arg_root);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
@ -1160,6 +1206,12 @@ static int verb_update(int argc, char **argv, void *userdata) {
|
||||
return reboot_now();
|
||||
}
|
||||
|
||||
if (strverscmp_improved(applied->version, booted_version) == 0 &&
|
||||
FLAGS_SET(applied->flags, UPDATE_INCOMPLETE)) {
|
||||
log_notice("Currently booted version was incomplete and has been repaired, rebooting.");
|
||||
return reboot_now();
|
||||
}
|
||||
|
||||
log_info("Booted version is newer or identical to newly installed version, not rebooting.");
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user