diff options
author | Colin Walters <walters@verbum.org> | 2014-08-18 17:29:33 -0400 |
---|---|---|
committer | Colin Walters <walters@verbum.org> | 2014-08-18 17:29:33 -0400 |
commit | 0882a36e56f80dd5bc1e30c99292834432e87769 (patch) | |
tree | 8de730e6e1e783bba6300c1dab5db3da1122b73f | |
parent | 89ff7fb617f44e0ccf5b5d9e1df284d8959787d7 (diff) | |
download | ostree-0882a36e56f80dd5bc1e30c99292834432e87769.tar.gz |
A bit more metalink work
-rw-r--r-- | Makefile-ostree.am | 1 | ||||
-rw-r--r-- | src/libostree/ostree-metalink.c | 219 | ||||
-rw-r--r-- | src/libostree/ostree-metalink.h | 10 | ||||
-rw-r--r-- | src/libostree/ostree-repo-pull.c | 2 | ||||
-rw-r--r-- | src/libostree/ostree-repo.c | 9 | ||||
-rw-r--r-- | src/libotutil/ot-checksum-utils.c | 26 | ||||
-rw-r--r-- | src/libotutil/ot-checksum-utils.h | 5 | ||||
-rw-r--r-- | src/ostree/main.c | 1 | ||||
-rw-r--r-- | src/ostree/ot-builtin-summary.c | 64 | ||||
-rw-r--r-- | src/ostree/ot-builtins.h | 1 | ||||
-rwxr-xr-x | tests/test-pull-metalink.sh | 68 |
11 files changed, 358 insertions, 48 deletions
diff --git a/Makefile-ostree.am b/Makefile-ostree.am index f1381acd..76df368b 100644 --- a/Makefile-ostree.am +++ b/Makefile-ostree.am @@ -40,6 +40,7 @@ ostree_SOURCES = src/ostree/main.c \ src/ostree/ot-builtin-remote.c \ src/ostree/ot-builtin-reset.c \ src/ostree/ot-builtin-rev-parse.c \ + src/ostree/ot-builtin-summary.c \ src/ostree/ot-builtin-show.c \ src/ostree/ot-builtin-static-delta.c \ src/ostree/ot-main.h \ diff --git a/src/libostree/ostree-metalink.c b/src/libostree/ostree-metalink.c index d84a5775..2c0541ed 100644 --- a/src/libostree/ostree-metalink.c +++ b/src/libostree/ostree-metalink.c @@ -36,25 +36,29 @@ typedef enum { OSTREE_METALINK_STATE_RESOURCES, OSTREE_METALINK_STATE_URL, - OSTREE_METALINK_STATE_PASSTHROUGH, /* Ignoring unknown elements */ - OSTREE_METALINK_STATE_ERROR + OSTREE_METALINK_STATE_PASSTHROUGH /* Ignoring unknown elements */ } OstreeMetalinkState; struct OstreeMetalink { GObject parent_instance; - OstreeMetalink *fetcher; + SoupURI *uri; + + OstreeFetcher *fetcher; char *requested_file; guint64 max_size; }; G_DEFINE_TYPE (OstreeMetalink, _ostree_metalink, G_TYPE_OBJECT) -struct OstreeMetalinkRequest +typedef struct { OstreeMetalink *metalink; + GTask *task; + GMarkupParseContext *parser; + guint passthrough_depth; OstreeMetalinkState passthrough_previous; @@ -62,14 +66,20 @@ struct OstreeMetalinkRequest guint found_our_file_element : 1; guint verification_known : 1; + GChecksumType in_verification_type; + guint64 size; char *verification_sha256; char *verification_sha512; + GFile *result; + + char *last_metalink_error; + guint current_url_index; GPtrArray *urls; OstreeMetalinkState state; -} +} OstreeMetalinkRequest; static void state_transition (OstreeMetalinkRequest *self, @@ -116,7 +126,7 @@ metalink_parser_start (GMarkupParseContext *context, /* If we've already processed a <file> element we're OK with, just * ignore the others. */ - if (self->urls->length > 0) + if (self->urls->len > 0) { state_transition (self, OSTREE_METALINK_STATE_PASSTHROUGH); } @@ -124,7 +134,6 @@ metalink_parser_start (GMarkupParseContext *context, { const char *file_name; - g_clear_pointer (&self->file_name, g_free); if (!g_markup_collect_attributes (element_name, attribute_names, attribute_values, @@ -137,7 +146,7 @@ metalink_parser_start (GMarkupParseContext *context, self->found_a_file_element = TRUE; - if (strcmp (file_name, self->requested_file) != 0) + if (strcmp (file_name, self->metalink->requested_file) != 0) { state_transition (self, OSTREE_METALINK_STATE_PASSTHROUGH); g_assert (self->passthrough_depth == 0); @@ -183,9 +192,9 @@ metalink_parser_start (GMarkupParseContext *context, /* Only accept sha256/sha512 */ self->verification_known = TRUE; if (strcmp (verification_type_str, "sha256") == 0) - self->verification_type = G_CHECKSUM_SHA256; + self->in_verification_type = G_CHECKSUM_SHA256; else if (strcmp (verification_type_str, "sha512") == 0) - self->verification_type = G_CHECKSUM_SHA512; + self->in_verification_type = G_CHECKSUM_SHA512; else self->verification_known = FALSE; } @@ -231,7 +240,6 @@ metalink_parser_start (GMarkupParseContext *context, } else unknown_element (self, element_name, error); - } break; case OSTREE_METALINK_STATE_URL: unknown_element (self, element_name, error); @@ -251,7 +259,6 @@ metalink_parser_end (GMarkupParseContext *context, gpointer user_data, GError **error) { - OstreeMetalinkRequest *self = user_data; } static void @@ -280,9 +287,11 @@ metalink_parser_text (GMarkupParseContext *context, } break; case OSTREE_METALINK_STATE_VERIFICATION: + break; + case OSTREE_METALINK_STATE_HASH: if (self->verification_known) { - switch (self->verification_type) + switch (self->in_verification_type) { case G_CHECKSUM_SHA256: self->verification_sha256 = g_strndup (text, text_len); @@ -295,12 +304,6 @@ metalink_parser_text (GMarkupParseContext *context, } } break; - case OSTREE_METALINK_STATE_HASH: - { - g_clear_pointer (&self->verification_value, g_free); - self->verification_value = g_strndup (text, text_len); - } - break; case OSTREE_METALINK_STATE_RESOURCES: break; case OSTREE_METALINK_STATE_URL: @@ -314,8 +317,7 @@ metalink_parser_text (GMarkupParseContext *context, case OSTREE_METALINK_STATE_PASSTHROUGH: break; } - out: - return; + } static void @@ -325,6 +327,9 @@ _ostree_metalink_finalize (GObject *object) self = OSTREE_METALINK (object); + g_free (self->requested_file); + g_clear_object (&self->uri); + G_OBJECT_CLASS (_ostree_metalink_parent_class)->finalize (object); } @@ -351,10 +356,14 @@ _ostree_metalink_new (OstreeFetcher *fetcher, self->requested_file = g_strdup (requested_file); self->max_size = max_size; + self->uri = g_object_ref (uri); return self; } +static void +try_next_url (OstreeMetalinkRequest *self); + static gboolean valid_hex_checksum (const char *s, gsize expected_len) { @@ -363,6 +372,108 @@ valid_hex_checksum (const char *s, gsize expected_len) return len == expected_len && s[len] == '\0'; } +static void +on_fetched_url (GObject *src, + GAsyncResult *res, + gpointer user_data) +{ + GTask *task = user_data; + OstreeMetalinkRequest *self = g_task_get_task_data (task); + GError *local_error = NULL; + gs_unref_object GFile *result = NULL; + gs_unref_object GFileInfo *finfo = NULL; + + result = _ostree_fetcher_request_uri_with_partial_finish ((OstreeFetcher*)src, res, &local_error); + if (!result) + goto out; + + finfo = g_file_query_info (result, OSTREE_GIO_FAST_QUERYINFO, 0, + g_task_get_cancellable (task), &local_error); + if (!finfo) + goto out; + + if (g_file_info_get_size (finfo) != self->size) + { + g_set_error (&local_error, G_IO_ERROR, G_IO_ERROR_FAILED, + "Expected size is %" G_GUINT64_FORMAT " bytes but content is %" G_GUINT64_FORMAT " bytes", + self->size, g_file_info_get_size (finfo)); + goto out; + } + + if (self->verification_sha512) + { + gs_free char *actual = ot_checksum_file (result, G_CHECKSUM_SHA512, + g_task_get_cancellable (task), + &local_error); + + if (!actual) + goto out; + + if (strcmp (self->verification_sha512, actual) != 0) + { + g_set_error (&local_error, G_IO_ERROR, G_IO_ERROR_FAILED, + "Expected checksum is %s but actual is %s", + self->verification_sha512, actual); + goto out; + } + } + + if (self->verification_sha256) + { + gs_free char *actual = ot_checksum_file (result, G_CHECKSUM_SHA256, + g_task_get_cancellable (task), + &local_error); + + if (!actual) + goto out; + + if (strcmp (self->verification_sha256, actual) != 0) + { + g_set_error (&local_error, G_IO_ERROR, G_IO_ERROR_FAILED, + "Expected checksum is %s but actual is %s", + self->verification_sha256, actual); + goto out; + } + } + + out: + if (local_error) + { + g_free (self->last_metalink_error); + self->last_metalink_error = g_strdup (local_error->message); + g_clear_error (&local_error); + + /* And here we iterate on the next one if we hit an error */ + self->current_url_index++; + try_next_url (self); + } + else + { + self->result = g_object_ref (result); + g_task_return_boolean (self->task, TRUE); + } +} + +static void +try_next_url (OstreeMetalinkRequest *self) +{ + if (self->current_url_index >= self->urls->len) + { + g_task_return_new_error (self->task, G_IO_ERROR, G_IO_ERROR_FAILED, + "Exhausted %u metalink targets, last error: %s", + self->urls->len, self->last_metalink_error); + } + else + { + SoupURI *next = self->urls->pdata[self->current_url_index]; + + _ostree_fetcher_request_uri_with_partial_async (self->metalink->fetcher, next, + self->metalink->max_size, + g_task_get_cancellable (self->task), + on_fetched_url, self->task); + } +} + static gboolean start_target_request_phase (OstreeMetalinkRequest *self, GError **error) @@ -404,13 +515,14 @@ start_target_request_phase (OstreeMetalinkRequest *self, goto out; } - if (self->urls->length == 0) + if (self->urls->len == 0) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "No <url method='http'> elements found"); goto out; } + try_next_url (self); ret = TRUE; out: @@ -430,20 +542,22 @@ on_metalink_bytes_read (GObject *src, const guint8 *data; bytes = g_input_stream_read_bytes_finish ((GInputStream*)src, - result, error); + result, &local_error); if (!bytes) goto out; - data = g_bytes_get_data (bytes, + data = g_bytes_get_data (bytes, &len); - if (g_bytes_get_size (bytes) == 0) + if (len == 0) { if (!start_target_request_phase (self, &local_error)) goto out; } else { - g_markup_parse_context_parse (self->parser, g_ + if (!g_markup_parse_context_parse (self->parser, (const char*)data, len, &local_error)) + goto out; + g_input_stream_read_bytes_async ((GInputStream*)src, 8192, G_PRIORITY_DEFAULT, g_task_get_cancellable (task), on_metalink_bytes_read, task); @@ -463,7 +577,7 @@ on_retrieved_metalink (GObject *src, GTask *task = user_data; gs_unref_object GInputStream *metalink_stream = NULL; - metalink_stream = ostree_fetcher_stream_uri_finish ((OstreeFetcher*)src, result, &local_error); + metalink_stream = _ostree_fetcher_stream_uri_finish ((OstreeFetcher*)src, result, &local_error); if (!metalink_stream) goto out; @@ -481,10 +595,19 @@ ostree_metalink_request_unref (gpointer data) { OstreeMetalinkRequest *request = data; g_object_unref (request->metalink); + g_clear_object (&request->result); + g_free (request->last_metalink_error); g_ptr_array_unref (request->urls); g_free (request); } +static const GMarkupParser metalink_parser = { + metalink_parser_start, + metalink_parser_end, + metalink_parser_text, + NULL, + NULL +}; void _ostree_metalink_request_async (OstreeMetalink *self, @@ -494,8 +617,13 @@ _ostree_metalink_request_async (OstreeMetalink *self, { GTask *task = g_task_new (self, cancellable, callback, user_data); OstreeMetalinkRequest *request = g_new0 (OstreeMetalinkRequest, 1); + request->metalink = g_object_ref (self); request->urls = g_ptr_array_new_with_free_func (g_free); + request->task = task; /* Unowned */ + + request->parser = g_markup_parse_context_new (&metalink_parser, G_MARKUP_PREFIX_ERROR_POSITION, task, NULL); + g_task_set_task_data (task, request, ostree_metalink_request_unref); _ostree_fetcher_stream_uri_async (self->fetcher, self->uri, self->max_size, cancellable, @@ -503,22 +631,36 @@ _ostree_metalink_request_async (OstreeMetalink *self, } gboolean -ostree_metalink_request_finish (OstreeMetalink *self, - GAsyncResult *result, - SoupURI **out_target_uri, - GFile **out_data, - GError **error) +_ostree_metalink_request_finish (OstreeMetalink *self, + GAsyncResult *result, + SoupURI **out_target_uri, + GFile **out_data, + GError **error) { + OstreeMetalinkRequest *request; + + g_return_val_if_fail (g_task_is_valid (result, self), FALSE); + + request = g_task_get_task_data ((GTask*)result); + + if (g_task_propagate_boolean ((GTask*)result, error)) + { + *out_target_uri = request->urls->pdata[request->current_url_index]; + *out_data = g_object_ref (request->result); + return TRUE; + } + else + return FALSE; } -struct MetalinkSyncCallState +typedef struct { gboolean running; gboolean success; SoupURI **out_target_uri; GFile **out_data; GError **error; -} +} MetalinkSyncCallState; static void on_async_result (GObject *src, @@ -527,17 +669,17 @@ on_async_result (GObject *src, { MetalinkSyncCallState *state = user_data; - state->success = ostree_metalink_request_finish ((OstreeMetalink*)src, result, - state->out_target_uri, state->out_data, - state->error); + state->success = _ostree_metalink_request_finish ((OstreeMetalink*)src, result, + state->out_target_uri, state->out_data, + state->error); state->running = FALSE; } gboolean _ostree_metalink_request_sync (OstreeMetalink *self, - GCancellable *cancellable, SoupURI **out_target_uri, GFile **out_data, + GCancellable *cancellable, GError **error) { gboolean ret = FALSE; @@ -550,7 +692,6 @@ _ostree_metalink_request_sync (OstreeMetalink *self, g_main_context_iteration (sync_context, TRUE); ret = state.success; - out: if (sync_context) g_main_context_unref (sync_context); return ret; diff --git a/src/libostree/ostree-metalink.h b/src/libostree/ostree-metalink.h index 72bdfb5e..09065b6a 100644 --- a/src/libostree/ostree-metalink.h +++ b/src/libostree/ostree-metalink.h @@ -60,11 +60,11 @@ void _ostree_metalink_request_async (OstreeMetalink *self, GAsyncReadyCallback callback, gpointer user_data); -gboolean_ostree_metalink_request_finish (OstreeMetalink *self, - GAsyncResult *result, - SoupURI **out_target_uri, - GFile **out_data, - GError **error); +gboolean _ostree_metalink_request_finish (OstreeMetalink *self, + GAsyncResult *result, + SoupURI **out_target_uri, + GFile **out_data, + GError **error); G_END_DECLS diff --git a/src/libostree/ostree-repo-pull.c b/src/libostree/ostree-repo-pull.c index 7a0917b8..000a2258 100644 --- a/src/libostree/ostree-repo-pull.c +++ b/src/libostree/ostree-repo-pull.c @@ -26,7 +26,7 @@ #include "ostree-core-private.h" #include "ostree-repo-private.h" #include "ostree-repo-static-delta-private.h" -#include "ostree-fetcher.h" +#include "ostree-metalink.h" #include "otutil.h" typedef struct { diff --git a/src/libostree/ostree-repo.c b/src/libostree/ostree-repo.c index a16b0496..ec281a4a 100644 --- a/src/libostree/ostree-repo.c +++ b/src/libostree/ostree-repo.c @@ -379,7 +379,7 @@ GS_DEFINE_CLEANUP_FUNCTION0(GKeyFile*, local_keyfile_unref, g_key_file_unref) * ostree_repo_remote_add: * @self: Repo * @name: Name of remote - * @url: URL for remote + * @url: URL for remote (if URL begins with metalink=, it will be used as such) * @options: (allow-none): GVariant of type a{sv} * @cancellable: Cancellable * @error: Error @@ -444,7 +444,11 @@ ostree_repo_remote_add (OstreeRepo *self, target_keyfile = ostree_repo_copy_config (self); } - g_key_file_set_string (target_keyfile, section, "url", url); + if (g_str_has_prefix (url, "metalink=")) + g_key_file_set_string (target_keyfile, section, "metalink", url + strlen ("metalink=")); + else + g_key_file_set_string (target_keyfile, section, "url", url); + if (options) keyfile_set_from_vardict (target_keyfile, section, options); @@ -2286,7 +2290,6 @@ ostree_repo_regenerate_summary (OstreeRepo *self, GList *ordered_keys = NULL; GList *iter = NULL; GHashTableIter hashiter; - gpointer hkey, hvalue; if (!ostree_repo_list_refs (self, NULL, &refs, cancellable, error)) goto out; diff --git a/src/libotutil/ot-checksum-utils.c b/src/libotutil/ot-checksum-utils.c index 6a45c01b..bf9b89fb 100644 --- a/src/libotutil/ot-checksum-utils.c +++ b/src/libotutil/ot-checksum-utils.c @@ -139,6 +139,32 @@ ot_gio_checksum_stream (GInputStream *in, return ot_gio_splice_get_checksum (NULL, in, out_csum, cancellable, error); } +char * +ot_checksum_file (GFile *file, + GChecksumType checksum_type, + GCancellable *cancellable, + GError **error) +{ + GChecksum *checksum = NULL; + gs_free gchar *ret = NULL; + gs_unref_object GInputStream *in = NULL; + + in = (GInputStream*)g_file_read (file, cancellable, error); + if (!in) + goto out; + + checksum = g_checksum_new (checksum_type); + + if (!ot_gio_splice_update_checksum (NULL, in, checksum, cancellable, error)) + goto out; + + ret = g_strdup (g_checksum_get_string (checksum)); + out: + g_clear_pointer (&checksum, (GDestroyNotify) g_checksum_free); + return ret; + +} + static void checksum_stream_thread (GSimpleAsyncResult *result, GObject *object, diff --git a/src/libotutil/ot-checksum-utils.h b/src/libotutil/ot-checksum-utils.h index 7778ed09..eb8bbc04 100644 --- a/src/libotutil/ot-checksum-utils.h +++ b/src/libotutil/ot-checksum-utils.h @@ -53,6 +53,11 @@ gboolean ot_gio_checksum_stream (GInputStream *in, GCancellable *cancellable, GError **error); +char * ot_checksum_file (GFile *file, + GChecksumType checksum_type, + GCancellable *cancellable, + GError **error); + void ot_gio_checksum_stream_async (GInputStream *in, int io_priority, GCancellable *cancellable, diff --git a/src/ostree/main.c b/src/ostree/main.c index b16d8c12..e114690d 100644 --- a/src/ostree/main.c +++ b/src/ostree/main.c @@ -55,6 +55,7 @@ static OstreeCommand commands[] = { { "rev-parse", ostree_builtin_rev_parse, 0 }, { "show", ostree_builtin_show, 0 }, { "static-delta", ostree_builtin_static_delta, 0 }, + { "summary", ostree_builtin_summary, 0 }, #ifdef HAVE_LIBSOUP { "trivial-httpd", ostree_builtin_trivial_httpd, OSTREE_BUILTIN_FLAG_NO_REPO }, #endif diff --git a/src/ostree/ot-builtin-summary.c b/src/ostree/ot-builtin-summary.c new file mode 100644 index 00000000..17feb344 --- /dev/null +++ b/src/ostree/ot-builtin-summary.c @@ -0,0 +1,64 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * + * Copyright (C) 2014 Colin Walters <walters@verbum.org> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "config.h" + +#include "ot-builtins.h" +#include "ostree.h" +#include "otutil.h" + +static gboolean opt_update; + +static GOptionEntry options[] = { + { "update", 'u', 0, G_OPTION_ARG_NONE, &opt_update, "Update the summary", NULL }, + { NULL } +}; + +gboolean +ostree_builtin_summary (int argc, char **argv, OstreeRepo *repo, GCancellable *cancellable, GError **error) +{ + gboolean ret = FALSE; + GOptionContext *context; + gs_unref_ptrarray GPtrArray *delta_names = NULL; + + context = g_option_context_new ("Manage summary metadata"); + g_option_context_add_main_entries (context, options, NULL); + + if (!g_option_context_parse (context, &argc, &argv, error)) + goto out; + + if (opt_update) + { + if (!ostree_repo_regenerate_summary (repo, cancellable, error)) + goto out; + } + else + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "No option specified; use -u to update summary"); + goto out; + } + + ret = TRUE; + out: + if (context) + g_option_context_free (context); + return ret; +} diff --git a/src/ostree/ot-builtins.h b/src/ostree/ot-builtins.h index fee66f2b..b8b6507f 100644 --- a/src/ostree/ot-builtins.h +++ b/src/ostree/ot-builtins.h @@ -46,6 +46,7 @@ BUILTINPROTO(reset); BUILTINPROTO(fsck); BUILTINPROTO(show); BUILTINPROTO(static_delta); +BUILTINPROTO(summary); BUILTINPROTO(rev_parse); BUILTINPROTO(remote); BUILTINPROTO(write_refs); diff --git a/tests/test-pull-metalink.sh b/tests/test-pull-metalink.sh new file mode 100755 index 00000000..fcadd196 --- /dev/null +++ b/tests/test-pull-metalink.sh @@ -0,0 +1,68 @@ +#!/bin/bash +# +# Copyright (C) 2014 Colin Walters <walters@verbum.org> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. + +set -e + +. $(dirname $0)/libtest.sh + +setup_fake_remote_repo1 "archive-z2" + +# And another web server acting as the metalink server +cd ${test_tmpdir} +mkdir metalink-data +cd metalink-data +ostree trivial-httpd --daemonize -p ${test_tmpdir}/metalink-httpd-port +metalink_port=$(cat ${test_tmpdir}/metalink-httpd-port) +echo "http://127.0.0.1:${metalink_port}" > ${test_tmpdir}/metalink-httpd-address + +ostree --repo=${test_tmpdir}/ostree-srv/gnomerepo summary -u + +summary_path=${test_tmpdir}/ostree-srv/gnomerepo/summary + +echo '1..1' +cd ${test_tmpdir} + +cat <<EOF +<?xml version="1.0" encoding="utf-8"?> +<metalink version="3.0" xmlns="http://www.metalinker.org/"> + <files> + <file name="summary"> + <size>$(stat -c '%s' ${summary_path})</size> + <verification> + <hash type="md5">$(md5sum ${summary_path} | cut -f 1 -d ' ')</hash> + <hash type="sha256">$(sha256sum ${summary_path} | cut -f 1 -d ' ')</hash> + <hash type="sha512">$(sha512sum ${summary_path} | cut -f 1 -d ' ')</hash> + </verification> + <resources maxconnections="1"> + <url protocol="http" type="http" location="US" preference="100" >$(cat httpd-address)</url> + </resources> + </file> + </files> +</metalink> +EOF + +cd ${test_tmpdir} +mkdir repo +${CMD_PREFIX} ostree --repo=repo init +${CMD_PREFIX} ostree --repo=repo remote add --set=gpg-verify=false origin metalink=$(cat httpd-address)/ostree/gnomerepo +# Try both syntaxes +${CMD_PREFIX} ostree --repo=repo pull origin main +${CMD_PREFIX} ostree --repo=repo pull origin:main +${CMD_PREFIX} ostree --repo=repo fsck +echo "ok pull" |