diff --git a/src/sysupdate/sysupdate-cleanup.c b/src/sysupdate/sysupdate-cleanup.c index fc18c594ac..af7b8cb130 100644 --- a/src/sysupdate/sysupdate-cleanup.c +++ b/src/sysupdate/sysupdate-cleanup.c @@ -112,6 +112,27 @@ static int installdb_make_names(const char *path, const char *pattern, char **re return 0; } +static const char* context_root_relative_path(const Context *c, const char *path) { + const char *p; + + assert(c); + assert(path); + + if (!c->root) + return path; + + p = path_startswith_full(path, c->root, PATH_STARTSWITH_RETURN_LEADING_SLASH); + if (p) + return p; + + /* path_startswith_full(..., PATH_STARTSWITH_RETURN_LEADING_SLASH) cannot return a pointer + * to a leading slash before the start of the original string when both paths are identical. */ + if (path_equal(path, c->root)) + return "/"; + + return NULL; +} + int context_installdb_record( Context *c, const char *path, @@ -129,7 +150,11 @@ int context_installdb_record( return 0; /* The provided path comes with c->root prefixed. Strip it here again */ - const char *p = c->root ? ASSERT_PTR(path_startswith(path, c->root)) : path; + const char *p = context_root_relative_path(c, path); + if (!p) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "Resolved resource path '%s' is not below root '%s'.", + path, c->root); r = context_installdb_acquire_fd(c, /* make= */ true); if (r < 0) @@ -170,7 +195,13 @@ static int context_is_path_currently_owned( if (!RESOURCE_IS_FILESYSTEM(t->target.type)) continue; - if (!path_equal(t->target.path, path)) + const char *target_path = context_root_relative_path(c, t->target.path); + if (!target_path) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "Resolved target path '%s' is not below root '%s'.", + t->target.path, c->root); + + if (!path_equal(target_path, path)) continue; /* OK, so we found a transfer that covers this directory. Now let's see if any of its patterns match */ diff --git a/src/sysupdate/sysupdate.c b/src/sysupdate/sysupdate.c index 07f6f7ab77..9ae80a2a8b 100644 --- a/src/sysupdate/sysupdate.c +++ b/src/sysupdate/sysupdate.c @@ -2949,15 +2949,13 @@ static int context_list_components(Context *context, char ***ret_component_names if (ret_component_names) *ret_component_names = TAKE_PTR(z); - /* Does the system have at least one transfer file in /etc/sysupdate.d, which can be considered a - * TARGET_HOST? See target_get_argument() in sysupdated.c */ + /* Does the system have at least one transfer file in sysupdate.d, which can be considered the + * default component-less installation? */ if (ret_has_default_component) *ret_has_default_component = !context->definitions && !context->component && - !context->root && - !context->image && - context->n_transfers > 0; + context->n_transfers + context->n_disabled_transfers > 0; return 0; } diff --git a/test/units/TEST-72-SYSUPDATE.sh b/test/units/TEST-72-SYSUPDATE.sh index c1978babd5..1ddb2ac9b9 100755 --- a/test/units/TEST-72-SYSUPDATE.sh +++ b/test/units/TEST-72-SYSUPDATE.sh @@ -907,6 +907,40 @@ test ! -f "$CLEANUP/target/alpha-v2.bin" rm -rf "$CONFIGDIR" "$INSTALLDB" "$CLEANUP" +# Under --root=, cleanup must compare current transfer target paths in the same +# root-relative form stored in the install database, and keep still-owned files. +ROOT_CLEANUP="$WORKDIR/root-cleanup" +ROOT_CLEANUP_ROOT="$ROOT_CLEANUP/root" +ROOT_CLEANUP_DB_VALUE="/target/./alpha-@v.bin" +ROOT_CLEANUP_DB_KEY="$(printf '%s' "$ROOT_CLEANUP_DB_VALUE" | sha256sum | cut -d' ' -f1)" +rm -rf "$ROOT_CLEANUP" +mkdir -p "$ROOT_CLEANUP_ROOT/etc/sysupdate.d" \ + "$ROOT_CLEANUP_ROOT/target" \ + "$ROOT_CLEANUP_ROOT/var/lib/systemd/sysupdate/installdb" + +cat >"$ROOT_CLEANUP_ROOT/etc/sysupdate.d/01-alpha.transfer" <"$ROOT_CLEANUP_ROOT/target/alpha-v1.bin" +ln -s "$ROOT_CLEANUP_DB_VALUE" \ + "$ROOT_CLEANUP_ROOT/var/lib/systemd/sysupdate/installdb/$ROOT_CLEANUP_DB_KEY" + +"$SYSUPDATE" --root="$ROOT_CLEANUP_ROOT" --verify=no cleanup +test -f "$ROOT_CLEANUP_ROOT/target/alpha-v1.bin" +test -L "$ROOT_CLEANUP_ROOT/var/lib/systemd/sysupdate/installdb/$ROOT_CLEANUP_DB_KEY" + +rm -rf "$ROOT_CLEANUP" + # Briefly check the "--component-all" switch of the "cleanup" verb. Each component # keeps its own install database (installdb.), and "cleanup # --component-all" must clean up orphaned resources across *all* of them in one @@ -1799,6 +1833,50 @@ assert_dropin "$(feat_enable_dropin_default featc)" yes test ! -e "$(feat_enable_dropin_default featb)" set_machine_tags "" +# --component-all must still include the default component when all of its +# transfers are currently disabled by features. +compfeat_reset +compfeat_source v1 +mkdir -p /run/sysupdate.d +compfeat_transfer /run/sysupdate.d/50-feata.transfer feata "$CF/target-default" feata +cat >/run/sysupdate.d/feata.feature <"$ROOT_FEATURE_ROOT/etc/sysupdate.d/50-feata.transfer" <"$ROOT_FEATURE_ROOT/etc/sysupdate.d/feata.feature" <