From 2e3d4b72a1de6b78f9202c68b3ad7bf4a40111b1 Mon Sep 17 00:00:00 2001 From: Jonathan Lebon Date: Wed, 26 Sep 2018 12:22:31 -0400 Subject: [PATCH] libpriv/scripts: Add rpmdb query sanity checks Make sure we can open and query the rpmdb when creating new deployments. This should help filter out cases where somehow librpm failed to actually write the rpmdb but didn't error out. This requires splitting the sanity checking in two so that we still get that nice error first on scripts that do `rm -rf`. See: #1566 Closes: #1584 Approved by: cgwalters --- src/daemon/rpmostree-sysroot-upgrader.c | 2 +- src/libpriv/rpmostree-core.c | 20 ++++++-- src/libpriv/rpmostree-scripts.c | 65 +++++++++++++++++++++++-- src/libpriv/rpmostree-scripts.h | 13 +++-- 4 files changed, 88 insertions(+), 12 deletions(-) diff --git a/src/daemon/rpmostree-sysroot-upgrader.c b/src/daemon/rpmostree-sysroot-upgrader.c index 76c56984..10146460 100644 --- a/src/daemon/rpmostree-sysroot-upgrader.c +++ b/src/daemon/rpmostree-sysroot-upgrader.c @@ -1332,7 +1332,7 @@ rpmostree_sysroot_upgrader_deploy (RpmOstreeSysrootUpgrader *self, &deployment_dfd, error)) return FALSE; - if (!rpmostree_deployment_sanitycheck (deployment_dfd, cancellable, error)) + if (!rpmostree_deployment_sanitycheck_true (deployment_dfd, cancellable, error)) return FALSE; } else diff --git a/src/libpriv/rpmostree-core.c b/src/libpriv/rpmostree-core.c index b57a4814..f736cb31 100644 --- a/src/libpriv/rpmostree-core.c +++ b/src/libpriv/rpmostree-core.c @@ -3806,6 +3806,9 @@ rpmostree_context_assemble (RpmOstreeContext *self, if (!rpmostree_rootfs_prepare_links (tmprootfs_dfd, cancellable, error)) return FALSE; + gboolean skip_sanity_check = FALSE; + g_variant_dict_lookup (self->spec->dict, "skip-sanity-check", "b", &skip_sanity_check); + /* NB: we're not running scripts right now for removals, so this is only for overlays and * replacements */ if (overlays->len > 0 || overrides_replace->len > 0) @@ -3978,11 +3981,10 @@ rpmostree_context_assemble (RpmOstreeContext *self, /* We want this to be the first error message if something went wrong * with a script; see https://github.com/projectatomic/rpm-ostree/pull/888 + * (otherwise, on a script that did `rm -rf`, we'd fail first on the renameat below) */ - gboolean skip_sanity_check = FALSE; - g_variant_dict_lookup (self->spec->dict, "skip-sanity-check", "b", &skip_sanity_check); if (!skip_sanity_check && - !rpmostree_deployment_sanitycheck (tmprootfs_dfd, cancellable, error)) + !rpmostree_deployment_sanitycheck_true (tmprootfs_dfd, cancellable, error)) return FALSE; if (have_systemctl) @@ -3997,12 +3999,12 @@ rpmostree_context_assemble (RpmOstreeContext *self, if (!rpmostree_passwd_complete_rpm_layering (tmprootfs_dfd, error)) return FALSE; } - } else { /* Also do a sanity check even if we have no layered packages */ - if (!rpmostree_deployment_sanitycheck (tmprootfs_dfd, cancellable, error)) + if (!skip_sanity_check && + !rpmostree_deployment_sanitycheck_true (tmprootfs_dfd, cancellable, error)) return FALSE; } @@ -4104,6 +4106,14 @@ rpmostree_context_assemble (RpmOstreeContext *self, rpmostree_output_task_end ("done"); + /* And now also sanity check the rpmdb */ + if (!skip_sanity_check) + { + if (!rpmostree_deployment_sanitycheck_rpmdb (tmprootfs_dfd, overlays, + overrides_replace, cancellable, error)) + return FALSE; + } + return TRUE; } diff --git a/src/libpriv/rpmostree-scripts.c b/src/libpriv/rpmostree-scripts.c index 9126fe79..3596cf6d 100644 --- a/src/libpriv/rpmostree-scripts.c +++ b/src/libpriv/rpmostree-scripts.c @@ -30,6 +30,7 @@ #include "libglnx.h" #include "rpmostree-scripts.h" +#include "rpmostree-rpm-util.h" #define RPMOSTREE_MESSAGE_PREPOST SD_ID128_MAKE(42,d3,72,22,dc,a2,4a,3b,9d,30,ce,d4,bb,bc,ac,d2) #define RPMOSTREE_MESSAGE_FILETRIGGER SD_ID128_MAKE(ef,dd,0e,4e,79,ca,45,d3,88,76,ac,45,e1,28,23,68) @@ -938,9 +939,9 @@ rpmostree_transfiletriggers_run_sync (Header hdr, * volatile mode, but that could just as easily be a separate tool. */ gboolean -rpmostree_deployment_sanitycheck (int rootfs_fd, - GCancellable *cancellable, - GError **error) +rpmostree_deployment_sanitycheck_true (int rootfs_fd, + GCancellable *cancellable, + GError **error) { /* Used by the test suite */ if (getenv ("RPMOSTREE_SKIP_SANITYCHECK")) @@ -956,6 +957,64 @@ rpmostree_deployment_sanitycheck (int rootfs_fd, rpmostree_bwrap_append_child_argv (bwrap, "/usr/bin/true", NULL); if (!rpmostree_bwrap_run (bwrap, cancellable, error)) return FALSE; + sd_journal_print (LOG_INFO, "sanitycheck(/usr/bin/true) successful"); return TRUE; } + +static gboolean +verify_packages_in_sack (DnfSack *sack, + GPtrArray *pkgs, + GError **error) +{ + if (!pkgs || pkgs->len == 0) + return TRUE; + + for (guint i = 0; i < pkgs->len; i++) + { + DnfPackage *pkg = pkgs->pdata[i]; + const char *nevra = dnf_package_get_nevra (pkg); + if (!rpmostree_sack_has_subject (sack, nevra)) + return glnx_throw (error, "Didn't find package '%s'", nevra); + } + + return TRUE; +} + +/* Check that we can load the rpmdb. See + * https://github.com/projectatomic/rpm-ostree/issues/1566. + * + * This is split out of the one above for practical reasons: the check above runs right + * after scripts are executed to give a nicer error if the scripts did `rm -rf`. + */ +gboolean +rpmostree_deployment_sanitycheck_rpmdb (int rootfs_fd, + /* just allow two args to avoid allocating */ + GPtrArray *overlays, + GPtrArray *overrides, + GCancellable *cancellable, + GError **error) +{ + g_autoptr(RpmOstreeRefSack) sack = rpmostree_get_refsack_for_root (rootfs_fd, ".", error); + if (!sack) + return FALSE; + + if ((overlays && overlays->len > 0) || (overrides && overrides->len > 0)) + { + if (!verify_packages_in_sack (sack->sack, overlays, error) || + !verify_packages_in_sack (sack->sack, overrides, error)) + return FALSE; + } + else + { + /* OK, let's just sanity check that there are *some* packages in the rpmdb */ + hy_autoquery HyQuery query = hy_query_create (sack->sack); + hy_query_filter (query, HY_PKG_REPONAME, HY_EQ, HY_SYSTEM_REPO_NAME); + g_autoptr(GPtrArray) pkgs = hy_query_run (query); + if (pkgs->len == 0) + return glnx_throw (error, "No packages found in rpmdb!"); + } + + sd_journal_print (LOG_INFO, "sanitycheck(rpmdb) successful"); + return TRUE; +} diff --git a/src/libpriv/rpmostree-scripts.h b/src/libpriv/rpmostree-scripts.h index 68fdab1b..87039b35 100644 --- a/src/libpriv/rpmostree-scripts.h +++ b/src/libpriv/rpmostree-scripts.h @@ -75,6 +75,13 @@ rpmostree_transfiletriggers_run_sync (Header hdr, GError **error); gboolean -rpmostree_deployment_sanitycheck (int rootfs_fd, - GCancellable *cancellable, - GError **error); +rpmostree_deployment_sanitycheck_true (int rootfs_fd, + GCancellable *cancellable, + GError **error); + +gboolean +rpmostree_deployment_sanitycheck_rpmdb (int rootfs_fd, + GPtrArray *overlays, + GPtrArray *overrides, + GCancellable *cancellable, + GError **error);