Skip to content

Commit

Permalink
sysroot: Detect "live" system on /run/ostree-live
Browse files Browse the repository at this point in the history
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:
coreos/fedora-coreos-tracker#354 (comment)

Closes: ostreedev#1921
  • Loading branch information
cgwalters committed Mar 19, 2020
1 parent 677f0bf commit 0c45183
Show file tree
Hide file tree
Showing 3 changed files with 133 additions and 31 deletions.
1 change: 1 addition & 0 deletions src/libostree/ostree-sysroot-private.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
153 changes: 122 additions & 31 deletions src/libostree/ostree-sysroot.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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))
Expand All @@ -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;
}

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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;

Expand Down
10 changes: 10 additions & 0 deletions src/libostree/ostree-sysroot.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down

0 comments on commit 0c45183

Please sign in to comment.