diff --git a/Makefile-tests.am b/Makefile-tests.am index e9a47913d6..49be14ab8d 100644 --- a/Makefile-tests.am +++ b/Makefile-tests.am @@ -42,6 +42,7 @@ tests/test-ucontainer.sh: tests/compose/test-repo-local.repo installed_test_data = tests/libtest.sh \ tests/compose/test-repo.repo \ tests/compose/test-repo.json \ + tests/compose/test-repo-add-files.json \ tests/compose/test-repo.repo.in \ $(NULL) diff --git a/docs/manual/treefile.md b/docs/manual/treefile.md index f25e3645df..2a95cac4c4 100644 --- a/docs/manual/treefile.md +++ b/docs/manual/treefile.md @@ -167,3 +167,11 @@ It supports the following parameters: rpm-ostree will not do any special handling of kernel, initrd or the /boot directory. This is useful if the target for the tree is some kind of container which does not have its own kernel. + + * `copy-files`: Array, optional: Copy external files to the rootfs. + + Each array element is an array, whose first member is the source + file name, and the second element is the destination name. The + source file must be in the same directory as the treefile. + + Example: `"add-files": [["bar", "/bar"], ["foo", "/foo"]]` diff --git a/src/app/rpmostree-compose-builtin-tree.c b/src/app/rpmostree-compose-builtin-tree.c index efbb20c775..5c3f5c8d27 100644 --- a/src/app/rpmostree-compose-builtin-tree.c +++ b/src/app/rpmostree-compose-builtin-tree.c @@ -101,6 +101,8 @@ typedef struct { static gboolean compute_checksum_from_treefile_and_goal (RpmOstreeTreeComposeContext *self, HyGoal goal, + GFile *contextdir, + JsonArray *add_files, char **out_checksum, GError **error) { @@ -118,6 +120,43 @@ compute_checksum_from_treefile_and_goal (RpmOstreeTreeComposeContext *self, g_checksum_update (checksum, buf, len); } + if (add_files) + { + guint i, len = json_array_get_length (add_files); + for (i = 0; i < len; i++) + { + gs_unref_object GFile *srcfile = NULL; + const char *src, *dest; + JsonArray *add_el = json_array_get_array_element (add_files, i); + gs_unref_object GFile *child = NULL; + + if (!add_el) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "Element in add-files is not an array"); + goto out; + } + src = _rpmostree_jsonutil_array_require_string_element (add_el, 0, error); + if (!src) + goto out; + + dest = _rpmostree_jsonutil_array_require_string_element (add_el, 1, error); + if (!dest) + goto out; + + srcfile = g_file_resolve_relative_path (contextdir, src); + + if (!_rpmostree_util_update_checksum_from_file (checksum, + srcfile, + NULL, + error)) + goto out; + + g_checksum_update (checksum, (const guint8 *) dest, strlen (dest)); + } + + } + /* FIXME; we should also hash the post script */ /* Hash in each package */ @@ -126,6 +165,7 @@ compute_checksum_from_treefile_and_goal (RpmOstreeTreeComposeContext *self, ret_checksum = g_strdup (g_checksum_get_string (checksum)); ret = TRUE; + out: gs_transfer_out_value (out_checksum, &ret_checksum); if (checksum) g_checksum_free (checksum); return ret; @@ -190,6 +230,7 @@ install_packages_in_root (RpmOstreeTreeComposeContext *self, gs_free char *ret_new_inputhash = NULL; g_autoptr(GKeyFile) treespec = g_key_file_new (); JsonArray *enable_repos = NULL; + JsonArray *add_files = NULL; /* TODO - uncomment this once we have SELinux working */ #if 0 @@ -268,8 +309,12 @@ install_packages_in_root (RpmOstreeTreeComposeContext *self, if (!rpmostree_context_prepare_install (ctx, &hifinstall, cancellable, error)) goto out; + if (json_object_has_member (treedata, "add-files")) + add_files = json_object_get_array_member (treedata, "add-files"); + /* FIXME - just do a depsolve here before we compute download requirements */ if (!compute_checksum_from_treefile_and_goal (self, hif_context_get_goal (hifctx), + contextdir, add_files, &ret_new_inputhash, error)) goto out; @@ -796,6 +841,9 @@ rpmostree_compose_builtin_tree (int argc, if (!rpmostree_prepare_rootfs_for_commit (yumroot, treefile, cancellable, error)) goto out; + if (!rpmostree_copy_additional_files (yumroot, self->treefile_context_dirs->pdata[0], treefile, cancellable, error)) + goto out; + if (!rpmostree_check_passwd (repo, yumroot, treefile_dirpath, treefile, cancellable, error)) goto out; diff --git a/src/libpriv/rpmostree-postprocess.c b/src/libpriv/rpmostree-postprocess.c index 675a304d0b..f95601a339 100644 --- a/src/libpriv/rpmostree-postprocess.c +++ b/src/libpriv/rpmostree-postprocess.c @@ -1171,6 +1171,83 @@ rpmostree_rootfs_postprocess_common (int rootfs_fd, return ret; } +/** + * rpmostree_copy_additional_files: + * + * Copy external files, if specified in the configuration file, from + * the context directory to the rootfs. + */ +gboolean +rpmostree_copy_additional_files (GFile *rootfs, + GFile *context_directory, + JsonObject *treefile, + GCancellable *cancellable, + GError **error) +{ + gboolean ret = FALSE; + JsonArray *add = NULL; + guint i, len; + + gs_free char *dest_rootfs_path = g_strconcat (gs_file_get_path_cached (rootfs), ".post", NULL); + gs_unref_object GFile *targetroot = g_file_new_for_path (dest_rootfs_path); + + if (json_object_has_member (treefile, "add-files")) + { + add = json_object_get_array_member (treefile, "add-files"); + len = json_array_get_length (add); + } + else + { + ret = TRUE; + goto out; + } + + for (i = 0; i < len; i++) + { + const char *src, *dest; + + JsonArray *add_el = json_array_get_array_element (add, i); + gs_unref_object GFile *child = NULL; + + if (!add_el) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "Element in add-files is not an array"); + goto out; + } + + src = _rpmostree_jsonutil_array_require_string_element (add_el, 0, error); + if (!src) + goto out; + + dest = _rpmostree_jsonutil_array_require_string_element (add_el, 1, error); + if (!dest) + goto out; + + { + gs_unref_object GFile *srcfile = g_file_resolve_relative_path (context_directory, src); + const char *rootfs_path = gs_file_get_path_cached (rootfs); + gs_free char *destpath = g_strconcat (rootfs_path, "/", dest, NULL); + gs_unref_object GFile *destfile = g_file_resolve_relative_path (targetroot, destpath); + gs_unref_object GFile *target_tmpfilesd_parent = g_file_get_parent (destfile); + + g_print ("Adding file '%s'\n", dest); + + if (!gs_file_ensure_directory (target_tmpfilesd_parent, TRUE, cancellable, error)) + goto out; + + if (!g_file_copy (srcfile, destfile, 0, cancellable, NULL, NULL, error)) + { + g_prefix_error (error, "Copying file '%s' into target: ", src); + goto out; + } + } + } + ret = TRUE; + out: + return ret; +} + gboolean rpmostree_treefile_postprocessing (GFile *yumroot, GFile *context_directory, diff --git a/src/libpriv/rpmostree-postprocess.h b/src/libpriv/rpmostree-postprocess.h index b86bd9cf72..d8fb64d399 100644 --- a/src/libpriv/rpmostree-postprocess.h +++ b/src/libpriv/rpmostree-postprocess.h @@ -51,3 +51,9 @@ rpmostree_commit (GFile *rootfs, gboolean enable_selinux, GCancellable *cancellable, GError **error); +gboolean +rpmostree_copy_additional_files (GFile *rootfs, + GFile *context_directory, + JsonObject *treefile, + GCancellable *cancellable, + GError **error); diff --git a/tests/compose/test-repo-add-files.json b/tests/compose/test-repo-add-files.json new file mode 100644 index 0000000000..8facf57da7 --- /dev/null +++ b/tests/compose/test-repo-add-files.json @@ -0,0 +1,11 @@ +{ + "ref": "fedora/test", + + "repos": ["test-repo"], + + "selinux": false, + + "packages": ["empty"], + + "add-files": [["exported_file", "/exports/exported_file"]] +} diff --git a/tests/test-compose.sh b/tests/test-compose.sh index 581ba568bb..037ddcd4d0 100755 --- a/tests/test-compose.sh +++ b/tests/test-compose.sh @@ -29,7 +29,7 @@ unset G_DEBUG (arch | grep -q x86_64) || { echo 1>&2 "$0 can be run only on x86_64"; echo "1..0" ; exit 77; } -echo "1..1" +echo "1..4" ostree init --repo=repo --mode=archive-z2 @@ -47,3 +47,32 @@ ostree --repo=repo refs >refs.txt assert_file_has_content refs.txt fedora/test echo "ok compose" + +# bring them in the current context so we can modify exported_file +ln -s $SRCDIR/test-repo-add-files.json . +ln -s $SRCDIR/test-repo.repo . + +echo hello > exported_file + +rpm-ostree --repo=repo compose tree --touch-if-changed=$(pwd)/touched test-repo-add-files.json +assert_has_file touched +old_mtime=$(stat -c %y touched) +ostree --repo=repo ls fedora/test /exports/exported_file | grep exported > exported.txt + +assert_file_has_content exported.txt "/exports/exported_file" +assert_file_has_content exported.txt "0 0" +ostree --repo=repo rev-parse fedora/test > oldref.txt +rpm-ostree --repo=repo compose tree --touch-if-changed=$(pwd)/touched test-repo-add-files.json +new_mtime=$(stat -c %y touched) +ostree --repo=repo rev-parse fedora/test > newref.txt +assert_streq $(cat oldref.txt) $(cat newref.txt) +assert_streq "$old_mtime" "$new_mtime" + +echo . >> exported_file +rpm-ostree --repo=repo compose tree --touch-if-changed=$(pwd)/touched test-repo-add-files.json +new_mtime=$(stat -c %y touched) +ostree --repo=repo rev-parse fedora/test > newref.txt +assert_not_streq $(cat oldref.txt) $(cat newref.txt) +assert_not_streq "$old_mtime" "$new_mtime" + +echo "ok compose add files"