diff --git a/src/app/rpm-ostree-0-integration.conf b/src/app/rpm-ostree-0-integration.conf index c5fd009a0b..f13b00a209 100644 --- a/src/app/rpm-ostree-0-integration.conf +++ b/src/app/rpm-ostree-0-integration.conf @@ -15,4 +15,10 @@ d /var/usrlocal/src 0755 root root - d /var/mnt 0755 root root - d /run/media 0755 root root - d /var/lib 0755 root root - +# At some point ideally this moves into rpm; see +# https://www.mail-archive.com/rpm-maint@lists.rpm.org/msg05421.html L /var/lib/rpm - - - - ../../usr/share/rpm +# These were added more recently; TODO(walters) investigate +# dropping them. +L /var/lib/alternatives - - - - ../../usr/lib/alternatives +L /var/lib/vagrant - - - - ../../usr/lib/vagrant diff --git a/src/app/rpmostree-compose-builtin-tree.c b/src/app/rpmostree-compose-builtin-tree.c index a2b6131155..6a89e52628 100644 --- a/src/app/rpmostree-compose-builtin-tree.c +++ b/src/app/rpmostree-compose-builtin-tree.c @@ -58,7 +58,6 @@ static gboolean opt_download_only; static gboolean opt_download_only_rpms; static gboolean opt_force_nocache; static gboolean opt_cache_only; -static gboolean opt_unified_core; static char *opt_proxy; static char *opt_output_repodata_dir; static char **opt_metadata_strings; @@ -70,6 +69,9 @@ static gboolean opt_print_only; static char *opt_write_commitid_to; static char *opt_write_composejson_to; +/* Dummy */ +static gboolean unused_opt_unified_core; + /* shared by both install & commit */ static GOptionEntry common_option_entries[] = { { "repo", 'r', 0, G_OPTION_ARG_STRING, &opt_repo, "Path to OSTree repository", "REPO" }, @@ -80,10 +82,10 @@ static GOptionEntry install_option_entries[] = { { "force-nocache", 0, 0, G_OPTION_ARG_NONE, &opt_force_nocache, "Always create a new OSTree commit, even if nothing appears to have changed", NULL }, { "cache-only", 0, 0, G_OPTION_ARG_NONE, &opt_cache_only, "Assume cache is present, do not attempt to update it", NULL }, { "cachedir", 0, 0, G_OPTION_ARG_STRING, &opt_cachedir, "Cached state", "CACHEDIR" }, - { "download-only", 0, 0, G_OPTION_ARG_NONE, &opt_download_only, "Like --dry-run, but download and import RPMs as well; requires --cachedir", NULL }, { "download-only-rpms", 0, 0, G_OPTION_ARG_NONE, &opt_download_only_rpms, "Like --dry-run, but download RPMs as well; requires --cachedir", NULL }, - { "ex-unified-core", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &opt_unified_core, "Compat alias for --unified-core", NULL }, // Compat - { "unified-core", 0, 0, G_OPTION_ARG_NONE, &opt_unified_core, "Use new \"unified core\" codepath", NULL }, + { "download-only", 0, 0, G_OPTION_ARG_NONE, &opt_download_only, "Like --dry-run, but download RPMs as well; requires --cachedir", NULL }, + { "ex-unified-core", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &unused_opt_unified_core, "Compat alias for --unified-core", NULL }, // Compat + { "unified-core", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &unused_opt_unified_core, "Now always enabled", NULL }, { "proxy", 0, 0, G_OPTION_ARG_STRING, &opt_proxy, "HTTP proxy", "PROXY" }, { "dry-run", 0, 0, G_OPTION_ARG_NONE, &opt_dry_run, "Just print the transaction and exit", NULL }, { "output-repodata-dir", 0, 0, G_OPTION_ARG_STRING, &opt_output_repodata_dir, "Save downloaded repodata in DIR", "DIR" }, @@ -160,15 +162,6 @@ rpm_ostree_tree_compose_context_free (RpmOstreeTreeComposeContext *ctx) } G_DEFINE_AUTOPTR_CLEANUP_FUNC(RpmOstreeTreeComposeContext, rpm_ostree_tree_compose_context_free) -static void -on_hifstate_percentage_changed (DnfState *hifstate, - guint percentage, - gpointer user_data) -{ - const char *text = user_data; - glnx_console_progress_text_percent (text, percentage); -} - static gboolean inputhash_from_commit (OstreeRepo *repo, const char *sha256, @@ -266,12 +259,6 @@ install_packages (RpmOstreeTreeComposeContext *self, dnf_context_set_repo_dir (dnfctx, abs_tf_path); } - /* By default, retain packages in addition to metadata with --cachedir, unless - * we're doing unified core, in which case the pkgcache repo is the cache. But - * the rojigSet build still requires the original RPMs too. - */ - if (opt_cachedir && !opt_unified_core) - dnf_context_set_keep_cache (dnfctx, TRUE); /* For compose, always try to refresh metadata; we're used in build servers * where fetching should be cheap. We also have --cache-only which is * used by coreos-assembler. Today we don't expose the default, but we @@ -291,42 +278,39 @@ install_packages (RpmOstreeTreeComposeContext *self, return FALSE; /* For unified core, we have a pkgcache repo. This is auto-created under the cachedir. */ - if (opt_unified_core) - { - g_assert (self->pkgcache_repo); - - if (!opt_cachedir) - { - /* This is part of enabling rpm-ostree inside Docker/Kubernetes/OpenShift; - * in this case we probably don't have access to FUSE as today it uses a - * suid binary which doesn't have the capabilities it needs. - * - * So this magical bit tells the core to disable FUSE, which we only do - * if --cachedir isn't specified. Another way to say this is that - * running inside an unprivileged container today requires turning off - * some of the rpm-ostree intelligence around caching. - * - * We don't make this actually conditional somehow on running in a - * container since if you're not using a persistent cache there's no - * real advantage to taking the overhead of FUSE. If the hardlinks are - * corrupted, it doesn't matter as they're going to be deleted - * anyways. - */ - rpmostree_context_disable_rofiles (self->corectx); - } - else - { - self->unified_core_and_fuse = TRUE; - /* We also only enable the devino cache if we know we have the FUSE protection - * against mutation of the underlying files. - */ - self->devino_cache = ostree_repo_devino_cache_new (); - rpmostree_context_set_devino_cache (self->corectx, self->devino_cache); - } + g_assert (self->pkgcache_repo); - rpmostree_context_set_repos (self->corectx, self->build_repo, self->pkgcache_repo); + if (!opt_cachedir) + { + /* This is part of enabling rpm-ostree inside Docker/Kubernetes/OpenShift; + * in this case we probably don't have access to FUSE as today it uses a + * suid binary which doesn't have the capabilities it needs. + * + * So this magical bit tells the core to disable FUSE, which we only do + * if --cachedir isn't specified. Another way to say this is that + * running inside an unprivileged container today requires turning off + * some of the rpm-ostree intelligence around caching. + * + * We don't make this actually conditional somehow on running in a + * container since if you're not using a persistent cache there's no + * real advantage to taking the overhead of FUSE. If the hardlinks are + * corrupted, it doesn't matter as they're going to be deleted + * anyways. + */ + rpmostree_context_disable_rofiles (self->corectx); + } + else + { + self->unified_core_and_fuse = TRUE; + /* We also only enable the devino cache if we know we have the FUSE protection + * against mutation of the underlying files. + */ + self->devino_cache = ostree_repo_devino_cache_new (); + rpmostree_context_set_devino_cache (self->corectx, self->devino_cache); } + rpmostree_context_set_repos (self->corectx, self->build_repo, self->pkgcache_repo); + if (!rpmostree_context_prepare (self->corectx, cancellable, error)) return FALSE; @@ -375,7 +359,7 @@ install_packages (RpmOstreeTreeComposeContext *self, if (opt_download_only || opt_download_only_rpms) { - if (opt_unified_core && !opt_download_only_rpms) + if (!opt_download_only_rpms) { if (!rpmostree_context_import (self->corectx, cancellable, error)) return FALSE; @@ -384,75 +368,30 @@ install_packages (RpmOstreeTreeComposeContext *self, } /* Before we install packages, inject /etc/{passwd,group} if configured. */ - if (!rpmostree_passwd_compose_prep (rootfs_dfd, self->repo, opt_unified_core, + if (!rpmostree_passwd_compose_prep (rootfs_dfd, self->repo, TRUE, self->treefile_rs, self->treefile, self->previous_checksum, cancellable, error)) return FALSE; - if (opt_unified_core) - { - if (!rpmostree_context_import (self->corectx, cancellable, error)) - return FALSE; - rpmostree_context_set_tmprootfs_dfd (self->corectx, rootfs_dfd); - if (!rpmostree_context_assemble (self->corectx, cancellable, error)) - return FALSE; - - /* Now reload the policy from the tmproot, and relabel the pkgcache - this - * is the same thing done in rpmostree_context_commit(). But here we want - * to ensure our pkgcache labels are accurate, since that will - * be important for the ostree-rojig work. - */ - g_autoptr(OstreeSePolicy) sepolicy = ostree_sepolicy_new_at (rootfs_dfd, cancellable, error); - if (sepolicy == NULL) - return FALSE; - - rpmostree_context_set_sepolicy (self->corectx, sepolicy); - - if (!rpmostree_context_force_relabel (self->corectx, cancellable, error)) - return FALSE; - } - else - { - /* The non-unified core path */ - - /* Before we install packages, drop a file to suppress the kernel.rpm dracut run. - * */ - const char *kernel_installd_path = "usr/lib/kernel/install.d"; - if (!glnx_shutil_mkdir_p_at (rootfs_dfd, kernel_installd_path, 0755, cancellable, error)) - return FALSE; - const char skip_kernel_install_data[] = "#!/usr/bin/sh\nexit 77\n"; - const char *kernel_skip_path = glnx_strjoina (kernel_installd_path, "/00-rpmostree-skip.install"); - if (!glnx_file_replace_contents_with_perms_at (rootfs_dfd, kernel_skip_path, - (guint8*)skip_kernel_install_data, - strlen (skip_kernel_install_data), - 0755, 0, 0, - GLNX_FILE_REPLACE_NODATASYNC, - cancellable, error)) - return FALSE; - - /* Now actually run through librpm to install the packages. Note this bit - * will be replaced in the future with a unified core: - * https://github.com/projectatomic/rpm-ostree/issues/729 - */ - g_auto(GLnxConsoleRef) console = { 0, }; - g_autoptr(DnfState) hifstate = dnf_state_new (); - - guint progress_sigid = g_signal_connect (hifstate, "percentage-changed", - G_CALLBACK (on_hifstate_percentage_changed), - "Installing packages:"); - - glnx_console_lock (&console); + if (!rpmostree_context_import (self->corectx, cancellable, error)) + return FALSE; + rpmostree_context_set_tmprootfs_dfd (self->corectx, rootfs_dfd); + if (!rpmostree_context_assemble (self->corectx, cancellable, error)) + return FALSE; - if (!rpmostree_composeutil_legacy_prep_dev (rootfs_dfd, error)) - return FALSE; + /* Now reload the policy from the tmproot, and relabel the pkgcache - this + * is the same thing done in rpmostree_context_commit(). But here we want + * to ensure our pkgcache labels are accurate, since that will + * be important for the ostree-rojig work. + */ + g_autoptr(OstreeSePolicy) sepolicy = ostree_sepolicy_new_at (rootfs_dfd, cancellable, error); + if (sepolicy == NULL) + return FALSE; - if (!dnf_transaction_commit (dnf_context_get_transaction (dnfctx), - dnf_context_get_goal (dnfctx), - hifstate, error)) - return FALSE; + rpmostree_context_set_sepolicy (self->corectx, sepolicy); - g_signal_handler_disconnect (hifstate, progress_sigid); - } + if (!rpmostree_context_force_relabel (self->corectx, cancellable, error)) + return FALSE; if (out_unmodified) *out_unmodified = FALSE; @@ -568,101 +507,67 @@ rpm_ostree_compose_context_new (const char *treefile_pathstr, if (opt_workdir_tmpfs) g_printerr ("note: --workdir-tmpfs is deprecated and will be ignored\n"); + /* We ignore --workdir because we want to be sure + * that we're going to get hardlinks. The only way to be sure of this is to place the + * workdir underneath the cachedir; the same fs where the pkgcache repo is. */ + if (opt_workdir) + g_printerr ("note: --workdir is ignored\n"); - if (opt_unified_core) + if (opt_cachedir) { - /* Unified mode works very differently. We ignore --workdir because we want to be sure - * that we're going to get hardlinks. The only way to be sure of this is to place the - * workdir underneath the cachedir; the same fs where the pkgcache repo is. */ - - if (opt_workdir) - g_printerr ("note: --workdir is ignored for --unified-core\n"); - - if (opt_cachedir) - { - if (!glnx_opendirat (AT_FDCWD, opt_cachedir, TRUE, &self->cachedir_dfd, error)) - return glnx_prefix_error (error, "Opening cachedir"); + if (!glnx_opendirat (AT_FDCWD, opt_cachedir, TRUE, &self->cachedir_dfd, error)) + return glnx_prefix_error (error, "Opening cachedir"); - /* Put workdir beneath cachedir, which is where the pkgcache repo also is */ - if (!glnx_mkdtempat (self->cachedir_dfd, "rpm-ostree-compose.XXXXXX", 0700, - &self->workdir_tmp, error)) - return FALSE; - } - else - { - /* Put cachedir under the target repo if it's not on NFS or fuse. It makes things - * more efficient if it's bare-user, and otherwise just restricts IO to within the - * same fs. If for whatever reason users don't want to run the compose there (e.g. - * weird filesystems that aren't fully POSIX compliant), they can just use - * --cachedir. - */ - if (!repo_is_on_netfs (self->repo)) - { - if (!glnx_mkdtempat (ostree_repo_get_dfd (self->repo), - "tmp/rpm-ostree-compose.XXXXXX", 0700, - &self->workdir_tmp, error)) - return FALSE; - } - else - { - if (!glnx_mkdtempat (AT_FDCWD, "/var/tmp/rpm-ostree-compose.XXXXXX", 0700, - &self->workdir_tmp, error)) - return FALSE; - } - - self->cachedir_dfd = fcntl (self->workdir_tmp.fd, F_DUPFD_CLOEXEC, 3); - if (self->cachedir_dfd < 0) - return glnx_throw_errno_prefix (error, "fcntl"); - } - - self->pkgcache_repo = ostree_repo_create_at (self->cachedir_dfd, "pkgcache-repo", - OSTREE_REPO_MODE_BARE_USER, NULL, - cancellable, error); - if (!self->pkgcache_repo) + /* Put workdir beneath cachedir, which is where the pkgcache repo also is */ + if (!glnx_mkdtempat (self->cachedir_dfd, "rpm-ostree-compose.XXXXXX", 0700, + &self->workdir_tmp, error)) return FALSE; - - /* We use a temporary repo for building and committing on the same FS as the - * pkgcache to guarantee links and devino caching. We then pull-local into the "real" - * target repo. */ - self->build_repo = ostree_repo_create_at (self->cachedir_dfd, "repo-build", - OSTREE_REPO_MODE_BARE_USER, NULL, - cancellable, error); - if (!self->build_repo) - return glnx_prefix_error (error, "Creating repo-build"); - - /* Note special handling of this aliasing in rpm_ostree_tree_compose_context_free() */ - self->workdir_dfd = self->workdir_tmp.fd; } else { - if (!opt_workdir) + /* Put cachedir under the target repo if it's not on NFS or fuse. It makes things + * more efficient if it's bare-user, and otherwise just restricts IO to within the + * same fs. If for whatever reason users don't want to run the compose there (e.g. + * weird filesystems that aren't fully POSIX compliant), they can just use + * --cachedir. + */ + if (!repo_is_on_netfs (self->repo)) { - if (!glnx_mkdtempat (AT_FDCWD, "/var/tmp/rpm-ostree.XXXXXX", 0700, &self->workdir_tmp, error)) + if (!glnx_mkdtempat (ostree_repo_get_dfd (self->repo), + "tmp/rpm-ostree-compose.XXXXXX", 0700, + &self->workdir_tmp, error)) return FALSE; - /* Note special handling of this aliasing in rpm_ostree_tree_compose_context_free() */ - self->workdir_dfd = self->workdir_tmp.fd; } else { - if (!glnx_opendirat (AT_FDCWD, opt_workdir, FALSE, &self->workdir_dfd, error)) + if (!glnx_mkdtempat (AT_FDCWD, "/var/tmp/rpm-ostree-compose.XXXXXX", 0700, + &self->workdir_tmp, error)) return FALSE; } - if (opt_cachedir) - { - if (!glnx_opendirat (AT_FDCWD, opt_cachedir, TRUE, &self->cachedir_dfd, error)) - return glnx_prefix_error (error, "Opening cachedir"); - } - else - { - self->cachedir_dfd = fcntl (self->workdir_dfd, F_DUPFD_CLOEXEC, 3); - if (self->cachedir_dfd < 0) - return glnx_throw_errno_prefix (error, "fcntl"); - } - - self->build_repo = g_object_ref (self->repo); + self->cachedir_dfd = fcntl (self->workdir_tmp.fd, F_DUPFD_CLOEXEC, 3); + if (self->cachedir_dfd < 0) + return glnx_throw_errno_prefix (error, "fcntl"); } + self->pkgcache_repo = ostree_repo_create_at (self->cachedir_dfd, "pkgcache-repo", + OSTREE_REPO_MODE_BARE_USER, NULL, + cancellable, error); + if (!self->pkgcache_repo) + return FALSE; + + /* We use a temporary repo for building and committing on the same FS as the + * pkgcache to guarantee links and devino caching. We then pull-local into the "real" + * target repo. */ + self->build_repo = ostree_repo_create_at (self->cachedir_dfd, "repo-build", + OSTREE_REPO_MODE_BARE_USER, NULL, + cancellable, error); + if (!self->build_repo) + return glnx_prefix_error (error, "Creating repo-build"); + + /* Note special handling of this aliasing in rpm_ostree_tree_compose_context_free() */ + self->workdir_dfd = self->workdir_tmp.fd; + self->treefile_path = g_file_new_for_path (treefile_pathstr); self->metadata = rpmostree_composeutil_read_json_metadata (opt_metadata_json, error); @@ -694,7 +599,7 @@ rpm_ostree_compose_context_new (const char *treefile_pathstr, self->treespec = rpmostree_composeutil_get_treespec (self->corectx, self->treefile_rs, self->treefile, - opt_unified_core, + TRUE, error); if (!self->treespec) return FALSE; @@ -727,27 +632,15 @@ impl_install_tree (RpmOstreeTreeComposeContext *self, /* Without specifying --cachedir we'd just toss the data we download, so let's * catch that. */ - if ((opt_download_only || opt_download_only_rpms) && !opt_unified_core && !opt_cachedir) + if ((opt_download_only || opt_download_only_rpms) && !opt_cachedir) return glnx_throw (error, "--download-only can only be used with --cachedir"); if (getuid () != 0) { - if (!opt_unified_core) - return glnx_throw (error, "This command requires root privileges"); g_printerr ("NOTICE: Running this command as non-root is currently known not to work completely.\n"); g_printerr ("NOTICE: Proceeding anyways.\n"); } - /* This fchdir() call is...old, dates back to when rpm-ostree wrapped - * running yum as a subprocess. It shouldn't be necessary any more, - * but let's be conservative and not do it in unified core mode. - */ - if (!opt_unified_core) - { - if (fchdir (self->workdir_dfd) != 0) - return glnx_throw_errno_prefix (error, "fchdir"); - } - /* Read the previous commit. Note we don't actually *need* the full commit; really, only * if one uses `check-passwd: { "type": "previous" }`. There are a few other optimizations * too, e.g. using the previous SELinux policy in unified core. Also, we might need the @@ -1025,17 +918,12 @@ impl_commit_tree (RpmOstreeTreeComposeContext *self, statsp = &stats; } - if (!opt_unified_core) - g_assert (self->repo == self->build_repo); - else - { - /* Now we actually pull it into the target repo specified by the user */ - g_assert (self->repo != self->build_repo); + /* Now we actually pull it into the target repo specified by the user */ + g_assert (self->repo != self->build_repo); - if (!pull_local_into_target_repo (self->build_repo, self->repo, new_revision, - cancellable, error)) - return FALSE; - } + if (!pull_local_into_target_repo (self->build_repo, self->repo, new_revision, + cancellable, error)) + return FALSE; g_autoptr(GVariant) new_commit = NULL; if (!ostree_repo_load_commit (self->repo, new_revision, &new_commit, NULL, error)) @@ -1120,14 +1008,11 @@ rpmostree_compose_builtin_install (int argc, self->failed = TRUE; return FALSE; } - if (opt_unified_core) - { - if (!glnx_renameat (self->workdir_tmp.src_dfd, self->workdir_tmp.path, - AT_FDCWD, destdir, error)) - return FALSE; - glnx_tmpdir_unset (&self->workdir_tmp); - self->workdir_dfd = -1; - } + if (!glnx_renameat (self->workdir_tmp.src_dfd, self->workdir_tmp.path, + AT_FDCWD, destdir, error)) + return FALSE; + glnx_tmpdir_unset (&self->workdir_tmp); + self->workdir_dfd = -1; g_print ("rootfs: %s/rootfs\n", destdir); return TRUE; @@ -1186,7 +1071,7 @@ rpmostree_compose_builtin_postprocess (int argc, return FALSE; if (!rpmostree_rootfs_postprocess_common (rootfs_dfd, cancellable, error)) return FALSE; - if (!rpmostree_postprocess_final (rootfs_dfd, treefile_rs, treefile, opt_unified_core, + if (!rpmostree_postprocess_final (rootfs_dfd, treefile_rs, treefile, TRUE, cancellable, error)) return FALSE; return TRUE; diff --git a/src/app/rpmostree-composeutil.c b/src/app/rpmostree-composeutil.c index 2f9426585c..2ec3138e6e 100644 --- a/src/app/rpmostree-composeutil.c +++ b/src/app/rpmostree-composeutil.c @@ -96,58 +96,6 @@ rpmostree_composeutil_checksum (HyGoal goal, return TRUE; } -/* Prepare /dev in the target root with the API devices. TODO: - * Delete this when we implement https://github.com/projectatomic/rpm-ostree/issues/729 - */ -gboolean -rpmostree_composeutil_legacy_prep_dev (int rootfs_dfd, - GError **error) -{ - - glnx_autofd int src_fd = openat (AT_FDCWD, "/dev", O_RDONLY | O_NONBLOCK | O_DIRECTORY | O_CLOEXEC | O_NOCTTY); - if (src_fd == -1) - return glnx_throw_errno (error); - - if (mkdirat (rootfs_dfd, "dev", 0755) != 0) - { - if (errno != ENOENT) - return glnx_throw_errno (error); - } - - glnx_autofd int dest_fd = openat (rootfs_dfd, "dev", O_RDONLY | O_NONBLOCK | O_DIRECTORY | O_CLOEXEC | O_NOCTTY); - if (dest_fd == -1) - return glnx_throw_errno (error); - - static const char *const devnodes[] = { "null", "zero", "full", "random", "urandom", "tty" }; - for (guint i = 0; i < G_N_ELEMENTS (devnodes); i++) - { - const char *nodename = devnodes[i]; - struct stat stbuf; - if (!glnx_fstatat_allow_noent (src_fd, nodename, &stbuf, 0, error)) - return FALSE; - if (errno == ENOENT) - continue; - - if (mknodat (dest_fd, nodename, stbuf.st_mode, stbuf.st_rdev) != 0) - return glnx_throw_errno (error); - if (fchmodat (dest_fd, nodename, stbuf.st_mode, 0) != 0) - return glnx_throw_errno (error); - } - - { GLNX_AUTO_PREFIX_ERROR ("Testing /dev/null in target root (is nodev set?)", error); - glnx_autofd int devnull_fd = -1; - if (!glnx_openat_rdonly (dest_fd, "null", TRUE, &devnull_fd, error)) - return FALSE; - char buf[1]; - ssize_t s = read (devnull_fd, buf, sizeof (buf)); - if (s < 0) - return glnx_throw_errno_prefix (error, "read"); - } - - return TRUE; -} - - gboolean rpmostree_composeutil_sanity_checks (RORTreefile *tf, JsonObject *treefile, diff --git a/src/app/rpmostree-composeutil.h b/src/app/rpmostree-composeutil.h index 11a19328fb..36032d163c 100644 --- a/src/app/rpmostree-composeutil.h +++ b/src/app/rpmostree-composeutil.h @@ -34,10 +34,6 @@ rpmostree_composeutil_checksum (HyGoal goal, char **out_checksum, GError **error); -gboolean -rpmostree_composeutil_legacy_prep_dev (int rootfs_dfd, - GError **error); - gboolean rpmostree_composeutil_sanity_checks (RORTreefile *tf, JsonObject *treefile, diff --git a/src/libpriv/rpmostree-postprocess.c b/src/libpriv/rpmostree-postprocess.c index d6c73280c7..e49e53863a 100644 --- a/src/libpriv/rpmostree-postprocess.c +++ b/src/libpriv/rpmostree-postprocess.c @@ -469,195 +469,6 @@ process_kernel_and_initramfs (int rootfs_dfd, return TRUE; } -static gboolean -convert_var_to_tmpfiles_d_recurse (GOutputStream *tmpfiles_out, - int dfd, - RpmOstreePasswdDB *pwdb, - GString *prefix, - GCancellable *cancellable, - GError **error) -{ - g_auto(GLnxDirFdIterator) dfd_iter = { 0, }; - gsize bytes_written; - - if (!glnx_dirfd_iterator_init_at (dfd, prefix->str + 1, TRUE, &dfd_iter, error)) - return FALSE; - - while (TRUE) - { - struct dirent *dent = NULL; - if (!glnx_dirfd_iterator_next_dent_ensure_dtype (&dfd_iter, &dent, cancellable, error)) - return FALSE; - if (!dent) - break; - - char filetype_c; - switch (dent->d_type) - { - case DT_DIR: - filetype_c = 'd'; - break; - case DT_LNK: - filetype_c = 'L'; - break; - case DT_REG: - /* nfs-utils in RHEL7; https://bugzilla.redhat.com/show_bug.cgi?id=1427537 */ - if (g_str_has_prefix (prefix->str, "/var/lib/nfs")) - { - filetype_c = 'f'; - break; - } - /* Fallthrough */ - default: - if (!glnx_unlinkat (dfd_iter.fd, dent->d_name, 0, error)) - return FALSE; - g_print ("Ignoring non-directory/non-symlink '%s/%s'\n", - prefix->str, - dent->d_name); - continue; - } - - g_autoptr(GString) tmpfiles_d_buf = g_string_new (""); - g_string_append_c (tmpfiles_d_buf, filetype_c); - g_string_append_c (tmpfiles_d_buf, ' '); - g_string_append (tmpfiles_d_buf, prefix->str); - g_string_append_c (tmpfiles_d_buf, '/'); - g_string_append (tmpfiles_d_buf, dent->d_name); - - if (filetype_c == 'd' || filetype_c == 'f') - { - struct stat stbuf; - if (!glnx_fstatat (dfd_iter.fd, dent->d_name, &stbuf, AT_SYMLINK_NOFOLLOW, error)) - return FALSE; - - g_string_append_printf (tmpfiles_d_buf, " 0%02o", stbuf.st_mode & ~S_IFMT); - const char *user = rpmostree_passwddb_lookup_user (pwdb, stbuf.st_uid); - if (!user) - return glnx_throw (error, "Failed to find user '%u' for %s", stbuf.st_uid, dent->d_name); - const char *group = rpmostree_passwddb_lookup_group (pwdb, stbuf.st_gid); - if (!group) - return glnx_throw (error, "Failed to find group '%u' for %s", stbuf.st_gid, dent->d_name); - g_string_append_printf (tmpfiles_d_buf, " %s %s - -", user, group); - - if (filetype_c == 'd') - { - /* Push prefix */ - gsize prev_len = prefix->len; - g_string_append_c (prefix, '/'); - g_string_append (prefix, dent->d_name); - - if (!convert_var_to_tmpfiles_d_recurse (tmpfiles_out, dfd, pwdb, prefix, - cancellable, error)) - return FALSE; - - /* Pop prefix */ - g_string_truncate (prefix, prev_len); - } - } - else - { - g_autofree char *link = glnx_readlinkat_malloc (dfd_iter.fd, dent->d_name, cancellable, error); - if (!link) - return FALSE; - g_string_append (tmpfiles_d_buf, " - - - - "); - g_string_append (tmpfiles_d_buf, link); - - } - - if (!glnx_unlinkat (dfd_iter.fd, dent->d_name, - dent->d_type == DT_DIR ? AT_REMOVEDIR : 0, error)) - return FALSE; - - g_string_append_c (tmpfiles_d_buf, '\n'); - - if (!g_output_stream_write_all (tmpfiles_out, tmpfiles_d_buf->str, - tmpfiles_d_buf->len, &bytes_written, - cancellable, error)) - return FALSE; - } - - return TRUE; -} - -static gboolean -convert_var_to_tmpfiles_d (int rootfs_dfd, - GCancellable *cancellable, - GError **error) -{ - GLNX_AUTO_PREFIX_ERROR ("Converting /var to tmpfiles.d", error); - - g_autoptr(RpmOstreePasswdDB) pwdb = rpmostree_passwddb_open (rootfs_dfd, cancellable, error); - if (!pwdb) - return FALSE; - - glnx_autofd int var_dfd = -1; - /* List of files that are known to possibly exist, but in practice - * things work fine if we simply ignore them. Don't add something - * to this list unless you've verified it's handled correctly at - * runtime. (And really both in CentOS and Fedora) - */ - const char *known_state_files[] = { - "lib/systemd/random-seed", /* https://bugzilla.redhat.com/show_bug.cgi?id=789407 */ - "lib/systemd/catalog/database", - "lib/plymouth/boot-duration", - "log/wtmp", /* These two are part of systemd's var.tmp */ - "log/btmp", - }; - - if (!glnx_opendirat (rootfs_dfd, "var", TRUE, &var_dfd, error)) - return FALSE; - - /* We never want to traverse into /run when making tmpfiles since it's a tmpfs */ - /* Note that in a Fedora root, /var/run is a symlink, though on el7, it can be a dir. - * See: https://github.com/projectatomic/rpm-ostree/pull/831 */ - if (!glnx_shutil_rm_rf_at (var_dfd, "run", cancellable, error)) - return FALSE; - - /* Here, delete some files ahead of time to avoid emitting warnings - * for things that are known to be harmless. - */ - for (guint i = 0; i < G_N_ELEMENTS (known_state_files); i++) - { - const char *path = known_state_files[i]; - if (unlinkat (var_dfd, path, 0) < 0) - { - if (errno != ENOENT) - return glnx_throw_errno_prefix (error, "unlinkat(%s)", path); - } - } - - /* Convert /var wholesale to tmpfiles.d. Note that with unified core, this - * code should no longer be necessary as we convert packages on import. - */ - g_auto(GLnxTmpfile) tmpf = { 0, }; - if (!glnx_open_tmpfile_linkable_at (rootfs_dfd, "usr/lib/tmpfiles.d", O_WRONLY | O_CLOEXEC, - &tmpf, error)) - return FALSE; - g_autoptr(GOutputStream) tmpfiles_out = g_unix_output_stream_new (tmpf.fd, FALSE); - if (!tmpfiles_out) - return FALSE; - - g_autoptr(GString) prefix = g_string_new ("/var"); - if (!convert_var_to_tmpfiles_d_recurse (tmpfiles_out, rootfs_dfd, pwdb, prefix, cancellable, error)) - return FALSE; - - if (!g_output_stream_close (tmpfiles_out, cancellable, error)) - return FALSE; - - /* Make it world-readable, no reason why not to - * https://bugzilla.redhat.com/show_bug.cgi?id=1631794 - */ - if (!glnx_fchmod (tmpf.fd, 0644, error)) - return FALSE; - - if (!glnx_link_tmpfile_at (&tmpf, GLNX_LINK_TMPFILE_NOREPLACE, - rootfs_dfd, "usr/lib/tmpfiles.d/rpm-ostree-1-autovar.conf", - error)) - return FALSE; - - return TRUE; -} - /* SELinux uses PCRE pre-compiled regexps for binary caches, which can * fail if the version of PCRE on the host differs from the version * which generated the cache (in the target root). @@ -1025,14 +836,30 @@ rpmostree_postprocess_final (int rootfs_dfd, return glnx_prefix_error (error, "SELinux postprocess"); } - if (!convert_var_to_tmpfiles_d (rootfs_dfd, cancellable, error)) - return FALSE; - if (!rpmostree_rootfs_prepare_links (rootfs_dfd, cancellable, error)) return FALSE; if (!rpmostree_rootfs_postprocess_common (rootfs_dfd, cancellable, error)) return FALSE; + /* Clean out /var */ + { g_auto(GLnxDirFdIterator) dfd_iter = { 0, }; + + if (!glnx_dirfd_iterator_init_at (rootfs_dfd, "var", TRUE, &dfd_iter, error)) + return FALSE; + + while (TRUE) + { + struct dirent *dent = NULL; + if (!glnx_dirfd_iterator_next_dent (&dfd_iter, &dent, cancellable, error)) + return FALSE; + if (!dent) + break; + + if (!glnx_shutil_rm_rf_at (dfd_iter.fd, dent->d_name, cancellable, error)) + return FALSE; + } + } + g_print ("Adding rpm-ostree-0-integration.conf\n"); /* This is useful if we're running in an uninstalled configuration, e.g. * during tests. */ diff --git a/tests/compose b/tests/compose index cb6fca5e71..a5e3a2a76f 100755 --- a/tests/compose +++ b/tests/compose @@ -42,17 +42,21 @@ test ${uid} = ${datadir_owner} # Create a consistent cache of the RPMs echo "Preparing compose tests... $(date)" -tmp_repo=${test_compose_datadir}/tmp-repo if test -z "${RPMOSTREE_COMPOSE_CACHEONLY:-}"; then setup_rpmmd_repos ${dn}/composedata + mkdir -p ${test_compose_datadir} + tmp_repo=${test_compose_datadir}/tmp-repo ostree --repo=${tmp_repo} init --mode=bare-user - # Ensure all subsequent tests have the RPMs + # Download the RPMs and cache them in `fedora-local`; + # they're needed by the rojig e2e tests. mkdir -p ${test_compose_datadir}/{fedora-local,cache} rpm-ostree compose --repo=${tmp_repo} tree --download-only-rpms --cachedir=${test_compose_datadir}/cache ${dn}/composedata/fedora-base.json find ${test_compose_datadir}/cache/ -name '*.rpm' | while read f; do - mv $f ${test_compose_datadir}/fedora-local + ln $f ${test_compose_datadir}/fedora-local done (cd ${test_compose_datadir}/fedora-local && createrepo_c .) + # And also import the RPMs once + rpm-ostree compose --repo=${tmp_repo} tree --download-only --cache-only --cachedir=${test_compose_datadir} ${dn}/composedata/fedora-base.json fi echo "Done preparing compose tests! $(date)" rm ${tmp_repo} -rf diff --git a/tests/compose-tests/libcomposetest.sh b/tests/compose-tests/libcomposetest.sh index 1f13bdb410..17a70cfb4e 100644 --- a/tests/compose-tests/libcomposetest.sh +++ b/tests/compose-tests/libcomposetest.sh @@ -60,16 +60,22 @@ with open(ofn, "w") as f: EOF export treefile=composedata/fedora-${name}.yaml fi + # The workdir will be cleaned up (or not) with the overall test dir + if ! [ -d cache ]; then + mkdir cache + ostree --repo=cache/pkgcache-repo init --mode=bare-user + echo 'fsync=false' >> cache/pkgcache-repo/config + # Clone the pkgcache + ostree --repo=cache/pkgcache-repo pull-local ${test_compose_datadir}/pkgcache-repo + # And copy everything else to avoid locking issues + cp -a --reflink=auto ${test_compose_datadir}/cache cache/cache + fi } composejson=$(pwd)/compose.json -compose_workdir=${test_tmpdir}/workdir -compose_base_argv="--workdir ${compose_workdir} --repo ${repobuild} --write-composejson-to ${composejson}" +compose_base_argv="--cachedir=./cache --cache-only --repo ${repobuild} --write-composejson-to ${composejson}" runcompose() { echo "$(date): starting compose" - # The workdir will be cleaned up (or not) with the overall test dir - rm ${compose_workdir} -rf - mkdir ${test_tmpdir}/workdir env RPMOSTREE_PRESERVE_TMPDIR=1 rpm-ostree compose tree ${compose_base_argv} ${treefile} "$@" commit=$(jq -r '.["ostree-commit"]' < "${composejson}") ostree --repo=${repo} pull-local ${repobuild} "${treeref:-${commit}}" diff --git a/tests/compose-tests/test-basic-unified.sh b/tests/compose-tests/test-basic-unified.sh deleted file mode 100755 index 81d43f1915..0000000000 --- a/tests/compose-tests/test-basic-unified.sh +++ /dev/null @@ -1,72 +0,0 @@ -#!/bin/bash - -set -xeuo pipefail - -dn=$(cd $(dirname $0) && pwd) -. ${dn}/libcomposetest.sh - -prepare_compose_test "basic-unified" -# Test --print-only, currently requires --repo. We also -# just in this test (for now) use ${basearch} to test substitution. -pysetjsonmember "ref" '"fedora/stable/${basearch}/basic-unified"' -rpm-ostree compose tree --repo=${repobuild} --print-only ${treefile} > treefile.json -# Verify it's valid JSON -jq -r .ref < treefile.json > ref.txt -# Test substitution of ${basearch} -assert_file_has_content_literal ref.txt "${treeref}" -# Test metadata json with objects, arrays, numbers -cat > metadata.json < autovar.txt -# Picked this one at random as an example of something that won't likely be -# converted to tmpfiles.d upstream. But if it is, we can change this test. -assert_file_has_content_literal autovar.txt 'd /var/cache 0755 root root - -' -ostree --repo=${repobuild} cat ${treeref} /usr/lib/tmpfiles.d/pkg-chrony.conf > autovar.txt -# And this one has a non-root uid -assert_file_has_content_literal autovar.txt 'd /var/log/chrony 0755 chrony chrony - -' -# see rpmostree-importer.c -if ostree --repo=${repobuild} cat ${treeref} /usr/lib/tmpfiles.d/pkg-rpm.conf > rpm.txt 2>/dev/null; then - assert_not_file_has_content rpm.txt 'd /var/lib/rpm' -fi -echo "ok autovar" - -# And verify we're not hardlinking zero-sized files since this path isn't using -# rofiles-fuse -co=${repobuild}/tmp/usr-etc -ostree --repo=${repobuild} checkout -UHz --subpath=/usr/etc ${treeref} ${co} -# Verify the files exist and are zero-sized -for f in ${co}/sub{u,g}id; do - test -f "$f" - test '!' -s "$f" -done -if files_are_hardlinked ${co}/sub{u,g}id; then - fatal "Hardlinked zero-sized files without cachedir" -fi -rm ${co} -rf -echo "ok no cachedir zero-sized hardlinks" - -# And redo it to trigger relabeling -origrev=$(ostree --repo=${repobuild} rev-parse ${treeref}) -runcompose --force-nocache --ex-unified-core -newrev=$(ostree --repo=${repobuild} rev-parse ${treeref}) -assert_not_streq "${origrev}" "${newrev}" - -echo "ok rerun" diff --git a/tests/compose-tests/test-basic.sh b/tests/compose-tests/test-basic.sh index bb43f7b496..81d43f1915 100755 --- a/tests/compose-tests/test-basic.sh +++ b/tests/compose-tests/test-basic.sh @@ -5,7 +5,15 @@ set -xeuo pipefail dn=$(cd $(dirname $0) && pwd) . ${dn}/libcomposetest.sh -prepare_compose_test "basic" +prepare_compose_test "basic-unified" +# Test --print-only, currently requires --repo. We also +# just in this test (for now) use ${basearch} to test substitution. +pysetjsonmember "ref" '"fedora/stable/${basearch}/basic-unified"' +rpm-ostree compose tree --repo=${repobuild} --print-only ${treefile} > treefile.json +# Verify it's valid JSON +jq -r .ref < treefile.json > ref.txt +# Test substitution of ${basearch} +assert_file_has_content_literal ref.txt "${treeref}" # Test metadata json with objects, arrays, numbers cat > metadata.json < metadata.json < autovar.txt +ostree --repo=${repobuild} cat ${treeref} /usr/lib/tmpfiles.d/pkg-filesystem.conf > autovar.txt # Picked this one at random as an example of something that won't likely be # converted to tmpfiles.d upstream. But if it is, we can change this test. assert_file_has_content_literal autovar.txt 'd /var/cache 0755 root root - -' +ostree --repo=${repobuild} cat ${treeref} /usr/lib/tmpfiles.d/pkg-chrony.conf > autovar.txt # And this one has a non-root uid assert_file_has_content_literal autovar.txt 'd /var/log/chrony 0755 chrony chrony - -' +# see rpmostree-importer.c +if ostree --repo=${repobuild} cat ${treeref} /usr/lib/tmpfiles.d/pkg-rpm.conf > rpm.txt 2>/dev/null; then + assert_not_file_has_content rpm.txt 'd /var/lib/rpm' +fi echo "ok autovar" -ostree --repo=${repobuild} cat ${treeref} /usr/lib/systemd/system-preset/40-rpm-ostree-auto.preset > preset.txt -assert_file_has_content preset.txt '^enable ostree-remount.service$' -assert_file_has_content preset.txt '^enable ostree-finalize-staged.path$' +# And verify we're not hardlinking zero-sized files since this path isn't using +# rofiles-fuse +co=${repobuild}/tmp/usr-etc +ostree --repo=${repobuild} checkout -UHz --subpath=/usr/etc ${treeref} ${co} +# Verify the files exist and are zero-sized +for f in ${co}/sub{u,g}id; do + test -f "$f" + test '!' -s "$f" +done +if files_are_hardlinked ${co}/sub{u,g}id; then + fatal "Hardlinked zero-sized files without cachedir" +fi +rm ${co} -rf +echo "ok no cachedir zero-sized hardlinks" -prepare_compose_test "from-yaml" -python <> yumrepo.repo ln yumrepo.repo composedata/test-repo.repo pyappendjsonmember "packages" '["test-pkg"]' pysetjsonmember "documentation" 'False' -# Need unified core for this, as well as a cachedir -mkdir cache -runcompose --unified-core --cachedir $(pwd)/cache --add-metadata-string version=42.0 +runcompose --add-metadata-string version=42.0 npkgs=$(rpm-ostree --repo=${repobuild} db list ${treeref} |grep -v '^ostree commit' | wc -l) echo "npkgs=${npkgs}" rpm-ostree --repo=${repobuild} db list ${treeref} test-pkg >test-pkg-list.txt @@ -49,7 +47,7 @@ gpgcheck=0 eof do_rojig2commit() { echo "$(date): starting rojig2commit" - rpm-ostree ex rojig2commit -d $(pwd)/composedata -e fedora-local -e test-repo -e rojig-test --repo=rojig-unpack-repo rojig-test:fedora-atomic-host | tee rojig2commit-out.txt + rpm-ostree ex rojig2commit -d $(pwd)/composedata -e test-repo -e rojig-test --repo=rojig-unpack-repo rojig-test:fedora-atomic-host | tee rojig2commit-out.txt echo "$(date): finished rojig2commit" } do_rojig2commit @@ -76,7 +74,7 @@ build_rpm test-newpkg \ files "/usr/bin/test-newpkg" \ install "mkdir -p %{buildroot}/usr/bin && echo new localpkg data > %{buildroot}/usr/bin/test-newpkg" pyappendjsonmember "packages" '["test-newpkg"]' -runcompose --unified-core --cachedir $(pwd)/cache --add-metadata-string version=42.1 +runcompose --add-metadata-string version=42.1 newrev=$(ostree --repo=${repobuild} rev-parse ${treeref}) rpm-ostree --repo=${repobuild} db list ${treeref} test-newpkg >test-newpkg-list.txt assert_file_has_content test-newpkg-list.txt 'test-newpkg-1.0-1.x86_64' @@ -107,7 +105,7 @@ echo "ok rojig ♲📦 update!" # Add all docs to test https://github.com/projectatomic/rpm-ostree/issues/1197 pysetjsonmember "documentation" 'True' -runcompose --unified-core --cachedir $(pwd)/cache --add-metadata-string version=42.2 +runcompose --add-metadata-string version=42.2 newrev=$(ostree --repo=${repobuild} rev-parse ${treeref}) do_commit2rojig ${newrev} find rojig-output -name '*.rpm' | tee rpms.txt