diff --git a/src/libostree/ostree-core.c b/src/libostree/ostree-core.c index 887108f6..3c9e11d4 100644 --- a/src/libostree/ostree-core.c +++ b/src/libostree/ostree-core.c @@ -245,13 +245,16 @@ ostree_checksum_file_from_input (GFileInfo *file_info, goto out; } - ostree_checksum_update_stat (ret_checksum, - g_file_info_get_attribute_uint32 (file_info, "unix::uid"), - g_file_info_get_attribute_uint32 (file_info, "unix::gid"), - g_file_info_get_attribute_uint32 (file_info, "unix::mode")); - /* FIXME - ensure empty xattrs are the same as NULL xattrs */ - if (xattrs) - g_checksum_update (ret_checksum, (guint8*)g_variant_get_data (xattrs), g_variant_get_size (xattrs)); + if (objtype != OSTREE_OBJECT_TYPE_ARCHIVED_FILE_CONTENT) + { + ostree_checksum_update_stat (ret_checksum, + g_file_info_get_attribute_uint32 (file_info, "unix::uid"), + g_file_info_get_attribute_uint32 (file_info, "unix::gid"), + g_file_info_get_attribute_uint32 (file_info, "unix::mode")); + /* FIXME - ensure empty xattrs are the same as NULL xattrs */ + if (xattrs) + g_checksum_update (ret_checksum, (guint8*)g_variant_get_data (xattrs), g_variant_get_size (xattrs)); + } ret = TRUE; ot_transfer_out_value (out_checksum, &ret_checksum); @@ -291,7 +294,7 @@ ostree_checksum_file (GFile *f, goto out; } - if (!OSTREE_OBJECT_TYPE_IS_META(objtype)) + if (objtype == OSTREE_OBJECT_TYPE_RAW_FILE) { xattrs = ostree_get_xattrs_for_file (f, error); if (!xattrs) @@ -444,35 +447,34 @@ ostree_set_xattrs (GFile *f, } gboolean -ostree_parse_metadata_file (GFile *file, - OstreeObjectType *out_type, - GVariant **out_variant, - GError **error) +ostree_map_metadata_file (GFile *file, + OstreeObjectType expected_type, + GVariant **out_variant, + GError **error) { gboolean ret = FALSE; GVariant *ret_variant = NULL; GVariant *container = NULL; - guint32 ret_type; + guint32 actual_type; - if (!ot_util_variant_map (file, G_VARIANT_TYPE (OSTREE_SERIALIZED_VARIANT_FORMAT), + if (!ot_util_variant_map (file, OSTREE_SERIALIZED_VARIANT_FORMAT, &container, error)) goto out; g_variant_get (container, "(uv)", - &ret_type, &ret_variant); - ret_type = GUINT32_FROM_BE (ret_type); - if (!OSTREE_OBJECT_TYPE_IS_META (ret_type)) + &actual_type, &ret_variant); + ot_util_variant_take_ref (ret_variant); + actual_type = GUINT32_FROM_BE (actual_type); + if (actual_type != expected_type) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, - "Corrupted metadata object '%s'; invalid type %d", - ot_gfile_get_path_cached (file), ret_type); + "Corrupted metadata object '%s'; found type %u, expected %u", + ot_gfile_get_path_cached (file), + actual_type, (guint32)expected_type); goto out; } - ot_util_variant_take_ref (ret_variant); ret = TRUE; - if (out_type) - *out_type = ret_type; ot_transfer_out_value(out_variant, &ret_variant); out: ot_clear_gvariant (&ret_variant); @@ -541,201 +543,66 @@ ostree_get_relative_object_path (const char *checksum, return g_string_free (path, FALSE); } -gboolean -ostree_archive_file_for_input (GOutputStream *output, - GFileInfo *finfo, - GInputStream *instream, - GVariant *xattrs, - GChecksum **out_checksum, - GCancellable *cancellable, - GError **error) +GVariant * +ostree_create_archive_file_metadata (GFileInfo *finfo, + GVariant *xattrs) { - gboolean ret = FALSE; - guint32 uid, gid, mode; - guint32 device = 0; - guint32 metadata_size_be; - const char *target = NULL; - guint64 object_size; - gboolean pack_builder_initialized = FALSE; + guint32 uid, gid, mode, rdev; GVariantBuilder pack_builder; - GVariant *pack_variant = NULL; - gsize bytes_written; - GChecksum *ret_checksum = NULL; + GVariant *ret = NULL; uid = g_file_info_get_attribute_uint32 (finfo, G_FILE_ATTRIBUTE_UNIX_UID); gid = g_file_info_get_attribute_uint32 (finfo, G_FILE_ATTRIBUTE_UNIX_GID); mode = g_file_info_get_attribute_uint32 (finfo, G_FILE_ATTRIBUTE_UNIX_MODE); + rdev = g_file_info_get_attribute_uint32 (finfo, G_FILE_ATTRIBUTE_UNIX_RDEV); - g_variant_builder_init (&pack_builder, G_VARIANT_TYPE (OSTREE_ARCHIVED_FILE_VARIANT_FORMAT)); - pack_builder_initialized = TRUE; - g_variant_builder_add (&pack_builder, "u", GUINT32_TO_BE (0)); + g_variant_builder_init (&pack_builder, OSTREE_ARCHIVED_FILE_VARIANT_FORMAT); + g_variant_builder_add (&pack_builder, "u", GUINT32_TO_BE (0)); /* Version */ g_variant_builder_add (&pack_builder, "u", GUINT32_TO_BE (uid)); g_variant_builder_add (&pack_builder, "u", GUINT32_TO_BE (gid)); g_variant_builder_add (&pack_builder, "u", GUINT32_TO_BE (mode)); + g_variant_builder_add (&pack_builder, "u", GUINT32_TO_BE (rdev)); + if (S_ISLNK (mode)) + g_variant_builder_add (&pack_builder, "s", g_file_info_get_symlink_target (finfo)); + else + g_variant_builder_add (&pack_builder, "s", ""); g_variant_builder_add (&pack_builder, "@a(ayay)", xattrs ? xattrs : g_variant_new_array (G_VARIANT_TYPE ("(ayay)"), NULL, 0)); - if (S_ISREG (mode)) - { - object_size = (guint64)g_file_info_get_size (finfo); - } - else if (S_ISLNK (mode)) - { - target = g_file_info_get_attribute_byte_string (finfo, G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET); - object_size = strlen (target); - } - else if (S_ISBLK (mode) || S_ISCHR (mode)) - { - device = g_file_info_get_attribute_uint32 (finfo, G_FILE_ATTRIBUTE_UNIX_RDEV); - object_size = 4; - } - else if (S_ISFIFO (mode)) - { - object_size = 0; - } - else - { - g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, - "Invalid mode %u", mode); - goto out; - } - - g_variant_builder_add (&pack_builder, "t", GUINT64_TO_BE (object_size)); - pack_variant = g_variant_builder_end (&pack_builder); - pack_builder_initialized = FALSE; - - metadata_size_be = GUINT32_TO_BE (g_variant_get_size (pack_variant)); - - if (!g_output_stream_write_all (output, &metadata_size_be, 4, - &bytes_written, cancellable, error)) - goto out; - g_assert (bytes_written == 4); - - if (!g_output_stream_write_all (output, g_variant_get_data (pack_variant), g_variant_get_size (pack_variant), - &bytes_written, cancellable, error)) - goto out; - - if (S_ISREG (mode)) - { - if (!ot_gio_splice_and_checksum (output, (GInputStream*)instream, - out_checksum ? &ret_checksum : NULL, - cancellable, error)) - goto out; - } - else if (S_ISLNK (mode)) - { - if (out_checksum) - { - ret_checksum = g_checksum_new (G_CHECKSUM_SHA256); - g_checksum_update (ret_checksum, (guint8*)target, object_size); - } - if (!g_output_stream_write_all (output, target, object_size, - &bytes_written, cancellable, error)) - goto out; - } - else if (S_ISBLK (mode) || S_ISCHR (mode)) - { - guint32 device_be = GUINT32_TO_BE (device); - g_assert (object_size == 4); - if (out_checksum) - { - ret_checksum = g_checksum_new (G_CHECKSUM_SHA256); - g_checksum_update (ret_checksum, (guint8*)&device_be, 4); - } - if (!g_output_stream_write_all (output, &device_be, object_size, - &bytes_written, cancellable, error)) - goto out; - g_assert (bytes_written == 4); - } - else if (S_ISFIFO (mode)) - { - if (out_checksum) - ret_checksum = g_checksum_new (G_CHECKSUM_SHA256); - } - else - g_assert_not_reached (); - - if (ret_checksum) - { - ostree_checksum_update_stat (ret_checksum, uid, gid, mode); - if (xattrs) - g_checksum_update (ret_checksum, (guint8*)g_variant_get_data (xattrs), g_variant_get_size (xattrs)); - } - - ret = TRUE; - ot_transfer_out_value(out_checksum, &ret_checksum); - out: - if (pack_builder_initialized) - g_variant_builder_clear (&pack_builder); - ot_clear_gvariant (&pack_variant); - ot_clear_checksum (&ret_checksum); + ret = g_variant_builder_end (&pack_builder); + g_variant_ref_sink (ret); + return ret; } gboolean -ostree_parse_archived_file (GFile *file, - GFileInfo **out_file_info, - GVariant **out_xattrs, - GInputStream **out_content, - GCancellable *cancellable, - GError **error) +ostree_parse_archived_file_meta (GVariant *metadata, + GFileInfo **out_file_info, + GVariant **out_xattrs, + GError **error) { gboolean ret = FALSE; - char *metadata_buf = NULL; - GVariant *metadata = NULL; GFileInfo *ret_file_info = NULL; GVariant *ret_xattrs = NULL; - GInputStream *in = NULL; - guint32 metadata_len; - guint32 version, uid, gid, mode; - guint64 content_len; - gsize bytes_read; + guint32 version, uid, gid, mode, rdev; + const char *symlink_target; - in = (GInputStream*)g_file_read (file, NULL, error); - if (!in) - goto out; - - if (!g_input_stream_read_all (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 archive file; too short while reading metadata length"); - goto out; - } - - metadata_len = GUINT32_FROM_BE (metadata_len); - if (metadata_len > OSTREE_MAX_METADATA_SIZE) - { - g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, - "Corrupted archive file; metadata length %u is larger than maximum %u", - metadata_len, OSTREE_MAX_METADATA_SIZE); - goto out; - } - metadata_buf = g_malloc (metadata_len); + g_variant_get (metadata, "(uuuuu&s@a(ayay))", + &version, &uid, &gid, &mode, &rdev, + &symlink_target, &ret_xattrs); + version = GUINT32_FROM_BE (version); - if (!g_input_stream_read_all (in, metadata_buf, metadata_len, &bytes_read, NULL, error)) - goto out; - if (bytes_read != metadata_len) + if (version != 0) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, - "Corrupted archive file; too short while reading metadata"); + "Invalid version %d in archived file metadata", version); goto out; } - metadata = g_variant_new_from_data (G_VARIANT_TYPE (OSTREE_ARCHIVED_FILE_VARIANT_FORMAT), - metadata_buf, metadata_len, FALSE, - (GDestroyNotify)g_free, - metadata_buf); - metadata_buf = NULL; - - g_variant_get (metadata, "(uuuu@a(ayay)t)", - &version, &uid, &gid, &mode, - &ret_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); + rdev = GUINT32_FROM_BE (rdev); ret_file_info = g_file_info_new (); g_file_info_set_attribute_uint32 (ret_file_info, "standard::type", ot_gfile_type_for_mode (mode)); @@ -743,46 +610,22 @@ ostree_parse_archived_file (GFile *file, g_file_info_set_attribute_uint32 (ret_file_info, "unix::uid", uid); g_file_info_set_attribute_uint32 (ret_file_info, "unix::gid", gid); g_file_info_set_attribute_uint32 (ret_file_info, "unix::mode", mode); - g_file_info_set_attribute_uint64 (ret_file_info, "standard::size", content_len); if (S_ISREG (mode)) { - g_file_info_set_attribute_uint64 (ret_file_info, "standard::size", content_len); + ; } else if (S_ISLNK (mode)) { - char target[PATH_MAX+1]; - if (!g_input_stream_read_all (in, target, sizeof(target)-1, &bytes_read, cancellable, error)) - goto out; - target[bytes_read] = '\0'; - - g_file_info_set_attribute_boolean (ret_file_info, "standard::is-symlink", TRUE); - g_file_info_set_attribute_byte_string (ret_file_info, "standard::symlink-target", target); - - g_input_stream_close (in, cancellable, error); - g_clear_object (&in); + g_file_info_set_attribute_byte_string (ret_file_info, "standard::symlink-target", symlink_target); } else if (S_ISCHR (mode) || S_ISBLK (mode)) { - guint32 dev; - - if (!g_input_stream_read_all (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 archive file; too short while reading device id"); - goto out; - } - dev = GUINT32_FROM_BE (dev); - g_file_info_set_attribute_uint32 (ret_file_info, "unix::rdev", dev); - g_input_stream_close (in, cancellable, error); - g_clear_object (&in); + g_file_info_set_attribute_uint32 (ret_file_info, "unix::rdev", rdev); } else if (S_ISFIFO (mode)) { - g_input_stream_close (in, cancellable, error); - g_clear_object (&in); + ; } else { @@ -794,12 +637,9 @@ ostree_parse_archived_file (GFile *file, ret = TRUE; ot_transfer_out_value(out_file_info, &ret_file_info); ot_transfer_out_value(out_xattrs, &ret_xattrs); - ot_transfer_out_value(out_content, &in); out: g_clear_object (&ret_file_info); ot_clear_gvariant (&ret_xattrs); - g_clear_object (&in); - ot_clear_gvariant (&metadata); return ret; } @@ -818,7 +658,11 @@ ostree_create_file_from_input (GFile *dest_file, GFileOutputStream *out = NULL; guint32 uid, gid, mode; GChecksum *ret_checksum = NULL; - gboolean is_meta = OSTREE_OBJECT_TYPE_IS_META (objtype); + gboolean is_meta; + gboolean is_archived_content; + + is_meta = OSTREE_OBJECT_TYPE_IS_META (objtype); + is_archived_content = objtype == OSTREE_OBJECT_TYPE_ARCHIVED_FILE_CONTENT; if (g_cancellable_set_error_if_cancelled (cancellable, error)) return FALSE; @@ -829,14 +673,13 @@ ostree_create_file_from_input (GFile *dest_file, } else { - mode = S_IFREG | 0666; + mode = S_IFREG | 0664; } dest_path = ot_gfile_get_path_cached (dest_file); if (S_ISDIR (mode)) { - if (mkdir (ot_gfile_get_path_cached (dest_file), - g_file_info_get_attribute_uint32 (finfo, "unix::mode")) < 0) + if (mkdir (ot_gfile_get_path_cached (dest_file), mode) < 0) { ot_util_set_error_from_errno (error, errno); goto out; @@ -907,7 +750,7 @@ ostree_create_file_from_input (GFile *dest_file, goto out; } - if (finfo != NULL) + if (finfo != NULL && !is_meta && !is_archived_content) { uid = g_file_info_get_attribute_uint32 (finfo, "unix::uid"); gid = g_file_info_get_attribute_uint32 (finfo, "unix::gid"); @@ -918,11 +761,6 @@ ostree_create_file_from_input (GFile *dest_file, goto out; } } - else - { - uid = geteuid (); - gid = getegid (); - } if (!S_ISLNK (mode)) { @@ -940,9 +778,14 @@ ostree_create_file_from_input (GFile *dest_file, goto out; } - if (ret_checksum && !is_meta) + if (ret_checksum && !is_meta && !is_archived_content) { - ostree_checksum_update_stat (ret_checksum, uid, gid, mode); + g_assert (finfo != NULL); + + ostree_checksum_update_stat (ret_checksum, + g_file_info_get_attribute_uint32 (finfo, "unix::uid"), + g_file_info_get_attribute_uint32 (finfo, "unix::gid"), + mode); if (xattrs) g_checksum_update (ret_checksum, (guint8*)g_variant_get_data (xattrs), g_variant_get_size (xattrs)); } @@ -965,14 +808,14 @@ create_tmp_string (const char *dirpath, GString *tmp_name = NULL; if (!prefix) - prefix = "tmp-"; + prefix = "tmp"; if (!suffix) - suffix = ".tmp"; + suffix = "tmp"; tmp_name = g_string_new (dirpath); g_string_append_c (tmp_name, '/'); g_string_append (tmp_name, prefix); - g_string_append (tmp_name, "XXXXXX"); + g_string_append (tmp_name, "-XXXXXXXXXXXX."); g_string_append (tmp_name, suffix); return tmp_name; diff --git a/src/libostree/ostree-core.h b/src/libostree/ostree-core.h index 88513b2a..0772ca90 100644 --- a/src/libostree/ostree-core.h +++ b/src/libostree/ostree-core.h @@ -33,16 +33,17 @@ G_BEGIN_DECLS typedef enum { OSTREE_OBJECT_TYPE_RAW_FILE = 1, /* .raw */ - OSTREE_OBJECT_TYPE_ARCHIVED_FILE = 2, /* .archive */ - OSTREE_OBJECT_TYPE_DIR_TREE = 3, /* .dirtree */ - OSTREE_OBJECT_TYPE_DIR_META = 4, /* .dirmeta */ - OSTREE_OBJECT_TYPE_COMMIT = 5 /* .commit */ + OSTREE_OBJECT_TYPE_ARCHIVED_FILE_CONTENT = 2, /* .archive-content */ + OSTREE_OBJECT_TYPE_ARCHIVED_FILE_META = 3, /* .archive-meta */ + OSTREE_OBJECT_TYPE_DIR_TREE = 4, /* .dirtree */ + OSTREE_OBJECT_TYPE_DIR_META = 5, /* .dirmeta */ + OSTREE_OBJECT_TYPE_COMMIT = 6 /* .commit */ } OstreeObjectType; -#define OSTREE_OBJECT_TYPE_IS_META(t) (t >= 3 && t <= 5) +#define OSTREE_OBJECT_TYPE_IS_META(t) (t >= 3 && t <= 6) #define OSTREE_OBJECT_TYPE_LAST OSTREE_OBJECT_TYPE_COMMIT -#define OSTREE_SERIALIZED_VARIANT_FORMAT "(uv)" +#define OSTREE_SERIALIZED_VARIANT_FORMAT G_VARIANT_TYPE("(uv)") /* * xattr objects: @@ -116,10 +117,10 @@ GVariant *ostree_wrap_metadata_variant (OstreeObjectType type, GVariant *metadat gboolean ostree_set_xattrs (GFile *f, GVariant *xattrs, GCancellable *cancellable, GError **error); -gboolean ostree_parse_metadata_file (GFile *file, - OstreeObjectType *out_type, - GVariant **out_variant, - GError **error); +gboolean ostree_map_metadata_file (GFile *file, + OstreeObjectType expected_type, + GVariant **out_variant, + GError **error); gboolean ostree_checksum_file_from_input (GFileInfo *file_info, GVariant *xattrs, @@ -150,32 +151,6 @@ gboolean ostree_checksum_file_async_finish (GFile *f, GVariant *ostree_create_directory_metadata (GFileInfo *dir_info, GVariant *xattrs); -/* Archived files: - * - * guint32 metadata_length [metadata gvariant] [content] - * - * metadata variant: - * u - Version - * u - uid - * u - gid - * u - mode - * a(ayay) - xattrs - * t - content length - * - * And then following the end of the variant is the content. If - * symlink, then this is the target; if device, then device ID as - * network byte order uint32. - */ -#define OSTREE_ARCHIVED_FILE_VARIANT_FORMAT "(uuuua(ayay)t)" - -gboolean ostree_archive_file_for_input (GOutputStream *output, - GFileInfo *finfo, - GInputStream *input, - GVariant *xattrs, - GChecksum **out_checksum, - GCancellable *cancellable, - GError **error); - gboolean ostree_create_file_from_input (GFile *file, GFileInfo *finfo, GVariant *xattrs, @@ -205,18 +180,13 @@ gboolean ostree_create_temp_regular_file (GFile *dir, GCancellable *cancellable, GError **error); -gboolean ostree_parse_archived_file (GFile *file, - GFileInfo **out_file_info, - GVariant **out_xattrs, - GInputStream **out_content, - GCancellable *cancellable, - GError **error); +GVariant *ostree_create_archive_file_metadata (GFileInfo *file_info, + GVariant *xattrs); -gboolean ostree_unpack_object (GFile *file, - OstreeObjectType objtype, - GFile *dest, - GChecksum **out_checksum, - GError **error); +gboolean ostree_parse_archived_file_meta (GVariant *data, + GFileInfo **out_file_info, + GVariant **out_xattrs, + GError **error); #endif /* _OSTREE_REPO */ diff --git a/src/libostree/ostree-repo-file.c b/src/libostree/ostree-repo-file.c index 7d11c112..55f6a984 100644 --- a/src/libostree/ostree-repo-file.c +++ b/src/libostree/ostree-repo-file.c @@ -307,6 +307,7 @@ _ostree_repo_file_get_xattrs (OstreeRepoFile *self, { gboolean ret = FALSE; GVariant *ret_xattrs = NULL; + GVariant *metadata = NULL; GFile *local_file = NULL; if (!_ostree_repo_file_ensure_resolved (self, error)) @@ -317,7 +318,12 @@ _ostree_repo_file_get_xattrs (OstreeRepoFile *self, else if (ostree_repo_get_mode (self->repo) == OSTREE_REPO_MODE_ARCHIVE) { local_file = _ostree_repo_file_nontree_get_local (self); - if (!ostree_parse_archived_file (local_file, NULL, &ret_xattrs, NULL, cancellable, error)) + + if (!ostree_map_metadata_file (local_file, OSTREE_OBJECT_TYPE_ARCHIVED_FILE_META, + &metadata, error)) + goto out; + + if (!ostree_parse_archived_file_meta (metadata, NULL, &ret_xattrs, error)) goto out; } else @@ -330,6 +336,7 @@ _ostree_repo_file_get_xattrs (OstreeRepoFile *self, ot_transfer_out_value(out_xattrs, &ret_xattrs); out: ot_clear_gvariant (&ret_xattrs); + ot_clear_gvariant (&metadata); g_clear_object (&local_file); return ret; } @@ -1002,6 +1009,7 @@ _ostree_repo_file_tree_query_child (OstreeRepoFile *self, const char *name = NULL; gboolean ret = FALSE; GFileInfo *ret_info = NULL; + GVariant *archive_metadata = NULL; GVariant *files_variant = NULL; GVariant *dirs_variant = NULL; GVariant *tree_child_metadata = NULL; @@ -1030,7 +1038,10 @@ _ostree_repo_file_tree_query_child (OstreeRepoFile *self, if (ostree_repo_get_mode (self->repo) == OSTREE_REPO_MODE_ARCHIVE) { - if (!ostree_parse_archived_file (local_child, &ret_info, NULL, NULL, cancellable, error)) + if (!ostree_map_metadata_file (local_child, OSTREE_OBJECT_TYPE_ARCHIVED_FILE_META, + &archive_metadata, error)) + goto out; + if (!ostree_parse_archived_file_meta (archive_metadata, &ret_info, NULL, error)) goto out; } else @@ -1086,6 +1097,7 @@ _ostree_repo_file_tree_query_child (OstreeRepoFile *self, g_clear_object (&local_child); if (matcher) g_file_attribute_matcher_unref (matcher); + ot_clear_gvariant (&archive_metadata); ot_clear_gvariant (&tree_child_metadata); ot_clear_gvariant (&files_variant); ot_clear_gvariant (&dirs_variant); diff --git a/src/libostree/ostree-repo.c b/src/libostree/ostree-repo.c index 6a8ead89..c658f60f 100644 --- a/src/libostree/ostree-repo.c +++ b/src/libostree/ostree-repo.c @@ -60,9 +60,12 @@ struct _OstreeRepoPrivate { char *config_path; gboolean inited; + gboolean in_transaction; GKeyFile *config; OstreeRepoMode mode; + + GHashTable *pending_transaction_tmpfiles; }; static void @@ -78,6 +81,7 @@ ostree_repo_finalize (GObject *object) g_clear_object (&priv->remote_heads_dir); g_free (priv->objects_path); g_free (priv->config_path); + g_hash_table_destroy (priv->pending_transaction_tmpfiles); if (priv->config) g_key_file_free (priv->config); @@ -175,6 +179,11 @@ ostree_repo_class_init (OstreeRepoClass *klass) static void ostree_repo_init (OstreeRepo *self) { + OstreeRepoPrivate *priv = GET_PRIVATE (self); + + priv->pending_transaction_tmpfiles = g_hash_table_new_full (g_str_hash, g_str_equal, + g_free, + g_free); } OstreeRepo* @@ -647,7 +656,7 @@ get_objtype_for_repo_file (OstreeRepo *self) { OstreeRepoPrivate *priv = GET_PRIVATE (self); if (priv->mode == OSTREE_REPO_MODE_ARCHIVE) - return OSTREE_OBJECT_TYPE_ARCHIVED_FILE; + return OSTREE_OBJECT_TYPE_ARCHIVED_FILE_META; else return OSTREE_OBJECT_TYPE_RAW_FILE; } @@ -659,64 +668,260 @@ ostree_repo_get_file_object_path (OstreeRepo *self, return ostree_repo_get_object_path (self, checksum, get_objtype_for_repo_file (self)); } -static gboolean -ostree_repo_stage_object (OstreeRepo *self, - OstreeObjectType objtype, - GFileInfo *file_info, - GVariant *xattrs, - GInputStream *input, - GFile **out_tmpname, - GChecksum **out_checksum, - GCancellable *cancellable, - GError **error) +static char * +create_checksum_and_objtype (const char *checksum, + OstreeObjectType objtype) +{ + return g_strconcat (checksum, ".", ostree_object_type_to_string (objtype), NULL); +} + +gboolean +ostree_repo_has_object (OstreeRepo *self, + OstreeObjectType objtype, + const char *checksum, + gboolean *out_have_object, + GCancellable *cancellable, + GError **error) { gboolean ret = FALSE; OstreeRepoPrivate *priv = GET_PRIVATE (self); - GOutputStream *temp_out = NULL; - GChecksum *ret_checksum = NULL; - GFile *temp_file = NULL; - GFile *ret_tmpname = NULL; + char *tmp_key = NULL; + GFile *object_path = NULL; - if (g_cancellable_set_error_if_cancelled (cancellable, error)) - return FALSE; + tmp_key = create_checksum_and_objtype (checksum, objtype); - if (!OSTREE_OBJECT_TYPE_IS_META(objtype) && priv->mode == OSTREE_REPO_MODE_ARCHIVE) + if (g_hash_table_lookup (priv->pending_transaction_tmpfiles, tmp_key)) { - if (!ostree_create_temp_regular_file (priv->tmp_dir, - "archive-tmp-", NULL, - &temp_file, &temp_out, - cancellable, error)) - goto out; - - if (!ostree_archive_file_for_input (temp_out, file_info, input, xattrs, - &ret_checksum, cancellable, error)) - goto out; - - if (!g_output_stream_close (temp_out, cancellable, error)) - goto out; + *out_have_object = TRUE; } else { - if (!ostree_create_temp_file_from_input (priv->tmp_dir, - "store-tmp-", NULL, - file_info, xattrs, input, - objtype, - &temp_file, &ret_checksum, - cancellable, error)) - goto out; + object_path = ostree_repo_get_object_path (self, checksum, objtype); + + *out_have_object = g_file_query_exists (object_path, cancellable); + } + + ret = TRUE; + /* out: */ + g_free (tmp_key); + g_clear_object (&object_path); + return ret; +} + +static GFileInfo * +dup_file_info_owned_by_me (GFileInfo *file_info) +{ + GFileInfo *ret = g_file_info_dup (file_info); + + g_file_info_set_attribute_uint32 (ret, "unix::uid", geteuid ()); + g_file_info_set_attribute_uint32 (ret, "unix::gid", getegid ()); + + return ret; +} + +static gboolean +stage_object (OstreeRepo *self, + OstreeObjectType objtype, + GFileInfo *file_info, + GVariant *xattrs, + GInputStream *input, + const char *expected_checksum, + GChecksum **out_checksum, + GCancellable *cancellable, + GError **error); + +static gboolean +impl_stage_archive_file_object_from_raw (OstreeRepo *self, + GFileInfo *file_info, + GVariant *xattrs, + GInputStream *input, + const char *expected_checksum, + GChecksum **out_checksum, + GCancellable *cancellable, + GError **error) +{ + gboolean ret = FALSE; + OstreeRepoPrivate *priv = GET_PRIVATE (self); + GChecksum *ret_checksum = NULL; + GVariant *archive_metadata = NULL; + GFileInfo *temp_info = NULL; + GFile *meta_temp_file = NULL; + GFile *content_temp_file = NULL; + GVariant *serialized = NULL; + GInputStream *mem = NULL; + const char *actual_checksum; + + archive_metadata = ostree_create_archive_file_metadata (file_info, xattrs); + + serialized = ostree_wrap_metadata_variant (OSTREE_OBJECT_TYPE_ARCHIVED_FILE_META, archive_metadata); + mem = g_memory_input_stream_new_from_data (g_variant_get_data (serialized), + g_variant_get_size (serialized), + NULL); + + if (!ostree_create_temp_file_from_input (priv->tmp_dir, + "archive-tmp-", NULL, + NULL, NULL, mem, + OSTREE_OBJECT_TYPE_ARCHIVED_FILE_META, + &meta_temp_file, + NULL, + cancellable, error)) + goto out; + + temp_info = dup_file_info_owned_by_me (file_info); + if (!ostree_create_temp_file_from_input (priv->tmp_dir, + "archive-tmp-", NULL, + temp_info, NULL, input, + OSTREE_OBJECT_TYPE_ARCHIVED_FILE_CONTENT, + &content_temp_file, + out_checksum ? &ret_checksum : NULL, + cancellable, error)) + goto out; + + if (ret_checksum) + { + ostree_checksum_update_stat (ret_checksum, + g_file_info_get_attribute_uint32 (file_info, "unix::uid"), + g_file_info_get_attribute_uint32 (file_info, "unix::gid"), + g_file_info_get_attribute_uint32 (file_info, "unix::mode")); + /* FIXME - ensure empty xattrs are the same as NULL xattrs */ + if (xattrs) + g_checksum_update (ret_checksum, (guint8*)g_variant_get_data (xattrs), g_variant_get_size (xattrs)); } - ret_tmpname = g_object_ref (temp_file); - g_clear_object (&temp_file); + if (expected_checksum) + { + if (strcmp (g_checksum_get_string (ret_checksum), expected_checksum) != 0) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "Corrupted %s object %s (actual checksum is %s)", + ostree_object_type_to_string (OSTREE_OBJECT_TYPE_RAW_FILE), + expected_checksum, g_checksum_get_string (ret_checksum)); + goto out; + } + actual_checksum = expected_checksum; + } + else + actual_checksum = g_checksum_get_string (ret_checksum); + + g_hash_table_insert (priv->pending_transaction_tmpfiles, + create_checksum_and_objtype (actual_checksum, OSTREE_OBJECT_TYPE_ARCHIVED_FILE_CONTENT), + g_strdup (ot_gfile_get_basename_cached (content_temp_file))); + g_hash_table_insert (priv->pending_transaction_tmpfiles, + create_checksum_and_objtype (actual_checksum, OSTREE_OBJECT_TYPE_ARCHIVED_FILE_META), + g_strdup (ot_gfile_get_basename_cached (meta_temp_file))); + + ret = TRUE; + ot_transfer_out_value (out_checksum, &ret_checksum); + out: + ot_clear_gvariant (&serialized); + ot_clear_gvariant (&archive_metadata); + g_clear_object (&mem); + g_clear_object (&temp_info); + g_clear_object (&meta_temp_file); + g_clear_object (&content_temp_file); + ot_clear_checksum (&ret_checksum); + return ret; +} + +static gboolean +stage_object (OstreeRepo *self, + OstreeObjectType objtype, + GFileInfo *file_info, + GVariant *xattrs, + GInputStream *input, + const char *expected_checksum, + GChecksum **out_checksum, + GCancellable *cancellable, + GError **error) +{ + gboolean ret = FALSE; + OstreeRepoPrivate *priv = GET_PRIVATE (self); + GChecksum *ret_checksum = NULL; + GFileInfo *temp_info = NULL; + GFile *temp_file = NULL; + gboolean already_exists; + const char *actual_checksum; + + g_return_val_if_fail (priv->in_transaction, FALSE); + + if (g_cancellable_set_error_if_cancelled (cancellable, error)) + return FALSE; + + g_assert (expected_checksum || out_checksum); + + if (expected_checksum) + { + if (!ostree_repo_has_object (self, objtype, expected_checksum, &already_exists, cancellable, error)) + goto out; + } + else + already_exists = FALSE; + + g_assert (objtype != OSTREE_OBJECT_TYPE_ARCHIVED_FILE_CONTENT); + g_assert (objtype != OSTREE_OBJECT_TYPE_ARCHIVED_FILE_META); + + if (objtype == OSTREE_OBJECT_TYPE_RAW_FILE) + { + g_assert (file_info != NULL); + if (g_file_info_get_file_type (file_info) == G_FILE_TYPE_REGULAR) + g_assert (input != NULL); + } + else if (OSTREE_OBJECT_TYPE_IS_META (objtype)) + { + g_assert (xattrs == NULL); + g_assert (input != NULL); + } + + if (!already_exists) + { + if (objtype == OSTREE_OBJECT_TYPE_RAW_FILE && priv->mode == OSTREE_REPO_MODE_ARCHIVE) + { + if (!impl_stage_archive_file_object_from_raw (self, file_info, xattrs, input, + expected_checksum, + out_checksum ? &ret_checksum : NULL, + cancellable, error)) + goto out; + } + else + { + if (!ostree_create_temp_file_from_input (priv->tmp_dir, + "store-tmp-", NULL, + file_info, xattrs, input, + objtype, + &temp_file, + out_checksum ? &ret_checksum : NULL, + cancellable, error)) + goto out; + + if (!ret_checksum) + actual_checksum = expected_checksum; + else + { + actual_checksum = g_checksum_get_string (ret_checksum); + if (expected_checksum && strcmp (actual_checksum, expected_checksum) != 0) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "Corrupted %s object %s (actual checksum is %s)", + ostree_object_type_to_string (objtype), + expected_checksum, actual_checksum); + goto out; + } + } + + g_hash_table_insert (priv->pending_transaction_tmpfiles, + create_checksum_and_objtype (actual_checksum, objtype), + g_strdup (ot_gfile_get_basename_cached (temp_file))); + g_clear_object (&temp_file); + } + } ret = TRUE; ot_transfer_out_value(out_checksum, &ret_checksum); - ot_transfer_out_value(out_tmpname, &ret_tmpname); out: if (temp_file) (void) unlink (ot_gfile_get_path_cached (temp_file)); g_clear_object (&temp_file); - g_clear_object (&temp_out); + g_clear_object (&temp_info); ot_clear_checksum (&ret_checksum); return ret; } @@ -726,7 +931,6 @@ commit_staged_file (OstreeRepo *self, GFile *file, const char *checksum, OstreeObjectType objtype, - gboolean *did_exist, GCancellable *cancellable, GError **error) { @@ -740,12 +944,9 @@ commit_staged_file (OstreeRepo *self, if (!ot_gfile_ensure_directory (checksum_dir, FALSE, error)) goto out; - *did_exist = FALSE; if (link (ot_gfile_get_path_cached (file), ot_gfile_get_path_cached (dest_file)) < 0) { - if (errno == EEXIST) - *did_exist = TRUE; - else + if (errno != EEXIST) { ot_util_set_error_from_errno (error, errno); g_prefix_error (error, "Storing file '%s': ", @@ -762,114 +963,73 @@ commit_staged_file (OstreeRepo *self, return ret; } -static gboolean -stage_and_commit_from_input (OstreeRepo *self, - OstreeObjectType objtype, - GFileInfo *file_info, - GVariant *xattrs, - GInputStream *input, - GChecksum **out_checksum, - GCancellable *cancellable, - GError **error) +gboolean +ostree_repo_prepare_transaction (OstreeRepo *self, + GCancellable *cancellable, + GError **error) { - gboolean ret = FALSE; - GFile *tmp_file = NULL; - GChecksum *ret_checksum = NULL; - gboolean did_exist; + OstreeRepoPrivate *priv = GET_PRIVATE (self); + + g_return_val_if_fail (priv->in_transaction == FALSE, FALSE); - if (!ostree_repo_stage_object (self, objtype, - file_info, xattrs, input, - &tmp_file, &ret_checksum, - cancellable, error)) - goto out; + priv->in_transaction = TRUE; - if (!commit_staged_file (self, tmp_file, g_checksum_get_string (ret_checksum), - objtype, &did_exist, cancellable, error)) - goto out; - g_clear_object (&tmp_file); - - ret = TRUE; - ot_transfer_out_value(out_checksum, &ret_checksum); - out: - if (tmp_file) - (void) unlink (ot_gfile_get_path_cached (tmp_file)); - g_clear_object (&tmp_file); - ot_clear_checksum (&ret_checksum); - return ret; + return TRUE; } -static gboolean -commit_file (OstreeRepo *self, - GFile *file, - const char *expected_checksum, - GChecksum **out_checksum, - GCancellable *cancellable, - GError **error) +gboolean +ostree_repo_commit_transaction (OstreeRepo *self, + GCancellable *cancellable, + GError **error) { gboolean ret = FALSE; - GFileInfo *file_info = NULL; - GVariant *xattrs = NULL; - GInputStream *input = NULL; - GFile *tmp_file = NULL; - GChecksum *ret_checksum = NULL; - gboolean did_exist; - OstreeObjectType objtype; + OstreeRepoPrivate *priv = GET_PRIVATE (self); + GFile *f = NULL; + GHashTableIter iter; + gpointer key, value; + char *checksum = NULL; - g_assert (expected_checksum || out_checksum); + g_return_val_if_fail (priv->in_transaction == TRUE, FALSE); - file_info = g_file_query_info (file, OSTREE_GIO_FAST_QUERYINFO, - G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, - cancellable, error); - if (!file_info) - goto out; + priv->in_transaction = FALSE; - xattrs = ostree_get_xattrs_for_file (file, error); - if (!xattrs) - goto out; - - if (g_file_info_get_file_type (file_info) == G_FILE_TYPE_REGULAR) + g_hash_table_iter_init (&iter, priv->pending_transaction_tmpfiles); + while (g_hash_table_iter_next (&iter, &key, &value)) { - input = (GInputStream*)g_file_read (file, cancellable, error); - if (!input) + const char *checksum_and_type = key; + const char *filename = value; + const char *type_str; + OstreeObjectType objtype; + + type_str = strrchr (checksum_and_type, '.'); + g_assert (type_str); + g_free (checksum); + checksum = g_strndup (checksum_and_type, type_str - checksum_and_type); + + objtype = ostree_object_type_from_string (type_str + 1); + + g_clear_object (&f); + f = g_file_get_child (priv->tmp_dir, filename); + + if (!commit_staged_file (self, f, checksum, objtype, cancellable, error)) goto out; } - objtype = get_objtype_for_repo_file (self); - - if (!ostree_repo_stage_object (self, objtype, - file_info, xattrs, input, - &tmp_file, out_checksum ? &ret_checksum : NULL, - cancellable, error)) - goto out; - - if (expected_checksum == NULL) - expected_checksum = g_checksum_get_string (ret_checksum); - - if (!commit_staged_file (self, tmp_file, expected_checksum, objtype, - &did_exist, cancellable, error)) - goto out; - g_clear_object (&tmp_file); - ret = TRUE; - ot_transfer_out_value(out_checksum, &ret_checksum); out: - if (tmp_file) - (void) unlink (ot_gfile_get_path_cached (tmp_file)); - g_clear_object (&tmp_file); - g_clear_object (&file_info); - ot_clear_gvariant (&xattrs); - g_clear_object (&input); - ot_clear_checksum (&ret_checksum); + g_free (checksum); + g_hash_table_remove_all (priv->pending_transaction_tmpfiles); + g_clear_object (&f); return ret; } static gboolean -import_gvariant_object (OstreeRepo *self, - OstreeObjectType type, - GVariant *variant, - GChecksum **out_checksum, - GCancellable *cancellable, - GError **error) +stage_gvariant_object (OstreeRepo *self, + OstreeObjectType type, + GVariant *variant, + GChecksum **out_checksum, + GCancellable *cancellable, + GError **error) { gboolean ret = FALSE; GChecksum *ret_checksum = NULL; @@ -881,10 +1041,9 @@ import_gvariant_object (OstreeRepo *self, g_variant_get_size (serialized), NULL); - if (!stage_and_commit_from_input (self, type, - NULL, NULL, mem, - &ret_checksum, - cancellable, error)) + if (!stage_object (self, type, + NULL, NULL, mem, + NULL, &ret_checksum, cancellable, error)) goto out; ret = TRUE; @@ -904,40 +1063,46 @@ ostree_repo_load_variant (OstreeRepo *self, GError **error) { gboolean ret = FALSE; - OstreeObjectType type; + OstreeRepoPrivate *priv = GET_PRIVATE (self); GFile *object_path = NULL; + GFile *tmpfile = NULL; GVariant *ret_variant = NULL; + char *pending_key = NULL; + const char *pending_tmpfile; g_return_val_if_fail (OSTREE_OBJECT_TYPE_IS_META (expected_type), FALSE); - object_path = ostree_repo_get_object_path (self, sha256, expected_type); - - if (!ostree_parse_metadata_file (object_path, &type, &ret_variant, error)) - goto out; - - if (type != expected_type) + pending_key = create_checksum_and_objtype (sha256, expected_type); + if ((pending_tmpfile = g_hash_table_lookup (priv->pending_transaction_tmpfiles, pending_key)) != NULL) { - g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, - "Corrupted metadata object '%s'; found type %u, expected %u", sha256, - type, (guint32)expected_type); - goto out; + tmpfile = g_file_get_child (priv->tmp_dir, pending_tmpfile); + if (!ostree_map_metadata_file (tmpfile, expected_type, &ret_variant, error)) + goto out; + } + else + { + object_path = ostree_repo_get_object_path (self, sha256, expected_type); + if (!ostree_map_metadata_file (object_path, expected_type, &ret_variant, error)) + goto out; } ret = TRUE; - ot_transfer_out_value(out_variant, &ret_variant); + ot_transfer_out_value (out_variant, &ret_variant); out: g_clear_object (&object_path); + g_clear_object (&tmpfile); + g_free (pending_key); ot_clear_gvariant (&ret_variant); return ret; } static gboolean -import_directory_meta (OstreeRepo *self, - GFileInfo *file_info, - GVariant *xattrs, - GChecksum **out_checksum, - GCancellable *cancellable, - GError **error) +stage_directory_meta (OstreeRepo *self, + GFileInfo *file_info, + GVariant *xattrs, + GChecksum **out_checksum, + GCancellable *cancellable, + GError **error) { gboolean ret = FALSE; GChecksum *ret_checksum = NULL; @@ -948,8 +1113,8 @@ import_directory_meta (OstreeRepo *self, dirmeta = ostree_create_directory_metadata (file_info, xattrs); - if (!import_gvariant_object (self, OSTREE_OBJECT_TYPE_DIR_META, - dirmeta, &ret_checksum, cancellable, error)) + if (!stage_gvariant_object (self, OSTREE_OBJECT_TYPE_DIR_META, + dirmeta, &ret_checksum, cancellable, error)) goto out; ret = TRUE; @@ -981,107 +1146,40 @@ ostree_repo_get_object_path (OstreeRepo *self, gboolean ostree_repo_store_object_trusted (OstreeRepo *self, - GFile *file, - const char *checksum, OstreeObjectType objtype, + const char *checksum, + GFileInfo *file_info, + GVariant *xattrs, + GInputStream *input, GCancellable *cancellable, GError **error) { - gboolean ret = FALSE; - GFile *local_object = NULL; - GInputStream *input = NULL; - - local_object = ostree_repo_get_object_path (self, checksum, objtype); - - if (!g_file_query_exists (local_object, cancellable)) - { - if (!OSTREE_OBJECT_TYPE_IS_META (objtype)) - { - if (!commit_file (self, file, checksum, NULL, cancellable, error)) - goto out; - } - else - { - input = (GInputStream*)g_file_read (file, cancellable, error); - if (!input) - goto out; - - if (!stage_and_commit_from_input (self, objtype, - NULL, NULL, input, - NULL, cancellable, error)) - goto out; - } - } - - ret = TRUE; - out: - g_clear_object (&input); - g_clear_object (&local_object); - return ret; + return stage_object (self, objtype, + file_info, xattrs, input, + checksum, NULL, cancellable, error); } gboolean -ostree_repo_store_archived_file (OstreeRepo *self, - const char *expected_checksum, - const char *path, - OstreeObjectType objtype, - gboolean *did_exist, - GError **error) +ostree_repo_store_object (OstreeRepo *self, + OstreeObjectType objtype, + const char *expected_checksum, + GFileInfo *file_info, + GVariant *xattrs, + GInputStream *input, + GCancellable *cancellable, + GError **error) { gboolean ret = FALSE; - GChecksum *checksum = NULL; - GFileInfo *file_info = NULL; - GFile *tmp_file = NULL; - GFile *src = NULL; - GInputStream *input = NULL; - GVariant *xattrs = NULL; - OstreeObjectType dest_objtype; - - src = ot_gfile_new_for_path (path); - - if (OSTREE_OBJECT_TYPE_IS_META(objtype)) - { - dest_objtype = objtype; - input = (GInputStream*)g_file_read (src, NULL, error); - if (!input) - goto out; - } - else - { - /* Ensure we convert archived files to bare, or vice versa */ - dest_objtype = get_objtype_for_repo_file (self); - if (!ostree_parse_archived_file (src, &file_info, &xattrs, &input, NULL, error)) - goto out; - } - - if (!ostree_repo_stage_object (self, dest_objtype, - file_info, xattrs, input, - &tmp_file, &checksum, NULL, error)) + GChecksum *actual_checksum = NULL; + + if (!stage_object (self, objtype, + file_info, xattrs, input, + expected_checksum, &actual_checksum, cancellable, error)) goto out; - - if (strcmp (g_checksum_get_string (checksum), expected_checksum) != 0) - { - g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, - "Corrupted object %s (actual checksum is %s)", - expected_checksum, g_checksum_get_string (checksum)); - goto out; - } - - if (!commit_staged_file (self, tmp_file, g_checksum_get_string (checksum), - dest_objtype, did_exist, NULL, error)) - goto out; - g_clear_object (&tmp_file); ret = TRUE; out: - if (tmp_file) - (void) unlink (ot_gfile_get_path_cached (tmp_file)); - g_clear_object (&tmp_file); - g_clear_object (&src); - g_clear_object (&file_info); - ot_clear_gvariant (&xattrs); - g_clear_object (&input); - ot_clear_checksum (&checksum); + ot_clear_checksum (&actual_checksum); return ret; } @@ -1124,16 +1222,17 @@ ostree_repo_write_ref (OstreeRepo *self, } static gboolean -import_commit (OstreeRepo *self, - const char *branch, - const char *parent, - const char *subject, - const char *body, - GVariant *metadata, - const char *root_contents_checksum, - const char *root_metadata_checksum, - GChecksum **out_commit, - GError **error) +do_commit_write_ref (OstreeRepo *self, + const char *branch, + const char *parent, + const char *subject, + const char *body, + GVariant *metadata, + const char *root_contents_checksum, + const char *root_metadata_checksum, + GChecksum **out_commit, + GCancellable *cancellable, + GError **error) { gboolean ret = FALSE; GChecksum *ret_commit = NULL; @@ -1153,8 +1252,11 @@ import_commit (OstreeRepo *self, root_contents_checksum, root_metadata_checksum); g_variant_ref_sink (commit); - if (!import_gvariant_object (self, OSTREE_OBJECT_TYPE_COMMIT, - commit, &ret_commit, NULL, error)) + if (!stage_gvariant_object (self, OSTREE_OBJECT_TYPE_COMMIT, + commit, &ret_commit, NULL, error)) + goto out; + + if (!ostree_repo_commit_transaction (self, cancellable, error)) goto out; if (!ostree_repo_write_ref (self, NULL, branch, g_checksum_get_string (ret_commit), error)) @@ -1240,13 +1342,13 @@ create_tree_variant_from_hashes (GHashTable *file_checksums, } static gboolean -import_directory_recurse (OstreeRepo *self, - GFile *base, - GFile *dir, - GChecksum **out_contents_checksum, - GChecksum **out_metadata_checksum, - GCancellable *cancellable, - GError **error) +stage_directory_recurse (OstreeRepo *self, + GFile *base, + GFile *dir, + GChecksum **out_contents_checksum, + GChecksum **out_metadata_checksum, + GCancellable *cancellable, + GError **error) { gboolean ret = FALSE; GError *temp_error = NULL; @@ -1273,7 +1375,8 @@ import_directory_recurse (OstreeRepo *self, if (!xattrs) goto out; - if (!import_directory_meta (self, child_info, xattrs, &ret_metadata_checksum, cancellable, error)) + if (!stage_directory_meta (self, child_info, xattrs, &ret_metadata_checksum, + cancellable, error)) goto out; g_clear_object (&child_info); @@ -1304,8 +1407,8 @@ import_directory_recurse (OstreeRepo *self, GChecksum *child_dir_metadata_checksum = NULL; GChecksum *child_dir_contents_checksum = NULL; - if (!import_directory_recurse (self, base, child, &child_dir_contents_checksum, - &child_dir_metadata_checksum, cancellable, error)) + if (!stage_directory_recurse (self, base, child, &child_dir_contents_checksum, + &child_dir_metadata_checksum, cancellable, error)) goto out; g_hash_table_replace (dir_contents_checksums, g_strdup (name), @@ -1332,9 +1435,9 @@ import_directory_recurse (OstreeRepo *self, if (!xattrs) goto out; - if (!stage_and_commit_from_input (self, get_objtype_for_repo_file (self), - child_info, xattrs, file_input, - &child_file_checksum, cancellable, error)) + if (!stage_object (self, OSTREE_OBJECT_TYPE_RAW_FILE, + child_info, xattrs, file_input, NULL, + &child_file_checksum, cancellable, error)) goto out; g_hash_table_replace (file_checksums, g_strdup (name), @@ -1353,9 +1456,9 @@ import_directory_recurse (OstreeRepo *self, dir_contents_checksums, dir_metadata_checksums); - if (!import_gvariant_object (self, OSTREE_OBJECT_TYPE_DIR_TREE, - serialized_tree, &ret_contents_checksum, - cancellable, error)) + if (!stage_gvariant_object (self, OSTREE_OBJECT_TYPE_DIR_TREE, + serialized_tree, &ret_contents_checksum, + cancellable, error)) goto out; ot_transfer_out_value(out_metadata_checksum, &ret_metadata_checksum); @@ -1405,19 +1508,22 @@ ostree_repo_commit_directory (OstreeRepo *self, g_return_val_if_fail (subject != NULL, FALSE); g_return_val_if_fail (metadata == NULL || g_variant_is_of_type (metadata, G_VARIANT_TYPE ("a{sv}")), FALSE); + if (!ostree_repo_prepare_transaction (self, cancellable, error)) + goto out; + if (parent == NULL) parent = branch; if (!ostree_repo_resolve_rev (self, parent, TRUE, ¤t_head, error)) goto out; - if (!import_directory_recurse (self, dir, dir, &root_contents_checksum, &root_metadata_checksum, cancellable, error)) + if (!stage_directory_recurse (self, dir, dir, &root_contents_checksum, &root_metadata_checksum, cancellable, error)) goto out; - if (!import_commit (self, branch, current_head, subject, body, metadata, - g_checksum_get_string (root_contents_checksum), - g_checksum_get_string (root_metadata_checksum), - &ret_commit_checksum, error)) + if (!do_commit_write_ref (self, branch, current_head, subject, body, metadata, + g_checksum_get_string (root_contents_checksum), + g_checksum_get_string (root_metadata_checksum), + &ret_commit_checksum, cancellable, error)) goto out; ret = TRUE; @@ -1491,9 +1597,10 @@ import_libarchive_entry_file (OstreeRepo *self, if (g_file_info_get_file_type (file_info) == G_FILE_TYPE_REGULAR) archive_stream = ostree_libarchive_input_stream_new (a); - if (!stage_and_commit_from_input (self, get_objtype_for_repo_file (self), - file_info, NULL, archive_stream, - &ret_checksum, cancellable, error)) + if (!stage_object (self, OSTREE_OBJECT_TYPE_RAW_FILE, + file_info, NULL, archive_stream, + NULL, &ret_checksum, + cancellable, error)) goto out; ret = TRUE; @@ -1611,9 +1718,9 @@ file_tree_import_recurse (OstreeRepo *self, dir_contents_checksums, dir_metadata_checksums); - if (!import_gvariant_object (self, OSTREE_OBJECT_TYPE_DIR_TREE, - serialized_tree, &ret_contents_checksum_obj, - cancellable, error)) + if (!stage_gvariant_object (self, OSTREE_OBJECT_TYPE_DIR_TREE, + serialized_tree, &ret_contents_checksum_obj, + cancellable, error)) goto out; ret_contents_checksum = g_strdup (g_checksum_get_string (ret_contents_checksum_obj)); @@ -1772,7 +1879,7 @@ import_libarchive (OstreeRepo *self, { } - if (!import_directory_meta (self, file_info, NULL, &tmp_checksum, cancellable, error)) + if (!stage_directory_meta (self, file_info, NULL, &tmp_checksum, cancellable, error)) goto out; if (parent == NULL) @@ -1879,6 +1986,9 @@ ostree_repo_commit_tarfile (OstreeRepo *self, g_return_val_if_fail (subject != NULL, FALSE); g_return_val_if_fail (metadata == NULL || g_variant_is_of_type (metadata, G_VARIANT_TYPE ("a{sv}")), FALSE); + if (!ostree_repo_prepare_transaction (self, cancellable, error)) + goto out; + if (parent == NULL) parent = branch; @@ -1888,8 +1998,9 @@ ostree_repo_commit_tarfile (OstreeRepo *self, if (!import_libarchive (self, path, &root_contents_checksum, &root_metadata_checksum, cancellable, error)) goto out; - if (!import_commit (self, branch, current_head, subject, body, metadata, - root_contents_checksum, root_metadata_checksum, &ret_commit_checksum, error)) + if (!do_commit_write_ref (self, branch, current_head, subject, body, metadata, + root_contents_checksum, root_metadata_checksum, &ret_commit_checksum, + cancellable, error)) goto out; ret = TRUE; @@ -1947,8 +2058,10 @@ iter_object_dir (OstreeRepo *self, if (g_str_has_suffix (name, ".file")) objtype = OSTREE_OBJECT_TYPE_RAW_FILE; - else if (g_str_has_suffix (name, ".archive")) - objtype = OSTREE_OBJECT_TYPE_ARCHIVED_FILE; + else if (g_str_has_suffix (name, ".archive-meta")) + objtype = OSTREE_OBJECT_TYPE_ARCHIVED_FILE_META; + else if (g_str_has_suffix (name, ".archive-content")) + objtype = OSTREE_OBJECT_TYPE_ARCHIVED_FILE_CONTENT; else if (g_str_has_suffix (name, ".dirtree")) objtype = OSTREE_OBJECT_TYPE_DIR_TREE; else if (g_str_has_suffix (name, ".dirmeta")) @@ -2098,13 +2211,15 @@ checkout_tree (OstreeRepo *self, OstreeRepoPrivate *priv = GET_PRIVATE (self); gboolean ret = FALSE; GError *temp_error = NULL; + GVariant *archive_metadata = NULL; GFileInfo *file_info = NULL; - GInputStream *packed_input = NULL; GVariant *xattrs = NULL; GFileEnumerator *dir_enum = NULL; GFile *src_child = NULL; GFile *dest_path = NULL; GFile *object_path = NULL; + GFile *content_object_path = NULL; + GInputStream *content_input = NULL; if (!_ostree_repo_file_get_xattrs (source, &xattrs, NULL, error)) goto out; @@ -2131,7 +2246,10 @@ checkout_tree (OstreeRepo *self, name = g_file_info_get_attribute_byte_string (file_info, "standard::name"); type = g_file_info_get_attribute_uint32 (file_info, "standard::type"); + g_clear_object (&dest_path); dest_path = g_file_get_child (destination, name); + + g_clear_object (&src_child); src_child = g_file_get_child ((GFile*)source, name); if (type == G_FILE_TYPE_DIRECTORY) @@ -2143,20 +2261,36 @@ checkout_tree (OstreeRepo *self, { const char *checksum = _ostree_repo_file_get_checksum ((OstreeRepoFile*)src_child); - object_path = ostree_repo_get_object_path (self, checksum, get_objtype_for_repo_file (self)); + g_clear_object (&object_path); if (priv->mode == OSTREE_REPO_MODE_ARCHIVE) { - if (!ostree_parse_archived_file (object_path, NULL, &xattrs, &packed_input, - cancellable, error)) + if (!ostree_repo_load_variant (self, OSTREE_OBJECT_TYPE_ARCHIVED_FILE_META, checksum, &archive_metadata, error)) goto out; + + ot_clear_gvariant (&xattrs); + if (!ostree_parse_archived_file_meta (archive_metadata, NULL, &xattrs, error)) + goto out; + + g_clear_object (&content_object_path); + content_object_path = ostree_repo_get_object_path (self, checksum, OSTREE_OBJECT_TYPE_ARCHIVED_FILE_CONTENT); + + g_clear_object (&content_input); + if (g_file_info_get_file_type (file_info) == G_FILE_TYPE_REGULAR) + { + content_input = (GInputStream*)g_file_read (content_object_path, cancellable, error); + if (!content_input) + goto out; + } if (!checkout_file_from_input (dest_path, mode, file_info, xattrs, - packed_input, cancellable, error)) + content_input, cancellable, error)) goto out; } else { + object_path = ostree_repo_get_object_path (self, checksum, OSTREE_OBJECT_TYPE_RAW_FILE); + if (link (ot_gfile_get_path_cached (object_path), ot_gfile_get_path_cached (dest_path)) < 0) { ot_util_set_error_from_errno (error, errno); @@ -2165,12 +2299,7 @@ checkout_tree (OstreeRepo *self, } } - g_clear_object (&object_path); - g_clear_object (&dest_path); g_clear_object (&file_info); - g_clear_object (&src_child); - g_clear_object (&packed_input); - ot_clear_gvariant (&xattrs); } if (file_info == NULL && temp_error != NULL) { @@ -2182,10 +2311,12 @@ checkout_tree (OstreeRepo *self, out: g_clear_object (&dir_enum); g_clear_object (&file_info); - g_clear_object (&packed_input); ot_clear_gvariant (&xattrs); + ot_clear_gvariant (&archive_metadata); g_clear_object (&src_child); g_clear_object (&object_path); + g_clear_object (&content_object_path); + g_clear_object (&content_input); g_clear_object (&dest_path); g_free (dest_path); return ret; diff --git a/src/libostree/ostree-repo.h b/src/libostree/ostree-repo.h index d01134bc..03f687b0 100644 --- a/src/libostree/ostree-repo.h +++ b/src/libostree/ostree-repo.h @@ -79,17 +79,36 @@ GFile * ostree_repo_get_object_path (OstreeRepo *self, GFile * ostree_repo_get_file_object_path (OstreeRepo *self, const char *object); -gboolean ostree_repo_store_archived_file (OstreeRepo *self, - const char *expected_checksum, - const char *path, - OstreeObjectType objtype, - gboolean *did_exist, - GError **error); +gboolean ostree_repo_prepare_transaction (OstreeRepo *self, + GCancellable *cancellable, + GError **error); + +gboolean ostree_repo_commit_transaction (OstreeRepo *self, + GCancellable *cancellable, + GError **error); + +gboolean ostree_repo_has_object (OstreeRepo *self, + OstreeObjectType objtype, + const char *checksum, + gboolean *out_have_object, + GCancellable *cancellable, + GError **error); + +gboolean ostree_repo_store_object (OstreeRepo *self, + OstreeObjectType objtype, + const char *expected_checksum, + GFileInfo *file_info, + GVariant *xattrs, + GInputStream *content, + GCancellable *cancellable, + GError **error); gboolean ostree_repo_store_object_trusted (OstreeRepo *self, - GFile *file, - const char *checksum, OstreeObjectType objtype, + const char *checksum, + GFileInfo *file_info, + GVariant *xattrs, + GInputStream *content, GCancellable *cancellable, GError **error); diff --git a/src/ostree/ostree-pull.c b/src/ostree/ostree-pull.c index 5ca9d13c..3c7a87e9 100644 --- a/src/ostree/ostree-pull.c +++ b/src/ostree/ostree-pull.c @@ -58,17 +58,18 @@ static gboolean fetch_uri (OstreeRepo *repo, SoupSession *soup, SoupURI *uri, - char **temp_filename, + const char *tmp_prefix, + GFile **out_temp_filename, GError **error) { gboolean ret = FALSE; SoupMessage *msg = NULL; guint response; - char *template = NULL; - int fd; SoupBuffer *buf = NULL; - GFile *tempf = NULL; char *uri_string = NULL; + GFile *ret_temp_filename = NULL; + GOutputStream *output_stream = NULL; + gsize bytes_written; uri_string = soup_uri_to_string (uri, FALSE); log_verbose ("Fetching %s", uri_string); @@ -83,31 +84,78 @@ fetch_uri (OstreeRepo *repo, goto out; } - template = g_strdup_printf ("%s/tmp-fetchXXXXXX", ostree_repo_get_path (repo)); - - fd = g_mkstemp (template); - if (fd < 0) - { - ot_util_set_error_from_errno (error, errno); - goto out; - } - close (fd); - tempf = ot_gfile_new_for_path (template); + if (!ostree_create_temp_regular_file (ostree_repo_get_tmpdir (repo), + tmp_prefix, NULL, + &ret_temp_filename, + &output_stream, + NULL, error)) + goto out; buf = soup_message_body_flatten (msg->response_body); - if (!g_file_replace_contents (tempf, buf->data, buf->length, NULL, FALSE, 0, NULL, NULL, error)) + if (!g_output_stream_write_all (output_stream, buf->data, buf->length, + &bytes_written, NULL, error)) + goto out; + + if (!g_output_stream_close (output_stream, NULL, error)) goto out; - *temp_filename = template; - template = NULL; + ret = TRUE; + ot_transfer_out_value (out_temp_filename, &ret_temp_filename); + out: + if (ret_temp_filename) + (void) unlink (ot_gfile_get_path_cached (ret_temp_filename)); + g_clear_object (&ret_temp_filename); + g_free (uri_string); + g_clear_object (&msg); + return ret; +} + +static gboolean +fetch_object (OstreeRepo *repo, + SoupSession *soup, + SoupURI *baseuri, + const char *checksum, + OstreeObjectType objtype, + gboolean *did_exist, + GFile **out_file, + GError **error) +{ + gboolean ret = FALSE; + GFile *ret_file = NULL; + char *objpath = NULL; + char *relpath = NULL; + SoupURI *obj_uri = NULL; + gboolean exists; + + g_assert (objtype != OSTREE_OBJECT_TYPE_RAW_FILE); + + if (!ostree_repo_has_object (repo, objtype, checksum, &exists, NULL, error)) + goto out; + + if (!exists) + { + objpath = ostree_get_relative_object_path (checksum, objtype); + obj_uri = soup_uri_copy (baseuri); + relpath = g_build_filename (soup_uri_get_path (obj_uri), objpath, NULL); + soup_uri_set_path (obj_uri, relpath); + + if (!fetch_uri (repo, soup, obj_uri, ostree_object_type_to_string (objtype), &ret_file, error)) + goto out; + + *did_exist = FALSE; + } + else + *did_exist = TRUE; ret = TRUE; + ot_transfer_out_value (out_file, &ret_file); out: - g_free (uri_string); - g_free (template); - g_clear_object (&msg); - g_clear_object (&tempf); + if (obj_uri) + soup_uri_free (obj_uri); + g_clear_object (&ret_file); + g_free (objpath); + g_free (relpath); return ret; } @@ -115,39 +163,40 @@ static gboolean store_object (OstreeRepo *repo, SoupSession *soup, SoupURI *baseuri, - const char *object, + const char *checksum, OstreeObjectType objtype, gboolean *did_exist, GError **error) { gboolean ret = FALSE; - char *filename = NULL; - char *objpath = NULL; - char *relpath = NULL; - SoupURI *obj_uri = NULL; + GFile *filename = NULL; + GFileInfo *file_info = NULL; + GInputStream *input = NULL; - g_assert (objtype != OSTREE_OBJECT_TYPE_RAW_FILE); - - objpath = ostree_get_relative_object_path (object, objtype); - obj_uri = soup_uri_copy (baseuri); - relpath = g_build_filename (soup_uri_get_path (obj_uri), objpath, NULL); - soup_uri_set_path (obj_uri, relpath); - - if (!fetch_uri (repo, soup, obj_uri, &filename, error)) + if (!fetch_object (repo, soup, baseuri, checksum, objtype, did_exist, &filename, error)) goto out; - if (!ostree_repo_store_archived_file (repo, object, filename, objtype, did_exist, error)) - goto out; + if (!*did_exist) + { + file_info = g_file_query_info (filename, OSTREE_GIO_FAST_QUERYINFO, + G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL, error); + if (!file_info) + goto out; + + input = (GInputStream*)g_file_read (filename, NULL, error); + if (!input) + goto out; + + if (!ostree_repo_store_object (repo, objtype, checksum, file_info, NULL, input, NULL, error)) + goto out; + } ret = TRUE; out: - if (obj_uri) - soup_uri_free (obj_uri); if (filename) - (void) unlink (filename); - g_free (filename); - g_free (objpath); - g_free (relpath); + (void) unlink (ot_gfile_get_path_cached (filename)); + g_clear_object (&file_info); + g_clear_object (&input); return ret; } @@ -164,6 +213,12 @@ store_tree_recurse (OstreeRepo *repo, GVariant *dirs_variant = NULL; gboolean did_exist; int i, n; + GVariant *archive_metadata = NULL; + GFileInfo *archive_file_info = NULL; + GVariant *archive_xattrs = NULL; + GFile *meta_file = NULL; + GFile *content_file = NULL; + GInputStream *input = NULL; if (!store_object (repo, soup, base_uri, rev, OSTREE_OBJECT_TYPE_DIR_TREE, &did_exist, error)) goto out; @@ -187,7 +242,41 @@ store_tree_recurse (OstreeRepo *repo, g_variant_get_child (files_variant, i, "(&s&s)", &filename, &checksum); - if (!store_object (repo, soup, base_uri, checksum, OSTREE_OBJECT_TYPE_ARCHIVED_FILE, &did_exist, error)) + g_clear_object (&meta_file); + + if (!fetch_object (repo, soup, base_uri, checksum, + OSTREE_OBJECT_TYPE_ARCHIVED_FILE_META, + &did_exist, + &meta_file, + error)) + goto out; + + g_clear_object (&content_file); + if (!fetch_object (repo, soup, base_uri, checksum, + OSTREE_OBJECT_TYPE_ARCHIVED_FILE_CONTENT, + &did_exist, + &content_file, + error)) + goto out; + + if (!ostree_map_metadata_file (meta_file, OSTREE_OBJECT_TYPE_ARCHIVED_FILE_META, + &archive_metadata, error)) + goto out; + + if (!ostree_parse_archived_file_meta (archive_metadata, &archive_file_info, &archive_xattrs, error)) + goto out; + + if (g_file_info_get_file_type (archive_file_info) == G_FILE_TYPE_REGULAR) + { + input = (GInputStream*)g_file_read (content_file, NULL, error); + if (!input) + goto out; + } + + if (!ostree_repo_store_object (repo, OSTREE_OBJECT_TYPE_RAW_FILE, + checksum, + archive_file_info, archive_xattrs, input, + NULL, error)) goto out; } @@ -214,6 +303,10 @@ store_tree_recurse (OstreeRepo *repo, ot_clear_gvariant (&tree); ot_clear_gvariant (&files_variant); ot_clear_gvariant (&dirs_variant); + ot_clear_gvariant (&archive_metadata); + ot_clear_gvariant (&archive_xattrs); + g_clear_object (&archive_file_info); + g_clear_object (&input); return ret; } @@ -269,7 +362,6 @@ ostree_builtin_pull (int argc, char **argv, const char *repo_path, GError **erro char *key = NULL; char *baseurl = NULL; char *refpath = NULL; - char *temppath = NULL; GFile *tempf = NULL; char *remote_ref = NULL; char *original_rev = NULL; @@ -326,9 +418,8 @@ ostree_builtin_pull (int argc, char **argv, const char *repo_path, GError **erro SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_CONTENT_DECODER, SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_COOKIE_JAR, NULL); - if (!fetch_uri (repo, soup, target_uri, &temppath, error)) + if (!fetch_uri (repo, soup, target_uri, "ref-", &tempf, error)) goto out; - tempf = ot_gfile_new_for_path (temppath); if (!ot_gfile_load_contents_utf8 (tempf, &rev, NULL, NULL, error)) goto out; @@ -342,9 +433,15 @@ ostree_builtin_pull (int argc, char **argv, const char *repo_path, GError **erro { if (!ostree_validate_checksum_string (rev, error)) goto out; + + if (!ostree_repo_prepare_transaction (repo, NULL, error)) + goto out; if (!store_commit_recurse (repo, soup, base_uri, rev, error)) goto out; + + if (!ostree_repo_commit_transaction (repo, NULL, error)) + goto out; if (!ostree_repo_write_ref (repo, remote, branch, rev, error)) goto out; @@ -356,9 +453,9 @@ ostree_builtin_pull (int argc, char **argv, const char *repo_path, GError **erro out: if (context) g_option_context_free (context); - if (temppath) - (void) unlink (temppath); - g_free (temppath); + if (tempf) + (void) unlink (ot_gfile_get_path_cached (tempf)); + g_clear_object (&tempf); g_free (key); g_free (rev); g_free (remote_ref); diff --git a/src/ostree/ot-builtin-fsck.c b/src/ostree/ot-builtin-fsck.c index 033a8ec7..4ca0aa7d 100644 --- a/src/ostree/ot-builtin-fsck.c +++ b/src/ostree/ot-builtin-fsck.c @@ -35,37 +35,53 @@ static GOptionEntry options[] = { }; typedef struct { + OstreeRepo *repo; guint n_objects; gboolean had_error; } OtFsckData; static gboolean checksum_archived_file (OtFsckData *data, + const char *exp_checksum, GFile *file, GChecksum **out_checksum, GError **error) { gboolean ret = FALSE; GChecksum *ret_checksum = NULL; - GInputStream *in = NULL; + GVariant *archive_metadata = NULL; GVariant *xattrs = NULL; + GFile *content_path = NULL; + GInputStream *content_input = NULL; GFileInfo *file_info = NULL; char buf[8192]; gsize bytes_read; guint32 mode; - if (!ostree_parse_archived_file (file, &file_info, &xattrs, &in, NULL, error)) + if (!ostree_map_metadata_file (file, OSTREE_OBJECT_TYPE_ARCHIVED_FILE_META, &archive_metadata, error)) goto out; + if (!ostree_parse_archived_file_meta (archive_metadata, &file_info, &xattrs, error)) + goto out; + + content_path = ostree_repo_get_object_path (data->repo, exp_checksum, OSTREE_OBJECT_TYPE_ARCHIVED_FILE_CONTENT); + + if (g_file_info_get_file_type (file_info) == G_FILE_TYPE_REGULAR) + { + content_input = (GInputStream*)g_file_read (content_path, NULL, error); + if (!content_input) + goto out; + } + ret_checksum = g_checksum_new (G_CHECKSUM_SHA256); mode = g_file_info_get_attribute_uint32 (file_info, "unix::mode"); if (S_ISREG (mode)) { - g_assert (in != NULL); + g_assert (content_input != NULL); do { - if (!g_input_stream_read_all (in, buf, sizeof(buf), &bytes_read, NULL, error)) + if (!g_input_stream_read_all (content_input, buf, sizeof(buf), &bytes_read, NULL, error)) goto out; g_checksum_update (ret_checksum, (guint8*)buf, bytes_read); } @@ -97,9 +113,11 @@ checksum_archived_file (OtFsckData *data, ot_transfer_out_value (out_checksum, &ret_checksum); out: ot_clear_checksum (&ret_checksum); - g_clear_object (&in); g_clear_object (&file_info); ot_clear_gvariant (&xattrs); + ot_clear_gvariant (&archive_metadata); + g_clear_object (&content_path); + g_clear_object (&content_input); return ret; } @@ -112,40 +130,38 @@ object_iter_callback (OstreeRepo *repo, gpointer user_data) { OtFsckData *data = user_data; - const char *path = NULL; GChecksum *real_checksum = NULL; GError *error = NULL; - path = ot_gfile_get_path_cached (objf); - /* nlinks = g_file_info_get_attribute_uint32 (file_info, "unix::nlink"); if (nlinks < 2 && !quiet) g_printerr ("note: floating object: %s\n", path); */ - if (ostree_repo_get_mode (repo) == OSTREE_REPO_MODE_ARCHIVE - && !OSTREE_OBJECT_TYPE_IS_META (objtype)) + if (objtype == OSTREE_OBJECT_TYPE_ARCHIVED_FILE_META) { - if (!g_str_has_suffix (path, ".archive")) + if (!g_str_has_suffix (ot_gfile_get_path_cached (objf), ".archive-meta")) { g_set_error (&error, G_IO_ERROR, G_IO_ERROR_FAILED, "Invalid archive filename '%s'", - path); + ot_gfile_get_path_cached (objf)); goto out; } - if (!checksum_archived_file (data, objf, &real_checksum, &error)) + if (!checksum_archived_file (data, exp_checksum, objf, &real_checksum, &error)) goto out; } + else if (objtype == OSTREE_OBJECT_TYPE_ARCHIVED_FILE_CONTENT) + ; /* Handled above */ else { if (!ostree_checksum_file (objf, objtype, &real_checksum, NULL, &error)) goto out; } - if (strcmp (exp_checksum, g_checksum_get_string (real_checksum)) != 0) + if (real_checksum && strcmp (exp_checksum, g_checksum_get_string (real_checksum)) != 0) { data->had_error = TRUE; - g_printerr ("ERROR: corrupted object '%s' expected checksum: %s\n", - exp_checksum, g_checksum_get_string (real_checksum)); + g_printerr ("ERROR: corrupted object '%s'; actual checksum: %s\n", + ot_gfile_get_path_cached (objf), g_checksum_get_string (real_checksum)); } data->n_objects++; @@ -173,13 +189,14 @@ ostree_builtin_fsck (int argc, char **argv, const char *repo_path, GError **erro if (!g_option_context_parse (context, &argc, &argv, error)) goto out; - data.n_objects = 0; - data.had_error = FALSE; - repo = ostree_repo_new (repo_path); if (!ostree_repo_check (repo, error)) goto out; + data.repo = repo; + data.n_objects = 0; + data.had_error = FALSE; + if (!ostree_repo_iter_objects (repo, object_iter_callback, &data, error)) goto out; diff --git a/src/ostree/ot-builtin-local-clone.c b/src/ostree/ot-builtin-local-clone.c index 132821d5..7f883e7e 100644 --- a/src/ostree/ot-builtin-local-clone.c +++ b/src/ostree/ot-builtin-local-clone.c @@ -25,7 +25,8 @@ #include "ot-builtins.h" #include "ostree.h" -#include +#include +#include static GOptionEntry options[] = { { NULL } @@ -94,34 +95,67 @@ object_iter_callback (OstreeRepo *repo, gpointer user_data) { OtLocalCloneData *data = user_data; - GError *error = NULL; - gboolean did_exist; + GError *real_error = NULL; + GError **error = &real_error; + GFile *content_path = NULL; + GFileInfo *archive_info = NULL; + GVariant *archive_metadata = NULL; + GVariant *xattrs = NULL; + GInputStream *input = NULL; - if (ostree_repo_get_mode (data->src_repo) == OSTREE_REPO_MODE_ARCHIVE) + if (objtype == OSTREE_OBJECT_TYPE_RAW_FILE) + xattrs = ostree_get_xattrs_for_file (objfile, error); + + if (objtype == OSTREE_OBJECT_TYPE_ARCHIVED_FILE_CONTENT) + ; + else if (objtype == OSTREE_OBJECT_TYPE_ARCHIVED_FILE_META) { - if (!ostree_repo_store_archived_file (data->dest_repo, checksum, - ot_gfile_get_path_cached (objfile), - objtype, - &did_exist, - &error)) + if (!ostree_repo_load_variant (repo, OSTREE_OBJECT_TYPE_ARCHIVED_FILE_META, checksum, &archive_metadata, error)) + goto out; + + if (!ostree_parse_archived_file_meta (archive_metadata, &archive_info, &xattrs, error)) + goto out; + + content_path = ostree_repo_get_object_path (repo, checksum, OSTREE_OBJECT_TYPE_ARCHIVED_FILE_CONTENT); + + if (g_file_info_get_file_type (archive_info) == G_FILE_TYPE_REGULAR) + { + input = (GInputStream*)g_file_read (content_path, NULL, error); + if (!input) + goto out; + } + + if (!ostree_repo_store_object_trusted (data->dest_repo, OSTREE_OBJECT_TYPE_RAW_FILE, checksum, + archive_info, xattrs, input, + NULL, error)) goto out; } else { - if (!ostree_repo_store_object_trusted (data->dest_repo, - objfile, - checksum, - objtype, - NULL, - &error)) + if (g_file_info_get_file_type (file_info) == G_FILE_TYPE_REGULAR) + { + input = (GInputStream*)g_file_read (objfile, NULL, error); + if (!input) + goto out; + } + + if (!ostree_repo_store_object_trusted (data->dest_repo, objtype, checksum, + file_info, xattrs, input, + NULL, error)) goto out; } out: - if (error != NULL) + ot_clear_gvariant (&archive_metadata); + ot_clear_gvariant (&xattrs); + g_clear_object (&archive_info); + g_clear_object (&input); + g_clear_object (&content_path); + if (real_error != NULL) { - g_printerr ("%s\n", error->message); - g_clear_error (&error); + g_printerr ("%s\n", real_error->message); + g_clear_error (error); + exit (1); } } @@ -183,8 +217,14 @@ ostree_builtin_local_clone (int argc, char **argv, const char *repo_path, GError data.uids_differ = g_file_info_get_attribute_uint32 (src_info, "unix::uid") != g_file_info_get_attribute_uint32 (dest_info, "unix::uid"); + if (!ostree_repo_prepare_transaction (data.dest_repo, NULL, error)) + goto out; + if (!ostree_repo_iter_objects (data.src_repo, object_iter_callback, &data, error)) goto out; + + if (!ostree_repo_commit_transaction (data.dest_repo, NULL, error)) + goto out; src_dir = g_file_resolve_relative_path (src_repo_dir, "refs/heads"); dest_dir = g_file_resolve_relative_path (dest_repo_dir, "refs/heads"); diff --git a/src/ostree/ot-builtin-log.c b/src/ostree/ot-builtin-log.c index 9fa7b020..9f55fb32 100644 --- a/src/ostree/ot-builtin-log.c +++ b/src/ostree/ot-builtin-log.c @@ -69,7 +69,6 @@ ostree_builtin_log (int argc, char **argv, const char *repo_path, GError **error while (TRUE) { - OstreeObjectType type; char *formatted = NULL; guint32 version; const char *parent; diff --git a/src/ostree/ot-builtin-show.c b/src/ostree/ot-builtin-show.c index ab5b5c75..c25dc8c3 100644 --- a/src/ostree/ot-builtin-show.c +++ b/src/ostree/ot-builtin-show.c @@ -124,7 +124,7 @@ show_repo_meta (OstreeRepo *repo, g_print ("%s", buf); } while (bytes_read > 0); } - else if (objtype == OSTREE_OBJECT_TYPE_ARCHIVED_FILE) + else if (objtype == OSTREE_OBJECT_TYPE_ARCHIVED_FILE_CONTENT) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, "Can't show archived files yet");