From 5657f5953c4c41e9c7cee5c1382ef42f7e54c377 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Wed, 22 Jan 2020 19:52:07 +0000 Subject: [PATCH] sysroot: Detect "live" system on /run/ostree-live Support read-only queries, but deny any attempts to make changes with a clear error. Now...in the future, we probably do want to support making "transient" overlays. See: https://github.com/coreos/fedora-coreos-tracker/issues/354#issuecomment-591417441 Closes: https://github.com/ostreedev/ostree/issues/1921 --- src/libostree/ostree-sysroot-private.h | 1 + src/libostree/ostree-sysroot.c | 153 ++++++++++++++++++++----- src/libostree/ostree-sysroot.h | 10 ++ 3 files changed, 133 insertions(+), 31 deletions(-) diff --git a/src/libostree/ostree-sysroot-private.h b/src/libostree/ostree-sysroot-private.h index 96670b137b..eb021e80e0 100644 --- a/src/libostree/ostree-sysroot-private.h +++ b/src/libostree/ostree-sysroot-private.h @@ -60,6 +60,7 @@ struct OstreeSysroot { OstreeSysrootLoadState loadstate; gboolean mount_namespace_in_use; /* TRUE if caller has told us they used CLONE_NEWNS */ gboolean root_is_ostree_booted; /* TRUE if sysroot is / and we are booted via ostree */ + gboolean root_is_ostree_live; /* TRUE if sysroot is / and we are running fully in RAM */ /* The device/inode for /, used to detect booted deployment */ dev_t root_device; ino_t root_inode; diff --git a/src/libostree/ostree-sysroot.c b/src/libostree/ostree-sysroot.c index 54b8990097..4129b88963 100644 --- a/src/libostree/ostree-sysroot.c +++ b/src/libostree/ostree-sysroot.c @@ -284,6 +284,9 @@ gboolean _ostree_sysroot_ensure_writable (OstreeSysroot *self, GError **error) { + if (self->root_is_ostree_live) + return glnx_throw (error, "sysroot does not support persistent changes"); + /* Do nothing if no mount namespace is in use */ if (!self->mount_namespace_in_use) return TRUE; @@ -908,6 +911,9 @@ ostree_sysroot_initialize (OstreeSysroot *self, if (!glnx_fstatat_allow_noent (AT_FDCWD, "/run/ostree-booted", NULL, 0, error)) return FALSE; const gboolean ostree_booted = (errno == 0); + if (!glnx_fstatat_allow_noent (AT_FDCWD, OSTREE_SYSROOT_LIVE_PATH, NULL, 0, error)) + return FALSE; + const gboolean ostree_live = (errno == 0); { struct stat root_stbuf; if (!glnx_fstatat (AT_FDCWD, "/", &root_stbuf, 0, error)) @@ -925,6 +931,7 @@ ostree_sysroot_initialize (OstreeSysroot *self, self->root_inode == self_stbuf.st_ino); self->root_is_ostree_booted = (ostree_booted && root_is_sysroot); + self->root_is_ostree_live = self->root_is_ostree_booted && ostree_live; self->loadstate = OSTREE_SYSROOT_LOAD_STATE_INIT; } @@ -993,8 +1000,6 @@ sysroot_load_from_bootloader_configs (OstreeSysroot *self, GCancellable *cancellable, GError **error) { - struct stat stbuf; - int bootversion = 0; if (!read_current_bootversion (self, &bootversion, cancellable, error)) return FALSE; @@ -1049,39 +1054,86 @@ sysroot_load_from_bootloader_configs (OstreeSysroot *self, if (self->staged_deployment) g_ptr_array_insert (deployments, 0, g_object_ref (self->staged_deployment)); - /* And then set their index variables */ - for (guint i = 0; i < deployments->len; i++) - { - OstreeDeployment *deployment = deployments->pdata[i]; - ostree_deployment_set_index (deployment, i); - } + self->bootversion = bootversion; + self->subbootversion = subbootversion; + self->deployments = g_steal_pointer (&deployments); - /* Determine whether we're "physical" or not, the first time we load deployments */ - if (self->loadstate < OSTREE_SYSROOT_LOAD_STATE_LOADED) + return TRUE; +} + +static gboolean +find_first_ent_at (int dirfd, + const char *path, + int dtype, + char **ret_name, + GCancellable *cancellable, + GError **error) +{ + g_auto(GLnxDirFdIterator) dfd_iter = { 0, }; + + if (!glnx_dirfd_iterator_init_at (dirfd, path, TRUE, + &dfd_iter, error)) + return FALSE; + + while (TRUE) { - /* If we have a booted deployment, the sysroot is / and we're definitely - * not physical. - */ - if (self->booted_deployment) - self->is_physical = FALSE; /* (the default, but explicit for clarity) */ - /* Otherwise - check for /sysroot which should only exist in a deployment, - * not in ${sysroot} (a metavariable for the real physical root). - */ - else - { - if (!glnx_fstatat_allow_noent (self->sysroot_fd, "sysroot", &stbuf, 0, error)) - return FALSE; - if (errno == ENOENT) - self->is_physical = TRUE; - } - /* Otherwise, the default is FALSE */ + struct dirent *dent; - self->loadstate = OSTREE_SYSROOT_LOAD_STATE_LOADED; + if (!glnx_dirfd_iterator_next_dent_ensure_dtype (&dfd_iter, &dent, + cancellable, error)) + return FALSE; + if (dent == NULL) + break; + + if (dent->d_type != dtype) + continue; + + *ret_name = g_strdup (dent->d_name); + break; } - self->bootversion = bootversion; - self->subbootversion = subbootversion; + return TRUE; +} + +static gboolean +sysroot_load_live (OstreeSysroot *self, + GCancellable *cancellable, + GError **error) +{ + g_autoptr(GString) bootlink = g_string_new ("/ostree/boot.1"); + + g_autofree char *osname = NULL; + if (!find_first_ent_at (AT_FDCWD, bootlink->str, DT_DIR, &osname, cancellable, error)) + return FALSE; + if (!osname) + return glnx_throw (error, "failed to find /ostree/boot subdirectory"); + g_string_append_c (bootlink, '/'); + g_string_append (bootlink, osname); + + g_autofree char *checksum = NULL; + if (!find_first_ent_at (AT_FDCWD, bootlink->str, DT_DIR, &checksum, cancellable, error)) + return FALSE; + if (!checksum) + return glnx_throw (error, "failed to find /ostree/boot statedir"); + g_string_append_c (bootlink, '/'); + g_string_append (bootlink, checksum); + + g_autofree char *treeserial = NULL; + if (!find_first_ent_at (AT_FDCWD, bootlink->str, DT_LNK, &treeserial, cancellable, error)) + return FALSE; + if (!treeserial) + return glnx_throw (error, "failed to find /ostree/boot treeserial"); + g_string_append_c (bootlink, '/'); + g_string_append (bootlink, treeserial); + + g_autoptr(OstreeDeployment) deployment = NULL; + if (!parse_deployment (self, bootlink->str, &deployment, + cancellable, error)) + return FALSE; + g_autoptr(GPtrArray) deployments = g_ptr_array_new_with_free_func ((GDestroyNotify)g_object_unref); + g_ptr_array_add (deployments, g_object_ref (deployment)); self->deployments = g_steal_pointer (&deployments); + self->booted_deployment = g_steal_pointer (&deployment); return TRUE; } @@ -1130,8 +1182,47 @@ ostree_sysroot_load_if_changed (OstreeSysroot *self, self->bootversion = -1; self->subbootversion = -1; - if (!sysroot_load_from_bootloader_configs (self, cancellable, error)) - return FALSE; + if (self->root_is_ostree_live) + { + if (!sysroot_load_live (self, cancellable, error)) + return FALSE; + } + else + { + if (!sysroot_load_from_bootloader_configs (self, cancellable, error)) + return FALSE; + } + + /* Assign the index variables */ + g_assert (self->deployments); + for (guint i = 0; i < self->deployments->len; i++) + { + OstreeDeployment *deployment = self->deployments->pdata[i]; + ostree_deployment_set_index (deployment, i); + } + + /* Determine whether we're "physical" or not, the first time we load deployments */ + if (self->loadstate < OSTREE_SYSROOT_LOAD_STATE_LOADED) + { + /* If we have a booted deployment, the sysroot is / and we're definitely + * not physical. + */ + if (self->booted_deployment) + self->is_physical = FALSE; /* (the default, but explicit for clarity) */ + /* Otherwise - check for /sysroot which should only exist in a deployment, + * not in ${sysroot} (a metavariable for the real physical root). + */ + else + { + if (!glnx_fstatat_allow_noent (self->sysroot_fd, "sysroot", &stbuf, 0, error)) + return FALSE; + if (errno == ENOENT) + self->is_physical = TRUE; + } + /* Otherwise, the default is FALSE */ + + self->loadstate = OSTREE_SYSROOT_LOAD_STATE_LOADED; + } self->loaded_ts = stbuf.st_mtim; diff --git a/src/libostree/ostree-sysroot.h b/src/libostree/ostree-sysroot.h index af8192fc40..5962d4bcca 100644 --- a/src/libostree/ostree-sysroot.h +++ b/src/libostree/ostree-sysroot.h @@ -32,6 +32,16 @@ G_BEGIN_DECLS #define OSTREE_IS_SYSROOT(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), OSTREE_TYPE_SYSROOT)) +/** + * OSTREE_SYSROOT_LIVE_PATH: + * + * The existence of this file declares that the system + * is booted "live" (fully in RAM). + * + * Since: 2020.1 + **/ +#define OSTREE_SYSROOT_LIVE_PATH "/run/ostree-live" + _OSTREE_PUBLIC GType ostree_sysroot_get_type (void);