From 3e59cc1305dce5e3d603c78d12f89df571ca1084 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Fri, 23 Dec 2011 06:49:04 -0500 Subject: [PATCH] core: Add --tar-autocreate-parents option for commit The tar files we're making of artifacts don't include parent directories. Now we could change the builder to make them, but we can also just autocreate them on import. Mode 0755 with no xattrs seems OK here. --- src/libostree/ostree-mutable-tree.c | 49 +++++++++++++++++++++++ src/libostree/ostree-mutable-tree.h | 7 ++++ src/libostree/ostree-repo.c | 61 +++++++++++++++++++++++------ src/libostree/ostree-repo.h | 1 + src/ostree/ot-builtin-commit.c | 3 ++ tests/t0006-libarchive.sh | 10 +++++ 6 files changed, 119 insertions(+), 12 deletions(-) diff --git a/src/libostree/ostree-mutable-tree.c b/src/libostree/ostree-mutable-tree.c index ca8be1ee..5f63f113 100644 --- a/src/libostree/ostree-mutable-tree.c +++ b/src/libostree/ostree-mutable-tree.c @@ -221,6 +221,55 @@ ostree_mutable_tree_lookup (OstreeMutableTree *self, return ret; } +gboolean +ostree_mutable_tree_ensure_parent_dirs (OstreeMutableTree *self, + GPtrArray *split_path, + const char *metadata_checksum, + OstreeMutableTree **out_parent, + GError **error) +{ + gboolean ret = FALSE; + int i; + OstreeMutableTree *ret_parent = NULL; + OstreeMutableTree *subdir = self; + + g_assert (metadata_checksum != NULL); + + if (!self->metadata_checksum) + ostree_mutable_tree_set_metadata_checksum (self, metadata_checksum); + + for (i = 0; i+1 < split_path->len; i++) + { + OstreeMutableTree *next; + const char *name = split_path->pdata[i]; + + if (g_hash_table_lookup (subdir->files, name)) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "Can't replace file with directory: %s", name); + goto out; + } + + next = g_hash_table_lookup (subdir->subdirs, name); + if (!next) + { + next = ostree_mutable_tree_new (); + ostree_mutable_tree_set_metadata_checksum (next, metadata_checksum); + g_hash_table_insert (subdir->subdirs, g_strdup (name), next); + } + + subdir = next; + } + + ret_parent = g_object_ref (subdir); + + ret = TRUE; + ot_transfer_out_value (out_parent, &ret_parent); + out: + g_clear_object (&ret_parent); + return ret; +} + gboolean ostree_mutable_tree_walk (OstreeMutableTree *self, GPtrArray *split_path, diff --git a/src/libostree/ostree-mutable-tree.h b/src/libostree/ostree-mutable-tree.h index 413da028..e47964d1 100644 --- a/src/libostree/ostree-mutable-tree.h +++ b/src/libostree/ostree-mutable-tree.h @@ -77,6 +77,13 @@ gboolean ostree_mutable_tree_lookup (OstreeMutableTree *self, OstreeMutableTree **out_subdir, GError **error); +gboolean +ostree_mutable_tree_ensure_parent_dirs (OstreeMutableTree *self, + GPtrArray *split_path, + const char *metadata_checksum, + OstreeMutableTree **out_parent, + GError **error); + gboolean ostree_mutable_tree_walk (OstreeMutableTree *self, GPtrArray *split_path, guint start, diff --git a/src/libostree/ostree-repo.c b/src/libostree/ostree-repo.c index 80b81812..71bf0555 100644 --- a/src/libostree/ostree-repo.c +++ b/src/libostree/ostree-repo.c @@ -1585,6 +1585,7 @@ ostree_repo_stage_mtree (OstreeRepo *self, while (g_hash_table_iter_next (&hash_iter, &key, &value)) { const char *name = key; + const char *metadata_checksum; OstreeMutableTree *child_dir = value; char *child_dir_contents_checksum; @@ -1592,10 +1593,13 @@ ostree_repo_stage_mtree (OstreeRepo *self, cancellable, error)) goto out; + g_assert (child_dir_contents_checksum); g_hash_table_replace (dir_contents_checksums, g_strdup (name), child_dir_contents_checksum); /* Transfer ownership */ + metadata_checksum = ostree_mutable_tree_get_metadata_checksum (child_dir); + g_assert (metadata_checksum); g_hash_table_replace (dir_metadata_checksums, g_strdup (name), - g_strdup (ostree_mutable_tree_get_metadata_checksum (child_dir))); + g_strdup (metadata_checksum)); } serialized_tree = create_tree_variant_from_hashes (ostree_mutable_tree_get_files (mtree), @@ -1709,6 +1713,7 @@ stage_libarchive_entry_to_mtree (OstreeRepo *self, struct archive *a, struct archive_entry *entry, OstreeRepoCommitModifier *modifier, + const char *tmp_dir_checksum, GCancellable *cancellable, GError **error) { @@ -1738,8 +1743,19 @@ stage_libarchive_entry_to_mtree (OstreeRepo *self, } else { - if (!ostree_mutable_tree_walk (root, split_path, 0, &parent, error)) - goto out; + if (tmp_dir_checksum) + { + if (!ostree_mutable_tree_ensure_parent_dirs (root, split_path, + tmp_dir_checksum, + &parent, + error)) + goto out; + } + else + { + if (!ostree_mutable_tree_walk (root, split_path, 0, &parent, error)) + goto out; + } basename = (char*)split_path->pdata[split_path->len-1]; } @@ -1854,18 +1870,21 @@ stage_libarchive_entry_to_mtree (OstreeRepo *self, #endif gboolean -ostree_repo_stage_archive_to_mtree (OstreeRepo *self, - GFile *archive_f, - OstreeMutableTree *root, - OstreeRepoCommitModifier *modifier, - GCancellable *cancellable, - GError **error) +ostree_repo_stage_archive_to_mtree (OstreeRepo *self, + GFile *archive_f, + OstreeMutableTree *root, + OstreeRepoCommitModifier *modifier, + gboolean autocreate_parents, + GCancellable *cancellable, + GError **error) { #ifdef HAVE_LIBARCHIVE gboolean ret = FALSE; - struct archive *a; + struct archive *a = NULL; struct archive_entry *entry; int r; + GFileInfo *tmp_dir_info = NULL; + GChecksum *tmp_dir_checksum = NULL; a = archive_read_new (); archive_read_support_compression_all (a); @@ -1887,7 +1906,22 @@ ostree_repo_stage_archive_to_mtree (OstreeRepo *self, goto out; } - if (!stage_libarchive_entry_to_mtree (self, root, a, entry, modifier, cancellable, error)) + if (autocreate_parents && !tmp_dir_checksum) + { + tmp_dir_info = g_file_info_new (); + + g_file_info_set_attribute_uint32 (tmp_dir_info, "unix::uid", archive_entry_uid (entry)); + g_file_info_set_attribute_uint32 (tmp_dir_info, "unix::gid", archive_entry_gid (entry)); + g_file_info_set_attribute_uint32 (tmp_dir_info, "unix::mode", 0755 | S_IFDIR); + + if (!stage_directory_meta (self, tmp_dir_info, NULL, &tmp_dir_checksum, cancellable, error)) + goto out; + } + + if (!stage_libarchive_entry_to_mtree (self, root, a, + entry, modifier, + autocreate_parents ? g_checksum_get_string (tmp_dir_checksum) : NULL, + cancellable, error)) goto out; } if (archive_read_close (a) != ARCHIVE_OK) @@ -1898,7 +1932,10 @@ ostree_repo_stage_archive_to_mtree (OstreeRepo *self, ret = TRUE; out: - (void)archive_read_close (a); + g_clear_object (&tmp_dir_info); + ot_clear_checksum (&tmp_dir_checksum); + if (a) + (void)archive_read_close (a); 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 08ebd58c..3777d840 100644 --- a/src/libostree/ostree-repo.h +++ b/src/libostree/ostree-repo.h @@ -158,6 +158,7 @@ gboolean ostree_repo_stage_archive_to_mtree (OstreeRepo *self, GFile *archive, OstreeMutableTree *tree, OstreeRepoCommitModifier *modifier, + gboolean autocreate_parents, GCancellable *cancellable, GError **error); diff --git a/src/ostree/ot-builtin-commit.c b/src/ostree/ot-builtin-commit.c index 5fcc0371..8e525ef2 100644 --- a/src/ostree/ot-builtin-commit.c +++ b/src/ostree/ot-builtin-commit.c @@ -36,6 +36,7 @@ static char *body; static char *parent; static char *branch; static gboolean skip_if_unchanged; +static gboolean tar_autocreate_parents; static char **trees; static gint owner_uid = -1; static gint owner_gid = -1; @@ -50,6 +51,7 @@ static GOptionEntry options[] = { { "tree", 0, 0, G_OPTION_ARG_STRING_ARRAY, &trees, "Overlay the given argument as a tree", "NAME" }, { "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" }, + { "tar-autocreate-parents", 0, 0, G_OPTION_ARG_NONE, &tar_autocreate_parents, "When loading tar archives, automatically create parent directories as needed", NULL }, { "skip-if-unchanged", 0, 0, G_OPTION_ARG_NONE, &skip_if_unchanged, "If the contents are unchanged from previous commit, do nothing", NULL }, { NULL } }; @@ -187,6 +189,7 @@ ostree_builtin_commit (int argc, char **argv, GFile *repo_path, GError **error) { arg = ot_gfile_new_for_path (tree); if (!ostree_repo_stage_archive_to_mtree (repo, arg, mtree, modifier, + tar_autocreate_parents, cancellable, error)) goto out; } diff --git a/tests/t0006-libarchive.sh b/tests/t0006-libarchive.sh index 4173c97a..06a0bf9c 100755 --- a/tests/t0006-libarchive.sh +++ b/tests/t0006-libarchive.sh @@ -100,3 +100,13 @@ 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" + +cd ${test_tmpdir}/multicommit-files +tar -c -C files1 -z -f partial.tar.gz subdir/original +$OSTREE commit -s 'partial' -b partial --tar-autocreate-parents --tree=tar=partial.tar.gz +echo "ok tar partial commit" + +cd ${test_tmpdir} +$OSTREE checkout partial partial-checkout +cd partial-checkout +assert_file_has_content subdir/original "original"