pkglayering: Commit in base repo, not pkgcache

Currently, we do the final commit into the pkgcache repo, then
pull it to the base.  The problem with this is that, combined
with the fact that we're not presently pruning the pkgcache repo,
we leak space.

In preparation for a cleaner fix for this, rework things so that the
core infra can know about *both* a base repo and a pkgcache repo.  If
they're separate (as is the case for rpm-ostree-on-host), whenever we
are doing layering, explicitly pull just the `.file` objects that are
referenced into the parent repo.  We do the final commit in the base
repo.

Closes: #437
Approved by: jlebon
This commit is contained in:
Colin Walters 2016-08-28 20:47:56 -04:00 committed by Atomic Bot
parent accd2b9f6f
commit 35d84f7507
5 changed files with 183 additions and 56 deletions

View File

@ -1119,7 +1119,7 @@ overlay_final_pkgset (RpmOstreeSysrootUpgrader *self,
if (!get_pkgcache_repo (repo, &pkgcache_repo, cancellable, error))
goto out;
rpmostree_context_set_repo (ctx, pkgcache_repo);
rpmostree_context_set_repos (ctx, repo, pkgcache_repo);
/* --- Downloading metadata --- */
if (!rpmostree_context_download_metadata (ctx, cancellable, error))
@ -1152,23 +1152,6 @@ overlay_final_pkgset (RpmOstreeSysrootUpgrader *self,
if (!final_assembly (self, ctx, repo, base_rev, cancellable, error))
goto out;
/* Send the final commit to the parent repo */
{
GFile *repo_path = ostree_repo_get_path (pkgcache_repo);
g_autofree char *uri =
g_strdup_printf ("file://%s", gs_file_get_path_cached (repo_path));
GVariantBuilder builder;
g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}"));
g_variant_builder_add (&builder, "{s@v}", "refs", g_variant_new_variant
(g_variant_new_strv ((const char * const*)&self->new_revision, 1)));
if (!ostree_repo_pull_with_options (repo, uri,
g_variant_builder_end (&builder),
NULL, cancellable, error))
goto out;
}
ret = TRUE;
out:
return ret;

View File

@ -43,6 +43,8 @@
#define RPMOSTREE_DIR_CACHE_SOLV "solv"
#define RPMOSTREE_DIR_LOCK "lock"
static OstreeRepo * get_pkgcache_repo (RpmOstreeContext *self);
/***********************************************************
* RpmOstreeTreespec *
***********************************************************
@ -281,6 +283,7 @@ struct _RpmOstreeContext {
DnfContext *hifctx;
GHashTable *ignore_scripts;
OstreeRepo *ostreerepo;
OstreeRepo *pkgcache_repo;
gboolean unprivileged;
char *dummy_instroot_path;
OstreeSePolicy *sepolicy;
@ -437,10 +440,18 @@ rpmostree_context_new_unprivileged (int basedir_dfd,
/* XXX: or put this in new_system() instead? */
void
rpmostree_context_set_repo (RpmOstreeContext *self,
OstreeRepo *repo)
rpmostree_context_set_repos (RpmOstreeContext *self,
OstreeRepo *base_repo,
OstreeRepo *pkgcache_repo)
{
g_set_object (&self->ostreerepo, repo);
g_set_object (&self->ostreerepo, base_repo);
g_set_object (&self->pkgcache_repo, pkgcache_repo);
}
static OstreeRepo *
get_pkgcache_repo (RpmOstreeContext *self)
{
return self->pkgcache_repo ? self->pkgcache_repo : self->ostreerepo;
}
/* I debated making this part of the treespec. Overall, I think it makes more
@ -996,7 +1007,7 @@ rpmostree_context_prepare_install (RpmOstreeContext *self,
rpmostree_output_task_end ("done");
if (!sort_packages (hifctx, self->ostreerepo, self->sepolicy,
if (!sort_packages (hifctx, get_pkgcache_repo (self), self->sepolicy,
ret_install, error))
goto out;
@ -1159,7 +1170,7 @@ import_one_package (RpmOstreeContext *self,
GError **error)
{
gboolean ret = FALSE;
OstreeRepo *ostreerepo = self->ostreerepo;
OstreeRepo *ostreerepo = get_pkgcache_repo (self);
g_autofree char *ostree_commit = NULL;
glnx_unref_object RpmOstreeUnpacker *unpacker = NULL;
g_autofree char *pkg_path;
@ -1229,7 +1240,7 @@ rpmostree_context_import (RpmOstreeContext *self,
if (n == 0)
return TRUE;
g_return_val_if_fail (self->ostreerepo != NULL, FALSE);
g_return_val_if_fail (get_pkgcache_repo (self) != NULL, FALSE);
{
glnx_unref_object DnfState *hifstate = dnf_state_new ();
@ -1257,14 +1268,14 @@ rpmostree_context_import (RpmOstreeContext *self,
}
static gboolean
ostree_checkout_package (OstreeRepo *repo,
DnfPackage *pkg,
int dfd,
const char *path,
OstreeRepoDevInoCache *devino_cache,
const char *pkg_commit,
GCancellable *cancellable,
GError **error)
checkout_package (OstreeRepo *repo,
DnfPackage *pkg,
int dfd,
const char *path,
OstreeRepoDevInoCache *devino_cache,
const char *pkg_commit,
GCancellable *cancellable,
GError **error)
{
gboolean ret = FALSE;
OstreeRepoCheckoutAtOptions opts = { OSTREE_REPO_CHECKOUT_MODE_USER,
@ -1293,6 +1304,36 @@ ostree_checkout_package (OstreeRepo *repo,
return ret;
}
static gboolean
checkout_package_into_root (RpmOstreeContext *self,
DnfPackage *pkg,
int dfd,
const char *path,
OstreeRepoDevInoCache *devino_cache,
const char *pkg_commit,
GCancellable *cancellable,
GError **error)
{
OstreeRepo *pkgcache_repo = get_pkgcache_repo (self);
if (pkgcache_repo != self->ostreerepo)
{
if (!rpmostree_pull_content_only (self->ostreerepo, pkgcache_repo, pkg_commit,
cancellable, error))
{
g_prefix_error (error, "Linking cached content for %s: ", dnf_package_get_nevra (pkg));
return FALSE;
}
}
if (!checkout_package (pkgcache_repo, pkg, dfd, path,
devino_cache, pkg_commit,
cancellable, error))
return FALSE;
return TRUE;
}
/* Given a path to a file/symlink, make a copy (reflink if possible)
* of it if it's a hard link. We need this for two places right now:
* - The RPM database
@ -1577,8 +1618,8 @@ relabel_one_package (OstreeRepo *repo,
cache = ostree_repo_devino_cache_new ();
if (!ostree_checkout_package (repo, pkg, tmprootfs_dfd, ".", cache,
commit_csum, cancellable, error))
if (!checkout_package (repo, pkg, tmprootfs_dfd, ".", cache,
commit_csum, cancellable, error))
goto out;
/* This is where the magic happens. We traverse the tree and relabel stuff,
@ -1675,13 +1716,14 @@ rpmostree_context_relabel (RpmOstreeContext *self,
gboolean ret = FALSE;
guint progress_sigid;
int n = install->packages_to_relabel->len;
OstreeRepo *ostreerepo = get_pkgcache_repo (self);
if (n == 0)
return TRUE;
g_assert (self->sepolicy);
g_return_val_if_fail (self->ostreerepo != NULL, FALSE);
g_return_val_if_fail (ostreerepo != NULL, FALSE);
{
glnx_unref_object DnfState *hifstate = dnf_state_new ();
@ -1696,7 +1738,7 @@ rpmostree_context_relabel (RpmOstreeContext *self,
for (guint i = 0; i < install->packages_to_relabel->len; i++)
{
DnfPackage *pkg = install->packages_to_relabel->pdata[i];
if (!relabel_one_package (self->ostreerepo, pkg, self->sepolicy,
if (!relabel_one_package (ostreerepo, pkg, self->sepolicy,
cancellable, error))
goto out;
dnf_state_assert_done (hifstate);
@ -1864,6 +1906,7 @@ rpmostree_context_assemble_commit (RpmOstreeContext *self,
GError **error)
{
gboolean ret = FALSE;
OstreeRepo *pkgcache_repo = get_pkgcache_repo (self);
DnfContext *hifctx = self->hifctx;
TransactionData tdata = { 0, -1 };
rpmts ordering_ts = NULL;
@ -1919,19 +1962,19 @@ rpmostree_context_assemble_commit (RpmOstreeContext *self,
{
g_autofree char *branch_head_rev = NULL;
if (!ostree_repo_resolve_rev (self->ostreerepo, cachebranch, FALSE,
if (!ostree_repo_resolve_rev (pkgcache_repo, cachebranch, FALSE,
&branch_head_rev, error))
goto out;
if (self->sepolicy == NULL)
cached_rev = g_steal_pointer (&branch_head_rev);
else if (!find_rev_with_sepolicy (self->ostreerepo, branch_head_rev,
else if (!find_rev_with_sepolicy (pkgcache_repo, branch_head_rev,
self->sepolicy, FALSE, &cached_rev,
error))
goto out;
}
if (!ostree_repo_load_variant (self->ostreerepo, OSTREE_OBJECT_TYPE_COMMIT, cached_rev,
if (!ostree_repo_load_variant (pkgcache_repo, OSTREE_OBJECT_TYPE_COMMIT, cached_rev,
&pkg_commit, error))
goto out;
@ -1990,11 +2033,11 @@ rpmostree_context_assemble_commit (RpmOstreeContext *self,
*/
if (filesystem_package)
{
if (!ostree_checkout_package (self->ostreerepo, filesystem_package,
tmprootfs_dfd, ".", devino_cache,
g_hash_table_lookup (pkg_to_ostree_commit,
filesystem_package),
cancellable, error))
if (!checkout_package_into_root (self, filesystem_package,
tmprootfs_dfd, ".", devino_cache,
g_hash_table_lookup (pkg_to_ostree_commit,
filesystem_package),
cancellable, error))
goto out;
}
else
@ -2007,11 +2050,11 @@ rpmostree_context_assemble_commit (RpmOstreeContext *self,
g_assert (pkg);
if (!ostree_checkout_package (self->ostreerepo, pkg,
tmprootfs_dfd, ".", devino_cache,
g_hash_table_lookup (pkg_to_ostree_commit,
pkg),
cancellable, error))
if (!checkout_package_into_root (self, pkg,
tmprootfs_dfd, ".", devino_cache,
g_hash_table_lookup (pkg_to_ostree_commit,
pkg),
cancellable, error))
goto out;
}
@ -2030,11 +2073,11 @@ rpmostree_context_assemble_commit (RpmOstreeContext *self,
if (pkg == filesystem_package)
continue;
if (!ostree_checkout_package (self->ostreerepo, pkg,
tmprootfs_dfd, ".", devino_cache,
g_hash_table_lookup (pkg_to_ostree_commit,
pkg),
cancellable, error))
if (!checkout_package_into_root (self, pkg,
tmprootfs_dfd, ".", devino_cache,
g_hash_table_lookup (pkg_to_ostree_commit,
pkg),
cancellable, error))
goto out;
}

View File

@ -64,8 +64,9 @@ gboolean rpmostree_context_setup (RpmOstreeContext *self,
GCancellable *cancellable,
GError **error);
void rpmostree_context_set_repo (RpmOstreeContext *self,
OstreeRepo *repo);
void rpmostree_context_set_repos (RpmOstreeContext *self,
OstreeRepo *base_repo,
OstreeRepo *pkgcache_repo);
void rpmostree_context_set_sepolicy (RpmOstreeContext *self,
OstreeSePolicy *sepolicy);
void rpmostree_context_set_ignore_scripts (RpmOstreeContext *self,

View File

@ -530,3 +530,95 @@ rpmostree_str_replace (const char *buf,
return g_regex_replace_literal (regex, buf, -1, 0, new, 0, error);
}
static gboolean
pull_content_only_recurse (OstreeRepo *dest,
OstreeRepo *src,
OstreeRepoCommitTraverseIter *iter,
GCancellable *cancellable,
GError **error)
{
gboolean done = FALSE;
while (!done)
{
OstreeRepoCommitIterResult iterres =
ostree_repo_commit_traverse_iter_next (iter, cancellable, error);
switch (iterres)
{
case OSTREE_REPO_COMMIT_ITER_RESULT_ERROR:
return FALSE;
case OSTREE_REPO_COMMIT_ITER_RESULT_END:
done = TRUE;
break;
case OSTREE_REPO_COMMIT_ITER_RESULT_FILE:
{
char *name;
char *checksum;
ostree_repo_commit_traverse_iter_get_file (iter, &name, &checksum);
if (!ostree_repo_import_object_from (dest, src, OSTREE_OBJECT_TYPE_FILE,
checksum, cancellable, error))
return FALSE;
}
break;
case OSTREE_REPO_COMMIT_ITER_RESULT_DIR:
{
char *name;
char *content_checksum;
char *meta_checksum;
g_autoptr(GVariant) dirtree = NULL;
ostree_cleanup_repo_commit_traverse_iter
OstreeRepoCommitTraverseIter subiter = { 0, };
ostree_repo_commit_traverse_iter_get_dir (iter, &name, &content_checksum, &meta_checksum);
if (!ostree_repo_load_variant (src, OSTREE_OBJECT_TYPE_DIR_TREE,
content_checksum, &dirtree,
error))
return FALSE;
if (!ostree_repo_commit_traverse_iter_init_dirtree (&subiter, src, dirtree,
OSTREE_REPO_COMMIT_TRAVERSE_FLAG_NONE,
error))
return FALSE;
if (!pull_content_only_recurse (dest, src, &subiter, cancellable, error))
return FALSE;
}
break;
}
}
return TRUE;
}
/* Migrate only the content (.file) objects from src+src_commit into dest.
* Used for package layering.
*/
gboolean
rpmostree_pull_content_only (OstreeRepo *dest,
OstreeRepo *src,
const char *src_commit,
GCancellable *cancellable,
GError **error)
{
g_autoptr(GVariant) commitdata = NULL;
ostree_cleanup_repo_commit_traverse_iter
OstreeRepoCommitTraverseIter iter = { 0, };
if (!ostree_repo_load_commit (src, src_commit, &commitdata, NULL, error))
return FALSE;
if (!ostree_repo_commit_traverse_iter_init_commit (&iter, src, commitdata,
OSTREE_REPO_COMMIT_TRAVERSE_FLAG_NONE,
error))
return FALSE;
if (!pull_content_only_recurse (dest, src, &iter, cancellable, error))
return FALSE;
return TRUE;
}

View File

@ -106,3 +106,11 @@ rpmostree_str_replace (const char *buf,
const char *old,
const char *new,
GError **error);
gboolean
rpmostree_pull_content_only (OstreeRepo *dest,
OstreeRepo *src,
const char *src_commit,
GCancellable *cancellable,
GError **error);