Skip to content

Commit

Permalink
Merge pull request #3090 from cgwalters/stabilize-locking
Browse files Browse the repository at this point in the history
sysroot: Stabilize deployment finalization, add API
  • Loading branch information
cgwalters committed Nov 27, 2023
2 parents ff7e7f4 + 28cc761 commit 12cbb3d
Show file tree
Hide file tree
Showing 19 changed files with 342 additions and 13 deletions.
1 change: 1 addition & 0 deletions Makefile-man.am
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ ostree-admin-init-fs.1 ostree-admin-instutil.1 ostree-admin-stateroot-init.1 ost
ostree-admin-status.1 ostree-admin-set-origin.1 ostree-admin-switch.1 \
ostree-admin-undeploy.1 ostree-admin-upgrade.1 ostree-admin-unlock.1 \
ostree-admin-pin.1 ostree-admin-post-copy.1 ostree-admin-set-default.1 \
ostree-admin-lock-finalization.1 \
ostree-admin.1 ostree-cat.1 ostree-checkout.1 ostree-checksum.1 \
ostree-commit.1 ostree-create-usb.1 ostree-export.1 \
ostree-config.1 ostree-diff.1 ostree-find-remotes.1 ostree-fsck.1 \
Expand Down
1 change: 1 addition & 0 deletions Makefile-ostree.am
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ ostree_SOURCES += \
src/ostree/ot-admin-builtin-diff.c \
src/ostree/ot-admin-builtin-deploy.c \
src/ostree/ot-admin-builtin-finalize-staged.c \
src/ostree/ot-admin-builtin-lock-finalization.c \
src/ostree/ot-admin-builtin-boot-complete.c \
src/ostree/ot-admin-builtin-undeploy.c \
src/ostree/ot-admin-builtin-set-default.c \
Expand Down
2 changes: 2 additions & 0 deletions apidoc/ostree-sections.txt
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,7 @@ ostree_deployment_get_origin_relpath
ostree_deployment_get_unlocked
ostree_deployment_is_pinned
ostree_deployment_is_staged
ostree_deployment_is_finalization_locked
ostree_deployment_set_index
ostree_deployment_set_bootserial
ostree_deployment_set_bootconfig
Expand Down Expand Up @@ -591,6 +592,7 @@ ostree_sysroot_write_origin_file
ostree_sysroot_stage_tree
ostree_sysroot_stage_tree_with_options
ostree_sysroot_stage_overlay_initrd
ostree_sysroot_change_finalization
ostree_sysroot_deploy_tree
ostree_sysroot_deploy_tree_with_options
ostree_sysroot_get_merge_deployment
Expand Down
10 changes: 10 additions & 0 deletions man/ostree-admin-deploy.xml
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,16 @@ License along with this library. If not, see <https://www.gnu.org/licenses/>.
</para></listitem>
</varlistentry>

<varlistentry>
<term><option>--lock-finalization</option></term>

<listitem><para>
The deployment will not be "finalized" by default on shutdown; to later
queue it, use <literal>ostree admin unlock-finalization</literal>.
</para></listitem>
</varlistentry>


<varlistentry>
<term><option>--karg-proc-cmdline</option></term>

Expand Down
92 changes: 92 additions & 0 deletions man/ostree-admin-lock-finalization.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
<?xml version='1.0'?> <!--*-nxml-*-->
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">

<!--
Copyright 2023 Red Hat, Inc.
SPDX-License-Identifier: LGPL-2.0+
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library. If not, see <https://www.gnu.org/licenses/>.
-->

<refentry id="ostree">

<refentryinfo>
<title>ostree admin lock-finalization</title>
<productname>OSTree</productname>

<authorgroup>
<author>
<contrib>Developer</contrib>
<firstname>Colin</firstname>
<surname>Walters</surname>
<email>walters@verbum.org</email>
</author>
</authorgroup>
</refentryinfo>

<refmeta>
<refentrytitle>ostree admin lock-finalization</refentrytitle>
<manvolnum>1</manvolnum>
</refmeta>

<refnamediv>
<refname>ostree-admin-lock-finalization</refname>
<refpurpose>Change whether staged deployment will be queued for next boot</refpurpose>
</refnamediv>

<refsynopsisdiv>
<cmdsynopsis>
<command>ostree admin lock-finalization</command> <arg choice="opt" rep="repeat">OPTIONS</arg>
</cmdsynopsis>
</refsynopsisdiv>

<refsect1>
<title>Description</title>

<para>
This command requires a staged deployment. By default, this command ensures the deployment
will be set into a "finalization locked" state, which means it will not be queued for the next boot by default.
</para>
<para>
This is the same as the <literal>--lock-finalization</literal> argument for <literal>ostree admin deploy</literal>.
</para>
<para>
However more commonly, one will use the <literal>--unlock</literal> argument for this command to later unlock
a deployment which was finalization locked.
</para>
</refsect1>

<refsect1>
<title>Options</title>

<variablelist>
<varlistentry>
<term><option>--sysroot</option>="PATH"</term>

<listitem><para>
Path to the system to use rather than the current one.
</para></listitem>
</varlistentry>
<varlistentry>
<term><option>--unlock</option>,<option>-u</option></term>

<listitem><para>
Unlock the deployment finalization state.
</para></listitem>
</varlistentry>
</variablelist>
</refsect1>
</refentry>
2 changes: 2 additions & 0 deletions src/libostree/libostree-devel.sym
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
LIBOSTREE_2023.8 {
global:
ostree_sysroot_update_post_copy;
ostree_deployment_is_finalization_locked;
ostree_sysroot_change_finalization;
} LIBOSTREE_2023.4;

/* Stub section for the stable release *after* this development one; don't
Expand Down
1 change: 1 addition & 0 deletions src/libostree/ostree-deployment-private.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ struct _OstreeDeployment
GKeyFile *origin;
OstreeDeploymentUnlockedState unlocked;
gboolean staged;
gboolean finalization_locked;
char **overlay_initrds;
char *overlay_initrds_id;
};
Expand Down
15 changes: 15 additions & 0 deletions src/libostree/ostree-deployment.c
Original file line number Diff line number Diff line change
Expand Up @@ -461,3 +461,18 @@ ostree_deployment_is_staged (OstreeDeployment *self)
{
return self->staged;
}

/**
* ostree_deployment_is_finalization_locked:
* @self: Deployment
*
* Returns: `TRUE` if deployment is queued to be "finalized" at shutdown time, but requires
* additional action.
*
* Since: 2023.8
*/
gboolean
ostree_deployment_is_finalization_locked (OstreeDeployment *self)
{
return self->finalization_locked;
}
2 changes: 2 additions & 0 deletions src/libostree/ostree-deployment.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ GKeyFile *ostree_deployment_get_origin (OstreeDeployment *self);
_OSTREE_PUBLIC
gboolean ostree_deployment_is_staged (OstreeDeployment *self);
_OSTREE_PUBLIC
gboolean ostree_deployment_is_finalization_locked (OstreeDeployment *self);
_OSTREE_PUBLIC
gboolean ostree_deployment_is_pinned (OstreeDeployment *self);

_OSTREE_PUBLIC
Expand Down
90 changes: 86 additions & 4 deletions src/libostree/ostree-sysroot-deploy.c
Original file line number Diff line number Diff line change
Expand Up @@ -3657,6 +3657,10 @@ ostree_sysroot_stage_tree_with_options (OstreeSysroot *self, const char *osname,
g_autoptr (GVariantBuilder) builder = g_variant_builder_new ((GVariantType *)"a{sv}");
g_variant_builder_add (builder, "{sv}", "target", serialize_deployment_to_variant (deployment));

if (opts->locked)
g_variant_builder_add (builder, "{sv}", _OSTREE_SYSROOT_STAGED_KEY_LOCKED,
g_variant_new_boolean (TRUE));

if (merge_deployment)
g_variant_builder_add (builder, "{sv}", "merge-deployment",
serialize_deployment_to_variant (merge_deployment));
Expand Down Expand Up @@ -3706,6 +3710,73 @@ ostree_sysroot_stage_tree_with_options (OstreeSysroot *self, const char *osname,
return TRUE;
}

/**
* ostree_sysroot_change_finalization:
* @self: Sysroot
* @deployment: Deployment which must be staged
* @error: Error
*
* Given the target deployment (which must be the staged deployment) this API
* will toggle its "finalization locking" state. If it is currently locked,
* it will be unlocked (and hence queued to apply on shutdown).
*
* Since: 2023.8
*/
_OSTREE_PUBLIC
gboolean
ostree_sysroot_change_finalization (OstreeSysroot *self, OstreeDeployment *deployment,
GError **error)
{
GCancellable *cancellable = NULL;
g_assert (ostree_deployment_is_staged (deployment));

gboolean new_locked_state = !ostree_deployment_is_finalization_locked (deployment);

/* Read the staged state from disk */
glnx_autofd int fd = -1;
if (!glnx_openat_rdonly (AT_FDCWD, _OSTREE_SYSROOT_RUNSTATE_STAGED, TRUE, &fd, error))
return FALSE;

g_autoptr (GBytes) contents = ot_fd_readall_or_mmap (fd, 0, error);
if (!contents)
return FALSE;
g_autoptr (GVariant) staged_deployment_data
= g_variant_new_from_bytes ((GVariantType *)"a{sv}", contents, TRUE);
g_autoptr (GVariantDict) staged_deployment_dict = g_variant_dict_new (staged_deployment_data);

g_variant_dict_insert (staged_deployment_dict, _OSTREE_SYSROOT_STAGED_KEY_LOCKED, "b",
new_locked_state);
g_autoptr (GVariant) new_staged_deployment_data = g_variant_dict_end (staged_deployment_dict);

if (!glnx_file_replace_contents_at (fd, _OSTREE_SYSROOT_RUNSTATE_STAGED,
g_variant_get_data (new_staged_deployment_data),
g_variant_get_size (new_staged_deployment_data),
GLNX_FILE_REPLACE_NODATASYNC, cancellable, error))
return FALSE;

if (!new_locked_state)
{
/* Delete the legacy lock if there was any. */
if (!ot_ensure_unlinked_at (AT_FDCWD, _OSTREE_SYSROOT_RUNSTATE_STAGED_LOCKED, error))
return FALSE;
}
else
{
/* Create the legacy lockfile; see also the code in ot-admin-builtin-deploy.c */
if (!glnx_shutil_mkdir_p_at (AT_FDCWD,
dirname (strdupa (_OSTREE_SYSROOT_RUNSTATE_STAGED_LOCKED)), 0755,
cancellable, error))
return FALSE;

glnx_autofd int lockfd = open (_OSTREE_SYSROOT_RUNSTATE_STAGED_LOCKED,
O_CREAT | O_WRONLY | O_NOCTTY | O_CLOEXEC, 0640);
if (lockfd == -1)
return glnx_throw_errno_prefix (error, "touch(%s)", _OSTREE_SYSROOT_RUNSTATE_STAGED_LOCKED);
}

return TRUE;
}

/* Invoked at shutdown time by ostree-finalize-staged.service */
static gboolean
_ostree_sysroot_finalize_staged_inner (OstreeSysroot *self, GCancellable *cancellable,
Expand All @@ -3722,11 +3793,22 @@ _ostree_sysroot_finalize_staged_inner (OstreeSysroot *self, GCancellable *cancel
}

/* Check if finalization is locked. */
if (!glnx_fstatat_allow_noent (AT_FDCWD, _OSTREE_SYSROOT_RUNSTATE_STAGED_LOCKED, NULL, 0, error))
return FALSE;
if (errno == 0)
gboolean locked = false;
(void)g_variant_lookup (self->staged_deployment_data, _OSTREE_SYSROOT_STAGED_KEY_LOCKED, "b",
&locked);
if (locked)
g_debug ("staged is locked via metadata");
else
{
if (!glnx_fstatat_allow_noent (AT_FDCWD, _OSTREE_SYSROOT_RUNSTATE_STAGED_LOCKED, NULL, 0,
error))
return FALSE;
if (errno == 0)
locked = TRUE;
}
if (locked)
{
ot_journal_print (LOG_INFO, "Not finalizing; found " _OSTREE_SYSROOT_RUNSTATE_STAGED_LOCKED);
ot_journal_print (LOG_INFO, "Not finalizing; deployment is locked for finalization");
return TRUE;
}

Expand Down
3 changes: 3 additions & 0 deletions src/libostree/ostree-sysroot-private.h
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,9 @@ struct OstreeSysroot
OstreeSysrootDebugFlags debug_flags;
};

/* Key in staged deployment variant for finalization locking */
#define _OSTREE_SYSROOT_STAGED_KEY_LOCKED "locked"

#define OSTREE_SYSROOT_LOCKFILE "ostree/lock"
/* We keep some transient state in /run */
#define _OSTREE_SYSROOT_RUNSTATE_STAGED "/run/ostree/staged-deployment"
Expand Down
2 changes: 2 additions & 0 deletions src/libostree/ostree-sysroot.c
Original file line number Diff line number Diff line change
Expand Up @@ -1106,6 +1106,8 @@ _ostree_sysroot_reload_staged (OstreeSysroot *self, GError **error)
* canonical "staged_deployment" reference.
*/
self->staged_deployment->staged = TRUE;
(void)g_variant_dict_lookup (staged_deployment_dict, _OSTREE_SYSROOT_STAGED_KEY_LOCKED,
"b", &self->staged_deployment->finalization_locked);
}
}

Expand Down
9 changes: 8 additions & 1 deletion src/libostree/ostree-sysroot.h
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,10 @@ gboolean ostree_sysroot_stage_overlay_initrd (OstreeSysroot *self, int fd, char

typedef struct
{
gboolean unused_bools[8];
/* If set to true, then this deployment will be staged but "locked" and not automatically applied
* on reboot. */
gboolean locked;
gboolean unused_bools[7];
int unused_ints[8];
char **override_kernel_argv;
char **overlay_initrds;
Expand Down Expand Up @@ -215,6 +218,10 @@ gboolean ostree_sysroot_stage_tree_with_options (OstreeSysroot *self, const char
OstreeDeployment **out_new_deployment,
GCancellable *cancellable, GError **error);

_OSTREE_PUBLIC
gboolean ostree_sysroot_change_finalization (OstreeSysroot *self, OstreeDeployment *deployment,
GError **error);

_OSTREE_PUBLIC
gboolean ostree_sysroot_deployment_set_mutable (OstreeSysroot *self, OstreeDeployment *deployment,
gboolean is_mutable, GCancellable *cancellable,
Expand Down
13 changes: 10 additions & 3 deletions src/ostree/ot-admin-builtin-deploy.c
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ static GOptionEntry options[] = {
"Do not apply configuration (/etc and kernel arguments) from booted deployment", NULL },
{ "retain", 0, 0, G_OPTION_ARG_NONE, &opt_retain, "Do not delete previous deployments", NULL },
{ "stage", 0, 0, G_OPTION_ARG_NONE, &opt_stage, "Complete deployment at OS shutdown", NULL },
{ "lock-finalization", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &opt_lock_finalization,
{ "lock-finalization", 0, 0, G_OPTION_ARG_NONE, &opt_lock_finalization,
"Prevent automatic deployment finalization on shutdown", NULL },
{ "retain-pending", 0, 0, G_OPTION_ARG_NONE, &opt_retain_pending,
"Do not delete pending deployments", NULL },
Expand Down Expand Up @@ -123,6 +123,10 @@ ot_admin_builtin_deploy (int argc, char **argv, OstreeCommandInvocation *invocat
return FALSE;
}

// Locking implies staging
if (opt_lock_finalization)
opt_stage = TRUE;

const char *refspec = argv[1];

OstreeRepo *repo = ostree_sysroot_repo (sysroot);
Expand Down Expand Up @@ -236,6 +240,7 @@ ot_admin_builtin_deploy (int argc, char **argv, OstreeCommandInvocation *invocat
g_auto (GStrv) kargs_strv = kargs ? ostree_kernel_args_to_strv (kargs) : NULL;

OstreeSysrootDeployTreeOpts opts = {
.locked = opt_lock_finalization,
.override_kernel_argv = kargs_strv,
.overlay_initrds = overlay_initrd_chksums ? (char **)overlay_initrd_chksums->pdata : NULL,
};
Expand All @@ -247,9 +252,11 @@ ot_admin_builtin_deploy (int argc, char **argv, OstreeCommandInvocation *invocat
return glnx_throw (error, "--stage cannot currently be combined with --retain arguments");
if (opt_not_as_default)
return glnx_throw (error, "--stage cannot currently be combined with --not-as-default");
/* touch file *before* we stage to avoid races */
/* For compatibility with older versions of ostree, also write this legacy file.
* This can likely be safely deleted in the middle of 2024 say. */
if (opt_lock_finalization)
{
g_debug ("Writing legacy finalization lockfile");
if (!glnx_shutil_mkdir_p_at (AT_FDCWD,
dirname (strdupa (_OSTREE_SYSROOT_RUNSTATE_STAGED_LOCKED)),
0755, cancellable, error))
Expand All @@ -262,7 +269,7 @@ ot_admin_builtin_deploy (int argc, char **argv, OstreeCommandInvocation *invocat
_OSTREE_SYSROOT_RUNSTATE_STAGED_LOCKED);
}
/* use old API if we can to exercise it in CI */
if (!overlay_initrd_chksums)
if (!(overlay_initrd_chksums || opt_lock_finalization))
{
if (!ostree_sysroot_stage_tree (sysroot, opt_osname, revision, origin, merge_deployment,
kargs_strv, &new_deployment, cancellable, error))
Expand Down
Loading

0 comments on commit 12cbb3d

Please sign in to comment.