From a89764231543c2a64da1e492963436ae97d327a6 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Tue, 1 Nov 2011 10:59:07 -0400 Subject: [PATCH] For archive checkouts, actually unpack the packfiles --- src/libostree/ostree-core.c | 257 ++++++++++++++++++++++++++++++++++++ src/libostree/ostree-core.h | 6 + src/libostree/ostree-repo.c | 226 +++++-------------------------- 3 files changed, 294 insertions(+), 195 deletions(-) diff --git a/src/libostree/ostree-core.c b/src/libostree/ostree-core.c index 4befb016..e36845d9 100644 --- a/src/libostree/ostree-core.c +++ b/src/libostree/ostree-core.c @@ -554,3 +554,260 @@ ostree_pack_object (GOutputStream *output, return ret; } +static gboolean +splice_and_checksum (GOutputStream *out, + GInputStream *in, + GChecksum *checksum, + GCancellable *cancellable, + GError **error) +{ + gboolean ret = FALSE; + + if (checksum != NULL) + { + gsize bytes_read, bytes_written; + char buf[4096]; + do + { + if (!g_input_stream_read_all (in, buf, sizeof(buf), &bytes_read, cancellable, error)) + goto out; + if (checksum) + g_checksum_update (checksum, (guint8*)buf, bytes_read); + if (!g_output_stream_write_all (out, buf, bytes_read, &bytes_written, cancellable, error)) + goto out; + } + while (bytes_read > 0); + } + else + { + if (g_output_stream_splice (out, in, 0, cancellable, error) < 0) + goto out; + } + + ret = TRUE; + out: + return ret; +} + +static gboolean +unpack_meta (const char *path, + const char *dest_path, + GChecksum **out_checksum, + GError **error) +{ + gboolean ret = FALSE; + GFile *file = NULL; + GFile *dest_file = NULL; + GFileInputStream *in = NULL; + GChecksum *ret_checksum = NULL; + GFileOutputStream *out = NULL; + + file = ot_util_new_file_for_path (path); + dest_file = ot_util_new_file_for_path (dest_path); + + if (out_checksum) + ret_checksum = g_checksum_new (G_CHECKSUM_SHA256); + + in = g_file_read (file, NULL, error); + if (!in) + goto out; + + out = g_file_replace (dest_file, NULL, FALSE, 0, NULL, error); + if (!out) + goto out; + + if (!splice_and_checksum ((GOutputStream*)out, (GInputStream*)in, ret_checksum, NULL, error)) + goto out; + + if (!g_output_stream_close ((GOutputStream*)out, NULL, error)) + goto out; + + ret = TRUE; + if (out_checksum) + *out_checksum = ret_checksum; + ret_checksum = NULL; + out: + if (!ret) + (void) unlink (dest_path); + if (ret_checksum) + g_checksum_free (ret_checksum); + g_clear_object (&file); + g_clear_object (&dest_file); + g_clear_object (&in); + return ret; +} + + +static gboolean +unpack_file (const char *path, + const char *dest_path, + GChecksum **out_checksum, + GError **error) +{ + gboolean ret = FALSE; + GFile *file = NULL; + GFile *dest_file = NULL; + char *metadata_buf = NULL; + GVariant *metadata = NULL; + GVariant *xattrs = NULL; + GFileInputStream *in = NULL; + GFileOutputStream *out = NULL; + GChecksum *ret_checksum = NULL; + guint32 metadata_len; + guint32 version, uid, gid, mode; + guint64 content_len; + gsize bytes_read, bytes_written; + int temp_fd = -1; + + file = ot_util_new_file_for_path (path); + + in = g_file_read (file, NULL, error); + if (!in) + goto out; + + if (!g_input_stream_read_all ((GInputStream*)in, &metadata_len, 4, &bytes_read, NULL, error)) + goto out; + if (bytes_read != 4) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "Corrupted packfile; too short while reading metadata length"); + goto out; + } + + metadata_len = GUINT32_FROM_BE (metadata_len); + metadata_buf = g_malloc (metadata_len); + + if (!g_input_stream_read_all ((GInputStream*)in, metadata_buf, metadata_len, &bytes_read, NULL, error)) + goto out; + if (bytes_read != metadata_len) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "Corrupted packfile; too short while reading metadata"); + goto out; + } + + metadata = g_variant_new_from_data (G_VARIANT_TYPE (OSTREE_PACK_FILE_VARIANT_FORMAT), + metadata_buf, metadata_len, FALSE, NULL, NULL); + + g_variant_get (metadata, "(uuuu@a(ayay)t)", + &version, &uid, &gid, &mode, + &xattrs, &content_len); + uid = GUINT32_FROM_BE (uid); + gid = GUINT32_FROM_BE (gid); + mode = GUINT32_FROM_BE (mode); + content_len = GUINT64_FROM_BE (content_len); + + dest_file = ot_util_new_file_for_path (dest_path); + + if (out_checksum) + ret_checksum = g_checksum_new (G_CHECKSUM_SHA256); + + if (S_ISREG (mode)) + { + out = g_file_replace (dest_file, NULL, FALSE, 0, NULL, error); + if (!out) + goto out; + + if (!splice_and_checksum ((GOutputStream*)out, (GInputStream*)in, ret_checksum, NULL, error)) + goto out; + + if (!g_output_stream_close ((GOutputStream*)out, NULL, error)) + goto out; + } + else if (S_ISLNK (mode)) + { + char target[PATH_MAX+1]; + + if (!g_input_stream_read_all ((GInputStream*)in, target, sizeof(target)-1, &bytes_read, NULL, error)) + goto out; + target[bytes_read] = '\0'; + if (ret_checksum) + g_checksum_update (ret_checksum, (guint8*)target, bytes_read); + if (symlink (target, dest_path) < 0) + { + ot_util_set_error_from_errno (error, errno); + goto out; + } + } + else if (S_ISCHR (mode) || S_ISBLK (mode)) + { + guint32 dev; + + if (!g_input_stream_read_all ((GInputStream*)in, &dev, 4, &bytes_read, NULL, error)) + goto out; + if (bytes_read != 4) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "Corrupted packfile; too short while reading device id"); + goto out; + } + dev = GUINT32_FROM_BE (dev); + if (ret_checksum) + g_checksum_update (ret_checksum, (guint8*)&dev, 4); + if (mknod (dest_path, mode, dev) < 0) + { + ot_util_set_error_from_errno (error, errno); + goto out; + } + } + else + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "Corrupted packfile; invalid mode %u", mode); + goto out; + } + + if (!S_ISLNK (mode)) + { + if (chmod (dest_path, mode) < 0) + { + ot_util_set_error_from_errno (error, errno); + goto out; + } + } + + if (!ostree_set_xattrs (dest_path, xattrs, error)) + goto out; + + if (ret_checksum) + { + ostree_checksum_update_stat (ret_checksum, uid, gid, mode); + g_checksum_update (ret_checksum, (guint8*)g_variant_get_data (xattrs), g_variant_get_size (xattrs)); + } + + ret = TRUE; + if (out_checksum) + *out_checksum = ret_checksum; + ret_checksum = NULL; + out: + if (!ret) + (void) unlink (dest_path); + if (ret_checksum) + g_checksum_free (ret_checksum); + g_free (metadata_buf); + g_clear_object (&file); + g_clear_object (&dest_file); + g_clear_object (&in); + g_clear_object (&out); + if (metadata) + g_variant_unref (metadata); + if (xattrs) + g_variant_unref (xattrs); + return ret; +} + +gboolean +ostree_unpack_object (const char *path, + OstreeObjectType objtype, + const char *dest_path, + GChecksum **out_checksum, + GError **error) +{ + if (objtype == OSTREE_OBJECT_TYPE_META) + return unpack_meta (path, dest_path, out_checksum, error); + else + return unpack_file (path, dest_path, out_checksum, error); +} + + + diff --git a/src/libostree/ostree-core.h b/src/libostree/ostree-core.h index 3c169189..c1fb4917 100644 --- a/src/libostree/ostree-core.h +++ b/src/libostree/ostree-core.h @@ -131,6 +131,12 @@ gboolean ostree_pack_object (GOutputStream *output, GCancellable *cancellable, GError **error); +gboolean ostree_unpack_object (const char *path, + OstreeObjectType objtype, + const char *dest_path, + GChecksum **out_checksum, + GError **error); + void ostree_checksum_update_stat (GChecksum *checksum, guint32 uid, guint32 gid, guint32 mode); diff --git a/src/libostree/ostree-repo.c b/src/libostree/ostree-repo.c index 721f62b2..d0f83de2 100644 --- a/src/libostree/ostree-repo.c +++ b/src/libostree/ostree-repo.c @@ -910,178 +910,6 @@ ostree_repo_link_file (OstreeRepo *self, return TRUE; } -static gboolean -unpack_and_checksum_packfile (OstreeRepo *self, - const char *path, - gchar **out_filename, - GChecksum **out_checksum, - GError **error) -{ - OstreeRepoPrivate *priv = GET_PRIVATE (self); - gboolean ret = FALSE; - GFile *file = NULL; - char *temp_path = NULL; - GFile *temp_file = NULL; - GFileOutputStream *temp_out = NULL; - char *metadata_buf = NULL; - GVariant *metadata = NULL; - GVariant *xattrs = NULL; - GFileInputStream *in = NULL; - GChecksum *ret_checksum = NULL; - guint32 metadata_len; - guint32 version, uid, gid, mode; - guint64 content_len; - gsize bytes_read, bytes_written; - char buf[8192]; - int temp_fd = -1; - - file = ot_util_new_file_for_path (path); - - in = g_file_read (file, NULL, error); - if (!in) - goto out; - - if (!g_input_stream_read_all ((GInputStream*)in, &metadata_len, 4, &bytes_read, NULL, error)) - goto out; - if (bytes_read != 4) - { - g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, - "Corrupted packfile; too short while reading metadata length"); - goto out; - } - - metadata_len = GUINT32_FROM_BE (metadata_len); - metadata_buf = g_malloc (metadata_len); - - if (!g_input_stream_read_all ((GInputStream*)in, metadata_buf, metadata_len, &bytes_read, NULL, error)) - goto out; - if (bytes_read != metadata_len) - { - g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, - "Corrupted packfile; too short while reading metadata"); - goto out; - } - - metadata = g_variant_new_from_data (G_VARIANT_TYPE (OSTREE_PACK_FILE_VARIANT_FORMAT), - metadata_buf, metadata_len, FALSE, NULL, NULL); - - g_variant_get (metadata, "(uuuu@a(ayay)t)", - &version, &uid, &gid, &mode, - &xattrs, &content_len); - uid = GUINT32_FROM_BE (uid); - gid = GUINT32_FROM_BE (gid); - mode = GUINT32_FROM_BE (mode); - content_len = GUINT64_FROM_BE (content_len); - - temp_path = g_build_filename (priv->path, "tmp-packfile-XXXXXX"); - temp_file = ot_util_new_file_for_path (temp_path); - - ret_checksum = g_checksum_new (G_CHECKSUM_SHA256); - - if (S_ISREG (mode)) - { - temp_fd = g_mkstemp (temp_path); - if (temp_fd < 0) - { - ot_util_set_error_from_errno (error, errno); - goto out; - } - close (temp_fd); - temp_fd = -1; - temp_out = g_file_replace (temp_file, NULL, FALSE, 0, NULL, error); - if (!temp_out) - goto out; - - do - { - if (!g_input_stream_read_all ((GInputStream*)in, buf, sizeof(buf), &bytes_read, NULL, error)) - goto out; - g_checksum_update (ret_checksum, (guint8*)buf, bytes_read); - if (!g_output_stream_write_all ((GOutputStream*)temp_out, buf, bytes_read, &bytes_written, NULL, error)) - goto out; - } - while (bytes_read > 0); - - if (!g_output_stream_close ((GOutputStream*)temp_out, NULL, error)) - goto out; - } - else if (S_ISLNK (mode)) - { - g_assert (sizeof (buf) > PATH_MAX); - - if (!g_input_stream_read_all ((GInputStream*)in, buf, sizeof(buf), &bytes_read, NULL, error)) - goto out; - buf[bytes_read] = '\0'; - if (symlink (buf, temp_path) < 0) - { - ot_util_set_error_from_errno (error, errno); - goto out; - } - } - else if (S_ISCHR (mode) || S_ISBLK (mode)) - { - guint32 dev; - - if (!g_input_stream_read_all ((GInputStream*)in, &dev, 4, &bytes_read, NULL, error)) - goto out; - if (bytes_read != 4) - { - g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, - "Corrupted packfile; too short while reading device id"); - goto out; - } - dev = GUINT32_FROM_BE (dev); - if (mknod (temp_path, mode, dev) < 0) - { - ot_util_set_error_from_errno (error, errno); - goto out; - } - } - else - { - g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, - "Corrupted packfile; invalid mode %u", mode); - goto out; - } - - if (!S_ISLNK (mode)) - { - if (chmod (temp_path, mode) < 0) - { - ot_util_set_error_from_errno (error, errno); - goto out; - } - } - - if (!ostree_set_xattrs (temp_path, xattrs, error)) - goto out; - - ostree_checksum_update_stat (ret_checksum, uid, gid, mode); - g_checksum_update (ret_checksum, (guint8*)g_variant_get_data (xattrs), g_variant_get_size (xattrs)); - - ret = TRUE; - *out_checksum = ret_checksum; - ret_checksum = NULL; - *out_filename = temp_path; - temp_path = NULL; - out: - if (ret_checksum) - g_checksum_free (ret_checksum); - g_free (metadata_buf); - if (temp_path) - (void) unlink (temp_path); - g_free (temp_path); - g_clear_object (&file); - g_clear_object (&in); - g_clear_object (&temp_file); - g_clear_object (&temp_out); - if (metadata) - g_variant_unref (metadata); - if (xattrs) - g_variant_unref (xattrs); - return ret; -} - gboolean ostree_repo_store_packfile (OstreeRepo *self, const char *expected_checksum, @@ -1089,23 +917,18 @@ ostree_repo_store_packfile (OstreeRepo *self, OstreeObjectType objtype, GError **error) { + OstreeRepoPrivate *priv = GET_PRIVATE (self); gboolean ret = FALSE; - char *tempfile = NULL; + GString *tempfile_path = NULL; GChecksum *checksum = NULL; struct stat stbuf; gboolean did_exist; - if (objtype == OSTREE_OBJECT_TYPE_META) - { - if (!ostree_stat_and_checksum_file (-1, path, objtype, &checksum, &stbuf, error)) - goto out; - } - else - { - if (!unpack_and_checksum_packfile (self, path, &tempfile, &checksum, error)) - goto out; - - } + tempfile_path = g_string_new (priv->path); + g_string_append_printf (tempfile_path, "/tmp-unpack-%s", expected_checksum); + + if (!ostree_unpack_object (path, objtype, tempfile_path->str, &checksum, error)) + goto out; if (strcmp (g_checksum_get_string (checksum), expected_checksum) != 0) { @@ -1115,7 +938,7 @@ ostree_repo_store_packfile (OstreeRepo *self, goto out; } - if (!ostree_repo_store_object_trusted (self, tempfile ? tempfile : path, + if (!ostree_repo_store_object_trusted (self, tempfile_path ? tempfile_path->str : path, expected_checksum, objtype, TRUE, FALSE, &did_exist, error)) @@ -1123,9 +946,11 @@ ostree_repo_store_packfile (OstreeRepo *self, ret = TRUE; out: - if (tempfile) - (void) unlink (tempfile); - g_free (tempfile); + if (tempfile_path) + { + (void) unlink (tempfile_path->str); + g_string_free (tempfile_path, TRUE); + } if (checksum) g_checksum_free (checksum); return ret; @@ -2184,8 +2009,9 @@ checkout_one_directory (OstreeRepo *self, if (!checkout_tree (self, dir->tree_data, dest_path, error)) goto out; - /* TODO - xattrs */ - + if (!ostree_set_xattrs (dest_path, xattr_variant, error)) + goto out; + ret = TRUE; out: g_free (dest_path); @@ -2199,6 +2025,7 @@ checkout_tree (OstreeRepo *self, const char *destination, GError **error) { + OstreeRepoPrivate *priv = GET_PRIVATE (self); gboolean ret = FALSE; GHashTableIter hash_iter; gpointer key, value; @@ -2213,15 +2040,24 @@ checkout_tree (OstreeRepo *self, object_path = get_object_path (self, checksum, OSTREE_OBJECT_TYPE_FILE); dest_path = g_build_filename (destination, filename, NULL); - if (link (object_path, dest_path) < 0) + + if (priv->archive) { - ot_util_set_error_from_errno (error, errno); + if (!ostree_unpack_object (object_path, OSTREE_OBJECT_TYPE_FILE, dest_path, NULL, error)) + goto out; + } + else + { + if (link (object_path, dest_path) < 0) + { + ot_util_set_error_from_errno (error, errno); + g_free (object_path); + g_free (dest_path); + goto out; + } g_free (object_path); g_free (dest_path); - goto out; } - g_free (object_path); - g_free (dest_path); } g_hash_table_iter_init (&hash_iter, tree->directories);