Skip to content

Commit

Permalink
deploy: Rework config merge to be based on deployments
Browse files Browse the repository at this point in the history
This is preparation for making the API public. The public version will 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 Mar 27, 2017
1 parent 7c8f95c commit 9509fda
Showing 1 changed file with 71 additions and 80 deletions.
151 changes: 71 additions & 80 deletions src/libostree/ostree-sysroot-deploy.c
Original file line number Diff line number Diff line change
Expand Up @@ -432,35 +432,46 @@ copy_modified_config_file (int orig_etc_fd,
/**
* merge_etc_changes:
*
* 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)
merge_etc_changes (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 @@ -474,7 +485,7 @@ merge_etc_changes (GFile *orig_etc,
cancellable, error))
{
g_prefix_error (error, "While computing configuration diff: ");
goto out;
return FALSE;
}

ot_log_structured_print_id_v (OSTREE_CONFIGMERGE_ID,
Expand All @@ -483,31 +494,29 @@ merge_etc_changes (GFile *orig_etc,
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 @@ -516,9 +525,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 @@ -527,12 +536,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 @@ -780,25 +787,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 @@ -812,70 +808,65 @@ 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 (!merge_etc_changes (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

0 comments on commit 9509fda

Please sign in to comment.