diff --git a/src/libostree/libostree-devel.sym b/src/libostree/libostree-devel.sym index ff5f52c4ab..ff856a0d7f 100644 --- a/src/libostree/libostree-devel.sym +++ b/src/libostree/libostree-devel.sym @@ -28,6 +28,7 @@ global: ostree_sysroot_initialize; ostree_sysroot_is_booted; ostree_sysroot_set_mount_namespace_in_use; + ostree_sysroot_stage_tree_with_flags; } LIBOSTREE_2019.6; /* Stub section for the stable release *after* this development one; don't diff --git a/src/libostree/ostree-sysroot-deploy.c b/src/libostree/ostree-sysroot-deploy.c index 6fc851c29b..d7654fe90d 100644 --- a/src/libostree/ostree-sysroot-deploy.c +++ b/src/libostree/ostree-sysroot-deploy.c @@ -2850,6 +2850,48 @@ ostree_sysroot_stage_tree (OstreeSysroot *self, OstreeDeployment **out_new_deployment, GCancellable *cancellable, GError **error) +{ + return ostree_sysroot_stage_tree_with_flags (self, + osname, + revision, + origin, + merge_deployment, + override_kernel_argv, + out_new_deployment, + OSTREE_SYSROOT_SIMPLE_WRITE_DEPLOYMENT_FLAGS_NONE, + cancellable, + error); +} + +/** + * ostree_sysroot_stage_tree_with_flags: + * @self: Sysroot + * @osname: (allow-none): osname to use for merge deployment + * @revision: Checksum to add + * @origin: (allow-none): Origin to use for upgrades + * @merge_deployment: (allow-none): Use this deployment for merge path + * @override_kernel_argv: (allow-none) (array zero-terminated=1) (element-type utf8): Use these as kernel arguments; if %NULL, inherit options from provided_merge_deployment + * @out_new_deployment: (out): The new deployment path + * @flags: flags to control how the deployment is written + * @cancellable: Cancellable + * @error: Error + * + * Like ostree_sysroot_deploy_tree(), but "finalization" only occurs at OS + * shutdown time. + * + * Since: 2019.7 + */ +gboolean +ostree_sysroot_stage_tree_with_flags (OstreeSysroot *self, + const char *osname, + const char *revision, + GKeyFile *origin, + OstreeDeployment *merge_deployment, + char **override_kernel_argv, + OstreeDeployment **out_new_deployment, + OstreeSysrootSimpleWriteDeploymentFlags flags, + GCancellable *cancellable, + GError **error) { if (!_ostree_sysroot_ensure_writable (self, error)) return FALSE; @@ -2921,6 +2963,11 @@ ostree_sysroot_stage_tree (OstreeSysroot *self, g_variant_builder_add (builder, "{sv}", "kargs", g_variant_new_strv ((const char *const*)override_kernel_argv, -1)); + /* Proxy across any flags */ + if (flags) + g_variant_builder_add (builder, "{sv}", "write-deployment-flags", + g_variant_new_uint32 ((guint32)flags)); + const char *parent = dirname (strdupa (_OSTREE_SYSROOT_RUNSTATE_STAGED)); if (!glnx_shutil_mkdir_p_at (AT_FDCWD, parent, 0755, cancellable, error)) return FALSE; @@ -3042,14 +3089,15 @@ _ostree_sysroot_finalize_staged (OstreeSysroot *self, staged->staged = FALSE; g_ptr_array_remove_index (self->deployments, 0); - /* TODO: Proxy across flags too? - * - * But note that we always use NO_CLEAN to avoid adding more latency at + OstreeSysrootSimpleWriteDeploymentFlags flags = 0; + g_variant_lookup (self->staged_deployment_data, "write-deployment-flags", "u", &flags); + /* + * Note that we always use NO_CLEAN to avoid adding more latency at * shutdown, and also because e.g. rpm-ostree wants to own the cleanup * process. */ - OstreeSysrootSimpleWriteDeploymentFlags flags = - OSTREE_SYSROOT_SIMPLE_WRITE_DEPLOYMENT_FLAGS_NO_CLEAN; + flags |= OSTREE_SYSROOT_SIMPLE_WRITE_DEPLOYMENT_FLAGS_NO_CLEAN; + if (!ostree_sysroot_simple_write_deployment (self, ostree_deployment_get_osname (staged), staged, merge_deployment, flags, cancellable, error)) diff --git a/src/libostree/ostree-sysroot.c b/src/libostree/ostree-sysroot.c index 23a069752d..c12d94ab0a 100644 --- a/src/libostree/ostree-sysroot.c +++ b/src/libostree/ostree-sysroot.c @@ -1698,6 +1698,9 @@ ostree_sysroot_init_osname (OstreeSysroot *self, * specified, then no cleanup will be performed after adding the * deployment. Make sure to call ostree_sysroot_cleanup() sometime * later, instead. + * + * If %OSTREE_SYSROOT_SIMPLE_WRITE_DEPLOYMENT_FLAGS_RETAIN_PREVIOUS_VERSION is + * specified, then the previous version will not be garbage collected. */ gboolean ostree_sysroot_simple_write_deployment (OstreeSysroot *sysroot, @@ -1716,6 +1719,8 @@ ostree_sysroot_simple_write_deployment (OstreeSysroot *sysroot, (flags & OSTREE_SYSROOT_SIMPLE_WRITE_DEPLOYMENT_FLAGS_RETAIN_PENDING) > 0; const gboolean retain_rollback = (flags & OSTREE_SYSROOT_SIMPLE_WRITE_DEPLOYMENT_FLAGS_RETAIN_ROLLBACK) > 0; + const gboolean retain_previous = + (flags & OSTREE_SYSROOT_SIMPLE_WRITE_DEPLOYMENT_FLAGS_RETAIN_PREVIOUS_VERSION) > 0; gboolean retain = (flags & OSTREE_SYSROOT_SIMPLE_WRITE_DEPLOYMENT_FLAGS_RETAIN) > 0; @@ -1738,6 +1743,26 @@ ostree_sysroot_simple_write_deployment (OstreeSysroot *sysroot, if (!booted_deployment && !merge_deployment && (retain_pending || retain_rollback)) retain = TRUE; + /* tracks current versioned deployment */ + OstreeRepo *repo = ostree_sysroot_repo (sysroot); + const gchar *new_version = + _ostree_deployment_get_version (new_deployment, repo, error); + + gboolean retained_previous_version = FALSE; + if (booted_deployment) + { + const gchar *booted_version = + _ostree_deployment_get_version (booted_deployment, repo, error); + retained_previous_version = (g_strcmp0 (booted_version, new_version) != 0); + } + + if (!retained_previous_version && merge_deployment) + { + const gchar *merge_version = + _ostree_deployment_get_version (merge_deployment, repo, error); + retained_previous_version = (g_strcmp0 (merge_version, new_version) != 0); + } + /* tracks when we come across the booted deployment */ gboolean before_booted = TRUE; gboolean before_merge = TRUE; @@ -1758,6 +1783,13 @@ ostree_sysroot_simple_write_deployment (OstreeSysroot *sysroot, * deployments, fall back on merge deployment */ const gboolean passed_crossover = booted_deployment ? !before_booted : !before_merge; + gboolean is_previous_version = FALSE; + if (passed_crossover && osname_matches && !retained_previous_version) + { + const gchar *version = _ostree_deployment_get_version (deployment, repo, error); + is_previous_version = (g_strcmp0 (version, new_version) != 0); + } + /* Retain deployment if: * - we're explicitly asked to, or * - it's pinned @@ -1773,6 +1805,15 @@ ostree_sysroot_simple_write_deployment (OstreeSysroot *sysroot, || (is_booted || is_merge) || (retain_rollback && passed_crossover)) g_ptr_array_add (new_deployments, g_object_ref (deployment)); + /* + * - we're keeping the previous version deployment + */ + else if (retain_previous && !retained_previous_version && is_previous_version) + { + g_ptr_array_add (new_deployments, g_object_ref (deployment)); + /* Just keep one previous version */ + retained_previous_version = TRUE; + } /* add right after booted/merge deployment */ if (!added_new && passed_crossover) diff --git a/src/libostree/ostree-sysroot.h b/src/libostree/ostree-sysroot.h index af8192fc40..9df50b7ea3 100644 --- a/src/libostree/ostree-sysroot.h +++ b/src/libostree/ostree-sysroot.h @@ -200,6 +200,16 @@ gboolean ostree_sysroot_deploy_tree (OstreeSysroot *self, GCancellable *cancellable, GError **error); +typedef enum { + OSTREE_SYSROOT_SIMPLE_WRITE_DEPLOYMENT_FLAGS_NONE = 0, + OSTREE_SYSROOT_SIMPLE_WRITE_DEPLOYMENT_FLAGS_RETAIN = (1 << 0), + OSTREE_SYSROOT_SIMPLE_WRITE_DEPLOYMENT_FLAGS_NOT_DEFAULT = (1 << 1), + OSTREE_SYSROOT_SIMPLE_WRITE_DEPLOYMENT_FLAGS_NO_CLEAN = (1 << 2), + OSTREE_SYSROOT_SIMPLE_WRITE_DEPLOYMENT_FLAGS_RETAIN_PENDING = (1 << 3), + OSTREE_SYSROOT_SIMPLE_WRITE_DEPLOYMENT_FLAGS_RETAIN_ROLLBACK = (1 << 4), + OSTREE_SYSROOT_SIMPLE_WRITE_DEPLOYMENT_FLAGS_RETAIN_PREVIOUS_VERSION = (1 << 5), +} OstreeSysrootSimpleWriteDeploymentFlags; + _OSTREE_PUBLIC gboolean ostree_sysroot_stage_tree (OstreeSysroot *self, const char *osname, @@ -211,6 +221,18 @@ gboolean ostree_sysroot_stage_tree (OstreeSysroot *self, GCancellable *cancellable, GError **error); +_OSTREE_PUBLIC +gboolean ostree_sysroot_stage_tree_with_flags (OstreeSysroot *self, + const char *osname, + const char *revision, + GKeyFile *origin, + OstreeDeployment *merge_deployment, + char **override_kernel_argv, + OstreeDeployment **out_new_deployment, + OstreeSysrootSimpleWriteDeploymentFlags flags, + GCancellable *cancellable, + GError **error); + _OSTREE_PUBLIC gboolean ostree_sysroot_deployment_set_mutable (OstreeSysroot *self, OstreeDeployment *deployment, @@ -246,15 +268,6 @@ _OSTREE_PUBLIC GKeyFile *ostree_sysroot_origin_new_from_refspec (OstreeSysroot *self, const char *refspec); -typedef enum { - OSTREE_SYSROOT_SIMPLE_WRITE_DEPLOYMENT_FLAGS_NONE = 0, - OSTREE_SYSROOT_SIMPLE_WRITE_DEPLOYMENT_FLAGS_RETAIN = (1 << 0), - OSTREE_SYSROOT_SIMPLE_WRITE_DEPLOYMENT_FLAGS_NOT_DEFAULT = (1 << 1), - OSTREE_SYSROOT_SIMPLE_WRITE_DEPLOYMENT_FLAGS_NO_CLEAN = (1 << 2), - OSTREE_SYSROOT_SIMPLE_WRITE_DEPLOYMENT_FLAGS_RETAIN_PENDING = (1 << 3), - OSTREE_SYSROOT_SIMPLE_WRITE_DEPLOYMENT_FLAGS_RETAIN_ROLLBACK = (1 << 4), -} OstreeSysrootSimpleWriteDeploymentFlags; - _OSTREE_PUBLIC gboolean ostree_sysroot_simple_write_deployment (OstreeSysroot *sysroot, const char *osname, diff --git a/src/ostree/ot-admin-builtin-deploy.c b/src/ostree/ot-admin-builtin-deploy.c index c1c3353de0..299439a686 100644 --- a/src/ostree/ot-admin-builtin-deploy.c +++ b/src/ostree/ot-admin-builtin-deploy.c @@ -174,7 +174,7 @@ ot_admin_builtin_deploy (int argc, char **argv, OstreeCommandInvocation *invocat if (opt_not_as_default) return glnx_throw (error, "--stage cannot currently be combined with --not-as-default"); if (!ostree_sysroot_stage_tree (sysroot, opt_osname, revision, origin, merge_deployment, - kargs_strv, &new_deployment, cancellable, error)) + kargs_strv, &new_deployment, 0, cancellable, error)) return FALSE; g_assert (new_deployment); }