diff --git a/src/libostree/ostree-repo-libarchive.c b/src/libostree/ostree-repo-libarchive.c index 64410cb2..82c8dd51 100644 --- a/src/libostree/ostree-repo-libarchive.c +++ b/src/libostree/ostree-repo-libarchive.c @@ -292,6 +292,23 @@ write_libarchive_entry_to_mtree (OstreeRepo *self, } #endif +static gboolean +create_empty_dir_with_uidgid (OstreeRepo *self, + guint32 uid, + guint32 gid, + guint8 **out_csum, + GCancellable *cancellable, + GError **error) +{ + g_autoptr(GFileInfo) tmp_dir_info = g_file_info_new (); + + g_file_info_set_attribute_uint32 (tmp_dir_info, "unix::uid", uid); + g_file_info_set_attribute_uint32 (tmp_dir_info, "unix::gid", gid); + g_file_info_set_attribute_uint32 (tmp_dir_info, "unix::mode", 0755 | S_IFDIR); + + return _ostree_repo_write_directory_meta (self, tmp_dir_info, NULL, out_csum, cancellable, error); +} + /** * ostree_repo_import_archive_to_mtree: * @self: An #OstreeRepo @@ -320,6 +337,7 @@ ostree_repo_import_archive_to_mtree (OstreeRepo *self, g_autofree guchar *tmp_csum = NULL; int r; + while (TRUE) { r = archive_read_next_header (a, &entry); @@ -336,13 +354,13 @@ ostree_repo_import_archive_to_mtree (OstreeRepo *self, */ if (opts->autocreate_parents && !tmp_csum) { - g_autoptr(GFileInfo) 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 (!_ostree_repo_write_directory_meta (self, tmp_dir_info, NULL, &tmp_csum, cancellable, error)) + /* Here, we auto-pick the first uid/gid we find in the + * archive. Realistically this is probably always going to + * be root, but eh, at least we try to match. + */ + if (!create_empty_dir_with_uidgid (self, archive_entry_uid (entry), + archive_entry_gid (entry), + &tmp_csum, cancellable, error)) goto out; } @@ -352,6 +370,26 @@ ostree_repo_import_archive_to_mtree (OstreeRepo *self, goto out; } + /* If we didn't import anything at all, and autocreation of parents + * is enabled, automatically create a root directory. This is + * useful primarily when importing Docker image layers, which can + * just be metadata. + */ + if (!ostree_mutable_tree_get_metadata_checksum (mtree) && opts->autocreate_parents) + { + char tmp_checksum[65]; + + if (!tmp_csum) + { + /* We didn't have any archive entries to match, so pick uid 0, gid 0. */ + if (!create_empty_dir_with_uidgid (self, 0, 0, &tmp_csum, cancellable, error)) + goto out; + } + + ostree_checksum_inplace_from_bytes (tmp_csum, tmp_checksum); + ostree_mutable_tree_set_metadata_checksum (mtree, tmp_checksum); + } + ret = TRUE; out: return ret; diff --git a/tests/test-libarchive-import.c b/tests/test-libarchive-import.c index 1dd0c68c..928b1491 100644 --- a/tests/test-libarchive-import.c +++ b/tests/test-libarchive-import.c @@ -32,6 +32,7 @@ typedef struct { OstreeRepo *repo; int fd; + int fd_empty; char *tmpd; } TestData; @@ -88,6 +89,19 @@ test_data_init (TestData *td) g_assert_cmpint (ARCHIVE_OK, ==, archive_write_close (a)); g_assert_cmpint (ARCHIVE_OK, ==, archive_write_free (a)); + td->fd_empty = openat (AT_FDCWD, "empty.tar.gz", O_CREAT | O_EXCL | O_RDWR | O_CLOEXEC, 0644); + g_assert (td->fd_empty >= 0); + (void) unlink ("empty.tar.gz"); + + a = archive_write_new (); + g_assert (a); + + g_assert_cmpint (0, ==, archive_write_set_format_pax (a)); + g_assert_cmpint (0, ==, archive_write_add_filter_gzip (a)); + g_assert_cmpint (0, ==, archive_write_open_fd (a, td->fd_empty)); + g_assert_cmpint (ARCHIVE_OK, ==, archive_write_close (a)); + g_assert_cmpint (ARCHIVE_OK, ==, archive_write_free (a)); + { g_autoptr(GFile) repopath = g_file_new_for_path ("repo"); td->repo = ostree_repo_new (repopath); @@ -109,6 +123,46 @@ spawn_cmdline (const char *cmd, GError **error) return TRUE; } +static void +test_libarchive_noautocreate_empty (gconstpointer data) +{ + TestData *td = (void*)data; + GError *error = NULL; + struct archive *a = archive_read_new (); + OstreeRepoImportArchiveOptions opts = { 0, }; + glnx_unref_object OstreeMutableTree *mtree = ostree_mutable_tree_new (); + + g_assert_cmpint (0, ==, lseek (td->fd_empty, 0, SEEK_SET)); + g_assert_cmpint (0, ==, archive_read_support_format_all (a)); + g_assert_cmpint (0, ==, archive_read_support_filter_all (a)); + g_assert_cmpint (0, ==, archive_read_open_fd (a, td->fd_empty, 8192)); + + (void)ostree_repo_import_archive_to_mtree (td->repo, &opts, a, mtree, NULL, NULL, &error); + g_assert_no_error (error); + g_assert (ostree_mutable_tree_get_metadata_checksum (mtree) == NULL); +} + +static void +test_libarchive_autocreate_empty (gconstpointer data) +{ + TestData *td = (void*)data; + GError *error = NULL; + struct archive *a = archive_read_new (); + OstreeRepoImportArchiveOptions opts = { 0, }; + glnx_unref_object OstreeMutableTree *mtree = ostree_mutable_tree_new (); + + opts.autocreate_parents = 1; + + g_assert_cmpint (0, ==, lseek (td->fd_empty, 0, SEEK_SET)); + g_assert_cmpint (0, ==, archive_read_support_format_all (a)); + g_assert_cmpint (0, ==, archive_read_support_filter_all (a)); + g_assert_cmpint (0, ==, archive_read_open_fd (a, td->fd_empty, 8192)); + + (void)ostree_repo_import_archive_to_mtree (td->repo, &opts, a, mtree, NULL, NULL, &error); + g_assert_no_error (error); + g_assert (ostree_mutable_tree_get_metadata_checksum (mtree) != NULL); +} + static void test_libarchive_error_device_file (gconstpointer data) { @@ -189,6 +243,8 @@ int main (int argc, char **argv) g_test_init (&argc, &argv, NULL); + g_test_add_data_func ("/libarchive/noautocreate-empty", &td, test_libarchive_noautocreate_empty); + g_test_add_data_func ("/libarchive/autocreate-empty", &td, test_libarchive_autocreate_empty); g_test_add_data_func ("/libarchive/error-device-file", &td, test_libarchive_error_device_file); g_test_add_data_func ("/libarchive/ignore-device-file", &td, test_libarchive_ignore_device_file);