New upstream version 2021.1
This commit is contained in:
commit
1c67002e63
82 files changed
+4382
-4088
No files matched your search
@@ -638,6 +638,12 @@ global:
|
||||
ostree_repo_gpg_sign_data;
|
||||
} LIBOSTREE_2020.7;
|
||||
|
||||
LIBOSTREE_2021.1 {
|
||||
global:
|
||||
ostree_commit_metadata_for_bootable;
|
||||
ostree_sysroot_require_booted_deployment;
|
||||
} LIBOSTREE_2020.8;
|
||||
|
||||
/* NOTE: Only add more content here in release commits! See the
|
||||
* comments at the top of this file.
|
||||
*/
|
||||
@@ -28,25 +28,26 @@
|
||||
|
||||
#include <string.h>
|
||||
|
||||
/* I only did some cursory research here, but it appears
|
||||
* that we only want to use "linux16" for x86 platforms.
|
||||
* At least, I got a report that "linux16" is definitely wrong
|
||||
* for ppc64. See
|
||||
* http://pkgs.fedoraproject.org/cgit/rpms/grub2.git/tree/0036-Use-linux16-when-appropriate-880840.patch?h=f25
|
||||
* https://bugzilla.redhat.com/show_bug.cgi?id=1108296
|
||||
* among others.
|
||||
/* Maintain backwards compatibility with legacy GRUB
|
||||
* installations that might rely on the -16 suffix
|
||||
* for real-mode booting.
|
||||
* Allow us to override this at build time if we're
|
||||
* using a modern GRUB installation.
|
||||
*/
|
||||
#if defined(__i386__) || defined(__x86_64__)
|
||||
#if !defined(WITH_MODERN_GRUB) && ( defined(__i386__) || defined(__x86_64__) )
|
||||
#define GRUB2_SUFFIX "16"
|
||||
#else
|
||||
#define GRUB2_SUFFIX ""
|
||||
#endif
|
||||
/* https://github.com/projectatomic/rpm-ostree-toolbox/issues/102#issuecomment-316483554
|
||||
* https://github.com/rhboot/grubby/blob/34b1436ccbd56eab8024314cab48f2fc880eef08/grubby.c#L63
|
||||
*
|
||||
* This is true at least on Fedora/Red Hat Enterprise Linux for aarch64.
|
||||
/* Maintain backwards compatibility with legacy GRUB
|
||||
* installations that might rely on the -efi suffix
|
||||
* for RHEL/fedora-based distros.
|
||||
* Allow us to override this at build time if we're
|
||||
* using a modern GRUB installation that makes the
|
||||
* suffix-less linux/initrd commands synonymous for
|
||||
* both EFI and BIOS booting.
|
||||
*/
|
||||
#if defined(__aarch64__)
|
||||
#if defined(WITH_MODERN_GRUB) || defined(__aarch64__)
|
||||
#define GRUB2_EFI_SUFFIX ""
|
||||
#else
|
||||
#define GRUB2_EFI_SUFFIX "efi"
|
||||
|
||||
@@ -33,9 +33,6 @@
|
||||
#include <sys/ioctl.h>
|
||||
#include <linux/fs.h>
|
||||
#include <ext2fs/ext2_fs.h>
|
||||
#ifdef HAVE_LINUX_FSVERITY_H
|
||||
#include <linux/fsverity.h>
|
||||
#endif
|
||||
|
||||
#include "otutil.h"
|
||||
#include "ostree.h"
|
||||
@@ -190,113 +187,6 @@ ot_security_smack_reset_fd (int fd)
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Wrapper around the fsverity ioctl, compressing the result to
|
||||
* "success, unsupported or error". This is used for /boot where
|
||||
* we enable verity if supported.
|
||||
* */
|
||||
gboolean
|
||||
_ostree_tmpf_fsverity_core (GLnxTmpfile *tmpf,
|
||||
_OstreeFeatureSupport fsverity_requested,
|
||||
gboolean *supported,
|
||||
GError **error)
|
||||
{
|
||||
/* Set this by default to simplify the code below */
|
||||
if (supported)
|
||||
*supported = FALSE;
|
||||
|
||||
if (fsverity_requested == _OSTREE_FEATURE_NO)
|
||||
return TRUE;
|
||||
|
||||
#ifdef HAVE_LINUX_FSVERITY_H
|
||||
GLNX_AUTO_PREFIX_ERROR ("fsverity", error);
|
||||
|
||||
/* fs-verity requires a read-only file descriptor */
|
||||
if (!glnx_tmpfile_reopen_rdonly (tmpf, error))
|
||||
return FALSE;
|
||||
|
||||
struct fsverity_enable_arg arg = { 0, };
|
||||
arg.version = 1;
|
||||
arg.hash_algorithm = FS_VERITY_HASH_ALG_SHA256; /* TODO configurable? */
|
||||
arg.block_size = 4096; /* FIXME query */
|
||||
arg.salt_size = 0; /* TODO store salt in ostree repo config */
|
||||
arg.salt_ptr = 0;
|
||||
arg.sig_size = 0; /* We don't currently expect use of in-kernel signature verification */
|
||||
arg.sig_ptr = 0;
|
||||
|
||||
if (ioctl (tmpf->fd, FS_IOC_ENABLE_VERITY, &arg) < 0)
|
||||
{
|
||||
switch (errno)
|
||||
{
|
||||
case ENOTTY:
|
||||
case EOPNOTSUPP:
|
||||
return TRUE;
|
||||
default:
|
||||
return glnx_throw_errno_prefix (error, "ioctl(FS_IOC_ENABLE_VERITY)");
|
||||
}
|
||||
}
|
||||
|
||||
if (supported)
|
||||
*supported = TRUE;
|
||||
#endif
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* Enable verity on a file, respecting the "wanted" and "supported" states.
|
||||
* The main idea here is to optimize out pointlessly calling the ioctl()
|
||||
* over and over in cases where it's not supported for the repo's filesystem,
|
||||
* as well as to support "opportunistic" use (requested and if filesystem supports).
|
||||
* */
|
||||
gboolean
|
||||
_ostree_tmpf_fsverity (OstreeRepo *self,
|
||||
GLnxTmpfile *tmpf,
|
||||
GError **error)
|
||||
{
|
||||
#ifdef HAVE_LINUX_FSVERITY_H
|
||||
g_mutex_lock (&self->txn_lock);
|
||||
_OstreeFeatureSupport fsverity_wanted = self->fs_verity_wanted;
|
||||
_OstreeFeatureSupport fsverity_supported = self->fs_verity_supported;
|
||||
g_mutex_unlock (&self->txn_lock);
|
||||
|
||||
switch (fsverity_wanted)
|
||||
{
|
||||
case _OSTREE_FEATURE_YES:
|
||||
{
|
||||
if (fsverity_supported == _OSTREE_FEATURE_NO)
|
||||
return glnx_throw (error, "fsverity required but filesystem does not support it");
|
||||
}
|
||||
break;
|
||||
case _OSTREE_FEATURE_MAYBE:
|
||||
break;
|
||||
case _OSTREE_FEATURE_NO:
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean supported = FALSE;
|
||||
if (!_ostree_tmpf_fsverity_core (tmpf, fsverity_wanted, &supported, error))
|
||||
return FALSE;
|
||||
|
||||
if (!supported)
|
||||
{
|
||||
if (G_UNLIKELY (fsverity_wanted == _OSTREE_FEATURE_YES))
|
||||
return glnx_throw (error, "fsverity required but filesystem does not support it");
|
||||
|
||||
/* If we got here, we must be trying "opportunistic" use of fs-verity */
|
||||
g_assert_cmpint (fsverity_wanted, ==, _OSTREE_FEATURE_MAYBE);
|
||||
g_mutex_lock (&self->txn_lock);
|
||||
self->fs_verity_supported = _OSTREE_FEATURE_NO;
|
||||
g_mutex_unlock (&self->txn_lock);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
g_mutex_lock (&self->txn_lock);
|
||||
self->fs_verity_supported = _OSTREE_FEATURE_YES;
|
||||
g_mutex_unlock (&self->txn_lock);
|
||||
#else
|
||||
g_assert_cmpint (self->fs_verity_wanted, !=, _OSTREE_FEATURE_YES);
|
||||
#endif
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* Given an O_TMPFILE regular file, link it into place. */
|
||||
gboolean
|
||||
_ostree_repo_commit_tmpf_final (OstreeRepo *self,
|
||||
|
||||
@@ -458,12 +458,43 @@ aic_get_xattrs (OstreeRepoArchiveImportContext *ctx,
|
||||
g_autoptr(GVariant) xattrs = NULL;
|
||||
const char *cb_path = abspath;
|
||||
|
||||
gboolean no_xattrs = ctx->modifier &&
|
||||
ctx->modifier->flags & OSTREE_REPO_COMMIT_MODIFIER_FLAGS_SKIP_XATTRS;
|
||||
|
||||
if (!no_xattrs && archive_entry_xattr_count (ctx->entry) > 0)
|
||||
{
|
||||
const char *name;
|
||||
const void *value;
|
||||
size_t size;
|
||||
|
||||
g_autoptr(GVariantBuilder) builder =
|
||||
ot_util_variant_builder_from_variant (xattrs,
|
||||
G_VARIANT_TYPE ("a(ayay)"));
|
||||
|
||||
archive_entry_xattr_reset (ctx->entry);
|
||||
while (archive_entry_xattr_next (
|
||||
ctx->entry, &name, &value, &size) == ARCHIVE_OK)
|
||||
{
|
||||
g_variant_builder_add (builder, "(@ay@ay)",
|
||||
g_variant_new_bytestring (name),
|
||||
g_variant_new_fixed_array (G_VARIANT_TYPE("y"),
|
||||
value, size, 1));
|
||||
}
|
||||
|
||||
xattrs = g_variant_builder_end (builder);
|
||||
g_variant_ref_sink (xattrs);
|
||||
}
|
||||
|
||||
if (ctx->opts->callback_with_entry_pathname)
|
||||
cb_path = archive_entry_pathname (ctx->entry);
|
||||
|
||||
if (ctx->modifier && ctx->modifier->xattr_callback)
|
||||
xattrs = ctx->modifier->xattr_callback (ctx->repo, cb_path, file_info,
|
||||
ctx->modifier->xattr_user_data);
|
||||
{
|
||||
if (xattrs)
|
||||
g_variant_unref (xattrs);
|
||||
xattrs = ctx->modifier->xattr_callback (ctx->repo, cb_path, file_info,
|
||||
ctx->modifier->xattr_user_data);
|
||||
}
|
||||
|
||||
if (ctx->modifier && ctx->modifier->sepolicy)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: LGPL-2.0+
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <gio/gfiledescriptorbased.h>
|
||||
#include <gio/gunixinputstream.h>
|
||||
#include "libglnx.h"
|
||||
#include "ostree.h"
|
||||
#include "ostree-core-private.h"
|
||||
#include "ostree-repo-os.h"
|
||||
#include "otutil.h"
|
||||
|
||||
/**
|
||||
* ostree_commit_metadata_for_bootable:
|
||||
* @root: Root filesystem to be committed
|
||||
* @dict: Dictionary to update
|
||||
*
|
||||
* Update provided @dict with standard metadata for bootable OSTree commits.
|
||||
* Since: 2021.1
|
||||
*/
|
||||
_OSTREE_PUBLIC
|
||||
gboolean
|
||||
ostree_commit_metadata_for_bootable (GFile *root, GVariantDict *dict, GCancellable *cancellable, GError **error)
|
||||
{
|
||||
g_autoptr(GFile) modules = g_file_resolve_relative_path (root, "usr/lib/modules");
|
||||
g_autoptr(GFileEnumerator) dir_enum
|
||||
= g_file_enumerate_children (modules, OSTREE_GIO_FAST_QUERYINFO,
|
||||
G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
|
||||
cancellable, error);
|
||||
if (!dir_enum)
|
||||
return glnx_prefix_error (error, "Opening usr/lib/modules");
|
||||
|
||||
g_autofree char *linux_release = NULL;
|
||||
while (TRUE)
|
||||
{
|
||||
GFileInfo *child_info;
|
||||
GFile *child_path;
|
||||
if (!g_file_enumerator_iterate (dir_enum, &child_info, &child_path,
|
||||
cancellable, error))
|
||||
return FALSE;
|
||||
if (child_info == NULL)
|
||||
break;
|
||||
if (g_file_info_get_file_type (child_info) != G_FILE_TYPE_DIRECTORY)
|
||||
continue;
|
||||
|
||||
g_autoptr(GFile) kernel_path = g_file_resolve_relative_path (child_path, "vmlinuz");
|
||||
if (!g_file_query_exists (kernel_path, NULL))
|
||||
continue;
|
||||
|
||||
if (linux_release != NULL)
|
||||
return glnx_throw (error, "Multiple kernels found in /usr/lib/modules");
|
||||
|
||||
linux_release = g_strdup (g_file_info_get_name (child_info));
|
||||
}
|
||||
|
||||
if (linux_release)
|
||||
{
|
||||
g_variant_dict_insert (dict, OSTREE_METADATA_KEY_BOOTABLE, "b", TRUE);
|
||||
g_variant_dict_insert (dict, OSTREE_METADATA_KEY_LINUX, "s", linux_release);
|
||||
return TRUE;
|
||||
}
|
||||
return glnx_throw (error, "No kernel found in /usr/lib/modules");
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: LGPL-2.0+
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <gio/gio.h>
|
||||
#include <ostree-types.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
/**
|
||||
* OSTREE_METADATA_KEY_BOOTABLE:
|
||||
*
|
||||
* GVariant type `b`: Set if this commit is intended to be bootable
|
||||
* Since: 2021.1
|
||||
*/
|
||||
#define OSTREE_METADATA_KEY_BOOTABLE "ostree.bootable"
|
||||
/**
|
||||
* OSTREE_METADATA_KEY_LINUX:
|
||||
*
|
||||
* GVariant type `s`: Contains the Linux kernel release (i.e. `uname -r`)
|
||||
* Since: 2021.1
|
||||
*/
|
||||
#define OSTREE_METADATA_KEY_LINUX "ostree.linux"
|
||||
|
||||
_OSTREE_PUBLIC
|
||||
gboolean
|
||||
ostree_commit_metadata_for_bootable (GFile *root, GVariantDict *dict, GCancellable *cancellable, GError **error);
|
||||
|
||||
G_END_DECLS
|
||||
@@ -521,6 +521,8 @@ OstreeRepoAutoLock * _ostree_repo_auto_lock_push (OstreeRepo *self,
|
||||
void _ostree_repo_auto_lock_cleanup (OstreeRepoAutoLock *lock);
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC (OstreeRepoAutoLock, _ostree_repo_auto_lock_cleanup)
|
||||
|
||||
gboolean _ostree_repo_parse_fsverity_config (OstreeRepo *self, GError **error);
|
||||
|
||||
gboolean
|
||||
_ostree_tmpf_fsverity_core (GLnxTmpfile *tmpf,
|
||||
_OstreeFeatureSupport fsverity_requested,
|
||||
|
||||
@@ -70,6 +70,7 @@ typedef struct {
|
||||
gboolean require_static_deltas;
|
||||
gboolean disable_static_deltas;
|
||||
gboolean has_tombstone_commits;
|
||||
gboolean disable_verify_bindings;
|
||||
|
||||
GBytes *summary_data;
|
||||
char *summary_etag;
|
||||
@@ -87,7 +88,7 @@ typedef struct {
|
||||
GHashTable *ref_keyring_map; /* Maps OstreeCollectionRef to keyring remote name */
|
||||
GPtrArray *static_delta_superblocks;
|
||||
GHashTable *expected_commit_sizes; /* Maps commit checksum to known size */
|
||||
GHashTable *commit_to_depth; /* Maps commit checksum maximum depth */
|
||||
GHashTable *commit_to_depth; /* Maps parent commit checksum maximum depth */
|
||||
GHashTable *scanned_metadata; /* Maps object name to itself */
|
||||
GHashTable *fetched_detached_metadata; /* Map<checksum,GVariant> */
|
||||
GHashTable *requested_metadata; /* Maps object name to itself */
|
||||
|
||||
@@ -706,7 +706,7 @@ scan_dirtree_object (OtPullData *pull_data,
|
||||
* before libostree's validation was strengthened.
|
||||
*/
|
||||
if (!ot_util_filename_validate (filename, error))
|
||||
return FALSE;
|
||||
return glnx_prefix_error (error, "File %u in dirtree", i);
|
||||
|
||||
/* Skip files if we're traversing a request only directory, unless it exactly
|
||||
* matches the path */
|
||||
@@ -781,7 +781,7 @@ scan_dirtree_object (OtPullData *pull_data,
|
||||
|
||||
/* See comment above for files */
|
||||
if (!ot_util_filename_validate (dirname, error))
|
||||
return FALSE;
|
||||
return glnx_prefix_error (error, "Dir %u in dirtree", i);
|
||||
|
||||
if (!pull_matches_subdir (pull_data, path, dirname, TRUE))
|
||||
continue;
|
||||
@@ -1113,6 +1113,18 @@ on_metadata_written (GObject *object,
|
||||
check_outstanding_requests_handle_error (pull_data, &local_error);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
is_parent_commit (OtPullData *pull_data,
|
||||
const char *checksum)
|
||||
{
|
||||
/* FIXME: Only parent commits are added to the commit_to_depth table,
|
||||
* so if the checksum isn't in the table then a new commit chain is
|
||||
* being started. However, if the desired commit was a parent in a
|
||||
* previously followed chain, then this will be wrong.
|
||||
*/
|
||||
return g_hash_table_contains (pull_data->commit_to_depth, checksum);
|
||||
}
|
||||
|
||||
static void
|
||||
meta_fetch_on_complete (GObject *object,
|
||||
GAsyncResult *result,
|
||||
@@ -1155,10 +1167,13 @@ meta_fetch_on_complete (GObject *object,
|
||||
}
|
||||
|
||||
/* When traversing parents, do not fail on a missing commit.
|
||||
* We may be pulling from a partial repository that ends in
|
||||
* a dangling parent reference. */
|
||||
* We may be pulling from a partial repository that ends in a
|
||||
* dangling parent reference. This logic should match the
|
||||
* local case in scan_one_metadata_object.
|
||||
*/
|
||||
else if (objtype == OSTREE_OBJECT_TYPE_COMMIT &&
|
||||
pull_data->maxdepth != 0)
|
||||
pull_data->maxdepth != 0 &&
|
||||
is_parent_commit (pull_data, checksum))
|
||||
{
|
||||
g_clear_error (&local_error);
|
||||
/* If the remote repo supports tombstone commits, check if the commit was intentionally
|
||||
@@ -1542,8 +1557,6 @@ scan_commit_object (OtPullData *pull_data,
|
||||
else
|
||||
{
|
||||
depth = pull_data->maxdepth;
|
||||
g_hash_table_insert (pull_data->commit_to_depth, g_strdup (checksum),
|
||||
GINT_TO_POINTER (depth));
|
||||
}
|
||||
|
||||
#ifndef OSTREE_DISABLE_GPGME
|
||||
@@ -1620,15 +1633,19 @@ scan_commit_object (OtPullData *pull_data,
|
||||
if (!ostree_repo_load_commit (pull_data->repo, checksum, &commit, &commitstate, error))
|
||||
return FALSE;
|
||||
|
||||
/* If ref is non-NULL then the commit we fetched was requested through the
|
||||
* branch, otherwise we requested a commit checksum without specifying a branch.
|
||||
*/
|
||||
g_autofree char *remote_collection_id = NULL;
|
||||
remote_collection_id = get_remote_repo_collection_id (pull_data);
|
||||
if (!_ostree_repo_verify_bindings (remote_collection_id,
|
||||
(ref != NULL) ? ref->ref_name : NULL,
|
||||
commit, error))
|
||||
return glnx_prefix_error (error, "Commit %s", checksum);
|
||||
if (!pull_data->disable_verify_bindings)
|
||||
{
|
||||
/* If ref is non-NULL then the commit we fetched was requested through
|
||||
* the branch, otherwise we requested a commit checksum without
|
||||
* specifying a branch.
|
||||
*/
|
||||
g_autofree char *remote_collection_id = NULL;
|
||||
remote_collection_id = get_remote_repo_collection_id (pull_data);
|
||||
if (!_ostree_repo_verify_bindings (remote_collection_id,
|
||||
(ref != NULL) ? ref->ref_name : NULL,
|
||||
commit, error))
|
||||
return glnx_prefix_error (error, "Commit %s", checksum);
|
||||
}
|
||||
|
||||
guint64 new_ts = ostree_commit_get_timestamp (commit);
|
||||
if (pull_data->timestamp_check)
|
||||
@@ -1680,40 +1697,19 @@ scan_commit_object (OtPullData *pull_data,
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (parent_csum_bytes != NULL && pull_data->maxdepth == -1)
|
||||
{
|
||||
queue_scan_one_metadata_object_c (pull_data, parent_csum_bytes,
|
||||
OSTREE_OBJECT_TYPE_COMMIT, NULL,
|
||||
recursion_depth + 1, NULL);
|
||||
}
|
||||
else if (parent_csum_bytes != NULL && depth > 0)
|
||||
if (parent_csum_bytes != NULL && (pull_data->maxdepth == -1 || depth > 0))
|
||||
{
|
||||
char parent_checksum[OSTREE_SHA256_STRING_LEN+1];
|
||||
gpointer parent_depthp;
|
||||
int parent_depth;
|
||||
|
||||
ostree_checksum_inplace_from_bytes (parent_csum_bytes, parent_checksum);
|
||||
|
||||
if (g_hash_table_lookup_extended (pull_data->commit_to_depth, parent_checksum,
|
||||
NULL, &parent_depthp))
|
||||
{
|
||||
parent_depth = GPOINTER_TO_INT (parent_depthp);
|
||||
}
|
||||
else
|
||||
{
|
||||
parent_depth = depth - 1;
|
||||
}
|
||||
|
||||
if (parent_depth >= 0)
|
||||
{
|
||||
g_hash_table_insert (pull_data->commit_to_depth, g_strdup (parent_checksum),
|
||||
GINT_TO_POINTER (parent_depth));
|
||||
queue_scan_one_metadata_object_c (pull_data, parent_csum_bytes,
|
||||
OSTREE_OBJECT_TYPE_COMMIT,
|
||||
NULL,
|
||||
recursion_depth + 1,
|
||||
NULL);
|
||||
}
|
||||
int parent_depth = (depth > 0) ? depth - 1 : -1;
|
||||
g_hash_table_insert (pull_data->commit_to_depth, g_strdup (parent_checksum),
|
||||
GINT_TO_POINTER (parent_depth));
|
||||
queue_scan_one_metadata_object_c (pull_data, parent_csum_bytes,
|
||||
OSTREE_OBJECT_TYPE_COMMIT,
|
||||
NULL,
|
||||
recursion_depth + 1,
|
||||
NULL);
|
||||
}
|
||||
|
||||
/* We only recurse to looking whether we need dirtree/dirmeta
|
||||
@@ -1826,10 +1822,46 @@ scan_one_metadata_object (OtPullData *pull_data,
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
g_autoptr(GError) local_error = NULL;
|
||||
if (!_ostree_repo_import_object (pull_data->repo, pull_data->remote_repo_local,
|
||||
objtype, checksum, pull_data->importflags,
|
||||
cancellable, error))
|
||||
return FALSE;
|
||||
cancellable, &local_error))
|
||||
{
|
||||
/* When traversing parents, do not fail on a missing commit.
|
||||
* We may be pulling from a partial repository that ends in a
|
||||
* dangling parent reference. This logic should match the
|
||||
* remote case in meta_fetch_on_complete.
|
||||
*
|
||||
* Note early return.
|
||||
*/
|
||||
if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND) &&
|
||||
objtype == OSTREE_OBJECT_TYPE_COMMIT &&
|
||||
pull_data->maxdepth != 0 &&
|
||||
is_parent_commit (pull_data, checksum))
|
||||
{
|
||||
g_clear_error (&local_error);
|
||||
|
||||
/* If the remote repo supports tombstone commits, check if
|
||||
* the commit was intentionally deleted.
|
||||
*/
|
||||
if (pull_data->has_tombstone_commits)
|
||||
{
|
||||
if (!_ostree_repo_import_object (pull_data->repo, pull_data->remote_repo_local,
|
||||
OSTREE_OBJECT_TYPE_TOMBSTONE_COMMIT,
|
||||
checksum, pull_data->importflags,
|
||||
cancellable, error))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
g_propagate_error (error, g_steal_pointer (&local_error));
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/* The import API will fetch both the commit and detached metadata, so
|
||||
* add it to the hash to avoid re-fetching it below.
|
||||
*/
|
||||
@@ -1902,7 +1934,7 @@ scan_one_metadata_object (OtPullData *pull_data,
|
||||
{
|
||||
if (!scan_dirtree_object (pull_data, checksum, path, recursion_depth,
|
||||
pull_data->cancellable, error))
|
||||
return FALSE;
|
||||
return glnx_prefix_error (error, "Validating dirtree %s (%s)", checksum, path);
|
||||
|
||||
g_hash_table_add (pull_data->scanned_metadata, g_variant_ref (object));
|
||||
pull_data->n_scanned_metadata++;
|
||||
@@ -3670,6 +3702,8 @@ all_requested_refs_have_commit (GHashTable *requested_refs /* (element-type Ostr
|
||||
* specified, the `summary` will be downloaded from the remote. Since: 2020.5
|
||||
* * `summary-sig-bytes` (`ay`): Contents of the `summary.sig` file. If this
|
||||
* is specified, `summary-bytes` must also be specified. Since: 2020.5
|
||||
* * `disable-verify-bindings` (`b`): Disable verification of commit bindings.
|
||||
* Since: 2020.9
|
||||
*/
|
||||
gboolean
|
||||
ostree_repo_pull_with_options (OstreeRepo *self,
|
||||
@@ -3771,6 +3805,7 @@ ostree_repo_pull_with_options (OstreeRepo *self,
|
||||
g_variant_lookup (options, "ref-keyring-map", "a(sss)", &ref_keyring_map_iter);
|
||||
(void) g_variant_lookup (options, "summary-bytes", "@ay", &summary_bytes_v);
|
||||
(void) g_variant_lookup (options, "summary-sig-bytes", "@ay", &summary_sig_bytes_v);
|
||||
(void) g_variant_lookup (options, "disable-verify-bindings", "b", &pull_data->disable_verify_bindings);
|
||||
|
||||
if (pull_data->remote_refspec_name != NULL)
|
||||
pull_data->remote_name = g_strdup (pull_data->remote_refspec_name);
|
||||
@@ -4069,24 +4104,25 @@ ostree_repo_pull_with_options (OstreeRepo *self,
|
||||
&configured_branches, error))
|
||||
goto out;
|
||||
|
||||
/* TODO reindent later */
|
||||
{ OstreeFetcherURI *first_uri = pull_data->meta_mirrorlist->pdata[0];
|
||||
/* Handle file:// URIs */
|
||||
{
|
||||
OstreeFetcherURI *first_uri = pull_data->meta_mirrorlist->pdata[0];
|
||||
g_autofree char *first_scheme = _ostree_fetcher_uri_get_scheme (first_uri);
|
||||
|
||||
/* NB: we don't support local mirrors in mirrorlists, so if this passes, it
|
||||
* means that we're not using mirrorlists (see also fetch_mirrorlist())
|
||||
* Also, we explicitly disable the "local repo" path if static deltas
|
||||
* were explicitly requested to be required; this is going to happen
|
||||
* most often for testing deltas without setting up a HTTP server.
|
||||
*/
|
||||
if (g_str_equal (first_scheme, "file") && !pull_data->require_static_deltas)
|
||||
{
|
||||
g_autofree char *path = _ostree_fetcher_uri_get_path (first_uri);
|
||||
g_autoptr(GFile) remote_repo_path = g_file_new_for_path (path);
|
||||
pull_data->remote_repo_local = ostree_repo_new (remote_repo_path);
|
||||
if (!ostree_repo_open (pull_data->remote_repo_local, cancellable, error))
|
||||
goto out;
|
||||
}
|
||||
/* NB: we don't support local mirrors in mirrorlists, so if this passes, it
|
||||
* means that we're not using mirrorlists (see also fetch_mirrorlist())
|
||||
* Also, we explicitly disable the "local repo" path if static deltas
|
||||
* were explicitly requested to be required; this is going to happen
|
||||
* most often for testing deltas without setting up a HTTP server.
|
||||
*/
|
||||
if (g_str_equal (first_scheme, "file") && !pull_data->require_static_deltas)
|
||||
{
|
||||
g_autofree char *uri = _ostree_fetcher_uri_to_string (first_uri);
|
||||
g_autoptr(GFile) remote_repo_path = g_file_new_for_uri (uri);
|
||||
pull_data->remote_repo_local = ostree_repo_new (remote_repo_path);
|
||||
if (!ostree_repo_open (pull_data->remote_repo_local, cancellable, error))
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
/* Change some option defaults if we're actually pulling from a local
|
||||
@@ -6639,7 +6675,7 @@ ostree_repo_remote_fetch_summary_with_options (OstreeRepo *self,
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (signatures)
|
||||
if (signatures && !summary)
|
||||
{
|
||||
if (!_ostree_repo_load_cache_summary_if_same_sig (self,
|
||||
name,
|
||||
|
||||
@@ -449,7 +449,7 @@ _ostree_repo_resolve_rev_internal (OstreeRepo *self,
|
||||
* @self: Repo
|
||||
* @refspec: A refspec
|
||||
* @allow_noent: Do not throw an error if refspec does not exist
|
||||
* @out_rev: (out) (transfer full): A checksum,or %NULL if @allow_noent is true and it does not exist
|
||||
* @out_rev: (out) (nullable) (transfer full): A checksum,or %NULL if @allow_noent is true and it does not exist
|
||||
* @error: Error
|
||||
*
|
||||
* Look up the given refspec, returning the checksum it references in
|
||||
@@ -472,7 +472,7 @@ ostree_repo_resolve_rev (OstreeRepo *self,
|
||||
* @refspec: A refspec
|
||||
* @allow_noent: Do not throw an error if refspec does not exist
|
||||
* @flags: Options controlling behavior
|
||||
* @out_rev: (out) (transfer full): A checksum,or %NULL if @allow_noent is true and it does not exist
|
||||
* @out_rev: (out) (nullable) (transfer full): A checksum,or %NULL if @allow_noent is true and it does not exist
|
||||
* @error: Error
|
||||
*
|
||||
* Look up the given refspec, returning the checksum it references in
|
||||
|
||||
@@ -636,7 +636,7 @@ try_content_rollsum (OstreeRepo *repo,
|
||||
if (!get_unpacked_unlinked_content (repo, to, &tmp_to, cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
OstreeRollsumMatches *matches = _ostree_compute_rollsum_matches (tmp_from, tmp_to);
|
||||
g_autoptr(OstreeRollsumMatches) matches = _ostree_compute_rollsum_matches (tmp_from, tmp_to);
|
||||
|
||||
const guint match_ratio = (matches->bufmatches*100)/matches->total;
|
||||
|
||||
|
||||
@@ -0,0 +1,174 @@
|
||||
/*
|
||||
* Copyright (C) Red Hat, Inc.
|
||||
*
|
||||
* SPDX-License-Identifier: LGPL-2.0+
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
#include "ostree-core-private.h"
|
||||
#include "ostree-repo-private.h"
|
||||
#include "otutil.h"
|
||||
#include "ot-fs-utils.h"
|
||||
#ifdef HAVE_LINUX_FSVERITY_H
|
||||
#include <linux/fsverity.h>
|
||||
#endif
|
||||
|
||||
gboolean
|
||||
_ostree_repo_parse_fsverity_config (OstreeRepo *self, GError **error)
|
||||
{
|
||||
/* Currently experimental */
|
||||
static const char fsverity_key[] = "ex-fsverity";
|
||||
self->fs_verity_wanted = _OSTREE_FEATURE_NO;
|
||||
#ifdef HAVE_LINUX_FSVERITY_H
|
||||
self->fs_verity_supported = _OSTREE_FEATURE_MAYBE;
|
||||
#else
|
||||
self->fs_verity_supported = _OSTREE_FEATURE_NO;
|
||||
#endif
|
||||
gboolean fsverity_required = FALSE;
|
||||
if (!ot_keyfile_get_boolean_with_default (self->config, fsverity_key, "required",
|
||||
FALSE, &fsverity_required, error))
|
||||
return FALSE;
|
||||
if (fsverity_required)
|
||||
{
|
||||
self->fs_verity_wanted = _OSTREE_FEATURE_YES;
|
||||
if (self->fs_verity_supported == _OSTREE_FEATURE_NO)
|
||||
return glnx_throw (error, "fsverity required, but libostree compiled without support");
|
||||
}
|
||||
else
|
||||
{
|
||||
gboolean fsverity_opportunistic = FALSE;
|
||||
if (!ot_keyfile_get_boolean_with_default (self->config, fsverity_key, "opportunistic",
|
||||
FALSE, &fsverity_opportunistic, error))
|
||||
return FALSE;
|
||||
if (fsverity_opportunistic)
|
||||
self->fs_verity_wanted = _OSTREE_FEATURE_MAYBE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
/* Wrapper around the fsverity ioctl, compressing the result to
|
||||
* "success, unsupported or error". This is used for /boot where
|
||||
* we enable verity if supported.
|
||||
* */
|
||||
gboolean
|
||||
_ostree_tmpf_fsverity_core (GLnxTmpfile *tmpf,
|
||||
_OstreeFeatureSupport fsverity_requested,
|
||||
gboolean *supported,
|
||||
GError **error)
|
||||
{
|
||||
/* Set this by default to simplify the code below */
|
||||
if (supported)
|
||||
*supported = FALSE;
|
||||
|
||||
if (fsverity_requested == _OSTREE_FEATURE_NO)
|
||||
return TRUE;
|
||||
|
||||
#ifdef HAVE_LINUX_FSVERITY_H
|
||||
GLNX_AUTO_PREFIX_ERROR ("fsverity", error);
|
||||
|
||||
/* fs-verity requires a read-only file descriptor */
|
||||
if (!glnx_tmpfile_reopen_rdonly (tmpf, error))
|
||||
return FALSE;
|
||||
|
||||
struct fsverity_enable_arg arg = { 0, };
|
||||
arg.version = 1;
|
||||
arg.hash_algorithm = FS_VERITY_HASH_ALG_SHA256; /* TODO configurable? */
|
||||
arg.block_size = 4096; /* FIXME query */
|
||||
arg.salt_size = 0; /* TODO store salt in ostree repo config */
|
||||
arg.salt_ptr = 0;
|
||||
arg.sig_size = 0; /* We don't currently expect use of in-kernel signature verification */
|
||||
arg.sig_ptr = 0;
|
||||
|
||||
if (ioctl (tmpf->fd, FS_IOC_ENABLE_VERITY, &arg) < 0)
|
||||
{
|
||||
switch (errno)
|
||||
{
|
||||
case ENOTTY:
|
||||
case EOPNOTSUPP:
|
||||
return TRUE;
|
||||
default:
|
||||
return glnx_throw_errno_prefix (error, "ioctl(FS_IOC_ENABLE_VERITY)");
|
||||
}
|
||||
}
|
||||
|
||||
if (supported)
|
||||
*supported = TRUE;
|
||||
#endif
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* Enable verity on a file, respecting the "wanted" and "supported" states.
|
||||
* The main idea here is to optimize out pointlessly calling the ioctl()
|
||||
* over and over in cases where it's not supported for the repo's filesystem,
|
||||
* as well as to support "opportunistic" use (requested and if filesystem supports).
|
||||
* */
|
||||
gboolean
|
||||
_ostree_tmpf_fsverity (OstreeRepo *self,
|
||||
GLnxTmpfile *tmpf,
|
||||
GError **error)
|
||||
{
|
||||
#ifdef HAVE_LINUX_FSVERITY_H
|
||||
g_mutex_lock (&self->txn_lock);
|
||||
_OstreeFeatureSupport fsverity_wanted = self->fs_verity_wanted;
|
||||
_OstreeFeatureSupport fsverity_supported = self->fs_verity_supported;
|
||||
g_mutex_unlock (&self->txn_lock);
|
||||
|
||||
switch (fsverity_wanted)
|
||||
{
|
||||
case _OSTREE_FEATURE_YES:
|
||||
{
|
||||
if (fsverity_supported == _OSTREE_FEATURE_NO)
|
||||
return glnx_throw (error, "fsverity required but filesystem does not support it");
|
||||
}
|
||||
break;
|
||||
case _OSTREE_FEATURE_MAYBE:
|
||||
break;
|
||||
case _OSTREE_FEATURE_NO:
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean supported = FALSE;
|
||||
if (!_ostree_tmpf_fsverity_core (tmpf, fsverity_wanted, &supported, error))
|
||||
return FALSE;
|
||||
|
||||
if (!supported)
|
||||
{
|
||||
if (G_UNLIKELY (fsverity_wanted == _OSTREE_FEATURE_YES))
|
||||
return glnx_throw (error, "fsverity required but filesystem does not support it");
|
||||
|
||||
/* If we got here, we must be trying "opportunistic" use of fs-verity */
|
||||
g_assert_cmpint (fsverity_wanted, ==, _OSTREE_FEATURE_MAYBE);
|
||||
g_mutex_lock (&self->txn_lock);
|
||||
self->fs_verity_supported = _OSTREE_FEATURE_NO;
|
||||
g_mutex_unlock (&self->txn_lock);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
g_mutex_lock (&self->txn_lock);
|
||||
self->fs_verity_supported = _OSTREE_FEATURE_YES;
|
||||
g_mutex_unlock (&self->txn_lock);
|
||||
#else
|
||||
g_assert_cmpint (self->fs_verity_wanted, !=, _OSTREE_FEATURE_YES);
|
||||
#endif
|
||||
return TRUE;
|
||||
}
|
||||
@@ -2584,7 +2584,7 @@ ostree_repo_create (OstreeRepo *self,
|
||||
* @dfd: Directory fd
|
||||
* @path: Path
|
||||
* @mode: The mode to store the repository in
|
||||
* @options: a{sv}: See below for accepted keys
|
||||
* @options: (nullable): a{sv}: See below for accepted keys
|
||||
* @cancellable: Cancellable
|
||||
* @error: Error
|
||||
*
|
||||
@@ -3017,33 +3017,8 @@ reload_core_config (OstreeRepo *self,
|
||||
}
|
||||
}
|
||||
|
||||
/* Currently experimental */
|
||||
static const char fsverity_key[] = "ex-fsverity";
|
||||
self->fs_verity_wanted = _OSTREE_FEATURE_NO;
|
||||
#ifdef HAVE_LINUX_FSVERITY_H
|
||||
self->fs_verity_supported = _OSTREE_FEATURE_MAYBE;
|
||||
#else
|
||||
self->fs_verity_supported = _OSTREE_FEATURE_NO;
|
||||
#endif
|
||||
gboolean fsverity_required = FALSE;
|
||||
if (!ot_keyfile_get_boolean_with_default (self->config, fsverity_key, "required",
|
||||
FALSE, &fsverity_required, error))
|
||||
if (!_ostree_repo_parse_fsverity_config (self, error))
|
||||
return FALSE;
|
||||
if (fsverity_required)
|
||||
{
|
||||
self->fs_verity_wanted = _OSTREE_FEATURE_YES;
|
||||
if (self->fs_verity_supported == _OSTREE_FEATURE_NO)
|
||||
return glnx_throw (error, "fsverity required, but libostree compiled without support");
|
||||
}
|
||||
else
|
||||
{
|
||||
gboolean fsverity_opportunistic = FALSE;
|
||||
if (!ot_keyfile_get_boolean_with_default (self->config, fsverity_key, "opportunistic",
|
||||
FALSE, &fsverity_opportunistic, error))
|
||||
return FALSE;
|
||||
if (fsverity_opportunistic)
|
||||
self->fs_verity_wanted = _OSTREE_FEATURE_MAYBE;
|
||||
}
|
||||
|
||||
{
|
||||
g_clear_pointer (&self->collection_id, g_free);
|
||||
|
||||
@@ -56,9 +56,6 @@
|
||||
#define OSTREE_DEPLOYMENT_FINALIZING_ID SD_ID128_MAKE(e8,64,6c,d6,3d,ff,46,25,b7,79,09,a8,e7,a4,09,94)
|
||||
#endif
|
||||
|
||||
static gboolean
|
||||
is_ro_mount (const char *path);
|
||||
|
||||
/*
|
||||
* Like symlinkat() but overwrites (atomically) an existing
|
||||
* symlink.
|
||||
@@ -1582,11 +1579,11 @@ full_system_sync (OstreeSysroot *self,
|
||||
|
||||
out_stats->root_syncfs_msec = (end_msec - start_msec);
|
||||
|
||||
start_msec = g_get_monotonic_time () / 1000;
|
||||
glnx_autofd int boot_dfd = -1;
|
||||
if (!glnx_opendirat (self->sysroot_fd, "boot", TRUE, &boot_dfd, error))
|
||||
if (!_ostree_sysroot_ensure_boot_fd (self, error))
|
||||
return FALSE;
|
||||
if (!fsfreeze_thaw_cycle (self, boot_dfd, cancellable, error))
|
||||
|
||||
start_msec = g_get_monotonic_time () / 1000;
|
||||
if (!fsfreeze_thaw_cycle (self, self->boot_fd, cancellable, error))
|
||||
return FALSE;
|
||||
end_msec = g_get_monotonic_time () / 1000;
|
||||
out_stats->boot_syncfs_msec = (end_msec - start_msec);
|
||||
@@ -1678,6 +1675,7 @@ static gboolean
|
||||
swap_bootlinks (OstreeSysroot *self,
|
||||
int bootversion,
|
||||
GPtrArray *new_deployments,
|
||||
char **out_subbootdir,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
@@ -1702,7 +1700,8 @@ swap_bootlinks (OstreeSysroot *self,
|
||||
if (!symlink_at_replace (ostree_subbootdir_name, ostree_dfd, ostree_bootdir_name,
|
||||
cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
if (out_subbootdir)
|
||||
*out_subbootdir = g_steal_pointer (&ostree_subbootdir_name);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@@ -1770,8 +1769,7 @@ install_deployment_kernel (OstreeSysroot *sysroot,
|
||||
cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
glnx_autofd int boot_dfd = -1;
|
||||
if (!glnx_opendirat (sysroot->sysroot_fd, "boot", TRUE, &boot_dfd, error))
|
||||
if (!_ostree_sysroot_ensure_boot_fd (sysroot, error))
|
||||
return FALSE;
|
||||
|
||||
const char *osname = ostree_deployment_get_osname (deployment);
|
||||
@@ -1781,14 +1779,14 @@ install_deployment_kernel (OstreeSysroot *sysroot,
|
||||
g_autofree char *bootconf_name = g_strdup_printf ("ostree-%d-%s.conf",
|
||||
n_deployments - ostree_deployment_get_index (deployment),
|
||||
osname);
|
||||
if (!glnx_shutil_mkdir_p_at (boot_dfd, bootcsumdir, 0775, cancellable, error))
|
||||
if (!glnx_shutil_mkdir_p_at (sysroot->boot_fd, bootcsumdir, 0775, cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
glnx_autofd int bootcsum_dfd = -1;
|
||||
if (!glnx_opendirat (boot_dfd, bootcsumdir, TRUE, &bootcsum_dfd, error))
|
||||
if (!glnx_opendirat (sysroot->boot_fd, bootcsumdir, TRUE, &bootcsum_dfd, error))
|
||||
return FALSE;
|
||||
|
||||
if (!glnx_shutil_mkdir_p_at (boot_dfd, bootconfdir, 0775, cancellable, error))
|
||||
if (!glnx_shutil_mkdir_p_at (sysroot->boot_fd, bootconfdir, 0775, cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
/* Install (hardlink/copy) the kernel into /boot/ostree/osname-${bootcsum} if
|
||||
@@ -1879,18 +1877,18 @@ install_deployment_kernel (OstreeSysroot *sysroot,
|
||||
{
|
||||
overlay_initrds = g_ptr_array_new_with_free_func (g_free);
|
||||
|
||||
if (!glnx_shutil_mkdir_p_at (boot_dfd, _OSTREE_SYSROOT_BOOT_INITRAMFS_OVERLAYS,
|
||||
if (!glnx_shutil_mkdir_p_at (sysroot->boot_fd, _OSTREE_SYSROOT_BOOT_INITRAMFS_OVERLAYS,
|
||||
0755, cancellable, error))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!glnx_fstatat_allow_noent (boot_dfd, rel_destpath, NULL, 0, error))
|
||||
if (!glnx_fstatat_allow_noent (sysroot->boot_fd, 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,
|
||||
if (!install_into_boot (repo, sepolicy, AT_FDCWD, srcpath, sysroot->boot_fd, rel_destpath,
|
||||
cancellable, error))
|
||||
return FALSE;
|
||||
}
|
||||
@@ -2019,7 +2017,7 @@ install_deployment_kernel (OstreeSysroot *sysroot,
|
||||
ostree_bootconfig_parser_set (bootconfig, "options", options_key);
|
||||
|
||||
glnx_autofd int bootconf_dfd = -1;
|
||||
if (!glnx_opendirat (boot_dfd, bootconfdir, TRUE, &bootconf_dfd, error))
|
||||
if (!glnx_opendirat (sysroot->boot_fd, bootconfdir, TRUE, &bootconf_dfd, error))
|
||||
return FALSE;
|
||||
|
||||
if (!ostree_bootconfig_parser_write_at (ostree_deployment_get_bootconfig (deployment),
|
||||
@@ -2076,15 +2074,14 @@ swap_bootloader (OstreeSysroot *sysroot,
|
||||
g_assert ((current_bootversion == 0 && new_bootversion == 1) ||
|
||||
(current_bootversion == 1 && new_bootversion == 0));
|
||||
|
||||
glnx_autofd int boot_dfd = -1;
|
||||
if (!glnx_opendirat (sysroot->sysroot_fd, "boot", TRUE, &boot_dfd, error))
|
||||
if (!_ostree_sysroot_ensure_boot_fd (sysroot, error))
|
||||
return FALSE;
|
||||
|
||||
/* The symlink was already written, and we used syncfs() to ensure
|
||||
* its data is in place. Renaming now should give us atomic semantics;
|
||||
* see https://bugzilla.gnome.org/show_bug.cgi?id=755595
|
||||
*/
|
||||
if (!glnx_renameat (boot_dfd, "loader.tmp", boot_dfd, "loader", error))
|
||||
if (!glnx_renameat (sysroot->boot_fd, "loader.tmp", sysroot->boot_fd, "loader", error))
|
||||
return FALSE;
|
||||
|
||||
/* Now we explicitly fsync this directory, even though it
|
||||
@@ -2096,7 +2093,7 @@ swap_bootloader (OstreeSysroot *sysroot,
|
||||
* for whatever reason, and we wouldn't want to confuse the
|
||||
* admin by going back to the previous session.
|
||||
*/
|
||||
if (fsync (boot_dfd) != 0)
|
||||
if (fsync (sysroot->boot_fd) != 0)
|
||||
return glnx_throw_errno_prefix (error, "fsync(boot)");
|
||||
|
||||
/* TODO: In the future also execute this automatically via a systemd unit
|
||||
@@ -2227,57 +2224,6 @@ cleanup_legacy_current_symlinks (OstreeSysroot *self,
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* Detect whether or not @path refers to a read-only mountpoint. This is
|
||||
* currently just used to handle a potentially read-only /boot by transiently
|
||||
* remounting it read-write. In the future we might also do this for e.g.
|
||||
* /sysroot.
|
||||
*/
|
||||
static gboolean
|
||||
is_ro_mount (const char *path)
|
||||
{
|
||||
#ifdef HAVE_LIBMOUNT
|
||||
/* Dragging in all of this crud is apparently necessary just to determine
|
||||
* whether something is a mount point.
|
||||
*
|
||||
* Systemd has a totally different implementation in
|
||||
* src/basic/mount-util.c.
|
||||
*/
|
||||
struct libmnt_table *tb = mnt_new_table_from_file ("/proc/self/mountinfo");
|
||||
struct libmnt_fs *fs;
|
||||
struct libmnt_cache *cache;
|
||||
gboolean is_mount = FALSE;
|
||||
struct statvfs stvfsbuf;
|
||||
|
||||
if (!tb)
|
||||
return FALSE;
|
||||
|
||||
/* to canonicalize all necessary paths */
|
||||
cache = mnt_new_cache ();
|
||||
mnt_table_set_cache (tb, cache);
|
||||
|
||||
fs = mnt_table_find_target(tb, path, MNT_ITER_BACKWARD);
|
||||
is_mount = fs && mnt_fs_get_target (fs);
|
||||
#ifdef HAVE_MNT_UNREF_CACHE
|
||||
mnt_unref_table (tb);
|
||||
mnt_unref_cache (cache);
|
||||
#else
|
||||
mnt_free_table (tb);
|
||||
mnt_free_cache (cache);
|
||||
#endif
|
||||
|
||||
if (!is_mount)
|
||||
return FALSE;
|
||||
|
||||
/* We *could* parse the options, but it seems more reliable to
|
||||
* introspect the actual mount at runtime.
|
||||
*/
|
||||
if (statvfs (path, &stvfsbuf) == 0)
|
||||
return (stvfsbuf.f_flag & ST_RDONLY) != 0;
|
||||
|
||||
#endif
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* ostree_sysroot_write_deployments:
|
||||
* @self: Sysroot
|
||||
@@ -2309,6 +2255,7 @@ write_deployments_bootswap (OstreeSysroot *self,
|
||||
OstreeSysrootWriteDeploymentsOpts *opts,
|
||||
OstreeBootloader *bootloader,
|
||||
SyncStats *out_syncstats,
|
||||
char **out_subbootdir,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
@@ -2356,7 +2303,8 @@ write_deployments_bootswap (OstreeSysroot *self,
|
||||
new_deployments,
|
||||
cancellable, error))
|
||||
return FALSE;
|
||||
if (!swap_bootlinks (self, new_bootversion, new_deployments,
|
||||
g_autofree char *new_subbootdir = NULL;
|
||||
if (!swap_bootlinks (self, new_bootversion, new_deployments, &new_subbootdir,
|
||||
cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
@@ -2382,6 +2330,8 @@ write_deployments_bootswap (OstreeSysroot *self,
|
||||
cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
if (out_subbootdir)
|
||||
*out_subbootdir = g_steal_pointer (&new_subbootdir);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@@ -2560,6 +2510,7 @@ ostree_sysroot_write_deployments_with_options (OstreeSysroot *self,
|
||||
gboolean bootloader_is_atomic = FALSE;
|
||||
SyncStats syncstats = { 0, };
|
||||
g_autoptr(OstreeBootloader) bootloader = NULL;
|
||||
g_autofree char *new_subbootdir = NULL;
|
||||
if (!requires_new_bootversion)
|
||||
{
|
||||
if (!create_new_bootlinks (self, self->bootversion,
|
||||
@@ -2571,7 +2522,7 @@ ostree_sysroot_write_deployments_with_options (OstreeSysroot *self,
|
||||
return FALSE;
|
||||
|
||||
if (!swap_bootlinks (self, self->bootversion,
|
||||
new_deployments,
|
||||
new_deployments, &new_subbootdir,
|
||||
cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
@@ -2579,49 +2530,21 @@ ostree_sysroot_write_deployments_with_options (OstreeSysroot *self,
|
||||
}
|
||||
else
|
||||
{
|
||||
gboolean boot_was_ro_mount = FALSE;
|
||||
if (self->booted_deployment)
|
||||
boot_was_ro_mount = is_ro_mount ("/boot");
|
||||
|
||||
g_debug ("boot is ro: %s", boot_was_ro_mount ? "yes" : "no");
|
||||
|
||||
if (boot_was_ro_mount)
|
||||
{
|
||||
if (mount ("/boot", "/boot", NULL, MS_REMOUNT | MS_SILENT, NULL) < 0)
|
||||
return glnx_throw_errno_prefix (error, "Remounting /boot read-write");
|
||||
}
|
||||
|
||||
if (!_ostree_sysroot_query_bootloader (self, &bootloader, cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
bootloader_is_atomic = bootloader != NULL && _ostree_bootloader_is_atomic (bootloader);
|
||||
|
||||
/* Note equivalent of try/finally here */
|
||||
gboolean success = write_deployments_bootswap (self, new_deployments, opts, bootloader,
|
||||
&syncstats, cancellable, error);
|
||||
/* Below here don't set GError until the if (!success) check.
|
||||
* Note we only bother remounting if a mount namespace isn't in use.
|
||||
* */
|
||||
if (boot_was_ro_mount && !self->mount_namespace_in_use)
|
||||
{
|
||||
if (mount ("/boot", "/boot", NULL, MS_REMOUNT | MS_RDONLY | MS_SILENT, NULL) < 0)
|
||||
{
|
||||
/* Only make this a warning because we don't want to
|
||||
* completely bomb out if some other process happened to
|
||||
* jump in and open a file there. See above TODO
|
||||
* around doing this in a new mount namespace.
|
||||
*/
|
||||
g_printerr ("warning: Failed to remount /boot read-only: %s\n", strerror (errno));
|
||||
}
|
||||
}
|
||||
if (!success)
|
||||
if (!write_deployments_bootswap (self, new_deployments, opts, bootloader,
|
||||
&syncstats, &new_subbootdir, cancellable, error))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
{ g_autofree char *msg =
|
||||
g_strdup_printf ("%s; bootconfig swap: %s; deployment count change: %i",
|
||||
g_strdup_printf ("%s; bootconfig swap: %s; bootversion: %s, deployment count change: %i",
|
||||
(bootloader_is_atomic ? "Transaction complete" : "Bootloader updated"),
|
||||
requires_new_bootversion ? "yes" : "no",
|
||||
new_subbootdir,
|
||||
new_deployments->len - self->deployments->len);
|
||||
const gchar *bootloader_config = ostree_repo_get_bootloader (ostree_sysroot_repo (self));
|
||||
ot_journal_send ("MESSAGE_ID=" SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(OSTREE_DEPLOYMENT_COMPLETE_ID),
|
||||
@@ -3143,9 +3066,9 @@ ostree_sysroot_stage_tree_with_options (OstreeSysroot *self,
|
||||
if (!_ostree_sysroot_ensure_writable (self, error))
|
||||
return FALSE;
|
||||
|
||||
OstreeDeployment *booted_deployment = ostree_sysroot_get_booted_deployment (self);
|
||||
OstreeDeployment *booted_deployment = ostree_sysroot_require_booted_deployment (self, error);
|
||||
if (booted_deployment == NULL)
|
||||
return glnx_throw (error, "Cannot stage a deployment when not currently booted into an OSTree system");
|
||||
return glnx_prefix_error (error, "Cannot stage deployment");
|
||||
|
||||
/* This is used by the testsuite to exercise the path unit, until it becomes the default
|
||||
* (which is pending on the preset making it everywhere). */
|
||||
|
||||
@@ -56,6 +56,7 @@ struct OstreeSysroot {
|
||||
|
||||
GFile *path;
|
||||
int sysroot_fd;
|
||||
int boot_fd;
|
||||
GLnxLockFile lock;
|
||||
|
||||
OstreeSysrootLoadState loadstate;
|
||||
@@ -160,6 +161,9 @@ char * _ostree_sysroot_get_runstate_path (OstreeDeployment *deployment, const ch
|
||||
|
||||
char *_ostree_sysroot_join_lines (GPtrArray *lines);
|
||||
|
||||
gboolean
|
||||
_ostree_sysroot_ensure_boot_fd (OstreeSysroot *self, GError **error);
|
||||
|
||||
gboolean _ostree_sysroot_query_bootloader (OstreeSysroot *sysroot,
|
||||
OstreeBootloader **out_bootloader,
|
||||
GCancellable *cancellable,
|
||||
|
||||
@@ -198,6 +198,7 @@ ostree_sysroot_init (OstreeSysroot *self)
|
||||
keys, G_N_ELEMENTS (keys));
|
||||
|
||||
self->sysroot_fd = -1;
|
||||
self->boot_fd = -1;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -278,6 +279,44 @@ ensure_sysroot_fd (OstreeSysroot *self,
|
||||
&self->sysroot_fd, error))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
_ostree_sysroot_ensure_boot_fd (OstreeSysroot *self, GError **error)
|
||||
{
|
||||
if (self->boot_fd == -1)
|
||||
{
|
||||
if (!glnx_opendirat (self->sysroot_fd, "boot", TRUE,
|
||||
&self->boot_fd, error))
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
remount_writable (const char *path, gboolean *did_remount, GError **error)
|
||||
{
|
||||
*did_remount = FALSE;
|
||||
struct statvfs stvfsbuf;
|
||||
if (statvfs (path, &stvfsbuf) < 0)
|
||||
{
|
||||
if (errno != ENOENT)
|
||||
return glnx_throw_errno_prefix (error, "statvfs(%s)", path);
|
||||
else
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if ((stvfsbuf.f_flag & ST_RDONLY) != 0)
|
||||
{
|
||||
/* OK, let's remount writable. */
|
||||
if (mount (path, path, NULL, MS_REMOUNT | MS_RELATIME, "") < 0)
|
||||
return glnx_throw_errno_prefix (error, "Remounting %s read-write", path);
|
||||
*did_remount = TRUE;
|
||||
g_debug ("remounted %s writable", path);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@@ -300,19 +339,19 @@ _ostree_sysroot_ensure_writable (OstreeSysroot *self,
|
||||
if (!self->root_is_ostree_booted)
|
||||
return TRUE;
|
||||
|
||||
/* Check if /sysroot is a read-only mountpoint */
|
||||
struct statvfs stvfsbuf;
|
||||
if (statvfs ("/sysroot", &stvfsbuf) < 0)
|
||||
return glnx_throw_errno_prefix (error, "fstatvfs(/sysroot)");
|
||||
if ((stvfsbuf.f_flag & ST_RDONLY) == 0)
|
||||
return TRUE;
|
||||
/* In these cases we also require /boot */
|
||||
if (!_ostree_sysroot_ensure_boot_fd (self, error))
|
||||
return FALSE;
|
||||
|
||||
/* OK, let's remount writable. */
|
||||
if (mount ("/sysroot", "/sysroot", NULL, MS_REMOUNT | MS_RELATIME, "") < 0)
|
||||
return glnx_throw_errno_prefix (error, "Remounting /sysroot read-write");
|
||||
gboolean did_remount_sysroot = FALSE;
|
||||
if (!remount_writable ("/sysroot", &did_remount_sysroot, error))
|
||||
return FALSE;
|
||||
gboolean did_remount_boot = FALSE;
|
||||
if (!remount_writable ("/boot", &did_remount_boot, error))
|
||||
return FALSE;
|
||||
|
||||
/* Reopen our fd */
|
||||
glnx_close_fd (&self->sysroot_fd);
|
||||
/* Now close and reopen our file descriptors */
|
||||
ostree_sysroot_unload (self);
|
||||
if (!ensure_sysroot_fd (self, error))
|
||||
return FALSE;
|
||||
|
||||
@@ -380,6 +419,7 @@ void
|
||||
ostree_sysroot_unload (OstreeSysroot *self)
|
||||
{
|
||||
glnx_close_fd (&self->sysroot_fd);
|
||||
glnx_close_fd (&self->boot_fd);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -449,6 +489,7 @@ _ostree_sysroot_parse_deploy_path_name (const char *name,
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* For a given bootversion, get its subbootversion from `/ostree/boot.$bootversion`. */
|
||||
gboolean
|
||||
_ostree_sysroot_read_current_subbootversion (OstreeSysroot *self,
|
||||
int bootversion,
|
||||
@@ -465,6 +506,7 @@ _ostree_sysroot_read_current_subbootversion (OstreeSysroot *self,
|
||||
return FALSE;
|
||||
if (errno == ENOENT)
|
||||
{
|
||||
g_debug ("Didn't find $sysroot/ostree/boot.%d symlink; assuming subbootversion 0", bootversion);
|
||||
*out_subbootversion = 0;
|
||||
}
|
||||
else
|
||||
@@ -516,6 +558,7 @@ compare_loader_configs_for_sorting (gconstpointer a_pp,
|
||||
return compare_boot_loader_configs (a, b);
|
||||
}
|
||||
|
||||
/* Read all the bootconfigs from `/boot/loader/`. */
|
||||
gboolean
|
||||
_ostree_sysroot_read_boot_loader_configs (OstreeSysroot *self,
|
||||
int bootversion,
|
||||
@@ -574,6 +617,7 @@ _ostree_sysroot_read_boot_loader_configs (OstreeSysroot *self,
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* Get the bootversion from the `/boot/loader` symlink. */
|
||||
static gboolean
|
||||
read_current_bootversion (OstreeSysroot *self,
|
||||
int *out_bootversion,
|
||||
@@ -587,6 +631,7 @@ read_current_bootversion (OstreeSysroot *self,
|
||||
return FALSE;
|
||||
if (errno == ENOENT)
|
||||
{
|
||||
g_debug ("Didn't find $sysroot/boot/loader symlink; assuming bootversion 0");
|
||||
ret_bootversion = 0;
|
||||
}
|
||||
else
|
||||
@@ -698,7 +743,7 @@ parse_deployment (OstreeSysroot *self,
|
||||
return FALSE;
|
||||
|
||||
g_autofree char *errprefix =
|
||||
g_strdup_printf ("Parsing deployment %i in stateroot '%s'", treebootserial, osname);
|
||||
g_strdup_printf ("Parsing deployment %s in stateroot '%s'", boot_link, osname);
|
||||
GLNX_AUTO_PREFIX_ERROR(errprefix, error);
|
||||
|
||||
const char *relative_boot_link = boot_link;
|
||||
@@ -799,6 +844,8 @@ get_ostree_kernel_arg_from_config (OstreeBootconfigParser *config)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* From a BLS config, use its ostree= karg to find the deployment it points to and add it to
|
||||
* the inout_deployments array. */
|
||||
static gboolean
|
||||
list_deployments_process_one_boot_entry (OstreeSysroot *self,
|
||||
OstreeBootconfigParser *config,
|
||||
@@ -1016,6 +1063,9 @@ _ostree_sysroot_reload_staged (OstreeSysroot *self,
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* Loads the current bootversion, subbootversion, and deplyments, starting from the
|
||||
* bootloader configs which are the source of truth.
|
||||
*/
|
||||
static gboolean
|
||||
sysroot_load_from_bootloader_configs (OstreeSysroot *self,
|
||||
GCancellable *cancellable,
|
||||
@@ -1196,6 +1246,27 @@ ostree_sysroot_get_booted_deployment (OstreeSysroot *self)
|
||||
return self->booted_deployment;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* ostree_sysroot_require_booted_deployment:
|
||||
* @self: Sysroot
|
||||
*
|
||||
* Find the booted deployment, or return an error if not booted via OSTree.
|
||||
*
|
||||
* Returns: (transfer none) (not nullable): The currently booted deployment, or an error
|
||||
* Since: 2021.1
|
||||
*/
|
||||
OstreeDeployment *
|
||||
ostree_sysroot_require_booted_deployment (OstreeSysroot *self, GError **error)
|
||||
{
|
||||
g_return_val_if_fail (self->loadstate == OSTREE_SYSROOT_LOAD_STATE_LOADED, NULL);
|
||||
|
||||
if (!self->booted_deployment)
|
||||
return glnx_null_throw (error, "Not currently booted into an OSTree system");
|
||||
return self->booted_deployment;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* ostree_sysroot_get_staged_deployment:
|
||||
* @self: Sysroot
|
||||
|
||||
@@ -85,6 +85,9 @@ GPtrArray *ostree_sysroot_get_deployments (OstreeSysroot *self);
|
||||
_OSTREE_PUBLIC
|
||||
OstreeDeployment *ostree_sysroot_get_booted_deployment (OstreeSysroot *self);
|
||||
_OSTREE_PUBLIC
|
||||
OstreeDeployment *
|
||||
ostree_sysroot_require_booted_deployment (OstreeSysroot *self, GError **error);
|
||||
_OSTREE_PUBLIC
|
||||
OstreeDeployment *ostree_sysroot_get_staged_deployment (OstreeSysroot *self);
|
||||
|
||||
_OSTREE_PUBLIC
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
*
|
||||
* Since: 2017.4
|
||||
*/
|
||||
#define OSTREE_YEAR_VERSION (2020)
|
||||
#define OSTREE_YEAR_VERSION (2021)
|
||||
|
||||
/**
|
||||
* OSTREE_RELEASE_VERSION:
|
||||
@@ -43,7 +43,7 @@
|
||||
*
|
||||
* Since: 2017.4
|
||||
*/
|
||||
#define OSTREE_RELEASE_VERSION (8)
|
||||
#define OSTREE_RELEASE_VERSION (1)
|
||||
|
||||
/**
|
||||
* OSTREE_VERSION
|
||||
@@ -52,7 +52,7 @@
|
||||
*
|
||||
* Since: 2017.4
|
||||
*/
|
||||
#define OSTREE_VERSION (2020.8)
|
||||
#define OSTREE_VERSION (2021.1)
|
||||
|
||||
/**
|
||||
* OSTREE_VERSION_S:
|
||||
@@ -62,7 +62,7 @@
|
||||
*
|
||||
* Since: 2017.4
|
||||
*/
|
||||
#define OSTREE_VERSION_S "2020.8"
|
||||
#define OSTREE_VERSION_S "2021.1"
|
||||
|
||||
#define OSTREE_ENCODE_VERSION(year,release) \
|
||||
((year) << 16 | (release))
|
||||
@@ -100,4 +100,4 @@
|
||||
*
|
||||
* Since: 2019.3
|
||||
*/
|
||||
#define OSTREE_BUILT_FEATURES "libsoup gpgme ex-fsverity libarchive selinux avahi libmount systemd release p2p"
|
||||
#define OSTREE_BUILT_FEATURES "libsoup gpgme ex-fsverity libarchive selinux libmount systemd release p2p"
|
||||
@@ -24,6 +24,7 @@
|
||||
#include <ostree-async-progress.h>
|
||||
#include <ostree-core.h>
|
||||
#include <ostree-repo.h>
|
||||
#include <ostree-repo-os.h>
|
||||
#include <ostree-mutable-tree.h>
|
||||
#include <ostree-remote.h>
|
||||
#include <ostree-repo-file.h>
|
||||
|
||||
Reference in new issue
Block a user