New upstream version 2020.7
This commit is contained in:
commit
803ac2144f
73 files changed
+3205
-784
No files matched your search
@@ -17,13 +17,6 @@
|
||||
Boston, MA 02111-1307, USA.
|
||||
***/
|
||||
|
||||
LIBOSTREE_2020.5 {
|
||||
global:
|
||||
/* Add symbols here, and uncomment the bits in
|
||||
* Makefile-libostree.am to enable this too.
|
||||
*/
|
||||
} LIBOSTREE_2020.4;
|
||||
|
||||
/* Stub section for the stable release *after* this development one; don't
|
||||
* edit this other than to update the year. This is just a copy/paste
|
||||
* source. Replace $LASTSTABLE with the last stable version, and $NEWVERSION
|
||||
|
||||
@@ -593,6 +593,9 @@ global:
|
||||
ostree_sysroot_set_mount_namespace_in_use;
|
||||
} LIBOSTREE_2019.6;
|
||||
|
||||
/* No new symbols in 2020.2 */
|
||||
/* No new symbols in 2020.3 */
|
||||
|
||||
/* Add new symbols here. Release commits should copy this section into -released.sym. */
|
||||
LIBOSTREE_2020.4 {
|
||||
global:
|
||||
@@ -615,7 +618,23 @@ global:
|
||||
ostree_sign_summary;
|
||||
} LIBOSTREE_2020.1;
|
||||
|
||||
/* No new symbols in 2020.2 */
|
||||
/* No new symbols in 2020.5 */
|
||||
|
||||
/* No new symbols in 2020.6 */
|
||||
|
||||
LIBOSTREE_2020.7 {
|
||||
global:
|
||||
/* Add symbols here, and uncomment the bits in
|
||||
* Makefile-libostree.am to enable this too.
|
||||
*/
|
||||
ostree_repo_static_delta_execute_offline_with_signature;
|
||||
ostree_repo_static_delta_verify_signature;
|
||||
ostree_bootconfig_parser_get_overlay_initrds;
|
||||
ostree_bootconfig_parser_set_overlay_initrds;
|
||||
ostree_sysroot_deploy_tree_with_options;
|
||||
ostree_sysroot_stage_tree_with_options;
|
||||
ostree_sysroot_stage_overlay_initrd;
|
||||
} LIBOSTREE_2020.4;
|
||||
|
||||
/* NOTE: Only add more content here in release commits! See the
|
||||
* comments at the top of this file.
|
||||
|
||||
@@ -30,6 +30,9 @@ struct _OstreeBootconfigParser
|
||||
const char *separators;
|
||||
|
||||
GHashTable *options;
|
||||
|
||||
/* Additional initrds; the primary initrd is in options. */
|
||||
char **overlay_initrds;
|
||||
};
|
||||
|
||||
typedef GObjectClass OstreeBootconfigParserClass;
|
||||
@@ -50,6 +53,8 @@ ostree_bootconfig_parser_clone (OstreeBootconfigParser *self)
|
||||
GLNX_HASH_TABLE_FOREACH_KV (self->options, const char*, k, const char*, v)
|
||||
g_hash_table_replace (parser->options, g_strdup (k), g_strdup (v));
|
||||
|
||||
parser->overlay_initrds = g_strdupv (self->overlay_initrds);
|
||||
|
||||
return parser;
|
||||
}
|
||||
|
||||
@@ -76,6 +81,8 @@ ostree_bootconfig_parser_parse_at (OstreeBootconfigParser *self,
|
||||
if (!contents)
|
||||
return FALSE;
|
||||
|
||||
g_autoptr(GPtrArray) overlay_initrds = NULL;
|
||||
|
||||
g_auto(GStrv) lines = g_strsplit (contents, "\n", -1);
|
||||
for (char **iter = lines; *iter; iter++)
|
||||
{
|
||||
@@ -87,8 +94,19 @@ ostree_bootconfig_parser_parse_at (OstreeBootconfigParser *self,
|
||||
items = g_strsplit_set (line, self->separators, 2);
|
||||
if (g_strv_length (items) == 2 && items[0][0] != '\0')
|
||||
{
|
||||
g_hash_table_insert (self->options, items[0], items[1]);
|
||||
g_free (items); /* Transfer ownership */
|
||||
if (g_str_equal (items[0], "initrd") &&
|
||||
g_hash_table_contains (self->options, "initrd"))
|
||||
{
|
||||
if (!overlay_initrds)
|
||||
overlay_initrds = g_ptr_array_new_with_free_func (g_free);
|
||||
g_ptr_array_add (overlay_initrds, items[1]);
|
||||
g_free (items[0]);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_hash_table_insert (self->options, items[0], items[1]);
|
||||
}
|
||||
g_free (items); /* Free container; we stole the elements */
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -97,6 +115,12 @@ ostree_bootconfig_parser_parse_at (OstreeBootconfigParser *self,
|
||||
}
|
||||
}
|
||||
|
||||
if (overlay_initrds)
|
||||
{
|
||||
g_ptr_array_add (overlay_initrds, NULL);
|
||||
self->overlay_initrds = (char**)g_ptr_array_free (g_steal_pointer (&overlay_initrds), FALSE);
|
||||
}
|
||||
|
||||
self->parsed = TRUE;
|
||||
|
||||
return TRUE;
|
||||
@@ -127,6 +151,41 @@ ostree_bootconfig_parser_get (OstreeBootconfigParser *self,
|
||||
return g_hash_table_lookup (self->options, key);
|
||||
}
|
||||
|
||||
/**
|
||||
* ostree_bootconfig_parser_set_overlay_initrds:
|
||||
* @self: Parser
|
||||
* @initrds: (array zero-terminated=1) (transfer none) (allow-none): Array of overlay
|
||||
* initrds or %NULL to unset.
|
||||
*
|
||||
* These are rendered as additional `initrd` keys in the final bootloader configs. The
|
||||
* base initrd is part of the primary keys.
|
||||
*
|
||||
* Since: 2020.7
|
||||
*/
|
||||
void
|
||||
ostree_bootconfig_parser_set_overlay_initrds (OstreeBootconfigParser *self,
|
||||
char **initrds)
|
||||
{
|
||||
g_assert (g_hash_table_contains (self->options, "initrd"));
|
||||
g_strfreev (self->overlay_initrds);
|
||||
self->overlay_initrds = g_strdupv (initrds);
|
||||
}
|
||||
|
||||
/**
|
||||
* ostree_bootconfig_parser_get_overlay_initrds:
|
||||
* @self: Parser
|
||||
*
|
||||
* Returns: (array zero-terminated=1) (transfer none) (nullable): Array of initrds or %NULL
|
||||
* if none are set.
|
||||
*
|
||||
* Since: 2020.7
|
||||
*/
|
||||
char**
|
||||
ostree_bootconfig_parser_get_overlay_initrds (OstreeBootconfigParser *self)
|
||||
{
|
||||
return self->overlay_initrds;
|
||||
}
|
||||
|
||||
static void
|
||||
write_key (OstreeBootconfigParser *self,
|
||||
GString *buf,
|
||||
@@ -165,6 +224,15 @@ ostree_bootconfig_parser_write_at (OstreeBootconfigParser *self,
|
||||
}
|
||||
}
|
||||
|
||||
/* Write overlay initrds */
|
||||
if (self->overlay_initrds && (g_strv_length (self->overlay_initrds) > 0))
|
||||
{
|
||||
/* we should've written the primary initrd already */
|
||||
g_assert (g_hash_table_contains (keys_written, "initrd"));
|
||||
for (char **it = self->overlay_initrds; it && *it; it++)
|
||||
write_key (self, buf, "initrd", *it);
|
||||
}
|
||||
|
||||
/* Write unknown fields */
|
||||
GLNX_HASH_TABLE_FOREACH_KV (self->options, const char*, k, const char*, v)
|
||||
{
|
||||
@@ -197,6 +265,7 @@ ostree_bootconfig_parser_finalize (GObject *object)
|
||||
{
|
||||
OstreeBootconfigParser *self = OSTREE_BOOTCONFIG_PARSER (object);
|
||||
|
||||
g_strfreev (self->overlay_initrds);
|
||||
g_hash_table_unref (self->options);
|
||||
|
||||
G_OBJECT_CLASS (ostree_bootconfig_parser_parent_class)->finalize (object);
|
||||
|
||||
@@ -73,5 +73,11 @@ _OSTREE_PUBLIC
|
||||
const char *ostree_bootconfig_parser_get (OstreeBootconfigParser *self,
|
||||
const char *key);
|
||||
|
||||
_OSTREE_PUBLIC
|
||||
void ostree_bootconfig_parser_set_overlay_initrds (OstreeBootconfigParser *self,
|
||||
char **initrds);
|
||||
|
||||
_OSTREE_PUBLIC
|
||||
char** ostree_bootconfig_parser_get_overlay_initrds (OstreeBootconfigParser *self);
|
||||
|
||||
G_END_DECLS
|
||||
@@ -2675,7 +2675,7 @@ _ostree_detached_metadata_append_gpg_sig (GVariant *existing_metadata,
|
||||
_OSTREE_METADATA_GPGSIGS_NAME,
|
||||
g_variant_builder_end (signature_builder));
|
||||
|
||||
return g_variant_dict_end (&metadata_dict);
|
||||
return g_variant_ref_sink (g_variant_dict_end (&metadata_dict));
|
||||
}
|
||||
#endif /* OSTREE_DISABLE_GPGME */
|
||||
|
||||
|
||||
@@ -37,6 +37,8 @@ G_BEGIN_DECLS
|
||||
* @origin: How to construct an upgraded version of this tree
|
||||
* @unlocked: The unlocked state
|
||||
* @staged: TRUE iff this deployment is staged
|
||||
* @overlay_initrds: Checksums of staged additional initrds for this deployment
|
||||
* @overlay_initrds_id: Unique ID generated from initrd checksums; used to compare deployments
|
||||
*/
|
||||
struct _OstreeDeployment
|
||||
{
|
||||
@@ -52,8 +54,15 @@ struct _OstreeDeployment
|
||||
GKeyFile *origin;
|
||||
OstreeDeploymentUnlockedState unlocked;
|
||||
gboolean staged;
|
||||
char **overlay_initrds;
|
||||
char *overlay_initrds_id;
|
||||
};
|
||||
|
||||
void _ostree_deployment_set_bootcsum (OstreeDeployment *self, const char *bootcsum);
|
||||
|
||||
void _ostree_deployment_set_overlay_initrds (OstreeDeployment *self,
|
||||
char **overlay_initrds);
|
||||
|
||||
char** _ostree_deployment_get_overlay_initrds (OstreeDeployment *self);
|
||||
|
||||
G_END_DECLS
|
||||
@@ -158,6 +158,34 @@ _ostree_deployment_set_bootcsum (OstreeDeployment *self,
|
||||
self->bootcsum = g_strdup (bootcsum);
|
||||
}
|
||||
|
||||
void
|
||||
_ostree_deployment_set_overlay_initrds (OstreeDeployment *self,
|
||||
char **overlay_initrds)
|
||||
{
|
||||
g_clear_pointer (&self->overlay_initrds, g_strfreev);
|
||||
g_clear_pointer (&self->overlay_initrds_id, g_free);
|
||||
|
||||
if (!overlay_initrds || g_strv_length (overlay_initrds) == 0)
|
||||
return;
|
||||
|
||||
/* Generate a unique ID representing this combination of overlay initrds. This is so that
|
||||
* ostree_sysroot_write_deployments_with_options() can easily compare initrds when
|
||||
* comparing deployments for whether a bootswap is necessary. We could be fancier here but
|
||||
* meh... this works. */
|
||||
g_autoptr(GString) id = g_string_new (NULL);
|
||||
for (char **it = overlay_initrds; it && *it; it++)
|
||||
g_string_append (id, *it);
|
||||
|
||||
self->overlay_initrds = g_strdupv (overlay_initrds);
|
||||
self->overlay_initrds_id = g_string_free (g_steal_pointer (&id), FALSE);
|
||||
}
|
||||
|
||||
char**
|
||||
_ostree_deployment_get_overlay_initrds (OstreeDeployment *self)
|
||||
{
|
||||
return self->overlay_initrds;
|
||||
}
|
||||
|
||||
/**
|
||||
* ostree_deployment_clone:
|
||||
* @self: Deployment
|
||||
@@ -175,6 +203,8 @@ ostree_deployment_clone (OstreeDeployment *self)
|
||||
new_bootconfig = ostree_bootconfig_parser_clone (self->bootconfig);
|
||||
ostree_deployment_set_bootconfig (ret, new_bootconfig);
|
||||
|
||||
_ostree_deployment_set_overlay_initrds (ret, self->overlay_initrds);
|
||||
|
||||
if (self->origin)
|
||||
{
|
||||
g_autoptr(GKeyFile) new_origin = NULL;
|
||||
@@ -238,6 +268,8 @@ ostree_deployment_finalize (GObject *object)
|
||||
g_free (self->bootcsum);
|
||||
g_clear_object (&self->bootconfig);
|
||||
g_clear_pointer (&self->origin, g_key_file_unref);
|
||||
g_strfreev (self->overlay_initrds);
|
||||
g_free (self->overlay_initrds_id);
|
||||
|
||||
G_OBJECT_CLASS (ostree_deployment_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
@@ -341,14 +341,14 @@ check_multi_info (OstreeFetcher *fetcher)
|
||||
|
||||
if (req->idx + 1 == req->mirrorlist->len)
|
||||
{
|
||||
g_autofree char *msg = g_strdup_printf ("Server returned HTTP %lu", response);
|
||||
g_autofree char *response_msg = g_strdup_printf ("Server returned HTTP %lu", response);
|
||||
g_task_return_new_error (task, G_IO_ERROR, giocode,
|
||||
"%s", msg);
|
||||
"%s", response_msg);
|
||||
if (req->fetcher->remote_name &&
|
||||
!((req->flags & OSTREE_FETCHER_REQUEST_OPTIONAL_CONTENT) > 0 &&
|
||||
giocode == G_IO_ERROR_NOT_FOUND))
|
||||
_ostree_fetcher_journal_failure (req->fetcher->remote_name,
|
||||
eff_url, msg);
|
||||
eff_url, response_msg);
|
||||
|
||||
}
|
||||
else
|
||||
|
||||
@@ -613,9 +613,12 @@ checkout_one_file_at (OstreeRepo *repo,
|
||||
}
|
||||
|
||||
const gboolean is_symlink = (g_file_info_get_file_type (source_info) == G_FILE_TYPE_SYMBOLIC_LINK);
|
||||
const guint32 source_mode = g_file_info_get_attribute_uint32 (source_info, "unix::mode");
|
||||
const gboolean is_unreadable = (!is_symlink && (source_mode & S_IRUSR) == 0);
|
||||
const gboolean is_whiteout = (!is_symlink && options->process_whiteouts &&
|
||||
g_str_has_prefix (destination_name, WHITEOUT_PREFIX));
|
||||
const gboolean is_reg_zerosized = (!is_symlink && g_file_info_get_size (source_info) == 0);
|
||||
const gboolean override_user_unreadable = (options->mode == OSTREE_REPO_CHECKOUT_MODE_USER && is_unreadable);
|
||||
|
||||
/* First, see if it's a Docker whiteout,
|
||||
* https://github.com/docker/docker/blob/1a714e76a2cb9008cd19609059e9988ff1660b78/pkg/archive/whiteouts.go
|
||||
@@ -634,8 +637,12 @@ checkout_one_file_at (OstreeRepo *repo,
|
||||
|
||||
need_copy = FALSE;
|
||||
}
|
||||
else if (options->force_copy_zerosized && is_reg_zerosized)
|
||||
else if (is_reg_zerosized || override_user_unreadable)
|
||||
{
|
||||
/* In https://github.com/ostreedev/ostree/commit/673cacd633f9d6b653cdea530657d3e780a41bbd we
|
||||
* made this an option, but in order to avoid hitting EMLINK, we now force copy zerosized
|
||||
* files unconditionally.
|
||||
*/
|
||||
need_copy = TRUE;
|
||||
}
|
||||
else if (!options->force_copy)
|
||||
@@ -735,7 +742,7 @@ checkout_one_file_at (OstreeRepo *repo,
|
||||
if (can_cache
|
||||
&& !is_whiteout
|
||||
&& !is_symlink
|
||||
&& !is_reg_zerosized
|
||||
&& !(is_reg_zerosized || override_user_unreadable)
|
||||
&& need_copy
|
||||
&& repo->mode == OSTREE_REPO_MODE_ARCHIVE
|
||||
&& options->mode == OSTREE_REPO_CHECKOUT_MODE_USER)
|
||||
@@ -799,12 +806,21 @@ checkout_one_file_at (OstreeRepo *repo,
|
||||
* succeeded at hardlinking above.
|
||||
*/
|
||||
if (options->no_copy_fallback)
|
||||
g_assert (is_bare_user_symlink || is_reg_zerosized);
|
||||
g_assert (is_bare_user_symlink || is_reg_zerosized || override_user_unreadable);
|
||||
if (!ostree_repo_load_file (repo, checksum, &input, NULL, &xattrs,
|
||||
cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
if (!create_file_copy_from_input_at (repo, options, state, checksum, source_info, xattrs, input,
|
||||
GFileInfo *copy_source_info = source_info;
|
||||
g_autoptr(GFileInfo) modified_info = NULL;
|
||||
if (override_user_unreadable)
|
||||
{
|
||||
modified_info = g_file_info_dup (source_info);
|
||||
g_file_info_set_attribute_uint32 (modified_info, "unix::mode", (source_mode | S_IRUSR));
|
||||
copy_source_info = modified_info;
|
||||
}
|
||||
|
||||
if (!create_file_copy_from_input_at (repo, options, state, checksum, copy_source_info, xattrs, input,
|
||||
destination_dfd, destination_name,
|
||||
cancellable, error))
|
||||
return glnx_prefix_error (error, "Copy checkout of %s to %s", checksum, destination_name);
|
||||
|
||||
@@ -3943,11 +3943,9 @@ write_dfd_iter_to_mtree_internal (OstreeRepo *self,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
g_autoptr(GFileInfo) child_info = NULL;
|
||||
g_autoptr(GFileInfo) modified_info = NULL;
|
||||
g_autoptr(GVariant) xattrs = NULL;
|
||||
g_autofree guchar *child_file_csum = NULL;
|
||||
g_autofree char *tmp_checksum = NULL;
|
||||
g_autofree char *relpath = NULL;
|
||||
OstreeRepoCommitFilterResult filter_result;
|
||||
struct stat dir_stbuf;
|
||||
@@ -3955,19 +3953,19 @@ write_dfd_iter_to_mtree_internal (OstreeRepo *self,
|
||||
if (!glnx_fstat (src_dfd_iter->fd, &dir_stbuf, error))
|
||||
return FALSE;
|
||||
|
||||
child_info = _ostree_stbuf_to_gfileinfo (&dir_stbuf);
|
||||
|
||||
if (modifier != NULL)
|
||||
{
|
||||
relpath = ptrarray_path_join (path);
|
||||
|
||||
filter_result = _ostree_repo_commit_modifier_apply (self, modifier, relpath, child_info, &modified_info);
|
||||
}
|
||||
else
|
||||
{
|
||||
filter_result = OSTREE_REPO_COMMIT_FILTER_ALLOW;
|
||||
modified_info = g_object_ref (child_info);
|
||||
}
|
||||
{
|
||||
g_autoptr(GFileInfo) child_info = _ostree_stbuf_to_gfileinfo (&dir_stbuf);
|
||||
if (modifier != NULL)
|
||||
{
|
||||
relpath = ptrarray_path_join (path);
|
||||
filter_result = _ostree_repo_commit_modifier_apply (self, modifier, relpath, child_info, &modified_info);
|
||||
}
|
||||
else
|
||||
{
|
||||
filter_result = OSTREE_REPO_COMMIT_FILTER_ALLOW;
|
||||
modified_info = g_object_ref (child_info);
|
||||
}
|
||||
}
|
||||
|
||||
if (filter_result == OSTREE_REPO_COMMIT_FILTER_ALLOW)
|
||||
{
|
||||
@@ -3979,8 +3977,7 @@ write_dfd_iter_to_mtree_internal (OstreeRepo *self,
|
||||
cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
g_free (tmp_checksum);
|
||||
tmp_checksum = ostree_checksum_from_bytes (child_file_csum);
|
||||
g_autofree char *tmp_checksum = ostree_checksum_from_bytes (child_file_csum);
|
||||
ostree_mutable_tree_set_metadata_checksum (mtree, tmp_checksum);
|
||||
}
|
||||
|
||||
|
||||
@@ -91,14 +91,14 @@ parse_txt_record (const guint8 *txt,
|
||||
|
||||
/* TODO: Docs. Return value is only valid as long as @txt is. Reference: RFC 6763, §6. */
|
||||
GHashTable *
|
||||
_ostree_txt_records_parse (AvahiStringList *txt)
|
||||
_ostree_txt_records_parse (AvahiStringList *txt_list)
|
||||
{
|
||||
AvahiStringList *l;
|
||||
g_autoptr(GHashTable) out = NULL;
|
||||
|
||||
out = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_bytes_unref);
|
||||
|
||||
for (l = txt; l != NULL; l = avahi_string_list_get_next (l))
|
||||
for (l = txt_list; l != NULL; l = avahi_string_list_get_next (l))
|
||||
{
|
||||
const guint8 *txt;
|
||||
gsize txt_len;
|
||||
|
||||
@@ -336,7 +336,6 @@ ostree_repo_finder_mount_resolve_async (OstreeRepoFinder *finde
|
||||
g_autoptr(GHashTable) repo_to_refs = NULL; /* (element-type UriAndKeyring GHashTable) */
|
||||
GHashTable *supported_ref_to_checksum; /* (element-type OstreeCollectionRef utf8) */
|
||||
GHashTableIter iter;
|
||||
UriAndKeyring *repo;
|
||||
g_autoptr(GError) local_error = NULL;
|
||||
|
||||
mount_name = g_mount_get_name (mount);
|
||||
@@ -525,6 +524,7 @@ G_GNUC_END_IGNORE_DEPRECATIONS
|
||||
/* Aggregate the results. */
|
||||
g_hash_table_iter_init (&iter, repo_to_refs);
|
||||
|
||||
UriAndKeyring *repo;
|
||||
while (g_hash_table_iter_next (&iter, (gpointer *) &repo, (gpointer *) &supported_ref_to_checksum))
|
||||
{
|
||||
g_autoptr(OstreeRemote) remote = NULL;
|
||||
|
||||
@@ -151,7 +151,6 @@ ostree_repo_finder_override_resolve_async (OstreeRepoFinder *fi
|
||||
GHashTable *supported_ref_to_checksum; /* (element-type OstreeCollectionRef utf8) */
|
||||
GHashTableIter iter;
|
||||
const gchar *remote_uri;
|
||||
OstreeRemote *remote;
|
||||
|
||||
task = g_task_new (finder, cancellable, callback, user_data);
|
||||
g_task_set_source_tag (task, ostree_repo_finder_override_resolve_async);
|
||||
@@ -242,6 +241,7 @@ ostree_repo_finder_override_resolve_async (OstreeRepoFinder *fi
|
||||
/* Aggregate the results. */
|
||||
g_hash_table_iter_init (&iter, repo_remote_to_refs);
|
||||
|
||||
OstreeRemote *remote;
|
||||
while (g_hash_table_iter_next (&iter, (gpointer *) &remote, (gpointer *) &supported_ref_to_checksum))
|
||||
g_ptr_array_add (results, ostree_repo_finder_result_new (remote, finder, priority, supported_ref_to_checksum, NULL, 0));
|
||||
|
||||
|
||||
@@ -1161,16 +1161,16 @@ write_directory_to_libarchive_recurse (OstreeRepo *self,
|
||||
{
|
||||
guint8 buf[8192];
|
||||
g_autoptr(GInputStream) file_in = NULL;
|
||||
g_autoptr(GFileInfo) file_info = NULL;
|
||||
g_autoptr(GFileInfo) regular_file_info = NULL;
|
||||
const char *checksum;
|
||||
|
||||
checksum = ostree_repo_file_get_checksum ((OstreeRepoFile*)path);
|
||||
|
||||
if (!ostree_repo_load_file (self, checksum, &file_in, &file_info, NULL,
|
||||
if (!ostree_repo_load_file (self, checksum, &file_in, ®ular_file_info, NULL,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
|
||||
archive_entry_set_size (entry, g_file_info_get_size (file_info));
|
||||
archive_entry_set_size (entry, g_file_info_get_size (regular_file_info));
|
||||
|
||||
if (archive_write_header (a, entry) != ARCHIVE_OK)
|
||||
{
|
||||
|
||||
@@ -55,6 +55,8 @@ G_BEGIN_DECLS
|
||||
#define OSTREE_SUMMARY_EXPIRES "ostree.summary.expires"
|
||||
#define OSTREE_SUMMARY_COLLECTION_ID "ostree.summary.collection-id"
|
||||
#define OSTREE_SUMMARY_COLLECTION_MAP "ostree.summary.collection-map"
|
||||
#define OSTREE_SUMMARY_MODE "ostree.summary.mode"
|
||||
#define OSTREE_SUMMARY_TOMBSTONE_COMMITS "ostree.summary.tombstone-commits"
|
||||
|
||||
#define _OSTREE_PAYLOAD_LINK_PREFIX "../"
|
||||
#define _OSTREE_PAYLOAD_LINK_PREFIX_LEN (sizeof (_OSTREE_PAYLOAD_LINK_PREFIX) - 1)
|
||||
|
||||
+357
-335
@@ -1625,13 +1625,13 @@ scan_commit_object (OtPullData *pull_data,
|
||||
}
|
||||
if (pull_data->timestamp_check_from_rev)
|
||||
{
|
||||
g_autoptr(GVariant) commit = NULL;
|
||||
g_autoptr(GVariant) timestamp_commit = NULL;
|
||||
if (!ostree_repo_load_commit (pull_data->repo, pull_data->timestamp_check_from_rev,
|
||||
&commit, NULL, error))
|
||||
×tamp_commit, NULL, error))
|
||||
return glnx_prefix_error (error, "Reading %s for timestamp-check-from-rev",
|
||||
pull_data->timestamp_check_from_rev);
|
||||
|
||||
guint64 ts = ostree_commit_get_timestamp (commit);
|
||||
guint64 ts = ostree_commit_get_timestamp (timestamp_commit);
|
||||
if (!_ostree_compare_timestamps (pull_data->timestamp_check_from_rev, ts, checksum, new_ts, error))
|
||||
return FALSE;
|
||||
}
|
||||
@@ -2001,6 +2001,8 @@ start_fetch (OtPullData *pull_data,
|
||||
is_meta ? meta_fetch_on_complete : content_fetch_on_complete, fetch);
|
||||
}
|
||||
|
||||
/* Deprecated: code should load options from the `summary` file rather than
|
||||
* downloading the remote’s `config` file, to save on network round trips. */
|
||||
static gboolean
|
||||
load_remote_repo_config (OtPullData *pull_data,
|
||||
GKeyFile **out_keyfile,
|
||||
@@ -2513,10 +2515,7 @@ on_superblock_fetched (GObject *src,
|
||||
const guchar *expected_summary_digest = g_hash_table_lookup (pull_data->summary_deltas_checksums, delta);
|
||||
guint8 actual_summary_digest[OSTREE_SHA256_DIGEST_LEN];
|
||||
|
||||
g_auto(OtChecksum) hasher = { 0, };
|
||||
ot_checksum_init (&hasher);
|
||||
ot_checksum_update_bytes (&hasher, delta_superblock_data);
|
||||
ot_checksum_get_digest (&hasher, actual_summary_digest, sizeof (actual_summary_digest));
|
||||
ot_checksum_bytes (delta_superblock_data, actual_summary_digest);
|
||||
|
||||
#ifndef OSTREE_DISABLE_GPGME
|
||||
/* At this point we've GPG verified the data, so in theory
|
||||
@@ -2626,54 +2625,180 @@ validate_variant_is_csum (GVariant *csum,
|
||||
return ostree_validate_structureof_csum_v (csum, error);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
_ostree_repo_verify_summary (OstreeRepo *self,
|
||||
const char *name,
|
||||
gboolean gpg_verify_summary,
|
||||
GPtrArray *signapi_summary_verifiers,
|
||||
GBytes *summary,
|
||||
GBytes *signatures,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
if (gpg_verify_summary)
|
||||
{
|
||||
if (summary == NULL)
|
||||
{
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
|
||||
"GPG verification enabled, but no summary found (check that the configured URL in remote config is correct)");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (signatures == NULL)
|
||||
{
|
||||
g_set_error (error, OSTREE_GPG_ERROR, OSTREE_GPG_ERROR_NO_SIGNATURE,
|
||||
"GPG verification enabled, but no summary signatures found (use gpg-verify-summary=false in remote config to disable)");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Verify any summary signatures. */
|
||||
if (summary != NULL && signatures != NULL)
|
||||
{
|
||||
g_autoptr(OstreeGpgVerifyResult) result = NULL;
|
||||
|
||||
result = ostree_repo_verify_summary (self,
|
||||
name,
|
||||
summary,
|
||||
signatures,
|
||||
cancellable,
|
||||
error);
|
||||
if (!ostree_gpg_verify_result_require_valid_signature (result, error))
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
if (signapi_summary_verifiers)
|
||||
{
|
||||
if (summary == NULL)
|
||||
{
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
|
||||
"Signature verification enabled, but no summary found (check that the configured URL in remote config is correct)");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (signatures == NULL)
|
||||
{
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
|
||||
"Signature verification enabled, but no summary signatures found (use sign-verify-summary=false in remote config to disable)");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Verify any summary signatures. */
|
||||
if (summary != NULL && signatures != NULL)
|
||||
{
|
||||
g_autoptr(GVariant) sig_variant = NULL;
|
||||
|
||||
sig_variant = g_variant_new_from_bytes (OSTREE_SUMMARY_SIG_GVARIANT_FORMAT,
|
||||
signatures, FALSE);
|
||||
|
||||
if (!_sign_verify_for_remote (signapi_summary_verifiers, summary, sig_variant, NULL, error))
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
_ostree_repo_load_cache_summary_file (OstreeRepo *self,
|
||||
const char *filename,
|
||||
const char *extension,
|
||||
GBytes **out_data,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
const char *file = glnx_strjoina (_OSTREE_SUMMARY_CACHE_DIR, "/", filename, extension);
|
||||
glnx_autofd int fd = -1;
|
||||
g_autoptr(GBytes) data = NULL;
|
||||
|
||||
*out_data = NULL;
|
||||
|
||||
if (self->cache_dir_fd == -1)
|
||||
return TRUE;
|
||||
|
||||
fd = openat (self->cache_dir_fd, file, O_CLOEXEC | O_RDONLY);
|
||||
if (fd < 0)
|
||||
{
|
||||
if (errno == ENOENT)
|
||||
return TRUE;
|
||||
return glnx_throw_errno_prefix (error, "openat(%s)", file);
|
||||
}
|
||||
|
||||
data = ot_fd_readall_or_mmap (fd, 0, error);
|
||||
if (!data)
|
||||
return FALSE;
|
||||
|
||||
*out_data =g_steal_pointer (&data);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* Load the summary from the cache if the provided .sig file is the same as the
|
||||
cached version. */
|
||||
static gboolean
|
||||
_ostree_repo_load_cache_summary_if_same_sig (OstreeRepo *self,
|
||||
const char *remote,
|
||||
GBytes *summary_sig,
|
||||
GBytes **summary,
|
||||
GBytes **out_summary,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
g_autoptr(GBytes) old_sig_contents = NULL;
|
||||
|
||||
*out_summary = NULL;
|
||||
|
||||
if (!_ostree_repo_load_cache_summary_file (self, remote, ".sig",
|
||||
&old_sig_contents,
|
||||
cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
if (old_sig_contents != NULL &&
|
||||
g_bytes_compare (old_sig_contents, summary_sig) == 0)
|
||||
{
|
||||
g_autoptr(GBytes) summary_data = NULL;
|
||||
|
||||
if (!_ostree_repo_load_cache_summary_file (self, remote, NULL,
|
||||
&summary_data,
|
||||
cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
if (summary_data == NULL)
|
||||
{
|
||||
/* Cached signature without cached summary, remove the signature */
|
||||
const char *summary_cache_sig_file = glnx_strjoina (_OSTREE_SUMMARY_CACHE_DIR, "/", remote, ".sig");
|
||||
(void) unlinkat (self->cache_dir_fd, summary_cache_sig_file, 0);
|
||||
}
|
||||
else
|
||||
*out_summary = g_steal_pointer (&summary_data);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
_ostree_repo_save_cache_summary_file (OstreeRepo *self,
|
||||
const char *filename,
|
||||
const char *extension,
|
||||
GBytes *data,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
const char *file = glnx_strjoina (_OSTREE_SUMMARY_CACHE_DIR, "/", filename, extension);
|
||||
glnx_autofd int fd = -1;
|
||||
|
||||
if (self->cache_dir_fd == -1)
|
||||
return TRUE;
|
||||
|
||||
const char *summary_cache_sig_file = glnx_strjoina (_OSTREE_SUMMARY_CACHE_DIR, "/", remote, ".sig");
|
||||
glnx_autofd int prev_fd = -1;
|
||||
if (!ot_openat_ignore_enoent (self->cache_dir_fd, summary_cache_sig_file, &prev_fd, error))
|
||||
return FALSE;
|
||||
if (prev_fd < 0)
|
||||
return TRUE; /* Note early return */
|
||||
|
||||
g_autoptr(GBytes) old_sig_contents = ot_fd_readall_or_mmap (prev_fd, 0, error);
|
||||
if (!old_sig_contents)
|
||||
if (!glnx_shutil_mkdir_p_at (self->cache_dir_fd, _OSTREE_SUMMARY_CACHE_DIR, DEFAULT_DIRECTORY_MODE, cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
if (g_bytes_compare (old_sig_contents, summary_sig) == 0)
|
||||
{
|
||||
const char *summary_cache_file = glnx_strjoina (_OSTREE_SUMMARY_CACHE_DIR, "/", remote);
|
||||
glnx_autofd int summary_fd = -1;
|
||||
GBytes *summary_data;
|
||||
if (!glnx_file_replace_contents_at (self->cache_dir_fd,
|
||||
file,
|
||||
g_bytes_get_data (data, NULL),
|
||||
g_bytes_get_size (data),
|
||||
self->disable_fsync ? GLNX_FILE_REPLACE_NODATASYNC : GLNX_FILE_REPLACE_DATASYNC_NEW,
|
||||
cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
|
||||
summary_fd = openat (self->cache_dir_fd, summary_cache_file, O_CLOEXEC | O_RDONLY);
|
||||
if (summary_fd < 0)
|
||||
{
|
||||
if (errno == ENOENT)
|
||||
{
|
||||
(void) unlinkat (self->cache_dir_fd, summary_cache_sig_file, 0);
|
||||
return TRUE; /* Note early return */
|
||||
}
|
||||
|
||||
return glnx_throw_errno_prefix (error, "openat(%s)", summary_cache_file);
|
||||
}
|
||||
|
||||
summary_data = glnx_fd_readall_bytes (summary_fd, cancellable, error);
|
||||
if (!summary_data)
|
||||
return FALSE;
|
||||
*summary = summary_data;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@@ -2686,28 +2811,12 @@ _ostree_repo_cache_summary (OstreeRepo *self,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
if (self->cache_dir_fd == -1)
|
||||
return TRUE;
|
||||
|
||||
if (!glnx_shutil_mkdir_p_at (self->cache_dir_fd, _OSTREE_SUMMARY_CACHE_DIR, DEFAULT_DIRECTORY_MODE, cancellable, error))
|
||||
if (!_ostree_repo_save_cache_summary_file (self, remote, NULL,
|
||||
summary, cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
const char *summary_cache_file = glnx_strjoina (_OSTREE_SUMMARY_CACHE_DIR, "/", remote);
|
||||
if (!glnx_file_replace_contents_at (self->cache_dir_fd,
|
||||
summary_cache_file,
|
||||
g_bytes_get_data (summary, NULL),
|
||||
g_bytes_get_size (summary),
|
||||
self->disable_fsync ? GLNX_FILE_REPLACE_NODATASYNC : GLNX_FILE_REPLACE_DATASYNC_NEW,
|
||||
cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
const char *summary_cache_sig_file = glnx_strjoina (_OSTREE_SUMMARY_CACHE_DIR, "/", remote, ".sig");
|
||||
if (!glnx_file_replace_contents_at (self->cache_dir_fd,
|
||||
summary_cache_sig_file,
|
||||
g_bytes_get_data (summary_sig, NULL),
|
||||
g_bytes_get_size (summary_sig),
|
||||
self->disable_fsync ? GLNX_FILE_REPLACE_NODATASYNC : GLNX_FILE_REPLACE_DATASYNC_NEW,
|
||||
cancellable, error))
|
||||
if (!_ostree_repo_save_cache_summary_file (self, remote, ".sig",
|
||||
summary_sig, cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
@@ -2717,6 +2826,8 @@ static OstreeFetcher *
|
||||
_ostree_repo_remote_new_fetcher (OstreeRepo *self,
|
||||
const char *remote_name,
|
||||
gboolean gzip,
|
||||
GVariant *extra_headers,
|
||||
const char *append_user_agent,
|
||||
OstreeFetcherSecurityState *out_state,
|
||||
GError **error)
|
||||
{
|
||||
@@ -2830,6 +2941,12 @@ _ostree_repo_remote_new_fetcher (OstreeRepo *self,
|
||||
_ostree_fetcher_set_cookie_jar (fetcher, jar_path);
|
||||
}
|
||||
|
||||
if (extra_headers)
|
||||
_ostree_fetcher_set_extra_headers (fetcher, extra_headers);
|
||||
|
||||
if (append_user_agent)
|
||||
_ostree_fetcher_set_extra_user_agent (fetcher, append_user_agent);
|
||||
|
||||
success = TRUE;
|
||||
|
||||
out:
|
||||
@@ -2977,127 +3094,43 @@ fetch_mirrorlist (OstreeFetcher *fetcher,
|
||||
}
|
||||
|
||||
static gboolean
|
||||
repo_remote_fetch_summary (OstreeRepo *self,
|
||||
const char *name,
|
||||
const char *metalink_url_string,
|
||||
GVariant *options,
|
||||
GBytes **out_summary,
|
||||
GBytes **out_signatures,
|
||||
gboolean *out_from_cache,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
compute_effective_mirrorlist (OstreeRepo *self,
|
||||
const char *remote_name_or_baseurl,
|
||||
const char *url_override,
|
||||
OstreeFetcher *fetcher,
|
||||
guint n_network_retries,
|
||||
GPtrArray **out_mirrorlist,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
g_autoptr(OstreeFetcher) fetcher = NULL;
|
||||
g_autoptr(GMainContext) mainctx = NULL;
|
||||
gboolean ret = FALSE;
|
||||
gboolean from_cache = FALSE;
|
||||
const char *url_override = NULL;
|
||||
g_autoptr(GVariant) extra_headers = NULL;
|
||||
g_autoptr(GPtrArray) mirrorlist = NULL;
|
||||
const char *append_user_agent = NULL;
|
||||
guint n_network_retries = DEFAULT_N_NETWORK_RETRIES;
|
||||
g_autofree char *baseurl = NULL;
|
||||
|
||||
if (options)
|
||||
if (url_override != NULL)
|
||||
baseurl = g_strdup (url_override);
|
||||
else if (!ostree_repo_remote_get_url (self, remote_name_or_baseurl, &baseurl, error))
|
||||
return FALSE;
|
||||
|
||||
if (g_str_has_prefix (baseurl, "mirrorlist="))
|
||||
{
|
||||
(void) g_variant_lookup (options, "override-url", "&s", &url_override);
|
||||
(void) g_variant_lookup (options, "http-headers", "@a(ss)", &extra_headers);
|
||||
(void) g_variant_lookup (options, "append-user-agent", "&s", &append_user_agent);
|
||||
(void) g_variant_lookup (options, "n-network-retries", "&u", &n_network_retries);
|
||||
if (!fetch_mirrorlist (fetcher,
|
||||
baseurl + strlen ("mirrorlist="),
|
||||
n_network_retries,
|
||||
out_mirrorlist,
|
||||
cancellable, error))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
mainctx = g_main_context_new ();
|
||||
g_main_context_push_thread_default (mainctx);
|
||||
|
||||
fetcher = _ostree_repo_remote_new_fetcher (self, name, TRUE, NULL, error);
|
||||
if (fetcher == NULL)
|
||||
goto out;
|
||||
|
||||
if (extra_headers)
|
||||
_ostree_fetcher_set_extra_headers (fetcher, extra_headers);
|
||||
|
||||
if (append_user_agent)
|
||||
_ostree_fetcher_set_extra_user_agent (fetcher, append_user_agent);
|
||||
|
||||
{
|
||||
g_autofree char *url_string = NULL;
|
||||
if (metalink_url_string)
|
||||
url_string = g_strdup (metalink_url_string);
|
||||
else if (url_override)
|
||||
url_string = g_strdup (url_override);
|
||||
else if (!ostree_repo_remote_get_url (self, name, &url_string, error))
|
||||
goto out;
|
||||
|
||||
if (metalink_url_string == NULL &&
|
||||
g_str_has_prefix (url_string, "mirrorlist="))
|
||||
{
|
||||
if (!fetch_mirrorlist (fetcher, url_string + strlen ("mirrorlist="),
|
||||
n_network_retries, &mirrorlist, cancellable, error))
|
||||
goto out;
|
||||
}
|
||||
else
|
||||
{
|
||||
g_autoptr(OstreeFetcherURI) uri = _ostree_fetcher_uri_parse (url_string, error);
|
||||
|
||||
if (!uri)
|
||||
goto out;
|
||||
|
||||
mirrorlist =
|
||||
g_ptr_array_new_with_free_func ((GDestroyNotify) _ostree_fetcher_uri_free);
|
||||
g_ptr_array_add (mirrorlist, g_steal_pointer (&uri));
|
||||
}
|
||||
}
|
||||
|
||||
/* FIXME: Send the ETag from the cache with the request for summary.sig to
|
||||
* avoid downloading summary.sig unnecessarily. This won’t normally provide
|
||||
* any benefits (but won’t do any harm) since summary.sig is typically 500B
|
||||
* in size. But if a repository has multiple keys, the signature file will
|
||||
* grow and this optimisation may be useful. */
|
||||
if (!_ostree_preload_metadata_file (self,
|
||||
fetcher,
|
||||
mirrorlist,
|
||||
"summary.sig",
|
||||
metalink_url_string ? TRUE : FALSE,
|
||||
n_network_retries,
|
||||
out_signatures,
|
||||
cancellable,
|
||||
error))
|
||||
goto out;
|
||||
|
||||
if (*out_signatures)
|
||||
{
|
||||
if (!_ostree_repo_load_cache_summary_if_same_sig (self,
|
||||
name,
|
||||
*out_signatures,
|
||||
out_summary,
|
||||
cancellable,
|
||||
error))
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (*out_summary)
|
||||
from_cache = TRUE;
|
||||
else
|
||||
{
|
||||
if (!_ostree_preload_metadata_file (self,
|
||||
fetcher,
|
||||
mirrorlist,
|
||||
"summary",
|
||||
metalink_url_string ? TRUE : FALSE,
|
||||
n_network_retries,
|
||||
out_summary,
|
||||
cancellable,
|
||||
error))
|
||||
goto out;
|
||||
g_autoptr(OstreeFetcherURI) baseuri = _ostree_fetcher_uri_parse (baseurl, error);
|
||||
|
||||
if (!baseuri)
|
||||
return FALSE;
|
||||
|
||||
*out_mirrorlist =
|
||||
g_ptr_array_new_with_free_func ((GDestroyNotify) _ostree_fetcher_uri_free);
|
||||
g_ptr_array_add (*out_mirrorlist, g_steal_pointer (&baseuri));
|
||||
}
|
||||
|
||||
ret = TRUE;
|
||||
|
||||
out:
|
||||
if (mainctx)
|
||||
g_main_context_pop_thread_default (mainctx);
|
||||
|
||||
*out_from_cache = from_cache;
|
||||
return ret;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* Create the fetcher by unioning options from the remote config, plus
|
||||
@@ -3109,17 +3142,13 @@ reinitialize_fetcher (OtPullData *pull_data, const char *remote_name,
|
||||
{
|
||||
g_clear_object (&pull_data->fetcher);
|
||||
pull_data->fetcher = _ostree_repo_remote_new_fetcher (pull_data->repo, remote_name, FALSE,
|
||||
pull_data->extra_headers,
|
||||
pull_data->append_user_agent,
|
||||
&pull_data->fetcher_security_state,
|
||||
error);
|
||||
if (pull_data->fetcher == NULL)
|
||||
return FALSE;
|
||||
|
||||
if (pull_data->extra_headers)
|
||||
_ostree_fetcher_set_extra_headers (pull_data->fetcher, pull_data->extra_headers);
|
||||
|
||||
if (pull_data->append_user_agent)
|
||||
_ostree_fetcher_set_extra_user_agent (pull_data->fetcher, pull_data->append_user_agent);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@@ -3362,7 +3391,7 @@ ostree_repo_pull_with_options (OstreeRepo *self,
|
||||
const char *url_override = NULL;
|
||||
gboolean inherit_transaction = FALSE;
|
||||
g_autoptr(GHashTable) updated_requested_refs_to_fetch = NULL; /* (element-type OstreeCollectionRef utf8) */
|
||||
int i;
|
||||
gsize i;
|
||||
g_autofree char **opt_localcache_repos = NULL;
|
||||
g_autoptr(GVariantIter) ref_keyring_map_iter = NULL;
|
||||
g_autoptr(GVariant) summary_bytes_v = NULL;
|
||||
@@ -3631,33 +3660,13 @@ ostree_repo_pull_with_options (OstreeRepo *self,
|
||||
|
||||
if (!metalink_url_str)
|
||||
{
|
||||
g_autofree char *baseurl = NULL;
|
||||
|
||||
if (url_override != NULL)
|
||||
baseurl = g_strdup (url_override);
|
||||
else if (!ostree_repo_remote_get_url (self, remote_name_or_baseurl, &baseurl, error))
|
||||
if (!compute_effective_mirrorlist (self, remote_name_or_baseurl,
|
||||
url_override,
|
||||
pull_data->fetcher,
|
||||
pull_data->n_network_retries,
|
||||
&pull_data->meta_mirrorlist,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
|
||||
if (g_str_has_prefix (baseurl, "mirrorlist="))
|
||||
{
|
||||
if (!fetch_mirrorlist (pull_data->fetcher,
|
||||
baseurl + strlen ("mirrorlist="),
|
||||
pull_data->n_network_retries,
|
||||
&pull_data->meta_mirrorlist,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
}
|
||||
else
|
||||
{
|
||||
g_autoptr(OstreeFetcherURI) baseuri = _ostree_fetcher_uri_parse (baseurl, error);
|
||||
|
||||
if (!baseuri)
|
||||
goto out;
|
||||
|
||||
pull_data->meta_mirrorlist =
|
||||
g_ptr_array_new_with_free_func ((GDestroyNotify) _ostree_fetcher_uri_free);
|
||||
g_ptr_array_add (pull_data->meta_mirrorlist, g_steal_pointer (&baseuri));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -3716,27 +3725,13 @@ ostree_repo_pull_with_options (OstreeRepo *self,
|
||||
}
|
||||
else
|
||||
{
|
||||
if (g_str_has_prefix (contenturl, "mirrorlist="))
|
||||
{
|
||||
if (!fetch_mirrorlist (pull_data->fetcher,
|
||||
contenturl + strlen ("mirrorlist="),
|
||||
pull_data->n_network_retries,
|
||||
&pull_data->content_mirrorlist,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
}
|
||||
else
|
||||
{
|
||||
g_autoptr(OstreeFetcherURI) contenturi = _ostree_fetcher_uri_parse (contenturl, error);
|
||||
|
||||
if (!contenturi)
|
||||
goto out;
|
||||
|
||||
pull_data->content_mirrorlist =
|
||||
g_ptr_array_new_with_free_func ((GDestroyNotify) _ostree_fetcher_uri_free);
|
||||
g_ptr_array_add (pull_data->content_mirrorlist,
|
||||
g_steal_pointer (&contenturi));
|
||||
}
|
||||
if (!compute_effective_mirrorlist (self, remote_name_or_baseurl,
|
||||
contenturl,
|
||||
pull_data->fetcher,
|
||||
pull_data->n_network_retries,
|
||||
&pull_data->content_mirrorlist,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3764,30 +3759,6 @@ ostree_repo_pull_with_options (OstreeRepo *self,
|
||||
if (!ostree_repo_open (pull_data->remote_repo_local, cancellable, error))
|
||||
goto out;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!load_remote_repo_config (pull_data, &remote_config, cancellable, error))
|
||||
goto out;
|
||||
|
||||
if (!ot_keyfile_get_value_with_default (remote_config, "core", "mode", "bare",
|
||||
&remote_mode_str, error))
|
||||
goto out;
|
||||
|
||||
if (!ostree_repo_mode_from_string (remote_mode_str, &pull_data->remote_mode, error))
|
||||
goto out;
|
||||
|
||||
if (!ot_keyfile_get_boolean_with_default (remote_config, "core", "tombstone-commits", FALSE,
|
||||
&pull_data->has_tombstone_commits, error))
|
||||
goto out;
|
||||
|
||||
if (pull_data->remote_mode != OSTREE_REPO_MODE_ARCHIVE)
|
||||
{
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||
"Can't pull from archives with mode \"%s\"",
|
||||
remote_mode_str);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Change some option defaults if we're actually pulling from a local
|
||||
@@ -3856,11 +3827,13 @@ ostree_repo_pull_with_options (OstreeRepo *self,
|
||||
|
||||
{
|
||||
g_autoptr(GBytes) bytes_sig = NULL;
|
||||
gsize i, n;
|
||||
gsize n;
|
||||
g_autoptr(GVariant) refs = NULL;
|
||||
g_autoptr(GVariant) deltas = NULL;
|
||||
g_autoptr(GVariant) additional_metadata = NULL;
|
||||
gboolean summary_from_cache = FALSE;
|
||||
gboolean remote_mode_loaded = FALSE;
|
||||
gboolean tombstone_commits = FALSE;
|
||||
|
||||
if (summary_sig_bytes_v)
|
||||
{
|
||||
@@ -4174,6 +4147,46 @@ ostree_repo_pull_with_options (OstreeRepo *self,
|
||||
csum_data);
|
||||
}
|
||||
}
|
||||
|
||||
if (pull_data->summary &&
|
||||
g_variant_lookup (additional_metadata, OSTREE_SUMMARY_MODE, "s", &remote_mode_str) &&
|
||||
g_variant_lookup (additional_metadata, OSTREE_SUMMARY_TOMBSTONE_COMMITS, "b", &tombstone_commits))
|
||||
{
|
||||
if (!ostree_repo_mode_from_string (remote_mode_str, &pull_data->remote_mode, error))
|
||||
goto out;
|
||||
pull_data->has_tombstone_commits = tombstone_commits;
|
||||
remote_mode_loaded = TRUE;
|
||||
}
|
||||
else if (pull_data->remote_repo_local == NULL)
|
||||
{
|
||||
/* Fall-back path which loads the necessary config from the remote’s
|
||||
* `config` file. Doing so is deprecated since it means an
|
||||
* additional round trip to the remote for each pull. No need to do
|
||||
* it for local pulls. */
|
||||
if (!load_remote_repo_config (pull_data, &remote_config, cancellable, error))
|
||||
goto out;
|
||||
|
||||
if (!ot_keyfile_get_value_with_default (remote_config, "core", "mode", "bare",
|
||||
&remote_mode_str, error))
|
||||
goto out;
|
||||
|
||||
if (!ostree_repo_mode_from_string (remote_mode_str, &pull_data->remote_mode, error))
|
||||
goto out;
|
||||
|
||||
if (!ot_keyfile_get_boolean_with_default (remote_config, "core", "tombstone-commits", FALSE,
|
||||
&pull_data->has_tombstone_commits, error))
|
||||
goto out;
|
||||
|
||||
remote_mode_loaded = TRUE;
|
||||
}
|
||||
|
||||
if (remote_mode_loaded && pull_data->remote_repo_local == NULL && pull_data->remote_mode != OSTREE_REPO_MODE_ARCHIVE)
|
||||
{
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||
"Can't pull from archives with mode \"%s\"",
|
||||
remote_mode_str);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
if (pull_data->is_mirror && !refs_to_fetch && !opt_collection_refs_set && !configured_branches)
|
||||
@@ -5234,7 +5247,7 @@ find_remotes_process_refs (OstreeRepo *self,
|
||||
|
||||
static void
|
||||
find_remotes_cb (GObject *obj,
|
||||
GAsyncResult *result,
|
||||
GAsyncResult *async_result,
|
||||
gpointer user_data)
|
||||
{
|
||||
OstreeRepo *self;
|
||||
@@ -5266,7 +5279,7 @@ find_remotes_cb (GObject *obj,
|
||||
/* progress = data->progress; */
|
||||
|
||||
/* Finish finding the remotes. */
|
||||
results = ostree_repo_finder_resolve_all_finish (result, &error);
|
||||
results = ostree_repo_finder_resolve_all_finish (async_result, &error);
|
||||
|
||||
if (results == NULL)
|
||||
{
|
||||
@@ -5359,8 +5372,8 @@ find_remotes_cb (GObject *obj,
|
||||
|
||||
/* Check the metadata in the summary file, especially whether it contains
|
||||
* all the @refs we are interested in. */
|
||||
summary_v = g_variant_new_from_bytes (OSTREE_SUMMARY_GVARIANT_FORMAT,
|
||||
summary_bytes, FALSE);
|
||||
summary_v = g_variant_ref_sink (g_variant_new_from_bytes (OSTREE_SUMMARY_GVARIANT_FORMAT,
|
||||
summary_bytes, FALSE));
|
||||
|
||||
/* Check the summary’s additional metadata and set up @commit_metadata
|
||||
* and @refs_and_remotes_table with the refs listed in the summary file,
|
||||
@@ -5471,7 +5484,7 @@ find_remotes_cb (GObject *obj,
|
||||
goto error;
|
||||
|
||||
fetcher = _ostree_repo_remote_new_fetcher (self, result->remote->name,
|
||||
TRUE, NULL, &error);
|
||||
TRUE, NULL, NULL, NULL, &error);
|
||||
if (fetcher == NULL)
|
||||
goto error;
|
||||
|
||||
@@ -6093,95 +6106,107 @@ ostree_repo_remote_fetch_summary_with_options (OstreeRepo *self,
|
||||
g_autoptr(GBytes) signatures = NULL;
|
||||
gboolean gpg_verify_summary;
|
||||
g_autoptr(GPtrArray) signapi_summary_verifiers = NULL;
|
||||
gboolean ret = FALSE;
|
||||
gboolean summary_is_from_cache;
|
||||
gboolean summary_is_from_cache = FALSE;
|
||||
g_autoptr(OstreeFetcher) fetcher = NULL;
|
||||
g_autoptr(GMainContextPopDefault) mainctx = NULL;
|
||||
const char *url_override = NULL;
|
||||
g_autoptr(GVariant) extra_headers = NULL;
|
||||
g_autoptr(GPtrArray) mirrorlist = NULL;
|
||||
const char *append_user_agent = NULL;
|
||||
guint n_network_retries = DEFAULT_N_NETWORK_RETRIES;
|
||||
|
||||
g_return_val_if_fail (OSTREE_REPO (self), FALSE);
|
||||
g_return_val_if_fail (name != NULL, FALSE);
|
||||
|
||||
if (!ostree_repo_get_remote_option (self, name, "metalink", NULL,
|
||||
&metalink_url_string, error))
|
||||
goto out;
|
||||
return FALSE;
|
||||
|
||||
if (!repo_remote_fetch_summary (self,
|
||||
name,
|
||||
metalink_url_string,
|
||||
options,
|
||||
&summary,
|
||||
&signatures,
|
||||
&summary_is_from_cache,
|
||||
cancellable,
|
||||
error))
|
||||
goto out;
|
||||
if (options)
|
||||
{
|
||||
(void) g_variant_lookup (options, "override-url", "&s", &url_override);
|
||||
(void) g_variant_lookup (options, "http-headers", "@a(ss)", &extra_headers);
|
||||
(void) g_variant_lookup (options, "append-user-agent", "&s", &append_user_agent);
|
||||
(void) g_variant_lookup (options, "n-network-retries", "u", &n_network_retries);
|
||||
}
|
||||
|
||||
if (!ostree_repo_remote_get_gpg_verify_summary (self, name, &gpg_verify_summary, error))
|
||||
goto out;
|
||||
|
||||
if (gpg_verify_summary)
|
||||
{
|
||||
if (summary == NULL)
|
||||
{
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
|
||||
"GPG verification enabled, but no summary found (check that the configured URL in remote config is correct)");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (signatures == NULL)
|
||||
{
|
||||
g_set_error (error, OSTREE_GPG_ERROR, OSTREE_GPG_ERROR_NO_SIGNATURE,
|
||||
"GPG verification enabled, but no summary signatures found (use gpg-verify-summary=false in remote config to disable)");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Verify any summary signatures. */
|
||||
if (summary != NULL && signatures != NULL)
|
||||
{
|
||||
g_autoptr(OstreeGpgVerifyResult) result = NULL;
|
||||
|
||||
result = ostree_repo_verify_summary (self,
|
||||
name,
|
||||
summary,
|
||||
signatures,
|
||||
cancellable,
|
||||
error);
|
||||
if (!ostree_gpg_verify_result_require_valid_signature (result, error))
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
|
||||
if (!_signapi_init_for_remote (self, name, NULL,
|
||||
&signapi_summary_verifiers,
|
||||
error))
|
||||
goto out;
|
||||
return FALSE;
|
||||
|
||||
if (signapi_summary_verifiers)
|
||||
mainctx = _ostree_main_context_new_default ();
|
||||
|
||||
fetcher = _ostree_repo_remote_new_fetcher (self, name, TRUE, extra_headers, append_user_agent, NULL, error);
|
||||
if (fetcher == NULL)
|
||||
return FALSE;
|
||||
|
||||
if (metalink_url_string)
|
||||
{
|
||||
if (summary == NULL)
|
||||
{
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
|
||||
"Signature verification enabled, but no summary found (check that the configured URL in remote config is correct)");
|
||||
goto out;
|
||||
}
|
||||
g_autoptr(OstreeFetcherURI) uri = _ostree_fetcher_uri_parse (metalink_url_string, error);
|
||||
if (!uri)
|
||||
return FALSE;
|
||||
|
||||
if (signatures == NULL)
|
||||
{
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
|
||||
"Signature verification enabled, but no summary signatures found (use sign-verify-summary=false in remote config to disable)");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Verify any summary signatures. */
|
||||
if (summary != NULL && signatures != NULL)
|
||||
{
|
||||
g_autoptr(GVariant) sig_variant = NULL;
|
||||
|
||||
sig_variant = g_variant_new_from_bytes (OSTREE_SUMMARY_SIG_GVARIANT_FORMAT,
|
||||
signatures, FALSE);
|
||||
|
||||
if (!_sign_verify_for_remote (signapi_summary_verifiers, summary, sig_variant, NULL, error))
|
||||
goto out;
|
||||
}
|
||||
mirrorlist =
|
||||
g_ptr_array_new_with_free_func ((GDestroyNotify) _ostree_fetcher_uri_free);
|
||||
g_ptr_array_add (mirrorlist, g_steal_pointer (&uri));
|
||||
}
|
||||
else if (!compute_effective_mirrorlist (self, name, url_override,
|
||||
fetcher, n_network_retries,
|
||||
&mirrorlist, cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
/* FIXME: Send the ETag from the cache with the request for summary.sig to
|
||||
* avoid downloading summary.sig unnecessarily. This won’t normally provide
|
||||
* any benefits (but won’t do any harm) since summary.sig is typically 500B
|
||||
* in size. But if a repository has multiple keys, the signature file will
|
||||
* grow and this optimisation may be useful. */
|
||||
if (!_ostree_preload_metadata_file (self,
|
||||
fetcher,
|
||||
mirrorlist,
|
||||
"summary.sig",
|
||||
metalink_url_string ? TRUE : FALSE,
|
||||
n_network_retries,
|
||||
&signatures,
|
||||
cancellable,
|
||||
error))
|
||||
return FALSE;
|
||||
|
||||
if (signatures)
|
||||
{
|
||||
if (!_ostree_repo_load_cache_summary_if_same_sig (self,
|
||||
name,
|
||||
signatures,
|
||||
&summary,
|
||||
cancellable,
|
||||
error))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (summary)
|
||||
summary_is_from_cache = TRUE;
|
||||
else
|
||||
{
|
||||
if (!_ostree_preload_metadata_file (self,
|
||||
fetcher,
|
||||
mirrorlist,
|
||||
"summary",
|
||||
metalink_url_string ? TRUE : FALSE,
|
||||
n_network_retries,
|
||||
&summary,
|
||||
cancellable,
|
||||
error))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!_ostree_repo_verify_summary (self, name,
|
||||
gpg_verify_summary, signapi_summary_verifiers,
|
||||
summary, signatures,
|
||||
cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
if (!summary_is_from_cache && summary && signatures)
|
||||
{
|
||||
@@ -6199,7 +6224,7 @@ ostree_repo_remote_fetch_summary_with_options (OstreeRepo *self,
|
||||
else
|
||||
{
|
||||
g_propagate_error (error, g_steal_pointer (&temp_error));
|
||||
goto out;
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6210,10 +6235,7 @@ ostree_repo_remote_fetch_summary_with_options (OstreeRepo *self,
|
||||
if (out_signatures != NULL)
|
||||
*out_signatures = g_steal_pointer (&signatures);
|
||||
|
||||
ret = TRUE;
|
||||
|
||||
out:
|
||||
return ret;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
#else /* HAVE_LIBCURL_OR_LIBSOUP */
|
||||
|
||||
@@ -36,6 +36,8 @@
|
||||
#include "libglnx.h"
|
||||
#include "ostree-varint.h"
|
||||
#include "bsdiff/bsdiff.h"
|
||||
#include "ostree-autocleanups.h"
|
||||
#include "ostree-sign.h"
|
||||
|
||||
#define CONTENT_SIZE_SIMILARITY_THRESHOLD_PERCENT (30)
|
||||
|
||||
@@ -1335,6 +1337,8 @@ get_fallback_headers (OstreeRepo *self,
|
||||
* - verbose: b: Print diagnostic messages. Default FALSE.
|
||||
* - endianness: b: Deltas use host byte order by default; this option allows choosing (G_BIG_ENDIAN or G_LITTLE_ENDIAN)
|
||||
* - filename: ay: Save delta superblock to this filename, and parts in the same directory. Default saves to repository.
|
||||
* - sign-name: ay: Signature type to use.
|
||||
* - sign-key-ids: as: Array of keys used to sign delta superblock.
|
||||
*/
|
||||
gboolean
|
||||
ostree_repo_static_delta_generate (OstreeRepo *self,
|
||||
@@ -1368,6 +1372,8 @@ ostree_repo_static_delta_generate (OstreeRepo *self,
|
||||
g_autoptr(GPtrArray) builder_fallback_objects = g_ptr_array_new_with_free_func ((GDestroyNotify)g_variant_unref);
|
||||
g_auto(GLnxTmpfile) descriptor_tmpf = { 0, };
|
||||
g_autoptr(OtVariantBuilder) descriptor_builder = NULL;
|
||||
const char *opt_sign_name;
|
||||
const char **opt_key_ids;
|
||||
|
||||
if (!g_variant_lookup (params, "min-fallback-size", "u", &min_fallback_size))
|
||||
min_fallback_size = 4;
|
||||
@@ -1407,6 +1413,12 @@ ostree_repo_static_delta_generate (OstreeRepo *self,
|
||||
if (!g_variant_lookup (params, "filename", "^&ay", &opt_filename))
|
||||
opt_filename = NULL;
|
||||
|
||||
if (!g_variant_lookup (params, "sign-name", "^&ay", &opt_sign_name))
|
||||
opt_sign_name = NULL;
|
||||
|
||||
if (!g_variant_lookup (params, "sign-key-ids", "^a&s", &opt_key_ids))
|
||||
opt_key_ids = NULL;
|
||||
|
||||
if (!ostree_repo_load_variant (self, OSTREE_OBJECT_TYPE_COMMIT, to,
|
||||
&to_commit, error))
|
||||
return FALSE;
|
||||
@@ -1442,7 +1454,7 @@ ostree_repo_static_delta_generate (OstreeRepo *self,
|
||||
cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
if (!glnx_open_tmpfile_linkable_at (descriptor_dfd, ".", O_WRONLY | O_CLOEXEC,
|
||||
if (!glnx_open_tmpfile_linkable_at (descriptor_dfd, ".", O_RDWR | O_CLOEXEC,
|
||||
&descriptor_tmpf, error))
|
||||
return FALSE;
|
||||
|
||||
@@ -1586,12 +1598,85 @@ ostree_repo_static_delta_generate (OstreeRepo *self,
|
||||
g_printerr ("bsdiff=%u objects\n", builder.n_bsdiff);
|
||||
}
|
||||
|
||||
if (fchmod (descriptor_tmpf.fd, 0644) < 0)
|
||||
return glnx_throw_errno_prefix (error, "fchmod");
|
||||
if (opt_sign_name != NULL && opt_key_ids != NULL)
|
||||
{
|
||||
g_autoptr(GBytes) tmpdata = NULL;
|
||||
g_autoptr(OstreeSign) sign = NULL;
|
||||
const gchar *signature_key = NULL;
|
||||
g_autoptr(GVariantBuilder) signature_builder = NULL;
|
||||
g_auto(GLnxTmpfile) descriptor_sign_tmpf = { 0, };
|
||||
g_autoptr(OtVariantBuilder) descriptor_sign_builder = NULL;
|
||||
|
||||
if (!glnx_link_tmpfile_at (&descriptor_tmpf, GLNX_LINK_TMPFILE_REPLACE,
|
||||
descriptor_dfd, descriptor_name, error))
|
||||
return FALSE;
|
||||
lseek (descriptor_tmpf.fd, 0, SEEK_SET);
|
||||
tmpdata = glnx_fd_readall_bytes (descriptor_tmpf.fd, cancellable, error);
|
||||
if (!tmpdata)
|
||||
return FALSE;
|
||||
|
||||
sign = ostree_sign_get_by_name (opt_sign_name, error);
|
||||
if (sign == NULL)
|
||||
return FALSE;
|
||||
|
||||
signature_key = ostree_sign_metadata_key (sign);
|
||||
const gchar *signature_format = ostree_sign_metadata_format (sign);
|
||||
|
||||
signature_builder = g_variant_builder_new (G_VARIANT_TYPE (signature_format));
|
||||
|
||||
for (const char **iter = opt_key_ids; iter && *iter; iter++)
|
||||
{
|
||||
const char *keyid = *iter;
|
||||
g_autoptr(GVariant) secret_key = NULL;
|
||||
g_autoptr(GBytes) signature_bytes = NULL;
|
||||
|
||||
secret_key = g_variant_new_string (keyid);
|
||||
if (!ostree_sign_set_sk (sign, secret_key, error))
|
||||
return FALSE;
|
||||
|
||||
if (!ostree_sign_data (sign, tmpdata, &signature_bytes,
|
||||
NULL, error))
|
||||
return FALSE;
|
||||
|
||||
g_variant_builder_add (signature_builder, "@ay", ot_gvariant_new_ay_bytes (signature_bytes));
|
||||
}
|
||||
|
||||
if (!glnx_open_tmpfile_linkable_at (descriptor_dfd, ".", O_WRONLY | O_CLOEXEC,
|
||||
&descriptor_sign_tmpf, error))
|
||||
return FALSE;
|
||||
|
||||
descriptor_sign_builder = ot_variant_builder_new (G_VARIANT_TYPE (OSTREE_STATIC_DELTA_SIGNED_FORMAT),
|
||||
descriptor_sign_tmpf.fd);
|
||||
|
||||
if (!ot_variant_builder_add (descriptor_sign_builder, error, "t",
|
||||
GUINT64_TO_BE (OSTREE_STATIC_DELTA_SIGNED_MAGIC)))
|
||||
return FALSE;
|
||||
if (!ot_variant_builder_add (descriptor_sign_builder, error, "@ay", ot_gvariant_new_ay_bytes (tmpdata)))
|
||||
return FALSE;
|
||||
if (!ot_variant_builder_open (descriptor_sign_builder, G_VARIANT_TYPE ("a{sv}"), error))
|
||||
return FALSE;
|
||||
if (!ot_variant_builder_add (descriptor_sign_builder, error, "{sv}",
|
||||
signature_key, g_variant_builder_end(signature_builder)))
|
||||
return FALSE;
|
||||
if (!ot_variant_builder_close (descriptor_sign_builder, error))
|
||||
return FALSE;
|
||||
|
||||
if (!ot_variant_builder_end (descriptor_sign_builder, error))
|
||||
return FALSE;
|
||||
|
||||
if (fchmod (descriptor_sign_tmpf.fd, 0644) < 0)
|
||||
return glnx_throw_errno_prefix (error, "fchmod");
|
||||
|
||||
if (!glnx_link_tmpfile_at (&descriptor_sign_tmpf, GLNX_LINK_TMPFILE_REPLACE,
|
||||
descriptor_dfd, descriptor_name, error))
|
||||
return FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (fchmod (descriptor_tmpf.fd, 0644) < 0)
|
||||
return glnx_throw_errno_prefix (error, "fchmod");
|
||||
|
||||
if (!glnx_link_tmpfile_at (&descriptor_tmpf, GLNX_LINK_TMPFILE_REPLACE,
|
||||
descriptor_dfd, descriptor_name, error))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
@@ -54,6 +54,28 @@ _ostree_static_delta_parse_checksum_array (GVariant *array,
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
GVariant *
|
||||
_ostree_repo_static_delta_superblock_digest (OstreeRepo *repo,
|
||||
const char *from,
|
||||
const char *to,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
g_autofree char *superblock = _ostree_get_relative_static_delta_superblock_path ((from && from[0]) ? from : NULL, to);
|
||||
glnx_autofd int superblock_file_fd = -1;
|
||||
guint8 digest[OSTREE_SHA256_DIGEST_LEN];
|
||||
|
||||
if (!glnx_openat_rdonly (repo->repo_dir_fd, superblock, TRUE, &superblock_file_fd, error))
|
||||
return NULL;
|
||||
|
||||
g_autoptr(GBytes) superblock_content = ot_fd_readall_or_mmap (superblock_file_fd, 0, error);
|
||||
if (!superblock_content)
|
||||
return NULL;
|
||||
|
||||
ot_checksum_bytes (superblock_content, digest);
|
||||
|
||||
return ot_gvariant_new_bytearray (digest, sizeof (digest));
|
||||
}
|
||||
|
||||
/**
|
||||
* ostree_repo_list_static_delta_names:
|
||||
@@ -109,7 +131,7 @@ ostree_repo_list_static_delta_names (OstreeRepo *self,
|
||||
return FALSE;
|
||||
if (sub_dent == NULL)
|
||||
break;
|
||||
if (dent->d_type != DT_DIR)
|
||||
if (sub_dent->d_type != DT_DIR)
|
||||
continue;
|
||||
|
||||
const char *name1 = dent->d_name;
|
||||
@@ -188,27 +210,126 @@ _ostree_repo_static_delta_part_have_all_objects (OstreeRepo *repo,
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
_ostree_repo_static_delta_is_signed (OstreeRepo *self,
|
||||
int fd,
|
||||
GPtrArray **out_value,
|
||||
GError **error)
|
||||
{
|
||||
g_autoptr(GVariant) delta = NULL;
|
||||
g_autoptr(GVariant) delta_sign_magic = NULL;
|
||||
g_autoptr(GVariant) delta_sign = NULL;
|
||||
GVariantIter iter;
|
||||
GVariant *item;
|
||||
g_autoptr(GPtrArray) signatures = NULL;
|
||||
gboolean ret = FALSE;
|
||||
|
||||
if (out_value)
|
||||
*out_value = NULL;
|
||||
|
||||
if (!ot_variant_read_fd (fd, 0, (GVariantType*)OSTREE_STATIC_DELTA_SIGNED_FORMAT, TRUE, &delta, error))
|
||||
return FALSE;
|
||||
|
||||
delta_sign_magic = g_variant_get_child_value (delta, 0);
|
||||
if (delta_sign_magic == NULL)
|
||||
return glnx_throw (error, "no signatures in static-delta");
|
||||
|
||||
if (GUINT64_FROM_BE (g_variant_get_uint64 (delta_sign_magic)) != OSTREE_STATIC_DELTA_SIGNED_MAGIC)
|
||||
return glnx_throw (error, "no signatures in static-delta");
|
||||
|
||||
delta_sign = g_variant_get_child_value (delta, 2);
|
||||
if (delta_sign == NULL)
|
||||
return glnx_throw (error, "no signatures in static-delta");
|
||||
|
||||
if (out_value)
|
||||
signatures = g_ptr_array_new_with_free_func (g_free);
|
||||
|
||||
/* Check if there are signatures in the superblock */
|
||||
g_variant_iter_init (&iter, delta_sign);
|
||||
while ((item = g_variant_iter_next_value (&iter)))
|
||||
{
|
||||
g_autoptr(GVariant) key_v = g_variant_get_child_value (item, 0);
|
||||
const char *str = g_variant_get_string (key_v, NULL);
|
||||
if (g_str_has_prefix (str, "ostree.sign."))
|
||||
{
|
||||
ret = TRUE;
|
||||
if (signatures)
|
||||
g_ptr_array_add (signatures, g_strdup (str + strlen ("ostree.sign.")));
|
||||
}
|
||||
g_variant_unref (item);
|
||||
}
|
||||
|
||||
if (out_value && ret)
|
||||
ot_transfer_out_value (out_value, &signatures);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
_ostree_repo_static_delta_verify_signature (OstreeRepo *self,
|
||||
int fd,
|
||||
OstreeSign *sign,
|
||||
char **out_success_message,
|
||||
GError **error)
|
||||
{
|
||||
g_autoptr(GVariant) delta = NULL;
|
||||
|
||||
if (!ot_variant_read_fd (fd, 0,
|
||||
(GVariantType*)OSTREE_STATIC_DELTA_SIGNED_FORMAT,
|
||||
TRUE, &delta, error))
|
||||
return FALSE;
|
||||
|
||||
/* Check if there are signatures for signature engine */
|
||||
const gchar *signature_key = ostree_sign_metadata_key(sign);
|
||||
GVariantType *signature_format = (GVariantType *) ostree_sign_metadata_format(sign);
|
||||
g_autoptr(GVariant) delta_meta = g_variant_get_child_value (delta, 2);
|
||||
if (delta_meta == NULL)
|
||||
return glnx_throw (error, "no metadata in static-delta superblock");
|
||||
g_autoptr(GVariant) signatures = g_variant_lookup_value (delta_meta,
|
||||
signature_key,
|
||||
signature_format);
|
||||
if (!signatures)
|
||||
return glnx_throw (error, "no signature for '%s' in static-delta superblock", signature_key);
|
||||
|
||||
/* Get static delta superblock */
|
||||
g_autoptr(GVariant) child = g_variant_get_child_value (delta, 1);
|
||||
if (child == NULL)
|
||||
return glnx_throw (error, "no metadata in static-delta superblock");
|
||||
g_autoptr(GBytes) signed_data = g_variant_get_data_as_bytes(child);
|
||||
|
||||
return ostree_sign_data_verify (sign, signed_data, signatures, out_success_message, error);
|
||||
}
|
||||
|
||||
/**
|
||||
* ostree_repo_static_delta_execute_offline:
|
||||
* ostree_repo_static_delta_execute_offline_with_signature:
|
||||
* @self: Repo
|
||||
* @dir_or_file: Path to a directory containing static delta data, or directly to the superblock
|
||||
* @sign: Signature engine used to check superblock
|
||||
* @skip_validation: If %TRUE, assume data integrity
|
||||
* @cancellable: Cancellable
|
||||
* @error: Error
|
||||
*
|
||||
* Given a directory representing an already-downloaded static delta
|
||||
* on disk, apply it, generating a new commit. The directory must be
|
||||
* named with the form "FROM-TO", where both are checksums, and it
|
||||
* must contain a file named "superblock", along with at least one part.
|
||||
* on disk, apply it, generating a new commit.
|
||||
* If sign is passed, the static delta signature is verified.
|
||||
* If sign-verify-deltas configuration option is set and static delta is signed,
|
||||
* signature verification will be mandatory before apply the static delta.
|
||||
* The directory must be named with the form "FROM-TO", where both are
|
||||
* checksums, and it must contain a file named "superblock", along with at least
|
||||
* one part.
|
||||
*
|
||||
* Since: 2020.7
|
||||
*/
|
||||
gboolean
|
||||
ostree_repo_static_delta_execute_offline (OstreeRepo *self,
|
||||
GFile *dir_or_file,
|
||||
gboolean skip_validation,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
ostree_repo_static_delta_execute_offline_with_signature (OstreeRepo *self,
|
||||
GFile *dir_or_file,
|
||||
OstreeSign *sign,
|
||||
gboolean skip_validation,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
g_autofree char *basename = NULL;
|
||||
g_autoptr(GVariant) meta = NULL;
|
||||
|
||||
const char *dir_or_file_path = gs_file_get_path_cached (dir_or_file);
|
||||
|
||||
@@ -234,10 +355,44 @@ ostree_repo_static_delta_execute_offline (OstreeRepo *self,
|
||||
if (meta_fd < 0)
|
||||
return glnx_throw_errno_prefix (error, "openat(%s)", basename);
|
||||
|
||||
g_autoptr(GVariant) meta = NULL;
|
||||
if (!ot_variant_read_fd (meta_fd, 0, G_VARIANT_TYPE (OSTREE_STATIC_DELTA_SUPERBLOCK_FORMAT),
|
||||
FALSE, &meta, error))
|
||||
return FALSE;
|
||||
gboolean is_signed = _ostree_repo_static_delta_is_signed (self, meta_fd, NULL, NULL);
|
||||
if (is_signed)
|
||||
{
|
||||
gboolean verify_deltas;
|
||||
gboolean verified;
|
||||
|
||||
if (!ot_keyfile_get_boolean_with_default (self->config, "core", "sign-verify-deltas",
|
||||
FALSE, &verify_deltas, error))
|
||||
return FALSE;
|
||||
|
||||
if (verify_deltas && !sign)
|
||||
return glnx_throw (error, "Key is mandatory to check delta signature");
|
||||
|
||||
if (sign)
|
||||
{
|
||||
verified = _ostree_repo_static_delta_verify_signature (self, meta_fd, sign, NULL, error);
|
||||
if (*error)
|
||||
return FALSE;
|
||||
if (!verified)
|
||||
return glnx_throw (error, "Delta signature verification failed");
|
||||
}
|
||||
|
||||
g_autoptr(GVariant) delta = NULL;
|
||||
if (!ot_variant_read_fd (meta_fd, 0, (GVariantType*)OSTREE_STATIC_DELTA_SIGNED_FORMAT,
|
||||
TRUE, &delta, error))
|
||||
return FALSE;
|
||||
|
||||
g_autoptr(GVariant) child = g_variant_get_child_value (delta, 1);
|
||||
g_autoptr(GBytes) bytes = g_variant_get_data_as_bytes (child);
|
||||
meta = g_variant_new_from_bytes ((GVariantType*)OSTREE_STATIC_DELTA_SUPERBLOCK_FORMAT,
|
||||
bytes, FALSE);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!ot_variant_read_fd (meta_fd, 0, G_VARIANT_TYPE (OSTREE_STATIC_DELTA_SUPERBLOCK_FORMAT),
|
||||
FALSE, &meta, error))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Parsing OSTREE_STATIC_DELTA_SUPERBLOCK_FORMAT */
|
||||
|
||||
@@ -280,9 +435,8 @@ ostree_repo_static_delta_execute_offline (OstreeRepo *self,
|
||||
if (!have_to_commit)
|
||||
{
|
||||
g_autofree char *detached_path = _ostree_get_relative_static_delta_path (from_checksum, to_checksum, "commitmeta");
|
||||
g_autoptr(GVariant) detached_data = NULL;
|
||||
|
||||
detached_data = g_variant_lookup_value (metadata, detached_path, G_VARIANT_TYPE("a{sv}"));
|
||||
g_autoptr(GVariant) detached_data =
|
||||
g_variant_lookup_value (metadata, detached_path, G_VARIANT_TYPE("a{sv}"));
|
||||
if (detached_data && !ostree_repo_write_commit_detached_metadata (self,
|
||||
to_checksum,
|
||||
detached_data,
|
||||
@@ -386,6 +540,32 @@ ostree_repo_static_delta_execute_offline (OstreeRepo *self,
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* ostree_repo_static_delta_execute_offline:
|
||||
* @self: Repo
|
||||
* @dir_or_file: Path to a directory containing static delta data, or directly to the superblock
|
||||
* @skip_validation: If %TRUE, assume data integrity
|
||||
* @cancellable: Cancellable
|
||||
* @error: Error
|
||||
*
|
||||
* Given a directory representing an already-downloaded static delta
|
||||
* on disk, apply it, generating a new commit. The directory must be
|
||||
* named with the form "FROM-TO", where both are checksums, and it
|
||||
* must contain a file named "superblock", along with at least one part.
|
||||
*/
|
||||
gboolean
|
||||
ostree_repo_static_delta_execute_offline (OstreeRepo *self,
|
||||
GFile *dir_or_file,
|
||||
gboolean skip_validation,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
return ostree_repo_static_delta_execute_offline_with_signature(self, dir_or_file, NULL,
|
||||
skip_validation,
|
||||
cancellable,
|
||||
error);
|
||||
}
|
||||
|
||||
gboolean
|
||||
_ostree_static_delta_part_open (GInputStream *part_in,
|
||||
GBytes *inline_part_bytes,
|
||||
@@ -726,6 +906,8 @@ _ostree_repo_static_delta_dump (OstreeRepo *self,
|
||||
GError **error)
|
||||
{
|
||||
glnx_autofd int superblock_fd = -1;
|
||||
g_autoptr(GVariant) delta = NULL;
|
||||
g_autoptr(GVariant) delta_superblock = NULL;
|
||||
|
||||
if (strchr (delta_id, '/'))
|
||||
{
|
||||
@@ -744,13 +926,28 @@ _ostree_repo_static_delta_dump (OstreeRepo *self,
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
g_autoptr(GVariant) delta_superblock = NULL;
|
||||
if (!ot_variant_read_fd (superblock_fd, 0,
|
||||
(GVariantType*)OSTREE_STATIC_DELTA_SUPERBLOCK_FORMAT,
|
||||
TRUE, &delta_superblock, error))
|
||||
return FALSE;
|
||||
gboolean is_signed = _ostree_repo_static_delta_is_signed(self, superblock_fd, NULL, NULL);
|
||||
if (is_signed)
|
||||
{
|
||||
if (!ot_variant_read_fd (superblock_fd, 0, (GVariantType*)OSTREE_STATIC_DELTA_SIGNED_FORMAT,
|
||||
TRUE, &delta, error))
|
||||
return FALSE;
|
||||
|
||||
g_autoptr(GVariant) child = g_variant_get_child_value (delta, 1);
|
||||
g_autoptr(GBytes) bytes = g_variant_get_data_as_bytes(child);
|
||||
delta_superblock = g_variant_new_from_bytes ((GVariantType*)OSTREE_STATIC_DELTA_SUPERBLOCK_FORMAT,
|
||||
bytes, FALSE);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!ot_variant_read_fd (superblock_fd, 0,
|
||||
(GVariantType*)OSTREE_STATIC_DELTA_SUPERBLOCK_FORMAT,
|
||||
TRUE, &delta_superblock, error))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
g_print ("Delta: %s\n", delta_id);
|
||||
g_print ("Signed: %s\n", is_signed ? "yes" : "no");
|
||||
g_autoptr(GVariant) from_commit_v = NULL;
|
||||
g_variant_get_child (delta_superblock, 2, "@ay", &from_commit_v);
|
||||
g_autofree char *from_commit = NULL;
|
||||
@@ -873,3 +1070,51 @@ _ostree_repo_static_delta_dump (OstreeRepo *self,
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* ostree_repo_static_delta_verify_signature:
|
||||
* @self: Repo
|
||||
* @delta_id: delta path
|
||||
* @sign: Signature engine used to check superblock
|
||||
* @out_success_message: success message
|
||||
* @error: Error
|
||||
*
|
||||
* Verify static delta file signature.
|
||||
*
|
||||
* Returns: TRUE if the signature of static delta file is valid using the
|
||||
* signature engine provided, FALSE otherwise.
|
||||
*
|
||||
* Since: 2020.7
|
||||
*/
|
||||
gboolean
|
||||
ostree_repo_static_delta_verify_signature (OstreeRepo *self,
|
||||
const char *delta_id,
|
||||
OstreeSign *sign,
|
||||
char **out_success_message,
|
||||
GError **error)
|
||||
{
|
||||
g_autoptr(GVariant) delta_meta = NULL;
|
||||
glnx_autofd int delta_fd = -1;
|
||||
|
||||
if (strchr (delta_id, '/'))
|
||||
{
|
||||
if (!glnx_openat_rdonly (AT_FDCWD, delta_id, TRUE, &delta_fd, error))
|
||||
return FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
g_autofree char *from = NULL;
|
||||
g_autofree char *to = NULL;
|
||||
if (!_ostree_parse_delta_name (delta_id, &from, &to, error))
|
||||
return FALSE;
|
||||
|
||||
g_autofree char *delta_path = _ostree_get_relative_static_delta_superblock_path (from, to);
|
||||
if (!glnx_openat_rdonly (self->repo_dir_fd, delta_path, TRUE, &delta_fd, error))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!_ostree_repo_static_delta_is_signed (self, delta_fd, NULL, error))
|
||||
return FALSE;
|
||||
|
||||
return _ostree_repo_static_delta_verify_signature (self, delta_fd, sign, out_success_message, error);
|
||||
}
|
||||
@@ -104,6 +104,25 @@ G_BEGIN_DECLS
|
||||
*/
|
||||
#define OSTREE_STATIC_DELTA_SUPERBLOCK_FORMAT "(a{sv}tayay" OSTREE_COMMIT_GVARIANT_STRING "aya" OSTREE_STATIC_DELTA_META_ENTRY_FORMAT "a" OSTREE_STATIC_DELTA_FALLBACK_FORMAT ")"
|
||||
|
||||
/**
|
||||
* OSTREE_STATIC_DELTA_SIGNED_FORMAT
|
||||
*
|
||||
* magic: t magic number, 8 bytes for alignment
|
||||
* superblock: ay delta supeblock variant
|
||||
* signatures: a{sv}
|
||||
*
|
||||
* The signed static delta starts with the 'OSTSGNDT' magic number followed by
|
||||
* the array of bytes containing the superblock used for the signature.
|
||||
*
|
||||
* Then, the signatures array contains the signatures of the superblock. A
|
||||
* signature has the following form:
|
||||
* type: signature key
|
||||
* signature: variant depending on type used
|
||||
*/
|
||||
#define OSTREE_STATIC_DELTA_SIGNED_FORMAT "(taya{sv})"
|
||||
|
||||
#define OSTREE_STATIC_DELTA_SIGNED_MAGIC 0x4F535453474E4454 /* OSTSGNDT */
|
||||
|
||||
typedef enum {
|
||||
OSTREE_STATIC_DELTA_OPEN_FLAGS_NONE = 0,
|
||||
OSTREE_STATIC_DELTA_OPEN_FLAGS_SKIP_CHECKSUM = (1 << 0),
|
||||
@@ -190,6 +209,12 @@ _ostree_repo_static_delta_query_exists (OstreeRepo *repo,
|
||||
gboolean *out_exists,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
GVariant *
|
||||
_ostree_repo_static_delta_superblock_digest (OstreeRepo *repo,
|
||||
const char *from,
|
||||
const char *to,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
gboolean
|
||||
_ostree_repo_static_delta_dump (OstreeRepo *repo,
|
||||
|
||||
+34
-23
@@ -3782,14 +3782,14 @@ load_metadata_internal (OstreeRepo *self,
|
||||
g_autofree char *commitpartial_path = _ostree_get_commitpartial_path (sha256);
|
||||
*out_state = 0;
|
||||
|
||||
glnx_autofd int fd = -1;
|
||||
if (!ot_openat_ignore_enoent (self->repo_dir_fd, commitpartial_path, &fd, error))
|
||||
glnx_autofd int commitpartial_fd = -1;
|
||||
if (!ot_openat_ignore_enoent (self->repo_dir_fd, commitpartial_path, &commitpartial_fd, error))
|
||||
return FALSE;
|
||||
if (fd != -1)
|
||||
if (commitpartial_fd != -1)
|
||||
{
|
||||
*out_state |= OSTREE_REPO_COMMIT_STATE_PARTIAL;
|
||||
char reason;
|
||||
if (read (fd, &reason, 1) == 1)
|
||||
if (read (commitpartial_fd, &reason, 1) == 1)
|
||||
{
|
||||
if (reason == 'f')
|
||||
*out_state |= OSTREE_REPO_COMMIT_STATE_FSCK_PARTIAL;
|
||||
@@ -5793,25 +5793,18 @@ ostree_repo_regenerate_summary (OstreeRepo *self,
|
||||
{
|
||||
g_autofree char *from = NULL;
|
||||
g_autofree char *to = NULL;
|
||||
GVariant *digest;
|
||||
|
||||
if (!_ostree_parse_delta_name (delta_names->pdata[i], &from, &to, error))
|
||||
return FALSE;
|
||||
|
||||
g_autofree char *superblock = _ostree_get_relative_static_delta_superblock_path ((from && from[0]) ? from : NULL, to);
|
||||
glnx_autofd int superblock_file_fd = -1;
|
||||
|
||||
if (!glnx_openat_rdonly (self->repo_dir_fd, superblock, TRUE, &superblock_file_fd, error))
|
||||
digest = _ostree_repo_static_delta_superblock_digest (self,
|
||||
(from && from[0]) ? from : NULL,
|
||||
to, cancellable, error);
|
||||
if (digest == NULL)
|
||||
return FALSE;
|
||||
|
||||
g_autoptr(GBytes) superblock_content = ot_fd_readall_or_mmap (superblock_file_fd, 0, error);
|
||||
if (!superblock_content)
|
||||
return FALSE;
|
||||
g_auto(OtChecksum) hasher = { 0, };
|
||||
ot_checksum_init (&hasher);
|
||||
ot_checksum_update_bytes (&hasher, superblock_content);
|
||||
guint8 digest[OSTREE_SHA256_DIGEST_LEN];
|
||||
ot_checksum_get_digest (&hasher, digest, sizeof (digest));
|
||||
|
||||
g_variant_dict_insert_value (&deltas_builder, delta_names->pdata[i], ot_gvariant_new_bytearray (digest, sizeof (digest)));
|
||||
g_variant_dict_insert_value (&deltas_builder, delta_names->pdata[i], digest);
|
||||
}
|
||||
|
||||
if (delta_names->len > 0)
|
||||
@@ -5823,6 +5816,24 @@ ostree_repo_regenerate_summary (OstreeRepo *self,
|
||||
g_variant_new_uint64 (GUINT64_TO_BE (g_get_real_time () / G_USEC_PER_SEC)));
|
||||
}
|
||||
|
||||
{
|
||||
g_autofree char *remote_mode_str = NULL;
|
||||
if (!ot_keyfile_get_value_with_default (self->config, "core", "mode", "bare",
|
||||
&remote_mode_str, error))
|
||||
return FALSE;
|
||||
g_variant_dict_insert_value (&additional_metadata_builder, OSTREE_SUMMARY_MODE,
|
||||
g_variant_new_string (remote_mode_str));
|
||||
}
|
||||
|
||||
{
|
||||
gboolean tombstone_commits = FALSE;
|
||||
if (!ot_keyfile_get_boolean_with_default (self->config, "core", "tombstone-commits", FALSE,
|
||||
&tombstone_commits, error))
|
||||
return FALSE;
|
||||
g_variant_dict_insert_value (&additional_metadata_builder, OSTREE_SUMMARY_TOMBSTONE_COMMITS,
|
||||
g_variant_new_boolean (tombstone_commits));
|
||||
}
|
||||
|
||||
/* Add refs which have a collection specified, which could be in refs/mirrors,
|
||||
* refs/heads, and/or refs/remotes. */
|
||||
{
|
||||
@@ -5838,19 +5849,19 @@ ostree_repo_regenerate_summary (OstreeRepo *self,
|
||||
collection_map = g_hash_table_new_full (g_str_hash, g_str_equal, NULL,
|
||||
(GDestroyNotify) g_hash_table_unref);
|
||||
|
||||
const OstreeCollectionRef *ref;
|
||||
const OstreeCollectionRef *c_ref;
|
||||
const char *checksum;
|
||||
while (g_hash_table_iter_next (&iter, (gpointer *) &ref, (gpointer *) &checksum))
|
||||
while (g_hash_table_iter_next (&iter, (gpointer *) &c_ref, (gpointer *) &checksum))
|
||||
{
|
||||
GHashTable *ref_map = g_hash_table_lookup (collection_map, ref->collection_id);
|
||||
GHashTable *ref_map = g_hash_table_lookup (collection_map, c_ref->collection_id);
|
||||
|
||||
if (ref_map == NULL)
|
||||
{
|
||||
ref_map = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, NULL);
|
||||
g_hash_table_insert (collection_map, ref->collection_id, ref_map);
|
||||
g_hash_table_insert (collection_map, c_ref->collection_id, ref_map);
|
||||
}
|
||||
|
||||
g_hash_table_insert (ref_map, ref->ref_name, (gpointer) checksum);
|
||||
g_hash_table_insert (ref_map, c_ref->ref_name, (gpointer) checksum);
|
||||
}
|
||||
|
||||
g_autoptr(GVariantBuilder) collection_refs_builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sa(s(taya{sv}))}"));
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#include "ostree-repo-finder.h"
|
||||
#include "ostree-sepolicy.h"
|
||||
#include "ostree-gpg-verify-result.h"
|
||||
#include "ostree-sign.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
@@ -1067,6 +1068,14 @@ gboolean ostree_repo_static_delta_generate (OstreeRepo *self,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
_OSTREE_PUBLIC
|
||||
gboolean ostree_repo_static_delta_execute_offline_with_signature (OstreeRepo *self,
|
||||
GFile *dir_or_file,
|
||||
OstreeSign *sign,
|
||||
gboolean skip_validation,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
_OSTREE_PUBLIC
|
||||
gboolean ostree_repo_static_delta_execute_offline (OstreeRepo *self,
|
||||
GFile *dir_or_file,
|
||||
@@ -1074,6 +1083,13 @@ gboolean ostree_repo_static_delta_execute_offline (OstreeRepo
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
_OSTREE_PUBLIC
|
||||
gboolean ostree_repo_static_delta_verify_signature (OstreeRepo *self,
|
||||
const char *delta_id,
|
||||
OstreeSign *sign,
|
||||
char **out_success_message,
|
||||
GError **error);
|
||||
|
||||
_OSTREE_PUBLIC
|
||||
GHashTable *ostree_repo_traverse_new_reachable (void);
|
||||
|
||||
|
||||
@@ -365,7 +365,7 @@ _sign_detached_metadata_append (OstreeSign *self,
|
||||
signature_key,
|
||||
g_variant_builder_end (signature_builder));
|
||||
|
||||
return g_variant_dict_end (&metadata_dict);
|
||||
return g_variant_ref_sink (g_variant_dict_end (&metadata_dict));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -348,7 +348,7 @@ soup_uri_new_with_base (SoupURI *base, const char *uri_string)
|
||||
{
|
||||
SoupURI *uri, fixed_base;
|
||||
const char *end, *hash, *colon, *at, *path, *question;
|
||||
const char *p, *hostend;
|
||||
const char *c, *hostend;
|
||||
gboolean remove_dot_segments = TRUE;
|
||||
int len;
|
||||
|
||||
@@ -402,17 +402,17 @@ soup_uri_new_with_base (SoupURI *base, const char *uri_string)
|
||||
}
|
||||
|
||||
/* Find scheme */
|
||||
p = uri_string;
|
||||
while (p < end && (g_ascii_isalpha (*p) ||
|
||||
(p > uri_string && (g_ascii_isdigit (*p) ||
|
||||
*p == '.' ||
|
||||
*p == '+' ||
|
||||
*p == '-'))))
|
||||
p++;
|
||||
c = uri_string;
|
||||
while (c < end && (g_ascii_isalpha (*c) ||
|
||||
(c > uri_string && (g_ascii_isdigit (*c) ||
|
||||
*c == '.' ||
|
||||
*c == '+' ||
|
||||
*c == '-'))))
|
||||
c++;
|
||||
|
||||
if (p > uri_string && *p == ':') {
|
||||
uri->scheme = soup_uri_parse_scheme (uri_string, p - uri_string);
|
||||
uri_string = p + 1;
|
||||
if (c > uri_string && *c == ':') {
|
||||
uri->scheme = soup_uri_parse_scheme (uri_string, c - uri_string);
|
||||
uri_string = c + 1;
|
||||
}
|
||||
|
||||
if (uri_string == end && !base && !uri->fragment) {
|
||||
|
||||
@@ -298,6 +298,8 @@ cleanup_old_deployments (OstreeSysroot *self,
|
||||
g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
|
||||
g_autoptr(GHashTable) active_boot_checksums =
|
||||
g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
|
||||
g_autoptr(GHashTable) active_overlay_initrds =
|
||||
g_hash_table_new (g_str_hash, g_str_equal); /* borrows from deployment's bootconfig */
|
||||
for (guint i = 0; i < self->deployments->len; i++)
|
||||
{
|
||||
OstreeDeployment *deployment = self->deployments->pdata[i];
|
||||
@@ -306,6 +308,11 @@ cleanup_old_deployments (OstreeSysroot *self,
|
||||
/* Transfer ownership */
|
||||
g_hash_table_replace (active_deployment_dirs, deployment_path, deployment_path);
|
||||
g_hash_table_replace (active_boot_checksums, bootcsum, bootcsum);
|
||||
|
||||
OstreeBootconfigParser *bootconfig = ostree_deployment_get_bootconfig (deployment);
|
||||
char **initrds = ostree_bootconfig_parser_get_overlay_initrds (bootconfig);
|
||||
for (char **it = initrds; it && *it; it++)
|
||||
g_hash_table_add (active_overlay_initrds, (char*)glnx_basename (*it));
|
||||
}
|
||||
|
||||
/* Find all deployment directories, both active and inactive */
|
||||
@@ -349,6 +356,42 @@ cleanup_old_deployments (OstreeSysroot *self,
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Clean up overlay initrds */
|
||||
glnx_autofd int overlays_dfd =
|
||||
glnx_opendirat_with_errno (self->sysroot_fd, _OSTREE_SYSROOT_INITRAMFS_OVERLAYS, FALSE);
|
||||
if (overlays_dfd < 0)
|
||||
{
|
||||
if (errno != ENOENT)
|
||||
return glnx_throw_errno_prefix (error, "open(initrd_overlays)");
|
||||
}
|
||||
else
|
||||
{
|
||||
g_autoptr(GPtrArray) initrds_to_delete = g_ptr_array_new_with_free_func (g_free);
|
||||
g_auto(GLnxDirFdIterator) dfd_iter = { 0, };
|
||||
if (!glnx_dirfd_iterator_init_at (overlays_dfd, ".", TRUE, &dfd_iter, error))
|
||||
return FALSE;
|
||||
while (TRUE)
|
||||
{
|
||||
struct dirent *dent;
|
||||
if (!glnx_dirfd_iterator_next_dent_ensure_dtype (&dfd_iter, &dent, cancellable, error))
|
||||
return FALSE;
|
||||
if (dent == NULL)
|
||||
break;
|
||||
|
||||
/* there shouldn't be other file types there, but let's be conservative */
|
||||
if (dent->d_type != DT_REG)
|
||||
continue;
|
||||
|
||||
if (!g_hash_table_lookup (active_overlay_initrds, dent->d_name))
|
||||
g_ptr_array_add (initrds_to_delete, g_strdup (dent->d_name));
|
||||
}
|
||||
for (guint i = 0; i < initrds_to_delete->len; i++)
|
||||
{
|
||||
if (!ot_ensure_unlinked_at (overlays_dfd, initrds_to_delete->pdata[i], error))
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
@@ -272,13 +272,13 @@ checksum_dir_recurse (int dfd,
|
||||
}
|
||||
else
|
||||
{
|
||||
int fd;
|
||||
glnx_autofd int fd = -1;
|
||||
|
||||
if (!ot_openat_ignore_enoent (dfditer.fd, d_name, &fd, error))
|
||||
return FALSE;
|
||||
if (fd != -1)
|
||||
{
|
||||
g_autoptr(GInputStream) in = g_unix_input_stream_new (fd, FALSE);
|
||||
g_autoptr(GInputStream) in = g_unix_input_stream_new (glnx_steal_fd (&fd), TRUE);
|
||||
if (!ot_gio_splice_update_checksum (NULL, in, checksum, cancellable, error))
|
||||
return FALSE;
|
||||
}
|
||||
@@ -313,7 +313,7 @@ copy_dir_recurse (int src_parent_dfd,
|
||||
|
||||
if (!dirfd_copy_attributes_and_xattrs (src_parent_dfd, name, src_dfd_iter.fd, dest_dfd,
|
||||
flags, cancellable, error))
|
||||
return FALSE;
|
||||
return glnx_prefix_error (error, "Copying attributes of %s", name);
|
||||
|
||||
while (TRUE)
|
||||
{
|
||||
@@ -340,7 +340,7 @@ copy_dir_recurse (int src_parent_dfd,
|
||||
dest_dfd, dent->d_name,
|
||||
sysroot_flags_to_copy_flags (GLNX_FILE_COPY_OVERWRITE, flags),
|
||||
cancellable, error))
|
||||
return FALSE;
|
||||
return glnx_prefix_error (error, "Copying %s", dent->d_name);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -488,7 +488,7 @@ copy_modified_config_file (int orig_etc_fd,
|
||||
new_etc_fd, path,
|
||||
sysroot_flags_to_copy_flags (GLNX_FILE_COPY_OVERWRITE, flags),
|
||||
cancellable, error))
|
||||
return FALSE;
|
||||
return glnx_prefix_error (error, "Copying %s", path);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1028,7 +1028,8 @@ _ostree_kernel_layout_new (void)
|
||||
|
||||
/* See get_kernel_from_tree() below */
|
||||
static gboolean
|
||||
get_kernel_from_tree_usrlib_modules (int deployment_dfd,
|
||||
get_kernel_from_tree_usrlib_modules (OstreeSysroot *sysroot,
|
||||
int deployment_dfd,
|
||||
OstreeKernelLayout **out_layout,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
@@ -1137,37 +1138,41 @@ get_kernel_from_tree_usrlib_modules (int deployment_dfd,
|
||||
g_clear_object (&in);
|
||||
glnx_close_fd (&fd);
|
||||
|
||||
/* Check for /usr/lib/modules/$kver/devicetree first, if it does not
|
||||
* exist check for /usr/lib/modules/$kver/dtb/ directory.
|
||||
*/
|
||||
if (!ot_openat_ignore_enoent (ret_layout->boot_dfd, "devicetree", &fd, error))
|
||||
return FALSE;
|
||||
if (fd != -1)
|
||||
/* Testing aid for https://github.com/ostreedev/ostree/issues/2154 */
|
||||
const gboolean no_dtb = (sysroot->debug_flags & OSTREE_SYSROOT_DEBUG_TEST_NO_DTB) > 0;
|
||||
if (!no_dtb)
|
||||
{
|
||||
ret_layout->devicetree_srcpath = g_strdup ("devicetree");
|
||||
ret_layout->devicetree_namever = g_strdup_printf ("devicetree-%s", kver);
|
||||
in = g_unix_input_stream_new (fd, FALSE);
|
||||
if (!ot_gio_splice_update_checksum (NULL, in, &checksum, cancellable, error))
|
||||
/* Check for /usr/lib/modules/$kver/devicetree first, if it does not
|
||||
* exist check for /usr/lib/modules/$kver/dtb/ directory.
|
||||
*/
|
||||
if (!ot_openat_ignore_enoent (ret_layout->boot_dfd, "devicetree", &fd, error))
|
||||
return FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
struct stat stbuf;
|
||||
/* Check for dtb directory */
|
||||
if (!glnx_fstatat_allow_noent (ret_layout->boot_dfd, "dtb", &stbuf, 0, error))
|
||||
return FALSE;
|
||||
|
||||
if (errno == 0 && S_ISDIR (stbuf.st_mode))
|
||||
if (fd != -1)
|
||||
{
|
||||
/* devicetree_namever set to NULL indicates a complete directory */
|
||||
ret_layout->devicetree_srcpath = g_strdup ("dtb");
|
||||
ret_layout->devicetree_namever = NULL;
|
||||
|
||||
if (!checksum_dir_recurse(ret_layout->boot_dfd, "dtb", &checksum, cancellable, error))
|
||||
ret_layout->devicetree_srcpath = g_strdup ("devicetree");
|
||||
ret_layout->devicetree_namever = g_strdup_printf ("devicetree-%s", kver);
|
||||
in = g_unix_input_stream_new (fd, FALSE);
|
||||
if (!ot_gio_splice_update_checksum (NULL, in, &checksum, cancellable, error))
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
struct stat stbuf;
|
||||
/* Check for dtb directory */
|
||||
if (!glnx_fstatat_allow_noent (ret_layout->boot_dfd, "dtb", &stbuf, 0, error))
|
||||
return FALSE;
|
||||
|
||||
if (errno == 0 && S_ISDIR (stbuf.st_mode))
|
||||
{
|
||||
/* devicetree_namever set to NULL indicates a complete directory */
|
||||
ret_layout->devicetree_srcpath = g_strdup ("dtb");
|
||||
ret_layout->devicetree_namever = NULL;
|
||||
|
||||
if (!checksum_dir_recurse(ret_layout->boot_dfd, "dtb", &checksum, cancellable, error))
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
g_clear_object (&in);
|
||||
glnx_close_fd (&fd);
|
||||
|
||||
@@ -1336,7 +1341,8 @@ get_kernel_from_tree_legacy_layouts (int deployment_dfd,
|
||||
* initramfs there, so we need to look in /usr/lib/ostree-boot first.
|
||||
*/
|
||||
static gboolean
|
||||
get_kernel_from_tree (int deployment_dfd,
|
||||
get_kernel_from_tree (OstreeSysroot *sysroot,
|
||||
int deployment_dfd,
|
||||
OstreeKernelLayout **out_layout,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
@@ -1345,7 +1351,7 @@ get_kernel_from_tree (int deployment_dfd,
|
||||
g_autoptr(OstreeKernelLayout) legacy_layout = NULL;
|
||||
|
||||
/* First, gather from usr/lib/modules/$kver if it exists */
|
||||
if (!get_kernel_from_tree_usrlib_modules (deployment_dfd, &usrlib_modules_layout, cancellable, error))
|
||||
if (!get_kernel_from_tree_usrlib_modules (sysroot, deployment_dfd, &usrlib_modules_layout, cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
/* Gather the legacy layout */
|
||||
@@ -1761,7 +1767,7 @@ install_deployment_kernel (OstreeSysroot *sysroot,
|
||||
|
||||
/* Find the kernel/initramfs/devicetree in the tree */
|
||||
g_autoptr(OstreeKernelLayout) kernel_layout = NULL;
|
||||
if (!get_kernel_from_tree (deployment_dfd, &kernel_layout,
|
||||
if (!get_kernel_from_tree (sysroot, deployment_dfd, &kernel_layout,
|
||||
cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
@@ -1771,7 +1777,6 @@ install_deployment_kernel (OstreeSysroot *sysroot,
|
||||
|
||||
const char *osname = ostree_deployment_get_osname (deployment);
|
||||
const char *bootcsum = ostree_deployment_get_bootcsum (deployment);
|
||||
g_assert_cmpstr (kernel_layout->bootcsum, ==, bootcsum);
|
||||
g_autofree char *bootcsumdir = g_strdup_printf ("ostree/%s-%s", osname, bootcsum);
|
||||
g_autofree char *bootconfdir = g_strdup_printf ("loader.%d/entries", new_bootversion);
|
||||
g_autofree char *bootconf_name = g_strdup_printf ("ostree-%d-%s.conf",
|
||||
@@ -1854,6 +1859,47 @@ install_deployment_kernel (OstreeSysroot *sysroot,
|
||||
}
|
||||
}
|
||||
|
||||
g_autoptr(GPtrArray) overlay_initrds = NULL;
|
||||
for (char **it = _ostree_deployment_get_overlay_initrds (deployment); it && *it; it++)
|
||||
{
|
||||
char *checksum = *it;
|
||||
|
||||
/* Overlay initrds are not part of the bootcsum dir; they're not part of the tree
|
||||
* proper. Instead they're in /boot/ostree/initramfs-overlays/ named by their csum.
|
||||
* Doing it this way allows sharing the same bootcsum dir for multiple deployments
|
||||
* with the only change being in overlay initrds (or conversely, the same overlay
|
||||
* across different boocsums). Eventually, it'd be nice to have an OSTree repo in
|
||||
* /boot itself and drop the boocsum dir concept entirely. */
|
||||
|
||||
g_autofree char *destpath =
|
||||
g_strdup_printf ("/" _OSTREE_SYSROOT_BOOT_INITRAMFS_OVERLAYS "/%s.img", checksum);
|
||||
const char *rel_destpath = destpath + 1;
|
||||
|
||||
/* lazily allocate array and create dir so we don't pollute /boot if not needed */
|
||||
if (overlay_initrds == NULL)
|
||||
{
|
||||
overlay_initrds = g_ptr_array_new_with_free_func (g_free);
|
||||
|
||||
if (!glnx_shutil_mkdir_p_at (boot_dfd, _OSTREE_SYSROOT_BOOT_INITRAMFS_OVERLAYS,
|
||||
0755, cancellable, error))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!glnx_fstatat_allow_noent (boot_dfd, rel_destpath, NULL, 0, error))
|
||||
return FALSE;
|
||||
if (errno == ENOENT)
|
||||
{
|
||||
g_autofree char *srcpath =
|
||||
g_strdup_printf (_OSTREE_SYSROOT_RUNSTATE_STAGED_INITRDS_DIR "/%s", checksum);
|
||||
if (!install_into_boot (repo, sepolicy, AT_FDCWD, srcpath, boot_dfd, rel_destpath,
|
||||
cancellable, error))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* these are used lower down to populate the bootconfig */
|
||||
g_ptr_array_add (overlay_initrds, g_steal_pointer (&destpath));
|
||||
}
|
||||
|
||||
g_autofree char *contents = NULL;
|
||||
if (!glnx_fstatat_allow_noent (deployment_dfd, "usr/lib/os-release", &stbuf, 0, error))
|
||||
return FALSE;
|
||||
@@ -1933,6 +1979,12 @@ install_deployment_kernel (OstreeSysroot *sysroot,
|
||||
g_autofree char * initrd_boot_relpath =
|
||||
g_strconcat ("/", bootcsumdir, "/", kernel_layout->initramfs_namever, NULL);
|
||||
ostree_bootconfig_parser_set (bootconfig, "initrd", initrd_boot_relpath);
|
||||
|
||||
if (overlay_initrds)
|
||||
{
|
||||
g_ptr_array_add (overlay_initrds, NULL);
|
||||
ostree_bootconfig_parser_set_overlay_initrds (bootconfig, (char**)overlay_initrds->pdata);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1945,8 +1997,8 @@ install_deployment_kernel (OstreeSysroot *sysroot,
|
||||
|
||||
if (kernel_layout->devicetree_namever)
|
||||
{
|
||||
g_autofree char * boot_relpath = g_strconcat ("/", bootcsumdir, "/", kernel_layout->devicetree_namever, NULL);
|
||||
ostree_bootconfig_parser_set (bootconfig, "devicetree", boot_relpath);
|
||||
g_autofree char * dt_boot_relpath = g_strconcat ("/", bootcsumdir, "/", kernel_layout->devicetree_namever, NULL);
|
||||
ostree_bootconfig_parser_set (bootconfig, "devicetree", dt_boot_relpath);
|
||||
}
|
||||
else if (kernel_layout->devicetree_srcpath)
|
||||
{
|
||||
@@ -1954,8 +2006,8 @@ install_deployment_kernel (OstreeSysroot *sysroot,
|
||||
* want to point to a whole directory of device trees.
|
||||
* See: https://github.com/ostreedev/ostree/issues/1900
|
||||
*/
|
||||
g_autofree char * boot_relpath = g_strconcat ("/", bootcsumdir, "/", kernel_layout->devicetree_srcpath, NULL);
|
||||
ostree_bootconfig_parser_set (bootconfig, "fdtdir", boot_relpath);
|
||||
g_autofree char * dt_boot_relpath = g_strconcat ("/", bootcsumdir, "/", kernel_layout->devicetree_srcpath, NULL);
|
||||
ostree_bootconfig_parser_set (bootconfig, "fdtdir", dt_boot_relpath);
|
||||
}
|
||||
|
||||
/* Note this is parsed in ostree-impl-system-generator.c */
|
||||
@@ -2130,6 +2182,10 @@ deployment_bootconfigs_equal (OstreeRepo *repo,
|
||||
if (strcmp (a_bootcsum, b_bootcsum) != 0)
|
||||
return FALSE;
|
||||
|
||||
/* same initrd overlays? */
|
||||
if (g_strcmp0 (a->overlay_initrds_id, b->overlay_initrds_id) != 0)
|
||||
return FALSE;
|
||||
|
||||
/* same kargs? */
|
||||
g_autofree char *a_boot_options_without_ostree = get_deployment_nonostree_kargs (a);
|
||||
g_autofree char *b_boot_options_without_ostree = get_deployment_nonostree_kargs (b);
|
||||
@@ -2683,7 +2739,7 @@ sysroot_initialize_deployment (OstreeSysroot *self,
|
||||
const char *osname,
|
||||
const char *revision,
|
||||
GKeyFile *origin,
|
||||
char **override_kernel_argv,
|
||||
OstreeSysrootDeployTreeOpts *opts,
|
||||
OstreeDeployment **out_new_deployment,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
@@ -2711,12 +2767,13 @@ sysroot_initialize_deployment (OstreeSysroot *self,
|
||||
return FALSE;
|
||||
|
||||
g_autoptr(OstreeKernelLayout) kernel_layout = NULL;
|
||||
if (!get_kernel_from_tree (deployment_dfd, &kernel_layout,
|
||||
if (!get_kernel_from_tree (self, deployment_dfd, &kernel_layout,
|
||||
cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
_ostree_deployment_set_bootcsum (new_deployment, kernel_layout->bootcsum);
|
||||
_ostree_deployment_set_bootconfig_from_kargs (new_deployment, override_kernel_argv);
|
||||
_ostree_deployment_set_bootconfig_from_kargs (new_deployment, opts ? opts->override_kernel_argv : NULL);
|
||||
_ostree_deployment_set_overlay_initrds (new_deployment, opts ? opts->overlay_initrds : NULL);
|
||||
|
||||
if (!prepare_deployment_etc (self, repo, new_deployment, deployment_dfd,
|
||||
cancellable, error))
|
||||
@@ -2847,6 +2904,53 @@ sysroot_finalize_deployment (OstreeSysroot *self,
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* ostree_sysroot_deploy_tree_with_options:
|
||||
* @self: Sysroot
|
||||
* @osname: (allow-none): osname to use for merge deployment
|
||||
* @revision: Checksum to add
|
||||
* @origin: (allow-none): Origin to use for upgrades
|
||||
* @provided_merge_deployment: (allow-none): Use this deployment for merge path
|
||||
* @opts: (allow-none): Options
|
||||
* @out_new_deployment: (out): The new deployment path
|
||||
* @cancellable: Cancellable
|
||||
* @error: Error
|
||||
*
|
||||
* Check out deployment tree with revision @revision, performing a 3
|
||||
* way merge with @provided_merge_deployment for configuration.
|
||||
*
|
||||
* When booted into the sysroot, you should use the
|
||||
* ostree_sysroot_stage_tree() API instead.
|
||||
*
|
||||
* Since: 2020.7
|
||||
*/
|
||||
gboolean
|
||||
ostree_sysroot_deploy_tree_with_options (OstreeSysroot *self,
|
||||
const char *osname,
|
||||
const char *revision,
|
||||
GKeyFile *origin,
|
||||
OstreeDeployment *provided_merge_deployment,
|
||||
OstreeSysrootDeployTreeOpts *opts,
|
||||
OstreeDeployment **out_new_deployment,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
if (!_ostree_sysroot_ensure_writable (self, error))
|
||||
return FALSE;
|
||||
|
||||
g_autoptr(OstreeDeployment) deployment = NULL;
|
||||
if (!sysroot_initialize_deployment (self, osname, revision, origin, opts,
|
||||
&deployment, cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
if (!sysroot_finalize_deployment (self, deployment, provided_merge_deployment,
|
||||
cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
*out_new_deployment = g_steal_pointer (&deployment);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* ostree_sysroot_deploy_tree:
|
||||
* @self: Sysroot
|
||||
@@ -2859,11 +2963,9 @@ sysroot_finalize_deployment (OstreeSysroot *self,
|
||||
* @cancellable: Cancellable
|
||||
* @error: Error
|
||||
*
|
||||
* Check out deployment tree with revision @revision, performing a 3
|
||||
* way merge with @provided_merge_deployment for configuration.
|
||||
* Older version of ostree_sysroot_stage_tree_with_options().
|
||||
*
|
||||
* When booted into the sysroot, you should use the
|
||||
* ostree_sysroot_stage_tree() API instead.
|
||||
* Since: 2018.5
|
||||
*/
|
||||
gboolean
|
||||
ostree_sysroot_deploy_tree (OstreeSysroot *self,
|
||||
@@ -2876,20 +2978,10 @@ ostree_sysroot_deploy_tree (OstreeSysroot *self,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
if (!_ostree_sysroot_ensure_writable (self, error))
|
||||
return FALSE;
|
||||
|
||||
g_autoptr(OstreeDeployment) deployment = NULL;
|
||||
if (!sysroot_initialize_deployment (self, osname, revision, origin, override_kernel_argv,
|
||||
&deployment, cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
if (!sysroot_finalize_deployment (self, deployment, provided_merge_deployment,
|
||||
cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
*out_new_deployment = g_steal_pointer (&deployment);
|
||||
return TRUE;
|
||||
OstreeSysrootDeployTreeOpts opts = { .override_kernel_argv = override_kernel_argv };
|
||||
return ostree_sysroot_deploy_tree_with_options (self, osname, revision, origin,
|
||||
provided_merge_deployment, &opts,
|
||||
out_new_deployment, cancellable, error);
|
||||
}
|
||||
|
||||
/* Serialize information about a deployment to a variant, used by the staging
|
||||
@@ -2951,6 +3043,63 @@ _ostree_sysroot_deserialize_deployment_from_variant (GVariant *v,
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* ostree_sysroot_stage_overlay_initrd:
|
||||
* @self: Sysroot
|
||||
* @fd: (transfer none): File descriptor to overlay initrd
|
||||
* @out_checksum: (out) (transfer full): Overlay initrd checksum
|
||||
* @cancellable: Cancellable
|
||||
* @error: Error
|
||||
*
|
||||
* Stage an overlay initrd to be used in an upcoming deployment. Returns a checksum which
|
||||
* can be passed to ostree_sysroot_deploy_tree_with_options() or
|
||||
* ostree_sysroot_stage_tree_with_options() via the `overlay_initrds` array option.
|
||||
*
|
||||
* Since: 2020.7
|
||||
*/
|
||||
gboolean
|
||||
ostree_sysroot_stage_overlay_initrd (OstreeSysroot *self,
|
||||
int fd,
|
||||
char **out_checksum,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
g_return_val_if_fail (fd != -1, FALSE);
|
||||
g_return_val_if_fail (out_checksum != NULL, FALSE);
|
||||
|
||||
if (!glnx_shutil_mkdir_p_at (AT_FDCWD, _OSTREE_SYSROOT_RUNSTATE_STAGED_INITRDS_DIR,
|
||||
0755, cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
glnx_autofd int staged_initrds_dfd = -1;
|
||||
if (!glnx_opendirat (AT_FDCWD, _OSTREE_SYSROOT_RUNSTATE_STAGED_INITRDS_DIR, FALSE,
|
||||
&staged_initrds_dfd, error))
|
||||
return FALSE;
|
||||
|
||||
g_auto(GLnxTmpfile) overlay_initrd = { 0, };
|
||||
if (!glnx_open_tmpfile_linkable_at (staged_initrds_dfd, ".", O_WRONLY | O_CLOEXEC,
|
||||
&overlay_initrd, error))
|
||||
return FALSE;
|
||||
|
||||
char checksum[_OSTREE_SHA256_STRING_LEN+1];
|
||||
{
|
||||
g_autoptr(GOutputStream) output = g_unix_output_stream_new (overlay_initrd.fd, FALSE);
|
||||
g_autoptr(GInputStream) input = g_unix_input_stream_new (fd, FALSE);
|
||||
g_autofree guchar *digest = NULL;
|
||||
if (!ot_gio_splice_get_checksum (output, input, &digest, cancellable, error))
|
||||
return FALSE;
|
||||
ot_bin2hex (checksum, (guint8*)digest, _OSTREE_SHA256_DIGEST_LEN);
|
||||
}
|
||||
|
||||
if (!glnx_link_tmpfile_at (&overlay_initrd, GLNX_LINK_TMPFILE_REPLACE,
|
||||
staged_initrds_dfd, checksum, error))
|
||||
return FALSE;
|
||||
|
||||
*out_checksum = g_strdup (checksum);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* ostree_sysroot_stage_tree:
|
||||
* @self: Sysroot
|
||||
@@ -2963,8 +3112,7 @@ _ostree_sysroot_deserialize_deployment_from_variant (GVariant *v,
|
||||
* @cancellable: Cancellable
|
||||
* @error: Error
|
||||
*
|
||||
* Like ostree_sysroot_deploy_tree(), but "finalization" only occurs at OS
|
||||
* shutdown time.
|
||||
* Older version of ostree_sysroot_stage_tree_with_options().
|
||||
*
|
||||
* Since: 2018.5
|
||||
*/
|
||||
@@ -2978,6 +3126,41 @@ ostree_sysroot_stage_tree (OstreeSysroot *self,
|
||||
OstreeDeployment **out_new_deployment,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
OstreeSysrootDeployTreeOpts opts = { .override_kernel_argv = override_kernel_argv };
|
||||
return ostree_sysroot_stage_tree_with_options (self, osname, revision, origin,
|
||||
merge_deployment, &opts,
|
||||
out_new_deployment, cancellable, error);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* ostree_sysroot_stage_tree_with_options:
|
||||
* @self: Sysroot
|
||||
* @osname: (allow-none): osname to use for merge deployment
|
||||
* @revision: Checksum to add
|
||||
* @origin: (allow-none): Origin to use for upgrades
|
||||
* @merge_deployment: (allow-none): Use this deployment for merge path
|
||||
* @opts: Options
|
||||
* @out_new_deployment: (out): The new deployment path
|
||||
* @cancellable: Cancellable
|
||||
* @error: Error
|
||||
*
|
||||
* Like ostree_sysroot_deploy_tree(), but "finalization" only occurs at OS
|
||||
* shutdown time.
|
||||
*
|
||||
* Since: 2020.7
|
||||
*/
|
||||
gboolean
|
||||
ostree_sysroot_stage_tree_with_options (OstreeSysroot *self,
|
||||
const char *osname,
|
||||
const char *revision,
|
||||
GKeyFile *origin,
|
||||
OstreeDeployment *merge_deployment,
|
||||
OstreeSysrootDeployTreeOpts *opts,
|
||||
OstreeDeployment **out_new_deployment,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
if (!_ostree_sysroot_ensure_writable (self, error))
|
||||
return FALSE;
|
||||
@@ -3009,8 +3192,8 @@ ostree_sysroot_stage_tree (OstreeSysroot *self,
|
||||
} /* OSTREE_SYSROOT_DEBUG_TEST_STAGED_PATH */
|
||||
|
||||
g_autoptr(OstreeDeployment) deployment = NULL;
|
||||
if (!sysroot_initialize_deployment (self, osname, revision, origin, override_kernel_argv,
|
||||
&deployment, cancellable, error))
|
||||
if (!sysroot_initialize_deployment (self, osname, revision, origin, opts, &deployment,
|
||||
cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
/* Write out the origin file using the sepolicy from the non-merged root for
|
||||
@@ -3045,9 +3228,12 @@ ostree_sysroot_stage_tree (OstreeSysroot *self,
|
||||
g_variant_builder_add (builder, "{sv}", "merge-deployment",
|
||||
serialize_deployment_to_variant (merge_deployment));
|
||||
|
||||
if (override_kernel_argv)
|
||||
if (opts && opts->override_kernel_argv)
|
||||
g_variant_builder_add (builder, "{sv}", "kargs",
|
||||
g_variant_new_strv ((const char *const*)override_kernel_argv, -1));
|
||||
g_variant_new_strv ((const char *const*)opts->override_kernel_argv, -1));
|
||||
if (opts && opts->overlay_initrds)
|
||||
g_variant_builder_add (builder, "{sv}", "overlay-initrds",
|
||||
g_variant_new_strv ((const char *const*)opts->overlay_initrds, -1));
|
||||
|
||||
const char *parent = dirname (strdupa (_OSTREE_SYSROOT_RUNSTATE_STAGED));
|
||||
if (!glnx_shutil_mkdir_p_at (AT_FDCWD, parent, 0755, cancellable, error))
|
||||
|
||||
@@ -38,6 +38,7 @@ typedef enum {
|
||||
/* This is a temporary flag until we fully drop the explicit `systemctl start
|
||||
* ostree-finalize-staged.service` so that tests can exercise the new path unit. */
|
||||
OSTREE_SYSROOT_DEBUG_TEST_STAGED_PATH = 1 << 3,
|
||||
OSTREE_SYSROOT_DEBUG_TEST_NO_DTB = 1 << 4, /* https://github.com/ostreedev/ostree/issues/2154 */
|
||||
} OstreeSysrootDebugFlags;
|
||||
|
||||
typedef enum {
|
||||
@@ -83,10 +84,14 @@ struct OstreeSysroot {
|
||||
/* We keep some transient state in /run */
|
||||
#define _OSTREE_SYSROOT_RUNSTATE_STAGED "/run/ostree/staged-deployment"
|
||||
#define _OSTREE_SYSROOT_RUNSTATE_STAGED_LOCKED "/run/ostree/staged-deployment-locked"
|
||||
#define _OSTREE_SYSROOT_RUNSTATE_STAGED_INITRDS_DIR "/run/ostree/staged-initrds/"
|
||||
#define _OSTREE_SYSROOT_DEPLOYMENT_RUNSTATE_DIR "/run/ostree/deployment-state/"
|
||||
#define _OSTREE_SYSROOT_DEPLOYMENT_RUNSTATE_FLAG_DEVELOPMENT "unlocked-development"
|
||||
#define _OSTREE_SYSROOT_DEPLOYMENT_RUNSTATE_FLAG_TRANSIENT "unlocked-transient"
|
||||
|
||||
#define _OSTREE_SYSROOT_BOOT_INITRAMFS_OVERLAYS "ostree/initramfs-overlays"
|
||||
#define _OSTREE_SYSROOT_INITRAMFS_OVERLAYS "boot/" _OSTREE_SYSROOT_BOOT_INITRAMFS_OVERLAYS
|
||||
|
||||
gboolean
|
||||
_ostree_sysroot_ensure_writable (OstreeSysroot *self,
|
||||
GError **error);
|
||||
|
||||
@@ -190,6 +190,7 @@ ostree_sysroot_init (OstreeSysroot *self)
|
||||
{ "test-fifreeze", OSTREE_SYSROOT_DEBUG_TEST_FIFREEZE },
|
||||
{ "no-xattrs", OSTREE_SYSROOT_DEBUG_NO_XATTRS },
|
||||
{ "test-staged-path", OSTREE_SYSROOT_DEBUG_TEST_STAGED_PATH },
|
||||
{ "no-dtb", OSTREE_SYSROOT_DEBUG_TEST_NO_DTB },
|
||||
};
|
||||
|
||||
self->debug_flags = g_parse_debug_string (g_getenv ("OSTREE_SYSROOT_DEBUG"),
|
||||
@@ -814,6 +815,24 @@ list_deployments_process_one_boot_entry (OstreeSysroot *self,
|
||||
return FALSE;
|
||||
|
||||
ostree_deployment_set_bootconfig (deployment, config);
|
||||
char **overlay_initrds = ostree_bootconfig_parser_get_overlay_initrds (config);
|
||||
g_autoptr(GPtrArray) initrds_chksums = NULL;
|
||||
for (char **it = overlay_initrds; it && *it; it++)
|
||||
{
|
||||
const char *basename = glnx_basename (*it);
|
||||
if (strlen (basename) != (_OSTREE_SHA256_STRING_LEN + strlen (".img")))
|
||||
return glnx_throw (error, "Malformed overlay initrd filename: %s", basename);
|
||||
|
||||
if (!initrds_chksums) /* lazy init */
|
||||
initrds_chksums = g_ptr_array_new_full (g_strv_length (overlay_initrds), g_free);
|
||||
g_ptr_array_add (initrds_chksums, g_strndup (basename, _OSTREE_SHA256_STRING_LEN));
|
||||
}
|
||||
|
||||
if (initrds_chksums)
|
||||
{
|
||||
g_ptr_array_add (initrds_chksums, NULL);
|
||||
_ostree_deployment_set_overlay_initrds (deployment, (char**)initrds_chksums->pdata);
|
||||
}
|
||||
|
||||
g_ptr_array_add (inout_deployments, g_object_ref (deployment));
|
||||
return TRUE;
|
||||
@@ -966,8 +985,10 @@ _ostree_sysroot_reload_staged (OstreeSysroot *self,
|
||||
/* Parse it */
|
||||
g_autoptr(GVariant) target = NULL;
|
||||
g_autofree char **kargs = NULL;
|
||||
g_autofree char **overlay_initrds = NULL;
|
||||
g_variant_dict_lookup (staged_deployment_dict, "target", "@a{sv}", &target);
|
||||
g_variant_dict_lookup (staged_deployment_dict, "kargs", "^a&s", &kargs);
|
||||
g_variant_dict_lookup (staged_deployment_dict, "overlay-initrds", "^a&s", &overlay_initrds);
|
||||
if (target)
|
||||
{
|
||||
g_autoptr(OstreeDeployment) staged =
|
||||
@@ -979,6 +1000,8 @@ _ostree_sysroot_reload_staged (OstreeSysroot *self,
|
||||
if (!load_origin (self, staged, NULL, error))
|
||||
return FALSE;
|
||||
|
||||
_ostree_deployment_set_overlay_initrds (staged, overlay_initrds);
|
||||
|
||||
self->staged_deployment = g_steal_pointer (&staged);
|
||||
self->staged_deployment_data = g_steal_pointer (&staged_deployment_data);
|
||||
/* We set this flag for ostree_deployment_is_staged() because that API
|
||||
|
||||
@@ -186,6 +186,21 @@ gboolean ostree_sysroot_write_deployments_with_options (OstreeSysroot *self,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
_OSTREE_PUBLIC
|
||||
gboolean ostree_sysroot_stage_overlay_initrd (OstreeSysroot *self,
|
||||
int fd,
|
||||
char **out_checksum,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
typedef struct {
|
||||
gboolean unused_bools[8];
|
||||
int unused_ints[8];
|
||||
char **override_kernel_argv;
|
||||
char **overlay_initrds;
|
||||
gpointer unused_ptrs[6];
|
||||
} OstreeSysrootDeployTreeOpts;
|
||||
|
||||
_OSTREE_PUBLIC
|
||||
gboolean ostree_sysroot_deploy_tree (OstreeSysroot *self,
|
||||
const char *osname,
|
||||
@@ -197,6 +212,17 @@ gboolean ostree_sysroot_deploy_tree (OstreeSysroot *self,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
_OSTREE_PUBLIC
|
||||
gboolean ostree_sysroot_deploy_tree_with_options (OstreeSysroot *self,
|
||||
const char *osname,
|
||||
const char *revision,
|
||||
GKeyFile *origin,
|
||||
OstreeDeployment *provided_merge_deployment,
|
||||
OstreeSysrootDeployTreeOpts *opts,
|
||||
OstreeDeployment **out_new_deployment,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
_OSTREE_PUBLIC
|
||||
gboolean ostree_sysroot_stage_tree (OstreeSysroot *self,
|
||||
const char *osname,
|
||||
@@ -208,6 +234,18 @@ gboolean ostree_sysroot_stage_tree (OstreeSysroot *self,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
_OSTREE_PUBLIC
|
||||
gboolean ostree_sysroot_stage_tree_with_options (OstreeSysroot *self,
|
||||
const char *osname,
|
||||
const char *revision,
|
||||
GKeyFile *origin,
|
||||
OstreeDeployment *merge_deployment,
|
||||
OstreeSysrootDeployTreeOpts *opts,
|
||||
OstreeDeployment **out_new_deployment,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
|
||||
_OSTREE_PUBLIC
|
||||
gboolean ostree_sysroot_deployment_set_mutable (OstreeSysroot *self,
|
||||
OstreeDeployment *deployment,
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
*
|
||||
* Since: 2017.4
|
||||
*/
|
||||
#define OSTREE_RELEASE_VERSION (6)
|
||||
#define OSTREE_RELEASE_VERSION (7)
|
||||
|
||||
/**
|
||||
* OSTREE_VERSION
|
||||
@@ -52,7 +52,7 @@
|
||||
*
|
||||
* Since: 2017.4
|
||||
*/
|
||||
#define OSTREE_VERSION (2020.6)
|
||||
#define OSTREE_VERSION (2020.7)
|
||||
|
||||
/**
|
||||
* OSTREE_VERSION_S:
|
||||
@@ -62,7 +62,7 @@
|
||||
*
|
||||
* Since: 2017.4
|
||||
*/
|
||||
#define OSTREE_VERSION_S "2020.6"
|
||||
#define OSTREE_VERSION_S "2020.7"
|
||||
|
||||
#define OSTREE_ENCODE_VERSION(year,release) \
|
||||
((year) << 16 | (release))
|
||||
|
||||
Reference in new issue
Block a user