New upstream version 2018.9.1

This commit is contained in:
Simon McVittie committed 2018-10-30 10:59:22 +00:00
commit 854d02fbe3
49 files changed
+973 -356

No files matched your search

+2 -2
View File
@@ -36,7 +36,7 @@ depends() {
install() {
dracut_install /usr/lib/ostree/ostree-prepare-root
inst_simple "${systemdsystemunitdir}/ostree-prepare-root.service"
mkdir -p "${initdir}${systemdsystemconfdir}/initrd-switch-root.target.wants"
mkdir -p "${initdir}${systemdsystemconfdir}/initrd-root-fs.target.wants"
ln_r "${systemdsystemunitdir}/ostree-prepare-root.service" \
"${systemdsystemconfdir}/initrd-switch-root.target.wants/ostree-prepare-root.service"
"${systemdsystemconfdir}/initrd-root-fs.target.wants/ostree-prepare-root.service"
}
+28
View File
@@ -0,0 +1,28 @@
# Copyright (C) 2018 Red Hat, Inc.
#
# 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.
# For some implementation discussion, see:
# https://lists.freedesktop.org/archives/systemd-devel/2018-March/040557.html
[Unit]
Description=OSTree Monitor Staged Deployment
Documentation=man:ostree(1)
[Path]
PathExists=/run/ostree/staged-deployment
[Install]
WantedBy=multi-user.target
+5 -3
View File
@@ -19,6 +19,7 @@
# https://lists.freedesktop.org/archives/systemd-devel/2018-March/040557.html
[Unit]
Description=OSTree Finalize Staged Deployment
Documentation=man:ostree(1)
ConditionPathExists=/run/ostree-booted
DefaultDependencies=no
@@ -31,6 +32,7 @@ Conflicts=final.target
Type=oneshot
RemainAfterExit=yes
ExecStop=/usr/bin/ostree admin finalize-staged
[Install]
WantedBy=multi-user.target
# This is a quite long timeout intentionally; the failure mode
# here is that people don't get an upgrade. We need to handle
# cases with slow rotational media, etc.
TimeoutStopSec=5m
+4 -3
View File
@@ -17,13 +17,13 @@
[Unit]
Description=OSTree Prepare OS/
Documentation=man:ostree(1)
DefaultDependencies=no
ConditionKernelCommandLine=ostree
ConditionPathExists=/etc/initrd-release
OnFailure=emergency.target
After=initrd-switch-root.target
Before=initrd-switch-root.service
Before=plymouth-switch-root.service
After=sysroot.mount
Before=initrd-root-fs.target
[Service]
Type=oneshot
@@ -31,3 +31,4 @@ ExecStart=/usr/lib/ostree/ostree-prepare-root /sysroot
StandardInput=null
StandardOutput=syslog
StandardError=syslog+console
RemainAfterExit=yes
+2 -1
View File
@@ -16,7 +16,8 @@
# Boston, MA 02111-1307, USA.
[Unit]
Description=OSTree Remount OS/ bind mounts
Description=OSTree Remount OS/ Bind Mounts
Documentation=man:ostree(1)
DefaultDependencies=no
ConditionKernelCommandLine=ostree
OnFailure=emergency.target
-2
View File
@@ -18,8 +18,6 @@
***/
/* Add new symbols here. Release commits should copy this section into -released.sym. */
LIBOSTREE_2018.9 {
} LIBOSTREE_2018.7;
/* Stub section for the stable release *after* this development one; don't
* edit this other than to update the last number. This is just a copy/paste
+6
View File
@@ -536,6 +536,12 @@ global:
/* No new symbols in 2018.8 */
LIBOSTREE_2018.9 {
ostree_mutable_tree_remove;
ostree_repo_get_min_free_space_bytes;
ostree_repo_get_default_repo_finders;
} LIBOSTREE_2018.7;
/* NOTE: Only add more content here in release commits! See the
* comments at the top of this file.
*/
+1 -1
View File
@@ -228,7 +228,7 @@ idle_invoke_async_progress (gpointer user_data)
OstreeAsyncProgress *self = user_data;
g_mutex_lock (&self->lock);
self->idle_source = NULL;
g_clear_pointer (&self->idle_source, g_source_unref);
g_mutex_unlock (&self->lock);
g_signal_emit (self, signals[CHANGED], 0);
+12 -6
View File
@@ -57,7 +57,8 @@ struct _OstreeBootloaderGrub2
GObject parent_instance;
OstreeSysroot *sysroot;
GFile *config_path_bios;
GFile *config_path_bios_1;
GFile *config_path_bios_2;
GFile *config_path_efi;
gboolean is_efi;
};
@@ -77,7 +78,8 @@ _ostree_bootloader_grub2_query (OstreeBootloader *bootloader,
OstreeBootloaderGrub2 *self = OSTREE_BOOTLOADER_GRUB2 (bootloader);
/* Look for the BIOS path first */
if (g_file_query_exists (self->config_path_bios, NULL))
if (g_file_query_exists (self->config_path_bios_1, NULL) ||
g_file_query_exists (self->config_path_bios_2, NULL))
{
/* If we found it, we're done */
*out_is_active = TRUE;
@@ -97,7 +99,7 @@ _ostree_bootloader_grub2_query (OstreeBootloader *bootloader,
cancellable, error);
if (!direnum)
return FALSE;
while (TRUE)
{
GFileInfo *file_info;
@@ -448,7 +450,7 @@ _ostree_bootloader_grub2_write_config (OstreeBootloader *bootloader,
}
static gboolean
_ostree_bootloader_grub2_is_atomic (OstreeBootloader *bootloader)
_ostree_bootloader_grub2_is_atomic (OstreeBootloader *bootloader)
{
OstreeBootloaderGrub2 *self = OSTREE_BOOTLOADER_GRUB2 (bootloader);
return !self->is_efi;
@@ -460,7 +462,8 @@ _ostree_bootloader_grub2_finalize (GObject *object)
OstreeBootloaderGrub2 *self = OSTREE_BOOTLOADER_GRUB2 (object);
g_clear_object (&self->sysroot);
g_clear_object (&self->config_path_bios);
g_clear_object (&self->config_path_bios_1);
g_clear_object (&self->config_path_bios_2);
g_clear_object (&self->config_path_efi);
G_OBJECT_CLASS (_ostree_bootloader_grub2_parent_class)->finalize (object);
@@ -493,6 +496,9 @@ _ostree_bootloader_grub2_new (OstreeSysroot *sysroot)
{
OstreeBootloaderGrub2 *self = g_object_new (OSTREE_TYPE_BOOTLOADER_GRUB2, NULL);
self->sysroot = g_object_ref (sysroot);
self->config_path_bios = g_file_resolve_relative_path (self->sysroot->path, "boot/grub2/grub.cfg");
/* Used by (at least) Debian */
self->config_path_bios_1 = g_file_resolve_relative_path (self->sysroot->path, "boot/grub/grub.cfg");
/* Used by (at least) Fedora */
self->config_path_bios_2 = g_file_resolve_relative_path (self->sysroot->path, "boot/grub2/grub.cfg");
return self;
}
+7 -7
View File
@@ -31,18 +31,18 @@ G_BEGIN_DECLS
/**
* OSTREE_MAX_METADATA_SIZE:
*
* Maximum permitted size in bytes of metadata objects. This is an
* arbitrary number, but really, no one should be putting humongous
* data in metadata.
*
* Default limit for maximum permitted size in bytes of metadata objects fetched
* over HTTP (including repo/config files, refs, and commit/dirtree/dirmeta
* objects). This is an arbitrary number intended to mitigate disk space
* exhaustion attacks.
*/
#define OSTREE_MAX_METADATA_SIZE (10 * 1024 * 1024)
/**
* OSTREE_MAX_METADATA_WARN_SIZE:
*
* Objects committed above this size will be allowed, but a warning
* will be emitted.
*
* This variable is no longer meaningful, it is kept only for compatibility.
*/
#define OSTREE_MAX_METADATA_WARN_SIZE (7 * 1024 * 1024)
+8 -7
View File
@@ -172,6 +172,9 @@ _ostree_fetcher_finalize (GObject *object)
curl_multi_cleanup (self->multi);
g_free (self->remote_name);
g_free (self->tls_ca_db_path);
g_free (self->tls_client_cert_path);
g_free (self->tls_client_key_path);
g_free (self->cookie_jar_path);
g_free (self->proxy);
g_assert_cmpint (g_hash_table_size (self->outstanding_requests), ==, 0);
@@ -319,17 +322,15 @@ check_multi_info (OstreeFetcher *fetcher)
{
/* Handle file not found */
g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
"%s",
curl_easy_strerror (curlres));
"%s", curl_easy_strerror (curlres));
}
else
{
g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_FAILED, "[%u] %s",
curlres,
g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_FAILED,
"While fetching %s: [%u] %s", eff_url, curlres,
curl_easy_strerror (curlres));
if (req->fetcher->remote_name)
_ostree_fetcher_journal_failure (req->fetcher->remote_name,
eff_url, curl_easy_strerror (curlres));
_ostree_fetcher_journal_failure (req->fetcher->remote_name,
eff_url, curl_easy_strerror (curlres));
}
}
else
+1
View File
@@ -172,6 +172,7 @@ _ostree_fetcher_journal_failure (const char *remote_name,
"MESSAGE_ID=" SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(OSTREE_HTTP_FAILURE_ID),
"OSTREE_REMOTE=%s", remote_name,
"OSTREE_URL=%s", url,
"PRIORITY=%i", LOG_ERR,
NULL);
#endif
}
+67 -64
View File
@@ -305,31 +305,57 @@ ostree_mutable_tree_replace_file (OstreeMutableTree *self,
const char *checksum,
GError **error)
{
gboolean ret = FALSE;
g_return_val_if_fail (name != NULL, FALSE);
if (!ot_util_filename_validate (name, error))
goto out;
return FALSE;
if (!_ostree_mutable_tree_make_whole (self, NULL, error))
goto out;
return FALSE;
if (g_hash_table_lookup (self->subdirs, name))
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"Can't replace directory with file: %s", name);
goto out;
}
return glnx_throw (error, "Can't replace directory with file: %s", name);
invalidate_contents_checksum (self);
g_hash_table_replace (self->files,
g_strdup (name),
g_strdup (checksum));
return TRUE;
}
ret = TRUE;
out:
return ret;
/**
* ostree_mutable_tree_remove:
* @self: Tree
* @name: Name of file or subdirectory to remove
* @allow_noent: If @FALSE, an error will be thrown if @name does not exist in the tree
* @error: a #GError
*
* Remove the file or subdirectory named @name from the mutable tree @self.
*/
gboolean
ostree_mutable_tree_remove (OstreeMutableTree *self,
const char *name,
gboolean allow_noent,
GError **error)
{
g_return_val_if_fail (name != NULL, FALSE);
if (!ot_util_filename_validate (name, error))
return FALSE;
if (!_ostree_mutable_tree_make_whole (self, NULL, error))
return FALSE;
if (!g_hash_table_remove (self->files, name) &&
!g_hash_table_remove (self->subdirs, name))
{
if (allow_noent)
return TRUE; /* NB: early return */
return set_error_noent (error, name);
}
invalidate_contents_checksum (self);
return TRUE;
}
/**
@@ -348,36 +374,29 @@ ostree_mutable_tree_ensure_dir (OstreeMutableTree *self,
OstreeMutableTree **out_subdir,
GError **error)
{
gboolean ret = FALSE;
g_autoptr(OstreeMutableTree) ret_dir = NULL;
g_return_val_if_fail (name != NULL, FALSE);
if (!ot_util_filename_validate (name, error))
goto out;
return FALSE;
if (!_ostree_mutable_tree_make_whole (self, NULL, error))
goto out;
return FALSE;
if (g_hash_table_lookup (self->files, name))
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"Can't replace file with directory: %s", name);
goto out;
}
return glnx_throw (error, "Can't replace file with directory: %s", name);
ret_dir = ot_gobject_refz (g_hash_table_lookup (self->subdirs, name));
g_autoptr(OstreeMutableTree) ret_dir =
ot_gobject_refz (g_hash_table_lookup (self->subdirs, name));
if (!ret_dir)
{
ret_dir = ostree_mutable_tree_new ();
invalidate_contents_checksum (self);
insert_child_mtree (self, name, g_object_ref (ret_dir));
}
ret = TRUE;
ot_transfer_out_value (out_subdir, &ret_dir);
out:
return ret;
if (out_subdir)
*out_subdir = g_steal_pointer (&ret_dir);
return TRUE;
}
gboolean
@@ -387,29 +406,24 @@ ostree_mutable_tree_lookup (OstreeMutableTree *self,
OstreeMutableTree **out_subdir,
GError **error)
{
gboolean ret = FALSE;
g_autoptr(OstreeMutableTree) ret_subdir = NULL;
g_autofree char *ret_file_checksum = NULL;
if (!_ostree_mutable_tree_make_whole (self, NULL, error))
goto out;
return FALSE;
ret_subdir = ot_gobject_refz (g_hash_table_lookup (self->subdirs, name));
g_autofree char *ret_file_checksum = NULL;
g_autoptr(OstreeMutableTree) ret_subdir =
ot_gobject_refz (g_hash_table_lookup (self->subdirs, name));
if (!ret_subdir)
{
ret_file_checksum = g_strdup (g_hash_table_lookup (self->files, name));
if (!ret_file_checksum)
{
set_error_noent (error, name);
goto out;
}
return set_error_noent (error, name);
}
ret = TRUE;
ot_transfer_out_value (out_file_checksum, &ret_file_checksum);
ot_transfer_out_value (out_subdir, &ret_subdir);
out:
return ret;
if (out_file_checksum)
*out_file_checksum = g_steal_pointer (&ret_file_checksum);
if (out_subdir)
*out_subdir = g_steal_pointer (&ret_subdir);
return TRUE;
}
/**
@@ -430,49 +444,38 @@ ostree_mutable_tree_ensure_parent_dirs (OstreeMutableTree *self,
OstreeMutableTree **out_parent,
GError **error)
{
gboolean ret = FALSE;
int i;
OstreeMutableTree *subdir = self; /* nofree */
g_autoptr(OstreeMutableTree) ret_parent = NULL;
g_assert (metadata_checksum != NULL);
if (!_ostree_mutable_tree_make_whole (self, NULL, error))
goto out;
g_assert (metadata_checksum != NULL);
return FALSE;
if (!self->metadata_checksum)
ostree_mutable_tree_set_metadata_checksum (self, metadata_checksum);
for (i = 0; i+1 < split_path->len; i++)
OstreeMutableTree *subdir = self; /* nofree */
for (guint i = 0; i+1 < split_path->len; i++)
{
OstreeMutableTree *next;
const char *name = split_path->pdata[i];
if (g_hash_table_lookup (subdir->files, name))
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"Can't replace file with directory: %s", name);
goto out;
}
return glnx_throw (error, "Can't replace file with directory: %s", name);
next = g_hash_table_lookup (subdir->subdirs, name);
if (!next)
if (!next)
{
invalidate_contents_checksum (subdir);
next = ostree_mutable_tree_new ();
ostree_mutable_tree_set_metadata_checksum (next, metadata_checksum);
insert_child_mtree (subdir, g_strdup (name), next);
}
subdir = next;
}
ret_parent = g_object_ref (subdir);
ret = TRUE;
ot_transfer_out_value (out_parent, &ret_parent);
out:
return ret;
if (out_parent)
*out_parent = g_object_ref (subdir);
return TRUE;
}
const char empty_tree_csum[] = "6e340b9cffb37a989ca544e6bb780a2c78901d3fb33738768511a30617afa01d";
+6
View File
@@ -77,6 +77,12 @@ gboolean ostree_mutable_tree_replace_file (OstreeMutableTree *self,
const char *checksum,
GError **error);
_OSTREE_PUBLIC
gboolean ostree_mutable_tree_remove (OstreeMutableTree *self,
const char *name,
gboolean allow_noent,
GError **error);
_OSTREE_PUBLIC
gboolean ostree_mutable_tree_ensure_dir (OstreeMutableTree *self,
const char *name,
+38 -4
View File
@@ -196,6 +196,7 @@ static gboolean
create_file_copy_from_input_at (OstreeRepo *repo,
OstreeRepoCheckoutAtOptions *options,
CheckoutState *state,
const char *checksum,
GFileInfo *file_info,
GVariant *xattrs,
GInputStream *input,
@@ -358,8 +359,35 @@ create_file_copy_from_input_at (OstreeRepo *repo,
replace_mode = GLNX_LINK_TMPFILE_NOREPLACE_IGNORE_EXIST;
break;
case OSTREE_REPO_CHECKOUT_OVERWRITE_UNION_IDENTICAL:
/* We don't support copying in union identical */
g_assert_not_reached ();
{
replace_mode = GLNX_LINK_TMPFILE_NOREPLACE;
struct stat dest_stbuf;
if (!glnx_fstatat_allow_noent (destination_dfd, destination_name, &dest_stbuf,
AT_SYMLINK_NOFOLLOW, error))
return FALSE;
if (errno == 0)
{
/* We do a checksum comparison; see also equivalent code in
* checkout_file_hardlink().
*/
OstreeChecksumFlags flags = 0;
if (repo->disable_xattrs)
flags |= OSTREE_CHECKSUM_FLAGS_IGNORE_XATTRS;
g_autofree char *actual_checksum = NULL;
if (!ostree_checksum_file_at (destination_dfd, destination_name,
&dest_stbuf, OSTREE_OBJECT_TYPE_FILE,
flags, &actual_checksum, cancellable, error))
return FALSE;
if (g_str_equal (checksum, actual_checksum))
return TRUE;
/* Otherwise, fall through and do the link, we should
* get EEXIST.
*/
}
}
break;
}
@@ -586,6 +614,7 @@ checkout_one_file_at (OstreeRepo *repo,
const gboolean is_symlink = (g_file_info_get_file_type (source_info) == G_FILE_TYPE_SYMBOLIC_LINK);
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);
/* First, see if it's a Docker whiteout,
* https://github.com/docker/docker/blob/1a714e76a2cb9008cd19609059e9988ff1660b78/pkg/archive/whiteouts.go
@@ -604,6 +633,10 @@ checkout_one_file_at (OstreeRepo *repo,
need_copy = FALSE;
}
else if (options->force_copy_zerosized && is_reg_zerosized)
{
need_copy = TRUE;
}
else if (!options->force_copy)
{
HardlinkResult hardlink_res = HARDLINK_RESULT_NOT_SUPPORTED;
@@ -699,6 +732,7 @@ checkout_one_file_at (OstreeRepo *repo,
if (can_cache
&& !is_whiteout
&& !is_symlink
&& !is_reg_zerosized
&& need_copy
&& repo->mode == OSTREE_REPO_MODE_ARCHIVE
&& options->mode == OSTREE_REPO_CHECKOUT_MODE_USER)
@@ -762,12 +796,12 @@ checkout_one_file_at (OstreeRepo *repo,
* succeeded at hardlinking above.
*/
if (options->no_copy_fallback)
g_assert (is_bare_user_symlink);
g_assert (is_bare_user_symlink || is_reg_zerosized);
if (!ostree_repo_load_file (repo, checksum, &input, NULL, &xattrs,
cancellable, error))
return FALSE;
if (!create_file_copy_from_input_at (repo, options, state, source_info, xattrs, input,
if (!create_file_copy_from_input_at (repo, options, state, checksum, source_info, xattrs, input,
destination_dfd, destination_name,
cancellable, error))
return glnx_prefix_error (error, "Copy checkout of %s to %s", checksum, destination_name);
+14 -71
View File
@@ -245,16 +245,7 @@ commit_loose_regfile_object (OstreeRepo *self,
GCancellable *cancellable,
GError **error)
{
/* We may be writing as root to a non-root-owned repository; if so,
* automatically inherit the non-root ownership.
*/
if (self->mode == OSTREE_REPO_MODE_ARCHIVE
&& self->target_owner_uid != -1)
{
if (fchown (tmpf->fd, self->target_owner_uid, self->target_owner_gid) < 0)
return glnx_throw_errno_prefix (error, "fchown");
}
else if (self->mode == OSTREE_REPO_MODE_BARE)
if (self->mode == OSTREE_REPO_MODE_BARE)
{
if (TEMP_FAILURE_RETRY (fchown (tmpf->fd, uid, gid)) < 0)
return glnx_throw_errno_prefix (error, "fchown");
@@ -1336,18 +1327,6 @@ write_metadata_object (OstreeRepo *self,
gsize len;
const guint8 *bufp = g_bytes_get_data (buf, &len);
/* Do the size warning here, to avoid warning for already extant metadata */
if (G_UNLIKELY (len > OSTREE_MAX_METADATA_WARN_SIZE))
{
g_autofree char *metasize = g_format_size (len);
g_autofree char *warnsize = g_format_size (OSTREE_MAX_METADATA_WARN_SIZE);
g_autofree char *maxsize = g_format_size (OSTREE_MAX_METADATA_SIZE);
g_warning ("metadata object %s is %s, which is larger than the warning threshold of %s." \
" The hard limit on metadata size is %s. Put large content in the tree itself, not in metadata.",
actual_checksum,
metasize, warnsize, maxsize);
}
/* Write the metadata to a temporary file */
g_auto(GLnxTmpfile) tmpf = { 0, };
if (!glnx_open_tmpfile_linkable_at (commit_tmp_dfd (self), ".", O_WRONLY|O_CLOEXEC,
@@ -1536,25 +1515,6 @@ devino_cache_lookup (OstreeRepo *self,
return dev_ino_val->checksum;
}
static guint64
min_free_space_calculate_reserved_blocks (OstreeRepo *self, struct statvfs *stvfsbuf)
{
guint64 reserved_blocks = 0;
if (self->min_free_space_mb > 0)
{
reserved_blocks = (self->min_free_space_mb << 20) / stvfsbuf->f_bsize;
}
else if (self->min_free_space_percent > 0)
{
/* Convert fragment to blocks to compute the total */
guint64 total_blocks = (stvfsbuf->f_frsize * stvfsbuf->f_blocks) / stvfsbuf->f_bsize;
reserved_blocks = ((double)total_blocks) * (self->min_free_space_percent/100.0);
}
return reserved_blocks;
}
/**
* ostree_repo_scan_hardlinks:
* @self: An #OstreeRepo
@@ -1626,6 +1586,7 @@ ostree_repo_prepare_transaction (OstreeRepo *self,
GError **error)
{
g_autoptr(_OstreeRepoAutoTransaction) txn = NULL;
guint64 reserved_bytes = 0;
g_return_val_if_fail (self->in_transaction == FALSE, FALSE);
@@ -1650,11 +1611,17 @@ ostree_repo_prepare_transaction (OstreeRepo *self,
g_mutex_lock (&self->txn_lock);
self->txn.blocksize = stvfsbuf.f_bsize;
guint64 reserved_blocks = min_free_space_calculate_reserved_blocks (self, &stvfsbuf);
if (!ostree_repo_get_min_free_space_bytes (self, &reserved_bytes, error))
{
g_mutex_unlock (&self->txn_lock);
return FALSE;
}
self->reserved_blocks = reserved_bytes / self->txn.blocksize;
/* Use the appropriate free block count if we're unprivileged */
guint64 bfree = (getuid () != 0 ? stvfsbuf.f_bavail : stvfsbuf.f_bfree);
if (bfree > reserved_blocks)
self->txn.max_blocks = bfree - reserved_blocks;
if (bfree > self->reserved_blocks)
self->txn.max_blocks = bfree - self->reserved_blocks;
else
{
self->cleanup_stagedir = TRUE;
@@ -2290,25 +2257,6 @@ ostree_repo_abort_transaction (OstreeRepo *self,
return TRUE;
}
/* These limits were introduced since in some cases we may be processing
* malicious metadata, and we want to make disk space exhaustion attacks harder.
*/
static gboolean
metadata_size_valid (OstreeObjectType objtype,
gsize len,
GError **error)
{
if (G_UNLIKELY (len > OSTREE_MAX_METADATA_SIZE))
{
g_autofree char *input_bytes = g_format_size (len);
g_autofree char *max_bytes = g_format_size (OSTREE_MAX_METADATA_SIZE);
return glnx_throw (error, "Metadata object of type '%s' is %s; maximum metadata size is %s",
ostree_object_type_to_string (objtype), input_bytes, max_bytes);
}
return TRUE;
}
/**
* ostree_repo_write_metadata:
* @self: Repo
@@ -2361,9 +2309,6 @@ ostree_repo_write_metadata (OstreeRepo *self,
normalized = g_variant_get_normal_form (object);
}
if (!metadata_size_valid (objtype, g_variant_get_size (normalized), error))
return FALSE;
/* For untrusted objects, verify their structure here */
if (expected_checksum)
{
@@ -2401,9 +2346,6 @@ ostree_repo_write_metadata_stream_trusted (OstreeRepo *self,
GCancellable *cancellable,
GError **error)
{
if (length > 0 && !metadata_size_valid (objtype, length, error))
return FALSE;
/* This is all pretty ridiculous, but we're keeping this API for backwards
* compatibility, it doesn't really need to be fast.
*/
@@ -4305,11 +4247,12 @@ import_one_object_direct (OstreeRepo *dest_repo,
}
/* Don't want to copy xattrs for archive repos, nor for
* bare-user-only.
* bare-user-only. We also only do this for content
* objects.
*/
const gboolean src_is_bare_or_bare_user =
G_IN_SET (src_repo->mode, OSTREE_REPO_MODE_BARE, OSTREE_REPO_MODE_BARE_USER);
if (src_is_bare_or_bare_user)
if (src_is_bare_or_bare_user && !OSTREE_OBJECT_TYPE_IS_META(objtype))
{
g_autoptr(GVariant) xattrs = NULL;
+27 -18
View File
@@ -509,6 +509,16 @@ fill_refs_and_checksums_from_summary (GVariant *summary,
return TRUE;
}
static gboolean
remove_null_checksum_refs_cb (gpointer key,
gpointer value,
gpointer user_data)
{
const char *checksum = value;
return (checksum == NULL);
}
/* Given a summary file (@summary_bytes), extract the refs it lists, and use that
* to fill in the checksums in the @supported_ref_to_checksum map. This includes
* the main refs list in the summary, and the map of collection IDs to further
@@ -518,14 +528,13 @@ fill_refs_and_checksums_from_summary (GVariant *summary,
* set and %FALSE will be returned. If the intersection of the summary file refs
* and the keys in @supported_ref_to_checksum is empty, an error is set. */
static gboolean
get_refs_and_checksums_from_summary (GBytes *summary_bytes,
GHashTable *supported_ref_to_checksum /* (element-type OstreeCollectionRef utf8) */,
GError **error)
get_refs_and_checksums_from_summary (GBytes *summary_bytes,
GHashTable *supported_ref_to_checksum /* (element-type OstreeCollectionRef utf8) */,
OstreeRemote *remote,
GError **error)
{
g_autoptr(GVariant) summary = g_variant_ref_sink (g_variant_new_from_bytes (OSTREE_SUMMARY_GVARIANT_FORMAT, summary_bytes, FALSE));
GHashTableIter iter;
const OstreeCollectionRef *ref;
const gchar *checksum;
guint removed_refs;
if (!g_variant_is_normal_form (summary))
{
@@ -544,20 +553,20 @@ get_refs_and_checksums_from_summary (GBytes *summary_bytes,
if (!fill_refs_and_checksums_from_summary (summary, supported_ref_to_checksum, error))
return FALSE;
/* Check that at least one of the refs has a non-%NULL checksum set, otherwise
* we can discard this peer. */
g_hash_table_iter_init (&iter, supported_ref_to_checksum);
while (g_hash_table_iter_next (&iter,
(gpointer *) &ref,
(gpointer *) &checksum))
removed_refs = g_hash_table_foreach_remove (supported_ref_to_checksum, remove_null_checksum_refs_cb, NULL);
if (removed_refs > 0)
g_debug ("Removed %d refs from the list resolved from ‘%s’ (possibly bloom filter false positives)",
removed_refs, remote->name);
/* If none of the refs had a non-%NULL checksum set, we can discard this peer. */
if (g_hash_table_size (supported_ref_to_checksum) == 0)
{
if (checksum != NULL)
return TRUE;
g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
"No matching refs were found in the summary file");
return FALSE;
}
g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
"No matching refs were found in the summary file");
return FALSE;
return TRUE;
}
/* Download the summary file from @remote, and return the bytes of the file in
@@ -661,7 +670,7 @@ get_checksums (OstreeRepoFinderAvahi *finder,
return FALSE;
}
return get_refs_and_checksums_from_summary (summary_bytes, supported_ref_to_checksum, error);
return get_refs_and_checksums_from_summary (summary_bytes, supported_ref_to_checksum, remote, error);
}
/* Build some #OstreeRepoFinderResults out of the given #OstreeAvahiService by
+2 -2
View File
@@ -149,10 +149,9 @@ struct OstreeRepo {
dev_t device;
ino_t inode;
uid_t owner_uid; /* Cache of repo's owner uid */
uid_t target_owner_uid; /* Ensure files are chowned to this uid/gid */
gid_t target_owner_gid;
guint min_free_space_percent; /* See the min-free-space-percent config option */
guint64 min_free_space_mb; /* See the min-free-space-size config option */
guint64 reserved_blocks;
gboolean cleanup_stagedir;
guint test_error_flags; /* OstreeRepoTestErrorFlags */
@@ -169,6 +168,7 @@ struct OstreeRepo {
gint lock_timeout_seconds;
guint64 payload_link_threshold;
gint fs_support_reflink; /* The underlying filesystem has support for ioctl (FICLONE..) */
gchar **repo_finders;
OstreeRepo *parent_repo;
};
+44 -27
View File
@@ -150,6 +150,7 @@ typedef struct {
gboolean timestamp_check; /* Verify commit timestamps */
int maxdepth;
guint64 max_metadata_size;
guint64 start_time;
gboolean is_mirror;
@@ -2193,7 +2194,7 @@ start_fetch (OtPullData *pull_data,
if (expected_max_size_p)
expected_max_size = *expected_max_size_p;
else if (OSTREE_OBJECT_TYPE_IS_META (objtype))
expected_max_size = OSTREE_MAX_METADATA_SIZE;
expected_max_size = pull_data->max_metadata_size;
else
expected_max_size = 0;
@@ -3488,6 +3489,7 @@ initiate_request (OtPullData *pull_data,
* * require-static-deltas (b): Require static deltas
* * override-commit-ids (as): Array of specific commit IDs to fetch for refs
* * timestamp-check (b): Verify commit timestamps are newer than current (when pulling via ref); Since: 2017.11
* * metadata-size-restriction (t): Restrict metadata objects to a maximum number of bytes; 0 to disable. Since: 2018.9
* * dry-run (b): Only print information on what will be downloaded (requires static deltas)
* * override-url (s): Fetch objects from this URL if remote specifies no metalink in options
* * inherit-transaction (b): Don't initiate, finish or abort a transaction, useful to do multiple pulls in one transaction.
@@ -3543,6 +3545,9 @@ ostree_repo_pull_with_options (OstreeRepo *self,
*/
const char *the_ref_to_fetch = NULL;
/* Default */
pull_data->max_metadata_size = OSTREE_MAX_METADATA_SIZE;
if (options)
{
int flags_i = OSTREE_REPO_PULL_FLAGS_NONE;
@@ -3570,6 +3575,7 @@ ostree_repo_pull_with_options (OstreeRepo *self,
(void) g_variant_lookup (options, "update-frequency", "u", &update_frequency);
(void) g_variant_lookup (options, "localcache-repos", "^a&s", &opt_localcache_repos);
(void) g_variant_lookup (options, "timestamp-check", "b", &pull_data->timestamp_check);
(void) g_variant_lookup (options, "max-metadata-size", "t", &pull_data->max_metadata_size);
(void) g_variant_lookup (options, "append-user-agent", "s", &pull_data->append_user_agent);
opt_n_network_retries_set =
g_variant_lookup (options, "n-network-retries", "u", &pull_data->n_network_retries);
@@ -4993,45 +4999,56 @@ ostree_repo_find_remotes_async (OstreeRepo *self,
/* Are we using #OstreeRepoFinders provided by the user, or the defaults? */
if (finders == NULL)
{
guint finder_index = 0;
#ifdef HAVE_AVAHI
guint avahi_index;
GMainContext *context = g_main_context_get_thread_default ();
g_autoptr(GError) local_error = NULL;
#endif /* HAVE_AVAHI */
finder_config = OSTREE_REPO_FINDER (ostree_repo_finder_config_new ());
finder_mount = OSTREE_REPO_FINDER (ostree_repo_finder_mount_new (NULL));
if (g_strv_contains ((const char * const *)self->repo_finders, "config"))
default_finders[finder_index++] = finder_config = OSTREE_REPO_FINDER (ostree_repo_finder_config_new ());
if (g_strv_contains ((const char * const *)self->repo_finders, "mount"))
default_finders[finder_index++] = finder_mount = OSTREE_REPO_FINDER (ostree_repo_finder_mount_new (NULL));
#ifdef HAVE_AVAHI
finder_avahi = OSTREE_REPO_FINDER (ostree_repo_finder_avahi_new (context));
if (g_strv_contains ((const char * const *)self->repo_finders, "lan"))
{
avahi_index = finder_index;
default_finders[finder_index++] = finder_avahi = OSTREE_REPO_FINDER (ostree_repo_finder_avahi_new (context));
}
#endif /* HAVE_AVAHI */
default_finders[0] = finder_config;
default_finders[1] = finder_mount;
default_finders[2] = finder_avahi;
/* self->repo_finders is guaranteed to be non-empty */
g_assert (default_finders != NULL);
finders = default_finders;
#ifdef HAVE_AVAHI
ostree_repo_finder_avahi_start (OSTREE_REPO_FINDER_AVAHI (finder_avahi),
&local_error);
if (local_error != NULL)
if (finder_avahi != NULL)
{
/* See ostree-repo-finder-avahi.c:ostree_repo_finder_avahi_start, we
* intentionally throw this so as to distinguish between the Avahi
* finder failing because the Avahi daemon wasn't running and
* the Avahi finder failing because of some actual error.
*
* We need to distinguish between g_debug and g_warning here because
* unit tests that use this code may set G_DEBUG=fatal-warnings which
* would cause client code to abort if a warning were emitted.
*/
if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
g_debug ("Avahi finder failed under normal operation; removing it: %s", local_error->message);
else
g_warning ("Avahi finder failed abnormally; removing it: %s", local_error->message);
ostree_repo_finder_avahi_start (OSTREE_REPO_FINDER_AVAHI (finder_avahi),
&local_error);
default_finders[2] = NULL;
g_clear_object (&finder_avahi);
if (local_error != NULL)
{
/* See ostree-repo-finder-avahi.c:ostree_repo_finder_avahi_start, we
* intentionally throw this so as to distinguish between the Avahi
* finder failing because the Avahi daemon wasn't running and
* the Avahi finder failing because of some actual error.
*
* We need to distinguish between g_debug and g_warning here because
* unit tests that use this code may set G_DEBUG=fatal-warnings which
* would cause client code to abort if a warning were emitted.
*/
if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
g_debug ("Avahi finder failed under normal operation; removing it: %s", local_error->message);
else
g_warning ("Avahi finder failed abnormally; removing it: %s", local_error->message);
default_finders[avahi_index] = NULL;
g_clear_object (&finder_avahi);
}
}
#endif /* HAVE_AVAHI */
}
+124 -23
View File
@@ -90,17 +90,19 @@ G_STATIC_ASSERT(sizeof(OstreeRepoPruneOptions) ==
* The #OstreeRepo is like git, a content-addressed object store.
* Unlike git, it records uid, gid, and extended attributes.
*
* There are three possible "modes" for an #OstreeRepo;
* %OSTREE_REPO_MODE_BARE is very simple - content files are
* represented exactly as they are, and checkouts are just hardlinks.
* %OSTREE_REPO_MODE_BARE_USER is similar, except the uid/gids are not
* set on the files, and checkouts as hardlinks hardlinks work only for user checkouts.
* A %OSTREE_REPO_MODE_ARCHIVE_Z2 repository in contrast stores
* content files zlib-compressed. It is suitable for non-root-owned
* There are four possible "modes" for an #OstreeRepo; %OSTREE_REPO_MODE_BARE
* is very simple - content files are represented exactly as they are, and
* checkouts are just hardlinks. %OSTREE_REPO_MODE_BARE_USER is similar, except
* the uid/gids are not set on the files, and checkouts as hardlinks work only
* for user checkouts. %OSTREE_REPO_MODE_BARE_USER_ONLY is the same as
* BARE_USER, but all metadata is not stored, so it can only be used for user
* checkouts. This mode does not require xattrs. A %OSTREE_REPO_MODE_ARCHIVE
* (also known as %OSTREE_REPO_MODE_ARCHIVE_Z2) repository in contrast stores
* content files zlib-compressed. It is suitable for non-root-owned
* repositories that can be served via a static HTTP server.
*
* Creating an #OstreeRepo does not invoke any file I/O, and thus needs
* to be initialized, either from an existing contents or with a new
* to be initialized, either from existing contents or as a new
* repository. If you have an existing repo, use ostree_repo_open()
* to load it from disk and check its validity. To initialize a new
* repository in the given filepath, use ostree_repo_create() instead.
@@ -452,9 +454,9 @@ pop_repo_lock (OstreeRepo *self,
* state is not changed and the stack is simply updated.
*
* ostree_repo_lock_push() waits for the lock depending on the repository's
* lock-timeout configuration. When lock-timeout is -1, a blocking lock is
* lock-timeout-secs configuration. When lock-timeout-secs is -1, a blocking lock is
* attempted. Otherwise, the lock is taken non-blocking and
* ostree_repo_lock_push() will sleep synchronously up to lock-timeout seconds
* ostree_repo_lock_push() will sleep synchronously up to lock-timeout-secs seconds
* attempting to acquire the lock. If the lock cannot be acquired within the
* timeout, a %G_IO_ERROR_WOULD_BLOCK error is returned.
*
@@ -542,9 +544,9 @@ _ostree_repo_lock_push (OstreeRepo *self,
* lock.
*
* ostree_repo_lock_pop() waits for the lock depending on the repository's
* lock-timeout configuration. When lock-timeout is -1, a blocking lock is
* lock-timeout-secs configuration. When lock-timeout-secs is -1, a blocking lock is
* attempted. Otherwise, the lock is removed non-blocking and
* ostree_repo_lock_pop() will sleep synchronously up to lock-timeout seconds
* ostree_repo_lock_pop() will sleep synchronously up to lock-timeout-secs seconds
* attempting to remove the lock. If the lock cannot be removed within the
* timeout, a %G_IO_ERROR_WOULD_BLOCK error is returned.
*
@@ -1033,6 +1035,7 @@ ostree_repo_finalize (GObject *object)
g_mutex_clear (&self->cache_lock);
g_mutex_clear (&self->txn_lock);
g_free (self->collection_id);
g_strfreev (self->repo_finders);
g_clear_pointer (&self->remotes, g_hash_table_destroy);
g_mutex_clear (&self->remotes_lock);
@@ -2653,6 +2656,37 @@ get_remotes_d_dir (OstreeRepo *self,
return g_file_resolve_relative_path (sysroot, SYSCONF_REMOTES);
}
static gboolean
min_free_space_calculate_reserved_bytes (OstreeRepo *self, guint64 *bytes, GError **error)
{
guint64 reserved_bytes = 0;
struct statvfs stvfsbuf;
if (TEMP_FAILURE_RETRY (fstatvfs (self->repo_dir_fd, &stvfsbuf)) < 0)
return glnx_throw_errno_prefix (error, "fstatvfs");
if (self->min_free_space_mb > 0)
{
if (self->min_free_space_mb > (G_MAXUINT64 >> 20))
return glnx_throw (error, "min-free-space value is greater than the maximum allowed value of %" G_GUINT64_FORMAT " bytes",
(G_MAXUINT64 >> 20));
reserved_bytes = self->min_free_space_mb << 20;
}
else if (self->min_free_space_percent > 0)
{
if (stvfsbuf.f_frsize > (G_MAXUINT64 / stvfsbuf.f_blocks))
return glnx_throw (error, "Filesystem's size is greater than the maximum allowed value of %" G_GUINT64_FORMAT " bytes",
(G_MAXUINT64 / stvfsbuf.f_blocks));
guint64 total_bytes = (stvfsbuf.f_frsize * stvfsbuf.f_blocks);
reserved_bytes = ((double)total_bytes) * (self->min_free_space_percent/100.0);
}
*bytes = reserved_bytes;
return TRUE;
}
static gboolean
min_free_space_size_validate_and_convert (OstreeRepo *self,
const char *min_free_space_size_str,
@@ -2799,7 +2833,7 @@ reload_core_config (OstreeRepo *self,
&lock_timeout_seconds, error))
return FALSE;
self->lock_timeout_seconds = g_ascii_strtoull (lock_timeout_seconds, NULL, 10);
self->lock_timeout_seconds = g_ascii_strtoll (lock_timeout_seconds, NULL, 10);
}
}
@@ -2905,6 +2939,40 @@ reload_core_config (OstreeRepo *self,
self->payload_link_threshold = g_ascii_strtoull (payload_threshold, NULL, 10);
}
{ g_auto(GStrv) configured_finders = NULL;
g_autoptr(GError) local_error = NULL;
configured_finders = g_key_file_get_string_list (self->config, "core", "default-repo-finders",
NULL, &local_error);
if (g_error_matches (local_error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_KEY_NOT_FOUND))
g_clear_error (&local_error);
else if (local_error != NULL)
{
g_propagate_error (error, g_steal_pointer (&local_error));
return FALSE;
}
if (configured_finders != NULL && *configured_finders == NULL)
return glnx_throw (error, "Invalid empty default-repo-finders configuration");
for (char **iter = configured_finders; iter && *iter; iter++)
{
const char *repo_finder = *iter;
if (strcmp (repo_finder, "config") != 0 &&
strcmp (repo_finder, "lan") != 0 &&
strcmp (repo_finder, "mount") != 0)
return glnx_throw (error, "Invalid configured repo-finder '%s'", repo_finder);
}
/* Fall back to a default set of finders */
if (configured_finders == NULL)
configured_finders = g_strsplit ("config;mount", ";", -1);
g_clear_pointer (&self->repo_finders, g_strfreev);
self->repo_finders = g_steal_pointer (&configured_finders);
}
return TRUE;
}
@@ -3047,16 +3115,6 @@ ostree_repo_open (OstreeRepo *self,
return FALSE;
self->owner_uid = stbuf.st_uid;
if (stbuf.st_uid != getuid () || stbuf.st_gid != getgid ())
{
self->target_owner_uid = stbuf.st_uid;
self->target_owner_gid = stbuf.st_gid;
}
else
{
self->target_owner_uid = self->target_owner_gid = -1;
}
if (self->writable)
{
/* Always try to recreate the tmpdir to be nice to people
@@ -3292,6 +3350,30 @@ ostree_repo_get_mode (OstreeRepo *self)
return self->mode;
}
/**
* ostree_repo_get_min_free_space:
* @self: Repo
* @out_reserved_bytes: (out): Location to store the result
* @error: Return location for a #GError
*
* It can be used to query the value (in bytes) of min-free-space-* config option.
*
* Returns: %TRUE on success, %FALSE otherwise.
* Since: 2018.9
*/
gboolean
ostree_repo_get_min_free_space_bytes (OstreeRepo *self, guint64 *out_reserved_bytes, GError **error)
{
g_return_val_if_fail (OSTREE_IS_REPO (self), FALSE);
g_return_val_if_fail (out_reserved_bytes != NULL, FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
if (!min_free_space_calculate_reserved_bytes (self, out_reserved_bytes, error))
return glnx_prefix_error (error, "Error calculating min-free-space bytes");
return TRUE;
}
/**
* ostree_repo_get_parent:
* @self: Repo
@@ -5868,3 +5950,22 @@ ostree_repo_set_collection_id (OstreeRepo *self,
return TRUE;
}
/**
* ostree_repo_get_default_repo_finders:
* @self: an #OstreeRepo
*
* Get the set of default repo finders configured. See the documentation for
* the "core.default-repo-finders" config key.
*
* Returns: (array zero-terminated=1) (element-type utf8):
* %NULL-terminated array of strings.
* Since: 2018.9
*/
const gchar * const *
ostree_repo_get_default_repo_finders (OstreeRepo *self)
{
g_return_val_if_fail (OSTREE_IS_REPO (self), NULL);
return (const gchar * const *)self->repo_finders;
}
+32 -1
View File
@@ -112,6 +112,9 @@ gboolean ostree_repo_set_collection_id (OstreeRepo *self,
const gchar *collection_id,
GError **error);
_OSTREE_PUBLIC
const gchar * const * ostree_repo_get_default_repo_finders (OstreeRepo *self);
_OSTREE_PUBLIC
GFile * ostree_repo_get_path (OstreeRepo *self);
@@ -127,6 +130,10 @@ gboolean ostree_repo_equal (OstreeRepo *a,
_OSTREE_PUBLIC
OstreeRepoMode ostree_repo_get_mode (OstreeRepo *self);
_OSTREE_PUBLIC
gboolean ostree_repo_get_min_free_space_bytes (OstreeRepo *self,
guint64 *out_reserved_bytes,
GError **error);
_OSTREE_PUBLIC
GKeyFile * ostree_repo_get_config (OstreeRepo *self);
@@ -929,7 +936,8 @@ typedef struct {
gboolean no_copy_fallback;
gboolean force_copy; /* Since: 2017.6 */
gboolean bareuseronly_dirs; /* Since: 2017.7 */
gboolean unused_bools[5];
gboolean force_copy_zerosized; /* Since: 2018.9 */
gboolean unused_bools[4];
/* 4 byte hole on 64 bit */
const char *subpath;
@@ -1395,6 +1403,29 @@ gboolean ostree_repo_regenerate_summary (OstreeRepo *self,
*/
#define OSTREE_REPO_METADATA_REF "ostree-metadata"
/**
* OSTREE_META_KEY_DEPLOY_COLLECTION_ID:
*
* GVariant type `s`. This key can be used in the repo metadata which is stored
* in OSTREE_REPO_METADATA_REF as well as in the summary. The semantics of this
* are that the remote repository wants clients to update their remote config
* to add this collection ID (clients can't do P2P operations involving a
* remote without a collection ID configured on it, even if one is configured
* on the server side). Clients must never change or remove a collection ID
* already set in their remote config.
*
* Currently, OSTree does not implement changing a remote config based on this
* key, but it may do so in a later release, and until then clients such as
* Flatpak may implement it.
*
* This is a replacement for the similar metadata key implemented by flatpak,
* `xa.collection-id`, which is now deprecated as clients which supported it had
* bugs with their P2P implementations.
*
* Since: 2018.9
*/
#define OSTREE_META_KEY_DEPLOY_COLLECTION_ID "ostree.deploy-collection-id"
G_END_DECLS
+73 -4
View File
@@ -49,9 +49,10 @@
#include "libglnx.h"
#ifdef HAVE_LIBSYSTEMD
#define OSTREE_VARRELABEL_ID SD_ID128_MAKE(da,67,9b,08,ac,d3,45,04,b7,89,d9,6f,81,8e,a7,81)
#define OSTREE_CONFIGMERGE_ID SD_ID128_MAKE(d3,86,3b,ae,c1,3e,44,49,ab,03,84,68,4a,8a,f3,a7)
#define OSTREE_DEPLOYMENT_COMPLETE_ID SD_ID128_MAKE(dd,44,0e,3e,54,90,83,b6,3d,0e,fc,7d,c1,52,55,f1)
#define OSTREE_VARRELABEL_ID SD_ID128_MAKE(da,67,9b,08,ac,d3,45,04,b7,89,d9,6f,81,8e,a7,81)
#define OSTREE_CONFIGMERGE_ID SD_ID128_MAKE(d3,86,3b,ae,c1,3e,44,49,ab,03,84,68,4a,8a,f3,a7)
#define OSTREE_DEPLOYMENT_COMPLETE_ID SD_ID128_MAKE(dd,44,0e,3e,54,90,83,b6,3d,0e,fc,7d,c1,52,55,f1)
#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
/*
@@ -2509,6 +2510,47 @@ sysroot_initialize_deployment (OstreeSysroot *self,
return TRUE;
}
/* Get a directory fd for the /var of @deployment.
* Before we supported having /var be a separate mount point,
* this was easy. However, as https://github.com/ostreedev/ostree/issues/1729
* raised, in the primary case where we're
* doing a new deployment for the booted stateroot,
* we need to use /var/. This code doesn't correctly
* handle the case of `ostree admin --sysroot upgrade`,
* nor (relatedly) the case of upgrading a separate stateroot.
*/
static gboolean
get_var_dfd (OstreeSysroot *self,
int osdeploy_dfd,
OstreeDeployment *deployment,
int *ret_fd,
GError **error)
{
const char *booted_stateroot =
self->booted_deployment ? ostree_deployment_get_osname (self->booted_deployment) : NULL;
int base_dfd;
const char *base_path;
/* The common case is when we're doing a new deployment for the same stateroot (osname).
* If we have a separate mounted /var, then we need to use it - the /var in the
* stateroot will probably just be an empty directory.
*
* If the stateroot doesn't match, just fall back to /var in the target's stateroot.
*/
if (g_strcmp0 (booted_stateroot, ostree_deployment_get_osname (deployment)) == 0)
{
base_dfd = AT_FDCWD;
base_path = "/var";
}
else
{
base_dfd = osdeploy_dfd;
base_path = "var";
}
return glnx_opendirat (base_dfd, base_path, TRUE, ret_fd, error);
}
static gboolean
sysroot_finalize_deployment (OstreeSysroot *self,
OstreeDeployment *deployment,
@@ -2547,6 +2589,9 @@ sysroot_finalize_deployment (OstreeSysroot *self,
glnx_autofd int os_deploy_dfd = -1;
if (!glnx_opendirat (self->sysroot_fd, osdeploypath, TRUE, &os_deploy_dfd, error))
return FALSE;
glnx_autofd int var_dfd = -1;
if (!get_var_dfd (self, os_deploy_dfd, deployment, &var_dfd, error))
return FALSE;
/* Ensure that the new deployment does not have /etc/.updated or
* /var/.updated so that systemd ConditionNeedsUpdate=/etc|/var services run
@@ -2554,7 +2599,7 @@ sysroot_finalize_deployment (OstreeSysroot *self,
*/
if (!ot_ensure_unlinked_at (deployment_dfd, "etc/.updated", error))
return FALSE;
if (!ot_ensure_unlinked_at (os_deploy_dfd, "var/.updated", error))
if (!ot_ensure_unlinked_at (var_dfd, ".updated", error))
return FALSE;
g_autoptr(OstreeSePolicy) sepolicy = ostree_sepolicy_new_at (deployment_dfd, cancellable, error);
@@ -2716,6 +2761,10 @@ ostree_sysroot_stage_tree (OstreeSysroot *self,
if (booted_deployment == NULL)
return glnx_throw (error, "Cannot stage a deployment when not currently booted into an OSTree system");
/* 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). */
if ((self->debug_flags & OSTREE_SYSROOT_DEBUG_TEST_STAGED_PATH) == 0)
{
/* This is a bit of a hack. When adding a new service we have to end up getting
* into the presets for downstream distros; see e.g. https://src.fedoraproject.org/rpms/ostree/pull-request/7
*
@@ -2728,6 +2777,11 @@ ostree_sysroot_stage_tree (OstreeSysroot *self,
return FALSE;
if (!g_spawn_check_exit_status (estatus, error))
return FALSE;
}
else
{
g_print ("test-staged-path: Not running `systemctl start`\n");
} /* OSTREE_SYSROOT_DEBUG_TEST_STAGED_PATH */
g_autoptr(OstreeDeployment) deployment = NULL;
if (!sysroot_initialize_deployment (self, osname, revision, origin, override_kernel_argv,
@@ -2818,6 +2872,21 @@ _ostree_sysroot_finalize_staged (OstreeSysroot *self,
if (!self->staged_deployment)
return TRUE;
/* Notice we send this *after* the trivial `return TRUE` above; this msg implies we've
* committed to finalizing the deployment. */
#ifdef HAVE_LIBSYSTEMD
sd_journal_send ("MESSAGE_ID=" SD_ID128_FORMAT_STR,
SD_ID128_FORMAT_VAL(OSTREE_DEPLOYMENT_FINALIZING_ID),
"MESSAGE=Finalizing staged deployment",
"OSTREE_OSNAME=%s",
ostree_deployment_get_osname (self->staged_deployment),
"OSTREE_CHECKSUM=%s",
ostree_deployment_get_csum (self->staged_deployment),
"OSTREE_DEPLOYSERIAL=%u",
ostree_deployment_get_deployserial (self->staged_deployment),
NULL);
#endif
g_assert (self->staged_deployment_data);
g_autoptr(OstreeDeployment) merge_deployment = NULL;
+3
View File
@@ -36,6 +36,9 @@ typedef enum {
OSTREE_SYSROOT_DEBUG_NO_XATTRS = 1 << 1,
/* https://github.com/ostreedev/ostree/pull/1049 */
OSTREE_SYSROOT_DEBUG_TEST_FIFREEZE = 1 << 2,
/* 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,
} OstreeSysrootDebugFlags;
/**
+17 -12
View File
@@ -189,6 +189,7 @@ ostree_sysroot_init (OstreeSysroot *self)
{ "mutable-deployments", OSTREE_SYSROOT_DEBUG_MUTABLE_DEPLOYMENTS },
{ "test-fifreeze", OSTREE_SYSROOT_DEBUG_TEST_FIFREEZE },
{ "no-xattrs", OSTREE_SYSROOT_DEBUG_NO_XATTRS },
{ "test-staged-path", OSTREE_SYSROOT_DEBUG_TEST_STAGED_PATH },
};
self->debug_flags = g_parse_debug_string (g_getenv ("OSTREE_SYSROOT_DEBUG"),
@@ -376,12 +377,11 @@ _ostree_sysroot_read_current_subbootversion (OstreeSysroot *self,
g_autofree char *ostree_bootdir_name = g_strdup_printf ("ostree/boot.%d", bootversion);
struct stat stbuf;
if (fstatat (self->sysroot_fd, ostree_bootdir_name, &stbuf, AT_SYMLINK_NOFOLLOW) != 0)
if (!glnx_fstatat_allow_noent (self->sysroot_fd, ostree_bootdir_name, &stbuf, AT_SYMLINK_NOFOLLOW, error))
return FALSE;
if (errno == ENOENT)
{
if (errno == ENOENT)
*out_subbootversion = 0;
else
return glnx_throw_errno (error);
*out_subbootversion = 0;
}
else
{
@@ -499,10 +499,10 @@ read_current_bootversion (OstreeSysroot *self,
int ret_bootversion;
struct stat stbuf;
if (fstatat (self->sysroot_fd, "boot/loader", &stbuf, AT_SYMLINK_NOFOLLOW) != 0)
if (!glnx_fstatat_allow_noent (self->sysroot_fd, "boot/loader", &stbuf, AT_SYMLINK_NOFOLLOW, error))
return FALSE;
if (errno == ENOENT)
{
if (errno != ENOENT)
return glnx_throw_errno (error);
ret_bootversion = 0;
}
else
@@ -613,6 +613,10 @@ parse_deployment (OstreeSysroot *self,
error))
return FALSE;
g_autofree char *errprefix =
g_strdup_printf ("Parsing deployment %i in stateroot '%s'", treebootserial, osname);
GLNX_AUTO_PREFIX_ERROR(errprefix, error);
const char *relative_boot_link = boot_link;
if (*relative_boot_link == '/')
relative_boot_link++;
@@ -976,11 +980,12 @@ ostree_sysroot_load_if_changed (OstreeSysroot *self,
/* Otherwise - check for /sysroot which should only exist in a deployment,
* not in ${sysroot} (a metavariable for the real physical root).
*/
else if (fstatat (self->sysroot_fd, "sysroot", &stbuf, 0) < 0)
else
{
if (errno != ENOENT)
return glnx_throw_errno_prefix (error, "fstatat");
self->is_physical = TRUE;
if (!glnx_fstatat_allow_noent (self->sysroot_fd, "sysroot", &stbuf, 0, error))
return FALSE;
if (errno == ENOENT)
self->is_physical = TRUE;
}
/* Otherwise, the default is FALSE */
}
+3 -3
View File
@@ -43,7 +43,7 @@
*
* Since: 2017.4
*/
#define OSTREE_RELEASE_VERSION (8)
#define OSTREE_RELEASE_VERSION (9)
/**
* OSTREE_VERSION
@@ -52,7 +52,7 @@
*
* Since: 2017.4
*/
#define OSTREE_VERSION (2018.8)
#define OSTREE_VERSION (2018.9)
/**
* OSTREE_VERSION_S:
@@ -62,7 +62,7 @@
*
* Since: 2017.4
*/
#define OSTREE_VERSION_S "2018.8"
#define OSTREE_VERSION_S "2018.9"
#define OSTREE_ENCODE_VERSION(year,release) \
((year) << 16 | (release))
+5 -1
View File
@@ -45,6 +45,7 @@ static char *opt_from_file;
static gboolean opt_disable_fsync;
static gboolean opt_require_hardlinks;
static gboolean opt_force_copy;
static gboolean opt_force_copy_zerosized;
static gboolean opt_bareuseronly_dirs;
static char *opt_skiplist_file;
static char *opt_selinux_policy;
@@ -84,6 +85,7 @@ static GOptionEntry options[] = {
{ "from-file", 0, 0, G_OPTION_ARG_STRING, &opt_from_file, "Process many checkouts from input file", "FILE" },
{ "fsync", 0, 0, G_OPTION_ARG_CALLBACK, parse_fsync_cb, "Specify how to invoke fsync()", "POLICY" },
{ "require-hardlinks", 'H', 0, G_OPTION_ARG_NONE, &opt_require_hardlinks, "Do not fall back to full copies if hardlinking fails", NULL },
{ "force-copy-zerosized", 'z', 0, G_OPTION_ARG_NONE, &opt_force_copy_zerosized, "Do not hardlink zero-sized files", NULL },
{ "force-copy", 'C', 0, G_OPTION_ARG_NONE, &opt_force_copy, "Never hardlink (but may reflink if available)", NULL },
{ "bareuseronly-dirs", 'M', 0, G_OPTION_ARG_NONE, &opt_bareuseronly_dirs, "Suppress mode bits outside of 0775 for directories (suid, world writable, etc.)", NULL },
{ "skip-list", 0, 0, G_OPTION_ARG_FILENAME, &opt_skiplist_file, "File containing list of files to skip", "PATH" },
@@ -130,7 +132,8 @@ process_one_checkout (OstreeRepo *repo,
* convenient infrastructure for testing C APIs with data.
*/
if (opt_disable_cache || opt_whiteouts || opt_require_hardlinks ||
opt_union_add || opt_force_copy || opt_bareuseronly_dirs || opt_union_identical ||
opt_union_add || opt_force_copy || opt_force_copy_zerosized ||
opt_bareuseronly_dirs || opt_union_identical ||
opt_skiplist_file || opt_selinux_policy || opt_selinux_prefix)
{
OstreeRepoCheckoutAtOptions options = { 0, };
@@ -218,6 +221,7 @@ process_one_checkout (OstreeRepo *repo,
options.no_copy_fallback = opt_require_hardlinks;
options.force_copy = opt_force_copy;
options.force_copy_zerosized = opt_force_copy_zerosized;
options.bareuseronly_dirs = opt_bareuseronly_dirs;
if (!ostree_repo_checkout_at (repo, &options,
+15 -11
View File
@@ -33,11 +33,13 @@
static gboolean opt_disable_fsync = FALSE;
static char *opt_destination_repo = NULL;
static char *opt_commit = NULL;
static GOptionEntry options[] =
{
{ "disable-fsync", 0, 0, G_OPTION_ARG_NONE, &opt_disable_fsync, "Do not invoke fsync()", NULL },
{ "destination-repo", 0, 0, G_OPTION_ARG_FILENAME, &opt_destination_repo, "Use custom repository directory within the mount", "DEST" },
{ "commit", 0, 0, G_OPTION_ARG_STRING, &opt_commit, "Pull a specific commit (only works when a single ref is specified)", "COMMIT" },
{ NULL }
};
@@ -78,6 +80,12 @@ ostree_builtin_create_usb (int argc,
return FALSE;
}
if (opt_commit && argc > 4)
{
ot_util_usage_error (context, "The --commit option can only be used when a single COLLECTION-ID REF pair is specified", error);
return FALSE;
}
/* Open the USB stick, which must exist. Allow automounting and following symlinks. */
const char *mount_root_path = argv[1];
struct stat mount_root_stbuf;
@@ -102,21 +110,17 @@ ostree_builtin_create_usb (int argc,
/* Open the destination repository on the USB stick or create it if it doesn’t exist.
* Check it’s below @mount_root_path, and that it’s not the same as the source
* repository.
*
* If the destination file system supports xattrs (for example, ext4), we use
* a BARE_USER repository; if it doesn’t (for example, FAT), we use ARCHIVE.
* In either case, we want a lossless repository. */
* repository. */
const char *dest_repo_path = (opt_destination_repo != NULL) ? opt_destination_repo : ".ostree/repo";
if (!glnx_shutil_mkdir_p_at (mount_root_dfd, dest_repo_path, 0755, cancellable, error))
return FALSE;
OstreeRepoMode mode = OSTREE_REPO_MODE_BARE_USER;
if (TEMP_FAILURE_RETRY (fgetxattr (mount_root_dfd, "user.test", NULL, 0)) < 0 &&
(errno == ENOTSUP || errno == EOPNOTSUPP))
mode = OSTREE_REPO_MODE_ARCHIVE;
/* Always use the archive repo mode, which works on FAT file systems that
* don't support xattrs, compresses files to save space, doesn't store
* permission info directly in the file attributes, and is at least sometimes
* more performant than bare-user */
OstreeRepoMode mode = OSTREE_REPO_MODE_ARCHIVE;
g_debug ("%s: Creating repository in mode %u", G_STRFUNC, mode);
@@ -158,7 +162,7 @@ ostree_builtin_create_usb (int argc,
const OstreeCollectionRef *ref = g_ptr_array_index (refs, i);
g_variant_builder_add (&refs_builder, "(sss)",
ref->collection_id, ref->ref_name, "");
ref->collection_id, ref->ref_name, opt_commit ?: "");
}
{
+15 -2
View File
@@ -146,8 +146,10 @@ static gboolean do_ref (OstreeRepo *repo, const char *refspec_prefix, GCancellab
/* If we're doing aliasing, we need the full list of aliases mostly to allow
* replacing existing aliases.
* If we are deleting a ref, we want to make sure that it doesn't have
* any corresponding aliases.
*/
if (opt_alias)
if (opt_alias || opt_delete)
{
if (!ostree_repo_list_refs_ext (repo, NULL, &ref_aliases,
OSTREE_REPO_LIST_REFS_EXT_ALIASES,
@@ -245,7 +247,18 @@ static gboolean do_ref (OstreeRepo *repo, const char *refspec_prefix, GCancellab
if (!ostree_parse_refspec (refspec, &remote, &ref, error))
goto out;
/* Look for alias if it exists for a ref we want to delete */
GLNX_HASH_TABLE_FOREACH_KV (ref_aliases, const char *,
ref_alias, const char *, value)
{
if (!strcmp (ref, value))
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"Ref '%s' has an active alias: '%s'", ref, ref_alias);
goto out;
}
}
if (!ostree_repo_set_ref_immediate (repo, remote, ref, NULL,
cancellable, error))
goto out;
+2 -4
View File
@@ -25,6 +25,7 @@
#include <sys/stat.h>
#include <sys/statvfs.h>
#include <stdio.h>
#include <err.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
@@ -628,10 +629,7 @@ rofs_parse_opt (void *data, const char *arg, int key,
{
basefd = openat (AT_FDCWD, arg, O_RDONLY | O_NONBLOCK | O_DIRECTORY | O_CLOEXEC | O_NOCTTY);
if (basefd == -1)
{
perror ("openat");
exit (EXIT_FAILURE);
}
err (1, "opening rootfs %s", arg);
return 0;
}
else
+33 -34
View File
@@ -39,13 +39,40 @@
#include "ostree-mount-util.h"
static void
do_remount (const char *target)
{
struct stat stbuf;
if (lstat (target, &stbuf) < 0)
return;
/* Silently ignore symbolic links; we expect these to point to
* /sysroot, and thus there isn't a bind mount there.
*/
if (S_ISLNK (stbuf.st_mode))
return;
/* If not a mountpoint, skip it */
struct statvfs stvfsbuf;
if (statvfs (target, &stvfsbuf) == -1)
return;
/* If no read-only flag, skip it */
if ((stvfsbuf.f_flag & ST_RDONLY) == 0)
return;
/* It's a mounted, read-only fs; remount it */
if (mount (target, target, NULL, MS_REMOUNT | MS_SILENT, NULL) < 0)
{
/* Also ignore EINVAL - if the target isn't a mountpoint
* already, then assume things are OK.
*/
if (errno != EINVAL)
err (EXIT_FAILURE, "failed to remount %s", target);
}
else
printf ("Remounted: %s\n", target);
}
int
main(int argc, char *argv[])
{
const char *remounts[] = { "/sysroot", "/var", NULL };
struct stat stbuf;
int i;
/* When systemd is in use this is normally created via the generator, but
* we ensure it's created here as well for redundancy.
*/
@@ -65,39 +92,11 @@ main(int argc, char *argv[])
/* If / isn't writable, don't do any remounts; we don't want
* to clear the readonly flag in that case.
*/
exit (EXIT_SUCCESS);
}
for (i = 0; remounts[i] != NULL; i++)
{
const char *target = remounts[i];
if (lstat (target, &stbuf) < 0)
continue;
/* Silently ignore symbolic links; we expect these to point to
* /sysroot, and thus there isn't a bind mount there.
*/
if (S_ISLNK (stbuf.st_mode))
continue;
/* If not a mountpoint, skip it */
struct statvfs stvfsbuf;
if (statvfs (target, &stvfsbuf) == -1)
continue;
/* If no read-only flag, skip it */
if ((stvfsbuf.f_flag & ST_RDONLY) == 0)
continue;
/* It's a mounted, read-only fs; remount it */
if (mount (target, target, NULL, MS_REMOUNT | MS_SILENT, NULL) < 0)
{
/* Also ignore EINVAL - if the target isn't a mountpoint
* already, then assume things are OK.
*/
if (errno != EINVAL)
err (EXIT_FAILURE, "failed to remount %s", target);
}
else
printf ("Remounted: %s\n", target);
}
do_remount ("/sysroot");
do_remount ("/var");
exit (EXIT_SUCCESS);
}