From bdfde03b61a0df53e0a6d2e45d9e0632528f28b7 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Tue, 20 Dec 2011 17:21:15 -0500 Subject: [PATCH] core: Support committing multiple tarballs in the same transaction ostbuild will generate two artifacts: foo-runtime.tar.gz and foo-devel.tar.gz in the general case. When committing to the devel tree, it'd be lame (i.e. slower and not atomic) to have to commit twice. --- src/libostree/ostree-repo.c | 94 +++++++++++++++------------------- src/libostree/ostree-repo.h | 22 ++++---- src/ostree/ot-builtin-commit.c | 15 ++++-- tests/t0006-libarchive.sh | 30 ++++++++++- 4 files changed, 93 insertions(+), 68 deletions(-) diff --git a/src/libostree/ostree-repo.c b/src/libostree/ostree-repo.c index 575c0e7a..dffc1625 100644 --- a/src/libostree/ostree-repo.c +++ b/src/libostree/ostree-repo.c @@ -1773,24 +1773,19 @@ file_tree_import_recurse (OstreeRepo *self, return ret; } - static gboolean -import_libarchive (OstreeRepo *self, - GFile *archive_f, - OstreeRepoCommitModifier *modifier, - char **out_contents_checksum, - char **out_metadata_checksum, - GCancellable *cancellable, - GError **error) +stage_libarchive_into_root (OstreeRepo *self, + FileTree *root, + GFile *archive_f, + OstreeRepoCommitModifier *modifier, + GCancellable *cancellable, + GError **error) { gboolean ret = FALSE; int r; - char *ret_contents_checksum = NULL; - char *ret_metadata_checksum = NULL; struct archive *a; struct archive_entry *entry; GFileInfo *file_info = NULL; - FileTree *root = NULL; GChecksum *tmp_checksum = NULL; GPtrArray *split_path = NULL; GPtrArray *hardlink_split_path = NULL; @@ -1804,8 +1799,6 @@ import_libarchive (OstreeRepo *self, goto out; } - root = file_tree_new (); - while (TRUE) { const char *pathname; @@ -1922,31 +1915,24 @@ import_libarchive (OstreeRepo *self, if (parent == NULL) { dir = root; - if (root->metadata_checksum) - { - g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, - "Directory exists: %s", pathname); - goto out; - } } else { - if (g_hash_table_lookup (parent->subdirs, basename)) - { - g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, - "Directory exists: %s", pathname); - goto out; - } if (g_hash_table_lookup (parent->file_checksums, basename)) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Can't replace file with directory: %s", pathname); goto out; } - dir = file_tree_new (); - g_assert (basename); - g_hash_table_insert (parent->subdirs, g_strdup (basename), dir); + /* Allow duplicate directories */ + if (!g_hash_table_lookup (parent->subdirs, basename)) + { + dir = file_tree_new (); + g_assert (basename); + g_hash_table_replace (parent->subdirs, g_strdup (basename), dir); + } } + g_free (dir->metadata_checksum); dir->metadata_checksum = g_strdup (g_checksum_get_string (tmp_checksum)); } else @@ -1979,44 +1965,35 @@ import_libarchive (OstreeRepo *self, goto out; } - if (!file_tree_import_recurse (self, root, &ret_contents_checksum, cancellable, error)) - goto out; - ret_metadata_checksum = g_strdup (root->metadata_checksum); - ret = TRUE; - ot_transfer_out_value(out_contents_checksum, &ret_contents_checksum); - ot_transfer_out_value(out_metadata_checksum, &ret_metadata_checksum); out: - if (root) - file_tree_free (root); g_clear_object (&file_info); - g_free (ret_contents_checksum); - g_free (ret_metadata_checksum); ot_clear_checksum (&tmp_checksum); return ret; } #endif gboolean -ostree_repo_commit_tarfile (OstreeRepo *self, - const char *branch, - const char *parent, - const char *subject, - const char *body, - GVariant *metadata, - GFile *path, - OstreeRepoCommitModifier *modifier, - GChecksum **out_commit, - GCancellable *cancellable, - GError **error) +ostree_repo_commit_tarfiles (OstreeRepo *self, + const char *branch, + const char *parent, + const char *subject, + const char *body, + GVariant *metadata, + GPtrArray *tarfiles, + OstreeRepoCommitModifier *modifier, + GChecksum **out_commit, + GCancellable *cancellable, + GError **error) { #ifdef HAVE_LIBARCHIVE OstreeRepoPrivate *priv = GET_PRIVATE (self); gboolean ret = FALSE; GChecksum *ret_commit_checksum = NULL; + FileTree *root = NULL; char *root_contents_checksum = NULL; - char *root_metadata_checksum = NULL; char *current_head = NULL; + int i; g_return_val_if_fail (error == NULL || *error == NULL, FALSE); g_return_val_if_fail (priv->inited, FALSE); @@ -2033,11 +2010,21 @@ ostree_repo_commit_tarfile (OstreeRepo *self, if (!ostree_repo_resolve_rev (self, parent, TRUE, ¤t_head, error)) goto out; - if (!import_libarchive (self, path, modifier, &root_contents_checksum, &root_metadata_checksum, cancellable, error)) + root = file_tree_new (); + + for (i = 0; i < tarfiles->len; i++) + { + GFile *archive_f = tarfiles->pdata[i]; + + if (!stage_libarchive_into_root (self, root, archive_f, modifier, cancellable, error)) + goto out; + } + + if (!file_tree_import_recurse (self, root, &root_contents_checksum, cancellable, error)) goto out; if (!do_commit_write_ref (self, branch, current_head, subject, body, metadata, - root_contents_checksum, root_metadata_checksum, &ret_commit_checksum, + root_contents_checksum, root->metadata_checksum, &ret_commit_checksum, cancellable, error)) goto out; @@ -2047,8 +2034,9 @@ ostree_repo_commit_tarfile (OstreeRepo *self, out: ot_clear_checksum (&ret_commit_checksum); g_free (current_head); - g_free (root_metadata_checksum); g_free (root_contents_checksum); + if (root) + file_tree_free (root); return ret; #else g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, diff --git a/src/libostree/ostree-repo.h b/src/libostree/ostree-repo.h index 0222a629..c87aa7b5 100644 --- a/src/libostree/ostree-repo.h +++ b/src/libostree/ostree-repo.h @@ -154,17 +154,17 @@ gboolean ostree_repo_commit_directory (OstreeRepo *self, GCancellable *cancellable, GError **error); -gboolean ostree_repo_commit_tarfile (OstreeRepo *self, - const char *branch, - const char *parent, - const char *subject, - const char *body, - GVariant *metadata, - GFile *base, - OstreeRepoCommitModifier *modifier, - GChecksum **out_commit, - GCancellable *cancellable, - GError **error); +gboolean ostree_repo_commit_tarfiles (OstreeRepo *self, + const char *branch, + const char *parent, + const char *subject, + const char *body, + GVariant *metadata, + GPtrArray *tarfiles, + OstreeRepoCommitModifier *modifier, + GChecksum **out_commit, + GCancellable *cancellable, + GError **error); typedef enum { OSTREE_REPO_CHECKOUT_MODE_NONE, diff --git a/src/ostree/ot-builtin-commit.c b/src/ostree/ot-builtin-commit.c index 61f62c63..0147eb03 100644 --- a/src/ostree/ot-builtin-commit.c +++ b/src/ostree/ot-builtin-commit.c @@ -46,7 +46,7 @@ static GOptionEntry options[] = { { "metadata-variant", 0, 0, G_OPTION_ARG_FILENAME, &metadata_bin_path, "File containing serialized variant, in host endianness", "path" }, { "branch", 'b', 0, G_OPTION_ARG_STRING, &branch, "Branch", "branch" }, { "parent", 'p', 0, G_OPTION_ARG_STRING, &parent, "Parent commit", "commit" }, - { "tar", 0, 0, G_OPTION_ARG_NONE, &tar, "Given argument is a tar file", NULL }, + { "tar", 0, 0, G_OPTION_ARG_NONE, &tar, "Given arguments are tar files", NULL }, { "owner-uid", 0, 0, G_OPTION_ARG_INT, &owner_uid, "Set file ownership user id", "UID" }, { "owner-gid", 0, 0, G_OPTION_ARG_INT, &owner_gid, "Set file ownership group id", "GID" }, { NULL } @@ -60,11 +60,13 @@ ostree_builtin_commit (int argc, char **argv, const char *repo_path, GError **er OstreeRepo *repo = NULL; char *argpath = NULL; GFile *arg = NULL; + GPtrArray *tar_files = NULL; GChecksum *commit_checksum = NULL; GVariant *metadata = NULL; GMappedFile *metadata_mappedf = NULL; GFile *metadata_f = NULL; OstreeRepoCommitModifier *modifier = NULL; + int i; context = g_option_context_new ("[ARG] - Commit a new revision"); g_option_context_add_main_entries (context, options, NULL); @@ -146,8 +148,13 @@ ostree_builtin_commit (int argc, char **argv, const char *repo_path, GError **er } else { - if (!ostree_repo_commit_tarfile (repo, branch, parent, subject, body, metadata, - arg, modifier, &commit_checksum, NULL, error)) + tar_files = g_ptr_array_new_with_free_func ((GDestroyNotify)g_object_unref); + g_ptr_array_add (tar_files, g_object_ref (arg)); + for (i = 2; i < argc; i++) + g_ptr_array_add (tar_files, ot_gfile_new_for_path (argv[i])); + + if (!ostree_repo_commit_tarfiles (repo, branch, parent, subject, body, metadata, + tar_files, modifier, &commit_checksum, NULL, error)) goto out; } @@ -156,6 +163,8 @@ ostree_builtin_commit (int argc, char **argv, const char *repo_path, GError **er out: g_free (argpath); g_clear_object (&arg); + if (tar_files) + g_ptr_array_unref (tar_files); if (metadata_mappedf) g_mapped_file_unref (metadata_mappedf); if (context) diff --git a/tests/t0006-libarchive.sh b/tests/t0006-libarchive.sh index 76bcd88c..7094c221 100755 --- a/tests/t0006-libarchive.sh +++ b/tests/t0006-libarchive.sh @@ -19,7 +19,7 @@ set -e -echo "1..4" +echo "1..6" . libtest.sh @@ -72,3 +72,31 @@ cd test-hardlinks-checkout assert_file_has_content foo foo1 assert_file_has_content bar foo1 echo "ok hardlink contents" + +cd ${test_tmpdir} +mkdir multicommit-files +cd multicommit-files +mkdir -p files1/subdir files2/subdir +echo "to be overwritten file" > files1/testfile +echo "not overwritten" > files1/otherfile +echo "overwriting file" > files2/testfile +ln -s "to-be-overwritten-symlink" files1/testsymlink +ln -s "overwriting-symlink" files2/testsymlink +ln -s "not overwriting symlink" files2/ohersymlink +echo "original" > files1/subdir/original +echo "new" > files2/subdir/new + +tar -c -C files1 -z -f files1.tar.gz . +tar -c -C files2 -z -f files2.tar.gz . + +$OSTREE commit -s 'multi tar' -b multicommit --tar files1.tar.gz files2.tar.gz +echo "ok tar multicommit" + +cd ${test_tmpdir} +$OSTREE checkout multicommit multicommit-checkout +cd multicommit-checkout +assert_file_has_content testfile "overwriting file" +assert_file_has_content otherfile "not overwritten" +assert_file_has_content subdir/original "original" +assert_file_has_content subdir/new "new" +echo "ok tar multicommit contents"