Skip to content

Commit

Permalink
deploy: Expose API for configuration merge
Browse files Browse the repository at this point in the history
For rpm-ostree do [live updates](coreos/rpm-ostree#652),
we need the ability to do a configuration merge targeting the *booted* deployment.

Change the (previously internal) config merge function to take
`OstreeDeployment*`, which the caller is expected to already have, rather than
`GFile*`s pointing to the config directories.

While here, much of the code was converted to "2017 style" i.e.
"decl-after-stmt+return FALSE", as well as fd-relative to some extent. A full
fd-relative conversion would be hard right now.
  • Loading branch information
cgwalters committed Apr 13, 2017
1 parent 8742287 commit 90d86fe
Show file tree
Hide file tree
Showing 4 changed files with 92 additions and 90 deletions.
1 change: 1 addition & 0 deletions apidoc/ostree-sections.txt
Original file line number Diff line number Diff line change
Expand Up @@ -482,6 +482,7 @@ ostree_sysroot_init_osname
ostree_sysroot_deployment_set_kargs
ostree_sysroot_deployment_set_mutable
ostree_sysroot_deployment_unlock
ostree_sysroot_merge_etc
ostree_sysroot_write_deployments
ostree_sysroot_write_deployments_with_options
ostree_sysroot_write_origin_file
Expand Down
6 changes: 2 additions & 4 deletions src/libostree/libostree.sym
Original file line number Diff line number Diff line change
Expand Up @@ -395,12 +395,10 @@ global:
*/

/* Uncomment when adding the first new symbol */
/*
LIBOSTREE_2017.$NEWVERSION {
LIBOSTREE_2017.5 {
global:
someostree_symbol_deleteme;
ostree_sysroot_merge_etc;
} LIBOSTREE_2017.4;
*/

/* Stub section for the stable release *after* this development one; don't
* edit this other than to update the last number. This is just a copy/paste
Expand Down
166 changes: 80 additions & 86 deletions src/libostree/ostree-sysroot-deploy.c
Original file line number Diff line number Diff line change
Expand Up @@ -424,37 +424,55 @@ copy_modified_config_file (int orig_etc_fd,
}

/**
* merge_etc_changes:
* ostree_sysroot_merge_etc:
* @sysroot: Sysroot
* @merge_deployment: Source of configuration differences
* @merge_deployment_dfd: Directory fd, may be -1
* @new_deployment: Target for merge of configuration
* @new_deployment_dfd: Directory fd for @new_deployment (may *not* be -1)
* @cancellable: Cancellable
* @error: Error
*
* Compute the difference between @orig_etc and @modified_etc,
* and apply that to @new_etc.
* Compute the difference between @merge_deployment's `/usr/etc` and `/etc`, and
* apply that to @new_deployment's `/etc`.
*
* The algorithm for computing the difference is pretty simple; it's
* approximately equivalent to "diff -unR orig_etc modified_etc",
* except that rather than attempting a 3-way merge if a file is also
* changed in @new_etc, the modified version always wins.
*/
static gboolean
merge_etc_changes (GFile *orig_etc,
GFile *modified_etc,
GFile *new_etc,
OstreeSysrootDebugFlags flags,
GCancellable *cancellable,
GError **error)
gboolean
ostree_sysroot_merge_etc (OstreeSysroot *sysroot,
OstreeDeployment *merge_deployment,
int merge_deployment_dfd,
OstreeDeployment *new_deployment,
int new_deployment_dfd,
GCancellable *cancellable,
GError **error)
{
gboolean ret = FALSE;
g_autoptr(GPtrArray) modified = NULL;
g_autoptr(GPtrArray) removed = NULL;
g_autoptr(GPtrArray) added = NULL;
guint i;
glnx_fd_close int orig_etc_fd = -1;
glnx_fd_close int modified_etc_fd = -1;
glnx_fd_close int new_etc_fd = -1;
glnx_fd_close int owned_merge_deployment_dfd = -1;
const OstreeSysrootDebugFlags flags = sysroot->debug_flags;

modified = g_ptr_array_new_with_free_func ((GDestroyNotify) ostree_diff_item_unref);
removed = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
added = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
g_assert (merge_deployment != NULL && new_deployment != NULL);
g_assert (new_deployment_dfd != -1);

/* Allow the caller to pass -1 for the merge, for convenience */
if (merge_deployment_dfd == -1)
{
g_autofree char *merge_deployment_path = ostree_sysroot_get_deployment_dirpath (sysroot, merge_deployment);
if (!glnx_opendirat (sysroot->sysroot_fd, merge_deployment_path, FALSE,
&owned_merge_deployment_dfd, error))
return FALSE;
merge_deployment_dfd = owned_merge_deployment_dfd;
}

/* TODO: get rid of GFile usage here */
g_autoptr(GFile) orig_etc = ot_fdrel_to_gfile (merge_deployment_dfd, "usr/etc");
g_autoptr(GFile) modified_etc = ot_fdrel_to_gfile (merge_deployment_dfd, "etc");
/* Return values for below */
g_autoptr(GPtrArray) modified = g_ptr_array_new_with_free_func ((GDestroyNotify) ostree_diff_item_unref);
g_autoptr(GPtrArray) removed = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
g_autoptr(GPtrArray) added = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
/* For now, ignore changes to xattrs; the problem is that
* security.selinux will be different between the /usr/etc labels
* and the ones in the real /etc, so they all show up as different.
Expand All @@ -466,42 +484,37 @@ merge_etc_changes (GFile *orig_etc,
if (!ostree_diff_dirs (OSTREE_DIFF_FLAGS_IGNORE_XATTRS,
orig_etc, modified_etc, modified, removed, added,
cancellable, error))
{
g_prefix_error (error, "While computing configuration diff: ");
goto out;
}
return g_prefix_error (error, "While computing configuration diff: "), FALSE;

ot_log_structured_print_id_v (OSTREE_CONFIGMERGE_ID,
"Copying /etc changes: %u modified, %u removed, %u added",
"Copying /etc changes: %u modified, %u removed, %u added",
modified->len,
removed->len,
added->len);

if (!glnx_opendirat (AT_FDCWD, gs_file_get_path_cached (orig_etc), TRUE,
&orig_etc_fd, error))
goto out;
if (!glnx_opendirat (AT_FDCWD, gs_file_get_path_cached (modified_etc), TRUE,
&modified_etc_fd, error))
goto out;
if (!glnx_opendirat (AT_FDCWD, gs_file_get_path_cached (new_etc), TRUE,
&new_etc_fd, error))
goto out;
glnx_fd_close int orig_etc_fd = -1;
if (!glnx_opendirat (merge_deployment_dfd, "usr/etc", TRUE, &orig_etc_fd, error))
return FALSE;
glnx_fd_close int modified_etc_fd = -1;
if (!glnx_opendirat (merge_deployment_dfd, "etc", TRUE, &modified_etc_fd, error))
return FALSE;
glnx_fd_close int new_etc_fd = -1;
if (!glnx_opendirat (new_deployment_dfd, "etc", TRUE, &new_etc_fd, error))
return FALSE;

for (i = 0; i < removed->len; i++)
for (guint i = 0; i < removed->len; i++)
{
GFile *file = removed->pdata[i];
g_autoptr(GFile) target_file = NULL;
g_autofree char *path = NULL;

path = g_file_get_relative_path (orig_etc, file);
g_assert (path);
target_file = g_file_resolve_relative_path (new_etc, path);

if (!glnx_shutil_rm_rf_at (AT_FDCWD, gs_file_get_path_cached (target_file), cancellable, error))
goto out;
if (!glnx_shutil_rm_rf_at (new_etc_fd, path, cancellable, error))
return FALSE;
}

for (i = 0; i < modified->len; i++)
for (guint i = 0; i < modified->len; i++)
{
OstreeDiffItem *diff = modified->pdata[i];
g_autofree char *path = g_file_get_relative_path (modified_etc, diff->target);
Expand All @@ -510,9 +523,9 @@ merge_etc_changes (GFile *orig_etc,

if (!copy_modified_config_file (orig_etc_fd, modified_etc_fd, new_etc_fd, path,
flags, cancellable, error))
goto out;
return FALSE;
}
for (i = 0; i < added->len; i++)
for (guint i = 0; i < added->len; i++)
{
GFile *file = added->pdata[i];
g_autofree char *path = g_file_get_relative_path (modified_etc, file);
Expand All @@ -521,12 +534,10 @@ merge_etc_changes (GFile *orig_etc,

if (!copy_modified_config_file (orig_etc_fd, modified_etc_fd, new_etc_fd, path,
flags, cancellable, error))
goto out;
return FALSE;
}

ret = TRUE;
out:
return ret;
return TRUE;
}

/**
Expand Down Expand Up @@ -774,25 +785,14 @@ merge_configuration (OstreeSysroot *sysroot,
GCancellable *cancellable,
GError **error)
{
gboolean ret = FALSE;
g_autofree char *deployment_abspath = glnx_fdrel_abspath (deployment_dfd, ".");
g_autoptr(GFile) deployment_path = g_file_new_for_path (deployment_abspath);
g_autoptr(GFile) source_etc_path = NULL;
g_autoptr(GFile) source_etc_pristine_path = NULL;
g_autoptr(GFile) deployment_usretc_path = NULL;
g_autoptr(GFile) deployment_etc_path = NULL;
glnx_unref_object OstreeSePolicy *sepolicy = NULL;
gboolean etc_exists;
gboolean usretc_exists;

if (previous_deployment)
{
g_autoptr(GFile) previous_path = NULL;
OstreeBootconfigParser *previous_bootconfig;

previous_path = ostree_sysroot_get_deployment_directory (sysroot, previous_deployment);
source_etc_path = g_file_resolve_relative_path (previous_path, "etc");
source_etc_pristine_path = g_file_resolve_relative_path (previous_path, "usr/etc");

previous_bootconfig = ostree_deployment_get_bootconfig (previous_deployment);
if (previous_bootconfig)
Expand All @@ -806,70 +806,64 @@ merge_configuration (OstreeSysroot *sysroot,
}
}

deployment_etc_path = g_file_get_child (deployment_path, "etc");
deployment_usretc_path = g_file_resolve_relative_path (deployment_path, "usr/etc");

etc_exists = g_file_query_exists (deployment_etc_path, NULL);
usretc_exists = g_file_query_exists (deployment_usretc_path, NULL);
gboolean etc_exists = FALSE;
if (!ot_query_exists_at (deployment_dfd, "etc", &etc_exists, error))
return FALSE;
gboolean usretc_exists = FALSE;
if (!ot_query_exists_at (deployment_dfd, "usr/etc", &usretc_exists, error))
return FALSE;

if (etc_exists && usretc_exists)
{
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
"Tree contains both /etc and /usr/etc");
goto out;
}
return glnx_throw (error, "Tree contains both /etc and /usr/etc");
else if (etc_exists)
{
/* Compatibility hack */
if (renameat (deployment_dfd, "etc", deployment_dfd, "usr/etc") < 0)
{
glnx_set_error_from_errno (error);
goto out;
}
return glnx_throw_errno (error);
usretc_exists = TRUE;
etc_exists = FALSE;
}

if (usretc_exists)
{
glnx_fd_close int deployment_usr_dfd = -1;

if (!glnx_opendirat (deployment_dfd, "usr", TRUE, &deployment_usr_dfd, error))
goto out;
return FALSE;

/* TODO - set out labels as we copy files */
g_assert (!etc_exists);
if (!copy_dir_recurse (deployment_usr_dfd, deployment_dfd, "etc",
sysroot->debug_flags, cancellable, error))
goto out;
return FALSE;

/* Here, we initialize SELinux policy from the /usr/etc inside
* the root - this is before we've finalized the configuration
* merge into /etc. */
sepolicy = ostree_sepolicy_new (deployment_path, cancellable, error);
sepolicy = ostree_sepolicy_new_at (deployment_dfd, cancellable, error);
if (!sepolicy)
goto out;
return FALSE;

if (ostree_sepolicy_get_name (sepolicy) != NULL)
{
if (!selinux_relabel_dir (sysroot, sepolicy, deployment_etc_path, "etc",
g_autoptr(GFile) deployment_etc = ot_fdrel_to_gfile (deployment_dfd, "etc");
if (!selinux_relabel_dir (sysroot, sepolicy, deployment_etc, "etc",
cancellable, error))
goto out;
return FALSE;
}
}

if (source_etc_path)
if (previous_deployment)
{
if (!merge_etc_changes (source_etc_pristine_path, source_etc_path, deployment_etc_path,
sysroot->debug_flags, cancellable, error))
goto out;
if (!ostree_sysroot_merge_etc (sysroot, previous_deployment, -1,
deployment, deployment_dfd,
cancellable, error))
return FALSE;
}

ret = TRUE;
if (out_sepolicy)
*out_sepolicy = g_steal_pointer (&sepolicy);
out:
return ret;
return TRUE;
}

static gboolean
Expand Down
9 changes: 9 additions & 0 deletions src/libostree/ostree-sysroot.h
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,15 @@ gboolean ostree_sysroot_deploy_tree (OstreeSysroot *self,
GCancellable *cancellable,
GError **error);

_OSTREE_PUBLIC
gboolean ostree_sysroot_merge_etc (OstreeSysroot *sysroot,
OstreeDeployment *merge_deployment,
int merge_deployment_dfd,
OstreeDeployment *new_deployment,
int new_deployment_dfd,
GCancellable *cancellable,
GError **error);

_OSTREE_PUBLIC
gboolean ostree_sysroot_deployment_set_mutable (OstreeSysroot *self,
OstreeDeployment *deployment,
Expand Down

0 comments on commit 90d86fe

Please sign in to comment.