New upstream version 2017.11
This commit is contained in:
commit
573491dc3d
154 files changed
+3866
-2564
No files matched your search
@@ -0,0 +1,19 @@
|
||||
# Copyright (C) 2017 Colin Walters <walters@verbum.org>
|
||||
#
|
||||
# This library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation; either
|
||||
# version 2 of the License, or (at your option) any later version.
|
||||
#
|
||||
# This library is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with this library; if not, write to the
|
||||
# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
# Boston, MA 02111-1307, USA.
|
||||
|
||||
# https://github.com/ostreedev/ostree/issues/393
|
||||
R! /var/tmp/ostree-unlock-ovl.*
|
||||
@@ -18,6 +18,9 @@
|
||||
***/
|
||||
|
||||
/* Add new symbols here. Release commits should copy this section into -released.sym. */
|
||||
LIBOSTREE_2017.12 {
|
||||
} LIBOSTREE_2017.11;
|
||||
|
||||
|
||||
/* 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
|
||||
|
||||
@@ -427,6 +427,9 @@ LIBOSTREE_2017.10 {
|
||||
*/
|
||||
} LIBOSTREE_2017.8;
|
||||
|
||||
LIBOSTREE_2017.11 {
|
||||
} LIBOSTREE_2017.10;
|
||||
|
||||
/* NOTE: Only add more content here in release commits! See the
|
||||
* comments at the top of this file.
|
||||
*/
|
||||
@@ -75,18 +75,17 @@ _ostree_bootloader_grub2_query (OstreeBootloader *bootloader,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
gboolean ret = FALSE;
|
||||
OstreeBootloaderGrub2 *self = OSTREE_BOOTLOADER_GRUB2 (bootloader);
|
||||
g_autoptr(GFile) efi_basedir = NULL;
|
||||
|
||||
/* Look for the BIOS path first */
|
||||
if (g_file_query_exists (self->config_path_bios, NULL))
|
||||
{
|
||||
/* If we found it, we're done */
|
||||
*out_is_active = TRUE;
|
||||
ret = TRUE;
|
||||
goto out;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
efi_basedir = g_file_resolve_relative_path (self->sysroot->path, "boot/efi/EFI");
|
||||
g_autoptr(GFile) efi_basedir = g_file_resolve_relative_path (self->sysroot->path, "boot/efi/EFI");
|
||||
|
||||
g_clear_object (&self->config_path_efi);
|
||||
|
||||
@@ -98,17 +97,16 @@ _ostree_bootloader_grub2_query (OstreeBootloader *bootloader,
|
||||
G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
|
||||
cancellable, error);
|
||||
if (!direnum)
|
||||
goto out;
|
||||
return FALSE;
|
||||
|
||||
while (TRUE)
|
||||
{
|
||||
GFileInfo *file_info;
|
||||
const char *fname;
|
||||
g_autofree char *subdir_grub_cfg = NULL;
|
||||
|
||||
if (!g_file_enumerator_iterate (direnum, &file_info, NULL,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
return FALSE;
|
||||
if (file_info == NULL)
|
||||
break;
|
||||
|
||||
@@ -119,8 +117,9 @@ _ostree_bootloader_grub2_query (OstreeBootloader *bootloader,
|
||||
if (g_file_info_get_file_type (file_info) != G_FILE_TYPE_DIRECTORY)
|
||||
continue;
|
||||
|
||||
subdir_grub_cfg = g_build_filename (gs_file_get_path_cached (efi_basedir), fname, "grub.cfg", NULL);
|
||||
|
||||
g_autofree char *subdir_grub_cfg =
|
||||
g_build_filename (gs_file_get_path_cached (efi_basedir), fname, "grub.cfg", NULL);
|
||||
|
||||
if (g_file_test (subdir_grub_cfg, G_FILE_TEST_EXISTS))
|
||||
{
|
||||
self->config_path_efi = g_file_new_for_path (subdir_grub_cfg);
|
||||
@@ -128,20 +127,18 @@ _ostree_bootloader_grub2_query (OstreeBootloader *bootloader,
|
||||
}
|
||||
}
|
||||
|
||||
/* If we found the EFI path, we're done */
|
||||
if (self->config_path_efi)
|
||||
{
|
||||
self->is_efi = TRUE;
|
||||
*out_is_active = TRUE;
|
||||
ret = TRUE;
|
||||
goto out;
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
else
|
||||
*out_is_active = FALSE;
|
||||
|
||||
ret = TRUE;
|
||||
out:
|
||||
return ret;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static const char *
|
||||
@@ -150,6 +147,10 @@ _ostree_bootloader_grub2_get_name (OstreeBootloader *bootloader)
|
||||
return "grub2";
|
||||
}
|
||||
|
||||
/* This implementation is quite complex; see this issue for
|
||||
* a starting point:
|
||||
* https://github.com/ostreedev/ostree/issues/717
|
||||
*/
|
||||
gboolean
|
||||
_ostree_bootloader_grub2_generate_config (OstreeSysroot *sysroot,
|
||||
int bootversion,
|
||||
@@ -157,13 +158,6 @@ _ostree_bootloader_grub2_generate_config (OstreeSysroot *sysroot
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
gboolean ret = FALSE;
|
||||
g_autoptr(GString) output = g_string_new ("");
|
||||
g_autoptr(GOutputStream) out_stream = NULL;
|
||||
g_autoptr(GPtrArray) loader_configs = NULL;
|
||||
guint i;
|
||||
gsize bytes_written;
|
||||
gboolean is_efi;
|
||||
/* So... yeah. Just going to hardcode these. */
|
||||
static const char hardcoded_video[] = "load_video\n"
|
||||
"set gfxpayload=keep\n";
|
||||
@@ -178,16 +172,18 @@ _ostree_bootloader_grub2_generate_config (OstreeSysroot *sysroot
|
||||
g_assert (grub2_prepare_root_cache != NULL);
|
||||
|
||||
/* Passed from the parent */
|
||||
is_efi = g_getenv ("_OSTREE_GRUB2_IS_EFI") != NULL;
|
||||
gboolean is_efi = g_getenv ("_OSTREE_GRUB2_IS_EFI") != NULL;
|
||||
|
||||
out_stream = g_unix_output_stream_new (target_fd, FALSE);
|
||||
g_autoptr(GOutputStream) out_stream = g_unix_output_stream_new (target_fd, FALSE);
|
||||
|
||||
g_autoptr(GPtrArray) loader_configs = NULL;
|
||||
if (!_ostree_sysroot_read_boot_loader_configs (sysroot, bootversion,
|
||||
&loader_configs,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
return FALSE;
|
||||
|
||||
for (i = 0; i < loader_configs->len; i++)
|
||||
g_autoptr(GString) output = g_string_new ("");
|
||||
for (guint i = 0; i < loader_configs->len; i++)
|
||||
{
|
||||
OstreeBootconfigParser *config = loader_configs->pdata[i];
|
||||
const char *title;
|
||||
@@ -217,13 +213,9 @@ _ostree_bootloader_grub2_generate_config (OstreeSysroot *sysroot
|
||||
g_string_append (output, hardcoded_insmods);
|
||||
g_string_append (output, grub2_prepare_root_cache);
|
||||
g_string_append_c (output, '\n');
|
||||
|
||||
|
||||
if (!kernel)
|
||||
{
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||
"No \"linux\" key in bootloader config");
|
||||
goto out;
|
||||
}
|
||||
return glnx_throw (error, "No \"linux\" key in bootloader config");
|
||||
g_string_append (output, "linux");
|
||||
if (is_efi)
|
||||
g_string_append (output, GRUB2_EFI_SUFFIX);
|
||||
@@ -256,13 +248,12 @@ _ostree_bootloader_grub2_generate_config (OstreeSysroot *sysroot
|
||||
g_string_append (output, "}\n");
|
||||
}
|
||||
|
||||
gsize bytes_written;
|
||||
if (!g_output_stream_write_all (out_stream, output->str, output->len,
|
||||
&bytes_written, cancellable, error))
|
||||
goto out;
|
||||
return FALSE;
|
||||
|
||||
ret = TRUE;
|
||||
out:
|
||||
return ret;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
@@ -271,19 +262,24 @@ typedef struct {
|
||||
gboolean is_efi;
|
||||
} Grub2ChildSetupData;
|
||||
|
||||
/* Post-fork, pre-exec child setup for grub2-mkconfig */
|
||||
static void
|
||||
grub2_child_setup (gpointer user_data)
|
||||
{
|
||||
Grub2ChildSetupData *cdata = user_data;
|
||||
|
||||
setenv ("_OSTREE_GRUB2_BOOTVERSION", cdata->bootversion_str, TRUE);
|
||||
/* We have to pass our state to the child */
|
||||
/* We have to pass our state (whether or not we're using EFI) to the child */
|
||||
if (cdata->is_efi)
|
||||
setenv ("_OSTREE_GRUB2_IS_EFI", "1", TRUE);
|
||||
|
||||
/* Everything below this is dealing with the chroot case; if
|
||||
* we're not doing that, return early.
|
||||
*/
|
||||
if (!cdata->root)
|
||||
return;
|
||||
|
||||
/* TODO: investigate replacing this with bwrap */
|
||||
if (chdir (cdata->root) != 0)
|
||||
{
|
||||
perror ("chdir");
|
||||
@@ -321,6 +317,7 @@ grub2_child_setup (gpointer user_data)
|
||||
}
|
||||
}
|
||||
|
||||
/* Main entrypoint for writing GRUB configuration. */
|
||||
static gboolean
|
||||
_ostree_bootloader_grub2_write_config (OstreeBootloader *bootloader,
|
||||
int bootversion,
|
||||
@@ -328,23 +325,13 @@ _ostree_bootloader_grub2_write_config (OstreeBootloader *bootloader,
|
||||
GError **error)
|
||||
{
|
||||
OstreeBootloaderGrub2 *self = OSTREE_BOOTLOADER_GRUB2 (bootloader);
|
||||
gboolean ret = FALSE;
|
||||
g_autoptr(GFile) new_config_path = NULL;
|
||||
g_autofree char *bootversion_str = g_strdup_printf ("%u", (guint)bootversion);
|
||||
g_autoptr(GFile) config_path_efi_dir = NULL;
|
||||
g_autofree char *grub2_mkconfig_chroot = NULL;
|
||||
gboolean use_system_grub2_mkconfig = TRUE;
|
||||
const gchar *grub_exec = NULL;
|
||||
const char *grub_argv[4] = { NULL, "-o", NULL, NULL};
|
||||
GSpawnFlags grub_spawnflags = G_SPAWN_SEARCH_PATH;
|
||||
int grub2_estatus;
|
||||
Grub2ChildSetupData cdata = { NULL, };
|
||||
|
||||
/* Autotests can set this envvar to select which code path to test, useful for OS installers as well */
|
||||
gboolean use_system_grub2_mkconfig = TRUE;
|
||||
#ifdef USE_BUILTIN_GRUB2_MKCONFIG
|
||||
use_system_grub2_mkconfig = FALSE;
|
||||
#endif
|
||||
/* Autotests can set this envvar to select which code path to test, useful for OS installers as well */
|
||||
grub_exec = g_getenv ("OSTREE_GRUB2_EXEC");
|
||||
const gchar *grub_exec = g_getenv ("OSTREE_GRUB2_EXEC");
|
||||
if (grub_exec)
|
||||
{
|
||||
if (g_str_has_suffix (grub_exec, GRUB2_MKCONFIG_PATH))
|
||||
@@ -355,6 +342,7 @@ _ostree_bootloader_grub2_write_config (OstreeBootloader *bootloader,
|
||||
else
|
||||
grub_exec = use_system_grub2_mkconfig ? GRUB2_MKCONFIG_PATH : TARGET_PREFIX "/lib/ostree/ostree-grub-generator";
|
||||
|
||||
g_autofree char *grub2_mkconfig_chroot = NULL;
|
||||
if (use_system_grub2_mkconfig && ostree_sysroot_get_booted_deployment (self->sysroot) == NULL
|
||||
&& g_file_has_parent (self->sysroot->path, NULL))
|
||||
{
|
||||
@@ -381,13 +369,15 @@ _ostree_bootloader_grub2_write_config (OstreeBootloader *bootloader,
|
||||
grub2_mkconfig_chroot = g_file_get_path (tool_deployment_root);
|
||||
}
|
||||
|
||||
g_autoptr(GFile) new_config_path = NULL;
|
||||
g_autoptr(GFile) config_path_efi_dir = NULL;
|
||||
if (self->is_efi)
|
||||
{
|
||||
config_path_efi_dir = g_file_get_parent (self->config_path_efi);
|
||||
new_config_path = g_file_get_child (config_path_efi_dir, "grub.cfg.new");
|
||||
/* We use grub2-mkconfig to write to a temporary file first */
|
||||
if (!ot_gfile_ensure_unlinked (new_config_path, cancellable, error))
|
||||
goto out;
|
||||
return FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -395,12 +385,16 @@ _ostree_bootloader_grub2_write_config (OstreeBootloader *bootloader,
|
||||
bootversion);
|
||||
}
|
||||
|
||||
const char *grub_argv[4] = { NULL, "-o", NULL, NULL};
|
||||
Grub2ChildSetupData cdata = { NULL, };
|
||||
grub_argv[0] = grub_exec;
|
||||
grub_argv[2] = gs_file_get_path_cached (new_config_path);
|
||||
|
||||
GSpawnFlags grub_spawnflags = G_SPAWN_SEARCH_PATH;
|
||||
if (!g_getenv ("OSTREE_DEBUG_GRUB2"))
|
||||
grub_spawnflags |= G_SPAWN_STDOUT_TO_DEV_NULL | G_SPAWN_STDERR_TO_DEV_NULL;
|
||||
cdata.root = grub2_mkconfig_chroot;
|
||||
g_autofree char *bootversion_str = g_strdup_printf ("%u", (guint)bootversion);
|
||||
cdata.bootversion_str = bootversion_str;
|
||||
cdata.is_efi = self->is_efi;
|
||||
/* Note in older versions of the grub2 package, this script doesn't even try
|
||||
@@ -411,54 +405,47 @@ _ostree_bootloader_grub2_write_config (OstreeBootloader *bootloader,
|
||||
|
||||
Upstream is fixed though.
|
||||
*/
|
||||
int grub2_estatus;
|
||||
if (!g_spawn_sync (NULL, (char**)grub_argv, NULL, grub_spawnflags,
|
||||
grub2_child_setup, &cdata, NULL, NULL,
|
||||
&grub2_estatus, error))
|
||||
goto out;
|
||||
return FALSE;
|
||||
if (!g_spawn_check_exit_status (grub2_estatus, error))
|
||||
{
|
||||
g_prefix_error (error, "%s: ", grub_argv[0]);
|
||||
goto out;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Now let's fdatasync() for the new file */
|
||||
{ glnx_fd_close int new_config_fd = -1;
|
||||
if (!glnx_openat_rdonly (AT_FDCWD, gs_file_get_path_cached (new_config_path), TRUE, &new_config_fd, error))
|
||||
goto out;
|
||||
return FALSE;
|
||||
|
||||
if (fdatasync (new_config_fd) < 0)
|
||||
{
|
||||
(void)glnx_throw_errno_prefix (error, "fdatasync");
|
||||
goto out;
|
||||
}
|
||||
return glnx_throw_errno_prefix (error, "fdatasync");
|
||||
}
|
||||
|
||||
if (self->is_efi)
|
||||
{
|
||||
g_autoptr(GFile) config_path_efi_old = g_file_get_child (config_path_efi_dir, "grub.cfg.old");
|
||||
|
||||
|
||||
/* copy current to old */
|
||||
if (!ot_gfile_ensure_unlinked (config_path_efi_old, cancellable, error))
|
||||
goto out;
|
||||
return FALSE;
|
||||
if (!g_file_copy (self->config_path_efi, config_path_efi_old,
|
||||
G_FILE_COPY_OVERWRITE, cancellable, NULL, NULL, error))
|
||||
goto out;
|
||||
return FALSE;
|
||||
|
||||
/* NOTE: NON-ATOMIC REPLACEMENT; WE can't do anything else on FAT;
|
||||
* see https://bugzilla.gnome.org/show_bug.cgi?id=724246
|
||||
*/
|
||||
if (!ot_gfile_ensure_unlinked (self->config_path_efi, cancellable, error))
|
||||
goto out;
|
||||
return FALSE;
|
||||
if (rename (gs_file_get_path_cached (new_config_path), gs_file_get_path_cached (self->config_path_efi)) < 0)
|
||||
{
|
||||
glnx_set_error_from_errno (error);
|
||||
goto out;
|
||||
}
|
||||
return glnx_throw_errno_prefix (error, "rename");
|
||||
}
|
||||
|
||||
ret = TRUE;
|
||||
out:
|
||||
return ret;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
|
||||
@@ -59,44 +59,33 @@ _ostree_bootloader_syslinux_get_name (OstreeBootloader *bootloader)
|
||||
}
|
||||
|
||||
static gboolean
|
||||
append_config_from_boostree_loader_entries (OstreeBootloaderSyslinux *self,
|
||||
gboolean regenerate_default,
|
||||
int bootversion,
|
||||
GPtrArray *new_lines,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
append_config_from_loader_entries (OstreeBootloaderSyslinux *self,
|
||||
gboolean regenerate_default,
|
||||
int bootversion,
|
||||
GPtrArray *new_lines,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
gboolean ret = FALSE;
|
||||
g_autoptr(GPtrArray) boostree_loader_configs = NULL;
|
||||
guint i;
|
||||
|
||||
if (!_ostree_sysroot_read_boot_loader_configs (self->sysroot, bootversion, &boostree_loader_configs,
|
||||
g_autoptr(GPtrArray) loader_configs = NULL;
|
||||
if (!_ostree_sysroot_read_boot_loader_configs (self->sysroot, bootversion, &loader_configs,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
return FALSE;
|
||||
|
||||
for (i = 0; i < boostree_loader_configs->len; i++)
|
||||
for (guint i = 0; i < loader_configs->len; i++)
|
||||
{
|
||||
OstreeBootconfigParser *config = boostree_loader_configs->pdata[i];
|
||||
const char *val;
|
||||
|
||||
val = ostree_bootconfig_parser_get (config, "title");
|
||||
OstreeBootconfigParser *config = loader_configs->pdata[i];
|
||||
const char *val = ostree_bootconfig_parser_get (config, "title");
|
||||
if (!val)
|
||||
val = "(Untitled)";
|
||||
|
||||
if (regenerate_default && i == 0)
|
||||
{
|
||||
g_ptr_array_add (new_lines, g_strdup_printf ("DEFAULT %s", val));
|
||||
}
|
||||
g_ptr_array_add (new_lines, g_strdup_printf ("DEFAULT %s", val));
|
||||
|
||||
g_ptr_array_add (new_lines, g_strdup_printf ("LABEL %s", val));
|
||||
|
||||
|
||||
val = ostree_bootconfig_parser_get (config, "linux");
|
||||
if (!val)
|
||||
{
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||
"No \"linux\" key in bootloader config");
|
||||
goto out;
|
||||
}
|
||||
return glnx_throw (error, "No \"linux\" key in bootloader config");
|
||||
g_ptr_array_add (new_lines, g_strdup_printf ("\tKERNEL %s", val));
|
||||
|
||||
val = ostree_bootconfig_parser_get (config, "initrd");
|
||||
@@ -108,72 +97,57 @@ append_config_from_boostree_loader_entries (OstreeBootloaderSyslinux *self,
|
||||
g_ptr_array_add (new_lines, g_strdup_printf ("\tAPPEND %s", val));
|
||||
}
|
||||
|
||||
ret = TRUE;
|
||||
out:
|
||||
return ret;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
_ostree_bootloader_syslinux_write_config (OstreeBootloader *bootloader,
|
||||
int bootversion,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
int bootversion,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
gboolean ret = FALSE;
|
||||
OstreeBootloaderSyslinux *self = OSTREE_BOOTLOADER_SYSLINUX (bootloader);
|
||||
g_autoptr(GFile) new_config_path = NULL;
|
||||
g_autofree char *config_contents = NULL;
|
||||
g_autofree char *new_config_contents = NULL;
|
||||
g_autoptr(GPtrArray) new_lines = NULL;
|
||||
g_autoptr(GPtrArray) tmp_lines = NULL;
|
||||
|
||||
g_autoptr(GFile) new_config_path =
|
||||
ot_gfile_resolve_path_printf (self->sysroot->path, "boot/loader.%d/syslinux.cfg", bootversion);
|
||||
|
||||
/* This should follow the symbolic link to the current bootversion. */
|
||||
g_autofree char *config_contents =
|
||||
glnx_file_get_contents_utf8_at (AT_FDCWD, gs_file_get_path_cached (self->config_path), NULL,
|
||||
cancellable, error);
|
||||
if (!config_contents)
|
||||
return FALSE;
|
||||
|
||||
g_auto(GStrv) lines = g_strsplit (config_contents, "\n", -1);
|
||||
g_autoptr(GPtrArray) new_lines = g_ptr_array_new_with_free_func (g_free);
|
||||
g_autoptr(GPtrArray) tmp_lines = g_ptr_array_new_with_free_func (g_free);
|
||||
|
||||
g_autofree char *kernel_arg = NULL;
|
||||
gboolean saw_default = FALSE;
|
||||
gboolean regenerate_default = FALSE;
|
||||
gboolean parsing_label = FALSE;
|
||||
char **lines = NULL;
|
||||
char **iter;
|
||||
guint i;
|
||||
|
||||
new_config_path = ot_gfile_resolve_path_printf (self->sysroot->path, "boot/loader.%d/syslinux.cfg",
|
||||
bootversion);
|
||||
|
||||
/* This should follow the symbolic link to the current bootversion. */
|
||||
config_contents = glnx_file_get_contents_utf8_at (AT_FDCWD, gs_file_get_path_cached (self->config_path), NULL,
|
||||
cancellable, error);
|
||||
if (!config_contents)
|
||||
goto out;
|
||||
|
||||
lines = g_strsplit (config_contents, "\n", -1);
|
||||
new_lines = g_ptr_array_new_with_free_func (g_free);
|
||||
tmp_lines = g_ptr_array_new_with_free_func (g_free);
|
||||
|
||||
/* Note special iteration condition here; we want to also loop one
|
||||
* more time at the end where line = NULL to ensure we finish off
|
||||
* processing the last LABEL.
|
||||
*/
|
||||
iter = lines;
|
||||
while (TRUE)
|
||||
for (char **iter = lines; iter; iter++)
|
||||
{
|
||||
char *line = *iter;
|
||||
const char *line = *iter;
|
||||
gboolean skip = FALSE;
|
||||
|
||||
if (parsing_label &&
|
||||
if (parsing_label &&
|
||||
(line == NULL || !g_str_has_prefix (line, "\t")))
|
||||
{
|
||||
parsing_label = FALSE;
|
||||
if (kernel_arg == NULL)
|
||||
{
|
||||
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||
"No KERNEL argument found after LABEL");
|
||||
goto out;
|
||||
}
|
||||
return glnx_throw (error, "No KERNEL argument found after LABEL");
|
||||
|
||||
/* If this is a non-ostree kernel, just emit the lines
|
||||
* we saw.
|
||||
*/
|
||||
if (!g_str_has_prefix (kernel_arg, "/ostree/"))
|
||||
{
|
||||
for (i = 0; i < tmp_lines->len; i++)
|
||||
for (guint i = 0; i < tmp_lines->len; i++)
|
||||
{
|
||||
g_ptr_array_add (new_lines, tmp_lines->pdata[i]);
|
||||
tmp_lines->pdata[i] = NULL; /* Transfer ownership */
|
||||
@@ -211,55 +185,37 @@ _ostree_bootloader_syslinux_write_config (OstreeBootloader *bootloader,
|
||||
* the title to hopefully avoid regressions. */
|
||||
if (g_str_has_prefix (line, "DEFAULT ostree:") || /* old format */
|
||||
strstr (line, "(ostree") != NULL) /* new format */
|
||||
{
|
||||
regenerate_default = TRUE;
|
||||
}
|
||||
regenerate_default = TRUE;
|
||||
skip = TRUE;
|
||||
}
|
||||
|
||||
if (skip)
|
||||
{
|
||||
g_free (line);
|
||||
}
|
||||
else
|
||||
|
||||
if (!skip)
|
||||
{
|
||||
if (parsing_label)
|
||||
{
|
||||
g_ptr_array_add (tmp_lines, line);
|
||||
}
|
||||
g_ptr_array_add (tmp_lines, g_strdup (line));
|
||||
else
|
||||
{
|
||||
g_ptr_array_add (new_lines, line);
|
||||
}
|
||||
g_ptr_array_add (new_lines, g_strdup (line));
|
||||
}
|
||||
/* Transfer ownership */
|
||||
*iter = NULL;
|
||||
iter++;
|
||||
}
|
||||
|
||||
if (!saw_default)
|
||||
regenerate_default = TRUE;
|
||||
|
||||
if (!append_config_from_boostree_loader_entries (self, regenerate_default,
|
||||
bootversion, new_lines,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
|
||||
new_config_contents = _ostree_sysroot_join_lines (new_lines);
|
||||
{
|
||||
g_autoptr(GBytes) new_config_contents_bytes =
|
||||
g_bytes_new_static (new_config_contents,
|
||||
strlen (new_config_contents));
|
||||
|
||||
if (!ot_gfile_replace_contents_fsync (new_config_path, new_config_contents_bytes,
|
||||
if (!append_config_from_loader_entries (self, regenerate_default,
|
||||
bootversion, new_lines,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = TRUE;
|
||||
out:
|
||||
g_free (lines); /* Note we freed elements individually */
|
||||
return ret;
|
||||
return FALSE;
|
||||
|
||||
g_autofree char *new_config_contents = _ostree_sysroot_join_lines (new_lines);
|
||||
g_autoptr(GBytes) new_config_contents_bytes =
|
||||
g_bytes_new_static (new_config_contents,
|
||||
strlen (new_config_contents));
|
||||
|
||||
if (!ot_gfile_replace_contents_fsync (new_config_path, new_config_contents_bytes,
|
||||
cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
|
||||
@@ -62,6 +62,44 @@ _ostree_bootloader_uboot_get_name (OstreeBootloader *bootloader)
|
||||
return "U-Boot";
|
||||
}
|
||||
|
||||
/* Append system's uEnv.txt, if it exists in $deployment/usr/lib/ostree-boot/ */
|
||||
static gboolean
|
||||
append_system_uenv (OstreeBootloaderUboot *self,
|
||||
const char *bootargs,
|
||||
GPtrArray *new_lines,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
glnx_fd_close int uenv_fd = -1;
|
||||
__attribute__((cleanup(_ostree_kernel_args_cleanup))) OstreeKernelArgs *kargs = NULL;
|
||||
const char *uenv_path = NULL;
|
||||
const char *ostree_arg = NULL;
|
||||
|
||||
kargs = _ostree_kernel_args_from_string (bootargs);
|
||||
ostree_arg = _ostree_kernel_args_get_last_value (kargs, "ostree");
|
||||
if (!ostree_arg)
|
||||
{
|
||||
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||
"No ostree= kernel argument found in boot loader configuration file");
|
||||
return FALSE;
|
||||
}
|
||||
ostree_arg += 1;
|
||||
uenv_path = glnx_strjoina (ostree_arg, "/usr/lib/ostree-boot/uEnv.txt");
|
||||
if (!ot_openat_ignore_enoent (self->sysroot->sysroot_fd, uenv_path, &uenv_fd, error))
|
||||
return FALSE;
|
||||
if (uenv_fd != -1)
|
||||
{
|
||||
char *uenv = glnx_fd_readall_utf8 (uenv_fd, NULL, cancellable, error);
|
||||
if (!uenv)
|
||||
{
|
||||
g_prefix_error (error, "Reading %s: ", uenv_path);
|
||||
return FALSE;
|
||||
}
|
||||
g_ptr_array_add (new_lines, uenv);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
create_config_from_boot_loader_entries (OstreeBootloaderUboot *self,
|
||||
int bootversion,
|
||||
@@ -77,61 +115,35 @@ create_config_from_boot_loader_entries (OstreeBootloaderUboot *self,
|
||||
cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
/* U-Boot doesn't support a menu so just pick the first one since the list is ordered */
|
||||
config = boot_loader_configs->pdata[0];
|
||||
|
||||
val = ostree_bootconfig_parser_get (config, "linux");
|
||||
if (!val)
|
||||
for (int i = 0; i < boot_loader_configs->len; i++)
|
||||
{
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||
"No \"linux\" key in bootloader config");
|
||||
return FALSE;
|
||||
}
|
||||
g_ptr_array_add (new_lines, g_strdup_printf ("kernel_image=%s", val));
|
||||
|
||||
val = ostree_bootconfig_parser_get (config, "initrd");
|
||||
if (val)
|
||||
g_ptr_array_add (new_lines, g_strdup_printf ("ramdisk_image=%s", val));
|
||||
|
||||
val = ostree_bootconfig_parser_get (config, "options");
|
||||
if (val)
|
||||
{
|
||||
glnx_fd_close int uenv_fd = -1;
|
||||
__attribute__((cleanup(_ostree_kernel_args_cleanup))) OstreeKernelArgs *kargs = NULL;
|
||||
const char *uenv_path = NULL;
|
||||
const char *ostree_arg = NULL;
|
||||
|
||||
g_ptr_array_add (new_lines, g_strdup_printf ("bootargs=%s", val));
|
||||
|
||||
/* Append system's uEnv.txt, if it exists in $deployment/usr/lib/ostree-boot/ */
|
||||
kargs = _ostree_kernel_args_from_string (val);
|
||||
ostree_arg = _ostree_kernel_args_get_last_value (kargs, "ostree");
|
||||
if (!ostree_arg)
|
||||
{
|
||||
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||
"No ostree= kernel argument found in boot loader configuration file");
|
||||
return FALSE;
|
||||
}
|
||||
ostree_arg += 1;
|
||||
uenv_path = glnx_strjoina (ostree_arg, "/usr/lib/ostree-boot/uEnv.txt");
|
||||
uenv_fd = openat (self->sysroot->sysroot_fd, uenv_path, O_CLOEXEC | O_RDONLY);
|
||||
if (uenv_fd != -1)
|
||||
{
|
||||
char *uenv = glnx_fd_readall_utf8 (uenv_fd, NULL, cancellable, error);
|
||||
if (!uenv)
|
||||
{
|
||||
g_prefix_error (error, "Reading %s: ", uenv_path);
|
||||
return FALSE;
|
||||
}
|
||||
g_ptr_array_add (new_lines, uenv);
|
||||
}
|
||||
g_autofree char *index_suffix = NULL;
|
||||
if (i == 0)
|
||||
index_suffix = g_strdup ("");
|
||||
else
|
||||
index_suffix = g_strdup_printf ("%d", i+1);
|
||||
config = boot_loader_configs->pdata[i];
|
||||
|
||||
val = ostree_bootconfig_parser_get (config, "linux");
|
||||
if (!val)
|
||||
{
|
||||
if (errno != ENOENT)
|
||||
{
|
||||
g_prefix_error (error, "openat %s: ", uenv_path);
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||
"No \"linux\" key in bootloader config");
|
||||
return FALSE;
|
||||
}
|
||||
g_ptr_array_add (new_lines, g_strdup_printf ("kernel_image%s=%s", index_suffix, val));
|
||||
|
||||
val = ostree_bootconfig_parser_get (config, "initrd");
|
||||
if (val)
|
||||
g_ptr_array_add (new_lines, g_strdup_printf ("ramdisk_image%s=%s", index_suffix, val));
|
||||
|
||||
val = ostree_bootconfig_parser_get (config, "options");
|
||||
if (val)
|
||||
{
|
||||
g_ptr_array_add (new_lines, g_strdup_printf ("bootargs%s=%s", index_suffix, val));
|
||||
if (i == 0)
|
||||
if (!append_system_uenv (self, val, new_lines, cancellable, error))
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -179,6 +179,13 @@ _ostree_raw_file_to_archive_stream (GInputStream *input,
|
||||
gboolean ostree_validate_collection_id (const char *collection_id, GError **error);
|
||||
#endif /* !OSTREE_ENABLE_EXPERIMENTAL_API */
|
||||
|
||||
gboolean
|
||||
_ostree_compare_timestamps (const char *current_rev,
|
||||
guint64 current_ts,
|
||||
const char *new_rev,
|
||||
guint64 new_ts,
|
||||
GError **error);
|
||||
|
||||
#if (defined(OSTREE_COMPILATION) || GLIB_CHECK_VERSION(2, 44, 0)) && !defined(OSTREE_ENABLE_EXPERIMENTAL_API)
|
||||
#include <libglnx.h>
|
||||
#include "ostree-ref.h"
|
||||
|
||||
@@ -312,7 +312,7 @@ _ostree_file_header_new (GFileInfo *file_info,
|
||||
* @file_info: a #GFileInfo
|
||||
* @xattrs: (allow-none): Optional extended attribute array
|
||||
*
|
||||
* Returns: (transfer full): A new #GVariant containing file header for an archive-z2 repository
|
||||
* Returns: (transfer full): A new #GVariant containing file header for an archive repository
|
||||
*/
|
||||
GVariant *
|
||||
_ostree_zlib_file_header_new (GFileInfo *file_info,
|
||||
@@ -2086,6 +2086,38 @@ ostree_commit_get_timestamp (GVariant *commit_variant)
|
||||
return GUINT64_FROM_BE (ret);
|
||||
}
|
||||
|
||||
/* Used in pull/deploy to validate we're not being downgraded */
|
||||
gboolean
|
||||
_ostree_compare_timestamps (const char *current_rev,
|
||||
guint64 current_ts,
|
||||
const char *new_rev,
|
||||
guint64 new_ts,
|
||||
GError **error)
|
||||
{
|
||||
/* Newer timestamp is OK */
|
||||
if (new_ts > current_ts)
|
||||
return TRUE;
|
||||
/* If they're equal, ensure they're the same rev */
|
||||
if (new_ts == current_ts || strcmp (current_rev, new_rev) == 0)
|
||||
return TRUE;
|
||||
|
||||
/* Looks like a downgrade, format an error message */
|
||||
g_autoptr(GDateTime) current_dt = g_date_time_new_from_unix_utc (current_ts);
|
||||
g_autoptr(GDateTime) new_dt = g_date_time_new_from_unix_utc (new_ts);
|
||||
|
||||
if (current_dt == NULL || new_dt == NULL)
|
||||
return glnx_throw (error, "Upgrade target revision '%s' timestamp (%" G_GINT64_FORMAT ") or current revision '%s' timestamp (%" G_GINT64_FORMAT ") is invalid",
|
||||
new_rev, new_ts,
|
||||
current_rev, current_ts);
|
||||
|
||||
g_autofree char *current_ts_str = g_date_time_format (current_dt, "%c");
|
||||
g_autofree char *new_ts_str = g_date_time_format (new_dt, "%c");
|
||||
|
||||
return glnx_throw (error, "Upgrade target revision '%s' with timestamp '%s' is chronologically older than current revision '%s' with timestamp '%s'",
|
||||
new_rev, new_ts_str, current_rev, current_ts_str);
|
||||
}
|
||||
|
||||
|
||||
GVariant *
|
||||
_ostree_detached_metadata_append_gpg_sig (GVariant *existing_metadata,
|
||||
GBytes *signature_bytes)
|
||||
|
||||
@@ -187,14 +187,14 @@ _ostree_gpg_verifier_check_signature (OstreeGpgVerifier *self,
|
||||
gpg_error = gpgme_data_new_from_fd (&kdata, fd);
|
||||
if (gpg_error != GPG_ERR_NO_ERROR)
|
||||
{
|
||||
ot_gpgme_error_to_gio_error (gpg_error, error);
|
||||
ot_gpgme_throw (gpg_error, error, "Loading data from fd %i", fd);
|
||||
goto out;
|
||||
}
|
||||
|
||||
gpg_error = gpgme_op_import (result->context, kdata);
|
||||
if (gpg_error != GPG_ERR_NO_ERROR)
|
||||
{
|
||||
ot_gpgme_error_to_gio_error (gpg_error, error);
|
||||
ot_gpgme_throw (gpg_error, error, "Failed to import key");
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
@@ -212,8 +212,7 @@ _ostree_gpg_verifier_check_signature (OstreeGpgVerifier *self,
|
||||
0 /* do not copy */);
|
||||
if (gpg_error != GPG_ERR_NO_ERROR)
|
||||
{
|
||||
ot_gpgme_error_to_gio_error (gpg_error, error);
|
||||
g_prefix_error (error, "Unable to read signed data: ");
|
||||
ot_gpgme_throw (gpg_error, error, "Unable to read signed data");
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -223,16 +222,14 @@ _ostree_gpg_verifier_check_signature (OstreeGpgVerifier *self,
|
||||
0 /* do not copy */);
|
||||
if (gpg_error != GPG_ERR_NO_ERROR)
|
||||
{
|
||||
ot_gpgme_error_to_gio_error (gpg_error, error);
|
||||
g_prefix_error (error, "Unable to read signature: ");
|
||||
ot_gpgme_throw (gpg_error, error, "Unable to read signature");
|
||||
goto out;
|
||||
}
|
||||
|
||||
gpg_error = gpgme_op_verify (result->context, signature_buffer, data_buffer, NULL);
|
||||
if (gpg_error != GPG_ERR_NO_ERROR)
|
||||
{
|
||||
ot_gpgme_error_to_gio_error (gpg_error, error);
|
||||
g_prefix_error (error, "Unable to complete signature verification: ");
|
||||
ot_gpgme_throw (gpg_error, error, "Unable to complete signature verification");
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -368,31 +365,22 @@ _ostree_gpg_verifier_add_global_keyring_dir (OstreeGpgVerifier *self,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
const char *global_keyring_path = g_getenv ("OSTREE_GPG_HOME");
|
||||
g_autoptr(GFile) global_keyring_dir = NULL;
|
||||
gboolean ret = FALSE;
|
||||
|
||||
g_return_val_if_fail (OSTREE_IS_GPG_VERIFIER (self), FALSE);
|
||||
|
||||
const char *global_keyring_path = g_getenv ("OSTREE_GPG_HOME");
|
||||
if (global_keyring_path == NULL)
|
||||
global_keyring_path = DATADIR "/ostree/trusted.gpg.d/";
|
||||
|
||||
if (g_file_test (global_keyring_path, G_FILE_TEST_IS_DIR))
|
||||
{
|
||||
global_keyring_dir = g_file_new_for_path (global_keyring_path);
|
||||
g_autoptr(GFile) global_keyring_dir = g_file_new_for_path (global_keyring_path);
|
||||
if (!_ostree_gpg_verifier_add_keyring_dir (self, global_keyring_dir,
|
||||
cancellable, error))
|
||||
{
|
||||
g_prefix_error (error, "Reading keyring directory '%s'",
|
||||
gs_file_get_path_cached (global_keyring_dir));
|
||||
goto out;
|
||||
}
|
||||
return glnx_prefix_error (error, "Reading keyring directory '%s'",
|
||||
gs_file_get_path_cached (global_keyring_dir));
|
||||
}
|
||||
|
||||
ret = TRUE;
|
||||
|
||||
out:
|
||||
return ret;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
OstreeGpgVerifier*
|
||||
|
||||
@@ -63,7 +63,8 @@ static OstreeGpgSignatureAttr all_signature_attrs[] = {
|
||||
OSTREE_GPG_SIGNATURE_ATTR_PUBKEY_ALGO_NAME,
|
||||
OSTREE_GPG_SIGNATURE_ATTR_HASH_ALGO_NAME,
|
||||
OSTREE_GPG_SIGNATURE_ATTR_USER_NAME,
|
||||
OSTREE_GPG_SIGNATURE_ATTR_USER_EMAIL
|
||||
OSTREE_GPG_SIGNATURE_ATTR_USER_EMAIL,
|
||||
OSTREE_GPG_SIGNATURE_ATTR_FINGERPRINT_PRIMARY
|
||||
};
|
||||
|
||||
static void ostree_gpg_verify_result_initable_iface_init (GInitableIface *iface);
|
||||
@@ -133,8 +134,7 @@ ostree_gpg_verify_result_initable_init (GInitable *initable,
|
||||
gpg_error = gpgme_new (&result->context);
|
||||
if (gpg_error != GPG_ERR_NO_ERROR)
|
||||
{
|
||||
ot_gpgme_error_to_gio_error (gpg_error, error);
|
||||
g_prefix_error (error, "Unable to create context: ");
|
||||
ot_gpgme_throw (gpg_error, error, "Unable to create context");
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -328,9 +328,9 @@ ostree_gpg_verify_result_get (OstreeGpgVerifyResult *result,
|
||||
* (OSTREE_GPG_SIGNATURE_ATTR_KEY_MISSING). */
|
||||
for (ii = 0; ii < n_attrs; ii++)
|
||||
{
|
||||
if (attrs[ii] == OSTREE_GPG_SIGNATURE_ATTR_FINGERPRINT ||
|
||||
attrs[ii] == OSTREE_GPG_SIGNATURE_ATTR_USER_NAME ||
|
||||
attrs[ii] == OSTREE_GPG_SIGNATURE_ATTR_USER_EMAIL)
|
||||
if (attrs[ii] == OSTREE_GPG_SIGNATURE_ATTR_USER_NAME ||
|
||||
attrs[ii] == OSTREE_GPG_SIGNATURE_ATTR_USER_EMAIL ||
|
||||
attrs[ii] == OSTREE_GPG_SIGNATURE_ATTR_FINGERPRINT_PRIMARY)
|
||||
{
|
||||
(void) gpgme_get_key (result->context, signature->fpr, &key, 0);
|
||||
break;
|
||||
@@ -373,11 +373,7 @@ ostree_gpg_verify_result_get (OstreeGpgVerifyResult *result,
|
||||
break;
|
||||
|
||||
case OSTREE_GPG_SIGNATURE_ATTR_FINGERPRINT:
|
||||
if (key != NULL && key->subkeys != NULL)
|
||||
v_string = key->subkeys->fpr;
|
||||
else
|
||||
v_string = signature->fpr;
|
||||
child = g_variant_new_string (v_string);
|
||||
child = g_variant_new_string (signature->fpr);
|
||||
break;
|
||||
|
||||
case OSTREE_GPG_SIGNATURE_ATTR_TIMESTAMP:
|
||||
@@ -418,6 +414,14 @@ ostree_gpg_verify_result_get (OstreeGpgVerifyResult *result,
|
||||
child = g_variant_new_string (v_string);
|
||||
break;
|
||||
|
||||
case OSTREE_GPG_SIGNATURE_ATTR_FINGERPRINT_PRIMARY:
|
||||
if (key != NULL && key->subkeys != NULL)
|
||||
v_string = key->subkeys->fpr;
|
||||
if (v_string == NULL)
|
||||
v_string = "";
|
||||
child = g_variant_new_string (v_string);
|
||||
break;
|
||||
|
||||
default:
|
||||
g_critical ("Invalid signature attribute (%d)", attrs[ii]);
|
||||
g_variant_builder_clear (&builder);
|
||||
@@ -535,6 +539,7 @@ ostree_gpg_verify_result_describe_variant (GVariant *variant,
|
||||
gint64 exp_timestamp;
|
||||
const char *type_string;
|
||||
const char *fingerprint;
|
||||
const char *fingerprint_primary;
|
||||
const char *pubkey_algo;
|
||||
const char *user_name;
|
||||
const char *user_email;
|
||||
@@ -550,7 +555,7 @@ ostree_gpg_verify_result_describe_variant (GVariant *variant,
|
||||
/* Verify the variant's type string. This code is
|
||||
* not prepared to handle just any random GVariant. */
|
||||
type_string = g_variant_get_type_string (variant);
|
||||
g_return_if_fail (strcmp (type_string, "(bbbbbsxxssss)") == 0);
|
||||
g_return_if_fail (strcmp (type_string, "(bbbbbsxxsssss)") == 0);
|
||||
|
||||
/* The default format roughly mimics the verify output generated by
|
||||
* check_sig_and_print() in gnupg/g10/mainproc.c, though obviously
|
||||
@@ -564,6 +569,8 @@ ostree_gpg_verify_result_describe_variant (GVariant *variant,
|
||||
"b", &key_missing);
|
||||
g_variant_get_child (variant, OSTREE_GPG_SIGNATURE_ATTR_FINGERPRINT,
|
||||
"&s", &fingerprint);
|
||||
g_variant_get_child (variant, OSTREE_GPG_SIGNATURE_ATTR_FINGERPRINT_PRIMARY,
|
||||
"&s", &fingerprint_primary);
|
||||
g_variant_get_child (variant, OSTREE_GPG_SIGNATURE_ATTR_TIMESTAMP,
|
||||
"x", ×tamp);
|
||||
g_variant_get_child (variant, OSTREE_GPG_SIGNATURE_ATTR_EXP_TIMESTAMP,
|
||||
@@ -628,8 +635,26 @@ ostree_gpg_verify_result_describe_variant (GVariant *variant,
|
||||
user_name, user_email);
|
||||
}
|
||||
|
||||
if (!key_missing && (g_strcmp0 (fingerprint, fingerprint_primary) != 0))
|
||||
{
|
||||
const char *key_id_primary;
|
||||
|
||||
len = strlen (fingerprint_primary);
|
||||
key_id_primary = (len > 16) ? fingerprint_primary + len - 16 :
|
||||
fingerprint_primary;
|
||||
|
||||
if (line_prefix != NULL)
|
||||
g_string_append (output_buffer, line_prefix);
|
||||
|
||||
g_string_append_printf (output_buffer,
|
||||
"Primary key ID %s\n", key_id_primary);
|
||||
}
|
||||
|
||||
if (exp_timestamp > 0)
|
||||
{
|
||||
if (line_prefix != NULL)
|
||||
g_string_append (output_buffer, line_prefix);
|
||||
|
||||
date_time_utc = g_date_time_new_from_unix_utc (exp_timestamp);
|
||||
if (date_time_utc == NULL)
|
||||
{
|
||||
@@ -642,9 +667,6 @@ ostree_gpg_verify_result_describe_variant (GVariant *variant,
|
||||
date_time_local = g_date_time_to_local (date_time_utc);
|
||||
formatted_date_time = g_date_time_format (date_time_local, "%c");
|
||||
|
||||
if (line_prefix != NULL)
|
||||
g_string_append (output_buffer, line_prefix);
|
||||
|
||||
if (sig_expired)
|
||||
{
|
||||
g_string_append_printf (output_buffer,
|
||||
|
||||
@@ -64,6 +64,11 @@ typedef struct OstreeGpgVerifyResult OstreeGpgVerifyResult;
|
||||
* @OSTREE_GPG_SIGNATURE_ATTR_USER_EMAIL:
|
||||
* [#G_VARIANT_TYPE_STRING] The email address of the signing key's primary
|
||||
* user
|
||||
* @OSTREE_GPG_SIGNATURE_ATTR_FINGERPRINT_PRIMARY:
|
||||
* [#G_VARIANT_TYPE_STRING] Fingerprint of the signing key's primary key
|
||||
* (will be the same as OSTREE_GPG_SIGNATURE_ATTR_FINGERPRINT if the
|
||||
* the signature is already from the primary key rather than a subkey,
|
||||
* and will be the empty string if the key is missing.)
|
||||
*
|
||||
* Signature attributes available from an #OstreeGpgVerifyResult.
|
||||
* The attribute's #GVariantType is shown in brackets.
|
||||
@@ -80,7 +85,8 @@ typedef enum {
|
||||
OSTREE_GPG_SIGNATURE_ATTR_PUBKEY_ALGO_NAME,
|
||||
OSTREE_GPG_SIGNATURE_ATTR_HASH_ALGO_NAME,
|
||||
OSTREE_GPG_SIGNATURE_ATTR_USER_NAME,
|
||||
OSTREE_GPG_SIGNATURE_ATTR_USER_EMAIL
|
||||
OSTREE_GPG_SIGNATURE_ATTR_USER_EMAIL,
|
||||
OSTREE_GPG_SIGNATURE_ATTR_FINGERPRINT_PRIMARY,
|
||||
} OstreeGpgSignatureAttr;
|
||||
|
||||
_OSTREE_PUBLIC
|
||||
|
||||
@@ -38,6 +38,28 @@ typedef struct archive OtAutoArchiveWrite;
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(OtAutoArchiveWrite, archive_write_free)
|
||||
typedef struct archive OtAutoArchiveRead;
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(OtAutoArchiveRead, archive_read_free)
|
||||
|
||||
static inline OtAutoArchiveRead *
|
||||
ot_open_archive_read (const char *path, GError **error)
|
||||
{
|
||||
g_autoptr(OtAutoArchiveRead) a = archive_read_new ();
|
||||
|
||||
#ifdef HAVE_ARCHIVE_READ_SUPPORT_FILTER_ALL
|
||||
archive_read_support_filter_all (a);
|
||||
#else
|
||||
archive_read_support_compression_all (a);
|
||||
#endif
|
||||
archive_read_support_format_all (a);
|
||||
if (archive_read_open_filename (a, path, 8192) != ARCHIVE_OK)
|
||||
{
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||
"%s", archive_error_string (a));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return g_steal_pointer (&a);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
G_END_DECLS
|
||||
@@ -188,8 +188,6 @@ create_file_copy_from_input_at (OstreeRepo *repo,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
const gboolean union_mode = options->overwrite_mode == OSTREE_REPO_CHECKOUT_OVERWRITE_UNION_FILES;
|
||||
const gboolean add_mode = options->overwrite_mode == OSTREE_REPO_CHECKOUT_OVERWRITE_ADD_FILES;
|
||||
const gboolean sepolicy_enabled = options->sepolicy && !repo->disable_xattrs;
|
||||
g_autoptr(GVariant) modified_xattrs = NULL;
|
||||
|
||||
@@ -218,23 +216,51 @@ create_file_copy_from_input_at (OstreeRepo *repo,
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (symlinkat (g_file_info_get_symlink_target (file_info),
|
||||
destination_dfd, destination_name) < 0)
|
||||
const char *target = g_file_info_get_symlink_target (file_info);
|
||||
if (symlinkat (target, destination_dfd, destination_name) < 0)
|
||||
{
|
||||
if (errno != EEXIST)
|
||||
return glnx_throw_errno_prefix (error, "symlinkat");
|
||||
|
||||
/* Handle union/add behaviors if we get EEXIST */
|
||||
if (errno == EEXIST && union_mode)
|
||||
switch (options->overwrite_mode)
|
||||
{
|
||||
/* Unioning? Let's unlink and try again */
|
||||
(void) unlinkat (destination_dfd, destination_name, 0);
|
||||
if (symlinkat (g_file_info_get_symlink_target (file_info),
|
||||
destination_dfd, destination_name) < 0)
|
||||
return glnx_throw_errno (error);
|
||||
case OSTREE_REPO_CHECKOUT_OVERWRITE_NONE:
|
||||
return glnx_throw_errno_prefix (error, "symlinkat");
|
||||
case OSTREE_REPO_CHECKOUT_OVERWRITE_UNION_FILES:
|
||||
{
|
||||
/* Unioning? Let's unlink and try again */
|
||||
(void) unlinkat (destination_dfd, destination_name, 0);
|
||||
if (symlinkat (target, destination_dfd, destination_name) < 0)
|
||||
return glnx_throw_errno_prefix (error, "symlinkat");
|
||||
}
|
||||
case OSTREE_REPO_CHECKOUT_OVERWRITE_ADD_FILES:
|
||||
/* Note early return - we don't want to set the xattrs below */
|
||||
return TRUE;
|
||||
case OSTREE_REPO_CHECKOUT_OVERWRITE_UNION_IDENTICAL:
|
||||
{
|
||||
/* See the comments for the hardlink version of this
|
||||
* for why we do this.
|
||||
*/
|
||||
struct stat dest_stbuf;
|
||||
if (!glnx_fstatat (destination_dfd, destination_name, &dest_stbuf,
|
||||
AT_SYMLINK_NOFOLLOW, error))
|
||||
return FALSE;
|
||||
if (S_ISLNK (dest_stbuf.st_mode))
|
||||
{
|
||||
g_autofree char *dest_target =
|
||||
glnx_readlinkat_malloc (destination_dfd, destination_name,
|
||||
cancellable, error);
|
||||
if (!dest_target)
|
||||
return FALSE;
|
||||
/* In theory we could also compare xattrs...but eh */
|
||||
if (g_str_equal (dest_target, target))
|
||||
return TRUE;
|
||||
}
|
||||
errno = EEXIST;
|
||||
return glnx_throw_errno_prefix (error, "symlinkat");
|
||||
}
|
||||
}
|
||||
else if (errno == EEXIST && add_mode)
|
||||
/* Note early return - we don't want to set the xattrs below */
|
||||
return TRUE;
|
||||
else
|
||||
return glnx_throw_errno (error);
|
||||
}
|
||||
|
||||
/* Process ownership and xattrs now that we made the link */
|
||||
@@ -255,7 +281,6 @@ create_file_copy_from_input_at (OstreeRepo *repo,
|
||||
else if (g_file_info_get_file_type (file_info) == G_FILE_TYPE_REGULAR)
|
||||
{
|
||||
g_auto(GLnxTmpfile) tmpf = { 0, };
|
||||
GLnxLinkTmpfileReplaceMode replace_mode;
|
||||
|
||||
if (!glnx_open_tmpfile_linkable_at (destination_dfd, ".", O_WRONLY | O_CLOEXEC,
|
||||
&tmpf, error))
|
||||
@@ -278,12 +303,23 @@ create_file_copy_from_input_at (OstreeRepo *repo,
|
||||
return FALSE;
|
||||
|
||||
/* The add/union/none behaviors map directly to GLnxLinkTmpfileReplaceMode */
|
||||
if (add_mode)
|
||||
replace_mode = GLNX_LINK_TMPFILE_NOREPLACE_IGNORE_EXIST;
|
||||
else if (union_mode)
|
||||
replace_mode = GLNX_LINK_TMPFILE_REPLACE;
|
||||
else
|
||||
replace_mode = GLNX_LINK_TMPFILE_NOREPLACE;
|
||||
GLnxLinkTmpfileReplaceMode replace_mode;
|
||||
switch (options->overwrite_mode)
|
||||
{
|
||||
case OSTREE_REPO_CHECKOUT_OVERWRITE_NONE:
|
||||
replace_mode = GLNX_LINK_TMPFILE_NOREPLACE;
|
||||
break;
|
||||
case OSTREE_REPO_CHECKOUT_OVERWRITE_UNION_FILES:
|
||||
replace_mode = GLNX_LINK_TMPFILE_REPLACE;
|
||||
break;
|
||||
case OSTREE_REPO_CHECKOUT_OVERWRITE_ADD_FILES:
|
||||
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 ();
|
||||
break;
|
||||
}
|
||||
|
||||
if (!glnx_link_tmpfile_at (&tmpf, replace_mode,
|
||||
destination_dfd, destination_name,
|
||||
@@ -329,25 +365,61 @@ checkout_file_hardlink (OstreeRepo *self,
|
||||
else if (allow_noent && errno == ENOENT)
|
||||
{
|
||||
}
|
||||
else if (errno == EEXIST && options->overwrite_mode == OSTREE_REPO_CHECKOUT_OVERWRITE_ADD_FILES)
|
||||
else if (errno == EEXIST)
|
||||
{
|
||||
/* In this mode, we keep existing content. Distinguish this case though to
|
||||
* avoid inserting into the devino cache.
|
||||
*/
|
||||
ret_result = HARDLINK_RESULT_SKIP_EXISTED;
|
||||
}
|
||||
else if (errno == EEXIST && options->overwrite_mode == OSTREE_REPO_CHECKOUT_OVERWRITE_UNION_FILES)
|
||||
{
|
||||
/* Idiocy, from man rename(2)
|
||||
*
|
||||
* "If oldpath and newpath are existing hard links referring to
|
||||
* the same file, then rename() does nothing, and returns a
|
||||
* success status."
|
||||
*
|
||||
* So we can't make this atomic.
|
||||
*/
|
||||
(void) unlinkat (destination_dfd, destination_name, 0);
|
||||
goto again;
|
||||
/* When we get EEXIST, we need to handle the different overwrite modes. */
|
||||
switch (options->overwrite_mode)
|
||||
{
|
||||
case OSTREE_REPO_CHECKOUT_OVERWRITE_NONE:
|
||||
/* Just throw */
|
||||
return glnx_throw_errno_prefix (error, "Hardlinking %s to %s", loose_path, destination_name);
|
||||
case OSTREE_REPO_CHECKOUT_OVERWRITE_ADD_FILES:
|
||||
/* In this mode, we keep existing content. Distinguish this case though to
|
||||
* avoid inserting into the devino cache.
|
||||
*/
|
||||
ret_result = HARDLINK_RESULT_SKIP_EXISTED;
|
||||
break;
|
||||
case OSTREE_REPO_CHECKOUT_OVERWRITE_UNION_FILES:
|
||||
{
|
||||
/* Idiocy, from man rename(2)
|
||||
*
|
||||
* "If oldpath and newpath are existing hard links referring to
|
||||
* the same file, then rename() does nothing, and returns a
|
||||
* success status."
|
||||
*
|
||||
* So we can't make this atomic.
|
||||
*/
|
||||
(void) unlinkat (destination_dfd, destination_name, 0);
|
||||
goto again;
|
||||
}
|
||||
case OSTREE_REPO_CHECKOUT_OVERWRITE_UNION_IDENTICAL:
|
||||
{
|
||||
/* In this mode, we error out on EEXIST *unless* the files are already
|
||||
* hardlinked, which is what rpm-ostree wants for package layering.
|
||||
* https://github.com/projectatomic/rpm-ostree/issues/982
|
||||
*
|
||||
* This should be similar to the librpm version:
|
||||
* https://github.com/rpm-software-management/rpm/blob/e3cd2bc85e0578f158d14e6f9624eb955c32543b/lib/rpmfi.c#L921
|
||||
* in rpmfilesCompare().
|
||||
*/
|
||||
struct stat src_stbuf;
|
||||
if (!glnx_fstatat (srcfd, loose_path, &src_stbuf,
|
||||
AT_SYMLINK_NOFOLLOW, error))
|
||||
return FALSE;
|
||||
struct stat dest_stbuf;
|
||||
if (!glnx_fstatat (destination_dfd, destination_name, &dest_stbuf,
|
||||
AT_SYMLINK_NOFOLLOW, error))
|
||||
return FALSE;
|
||||
const gboolean is_identical =
|
||||
(src_stbuf.st_dev == dest_stbuf.st_dev &&
|
||||
src_stbuf.st_ino == dest_stbuf.st_ino);
|
||||
if (is_identical)
|
||||
ret_result = HARDLINK_RESULT_SKIP_EXISTED;
|
||||
else
|
||||
return glnx_throw_errno_prefix (error, "Hardlinking %s to %s", loose_path, destination_name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -652,12 +724,20 @@ checkout_tree_at_recurse (OstreeRepo *self,
|
||||
*/
|
||||
if (TEMP_FAILURE_RETRY (mkdirat (destination_parent_fd, destination_name, 0700)) < 0)
|
||||
{
|
||||
if (errno == EEXIST &&
|
||||
(options->overwrite_mode == OSTREE_REPO_CHECKOUT_OVERWRITE_UNION_FILES
|
||||
|| options->overwrite_mode == OSTREE_REPO_CHECKOUT_OVERWRITE_ADD_FILES))
|
||||
did_exist = TRUE;
|
||||
else
|
||||
return glnx_throw_errno (error);
|
||||
if (errno != EEXIST)
|
||||
return glnx_throw_errno_prefix (error, "mkdirat");
|
||||
|
||||
switch (options->overwrite_mode)
|
||||
{
|
||||
case OSTREE_REPO_CHECKOUT_OVERWRITE_NONE:
|
||||
return glnx_throw_errno_prefix (error, "mkdirat");
|
||||
/* All of these cases are the same for directories */
|
||||
case OSTREE_REPO_CHECKOUT_OVERWRITE_UNION_FILES:
|
||||
case OSTREE_REPO_CHECKOUT_OVERWRITE_ADD_FILES:
|
||||
case OSTREE_REPO_CHECKOUT_OVERWRITE_UNION_IDENTICAL:
|
||||
did_exist = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1001,6 +1081,9 @@ ostree_repo_checkout_at (OstreeRepo *self,
|
||||
|
||||
g_return_val_if_fail (!(options->force_copy && options->no_copy_fallback), FALSE);
|
||||
g_return_val_if_fail (!options->sepolicy || options->force_copy, FALSE);
|
||||
/* union identical requires hardlink mode */
|
||||
g_return_val_if_fail (!(options->overwrite_mode == OSTREE_REPO_CHECKOUT_OVERWRITE_UNION_IDENTICAL &&
|
||||
!options->no_copy_fallback), FALSE);
|
||||
|
||||
g_autoptr(GFile) commit_root = (GFile*) _ostree_repo_file_new_for_commit (self, commit, error);
|
||||
if (!commit_root)
|
||||
@@ -1120,11 +1203,8 @@ ostree_repo_checkout_gc (OstreeRepo *self,
|
||||
|
||||
if (stbuf.st_nlink == 1)
|
||||
{
|
||||
if (unlinkat (dfd_iter.fd, dent->d_name, 0) != 0)
|
||||
{
|
||||
glnx_set_error_from_errno (error);
|
||||
return FALSE;
|
||||
}
|
||||
if (!glnx_unlinkat (dfd_iter.fd, dent->d_name, 0, error))
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
#include "ostree.h"
|
||||
#include "ostree-core-private.h"
|
||||
#include "ostree-repo-private.h"
|
||||
#include "ostree-sepolicy-private.h"
|
||||
#include "ostree-repo-file-enumerator.h"
|
||||
#include "ostree-checksum-input-stream.h"
|
||||
#include "ostree-varint.h"
|
||||
@@ -1028,35 +1029,33 @@ devino_cache_lookup (OstreeRepo *self,
|
||||
* @cancellable: Cancellable
|
||||
* @error: Error
|
||||
*
|
||||
* When ostree builds a mutable tree from directory like in
|
||||
* ostree_repo_write_directory_to_mtree(), it has to scan all files that you
|
||||
* pass in and compute their checksums. If your commit contains hardlinks from
|
||||
* ostree's existing repo, ostree can build a mapping of device numbers and
|
||||
* inodes to their checksum.
|
||||
* This function is deprecated in favor of using ostree_repo_devino_cache_new(),
|
||||
* which allows a precise mapping to be built up between hardlink checkout files
|
||||
* and their checksums between `ostree_repo_checkout_at()` and
|
||||
* `ostree_repo_write_directory_to_mtree()`.
|
||||
*
|
||||
* When invoking ostree_repo_write_directory_to_mtree(), it has to compute the
|
||||
* checksum of all files. If your commit contains hardlinks from a checkout,
|
||||
* this functions builds a mapping of device numbers and inodes to their
|
||||
* checksum.
|
||||
*
|
||||
* There is an upfront cost to creating this mapping, as this will scan the
|
||||
* entire objects directory. If your commit is composed of mostly hardlinks to
|
||||
* existing ostree objects, then this will speed up considerably, so call it
|
||||
* before you call ostree_write_directory_to_mtree() or similar.
|
||||
* before you call ostree_write_directory_to_mtree() or similar. However,
|
||||
* ostree_repo_devino_cache_new() is better as it avoids scanning all objects.
|
||||
*/
|
||||
gboolean
|
||||
ostree_repo_scan_hardlinks (OstreeRepo *self,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
gboolean ret = FALSE;
|
||||
|
||||
g_return_val_if_fail (self->in_transaction == TRUE, FALSE);
|
||||
|
||||
if (!self->loose_object_devino_hash)
|
||||
self->loose_object_devino_hash = (GHashTable*)ostree_repo_devino_cache_new ();
|
||||
g_hash_table_remove_all (self->loose_object_devino_hash);
|
||||
if (!scan_loose_devino (self, self->loose_object_devino_hash, cancellable, error))
|
||||
goto out;
|
||||
|
||||
ret = TRUE;
|
||||
out:
|
||||
return ret;
|
||||
return scan_loose_devino (self, self->loose_object_devino_hash, cancellable, error);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1132,6 +1131,7 @@ rename_pending_loose_objects (OstreeRepo *self,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
GLNX_AUTO_PREFIX_ERROR ("rename pending", error);
|
||||
g_auto(GLnxDirFdIterator) dfd_iter = { 0, };
|
||||
|
||||
if (!glnx_dirfd_iterator_init_at (self->commit_stagedir_fd, ".", FALSE, &dfd_iter, error))
|
||||
@@ -1224,11 +1224,10 @@ cleanup_tmpdir (OstreeRepo *self,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
GLNX_AUTO_PREFIX_ERROR ("tmpdir cleanup", error);
|
||||
const guint64 curtime_secs = g_get_real_time () / 1000000;
|
||||
|
||||
g_auto(GLnxDirFdIterator) dfd_iter = { 0, };
|
||||
guint64 curtime_secs;
|
||||
|
||||
curtime_secs = g_get_real_time () / 1000000;
|
||||
|
||||
if (!glnx_dirfd_iterator_init_at (self->tmp_dir_fd, ".", TRUE, &dfd_iter, error))
|
||||
return FALSE;
|
||||
|
||||
@@ -1256,7 +1255,7 @@ cleanup_tmpdir (OstreeRepo *self,
|
||||
{
|
||||
if (errno == ENOENT) /* Did another cleanup win? */
|
||||
continue;
|
||||
return glnx_throw_errno (error);
|
||||
return glnx_throw_errno_prefix (error, "fstatat(%s)", dent->d_name);
|
||||
}
|
||||
|
||||
/* First, if it's a directory which needs locking, but it's
|
||||
@@ -1283,7 +1282,7 @@ cleanup_tmpdir (OstreeRepo *self,
|
||||
* from *other* boots
|
||||
*/
|
||||
if (!glnx_shutil_rm_rf_at (dfd_iter.fd, dent->d_name, cancellable, error))
|
||||
return FALSE;
|
||||
return glnx_prefix_error (error, "Removing %s", dent->d_name);
|
||||
}
|
||||
/* FIXME - move OSTREE_REPO_TMPDIR_FETCHER underneath the
|
||||
* staging/boot-id scheme as well, since all of the "did it get
|
||||
@@ -1308,7 +1307,7 @@ cleanup_tmpdir (OstreeRepo *self,
|
||||
if (delta > self->tmp_expiry_seconds)
|
||||
{
|
||||
if (!glnx_shutil_rm_rf_at (dfd_iter.fd, dent->d_name, cancellable, error))
|
||||
return FALSE;
|
||||
return glnx_prefix_error (error, "Removing %s", dent->d_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1535,7 +1534,7 @@ ostree_repo_commit_transaction (OstreeRepo *self,
|
||||
if (g_getenv ("OSTREE_SUPPRESS_SYNCFS") == NULL)
|
||||
{
|
||||
if (syncfs (self->tmp_dir_fd) < 0)
|
||||
return glnx_throw_errno (error);
|
||||
return glnx_throw_errno_prefix (error, "syncfs");
|
||||
}
|
||||
|
||||
if (!rename_pending_loose_objects (self, cancellable, error))
|
||||
@@ -2335,6 +2334,10 @@ create_tree_variant_from_hashes (GHashTable *file_checksums,
|
||||
return serialized_tree;
|
||||
}
|
||||
|
||||
/* If any filtering is set up, perform it, and return modified file info in
|
||||
* @out_modified_info. Note that if no filtering is applied, @out_modified_info
|
||||
* will simply be another reference (with incremented refcount) to @file_info.
|
||||
*/
|
||||
OstreeRepoCommitFilterResult
|
||||
_ostree_repo_commit_modifier_apply (OstreeRepo *self,
|
||||
OstreeRepoCommitModifier *modifier,
|
||||
@@ -2410,24 +2413,6 @@ ptrarray_path_join (GPtrArray *path)
|
||||
return g_string_free (path_buf, FALSE);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
apply_commit_filter (OstreeRepo *self,
|
||||
OstreeRepoCommitModifier *modifier,
|
||||
const char *relpath,
|
||||
GFileInfo *file_info,
|
||||
GFileInfo **out_modified_info)
|
||||
{
|
||||
if (modifier == NULL ||
|
||||
(modifier->filter == NULL &&
|
||||
(modifier->flags & OSTREE_REPO_COMMIT_MODIFIER_FLAGS_CANONICAL_PERMISSIONS) == 0))
|
||||
{
|
||||
*out_modified_info = g_object_ref (file_info);
|
||||
return OSTREE_REPO_COMMIT_FILTER_ALLOW;
|
||||
}
|
||||
|
||||
return _ostree_repo_commit_modifier_apply (self, modifier, relpath, file_info, out_modified_info);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
get_modified_xattrs (OstreeRepo *self,
|
||||
OstreeRepoCommitModifier *modifier,
|
||||
@@ -2498,6 +2483,16 @@ get_modified_xattrs (OstreeRepo *self,
|
||||
{
|
||||
g_autoptr(GVariantBuilder) builder = NULL;
|
||||
|
||||
if (ret_xattrs)
|
||||
{
|
||||
/* drop out any existing SELinux policy from the set, so we don't end up
|
||||
* counting it twice in the checksum */
|
||||
g_autoptr(GVariant) new_ret_xattrs = NULL;
|
||||
new_ret_xattrs = _ostree_filter_selinux_xattr (ret_xattrs);
|
||||
g_variant_unref (ret_xattrs);
|
||||
ret_xattrs = g_steal_pointer (&new_ret_xattrs);
|
||||
}
|
||||
|
||||
/* ret_xattrs may be NULL */
|
||||
builder = ot_util_variant_builder_from_variant (ret_xattrs,
|
||||
G_VARIANT_TYPE ("a(ayay)"));
|
||||
@@ -2564,7 +2559,7 @@ write_directory_content_to_mtree_internal (OstreeRepo *self,
|
||||
if (modifier != NULL)
|
||||
child_relpath = ptrarray_path_join (path);
|
||||
|
||||
filter_result = apply_commit_filter (self, modifier, child_relpath, child_info, &modified_info);
|
||||
filter_result = _ostree_repo_commit_modifier_apply (self, modifier, child_relpath, child_info, &modified_info);
|
||||
|
||||
if (filter_result != OSTREE_REPO_COMMIT_FILTER_ALLOW)
|
||||
{
|
||||
@@ -2733,7 +2728,7 @@ write_directory_to_mtree_internal (OstreeRepo *self,
|
||||
relpath = ptrarray_path_join (path);
|
||||
|
||||
g_autoptr(GFileInfo) modified_info = NULL;
|
||||
filter_result = apply_commit_filter (self, modifier, relpath, child_info, &modified_info);
|
||||
filter_result = _ostree_repo_commit_modifier_apply (self, modifier, relpath, child_info, &modified_info);
|
||||
|
||||
if (filter_result == OSTREE_REPO_COMMIT_FILTER_ALLOW)
|
||||
{
|
||||
@@ -2803,8 +2798,8 @@ write_dfd_iter_to_mtree_internal (OstreeRepo *self,
|
||||
OstreeRepoCommitFilterResult filter_result;
|
||||
struct stat dir_stbuf;
|
||||
|
||||
if (fstat (src_dfd_iter->fd, &dir_stbuf) != 0)
|
||||
return glnx_throw_errno (error);
|
||||
if (!glnx_fstat (src_dfd_iter->fd, &dir_stbuf, error))
|
||||
return FALSE;
|
||||
|
||||
child_info = _ostree_stbuf_to_gfileinfo (&dir_stbuf);
|
||||
|
||||
@@ -2812,7 +2807,7 @@ write_dfd_iter_to_mtree_internal (OstreeRepo *self,
|
||||
{
|
||||
relpath = ptrarray_path_join (path);
|
||||
|
||||
filter_result = apply_commit_filter (self, modifier, relpath, child_info, &modified_info);
|
||||
filter_result = _ostree_repo_commit_modifier_apply (self, modifier, relpath, child_info, &modified_info);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -314,7 +314,9 @@ ostree_repo_finder_mount_resolve_async (OstreeRepoFinder *finde
|
||||
|
||||
g_autoptr(GHashTable) repo_refs = NULL; /* (element-type OstreeCollectionRef utf8) */
|
||||
|
||||
if (!ostree_repo_list_collection_refs (repo, refs[i]->collection_id, &repo_refs, cancellable, &local_error))
|
||||
if (!ostree_repo_list_collection_refs (repo, refs[i]->collection_id, &repo_refs,
|
||||
OSTREE_REPO_LIST_REFS_EXT_EXCLUDE_REMOTES,
|
||||
cancellable, &local_error))
|
||||
{
|
||||
g_debug ("Ignoring ref (%s, %s) on mount ‘%s’ as its refs could not be listed: %s",
|
||||
refs[i]->collection_id, refs[i]->ref_name, mount_name, local_error->message);
|
||||
|
||||
@@ -123,24 +123,30 @@ squash_trailing_slashes (char *path)
|
||||
*endp = '\0';
|
||||
}
|
||||
|
||||
/* Like archive_entry_stat(), but since some archives only store the permission
|
||||
* mode bits in hardlink entries, so let's just make it into a regular file.
|
||||
* Yes, this hack will work even if it's a hardlink to a symlink.
|
||||
*/
|
||||
static void
|
||||
read_archive_entry_stat (struct archive_entry *entry,
|
||||
struct stat *stbuf)
|
||||
{
|
||||
const struct stat *st = archive_entry_stat (entry);
|
||||
|
||||
*stbuf = *st;
|
||||
if (archive_entry_hardlink (entry))
|
||||
stbuf->st_mode |= S_IFREG;
|
||||
}
|
||||
|
||||
/* Create a GFileInfo from archive_entry_stat() */
|
||||
static GFileInfo *
|
||||
file_info_from_archive_entry (struct archive_entry *entry)
|
||||
{
|
||||
const struct stat *st = archive_entry_stat (entry);
|
||||
struct stat st_copy;
|
||||
struct stat stbuf;
|
||||
read_archive_entry_stat (entry, &stbuf);
|
||||
|
||||
/* Some archives only store the permission mode bits in hardlink entries, so
|
||||
* let's just make it into a regular file. Yes, this hack will work even if
|
||||
* it's a hardlink to a symlink. */
|
||||
if (archive_entry_hardlink (entry))
|
||||
{
|
||||
st_copy = *st;
|
||||
st_copy.st_mode |= S_IFREG;
|
||||
st = &st_copy;
|
||||
}
|
||||
|
||||
g_autoptr(GFileInfo) info = _ostree_stbuf_to_gfileinfo (st);
|
||||
if (S_ISLNK (st->st_mode))
|
||||
g_autoptr(GFileInfo) info = _ostree_stbuf_to_gfileinfo (&stbuf);
|
||||
if (S_ISLNK (stbuf.st_mode))
|
||||
g_file_info_set_attribute_byte_string (info, "standard::symlink-target",
|
||||
archive_entry_symlink (entry));
|
||||
|
||||
@@ -247,7 +253,18 @@ aic_get_final_path (OstreeRepoArchiveImportContext *ctx,
|
||||
const char *path,
|
||||
GError **error)
|
||||
{
|
||||
if (ctx->opts->use_ostree_convention)
|
||||
if (ctx->opts->translate_pathname)
|
||||
{
|
||||
struct stat stbuf;
|
||||
path = path_relative (path, error);
|
||||
read_archive_entry_stat (ctx->entry, &stbuf);
|
||||
char *ret = ctx->opts->translate_pathname (ctx->repo, &stbuf, path,
|
||||
ctx->opts->translate_pathname_user_data);
|
||||
if (ret)
|
||||
return ret;
|
||||
/* Fall through */
|
||||
}
|
||||
else if (ctx->opts->use_ostree_convention)
|
||||
return path_relative_ostree (path, error);
|
||||
return g_strdup (path_relative (path, error));
|
||||
}
|
||||
@@ -258,7 +275,6 @@ aic_get_final_entry_pathname (OstreeRepoArchiveImportContext *ctx,
|
||||
{
|
||||
const char *pathname = archive_entry_pathname (ctx->entry);
|
||||
g_autofree char *final = aic_get_final_path (ctx, pathname, error);
|
||||
|
||||
if (final == NULL)
|
||||
return NULL;
|
||||
|
||||
@@ -642,17 +658,17 @@ aic_import_entry (OstreeRepoArchiveImportContext *ctx,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
g_autoptr(GFileInfo) fi = NULL;
|
||||
g_autoptr(OstreeMutableTree) parent = NULL;
|
||||
g_autofree char *path = aic_get_final_entry_pathname (ctx, error);
|
||||
|
||||
if (path == NULL)
|
||||
return FALSE;
|
||||
|
||||
g_autoptr(GFileInfo) fi = NULL;
|
||||
if (aic_apply_modifier_filter (ctx, path, &fi)
|
||||
== OSTREE_REPO_COMMIT_FILTER_SKIP)
|
||||
return TRUE;
|
||||
|
||||
g_autoptr(OstreeMutableTree) parent = NULL;
|
||||
if (!aic_get_parent_dir (ctx, path, &parent, cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
@@ -861,7 +877,11 @@ ostree_repo_import_archive_to_mtree (OstreeRepo *self,
|
||||
g_file_info_set_attribute_uint32 (fi, "unix::gid", 0);
|
||||
g_file_info_set_attribute_uint32 (fi, "unix::mode", DEFAULT_DIRMODE);
|
||||
|
||||
if (!aic_ensure_parent_dir_with_file_info (&aictx, mtree, "/", fi, NULL,
|
||||
g_autoptr(GFileInfo) mfi = NULL;
|
||||
(void)_ostree_repo_commit_modifier_apply (self, modifier, "/",
|
||||
fi, &mfi);
|
||||
|
||||
if (!aic_ensure_parent_dir_with_file_info (&aictx, mtree, "/", mfi, NULL,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
}
|
||||
@@ -903,18 +923,9 @@ ostree_repo_write_archive_to_mtree (OstreeRepo *self,
|
||||
g_autoptr(OtAutoArchiveRead) a = archive_read_new ();
|
||||
OstreeRepoImportArchiveOptions opts = { 0, };
|
||||
|
||||
#ifdef HAVE_ARCHIVE_READ_SUPPORT_FILTER_ALL
|
||||
archive_read_support_filter_all (a);
|
||||
#else
|
||||
archive_read_support_compression_all (a);
|
||||
#endif
|
||||
archive_read_support_format_all (a);
|
||||
if (archive_read_open_filename (a, gs_file_get_path_cached (archive), 8192) != ARCHIVE_OK)
|
||||
{
|
||||
propagate_libarchive_error (error, a);
|
||||
goto out;
|
||||
}
|
||||
|
||||
a = ot_open_archive_read (gs_file_get_path_cached (archive), error);
|
||||
if (!a)
|
||||
goto out;
|
||||
opts.autocreate_parents = !!autocreate_parents;
|
||||
|
||||
if (!ostree_repo_import_archive_to_mtree (self, &opts, a, mtree, modifier, cancellable, error))
|
||||
|
||||
@@ -154,10 +154,32 @@ struct OstreeRepo {
|
||||
gboolean generate_sizes;
|
||||
guint64 tmp_expiry_seconds;
|
||||
gchar *collection_id;
|
||||
gboolean add_remotes_config_dir; /* Add new remotes in remotes.d dir */
|
||||
|
||||
OstreeRepo *parent_repo;
|
||||
};
|
||||
|
||||
/* Taken from flatpak; may be made into public API later */
|
||||
typedef OstreeRepo _OstreeRepoAutoTransaction;
|
||||
static inline void
|
||||
_ostree_repo_auto_transaction_cleanup (void *p)
|
||||
{
|
||||
OstreeRepo *repo = p;
|
||||
if (repo)
|
||||
(void) ostree_repo_abort_transaction (repo, NULL, NULL);
|
||||
}
|
||||
|
||||
static inline _OstreeRepoAutoTransaction *
|
||||
_ostree_repo_auto_transaction_start (OstreeRepo *repo,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
if (!ostree_repo_prepare_transaction (repo, NULL, cancellable, error))
|
||||
return NULL;
|
||||
return (_OstreeRepoAutoTransaction *)repo;
|
||||
}
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC (_OstreeRepoAutoTransaction, _ostree_repo_auto_transaction_cleanup)
|
||||
|
||||
typedef struct {
|
||||
dev_t dev;
|
||||
ino_t ino;
|
||||
@@ -396,11 +418,12 @@ gboolean ostree_repo_set_collection_id (OstreeRepo *self,
|
||||
const gchar *collection_id,
|
||||
GError **error);
|
||||
|
||||
gboolean ostree_repo_list_collection_refs (OstreeRepo *self,
|
||||
const char *match_collection_id,
|
||||
GHashTable **out_all_refs,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
gboolean ostree_repo_list_collection_refs (OstreeRepo *self,
|
||||
const char *match_collection_id,
|
||||
GHashTable **out_all_refs,
|
||||
OstreeRepoListRefsExtFlags flags,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
void ostree_repo_transaction_set_collection_ref (OstreeRepo *self,
|
||||
const OstreeCollectionRef *ref,
|
||||
|
||||
@@ -43,13 +43,7 @@ prune_commitpartial_file (OstreeRepo *repo,
|
||||
GError **error)
|
||||
{
|
||||
g_autofree char *path = _ostree_get_commitpartial_path (checksum);
|
||||
if (unlinkat (repo->repo_dir_fd, path, 0) != 0)
|
||||
{
|
||||
if (errno != ENOENT)
|
||||
return glnx_throw_errno_prefix (error, "unlinkat");
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
return ot_ensure_unlinked_at (repo->repo_dir_fd, path, error);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
@@ -147,8 +141,8 @@ _ostree_repo_prune_tmp (OstreeRepo *self,
|
||||
if (has_sig_suffix)
|
||||
dent->d_name[len - 4] = '.';
|
||||
|
||||
if (unlinkat (dfd_iter.fd, dent->d_name, 0) < 0)
|
||||
return glnx_throw_errno_prefix (error, "unlinkat");
|
||||
if (!glnx_unlinkat (dfd_iter.fd, dent->d_name, 0, error))
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -335,7 +329,7 @@ ostree_repo_prune (OstreeRepo *self,
|
||||
g_autoptr(GHashTable) all_collection_refs = NULL; /* (element-type OstreeChecksumRef utf8) */
|
||||
|
||||
if (!ostree_repo_list_collection_refs (self, NULL, &all_collection_refs,
|
||||
cancellable, error))
|
||||
OSTREE_REPO_LIST_REFS_EXT_EXCLUDE_REMOTES, cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
GLNX_HASH_TABLE_FOREACH_V (all_collection_refs, const char*, checksum)
|
||||
|
||||
@@ -104,6 +104,7 @@ typedef struct {
|
||||
GBytes *summary_data_sig;
|
||||
GVariant *summary;
|
||||
GHashTable *summary_deltas_checksums;
|
||||
GHashTable *ref_original_commits; /* Maps checksum to commit, used by timestamp checks */
|
||||
GPtrArray *static_delta_superblocks;
|
||||
GHashTable *expected_commit_sizes; /* Maps commit checksum to known size */
|
||||
GHashTable *commit_to_depth; /* Maps commit checksum maximum depth */
|
||||
@@ -136,6 +137,7 @@ typedef struct {
|
||||
guint n_fetched_localcache_metadata;
|
||||
guint n_fetched_localcache_content;
|
||||
|
||||
gboolean timestamp_check; /* Verify commit timestamps */
|
||||
int maxdepth;
|
||||
guint64 start_time;
|
||||
|
||||
@@ -1355,11 +1357,8 @@ static_deltapart_fetch_on_complete (GObject *object,
|
||||
goto out;
|
||||
|
||||
/* From here on, if we fail to apply the delta, we'll re-fetch it */
|
||||
if (unlinkat (_ostree_fetcher_get_dfd (fetcher), temp_path, 0) < 0)
|
||||
{
|
||||
glnx_set_error_from_errno (error);
|
||||
goto out;
|
||||
}
|
||||
if (!glnx_unlinkat (_ostree_fetcher_get_dfd (fetcher), temp_path, 0, error))
|
||||
goto out;
|
||||
|
||||
in = g_unix_input_stream_new (fd, FALSE);
|
||||
|
||||
@@ -1589,6 +1588,9 @@ verify_bindings (OtPullData *pull_data,
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* Look at a commit object, and determine whether there are
|
||||
* more things to fetch.
|
||||
*/
|
||||
static gboolean
|
||||
scan_commit_object (OtPullData *pull_data,
|
||||
const char *checksum,
|
||||
@@ -1597,19 +1599,8 @@ scan_commit_object (OtPullData *pull_data,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
gboolean ret = FALSE;
|
||||
/* If we found a legacy transaction flag, assume we have to scan.
|
||||
* We always do a scan of dirtree objects; see
|
||||
* https://github.com/ostreedev/ostree/issues/543
|
||||
*/
|
||||
OstreeRepoCommitState commitstate;
|
||||
g_autoptr(GVariant) commit = NULL;
|
||||
g_autoptr(GVariant) parent_csum = NULL;
|
||||
const guchar *parent_csum_bytes = NULL;
|
||||
gpointer depthp;
|
||||
gint depth;
|
||||
gboolean is_partial;
|
||||
|
||||
if (g_hash_table_lookup_extended (pull_data->commit_to_depth, checksum,
|
||||
NULL, &depthp))
|
||||
{
|
||||
@@ -1632,31 +1623,60 @@ scan_commit_object (OtPullData *pull_data,
|
||||
cancellable,
|
||||
error);
|
||||
if (!process_verify_result (pull_data, checksum, result, error))
|
||||
goto out;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* If we found a legacy transaction flag, assume we have to scan.
|
||||
* We always do a scan of dirtree objects; see
|
||||
* https://github.com/ostreedev/ostree/issues/543
|
||||
*/
|
||||
OstreeRepoCommitState commitstate;
|
||||
g_autoptr(GVariant) commit = NULL;
|
||||
if (!ostree_repo_load_commit (pull_data->repo, checksum, &commit, &commitstate, error))
|
||||
goto out;
|
||||
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.
|
||||
*/
|
||||
if (!verify_bindings (pull_data, commit, ref, error))
|
||||
return glnx_prefix_error (error, "Commit %s", checksum);
|
||||
|
||||
if (pull_data->timestamp_check)
|
||||
{
|
||||
g_prefix_error (error, "Commit %s: ", checksum);
|
||||
goto out;
|
||||
/* We don't support timestamp checking while recursing right now */
|
||||
g_assert (ref);
|
||||
g_assert_cmpint (recursion_depth, ==, 0);
|
||||
const char *orig_rev = NULL;
|
||||
if (!g_hash_table_lookup_extended (pull_data->ref_original_commits,
|
||||
ref, NULL, (void**)&orig_rev))
|
||||
g_assert_not_reached ();
|
||||
|
||||
g_autoptr(GVariant) orig_commit = NULL;
|
||||
if (orig_rev)
|
||||
{
|
||||
if (!ostree_repo_load_commit (pull_data->repo, orig_rev,
|
||||
&orig_commit, NULL, error))
|
||||
return glnx_prefix_error (error, "Reading %s for timestamp-check", ref->ref_name);
|
||||
|
||||
guint64 orig_ts = ostree_commit_get_timestamp (orig_commit);
|
||||
guint64 new_ts = ostree_commit_get_timestamp (commit);
|
||||
if (!_ostree_compare_timestamps (orig_rev, orig_ts, checksum, new_ts, error))
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/* If we found a legacy transaction flag, assume all commits are partial */
|
||||
is_partial = commitstate_is_partial (pull_data, commitstate);
|
||||
gboolean is_partial = commitstate_is_partial (pull_data, commitstate);
|
||||
|
||||
/* PARSE OSTREE_SERIALIZED_COMMIT_VARIANT */
|
||||
g_autoptr(GVariant) parent_csum = NULL;
|
||||
const guchar *parent_csum_bytes = NULL;
|
||||
g_variant_get_child (commit, 1, "@ay", &parent_csum);
|
||||
if (g_variant_n_children (parent_csum) > 0)
|
||||
{
|
||||
parent_csum_bytes = ostree_checksum_bytes_peek_validate (parent_csum, error);
|
||||
if (parent_csum_bytes == NULL)
|
||||
goto out;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (parent_csum_bytes != NULL && pull_data->maxdepth == -1)
|
||||
@@ -1672,7 +1692,7 @@ scan_commit_object (OtPullData *pull_data,
|
||||
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))
|
||||
{
|
||||
@@ -1711,11 +1731,11 @@ scan_commit_object (OtPullData *pull_data,
|
||||
|
||||
tree_contents_csum_bytes = ostree_checksum_bytes_peek_validate (tree_contents_csum, error);
|
||||
if (tree_contents_csum_bytes == NULL)
|
||||
goto out;
|
||||
return FALSE;
|
||||
|
||||
tree_meta_csum_bytes = ostree_checksum_bytes_peek_validate (tree_meta_csum, error);
|
||||
if (tree_meta_csum_bytes == NULL)
|
||||
goto out;
|
||||
return FALSE;
|
||||
|
||||
queue_scan_one_metadata_object_c (pull_data, tree_contents_csum_bytes,
|
||||
OSTREE_OBJECT_TYPE_DIR_TREE, "/", recursion_depth + 1, NULL);
|
||||
@@ -1724,9 +1744,7 @@ scan_commit_object (OtPullData *pull_data,
|
||||
OSTREE_OBJECT_TYPE_DIR_META, NULL, recursion_depth + 1, NULL);
|
||||
}
|
||||
|
||||
ret = TRUE;
|
||||
out:
|
||||
return ret;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -2506,22 +2524,11 @@ static gboolean
|
||||
validate_variant_is_csum (GVariant *csum,
|
||||
GError **error)
|
||||
{
|
||||
gboolean ret = FALSE;
|
||||
|
||||
if (!g_variant_is_of_type (csum, G_VARIANT_TYPE ("ay")))
|
||||
{
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||
"Invalid checksum variant of type '%s', expected 'ay'",
|
||||
g_variant_get_type_string (csum));
|
||||
goto out;
|
||||
}
|
||||
return glnx_throw (error, "Invalid checksum variant of type '%s', expected 'ay'",
|
||||
g_variant_get_type_string (csum));
|
||||
|
||||
if (!ostree_validate_structureof_csum_v (csum, error))
|
||||
goto out;
|
||||
|
||||
ret = TRUE;
|
||||
out:
|
||||
return ret;
|
||||
return ostree_validate_structureof_csum_v (csum, error);
|
||||
}
|
||||
|
||||
/* Load the summary from the cache if the provided .sig file is the same as the
|
||||
@@ -2534,27 +2541,19 @@ _ostree_repo_load_cache_summary_if_same_sig (OstreeRepo *self,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
gboolean ret = FALSE;
|
||||
const char *summary_cache_sig_file = glnx_strjoina (_OSTREE_SUMMARY_CACHE_DIR, "/", remote, ".sig");
|
||||
|
||||
glnx_fd_close int prev_fd = -1;
|
||||
g_autoptr(GBytes) old_sig_contents = NULL;
|
||||
|
||||
if (self->cache_dir_fd == -1)
|
||||
return TRUE;
|
||||
|
||||
const char *summary_cache_sig_file = glnx_strjoina (_OSTREE_SUMMARY_CACHE_DIR, "/", remote, ".sig");
|
||||
glnx_fd_close int prev_fd = -1;
|
||||
if (!ot_openat_ignore_enoent (self->cache_dir_fd, summary_cache_sig_file, &prev_fd, error))
|
||||
goto out;
|
||||
|
||||
return FALSE;
|
||||
if (prev_fd < 0)
|
||||
{
|
||||
ret = TRUE;
|
||||
goto out;
|
||||
}
|
||||
return TRUE; /* Note early return */
|
||||
|
||||
old_sig_contents = glnx_fd_readall_bytes (prev_fd, cancellable, error);
|
||||
g_autoptr(GBytes) old_sig_contents = glnx_fd_readall_bytes (prev_fd, cancellable, error);
|
||||
if (!old_sig_contents)
|
||||
goto out;
|
||||
return FALSE;
|
||||
|
||||
if (g_bytes_compare (old_sig_contents, summary_sig) == 0)
|
||||
{
|
||||
@@ -2569,25 +2568,21 @@ _ostree_repo_load_cache_summary_if_same_sig (OstreeRepo *self,
|
||||
if (errno == ENOENT)
|
||||
{
|
||||
(void) unlinkat (self->cache_dir_fd, summary_cache_sig_file, 0);
|
||||
ret = TRUE;
|
||||
goto out;
|
||||
return TRUE; /* Note early return */
|
||||
}
|
||||
|
||||
glnx_set_error_from_errno (error);
|
||||
goto out;
|
||||
return glnx_throw_errno_prefix (error, "openat(%s)", summary_cache_file);
|
||||
}
|
||||
|
||||
summary_data = glnx_fd_readall_bytes (summary_fd, cancellable, error);
|
||||
if (!summary_data)
|
||||
goto out;
|
||||
return FALSE;
|
||||
*summary = summary_data;
|
||||
}
|
||||
ret = TRUE;
|
||||
|
||||
out:
|
||||
return ret;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* Replace the current summary+signature with new versions */
|
||||
static gboolean
|
||||
_ostree_repo_cache_summary (OstreeRepo *self,
|
||||
const char *remote,
|
||||
@@ -2596,36 +2591,31 @@ _ostree_repo_cache_summary (OstreeRepo *self,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
gboolean ret = FALSE;
|
||||
const char *summary_cache_file = glnx_strjoina (_OSTREE_SUMMARY_CACHE_DIR, "/", remote);
|
||||
const char *summary_cache_sig_file = glnx_strjoina (_OSTREE_SUMMARY_CACHE_DIR, "/", remote, ".sig");
|
||||
|
||||
if (self->cache_dir_fd == -1)
|
||||
return TRUE;
|
||||
|
||||
if (!glnx_shutil_mkdir_p_at (self->cache_dir_fd, _OSTREE_SUMMARY_CACHE_DIR, 0775, cancellable, error))
|
||||
goto out;
|
||||
return FALSE;
|
||||
|
||||
const char *summary_cache_file = glnx_strjoina (_OSTREE_SUMMARY_CACHE_DIR, "/", remote);
|
||||
if (!glnx_file_replace_contents_at (self->cache_dir_fd,
|
||||
summary_cache_file,
|
||||
g_bytes_get_data (summary, NULL),
|
||||
g_bytes_get_size (summary),
|
||||
self->disable_fsync ? GLNX_FILE_REPLACE_NODATASYNC : GLNX_FILE_REPLACE_DATASYNC_NEW,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
return FALSE;
|
||||
|
||||
const char *summary_cache_sig_file = glnx_strjoina (_OSTREE_SUMMARY_CACHE_DIR, "/", remote, ".sig");
|
||||
if (!glnx_file_replace_contents_at (self->cache_dir_fd,
|
||||
summary_cache_sig_file,
|
||||
g_bytes_get_data (summary_sig, NULL),
|
||||
g_bytes_get_size (summary_sig),
|
||||
self->disable_fsync ? GLNX_FILE_REPLACE_NODATASYNC : GLNX_FILE_REPLACE_DATASYNC_NEW,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
|
||||
ret = TRUE;
|
||||
out:
|
||||
return ret;
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static OstreeFetcher *
|
||||
@@ -2717,18 +2707,19 @@ _ostree_repo_remote_new_fetcher (OstreeRepo *self,
|
||||
_ostree_fetcher_set_proxy (fetcher, http_proxy);
|
||||
}
|
||||
|
||||
{
|
||||
g_autofree char *cookie_file = g_strdup_printf ("%s.cookies.txt", remote_name);
|
||||
/* TODO; port away from this; a bit hard since both libsoup and libcurl
|
||||
* expect a file. Doing ot_fdrel_to_gfile() works for now though.
|
||||
*/
|
||||
GFile*repo_path = ostree_repo_get_path (self);
|
||||
g_autofree char *jar_path =
|
||||
g_build_filename (gs_file_get_path_cached (repo_path), cookie_file, NULL);
|
||||
if (!_ostree_repo_remote_name_is_file (remote_name))
|
||||
{
|
||||
g_autofree char *cookie_file = g_strdup_printf ("%s.cookies.txt", remote_name);
|
||||
/* TODO; port away from this; a bit hard since both libsoup and libcurl
|
||||
* expect a file. Doing ot_fdrel_to_gfile() works for now though.
|
||||
*/
|
||||
GFile*repo_path = ostree_repo_get_path (self);
|
||||
g_autofree char *jar_path =
|
||||
g_build_filename (gs_file_get_path_cached (repo_path), cookie_file, NULL);
|
||||
|
||||
if (g_file_test (jar_path, G_FILE_TEST_IS_REGULAR))
|
||||
_ostree_fetcher_set_cookie_jar (fetcher, jar_path);
|
||||
}
|
||||
if (g_file_test (jar_path, G_FILE_TEST_IS_REGULAR))
|
||||
_ostree_fetcher_set_cookie_jar (fetcher, jar_path);
|
||||
}
|
||||
|
||||
success = TRUE;
|
||||
|
||||
@@ -3199,6 +3190,7 @@ initiate_request (OtPullData *pull_data,
|
||||
* * disable-static-deltas (b): Do not use static deltas
|
||||
* * 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
|
||||
* * 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.
|
||||
@@ -3275,10 +3267,12 @@ ostree_repo_pull_with_options (OstreeRepo *self,
|
||||
(void) g_variant_lookup (options, "http-headers", "@a(ss)", &pull_data->extra_headers);
|
||||
(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);
|
||||
}
|
||||
|
||||
g_return_val_if_fail (OSTREE_IS_REPO (self), FALSE);
|
||||
g_return_val_if_fail (pull_data->maxdepth >= -1, FALSE);
|
||||
g_return_val_if_fail (!pull_data->timestamp_check || pull_data->maxdepth == 0, FALSE);
|
||||
g_return_val_if_fail (!opt_collection_refs_set ||
|
||||
(refs_to_fetch == NULL && override_commit_ids == NULL), FALSE);
|
||||
if (refs_to_fetch && override_commit_ids)
|
||||
@@ -3322,6 +3316,9 @@ ostree_repo_pull_with_options (OstreeRepo *self,
|
||||
pull_data->summary_deltas_checksums = g_hash_table_new_full (g_str_hash, g_str_equal,
|
||||
(GDestroyNotify)g_free,
|
||||
(GDestroyNotify)g_free);
|
||||
pull_data->ref_original_commits = g_hash_table_new_full (ostree_collection_ref_hash, ostree_collection_ref_equal,
|
||||
(GDestroyNotify)NULL,
|
||||
(GDestroyNotify)g_variant_unref);
|
||||
pull_data->scanned_metadata = g_hash_table_new_full (ostree_hash_object_name, g_variant_equal,
|
||||
(GDestroyNotify)g_variant_unref, NULL);
|
||||
pull_data->fetched_detached_metadata = g_hash_table_new_full (g_str_hash, g_str_equal,
|
||||
@@ -3596,7 +3593,7 @@ ostree_repo_pull_with_options (OstreeRepo *self,
|
||||
|
||||
}
|
||||
|
||||
/* We can't use static deltas if pulling into an archive-z2 repo. */
|
||||
/* We can't use static deltas if pulling into an archive repo. */
|
||||
if (self->mode == OSTREE_REPO_MODE_ARCHIVE_Z2)
|
||||
{
|
||||
if (pull_data->require_static_deltas)
|
||||
@@ -3923,6 +3920,24 @@ ostree_repo_pull_with_options (OstreeRepo *self,
|
||||
ref_with_collection = ostree_collection_ref_dup (ref);
|
||||
}
|
||||
|
||||
/* If we have timestamp checking enabled, find the current value of
|
||||
* the ref, and store its timestamp in the hash map, to check later.
|
||||
*/
|
||||
if (pull_data->timestamp_check)
|
||||
{
|
||||
g_autofree char *from_rev = NULL;
|
||||
if (!ostree_repo_resolve_rev (pull_data->repo, ref_with_collection->ref_name, TRUE,
|
||||
&from_rev, error))
|
||||
goto out;
|
||||
/* Explicitly store NULL if there's no previous revision. We do
|
||||
* this so we can assert() if we somehow didn't find a ref in the
|
||||
* hash at all. Note we don't copy the collection-ref, so the
|
||||
* lifetime of this hash must be equal to `requested_refs_to_fetch`.
|
||||
*/
|
||||
g_hash_table_insert (pull_data->ref_original_commits, ref_with_collection,
|
||||
g_steal_pointer (&from_rev));
|
||||
}
|
||||
|
||||
g_hash_table_replace (updated_requested_refs_to_fetch,
|
||||
g_steal_pointer (&ref_with_collection),
|
||||
g_steal_pointer (&contents));
|
||||
@@ -4223,6 +4238,7 @@ ostree_repo_pull_with_options (OstreeRepo *self,
|
||||
g_clear_pointer (&pull_data->scanned_metadata, (GDestroyNotify) g_hash_table_unref);
|
||||
g_clear_pointer (&pull_data->fetched_detached_metadata, (GDestroyNotify) g_hash_table_unref);
|
||||
g_clear_pointer (&pull_data->summary_deltas_checksums, (GDestroyNotify) g_hash_table_unref);
|
||||
g_clear_pointer (&pull_data->ref_original_commits, (GDestroyNotify) g_hash_table_unref);
|
||||
g_clear_pointer (&pull_data->requested_content, (GDestroyNotify) g_hash_table_unref);
|
||||
g_clear_pointer (&pull_data->requested_fallback_content, (GDestroyNotify) g_hash_table_unref);
|
||||
g_clear_pointer (&pull_data->requested_metadata, (GDestroyNotify) g_hash_table_unref);
|
||||
|
||||
@@ -560,7 +560,7 @@ _ostree_repo_list_refs_internal (OstreeRepo *self,
|
||||
if (!ostree_parse_refspec (refspec_prefix, &remote, &ref_prefix, error))
|
||||
return FALSE;
|
||||
|
||||
if (remote)
|
||||
if (!(flags & OSTREE_REPO_LIST_REFS_EXT_EXCLUDE_REMOTES) && remote)
|
||||
{
|
||||
prefix_path = glnx_strjoina ("refs/remotes/", remote, "/");
|
||||
path = glnx_strjoina (prefix_path, ref_prefix);
|
||||
@@ -620,32 +620,35 @@ _ostree_repo_list_refs_internal (OstreeRepo *self,
|
||||
ret_all_refs, cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
g_string_truncate (base_path, 0);
|
||||
|
||||
if (!glnx_dirfd_iterator_init_at (self->repo_dir_fd, "refs/remotes", TRUE, &dfd_iter, error))
|
||||
return FALSE;
|
||||
|
||||
while (TRUE)
|
||||
if (!(flags & OSTREE_REPO_LIST_REFS_EXT_EXCLUDE_REMOTES))
|
||||
{
|
||||
struct dirent *dent;
|
||||
glnx_fd_close int remote_dfd = -1;
|
||||
g_string_truncate (base_path, 0);
|
||||
|
||||
if (!glnx_dirfd_iterator_next_dent_ensure_dtype (&dfd_iter, &dent, cancellable, error))
|
||||
return FALSE;
|
||||
if (!dent)
|
||||
break;
|
||||
|
||||
if (dent->d_type != DT_DIR)
|
||||
continue;
|
||||
|
||||
if (!glnx_opendirat (dfd_iter.fd, dent->d_name, TRUE, &remote_dfd, error))
|
||||
if (!glnx_dirfd_iterator_init_at (self->repo_dir_fd, "refs/remotes", TRUE, &dfd_iter, error))
|
||||
return FALSE;
|
||||
|
||||
if (!enumerate_refs_recurse (self, dent->d_name, flags, NULL, remote_dfd, base_path,
|
||||
remote_dfd, ".",
|
||||
ret_all_refs,
|
||||
cancellable, error))
|
||||
return FALSE;
|
||||
while (TRUE)
|
||||
{
|
||||
struct dirent *dent;
|
||||
glnx_fd_close int remote_dfd = -1;
|
||||
|
||||
if (!glnx_dirfd_iterator_next_dent_ensure_dtype (&dfd_iter, &dent, cancellable, error))
|
||||
return FALSE;
|
||||
if (!dent)
|
||||
break;
|
||||
|
||||
if (dent->d_type != DT_DIR)
|
||||
continue;
|
||||
|
||||
if (!glnx_opendirat (dfd_iter.fd, dent->d_name, TRUE, &remote_dfd, error))
|
||||
return FALSE;
|
||||
|
||||
if (!enumerate_refs_recurse (self, dent->d_name, flags, NULL, remote_dfd, base_path,
|
||||
remote_dfd, ".",
|
||||
ret_all_refs,
|
||||
cancellable, error))
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1004,11 +1007,8 @@ _ostree_repo_write_ref (OstreeRepo *self,
|
||||
{
|
||||
if (dfd >= 0)
|
||||
{
|
||||
if (unlinkat (dfd, ref->ref_name, 0) != 0)
|
||||
{
|
||||
if (errno != ENOENT)
|
||||
return glnx_throw_errno (error);
|
||||
}
|
||||
if (!ot_ensure_unlinked_at (dfd, ref->ref_name, error))
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
else if (rev != NULL)
|
||||
@@ -1101,27 +1101,33 @@ _ostree_repo_update_collection_refs (OstreeRepo *self,
|
||||
* @self: Repo
|
||||
* @match_collection_id: (nullable): If non-%NULL, only list refs from this collection
|
||||
* @out_all_refs: (out) (element-type OstreeCollectionRef utf8): Mapping from collection–ref to checksum
|
||||
* @flags: Options controlling listing behavior
|
||||
* @cancellable: Cancellable
|
||||
* @error: Error
|
||||
*
|
||||
* List all local and mirrored refs, mapping them to the commit checksums they
|
||||
* currently point to in @out_all_refs. If @match_collection_id is specified,
|
||||
* the results will be limited to those with an equal collection ID.
|
||||
* List all local, mirrored, and remote refs, mapping them to the commit
|
||||
* checksums they currently point to in @out_all_refs. If @match_collection_id
|
||||
* is specified, the results will be limited to those with an equal collection
|
||||
* ID.
|
||||
*
|
||||
* #OstreeCollectionRefs are guaranteed to be returned with their collection ID
|
||||
* set to a non-%NULL value; so no refs from `refs/heads` will be listed if no
|
||||
* collection ID is configured for the repository
|
||||
* (ostree_repo_get_collection_id()).
|
||||
*
|
||||
* If you want to exclude refs from `refs/remotes`, use
|
||||
* %OSTREE_REPO_LIST_REFS_EXT_EXCLUDE_REMOTES in @flags.
|
||||
*
|
||||
* Returns: %TRUE on success, %FALSE otherwise
|
||||
* Since: 2017.8
|
||||
*/
|
||||
gboolean
|
||||
ostree_repo_list_collection_refs (OstreeRepo *self,
|
||||
const char *match_collection_id,
|
||||
GHashTable **out_all_refs,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
ostree_repo_list_collection_refs (OstreeRepo *self,
|
||||
const char *match_collection_id,
|
||||
GHashTable **out_all_refs,
|
||||
OstreeRepoListRefsExtFlags flags,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
g_return_val_if_fail (OSTREE_IS_REPO (self), FALSE);
|
||||
g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE);
|
||||
@@ -1130,6 +1136,10 @@ ostree_repo_list_collection_refs (OstreeRepo *self,
|
||||
if (match_collection_id != NULL && !ostree_validate_collection_id (match_collection_id, error))
|
||||
return FALSE;
|
||||
|
||||
const gchar *refs_dirs[] = { "refs/mirrors", "refs/remotes", NULL };
|
||||
if (flags & OSTREE_REPO_LIST_REFS_EXT_EXCLUDE_REMOTES)
|
||||
refs_dirs[1] = NULL;
|
||||
|
||||
g_autoptr(GHashTable) ret_all_refs = NULL;
|
||||
|
||||
ret_all_refs = g_hash_table_new_full (ostree_collection_ref_hash,
|
||||
@@ -1150,7 +1160,7 @@ ostree_repo_list_collection_refs (OstreeRepo *self,
|
||||
if (!glnx_opendirat (self->repo_dir_fd, "refs/heads", TRUE, &refs_heads_dfd, error))
|
||||
return FALSE;
|
||||
|
||||
if (!enumerate_refs_recurse (self, NULL, OSTREE_REPO_LIST_REFS_EXT_NONE,
|
||||
if (!enumerate_refs_recurse (self, NULL, flags,
|
||||
main_collection_id, refs_heads_dfd, base_path,
|
||||
refs_heads_dfd, ".",
|
||||
ret_all_refs, cancellable, error))
|
||||
@@ -1159,36 +1169,65 @@ ostree_repo_list_collection_refs (OstreeRepo *self,
|
||||
|
||||
g_string_truncate (base_path, 0);
|
||||
|
||||
gboolean refs_mirrors_exists = FALSE;
|
||||
if (!ot_dfd_iter_init_allow_noent (self->repo_dir_fd, "refs/mirrors",
|
||||
&dfd_iter, &refs_mirrors_exists, error))
|
||||
return FALSE;
|
||||
|
||||
while (refs_mirrors_exists)
|
||||
for (const char **iter = refs_dirs; iter && *iter; iter++)
|
||||
{
|
||||
struct dirent *dent;
|
||||
glnx_fd_close int collection_dfd = -1;
|
||||
|
||||
if (!glnx_dirfd_iterator_next_dent_ensure_dtype (&dfd_iter, &dent, cancellable, error))
|
||||
return FALSE;
|
||||
if (!dent)
|
||||
break;
|
||||
|
||||
if (dent->d_type != DT_DIR)
|
||||
continue;
|
||||
|
||||
if (match_collection_id != NULL && g_strcmp0 (match_collection_id, dent->d_name) != 0)
|
||||
continue;
|
||||
|
||||
if (!glnx_opendirat (dfd_iter.fd, dent->d_name, TRUE, &collection_dfd, error))
|
||||
const char *refs_dir = *iter;
|
||||
gboolean refs_dir_exists = FALSE;
|
||||
if (!ot_dfd_iter_init_allow_noent (self->repo_dir_fd, refs_dir,
|
||||
&dfd_iter, &refs_dir_exists, error))
|
||||
return FALSE;
|
||||
|
||||
if (!enumerate_refs_recurse (self, NULL, OSTREE_REPO_LIST_REFS_EXT_NONE,
|
||||
dent->d_name, collection_dfd, base_path,
|
||||
collection_dfd, ".",
|
||||
ret_all_refs,
|
||||
cancellable, error))
|
||||
return FALSE;
|
||||
while (refs_dir_exists)
|
||||
{
|
||||
struct dirent *dent;
|
||||
glnx_fd_close int subdir_fd = -1;
|
||||
const gchar *current_collection_id;
|
||||
g_autofree gchar *remote_collection_id = NULL;
|
||||
|
||||
if (!glnx_dirfd_iterator_next_dent_ensure_dtype (&dfd_iter, &dent, cancellable, error))
|
||||
return FALSE;
|
||||
if (!dent)
|
||||
break;
|
||||
|
||||
if (dent->d_type != DT_DIR)
|
||||
continue;
|
||||
|
||||
if (g_strcmp0 (refs_dir, "refs/mirrors") == 0)
|
||||
{
|
||||
if (match_collection_id != NULL && g_strcmp0 (match_collection_id, dent->d_name) != 0)
|
||||
continue;
|
||||
else
|
||||
current_collection_id = dent->d_name;
|
||||
}
|
||||
else /* refs_dir = "refs/remotes" */
|
||||
{
|
||||
g_autoptr(GError) local_error = NULL;
|
||||
if (!ostree_repo_get_remote_option (self, dent->d_name, "collection-id",
|
||||
NULL, &remote_collection_id, &local_error) ||
|
||||
!ostree_validate_collection_id (remote_collection_id, &local_error))
|
||||
{
|
||||
g_debug ("Ignoring remote ‘%s’ due to no valid collection ID being configured for it: %s",
|
||||
dent->d_name, local_error->message);
|
||||
g_clear_error (&local_error);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (match_collection_id != NULL && g_strcmp0 (match_collection_id, remote_collection_id) != 0)
|
||||
continue;
|
||||
else
|
||||
current_collection_id = remote_collection_id;
|
||||
}
|
||||
|
||||
if (!glnx_opendirat (dfd_iter.fd, dent->d_name, TRUE, &subdir_fd, error))
|
||||
return FALSE;
|
||||
|
||||
if (!enumerate_refs_recurse (self, NULL, flags,
|
||||
current_collection_id, subdir_fd, base_path,
|
||||
subdir_fd, ".",
|
||||
ret_all_refs,
|
||||
cancellable, error))
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
ot_transfer_out_value (out_all_refs, &ret_all_refs);
|
||||
|
||||
@@ -608,7 +608,7 @@ dispatch_open_splice_and_close (OstreeRepo *repo,
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Slower path, for symlinks and unpacking deltas into archive-z2 */
|
||||
/* Slower path, for symlinks and unpacking deltas into archive */
|
||||
g_autoptr(GFileInfo) finfo =
|
||||
_ostree_mode_uidgid_to_gfileinfo (state->mode, state->uid, state->gid);
|
||||
|
||||
|
||||
+186
-119
@@ -47,9 +47,36 @@
|
||||
#include <sys/file.h>
|
||||
#include <sys/statvfs.h>
|
||||
|
||||
/* ABI Size checks for ostree-repo.h, only for LP64 systems;
|
||||
* https://en.wikipedia.org/wiki/64-bit_computing#64-bit_data_models
|
||||
*/
|
||||
#if __SIZEOF_POINTER__ == 8 && __SIZEOF_LONG__ == 8 && __SIZEOF_INT__ == 4
|
||||
G_STATIC_ASSERT(sizeof(OstreeRepoTransactionStats) == sizeof(int) * 4 + 8 * 5);
|
||||
G_STATIC_ASSERT(sizeof(OstreeRepoImportArchiveOptions) == sizeof(int) * 9 + 4 + sizeof(void*) * 8);
|
||||
G_STATIC_ASSERT(sizeof(OstreeRepoExportArchiveOptions) == sizeof(int) * 9 + 4 + 8 + sizeof(void*) * 8);
|
||||
G_STATIC_ASSERT(sizeof(OstreeRepoCheckoutAtOptions) ==
|
||||
sizeof(OstreeRepoCheckoutMode) + sizeof(OstreeRepoCheckoutOverwriteMode) +
|
||||
sizeof(int)*6 +
|
||||
sizeof(int)*5 +
|
||||
sizeof(int) +
|
||||
sizeof(void*)*2 +
|
||||
sizeof(int)*6 +
|
||||
sizeof(void*)*7);
|
||||
G_STATIC_ASSERT(sizeof(OstreeRepoCommitTraverseIter) ==
|
||||
sizeof(int) + sizeof(int) +
|
||||
sizeof(void*) * 10 +
|
||||
130 + 6); /* 6 byte hole */
|
||||
G_STATIC_ASSERT(sizeof(OstreeRepoPruneOptions) ==
|
||||
sizeof(OstreeRepoPruneFlags) +
|
||||
4 +
|
||||
sizeof(void*) +
|
||||
sizeof(int) * 12 +
|
||||
sizeof(void*) * 7);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* SECTION:ostree-repo
|
||||
* @title: Content-addressed object store
|
||||
* @title: OstreeRepo: Content-addressed object store
|
||||
* @short_description: A git-like storage system for operating system binaries
|
||||
*
|
||||
* The #OstreeRepo is like git, a content-addressed object store.
|
||||
@@ -556,14 +583,32 @@ ostree_repo_class_init (OstreeRepoClass *klass)
|
||||
object_class->set_property = ostree_repo_set_property;
|
||||
object_class->finalize = ostree_repo_finalize;
|
||||
|
||||
/**
|
||||
* OstreeRepo:path:
|
||||
*
|
||||
* Path to repository. Note that if this repository was created
|
||||
* via `ostree_repo_new_at()`, this value will refer to a value in
|
||||
* the Linux kernel's `/proc/self/fd` directory. Generally, you
|
||||
* should avoid using this property at all; you can gain a reference
|
||||
* to the repository's directory fd via `ostree_repo_get_dfd()` and
|
||||
* use file-descriptor relative operations.
|
||||
*/
|
||||
g_object_class_install_property (object_class,
|
||||
PROP_PATH,
|
||||
g_param_spec_object ("path",
|
||||
"",
|
||||
"",
|
||||
g_param_spec_object ("path", "Path", "Path",
|
||||
G_TYPE_FILE,
|
||||
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
|
||||
|
||||
/**
|
||||
* OstreeRepo:sysroot-path:
|
||||
*
|
||||
* A system using libostree for the host has a "system" repository; this
|
||||
* property will be set for repositories referenced via
|
||||
* `ostree_sysroot_repo()` for example.
|
||||
*
|
||||
* You should avoid using this property; if your code is operating
|
||||
* on a system repository, use `OstreeSysroot` and access the repository
|
||||
* object via `ostree_sysroot_repo()`.
|
||||
*/
|
||||
g_object_class_install_property (object_class,
|
||||
PROP_SYSROOT_PATH,
|
||||
g_param_spec_object ("sysroot-path",
|
||||
@@ -571,7 +616,15 @@ ostree_repo_class_init (OstreeRepoClass *klass)
|
||||
"",
|
||||
G_TYPE_FILE,
|
||||
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
|
||||
|
||||
/**
|
||||
* OstreeRepo:remotes-config-dir:
|
||||
*
|
||||
* Path to directory containing remote definitions. The default is `NULL`.
|
||||
* If a `sysroot-path` property is defined, this value will default to
|
||||
* `${sysroot_path}/etc/ostree/remotes.d`.
|
||||
*
|
||||
* This value will only be used for system repositories.
|
||||
*/
|
||||
g_object_class_install_property (object_class,
|
||||
PROP_REMOTES_CONFIG_DIR,
|
||||
g_param_spec_string ("remotes-config-dir",
|
||||
@@ -880,6 +933,40 @@ ostree_repo_write_config (OstreeRepo *self,
|
||||
{
|
||||
g_return_val_if_fail (self->inited, FALSE);
|
||||
|
||||
/* Ensure that any remotes in the new config aren't defined in a
|
||||
* separate config file.
|
||||
*/
|
||||
gsize num_groups;
|
||||
g_auto(GStrv) groups = g_key_file_get_groups (new_config, &num_groups);
|
||||
for (gsize i = 0; i < num_groups; i++)
|
||||
{
|
||||
g_autoptr(OstreeRemote) new_remote = ostree_remote_new_from_keyfile (new_config, groups[i]);
|
||||
if (new_remote != NULL)
|
||||
{
|
||||
g_autoptr(GError) local_error = NULL;
|
||||
|
||||
g_autoptr(OstreeRemote) cur_remote =
|
||||
_ostree_repo_get_remote (self, new_remote->name, &local_error);
|
||||
if (cur_remote == NULL)
|
||||
{
|
||||
if (!g_error_matches (local_error, G_IO_ERROR,
|
||||
G_IO_ERROR_NOT_FOUND))
|
||||
{
|
||||
g_propagate_error (error, g_steal_pointer (&local_error));
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
else if (cur_remote->file != NULL)
|
||||
{
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_EXISTS,
|
||||
"Remote \"%s\" already defined in %s",
|
||||
new_remote->name,
|
||||
gs_file_get_path_cached (cur_remote->file));
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gsize len;
|
||||
g_autofree char *data = g_key_file_to_data (new_config, &len, error);
|
||||
if (!glnx_file_replace_contents_at (self->repo_dir_fd, "config",
|
||||
@@ -957,8 +1044,12 @@ impl_repo_remote_add (OstreeRepo *self,
|
||||
|
||||
remote = ostree_remote_new (name);
|
||||
|
||||
/* Only add repos in remotes.d if the repo option
|
||||
* add-remotes-config-dir is true. This is the default for system
|
||||
* repos.
|
||||
*/
|
||||
g_autoptr(GFile) etc_ostree_remotes_d = get_remotes_d_dir (self, sysroot);
|
||||
if (etc_ostree_remotes_d)
|
||||
if (etc_ostree_remotes_d && self->add_remotes_config_dir)
|
||||
{
|
||||
g_autoptr(GError) local_error = NULL;
|
||||
|
||||
@@ -1076,8 +1167,8 @@ impl_repo_remote_delete (OstreeRepo *self,
|
||||
|
||||
if (remote->file != NULL)
|
||||
{
|
||||
if (unlink (gs_file_get_path_cached (remote->file)) != 0)
|
||||
return glnx_throw_errno (error);
|
||||
if (!glnx_unlinkat (AT_FDCWD, gs_file_get_path_cached (remote->file), 0, error))
|
||||
return FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1412,8 +1503,7 @@ ostree_repo_remote_gpg_import (OstreeRepo *self,
|
||||
gpg_error = gpgme_op_import (source_context, data_buffer);
|
||||
if (gpg_error != GPG_ERR_NO_ERROR)
|
||||
{
|
||||
ot_gpgme_error_to_gio_error (gpg_error, error);
|
||||
g_prefix_error (error, "Unable to import keys: ");
|
||||
ot_gpgme_throw (gpg_error, error, "Unable to import keys");
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -1438,8 +1528,7 @@ ostree_repo_remote_gpg_import (OstreeRepo *self,
|
||||
gpg_error = gpgme_get_key (source_context, key_ids[ii], &key, 0);
|
||||
if (gpg_error != GPG_ERR_NO_ERROR)
|
||||
{
|
||||
ot_gpgme_error_to_gio_error (gpg_error, error);
|
||||
g_prefix_error (error, "Unable to find key \"%s\": ", key_ids[ii]);
|
||||
ot_gpgme_throw (gpg_error, error, "Unable to find key \"%s\"", key_ids[ii]);
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -1466,8 +1555,7 @@ ostree_repo_remote_gpg_import (OstreeRepo *self,
|
||||
|
||||
if (gpgme_err_code (gpg_error) != GPG_ERR_EOF)
|
||||
{
|
||||
ot_gpgme_error_to_gio_error (gpg_error, error);
|
||||
g_prefix_error (error, "Unable to list keys: ");
|
||||
ot_gpgme_throw (gpg_error, error, "Unable to list keys");
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
@@ -1539,8 +1627,7 @@ ostree_repo_remote_gpg_import (OstreeRepo *self,
|
||||
gpg_error = gpgme_data_new (&data_buffer);
|
||||
if (gpg_error != GPG_ERR_NO_ERROR)
|
||||
{
|
||||
ot_gpgme_error_to_gio_error (gpg_error, error);
|
||||
g_prefix_error (error, "Unable to create data buffer: ");
|
||||
ot_gpgme_throw (gpg_error, error, "Unable to create data buffer");
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -1549,8 +1636,7 @@ ostree_repo_remote_gpg_import (OstreeRepo *self,
|
||||
data_buffer);
|
||||
if (gpg_error != GPG_ERR_NO_ERROR)
|
||||
{
|
||||
ot_gpgme_error_to_gio_error (gpg_error, error);
|
||||
g_prefix_error (error, "Unable to export keys: ");
|
||||
ot_gpgme_throw (gpg_error, error, "Unable to export keys");
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -1559,8 +1645,7 @@ ostree_repo_remote_gpg_import (OstreeRepo *self,
|
||||
gpg_error = gpgme_op_import (target_context, data_buffer);
|
||||
if (gpg_error != GPG_ERR_NO_ERROR)
|
||||
{
|
||||
ot_gpgme_error_to_gio_error (gpg_error, error);
|
||||
g_prefix_error (error, "Unable to import keys: ");
|
||||
ot_gpgme_throw (gpg_error, error, "Unable to import keys");
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -1575,8 +1660,7 @@ ostree_repo_remote_gpg_import (OstreeRepo *self,
|
||||
{
|
||||
if (import_status->result != GPG_ERR_NO_ERROR)
|
||||
{
|
||||
ot_gpgme_error_to_gio_error (gpg_error, error);
|
||||
g_prefix_error (error, "Unable to import key \"%s\": ",
|
||||
ot_gpgme_throw (gpg_error, error, "Unable to import key \"%s\"",
|
||||
import_status->fpr);
|
||||
goto out;
|
||||
}
|
||||
@@ -1994,10 +2078,6 @@ static GFile *
|
||||
get_remotes_d_dir (OstreeRepo *self,
|
||||
GFile *sysroot)
|
||||
{
|
||||
/* Support explicit override */
|
||||
if (self->sysroot_dir != NULL && self->remotes_config_dir != NULL)
|
||||
return g_file_resolve_relative_path (self->sysroot_dir, self->remotes_config_dir);
|
||||
|
||||
g_autoptr(GFile) sysroot_owned = NULL;
|
||||
/* Very complicated sysroot logic; this bit breaks the otherwise mostly clean
|
||||
* layering between OstreeRepo and OstreeSysroot. First, If a sysroot was
|
||||
@@ -2033,10 +2113,18 @@ get_remotes_d_dir (OstreeRepo *self,
|
||||
if (sysroot == NULL && sysroot_ref == NULL)
|
||||
sysroot = self->sysroot_dir;
|
||||
|
||||
/* Did we find a sysroot? If not, NULL means use the repo config, otherwise
|
||||
* return the path in /etc.
|
||||
/* Was the config directory specified? If so, use that with the
|
||||
* optional sysroot prepended. If not, return the path in /etc if the
|
||||
* sysroot was found and NULL otherwise to use the repo config.
|
||||
*/
|
||||
if (sysroot == NULL)
|
||||
if (self->remotes_config_dir != NULL)
|
||||
{
|
||||
if (sysroot == NULL)
|
||||
return g_file_new_for_path (self->remotes_config_dir);
|
||||
else
|
||||
return g_file_resolve_relative_path (sysroot, self->remotes_config_dir);
|
||||
}
|
||||
else if (sysroot == NULL)
|
||||
return NULL;
|
||||
else
|
||||
return g_file_resolve_relative_path (sysroot, SYSCONF_REMOTES);
|
||||
@@ -2178,6 +2266,17 @@ reload_core_config (OstreeRepo *self,
|
||||
}
|
||||
}
|
||||
|
||||
/* By default, only add remotes in a remotes config directory for
|
||||
* system repos. This is to preserve legacy behavior for non-system
|
||||
* repos that specify a remotes config dir (flatpak).
|
||||
*/
|
||||
{ gboolean is_system = ostree_repo_is_system (self);
|
||||
|
||||
if (!ot_keyfile_get_boolean_with_default (self->config, "core", "add-remotes-config-dir",
|
||||
is_system, &self->add_remotes_config_dir, error))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@@ -2316,8 +2415,8 @@ ostree_repo_open (OstreeRepo *self,
|
||||
/* Note - we don't return this error yet! */
|
||||
}
|
||||
|
||||
if (fstat (self->objects_dir_fd, &stbuf) != 0)
|
||||
return glnx_throw_errno (error);
|
||||
if (!glnx_fstat (self->objects_dir_fd, &stbuf, error))
|
||||
return FALSE;
|
||||
self->owner_uid = stbuf.st_uid;
|
||||
|
||||
if (stbuf.st_uid != getuid () || stbuf.st_gid != getgid ())
|
||||
@@ -2703,8 +2802,8 @@ load_metadata_internal (OstreeRepo *self,
|
||||
|
||||
if (fd != -1)
|
||||
{
|
||||
if (fstat (fd, &stbuf) < 0)
|
||||
return glnx_throw_errno (error);
|
||||
if (!glnx_fstat (fd, &stbuf, error))
|
||||
return FALSE;
|
||||
|
||||
if (out_variant)
|
||||
{
|
||||
@@ -3127,7 +3226,7 @@ _ostree_repo_has_loose_object (OstreeRepo *self,
|
||||
if (errno == ENOENT)
|
||||
; /* Next dfd */
|
||||
else
|
||||
return glnx_throw_errno (error);
|
||||
return glnx_throw_errno_prefix (error, "fstatat(%s)", loose_path_buf);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -3210,15 +3309,12 @@ ostree_repo_delete_object (OstreeRepo *self,
|
||||
|
||||
_ostree_loose_path (meta_loose, sha256, OSTREE_OBJECT_TYPE_COMMIT_META, self->mode);
|
||||
|
||||
if (TEMP_FAILURE_RETRY (unlinkat (self->objects_dir_fd, meta_loose, 0)) < 0)
|
||||
{
|
||||
if (G_UNLIKELY (errno != ENOENT))
|
||||
return glnx_throw_errno_prefix (error, "unlinkat(%s)", meta_loose);
|
||||
}
|
||||
if (!ot_ensure_unlinked_at (self->objects_dir_fd, meta_loose, error))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (TEMP_FAILURE_RETRY (unlinkat (self->objects_dir_fd, loose_path, 0)) < 0)
|
||||
return glnx_throw_errno_prefix (error, "Deleting object %s.%s", sha256, ostree_object_type_to_string (objtype));
|
||||
if (!glnx_unlinkat (self->objects_dir_fd, loose_path, 0, error))
|
||||
return glnx_prefix_error (error, "Deleting object %s.%s", sha256, ostree_object_type_to_string (objtype));
|
||||
|
||||
/* If the repository is configured to use tombstone commits, create one when deleting a commit. */
|
||||
if (objtype == OSTREE_OBJECT_TYPE_COMMIT)
|
||||
@@ -3297,6 +3393,9 @@ import_one_object_link (OstreeRepo *self,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
const char *errprefix = glnx_strjoina ("Importing ", checksum, ".",
|
||||
ostree_object_type_to_string (objtype));
|
||||
GLNX_AUTO_PREFIX_ERROR (errprefix, error);
|
||||
char loose_path_buf[_OSTREE_LOOSE_PATH_MAX];
|
||||
_ostree_loose_path (loose_path_buf, checksum, objtype, self->mode);
|
||||
|
||||
@@ -3343,7 +3442,7 @@ import_one_object_link (OstreeRepo *self,
|
||||
return TRUE;
|
||||
}
|
||||
else
|
||||
return glnx_throw_errno (error);
|
||||
return glnx_throw_errno_prefix (error, "linkat");
|
||||
}
|
||||
|
||||
if (objtype == OSTREE_OBJECT_TYPE_COMMIT)
|
||||
@@ -3650,7 +3749,7 @@ ostree_repo_load_commit (OstreeRepo *self,
|
||||
}
|
||||
else if (errno != ENOENT)
|
||||
{
|
||||
return glnx_throw_errno (error);
|
||||
return glnx_throw_errno_prefix (error, "fstatat(%s)", commitpartial_path);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4123,26 +4222,23 @@ sign_data (OstreeRepo *self,
|
||||
}
|
||||
else if (err != GPG_ERR_NO_ERROR)
|
||||
{
|
||||
ot_gpgme_error_to_gio_error (err, error);
|
||||
g_prefix_error (error, "Unable to lookup key ID %s: ", key_id);
|
||||
ot_gpgme_throw (err, error, "Unable to lookup key ID %s", key_id);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Add the key to the context as a signer */
|
||||
if ((err = gpgme_signers_add (context, key)) != GPG_ERR_NO_ERROR)
|
||||
{
|
||||
ot_gpgme_error_to_gio_error (err, error);
|
||||
g_prefix_error (error, "Error signing commit: ");
|
||||
ot_gpgme_throw (err, error, "Error signing commit");
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
||||
{
|
||||
gsize len;
|
||||
const char *buf = g_bytes_get_data (input_data, &len);
|
||||
if ((err = gpgme_data_new_from_mem (&commit_buffer, buf, len, FALSE)) != GPG_ERR_NO_ERROR)
|
||||
{
|
||||
ot_gpgme_error_to_gio_error (err, error);
|
||||
g_prefix_error (error, "Failed to create buffer from commit file: ");
|
||||
ot_gpgme_throw (err, error, "Failed to create buffer from commit file");
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
@@ -4152,8 +4248,7 @@ sign_data (OstreeRepo *self,
|
||||
if ((err = gpgme_op_sign (context, commit_buffer, signature_buffer, GPGME_SIG_MODE_DETACH))
|
||||
!= GPG_ERR_NO_ERROR)
|
||||
{
|
||||
ot_gpgme_error_to_gio_error (err, error);
|
||||
g_prefix_error (error, "Failure signing commit file: ");
|
||||
ot_gpgme_throw (err, error, "Failure signing commit file");
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -4219,11 +4314,14 @@ ostree_repo_sign_commit (OstreeRepo *self,
|
||||
|
||||
/* The verify operation is merely to parse any existing signatures to
|
||||
* check if the commit has already been signed with the given key ID.
|
||||
* We want to avoid storing duplicate signatures in the metadata. */
|
||||
* We want to avoid storing duplicate signatures in the metadata. We
|
||||
* pass the homedir so that the signing key can be imported, allowing
|
||||
* subkey signatures to be recognised. */
|
||||
g_autoptr(GError) local_error = NULL;
|
||||
g_autoptr(GFile) verify_keydir = g_file_new_for_path (homedir);
|
||||
g_autoptr(OstreeGpgVerifyResult) result
|
||||
=_ostree_repo_gpg_verify_with_metadata (self, commit_data, old_metadata,
|
||||
NULL, NULL, NULL,
|
||||
NULL, verify_keydir, NULL,
|
||||
cancellable, &local_error);
|
||||
if (!result)
|
||||
{
|
||||
@@ -4534,32 +4632,23 @@ _ostree_repo_verify_commit_internal (OstreeRepo *self,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
OstreeGpgVerifyResult *result = NULL;
|
||||
g_autoptr(GVariant) commit_variant = NULL;
|
||||
g_autoptr(GVariant) metadata = NULL;
|
||||
g_autoptr(GBytes) signed_data = NULL;
|
||||
|
||||
/* Load the commit */
|
||||
if (!ostree_repo_load_variant (self, OSTREE_OBJECT_TYPE_COMMIT,
|
||||
commit_checksum, &commit_variant,
|
||||
error))
|
||||
{
|
||||
g_prefix_error (error, "Failed to read commit: ");
|
||||
goto out;
|
||||
}
|
||||
return glnx_prefix_error_null (error, "Failed to read commit");
|
||||
|
||||
/* Load the metadata */
|
||||
g_autoptr(GVariant) metadata = NULL;
|
||||
if (!ostree_repo_read_commit_detached_metadata (self,
|
||||
commit_checksum,
|
||||
&metadata,
|
||||
cancellable,
|
||||
error))
|
||||
{
|
||||
g_prefix_error (error, "Failed to read detached metadata: ");
|
||||
goto out;
|
||||
}
|
||||
return glnx_prefix_error_null (error, "Failed to read detached metadata");
|
||||
|
||||
signed_data = g_variant_get_data_as_bytes (commit_variant);
|
||||
g_autoptr(GBytes) signed_data = g_variant_get_data_as_bytes (commit_variant);
|
||||
|
||||
/* XXX This is a hackish way to indicate to use ALL remote-specific
|
||||
* keyrings in the signature verification. We want this when
|
||||
@@ -4567,17 +4656,10 @@ _ostree_repo_verify_commit_internal (OstreeRepo *self,
|
||||
if (remote_name == NULL)
|
||||
remote_name = OSTREE_ALL_REMOTES;
|
||||
|
||||
result = _ostree_repo_gpg_verify_with_metadata (self,
|
||||
signed_data,
|
||||
metadata,
|
||||
remote_name,
|
||||
keyringdir,
|
||||
extra_keyring,
|
||||
cancellable,
|
||||
error);
|
||||
|
||||
out:
|
||||
return result;
|
||||
return _ostree_repo_gpg_verify_with_metadata (self, signed_data,
|
||||
metadata, remote_name,
|
||||
keyringdir, extra_keyring,
|
||||
cancellable, error);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -4609,10 +4691,7 @@ ostree_repo_verify_commit (OstreeRepo *self,
|
||||
cancellable, error);
|
||||
|
||||
if (!ostree_gpg_verify_result_require_valid_signature (result, error))
|
||||
{
|
||||
g_prefix_error (error, "Commit %s: ", commit_checksum);
|
||||
return FALSE;
|
||||
}
|
||||
return glnx_prefix_error (error, "Commit %s", commit_checksum);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@@ -4907,14 +4986,12 @@ ostree_repo_regenerate_summary (OstreeRepo *self,
|
||||
g_variant_new_uint64 (GUINT64_TO_BE (g_get_real_time () / G_USEC_PER_SEC)));
|
||||
}
|
||||
|
||||
/* Add refs which have a collection specified. ostree_repo_list_collection_refs()
|
||||
* is guaranteed to only return refs which are in refs/mirrors, or those which
|
||||
* are in refs/heads if the repository configuration specifies a collection ID
|
||||
* (which we put in the main refs map, rather than the collection map, for
|
||||
* backwards compatibility). */
|
||||
/* Add refs which have a collection specified, which could be in refs/mirrors,
|
||||
* refs/heads, and/or refs/remotes. */
|
||||
{
|
||||
g_autoptr(GHashTable) collection_refs = NULL;
|
||||
if (!ostree_repo_list_collection_refs (self, NULL, &collection_refs, cancellable, error))
|
||||
if (!ostree_repo_list_collection_refs (self, NULL, &collection_refs,
|
||||
OSTREE_REPO_LIST_REFS_EXT_NONE, cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
gsize collection_map_size = 0;
|
||||
@@ -4949,6 +5026,8 @@ ostree_repo_regenerate_summary (OstreeRepo *self,
|
||||
const char *collection_id = collection_iter->data;
|
||||
GHashTable *ref_map = g_hash_table_lookup (collection_map, collection_id);
|
||||
|
||||
/* We put the local repo's collection ID in the main refs map, rather
|
||||
* than the collection map, for backwards compatibility. */
|
||||
gboolean is_main_collection_id = (main_collection_id != NULL && g_str_equal (collection_id, main_collection_id));
|
||||
|
||||
if (!is_main_collection_id)
|
||||
@@ -5009,11 +5088,8 @@ ostree_repo_regenerate_summary (OstreeRepo *self,
|
||||
error))
|
||||
return FALSE;
|
||||
|
||||
if (unlinkat (self->repo_dir_fd, "summary.sig", 0) < 0)
|
||||
{
|
||||
if (errno != ENOENT)
|
||||
return glnx_throw_errno_prefix (error, "unlinkat");
|
||||
}
|
||||
if (!ot_ensure_unlinked_at (self->repo_dir_fd, "summary.sig", error))
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
@@ -5072,19 +5148,17 @@ _ostree_repo_allocate_tmpdir (int tmpdir_dfd,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
gboolean ret = FALSE;
|
||||
gboolean reusing_dir = FALSE;
|
||||
gboolean did_lock;
|
||||
g_autofree char *tmpdir_name = NULL;
|
||||
glnx_fd_close int tmpdir_fd = -1;
|
||||
g_auto(GLnxDirFdIterator) dfd_iter = { 0, };
|
||||
|
||||
g_return_val_if_fail (_ostree_repo_is_locked_tmpdir (tmpdir_prefix), FALSE);
|
||||
|
||||
/* Look for existing tmpdir (with same prefix) to reuse */
|
||||
g_auto(GLnxDirFdIterator) dfd_iter = { 0, };
|
||||
if (!glnx_dirfd_iterator_init_at (tmpdir_dfd, ".", FALSE, &dfd_iter, error))
|
||||
goto out;
|
||||
return FALSE;
|
||||
|
||||
gboolean reusing_dir = FALSE;
|
||||
gboolean did_lock = FALSE;
|
||||
g_autofree char *tmpdir_name = NULL;
|
||||
glnx_fd_close int tmpdir_fd = -1;
|
||||
while (tmpdir_name == NULL)
|
||||
{
|
||||
struct dirent *dent;
|
||||
@@ -5092,7 +5166,7 @@ _ostree_repo_allocate_tmpdir (int tmpdir_dfd,
|
||||
g_autoptr(GError) local_error = NULL;
|
||||
|
||||
if (!glnx_dirfd_iterator_next_dent (&dfd_iter, &dent, cancellable, error))
|
||||
goto out;
|
||||
return FALSE;
|
||||
|
||||
if (dent == NULL)
|
||||
break;
|
||||
@@ -5113,7 +5187,7 @@ _ostree_repo_allocate_tmpdir (int tmpdir_dfd,
|
||||
else
|
||||
{
|
||||
g_propagate_error (error, g_steal_pointer (&local_error));
|
||||
goto out;
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5122,12 +5196,12 @@ _ostree_repo_allocate_tmpdir (int tmpdir_dfd,
|
||||
if (!_ostree_repo_try_lock_tmpdir (tmpdir_dfd, dent->d_name,
|
||||
file_lock_out, &did_lock,
|
||||
error))
|
||||
goto out;
|
||||
return FALSE;
|
||||
if (!did_lock)
|
||||
continue;
|
||||
|
||||
/* Touch the reused directory so that we don't accidentally
|
||||
* remove it due to being old when cleaning up the tmpdir
|
||||
* remove it due to being old when cleaning up the tmpdir.
|
||||
*/
|
||||
(void)futimens (existing_tmpdir_fd, NULL);
|
||||
|
||||
@@ -5139,24 +5213,22 @@ _ostree_repo_allocate_tmpdir (int tmpdir_dfd,
|
||||
|
||||
while (tmpdir_name == NULL)
|
||||
{
|
||||
g_autofree char *tmpdir_name_template = g_strconcat (tmpdir_prefix, "XXXXXX", NULL);
|
||||
glnx_fd_close int new_tmpdir_fd = -1;
|
||||
|
||||
/* No existing tmpdir found, create a new */
|
||||
|
||||
g_autofree char *tmpdir_name_template = g_strconcat (tmpdir_prefix, "XXXXXX", NULL);
|
||||
if (!glnx_mkdtempat (tmpdir_dfd, tmpdir_name_template, 0777, error))
|
||||
goto out;
|
||||
return FALSE;
|
||||
|
||||
glnx_fd_close int new_tmpdir_fd = -1;
|
||||
if (!glnx_opendirat (tmpdir_dfd, tmpdir_name_template, FALSE,
|
||||
&new_tmpdir_fd, error))
|
||||
goto out;
|
||||
return FALSE;
|
||||
|
||||
/* Note, at this point we can race with another process that picks up this
|
||||
* new directory. If that happens we need to retry, making a new directory. */
|
||||
if (!_ostree_repo_try_lock_tmpdir (tmpdir_dfd, tmpdir_name_template,
|
||||
file_lock_out, &did_lock,
|
||||
error))
|
||||
goto out;
|
||||
return FALSE;
|
||||
if (!did_lock)
|
||||
continue;
|
||||
|
||||
@@ -5166,16 +5238,11 @@ _ostree_repo_allocate_tmpdir (int tmpdir_dfd,
|
||||
|
||||
if (tmpdir_name_out)
|
||||
*tmpdir_name_out = g_steal_pointer (&tmpdir_name);
|
||||
|
||||
if (tmpdir_fd_out)
|
||||
*tmpdir_fd_out = glnx_steal_fd (&tmpdir_fd);
|
||||
|
||||
if (reusing_dir_out)
|
||||
*reusing_dir_out = reusing_dir;
|
||||
|
||||
ret = TRUE;
|
||||
out:
|
||||
return ret;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* See ostree-repo-private.h for more information about this */
|
||||
|
||||
@@ -22,6 +22,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "ostree-core.h"
|
||||
#include "ostree-types.h"
|
||||
#include "ostree-async-progress.h"
|
||||
@@ -475,10 +477,12 @@ gboolean ostree_repo_list_refs (OstreeRepo *self,
|
||||
* OstreeRepoListRefsExtFlags:
|
||||
* @OSTREE_REPO_LIST_REFS_EXT_NONE: No flags.
|
||||
* @OSTREE_REPO_LIST_REFS_EXT_ALIASES: Only list aliases. Since: 2017.10
|
||||
* @OSTREE_REPO_LIST_REFS_EXT_EXCLUDE_REMOTES: Exclude remote refs. Since: 2017.11
|
||||
*/
|
||||
typedef enum {
|
||||
OSTREE_REPO_LIST_REFS_EXT_NONE = 0,
|
||||
OSTREE_REPO_LIST_REFS_EXT_ALIASES = 1,
|
||||
OSTREE_REPO_LIST_REFS_EXT_ALIASES = (1 << 0),
|
||||
OSTREE_REPO_LIST_REFS_EXT_EXCLUDE_REMOTES = (1 << 1),
|
||||
} OstreeRepoListRefsExtFlags;
|
||||
|
||||
_OSTREE_PUBLIC
|
||||
@@ -686,6 +690,31 @@ gboolean ostree_repo_write_archive_to_mtree (OstreeRepo *
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
/**
|
||||
* OstreeRepoImportArchiveTranslatePathname:
|
||||
* @repo: Repo
|
||||
* @stbuf: Stat buffer
|
||||
* @src_path: Path in the archive
|
||||
* @user_data: User data
|
||||
*
|
||||
* Possibly change a pathname while importing an archive. If %NULL is returned,
|
||||
* then @src_path will be used unchanged. Otherwise, return a new pathname which
|
||||
* will be freed via `g_free()`.
|
||||
*
|
||||
* This pathname translation will be performed *before* any processing from an
|
||||
* active `OstreeRepoCommitModifier`. Will be invoked for all directory and file
|
||||
* types, first with outer directories, then their sub-files and directories.
|
||||
*
|
||||
* Note that enabling pathname translation will always override the setting for
|
||||
* `use_ostree_convention`.
|
||||
*
|
||||
* Since: 2017.11
|
||||
*/
|
||||
typedef char *(*OstreeRepoImportArchiveTranslatePathname) (OstreeRepo *repo,
|
||||
const struct stat *stbuf,
|
||||
const char *src_path,
|
||||
gpointer user_data);
|
||||
|
||||
/**
|
||||
* OstreeRepoImportArchiveOptions: (skip)
|
||||
*
|
||||
@@ -701,7 +730,9 @@ typedef struct {
|
||||
guint reserved : 28;
|
||||
|
||||
guint unused_uint[8];
|
||||
gpointer unused_ptrs[8];
|
||||
OstreeRepoImportArchiveTranslatePathname translate_pathname;
|
||||
gpointer translate_pathname_user_data;
|
||||
gpointer unused_ptrs[6];
|
||||
} OstreeRepoImportArchiveOptions;
|
||||
|
||||
_OSTREE_PUBLIC
|
||||
@@ -724,6 +755,7 @@ typedef struct {
|
||||
guint disable_xattrs : 1;
|
||||
guint reserved : 31;
|
||||
|
||||
/* 4 byte hole on 64 bit arches */
|
||||
guint64 timestamp_secs;
|
||||
|
||||
guint unused_uint[8];
|
||||
@@ -800,11 +832,13 @@ typedef enum {
|
||||
* @OSTREE_REPO_CHECKOUT_OVERWRITE_NONE: No special options
|
||||
* @OSTREE_REPO_CHECKOUT_OVERWRITE_UNION_FILES: When layering checkouts, unlink() and replace existing files, but do not modify existing directories
|
||||
* @OSTREE_REPO_CHECKOUT_OVERWRITE_ADD_FILES: Only add new files/directories
|
||||
* @OSTREE_REPO_CHECKOUT_OVERWRITE_UNION_IDENTICAL: Like UNION_FILES, but error if files are not identical (requires hardlink checkouts)
|
||||
*/
|
||||
typedef enum {
|
||||
OSTREE_REPO_CHECKOUT_OVERWRITE_NONE = 0,
|
||||
OSTREE_REPO_CHECKOUT_OVERWRITE_UNION_FILES = 1,
|
||||
OSTREE_REPO_CHECKOUT_OVERWRITE_ADD_FILES = 2, /* Since: 2017.3 */
|
||||
OSTREE_REPO_CHECKOUT_OVERWRITE_UNION_IDENTICAL = 3, /* Since: 2017.11 */
|
||||
} OstreeRepoCheckoutOverwriteMode;
|
||||
|
||||
_OSTREE_PUBLIC
|
||||
@@ -838,6 +872,7 @@ typedef struct {
|
||||
gboolean force_copy; /* Since: 2017.6 */
|
||||
gboolean bareuseronly_dirs; /* Since: 2017.7 */
|
||||
gboolean unused_bools[5];
|
||||
/* 4 byte hole on 64 bit */
|
||||
|
||||
const char *subpath;
|
||||
|
||||
@@ -972,6 +1007,7 @@ gboolean ostree_repo_traverse_commit_union (OstreeRepo *repo,
|
||||
|
||||
struct _OstreeRepoCommitTraverseIter {
|
||||
gboolean initialized;
|
||||
/* 4 byte hole on 64 bit */
|
||||
gpointer dummy[10];
|
||||
char dummy_checksum_data[(OSTREE_SHA256_STRING_LEN+1)*2];
|
||||
};
|
||||
@@ -1060,6 +1096,8 @@ gboolean ostree_repo_prune (OstreeRepo *self,
|
||||
struct _OstreeRepoPruneOptions {
|
||||
OstreeRepoPruneFlags flags;
|
||||
|
||||
/* 4 byte hole on 64 bit */
|
||||
|
||||
GHashTable *reachable; /* Set<GVariant> (object names) */
|
||||
|
||||
gboolean unused_bools[6];
|
||||
@@ -1081,7 +1119,7 @@ gboolean ostree_repo_prune_from_reachable (OstreeRepo *self,
|
||||
/**
|
||||
* OstreeRepoPullFlags:
|
||||
* @OSTREE_REPO_PULL_FLAGS_NONE: No special options for pull
|
||||
* @OSTREE_REPO_PULL_FLAGS_MIRROR: Write out refs suitable for mirrors
|
||||
* @OSTREE_REPO_PULL_FLAGS_MIRROR: Write out refs suitable for mirrors and fetch all refs if none requested
|
||||
* @OSTREE_REPO_PULL_FLAGS_COMMIT_ONLY: Fetch only the commit metadata
|
||||
* @OSTREE_REPO_PULL_FLAGS_UNTRUSTED: Don't trust local remote
|
||||
* @OSTREE_REPO_PULL_FLAGS_BAREUSERONLY_FILES: Since 2017.7. Reject writes of content objects with modes outside of 0775.
|
||||
@@ -1185,11 +1223,12 @@ gchar *ostree_repo_resolve_keyring_for_collection (OstreeRepo *self,
|
||||
GError **error);
|
||||
|
||||
_OSTREE_PUBLIC
|
||||
gboolean ostree_repo_list_collection_refs (OstreeRepo *self,
|
||||
const char *match_collection_id,
|
||||
GHashTable **out_all_refs,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
gboolean ostree_repo_list_collection_refs (OstreeRepo *self,
|
||||
const char *match_collection_id,
|
||||
GHashTable **out_all_refs,
|
||||
OstreeRepoListRefsExtFlags flags,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
#endif /* OSTREE_ENABLE_EXPERIMENTAL_API */
|
||||
|
||||
|
||||
@@ -521,7 +521,7 @@ ostree_sepolicy_get_label (OstreeSePolicy *self,
|
||||
if (errno == ENOENT)
|
||||
*out_label = NULL;
|
||||
else
|
||||
return glnx_throw_errno (error);
|
||||
return glnx_throw_errno_prefix (error, "selabel_lookup");
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include "config.h"
|
||||
|
||||
#include "otutil.h"
|
||||
#include "ostree-repo-private.h"
|
||||
#include "ostree-linuxfsutil.h"
|
||||
|
||||
#include "ostree-sysroot-private.h"
|
||||
@@ -201,73 +202,64 @@ list_all_boot_directories (OstreeSysroot *self,
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* A sysroot has at most one active "boot version" (pair of version,subversion)
|
||||
* out of a total of 4 possible. This function deletes from the filesystem the 3
|
||||
* other versions that aren't active.
|
||||
*/
|
||||
static gboolean
|
||||
cleanup_other_bootversions (OstreeSysroot *self,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
gboolean ret = FALSE;
|
||||
int cleanup_bootversion;
|
||||
int cleanup_subbootversion;
|
||||
g_autoptr(GFile) cleanup_boot_dir = NULL;
|
||||
const int cleanup_bootversion = self->bootversion == 0 ? 1 : 0;
|
||||
const int cleanup_subbootversion = self->subbootversion == 0 ? 1 : 0;
|
||||
/* Reusable buffer for path */
|
||||
g_autoptr(GString) buf = g_string_new ("");
|
||||
|
||||
cleanup_bootversion = self->bootversion == 0 ? 1 : 0;
|
||||
cleanup_subbootversion = self->subbootversion == 0 ? 1 : 0;
|
||||
/* These directories are for the other major version */
|
||||
g_string_truncate (buf, 0); g_string_append_printf (buf, "boot/loader.%d", cleanup_bootversion);
|
||||
if (!glnx_shutil_rm_rf_at (self->sysroot_fd, buf->str, cancellable, error))
|
||||
return FALSE;
|
||||
g_string_truncate (buf, 0); g_string_append_printf (buf, "ostree/boot.%d", cleanup_bootversion);
|
||||
if (!glnx_shutil_rm_rf_at (self->sysroot_fd, buf->str, cancellable, error))
|
||||
return FALSE;
|
||||
g_string_truncate (buf, 0); g_string_append_printf (buf, "ostree/boot.%d.0", cleanup_bootversion);
|
||||
if (!glnx_shutil_rm_rf_at (self->sysroot_fd, buf->str, cancellable, error))
|
||||
return FALSE;
|
||||
g_string_truncate (buf, 0); g_string_append_printf (buf, "ostree/boot.%d.1", cleanup_bootversion);
|
||||
if (!glnx_shutil_rm_rf_at (self->sysroot_fd, buf->str, cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
cleanup_boot_dir = ot_gfile_resolve_path_printf (self->path, "boot/loader.%d", cleanup_bootversion);
|
||||
if (!glnx_shutil_rm_rf_at (AT_FDCWD, gs_file_get_path_cached (cleanup_boot_dir), cancellable, error))
|
||||
goto out;
|
||||
g_clear_object (&cleanup_boot_dir);
|
||||
/* And finally the other subbootversion */
|
||||
g_string_truncate (buf, 0); g_string_append_printf (buf, "ostree/boot.%d.%d", self->bootversion, cleanup_subbootversion);
|
||||
if (!glnx_shutil_rm_rf_at (self->sysroot_fd, buf->str, cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
cleanup_boot_dir = ot_gfile_resolve_path_printf (self->path, "ostree/boot.%d", cleanup_bootversion);
|
||||
if (!glnx_shutil_rm_rf_at (AT_FDCWD, gs_file_get_path_cached (cleanup_boot_dir), cancellable, error))
|
||||
goto out;
|
||||
g_clear_object (&cleanup_boot_dir);
|
||||
|
||||
cleanup_boot_dir = ot_gfile_resolve_path_printf (self->path, "ostree/boot.%d.0", cleanup_bootversion);
|
||||
if (!glnx_shutil_rm_rf_at (AT_FDCWD, gs_file_get_path_cached (cleanup_boot_dir), cancellable, error))
|
||||
goto out;
|
||||
g_clear_object (&cleanup_boot_dir);
|
||||
|
||||
cleanup_boot_dir = ot_gfile_resolve_path_printf (self->path, "ostree/boot.%d.1", cleanup_bootversion);
|
||||
if (!glnx_shutil_rm_rf_at (AT_FDCWD, gs_file_get_path_cached (cleanup_boot_dir), cancellable, error))
|
||||
goto out;
|
||||
g_clear_object (&cleanup_boot_dir);
|
||||
|
||||
cleanup_boot_dir = ot_gfile_resolve_path_printf (self->path, "ostree/boot.%d.%d", self->bootversion,
|
||||
cleanup_subbootversion);
|
||||
if (!glnx_shutil_rm_rf_at (AT_FDCWD, gs_file_get_path_cached (cleanup_boot_dir), cancellable, error))
|
||||
goto out;
|
||||
g_clear_object (&cleanup_boot_dir);
|
||||
|
||||
ret = TRUE;
|
||||
out:
|
||||
return ret;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* As the bootloader configuration changes, we will have leftover deployments
|
||||
* on disk. This function deletes all deployments which aren't actively
|
||||
* referenced.
|
||||
*/
|
||||
static gboolean
|
||||
cleanup_old_deployments (OstreeSysroot *self,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
gboolean ret = FALSE;
|
||||
/* Gather the device/inode of the rootfs, so we can double
|
||||
* check we won't delete it.
|
||||
*/
|
||||
struct stat root_stbuf;
|
||||
guint i;
|
||||
g_autoptr(GHashTable) active_deployment_dirs = NULL;
|
||||
g_autoptr(GHashTable) active_boot_checksums = NULL;
|
||||
g_autoptr(GPtrArray) all_deployment_dirs = NULL;
|
||||
g_autoptr(GPtrArray) all_boot_dirs = NULL;
|
||||
if (!glnx_fstatat (AT_FDCWD, "/", &root_stbuf, 0, error))
|
||||
return FALSE;
|
||||
|
||||
if (stat ("/", &root_stbuf) != 0)
|
||||
{
|
||||
glnx_set_error_from_errno (error);
|
||||
goto out;
|
||||
}
|
||||
|
||||
active_deployment_dirs = g_hash_table_new_full (g_str_hash, (GEqualFunc)g_str_equal, g_free, NULL);
|
||||
active_boot_checksums = g_hash_table_new_full (g_str_hash, (GEqualFunc)g_str_equal, g_free, NULL);
|
||||
|
||||
for (i = 0; i < self->deployments->len; i++)
|
||||
/* Load all active deployments referenced by bootloader configuration. */
|
||||
g_autoptr(GHashTable) active_deployment_dirs =
|
||||
g_hash_table_new_full (g_str_hash, (GEqualFunc)g_str_equal, g_free, NULL);
|
||||
g_autoptr(GHashTable) active_boot_checksums =
|
||||
g_hash_table_new_full (g_str_hash, (GEqualFunc)g_str_equal, g_free, NULL);
|
||||
for (guint i = 0; i < self->deployments->len; i++)
|
||||
{
|
||||
OstreeDeployment *deployment = self->deployments->pdata[i];
|
||||
char *deployment_path = ostree_sysroot_get_deployment_dirpath (self, deployment);
|
||||
@@ -277,11 +269,12 @@ cleanup_old_deployments (OstreeSysroot *self,
|
||||
g_hash_table_replace (active_boot_checksums, bootcsum, bootcsum);
|
||||
}
|
||||
|
||||
/* Find all deployment directories, both active and inactive */
|
||||
g_autoptr(GPtrArray) all_deployment_dirs = NULL;
|
||||
if (!list_all_deployment_directories (self, &all_deployment_dirs,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
|
||||
for (i = 0; i < all_deployment_dirs->len; i++)
|
||||
return FALSE;
|
||||
for (guint i = 0; i < all_deployment_dirs->len; i++)
|
||||
{
|
||||
OstreeDeployment *deployment = all_deployment_dirs->pdata[i];
|
||||
g_autofree char *deployment_path = ostree_sysroot_get_deployment_dirpath (self, deployment);
|
||||
@@ -294,13 +287,10 @@ cleanup_old_deployments (OstreeSysroot *self,
|
||||
|
||||
if (!glnx_opendirat (self->sysroot_fd, deployment_path, TRUE,
|
||||
&deployment_fd, error))
|
||||
goto out;
|
||||
return FALSE;
|
||||
|
||||
if (fstat (deployment_fd, &stbuf) != 0)
|
||||
{
|
||||
glnx_set_error_from_errno (error);
|
||||
goto out;
|
||||
}
|
||||
if (!glnx_fstat (deployment_fd, &stbuf, error))
|
||||
return FALSE;
|
||||
|
||||
/* This shouldn't happen, because higher levels should
|
||||
* disallow having the booted deployment not in the active
|
||||
@@ -309,22 +299,24 @@ cleanup_old_deployments (OstreeSysroot *self,
|
||||
stbuf.st_ino == root_stbuf.st_ino)
|
||||
continue;
|
||||
|
||||
/* This deployment wasn't referenced, so delete it */
|
||||
if (!_ostree_linuxfs_fd_alter_immutable_flag (deployment_fd, FALSE,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
|
||||
if (!glnx_shutil_rm_rf_at (self->sysroot_fd, deployment_path, cancellable, error))
|
||||
goto out;
|
||||
return FALSE;
|
||||
if (!glnx_shutil_rm_rf_at (self->sysroot_fd, origin_relpath, cancellable, error))
|
||||
goto out;
|
||||
return FALSE;
|
||||
if (!glnx_shutil_rm_rf_at (self->sysroot_fd, deployment_path, cancellable, error))
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/* Clean up boot directories */
|
||||
g_autoptr(GPtrArray) all_boot_dirs = NULL;
|
||||
if (!list_all_boot_directories (self, &all_boot_dirs,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
|
||||
for (i = 0; i < all_boot_dirs->len; i++)
|
||||
return FALSE;
|
||||
|
||||
for (guint i = 0; i < all_boot_dirs->len; i++)
|
||||
{
|
||||
GFile *bootdir = all_boot_dirs->pdata[i];
|
||||
g_autofree char *osname = NULL;
|
||||
@@ -338,14 +330,13 @@ cleanup_old_deployments (OstreeSysroot *self,
|
||||
continue;
|
||||
|
||||
if (!glnx_shutil_rm_rf_at (AT_FDCWD, gs_file_get_path_cached (bootdir), cancellable, error))
|
||||
goto out;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
ret = TRUE;
|
||||
out:
|
||||
return ret;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* Delete the ref bindings for a non-active boot version */
|
||||
static gboolean
|
||||
cleanup_ref_prefix (OstreeRepo *repo,
|
||||
int bootversion,
|
||||
@@ -353,36 +344,24 @@ cleanup_ref_prefix (OstreeRepo *repo,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
gboolean ret = FALSE;
|
||||
g_autofree char *prefix = NULL;
|
||||
g_autofree char *prefix = g_strdup_printf ("ostree/%d/%d", bootversion, subbootversion);
|
||||
g_autoptr(GHashTable) refs = NULL;
|
||||
GHashTableIter hashiter;
|
||||
gpointer hashkey, hashvalue;
|
||||
|
||||
prefix = g_strdup_printf ("ostree/%d/%d", bootversion, subbootversion);
|
||||
|
||||
if (!ostree_repo_list_refs_ext (repo, prefix, &refs, OSTREE_REPO_LIST_REFS_EXT_NONE, cancellable, error))
|
||||
goto out;
|
||||
return FALSE;
|
||||
|
||||
if (!ostree_repo_prepare_transaction (repo, NULL, cancellable, error))
|
||||
goto out;
|
||||
|
||||
g_hash_table_iter_init (&hashiter, refs);
|
||||
while (g_hash_table_iter_next (&hashiter, &hashkey, &hashvalue))
|
||||
GLNX_HASH_TABLE_FOREACH (refs, const char *, ref)
|
||||
{
|
||||
const char *ref = hashkey;
|
||||
ostree_repo_transaction_set_refspec (repo, ref, NULL);
|
||||
if (!ostree_repo_set_ref_immediate (repo, NULL, ref, NULL, cancellable, error))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!ostree_repo_commit_transaction (repo, NULL, cancellable, error))
|
||||
goto out;
|
||||
|
||||
ret = TRUE;
|
||||
out:
|
||||
ostree_repo_abort_transaction (repo, cancellable, NULL);
|
||||
return ret;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* libostree holds a ref for each deployment's exact checksum to avoid it being
|
||||
* GC'd even if the origin ref changes. This function resets those refs
|
||||
* to match active deployments.
|
||||
*/
|
||||
static gboolean
|
||||
generate_deployment_refs (OstreeSysroot *self,
|
||||
OstreeRepo *repo,
|
||||
@@ -392,46 +371,38 @@ generate_deployment_refs (OstreeSysroot *self,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
gboolean ret = FALSE;
|
||||
int cleanup_bootversion;
|
||||
int cleanup_subbootversion;
|
||||
guint i;
|
||||
|
||||
cleanup_bootversion = (bootversion == 0) ? 1 : 0;
|
||||
cleanup_subbootversion = (subbootversion == 0) ? 1 : 0;
|
||||
int cleanup_bootversion = (bootversion == 0) ? 1 : 0;
|
||||
int cleanup_subbootversion = (subbootversion == 0) ? 1 : 0;
|
||||
|
||||
if (!cleanup_ref_prefix (repo, cleanup_bootversion, 0,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
return FALSE;
|
||||
|
||||
if (!cleanup_ref_prefix (repo, cleanup_bootversion, 1,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
return FALSE;
|
||||
|
||||
if (!cleanup_ref_prefix (repo, bootversion, cleanup_subbootversion,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
return FALSE;
|
||||
|
||||
for (i = 0; i < deployments->len; i++)
|
||||
g_autoptr(_OstreeRepoAutoTransaction) txn =
|
||||
_ostree_repo_auto_transaction_start (repo, cancellable, error);
|
||||
if (!txn)
|
||||
return FALSE;
|
||||
for (guint i = 0; i < deployments->len; i++)
|
||||
{
|
||||
OstreeDeployment *deployment = deployments->pdata[i];
|
||||
g_autofree char *refname = g_strdup_printf ("ostree/%d/%d/%u",
|
||||
bootversion, subbootversion,
|
||||
i);
|
||||
|
||||
if (!ostree_repo_prepare_transaction (repo, NULL, cancellable, error))
|
||||
goto out;
|
||||
|
||||
ostree_repo_transaction_set_refspec (repo, refname, ostree_deployment_get_csum (deployment));
|
||||
|
||||
if (!ostree_repo_commit_transaction (repo, NULL, cancellable, error))
|
||||
goto out;
|
||||
}
|
||||
if (!ostree_repo_commit_transaction (repo, NULL, cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
ret = TRUE;
|
||||
out:
|
||||
ostree_repo_abort_transaction (repo, cancellable, NULL);
|
||||
return ret;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
@@ -442,12 +413,10 @@ prune_repo (OstreeRepo *repo,
|
||||
gint n_objects_total;
|
||||
gint n_objects_pruned;
|
||||
guint64 freed_space;
|
||||
gboolean ret = FALSE;
|
||||
|
||||
if (!ostree_repo_prune (repo, OSTREE_REPO_PRUNE_FLAGS_REFS_ONLY, 0,
|
||||
&n_objects_total, &n_objects_pruned, &freed_space,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
return FALSE;
|
||||
|
||||
if (freed_space > 0)
|
||||
{
|
||||
@@ -455,10 +424,7 @@ prune_repo (OstreeRepo *repo,
|
||||
g_print ("Freed objects: %s\n", freed_space_str);
|
||||
}
|
||||
|
||||
ret = TRUE;
|
||||
|
||||
out:
|
||||
return ret;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -171,8 +171,8 @@ copy_dir_recurse (int src_parent_dfd,
|
||||
return FALSE;
|
||||
|
||||
/* Create with mode 0700, we'll fchmod/fchown later */
|
||||
if (mkdirat (dest_parent_dfd, name, 0700) != 0)
|
||||
return glnx_throw_errno (error);
|
||||
if (!glnx_ensure_dir (dest_parent_dfd, name, 0700, error))
|
||||
return FALSE;
|
||||
|
||||
if (!glnx_opendirat (dest_parent_dfd, name, TRUE, &dest_dfd, error))
|
||||
return FALSE;
|
||||
@@ -882,46 +882,187 @@ ostree_sysroot_write_origin_file (OstreeSysroot *sysroot,
|
||||
cancellable, error);
|
||||
}
|
||||
|
||||
/* Originally OSTree defined kernels to be found underneath /boot
|
||||
* in the tree. But that means when mounting /boot at runtime
|
||||
* we end up masking the content underneath, triggering a warning.
|
||||
*
|
||||
* For that reason, and also consistency with the "/usr defines the OS" model we
|
||||
* later switched to defining the in-tree kernels to be found under
|
||||
* /usr/lib/ostree-boot.
|
||||
*/
|
||||
static gboolean
|
||||
get_kernel_from_tree (int deployment_dfd,
|
||||
int *out_boot_dfd,
|
||||
char **out_kernel_srcpath,
|
||||
char **out_kernel_namever,
|
||||
char **out_initramfs_srcpath,
|
||||
char **out_initramfs_namever,
|
||||
char **out_bootcsum,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
typedef struct {
|
||||
int boot_dfd;
|
||||
char *kernel_srcpath;
|
||||
char *kernel_namever;
|
||||
char *initramfs_srcpath;
|
||||
char *initramfs_namever;
|
||||
char *bootcsum;
|
||||
} OstreeKernelLayout;
|
||||
static void
|
||||
_ostree_kernel_layout_free (OstreeKernelLayout *layout)
|
||||
{
|
||||
g_autofree char *ret_kernel_srcpath = NULL;
|
||||
g_autofree char *ret_kernel_namever = NULL;
|
||||
g_autofree char *ret_initramfs_srcpath = NULL;
|
||||
g_autofree char *ret_initramfs_namever = NULL;
|
||||
g_autofree char *kernel_checksum = NULL;
|
||||
g_autofree char *initramfs_checksum = NULL;
|
||||
if (layout->boot_dfd != -1)
|
||||
(void) close (layout->boot_dfd);
|
||||
g_free (layout->kernel_srcpath);
|
||||
g_free (layout->kernel_namever);
|
||||
g_free (layout->initramfs_srcpath);
|
||||
g_free (layout->initramfs_namever);
|
||||
g_free (layout->bootcsum);
|
||||
g_free (layout);
|
||||
}
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(OstreeKernelLayout, _ostree_kernel_layout_free);
|
||||
|
||||
glnx_fd_close int ret_boot_dfd = glnx_opendirat_with_errno (deployment_dfd, "usr/lib/ostree-boot", TRUE);
|
||||
if (ret_boot_dfd == -1)
|
||||
static OstreeKernelLayout*
|
||||
_ostree_kernel_layout_new (void)
|
||||
{
|
||||
OstreeKernelLayout *ret = g_new0 (OstreeKernelLayout, 1);
|
||||
ret->boot_dfd = -1;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* See get_kernel_from_tree() below */
|
||||
static gboolean
|
||||
get_kernel_from_tree_usrlib_modules (int deployment_dfd,
|
||||
OstreeKernelLayout **out_layout,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
g_autofree char *kver = NULL;
|
||||
/* Look in usr/lib/modules */
|
||||
g_auto(GLnxDirFdIterator) mod_dfditer = { 0, };
|
||||
gboolean exists;
|
||||
if (!ot_dfd_iter_init_allow_noent (deployment_dfd, "usr/lib/modules", &mod_dfditer,
|
||||
&exists, error))
|
||||
return FALSE;
|
||||
if (!exists)
|
||||
{
|
||||
if (errno != ENOENT)
|
||||
return glnx_throw_errno_prefix (error, "%s", "openat(usr/lib/ostree-boot)");
|
||||
else
|
||||
{
|
||||
if (!glnx_opendirat (deployment_dfd, "boot", TRUE, &ret_boot_dfd, error))
|
||||
return FALSE;
|
||||
}
|
||||
/* No usr/lib/modules? We're done */
|
||||
*out_layout = NULL;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
g_autoptr(OstreeKernelLayout) ret_layout = _ostree_kernel_layout_new ();
|
||||
|
||||
/* Reusable buffer for path string */
|
||||
g_autoptr(GString) pathbuf = g_string_new ("");
|
||||
/* Loop until we find something that looks like a valid /usr/lib/modules/$kver */
|
||||
while (ret_layout->boot_dfd == -1)
|
||||
{
|
||||
struct dirent *dent;
|
||||
struct stat stbuf;
|
||||
|
||||
if (!glnx_dirfd_iterator_next_dent_ensure_dtype (&mod_dfditer, &dent, cancellable, error))
|
||||
return FALSE;
|
||||
if (dent == NULL)
|
||||
break;
|
||||
if (dent->d_type != DT_DIR)
|
||||
continue;
|
||||
|
||||
/* It's a directory, look for /vmlinuz as a regular file */
|
||||
g_string_truncate (pathbuf, 0);
|
||||
g_string_append_printf (pathbuf, "%s/vmlinuz", dent->d_name);
|
||||
if (fstatat (mod_dfditer.fd, pathbuf->str, &stbuf, 0) < 0)
|
||||
{
|
||||
if (errno != ENOENT)
|
||||
return glnx_throw_errno_prefix (error, "fstatat(%s)", pathbuf->str);
|
||||
else
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Not a regular file? Loop again */
|
||||
if (!S_ISREG (stbuf.st_mode))
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Looks valid, this should exit the loop */
|
||||
if (!glnx_opendirat (mod_dfditer.fd, dent->d_name, FALSE, &ret_layout->boot_dfd, error))
|
||||
return FALSE;
|
||||
kver = g_strdup (dent->d_name);
|
||||
ret_layout->kernel_srcpath = g_strdup ("vmlinuz");
|
||||
ret_layout->kernel_namever = g_strdup_printf ("vmlinuz-%s", kver);
|
||||
}
|
||||
|
||||
if (ret_layout->boot_dfd == -1)
|
||||
{
|
||||
*out_layout = NULL;
|
||||
/* No kernel found? We're done. */
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* We found a module directory, compute the checksum */
|
||||
g_autoptr(GChecksum) checksum = g_checksum_new (G_CHECKSUM_SHA256);
|
||||
glnx_fd_close int fd = -1;
|
||||
/* Checksum the kernel */
|
||||
if (!glnx_openat_rdonly (ret_layout->boot_dfd, "vmlinuz", TRUE, &fd, error))
|
||||
return FALSE;
|
||||
g_autoptr(GInputStream) in = g_unix_input_stream_new (fd, FALSE);
|
||||
if (!ot_gio_splice_update_checksum (NULL, in, checksum, cancellable, error))
|
||||
return FALSE;
|
||||
g_clear_object (&in);
|
||||
(void) close (fd); fd = -1;
|
||||
|
||||
/* Look for an initramfs, but it's optional; since there wasn't any precedent
|
||||
* for this, let's be a bit conservative and support both `initramfs.img` and
|
||||
* `initramfs`.
|
||||
*/
|
||||
const char *initramfs_paths[] = {"initramfs.img", "initramfs"};
|
||||
const char *initramfs_path = NULL;
|
||||
for (guint i = 0; i < G_N_ELEMENTS(initramfs_paths); i++)
|
||||
{
|
||||
initramfs_path = initramfs_paths[i];
|
||||
if (!ot_openat_ignore_enoent (ret_layout->boot_dfd, initramfs_path, &fd, error))
|
||||
return FALSE;
|
||||
if (fd != -1)
|
||||
break;
|
||||
else
|
||||
initramfs_path = NULL;
|
||||
}
|
||||
if (fd != -1)
|
||||
{
|
||||
g_assert (initramfs_path);
|
||||
ret_layout->initramfs_srcpath = g_strdup (initramfs_path);
|
||||
ret_layout->initramfs_namever = g_strdup_printf ("initramfs-%s.img", kver);
|
||||
in = g_unix_input_stream_new (fd, FALSE);
|
||||
if (!ot_gio_splice_update_checksum (NULL, in, checksum, cancellable, error))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
ret_layout->bootcsum = g_strdup (g_checksum_get_string (checksum));
|
||||
|
||||
*out_layout = g_steal_pointer (&ret_layout);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* See get_kernel_from_tree() below */
|
||||
static gboolean
|
||||
get_kernel_from_tree_legacy_layouts (int deployment_dfd,
|
||||
OstreeKernelLayout **out_layout,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
const char *legacy_paths[] = {"usr/lib/ostree-boot", "boot"};
|
||||
g_autofree char *kernel_checksum = NULL;
|
||||
g_autofree char *initramfs_checksum = NULL;
|
||||
g_autoptr(OstreeKernelLayout) ret_layout = _ostree_kernel_layout_new ();
|
||||
|
||||
for (guint i = 0; i < G_N_ELEMENTS (legacy_paths); i++)
|
||||
{
|
||||
const char *path = legacy_paths[i];
|
||||
ret_layout->boot_dfd = glnx_opendirat_with_errno (deployment_dfd, path, TRUE);
|
||||
if (ret_layout->boot_dfd == -1)
|
||||
{
|
||||
if (errno != ENOENT)
|
||||
return glnx_throw_errno_prefix (error, "openat(%s)", path);
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
if (ret_layout->boot_dfd == -1)
|
||||
{
|
||||
/* No legacy found? We're done */
|
||||
*out_layout = NULL;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* ret_layout->boot_dfd will point to either /usr/lib/ostree-boot or /boot, let's
|
||||
* inspect it.
|
||||
*/
|
||||
g_auto(GLnxDirFdIterator) dfditer = { 0, };
|
||||
if (!glnx_dirfd_iterator_init_at (ret_boot_dfd, ".", FALSE, &dfditer, error))
|
||||
if (!glnx_dirfd_iterator_init_at (ret_layout->boot_dfd, ".", FALSE, &dfditer, error))
|
||||
return FALSE;
|
||||
|
||||
while (TRUE)
|
||||
@@ -930,62 +1071,144 @@ get_kernel_from_tree (int deployment_dfd,
|
||||
|
||||
if (!glnx_dirfd_iterator_next_dent (&dfditer, &dent, cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
if (dent == NULL)
|
||||
break;
|
||||
|
||||
const char *name = dent->d_name;
|
||||
if (ret_kernel_srcpath == NULL && g_str_has_prefix (name, "vmlinuz-"))
|
||||
/* See if this is the kernel */
|
||||
if (ret_layout->kernel_srcpath == NULL && g_str_has_prefix (name, "vmlinuz-"))
|
||||
{
|
||||
const char *dash = strrchr (name, '-');
|
||||
g_assert (dash);
|
||||
/* In this version, we require that the tree builder generated a
|
||||
* sha256 of the kernel+initramfs and appended it to the file names.
|
||||
*/
|
||||
if (ostree_validate_structureof_checksum_string (dash + 1, NULL))
|
||||
{
|
||||
kernel_checksum = g_strdup (dash + 1);
|
||||
ret_kernel_srcpath = g_strdup (name);
|
||||
ret_kernel_namever = g_strndup (name, dash - name);
|
||||
ret_layout->kernel_srcpath = g_strdup (name);
|
||||
ret_layout->kernel_namever = g_strndup (name, dash - name);
|
||||
}
|
||||
}
|
||||
else if (ret_initramfs_srcpath == NULL && g_str_has_prefix (name, "initramfs-"))
|
||||
/* See if this is the initramfs */
|
||||
else if (ret_layout->initramfs_srcpath == NULL && g_str_has_prefix (name, "initramfs-"))
|
||||
{
|
||||
const char *dash = strrchr (name, '-');
|
||||
g_assert (dash);
|
||||
if (ostree_validate_structureof_checksum_string (dash + 1, NULL))
|
||||
{
|
||||
initramfs_checksum = g_strdup (dash + 1);
|
||||
ret_initramfs_srcpath = g_strdup (name);
|
||||
ret_initramfs_namever = g_strndup (name, dash - name);
|
||||
ret_layout->initramfs_srcpath = g_strdup (name);
|
||||
ret_layout->initramfs_namever = g_strndup (name, dash - name);
|
||||
}
|
||||
}
|
||||
|
||||
if (ret_kernel_srcpath != NULL && ret_initramfs_srcpath != NULL)
|
||||
/* If we found both a kernel and initramfs, break out of the loop */
|
||||
if (ret_layout->kernel_srcpath != NULL && ret_layout->initramfs_srcpath != NULL)
|
||||
break;
|
||||
}
|
||||
|
||||
if (ret_kernel_srcpath == NULL)
|
||||
/* No kernel found? We're done */
|
||||
if (ret_layout->kernel_srcpath == NULL)
|
||||
{
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
|
||||
"Failed to find kernel in /usr/lib/ostree-boot or /boot");
|
||||
return FALSE;
|
||||
*out_layout = NULL;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if (ret_initramfs_srcpath != NULL)
|
||||
/* The kernel/initramfs checksums must be the same */
|
||||
if (ret_layout->initramfs_srcpath != NULL)
|
||||
{
|
||||
g_assert (kernel_checksum != NULL);
|
||||
g_assert (initramfs_checksum != NULL);
|
||||
if (strcmp (kernel_checksum, initramfs_checksum) != 0)
|
||||
return glnx_throw (error, "Mismatched kernel checksum vs initrd");
|
||||
}
|
||||
|
||||
ret_layout->bootcsum = g_steal_pointer (&kernel_checksum);
|
||||
|
||||
*out_layout = g_steal_pointer (&ret_layout);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* Locate kernel/initramfs in the tree; the current standard is to look in
|
||||
* /usr/lib/modules/$kver/vmlinuz first.
|
||||
*
|
||||
* Originally OSTree defined kernels to be found underneath /boot
|
||||
* in the tree. But that means when mounting /boot at runtime
|
||||
* we end up masking the content underneath, triggering a warning.
|
||||
*
|
||||
* For that reason, and also consistency with the "/usr defines the OS" model we
|
||||
* later switched to defining the in-tree kernels to be found under
|
||||
* /usr/lib/ostree-boot. But since then, Fedora at least switched to storing the
|
||||
* kernel in /usr/lib/modules, which makes sense and isn't ostree-specific, so
|
||||
* we prefer that now. However, the default Fedora layout doesn't put the
|
||||
* initramfs there, so we need to look in /usr/lib/ostree-boot first.
|
||||
*/
|
||||
static gboolean
|
||||
get_kernel_from_tree (int deployment_dfd,
|
||||
OstreeKernelLayout **out_layout,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
g_autoptr(OstreeKernelLayout) usrlib_modules_layout = NULL;
|
||||
g_autoptr(OstreeKernelLayout) legacy_layout = NULL;
|
||||
|
||||
/* First, gather from usr/lib/modules/$kver if it exists */
|
||||
if (!get_kernel_from_tree_usrlib_modules (deployment_dfd, &usrlib_modules_layout, cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
/* Gather the legacy layout */
|
||||
if (!get_kernel_from_tree_legacy_layouts (deployment_dfd, &legacy_layout, cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
/* Evaluate the state of both layouts. If there's no legacy layout
|
||||
* If a legacy layout exists, and it has
|
||||
* an initramfs but the module layout doesn't, the legacy layout wins. This is
|
||||
* what happens with rpm-ostree with Fedora today, until rpm-ostree learns the
|
||||
* new layout.
|
||||
*/
|
||||
if (legacy_layout == NULL)
|
||||
{
|
||||
/* No legacy layout, let's see if we have a module layout...*/
|
||||
if (usrlib_modules_layout == NULL)
|
||||
{
|
||||
/* Both layouts are not found? Throw. */
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
|
||||
"Mismatched kernel checksum vs initrd");
|
||||
"Failed to find kernel in /usr/lib/modules, /usr/lib/ostree-boot or /boot");
|
||||
return FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* No legacy, just usr/lib/modules? We're done */
|
||||
*out_layout = g_steal_pointer (&usrlib_modules_layout);
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
else if (usrlib_modules_layout != NULL &&
|
||||
usrlib_modules_layout->initramfs_srcpath == NULL &&
|
||||
legacy_layout->initramfs_srcpath != NULL)
|
||||
{
|
||||
/* Does the module path not have an initramfs, but the legacy does? Prefer
|
||||
* the latter then, to make rpm-ostree work as is today.
|
||||
*/
|
||||
*out_layout = g_steal_pointer (&legacy_layout);
|
||||
return TRUE;
|
||||
}
|
||||
/* Prefer module layout */
|
||||
else if (usrlib_modules_layout != NULL)
|
||||
{
|
||||
*out_layout = g_steal_pointer (&usrlib_modules_layout);
|
||||
return TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* And finally fall back to legacy; we know one exists since we
|
||||
* checked first above.
|
||||
*/
|
||||
g_assert (legacy_layout->kernel_srcpath);
|
||||
*out_layout = g_steal_pointer (&legacy_layout);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
*out_boot_dfd = glnx_steal_fd (&ret_boot_dfd);
|
||||
*out_kernel_srcpath = g_steal_pointer (&ret_kernel_srcpath);
|
||||
*out_kernel_namever = g_steal_pointer (&ret_kernel_namever);
|
||||
*out_initramfs_srcpath = g_steal_pointer (&ret_initramfs_srcpath);
|
||||
*out_initramfs_namever = g_steal_pointer (&ret_initramfs_namever);
|
||||
*out_bootcsum = g_steal_pointer (&kernel_checksum);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* We used to syncfs(), but that doesn't flush the journal on XFS,
|
||||
@@ -1315,18 +1538,8 @@ install_deployment_kernel (OstreeSysroot *sysroot,
|
||||
return FALSE;
|
||||
|
||||
/* Find the kernel/initramfs in the tree */
|
||||
glnx_fd_close int tree_boot_dfd = -1;
|
||||
g_autofree char *tree_kernel_srcpath = NULL;
|
||||
g_autofree char *tree_kernel_namever = NULL;
|
||||
g_autofree char *tree_initramfs_srcpath = NULL;
|
||||
g_autofree char *tree_initramfs_namever = NULL;
|
||||
g_autofree char *tree_bootcsum = NULL;
|
||||
if (!get_kernel_from_tree (deployment_dfd, &tree_boot_dfd,
|
||||
&tree_kernel_srcpath,
|
||||
&tree_kernel_namever,
|
||||
&tree_initramfs_srcpath,
|
||||
&tree_initramfs_namever,
|
||||
&tree_bootcsum,
|
||||
g_autoptr(OstreeKernelLayout) kernel_layout = NULL;
|
||||
if (!get_kernel_from_tree (deployment_dfd, &kernel_layout,
|
||||
cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
@@ -1336,7 +1549,7 @@ install_deployment_kernel (OstreeSysroot *sysroot,
|
||||
|
||||
const char *osname = ostree_deployment_get_osname (deployment);
|
||||
const char *bootcsum = ostree_deployment_get_bootcsum (deployment);
|
||||
g_assert_cmpstr (bootcsum, ==, tree_bootcsum);
|
||||
g_assert_cmpstr (kernel_layout->bootcsum, ==, bootcsum);
|
||||
g_autofree char *bootcsumdir = g_strdup_printf ("ostree/%s-%s", osname, bootcsum);
|
||||
g_autofree char *bootconfdir = g_strdup_printf ("loader.%d/entries", new_bootversion);
|
||||
g_autofree char *bootconf_name = g_strdup_printf ("ostree-%s-%d.conf", osname,
|
||||
@@ -1355,12 +1568,13 @@ install_deployment_kernel (OstreeSysroot *sysroot,
|
||||
* it doesn't exist already.
|
||||
*/
|
||||
struct stat stbuf;
|
||||
if (fstatat (bootcsum_dfd, tree_kernel_namever, &stbuf, 0) != 0)
|
||||
if (fstatat (bootcsum_dfd, kernel_layout->kernel_namever, &stbuf, 0) != 0)
|
||||
{
|
||||
if (errno != ENOENT)
|
||||
return glnx_throw_errno_prefix (error, "fstat %s", tree_kernel_namever);
|
||||
if (!hardlink_or_copy_at (tree_boot_dfd, tree_kernel_srcpath,
|
||||
bootcsum_dfd, tree_kernel_namever,
|
||||
return glnx_throw_errno_prefix (error, "fstat %s", kernel_layout->kernel_namever);
|
||||
if (!hardlink_or_copy_at (kernel_layout->boot_dfd,
|
||||
kernel_layout->kernel_srcpath,
|
||||
bootcsum_dfd, kernel_layout->kernel_namever,
|
||||
sysroot->debug_flags,
|
||||
cancellable, error))
|
||||
return FALSE;
|
||||
@@ -1369,15 +1583,15 @@ install_deployment_kernel (OstreeSysroot *sysroot,
|
||||
/* If we have an initramfs, then install it into
|
||||
* /boot/ostree/osname-${bootcsum} if it doesn't exist already.
|
||||
*/
|
||||
if (tree_initramfs_srcpath)
|
||||
if (kernel_layout->initramfs_srcpath)
|
||||
{
|
||||
g_assert (tree_initramfs_namever);
|
||||
if (fstatat (bootcsum_dfd, tree_initramfs_namever, &stbuf, 0) != 0)
|
||||
g_assert (kernel_layout->initramfs_namever);
|
||||
if (fstatat (bootcsum_dfd, kernel_layout->initramfs_namever, &stbuf, 0) != 0)
|
||||
{
|
||||
if (errno != ENOENT)
|
||||
return glnx_throw_errno_prefix (error, "fstat %s", tree_initramfs_namever);
|
||||
if (!hardlink_or_copy_at (tree_boot_dfd, tree_initramfs_srcpath,
|
||||
bootcsum_dfd, tree_initramfs_namever,
|
||||
return glnx_throw_errno_prefix (error, "fstat %s", kernel_layout->initramfs_namever);
|
||||
if (!hardlink_or_copy_at (kernel_layout->boot_dfd, kernel_layout->initramfs_srcpath,
|
||||
bootcsum_dfd, kernel_layout->initramfs_namever,
|
||||
sysroot->debug_flags,
|
||||
cancellable, error))
|
||||
return FALSE;
|
||||
@@ -1458,12 +1672,12 @@ install_deployment_kernel (OstreeSysroot *sysroot,
|
||||
|
||||
g_autofree char *version_key = g_strdup_printf ("%d", n_deployments - ostree_deployment_get_index (deployment));
|
||||
ostree_bootconfig_parser_set (bootconfig, OSTREE_COMMIT_META_KEY_VERSION, version_key);
|
||||
g_autofree char * boot_relpath = g_strconcat ("/", bootcsumdir, "/", tree_kernel_namever, NULL);
|
||||
g_autofree char * boot_relpath = g_strconcat ("/", bootcsumdir, "/", kernel_layout->kernel_namever, NULL);
|
||||
ostree_bootconfig_parser_set (bootconfig, "linux", boot_relpath);
|
||||
|
||||
if (tree_initramfs_namever)
|
||||
if (kernel_layout->initramfs_namever)
|
||||
{
|
||||
g_autofree char * boot_relpath = g_strconcat ("/", bootcsumdir, "/", tree_initramfs_namever, NULL);
|
||||
g_autofree char * boot_relpath = g_strconcat ("/", bootcsumdir, "/", kernel_layout->initramfs_namever, NULL);
|
||||
ostree_bootconfig_parser_set (bootconfig, "initrd", boot_relpath);
|
||||
}
|
||||
|
||||
@@ -2084,22 +2298,12 @@ ostree_sysroot_deploy_tree (OstreeSysroot *self,
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
glnx_fd_close int tree_boot_dfd = -1;
|
||||
g_autofree char *tree_kernel_srcpath = NULL;
|
||||
g_autofree char *tree_kernel_namever = NULL;
|
||||
g_autofree char *tree_initramfs_srcpath = NULL;
|
||||
g_autofree char *tree_initramfs_namever = NULL;
|
||||
g_autofree char *tree_bootcsum = NULL;
|
||||
if (!get_kernel_from_tree (deployment_dfd, &tree_boot_dfd,
|
||||
&tree_kernel_srcpath,
|
||||
&tree_kernel_namever,
|
||||
&tree_initramfs_srcpath,
|
||||
&tree_initramfs_namever,
|
||||
&new_bootcsum,
|
||||
g_autoptr(OstreeKernelLayout) kernel_layout = NULL;
|
||||
if (!get_kernel_from_tree (deployment_dfd, &kernel_layout,
|
||||
cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
_ostree_deployment_set_bootcsum (new_deployment, new_bootcsum);
|
||||
_ostree_deployment_set_bootcsum (new_deployment, kernel_layout->bootcsum);
|
||||
|
||||
/* Create an empty boot configuration; we will merge things into
|
||||
* it as we go.
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
|
||||
#include "ostree.h"
|
||||
#include "ostree-sysroot-upgrader.h"
|
||||
#include "ostree-core-private.h"
|
||||
|
||||
/**
|
||||
* SECTION:ostree-sysroot-upgrader
|
||||
@@ -429,26 +430,10 @@ ostree_sysroot_upgrader_check_timestamps (OstreeRepo *repo,
|
||||
error))
|
||||
return FALSE;
|
||||
|
||||
if (ostree_commit_get_timestamp (old_commit) > ostree_commit_get_timestamp (new_commit))
|
||||
{
|
||||
GDateTime *old_ts = g_date_time_new_from_unix_utc (ostree_commit_get_timestamp (old_commit));
|
||||
GDateTime *new_ts = g_date_time_new_from_unix_utc (ostree_commit_get_timestamp (new_commit));
|
||||
g_autofree char *old_ts_str = NULL;
|
||||
g_autofree char *new_ts_str = NULL;
|
||||
|
||||
if (old_ts == NULL || new_ts == NULL)
|
||||
return glnx_throw (error, "Upgrade target revision '%s' timestamp (%" G_GINT64_FORMAT ") or current revision '%s' timestamp (%" G_GINT64_FORMAT ") is invalid",
|
||||
to_rev, ostree_commit_get_timestamp (new_commit),
|
||||
from_rev, ostree_commit_get_timestamp (old_commit));
|
||||
|
||||
old_ts_str = g_date_time_format (old_ts, "%c");
|
||||
new_ts_str = g_date_time_format (new_ts, "%c");
|
||||
g_date_time_unref (old_ts);
|
||||
g_date_time_unref (new_ts);
|
||||
|
||||
return glnx_throw (error, "Upgrade target revision '%s' with timestamp '%s' is chronologically older than current revision '%s' with timestamp '%s'; use --allow-downgrade to permit",
|
||||
to_rev, new_ts_str, from_rev, old_ts_str);
|
||||
}
|
||||
if (!_ostree_compare_timestamps (from_rev, ostree_commit_get_timestamp (old_commit),
|
||||
to_rev, ostree_commit_get_timestamp (new_commit),
|
||||
error))
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
@@ -536,9 +521,23 @@ ostree_sysroot_upgrader_pull_one_dir (OstreeSysrootUpgrader *self,
|
||||
if (self->origin_remote &&
|
||||
(upgrader_flags & OSTREE_SYSROOT_UPGRADER_PULL_FLAGS_SYNTHETIC) == 0)
|
||||
{
|
||||
if (!ostree_repo_pull_one_dir (repo, self->origin_remote, dir_to_pull, refs_to_fetch,
|
||||
flags, progress,
|
||||
cancellable, error))
|
||||
g_autoptr(GVariantBuilder) optbuilder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}"));
|
||||
if (dir_to_pull && *dir_to_pull)
|
||||
g_variant_builder_add (optbuilder, "{s@v}", "subdir",
|
||||
g_variant_new_variant (g_variant_new_string (dir_to_pull)));
|
||||
g_variant_builder_add (optbuilder, "{s@v}", "flags",
|
||||
g_variant_new_variant (g_variant_new_int32 (flags)));
|
||||
/* Add the timestamp check, unless disabled */
|
||||
if ((upgrader_flags & OSTREE_SYSROOT_UPGRADER_PULL_FLAGS_ALLOW_OLDER) == 0)
|
||||
g_variant_builder_add (optbuilder, "{s@v}", "timestamp-check",
|
||||
g_variant_new_variant (g_variant_new_boolean (TRUE)));
|
||||
|
||||
g_variant_builder_add (optbuilder, "{s@v}", "refs",
|
||||
g_variant_new_variant (g_variant_new_strv ((const char *const*) refs_to_fetch, -1)));
|
||||
g_autoptr(GVariant) opts = g_variant_ref_sink (g_variant_builder_end (optbuilder));
|
||||
if (!ostree_repo_pull_with_options (repo, self->origin_remote,
|
||||
opts, progress,
|
||||
cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
if (progress)
|
||||
|
||||
@@ -256,7 +256,7 @@ ensure_sysroot_fd (OstreeSysroot *self,
|
||||
* Access a file descriptor that refers to the root directory of this
|
||||
* sysroot. ostree_sysroot_load() must have been invoked prior to
|
||||
* calling this function.
|
||||
*
|
||||
*
|
||||
* Returns: A file descriptor valid for the lifetime of @self
|
||||
*/
|
||||
int
|
||||
@@ -276,7 +276,7 @@ _ostree_sysroot_bump_mtime (OstreeSysroot *self,
|
||||
glnx_set_prefix_error_from_errno (error, "%s", "futimens");
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -471,8 +471,8 @@ _ostree_sysroot_read_boot_loader_configs (OstreeSysroot *self,
|
||||
if (dent == NULL)
|
||||
break;
|
||||
|
||||
if (fstatat (dfd_iter.fd, dent->d_name, &stbuf, 0) != 0)
|
||||
return glnx_throw_errno (error);
|
||||
if (!glnx_fstatat (dfd_iter.fd, dent->d_name, &stbuf, 0, error))
|
||||
return FALSE;
|
||||
|
||||
if (g_str_has_prefix (dent->d_name, "ostree-") &&
|
||||
g_str_has_suffix (dent->d_name, ".conf") &&
|
||||
@@ -772,6 +772,13 @@ ensure_repo (OstreeSysroot *self,
|
||||
*/
|
||||
g_weak_ref_init (&self->repo->sysroot, self);
|
||||
self->repo->sysroot_kind = OSTREE_REPO_SYSROOT_KIND_VIA_SYSROOT;
|
||||
|
||||
/* Reload the repo config in case any defaults depend on knowing if this is
|
||||
* a system repo.
|
||||
*/
|
||||
if (!ostree_repo_reload_config (self->repo, NULL, error))
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@@ -894,7 +901,7 @@ ostree_sysroot_get_subbootversion (OstreeSysroot *self)
|
||||
/**
|
||||
* ostree_sysroot_get_booted_deployment:
|
||||
* @self: Sysroot
|
||||
*
|
||||
*
|
||||
* Returns: (transfer none): The currently booted deployment, or %NULL if none
|
||||
*/
|
||||
OstreeDeployment *
|
||||
@@ -1377,7 +1384,7 @@ lock_in_thread (GTask *task,
|
||||
* @cancellable: Cancellable
|
||||
* @callback: Callback
|
||||
* @user_data: User data
|
||||
*
|
||||
*
|
||||
* An asynchronous version of ostree_sysroot_lock().
|
||||
*/
|
||||
void
|
||||
@@ -1395,7 +1402,7 @@ ostree_sysroot_lock_async (OstreeSysroot *self,
|
||||
* @self: Self
|
||||
* @result: Result
|
||||
* @error: Error
|
||||
*
|
||||
*
|
||||
* Call when ostree_sysroot_lock_async() is ready.
|
||||
*/
|
||||
gboolean
|
||||
@@ -1413,7 +1420,7 @@ ostree_sysroot_lock_finish (OstreeSysroot *self,
|
||||
* @osname: Name group of operating system checkouts
|
||||
* @cancellable: Cancellable
|
||||
* @error: Error
|
||||
*
|
||||
*
|
||||
* Initialize the directory structure for an "osname", which is a
|
||||
* group of operating system deployments, with a shared `/var`. One
|
||||
* is required for generating a deployment.
|
||||
@@ -1487,6 +1494,12 @@ ostree_sysroot_init_osname (OstreeSysroot *self,
|
||||
* If %OSTREE_SYSROOT_SIMPLE_WRITE_DEPLOYMENT_FLAGS_RETAIN is
|
||||
* specified, then all current deployments will be kept.
|
||||
*
|
||||
* If %OSTREE_SYSROOT_SIMPLE_WRITE_DEPLOYMENT_FLAGS_RETAIN_PENDING is
|
||||
* specified, then pending deployments will be kept.
|
||||
*
|
||||
* If %OSTREE_SYSROOT_SIMPLE_WRITE_DEPLOYMENT_FLAGS_RETAIN_ROLLBACK is
|
||||
* specified, then rollback deployments will be kept.
|
||||
*
|
||||
* If %OSTREE_SYSROOT_SIMPLE_WRITE_DEPLOYMENT_FLAGS_NOT_DEFAULT is
|
||||
* specified, then instead of prepending, the new deployment will be
|
||||
* added right after the booted or merge deployment, instead of first.
|
||||
@@ -1505,70 +1518,91 @@ ostree_sysroot_simple_write_deployment (OstreeSysroot *sysroot,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
gboolean ret = FALSE;
|
||||
guint i;
|
||||
OstreeDeployment *booted_deployment = NULL;
|
||||
g_autoptr(GPtrArray) deployments = NULL;
|
||||
g_autoptr(GPtrArray) new_deployments = g_ptr_array_new_with_free_func (g_object_unref);
|
||||
const gboolean postclean = (flags & OSTREE_SYSROOT_SIMPLE_WRITE_DEPLOYMENT_FLAGS_NO_CLEAN) == 0;
|
||||
OstreeSysrootWriteDeploymentsOpts write_opts = { .do_postclean = postclean };
|
||||
gboolean retain = (flags & OSTREE_SYSROOT_SIMPLE_WRITE_DEPLOYMENT_FLAGS_RETAIN) > 0;
|
||||
const gboolean make_default = !((flags & OSTREE_SYSROOT_SIMPLE_WRITE_DEPLOYMENT_FLAGS_NOT_DEFAULT) > 0);
|
||||
gboolean added_new = FALSE;
|
||||
const gboolean postclean =
|
||||
(flags & OSTREE_SYSROOT_SIMPLE_WRITE_DEPLOYMENT_FLAGS_NO_CLEAN) == 0;
|
||||
const gboolean make_default =
|
||||
!((flags & OSTREE_SYSROOT_SIMPLE_WRITE_DEPLOYMENT_FLAGS_NOT_DEFAULT) > 0);
|
||||
const gboolean retain_pending =
|
||||
(flags & OSTREE_SYSROOT_SIMPLE_WRITE_DEPLOYMENT_FLAGS_RETAIN_PENDING) > 0;
|
||||
const gboolean retain_rollback =
|
||||
(flags & OSTREE_SYSROOT_SIMPLE_WRITE_DEPLOYMENT_FLAGS_RETAIN_ROLLBACK) > 0;
|
||||
gboolean retain =
|
||||
(flags & OSTREE_SYSROOT_SIMPLE_WRITE_DEPLOYMENT_FLAGS_RETAIN) > 0;
|
||||
|
||||
deployments = ostree_sysroot_get_deployments (sysroot);
|
||||
booted_deployment = ostree_sysroot_get_booted_deployment (sysroot);
|
||||
g_autoptr(GPtrArray) deployments = ostree_sysroot_get_deployments (sysroot);
|
||||
OstreeDeployment *booted_deployment = ostree_sysroot_get_booted_deployment (sysroot);
|
||||
|
||||
if (osname == NULL && booted_deployment)
|
||||
osname = ostree_deployment_get_osname (booted_deployment);
|
||||
|
||||
gboolean added_new = FALSE;
|
||||
g_autoptr(GPtrArray) new_deployments = g_ptr_array_new_with_free_func (g_object_unref);
|
||||
if (make_default)
|
||||
{
|
||||
g_ptr_array_add (new_deployments, g_object_ref (new_deployment));
|
||||
added_new = TRUE;
|
||||
}
|
||||
|
||||
for (i = 0; i < deployments->len; i++)
|
||||
/* without a booted and a merge deployment, retain_pending/rollback become meaningless;
|
||||
* let's just retain all deployments in that case */
|
||||
if (!booted_deployment && !merge_deployment && (retain_pending || retain_rollback))
|
||||
retain = TRUE;
|
||||
|
||||
/* tracks when we come across the booted deployment */
|
||||
gboolean before_booted = TRUE;
|
||||
gboolean before_merge = TRUE;
|
||||
for (guint i = 0; i < deployments->len; i++)
|
||||
{
|
||||
OstreeDeployment *deployment = deployments->pdata[i];
|
||||
const gboolean is_merge_or_booted =
|
||||
ostree_deployment_equal (deployment, booted_deployment) ||
|
||||
ostree_deployment_equal (deployment, merge_deployment);
|
||||
|
||||
/* Keep deployments with different osnames, as well as the
|
||||
* booted and merge deployments
|
||||
*/
|
||||
if (retain ||
|
||||
(osname != NULL && strcmp (ostree_deployment_get_osname (deployment), osname) != 0) ||
|
||||
is_merge_or_booted)
|
||||
{
|
||||
g_ptr_array_add (new_deployments, g_object_ref (deployment));
|
||||
}
|
||||
const gboolean osname_matches =
|
||||
(osname == NULL || g_str_equal (ostree_deployment_get_osname (deployment), osname));
|
||||
const gboolean is_booted = ostree_deployment_equal (deployment, booted_deployment);
|
||||
const gboolean is_merge = ostree_deployment_equal (deployment, merge_deployment);
|
||||
|
||||
if (!added_new)
|
||||
if (is_booted)
|
||||
before_booted = FALSE;
|
||||
if (is_merge)
|
||||
before_merge = FALSE;
|
||||
|
||||
/* use the booted deployment as the "crossover" point between pending and rollback
|
||||
* deployments, fall back on merge deployment */
|
||||
const gboolean passed_crossover = booted_deployment ? !before_booted : !before_merge;
|
||||
|
||||
/* Retain deployment if:
|
||||
* - we're explicitly asked to, or
|
||||
* - the deployment is for another osname, or
|
||||
* - we're keeping pending deployments and this is a pending deployment, or
|
||||
* - this is the merge or boot deployment, or
|
||||
* - we're keeping rollback deployments and this is a rollback deployment
|
||||
*/
|
||||
if (retain
|
||||
|| !osname_matches
|
||||
|| (retain_pending && !passed_crossover)
|
||||
|| (is_booted || is_merge)
|
||||
|| (retain_rollback && passed_crossover))
|
||||
g_ptr_array_add (new_deployments, g_object_ref (deployment));
|
||||
|
||||
/* add right after booted/merge deployment */
|
||||
if (!added_new && passed_crossover)
|
||||
{
|
||||
g_ptr_array_add (new_deployments, g_object_ref (new_deployment));
|
||||
added_new = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
/* In this non-default case , an improvement in the future would be
|
||||
* to put the new deployment right after the current default in the
|
||||
* order.
|
||||
*/
|
||||
/* add it last if no crossover defined (or it's the first deployment in the sysroot) */
|
||||
if (!added_new)
|
||||
{
|
||||
g_ptr_array_add (new_deployments, g_object_ref (new_deployment));
|
||||
added_new = TRUE;
|
||||
}
|
||||
|
||||
OstreeSysrootWriteDeploymentsOpts write_opts = { .do_postclean = postclean };
|
||||
if (!ostree_sysroot_write_deployments_with_options (sysroot, new_deployments, &write_opts,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
return FALSE;
|
||||
|
||||
ret = TRUE;
|
||||
out:
|
||||
return ret;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* Deploy a copy of @target_deployment */
|
||||
@@ -1704,8 +1738,8 @@ ostree_sysroot_deployment_unlock (OstreeSysroot *self,
|
||||
"/usr", 0755, error))
|
||||
return FALSE;
|
||||
|
||||
if (!glnx_mkdtempat (AT_FDCWD, development_ovldir, 0755, error))
|
||||
return FALSE;
|
||||
if (g_mkdtemp_full (development_ovldir, 0755) == NULL)
|
||||
return glnx_throw_errno_prefix (error, "mkdtemp");
|
||||
}
|
||||
|
||||
development_ovl_upper = glnx_strjoina (development_ovldir, "/upper");
|
||||
|
||||
@@ -207,6 +207,8 @@ typedef enum {
|
||||
OSTREE_SYSROOT_SIMPLE_WRITE_DEPLOYMENT_FLAGS_RETAIN = (1 << 0),
|
||||
OSTREE_SYSROOT_SIMPLE_WRITE_DEPLOYMENT_FLAGS_NOT_DEFAULT = (1 << 1),
|
||||
OSTREE_SYSROOT_SIMPLE_WRITE_DEPLOYMENT_FLAGS_NO_CLEAN = (1 << 2),
|
||||
OSTREE_SYSROOT_SIMPLE_WRITE_DEPLOYMENT_FLAGS_RETAIN_PENDING = (1 << 3),
|
||||
OSTREE_SYSROOT_SIMPLE_WRITE_DEPLOYMENT_FLAGS_RETAIN_ROLLBACK = (1 << 4),
|
||||
} OstreeSysrootSimpleWriteDeploymentFlags;
|
||||
|
||||
_OSTREE_PUBLIC
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
*
|
||||
* Since: 2017.4
|
||||
*/
|
||||
#define OSTREE_RELEASE_VERSION (10)
|
||||
#define OSTREE_RELEASE_VERSION (11)
|
||||
|
||||
/**
|
||||
* OSTREE_VERSION
|
||||
@@ -53,7 +53,7 @@
|
||||
*
|
||||
* Since: 2017.4
|
||||
*/
|
||||
#define OSTREE_VERSION (2017.10)
|
||||
#define OSTREE_VERSION (2017.11)
|
||||
|
||||
/**
|
||||
* OSTREE_VERSION_S:
|
||||
@@ -63,7 +63,7 @@
|
||||
*
|
||||
* Since: 2017.4
|
||||
*/
|
||||
#define OSTREE_VERSION_S "2017.10"
|
||||
#define OSTREE_VERSION_S "2017.11"
|
||||
|
||||
#define OSTREE_ENCODE_VERSION(year,release) \
|
||||
((year) << 16 | (release))
|
||||
|
||||
@@ -90,6 +90,7 @@ ot_openat_read_stream (int dfd,
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* Like unlinkat() but ignore ENOENT */
|
||||
gboolean
|
||||
ot_ensure_unlinked_at (int dfd,
|
||||
const char *path,
|
||||
|
||||
@@ -26,20 +26,23 @@
|
||||
|
||||
#include "libglnx.h"
|
||||
|
||||
void
|
||||
ot_gpgme_error_to_gio_error (gpgme_error_t gpg_error,
|
||||
GError **error)
|
||||
/* Like glnx_throw_errno_prefix, but takes @gpg_error */
|
||||
gboolean
|
||||
ot_gpgme_throw (gpgme_error_t gpg_error, GError **error,
|
||||
const char *fmt, ...)
|
||||
{
|
||||
if (error == NULL)
|
||||
return FALSE;
|
||||
|
||||
GIOErrorEnum errcode;
|
||||
char errbuf[1024];
|
||||
|
||||
/* XXX This list is incomplete. Add cases as needed. */
|
||||
|
||||
switch (gpgme_err_code (gpg_error))
|
||||
{
|
||||
/* special case - shouldn't be here */
|
||||
case GPG_ERR_NO_ERROR:
|
||||
g_return_if_reached ();
|
||||
g_assert_not_reached ();
|
||||
|
||||
/* special case - abort on out-of-memory */
|
||||
case GPG_ERR_ENOMEM:
|
||||
@@ -63,6 +66,12 @@ ot_gpgme_error_to_gio_error (gpgme_error_t gpg_error,
|
||||
g_set_error (error, G_IO_ERROR, errcode, "%s: %s",
|
||||
gpgme_strsource (gpg_error),
|
||||
errbuf);
|
||||
va_list args;
|
||||
va_start (args, fmt);
|
||||
glnx_real_set_prefix_error_va (*error, fmt, args);
|
||||
va_end (args);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
@@ -99,7 +108,7 @@ ot_gpgme_ctx_tmp_home_dir (gpgme_ctx_t gpgme_ctx,
|
||||
NULL, tmp_home_dir);
|
||||
if (gpg_error != GPG_ERR_NO_ERROR)
|
||||
{
|
||||
ot_gpgme_error_to_gio_error (gpg_error, error);
|
||||
ot_gpgme_throw (gpg_error, error, "gpgme_ctx_set_engine_info");
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -376,7 +385,6 @@ ot_gpgme_data_input (GInputStream *input_stream)
|
||||
if (gpg_error != GPG_ERR_NO_ERROR)
|
||||
{
|
||||
g_assert (gpgme_err_code (gpg_error) == GPG_ERR_ENOMEM);
|
||||
ot_gpgme_error_to_gio_error (gpg_error, NULL);
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
|
||||
@@ -399,7 +407,6 @@ ot_gpgme_data_output (GOutputStream *output_stream)
|
||||
if (gpg_error != GPG_ERR_NO_ERROR)
|
||||
{
|
||||
g_assert (gpgme_err_code (gpg_error) == GPG_ERR_ENOMEM);
|
||||
ot_gpgme_error_to_gio_error (gpg_error, NULL);
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
|
||||
@@ -416,11 +423,7 @@ ot_gpgme_new_ctx (const char *homedir,
|
||||
g_auto(gpgme_ctx_t) context = NULL;
|
||||
|
||||
if ((err = gpgme_new (&context)) != GPG_ERR_NO_ERROR)
|
||||
{
|
||||
ot_gpgme_error_to_gio_error (err, error);
|
||||
g_prefix_error (error, "Unable to create gpg context: ");
|
||||
return NULL;
|
||||
}
|
||||
return ot_gpgme_throw (err, error, "Unable to create gpg context"), NULL;
|
||||
|
||||
if (homedir != NULL)
|
||||
{
|
||||
@@ -430,12 +433,7 @@ ot_gpgme_new_ctx (const char *homedir,
|
||||
|
||||
if ((err = gpgme_ctx_set_engine_info (context, info->protocol, NULL, homedir))
|
||||
!= GPG_ERR_NO_ERROR)
|
||||
{
|
||||
ot_gpgme_error_to_gio_error (err, error);
|
||||
g_prefix_error (error, "Unable to set gpg homedir to '%s': ",
|
||||
homedir);
|
||||
return NULL;
|
||||
}
|
||||
return ot_gpgme_throw (err, error, "Unable to set gpg homedir to '%s'", homedir), NULL;
|
||||
}
|
||||
|
||||
return g_steal_pointer (&context);
|
||||
|
||||
@@ -30,7 +30,8 @@ G_DEFINE_AUTO_CLEANUP_FREE_FUNC(gpgme_data_t, gpgme_data_release, NULL)
|
||||
G_DEFINE_AUTO_CLEANUP_FREE_FUNC(gpgme_ctx_t, gpgme_release, NULL)
|
||||
G_DEFINE_AUTO_CLEANUP_FREE_FUNC(gpgme_key_t, gpgme_key_unref, NULL)
|
||||
|
||||
void ot_gpgme_error_to_gio_error (gpgme_error_t gpg_error, GError **error);
|
||||
gboolean ot_gpgme_throw (gpgme_error_t gpg_error, GError **error,
|
||||
const char *fmt, ...) G_GNUC_PRINTF (3, 4);
|
||||
|
||||
gboolean ot_gpgme_ctx_tmp_home_dir (gpgme_ctx_t gpgme_ctx,
|
||||
char **out_tmp_home_dir,
|
||||
|
||||
@@ -52,9 +52,6 @@ ot_admin_builtin_cleanup (int argc, char **argv, GCancellable *cancellable, GErr
|
||||
&sysroot, cancellable, error))
|
||||
goto out;
|
||||
|
||||
if (!ostree_sysroot_load (sysroot, cancellable, error))
|
||||
goto out;
|
||||
|
||||
if (!ostree_sysroot_cleanup (sysroot, cancellable, error))
|
||||
goto out;
|
||||
|
||||
|
||||
@@ -33,6 +33,9 @@
|
||||
#include <glib/gi18n.h>
|
||||
|
||||
static gboolean opt_retain;
|
||||
static gboolean opt_retain_pending;
|
||||
static gboolean opt_retain_rollback;
|
||||
static gboolean opt_not_as_default;
|
||||
static char **opt_kernel_argv;
|
||||
static char **opt_kernel_argv_append;
|
||||
static gboolean opt_kernel_proc_cmdline;
|
||||
@@ -47,7 +50,10 @@ static char *opt_origin_path;
|
||||
static GOptionEntry options[] = {
|
||||
{ "os", 0, 0, G_OPTION_ARG_STRING, &opt_osname, "Use a different operating system root than the current one", "OSNAME" },
|
||||
{ "origin-file", 0, 0, G_OPTION_ARG_FILENAME, &opt_origin_path, "Specify origin file", "FILENAME" },
|
||||
{ "retain", 0, 0, G_OPTION_ARG_NONE, &opt_retain, "Do not delete previous deployment", NULL },
|
||||
{ "retain", 0, 0, G_OPTION_ARG_NONE, &opt_retain, "Do not delete previous deployments", NULL },
|
||||
{ "retain-pending", 0, 0, G_OPTION_ARG_NONE, &opt_retain_pending, "Do not delete pending deployments", NULL },
|
||||
{ "retain-rollback", 0, 0, G_OPTION_ARG_NONE, &opt_retain_rollback, "Do not delete rollback deployments", NULL },
|
||||
{ "not-as-default", 0, 0, G_OPTION_ARG_NONE, &opt_not_as_default, "Append rather than prepend new deployment", NULL },
|
||||
{ "karg-proc-cmdline", 0, 0, G_OPTION_ARG_NONE, &opt_kernel_proc_cmdline, "Import current /proc/cmdline", NULL },
|
||||
{ "karg", 0, 0, G_OPTION_ARG_STRING_ARRAY, &opt_kernel_argv, "Set kernel argument, like root=/dev/sda1; this overrides any earlier argument with the same name", "NAME=VALUE" },
|
||||
{ "karg-append", 0, 0, G_OPTION_ARG_STRING_ARRAY, &opt_kernel_argv_append, "Append kernel argument; useful with e.g. console= that can be used multiple times", "NAME=VALUE" },
|
||||
@@ -57,64 +63,53 @@ static GOptionEntry options[] = {
|
||||
gboolean
|
||||
ot_admin_builtin_deploy (int argc, char **argv, GCancellable *cancellable, GError **error)
|
||||
{
|
||||
gboolean ret = FALSE;
|
||||
const char *refspec;
|
||||
g_autoptr(GOptionContext) context = NULL;
|
||||
g_autoptr(OstreeSysroot) sysroot = NULL;
|
||||
g_autoptr(GKeyFile) origin = NULL;
|
||||
g_autoptr(OstreeRepo) repo = NULL;
|
||||
g_autoptr(OstreeDeployment) new_deployment = NULL;
|
||||
g_autoptr(OstreeDeployment) merge_deployment = NULL;
|
||||
g_autofree char *revision = NULL;
|
||||
__attribute__((cleanup(_ostree_kernel_args_cleanup))) OstreeKernelArgs *kargs = NULL;
|
||||
|
||||
context = g_option_context_new ("REFSPEC - Checkout revision REFSPEC as the new default deployment");
|
||||
g_autoptr(GOptionContext) context =
|
||||
g_option_context_new ("REFSPEC - Checkout revision REFSPEC as the new default deployment");
|
||||
|
||||
g_autoptr(OstreeSysroot) sysroot = NULL;
|
||||
if (!ostree_admin_option_context_parse (context, options, &argc, &argv,
|
||||
OSTREE_ADMIN_BUILTIN_FLAG_SUPERUSER,
|
||||
&sysroot, cancellable, error))
|
||||
goto out;
|
||||
return FALSE;
|
||||
|
||||
if (argc < 2)
|
||||
{
|
||||
ot_util_usage_error (context, "REF/REV must be specified", error);
|
||||
goto out;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
refspec = argv[1];
|
||||
const char *refspec = argv[1];
|
||||
|
||||
if (!ostree_sysroot_load (sysroot, cancellable, error))
|
||||
goto out;
|
||||
|
||||
if (!ostree_sysroot_get_repo (sysroot, &repo, cancellable, error))
|
||||
goto out;
|
||||
OstreeRepo *repo = ostree_sysroot_repo (sysroot);
|
||||
|
||||
/* Find the currently booted deployment, if any; we will ensure it
|
||||
* is present in the new deployment list.
|
||||
*/
|
||||
if (!ot_admin_require_booted_deployment_or_osname (sysroot, opt_osname,
|
||||
cancellable, error))
|
||||
{
|
||||
g_prefix_error (error, "Looking for booted deployment: ");
|
||||
goto out;
|
||||
}
|
||||
return glnx_prefix_error (error, "Looking for booted deployment");
|
||||
|
||||
g_autoptr(GKeyFile) origin = NULL;
|
||||
if (opt_origin_path)
|
||||
{
|
||||
origin = g_key_file_new ();
|
||||
|
||||
|
||||
if (!g_key_file_load_from_file (origin, opt_origin_path, 0, error))
|
||||
goto out;
|
||||
return FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
origin = ostree_sysroot_origin_new_from_refspec (sysroot, refspec);
|
||||
}
|
||||
|
||||
g_autofree char *revision = NULL;
|
||||
if (!ostree_repo_resolve_rev (repo, refspec, FALSE, &revision, error))
|
||||
goto out;
|
||||
return FALSE;
|
||||
|
||||
merge_deployment = ostree_sysroot_get_merge_deployment (sysroot, opt_osname);
|
||||
g_autoptr(OstreeDeployment) merge_deployment =
|
||||
ostree_sysroot_get_merge_deployment (sysroot, opt_osname);
|
||||
|
||||
/* Here we perform cleanup of any leftover data from previous
|
||||
* partial failures. This avoids having to call
|
||||
@@ -124,10 +119,7 @@ ot_admin_builtin_deploy (int argc, char **argv, GCancellable *cancellable, GErro
|
||||
* we find it.
|
||||
*/
|
||||
if (!ostree_sysroot_prepare_cleanup (sysroot, cancellable, error))
|
||||
{
|
||||
g_prefix_error (error, "Performing initial cleanup: ");
|
||||
goto out;
|
||||
}
|
||||
return glnx_prefix_error (error, "Performing initial cleanup");
|
||||
|
||||
kargs = _ostree_kernel_args_new ();
|
||||
|
||||
@@ -137,7 +129,7 @@ ot_admin_builtin_deploy (int argc, char **argv, GCancellable *cancellable, GErro
|
||||
if (opt_kernel_proc_cmdline)
|
||||
{
|
||||
if (!_ostree_kernel_args_append_proc_cmdline (kargs, cancellable, error))
|
||||
goto out;
|
||||
return FALSE;
|
||||
}
|
||||
else if (merge_deployment)
|
||||
{
|
||||
@@ -157,24 +149,29 @@ ot_admin_builtin_deploy (int argc, char **argv, GCancellable *cancellable, GErro
|
||||
_ostree_kernel_args_append_argv (kargs, opt_kernel_argv_append);
|
||||
}
|
||||
|
||||
{
|
||||
g_auto(GStrv) kargs_strv = _ostree_kernel_args_to_strv (kargs);
|
||||
g_autoptr(OstreeDeployment) new_deployment = NULL;
|
||||
g_auto(GStrv) kargs_strv = _ostree_kernel_args_to_strv (kargs);
|
||||
if (!ostree_sysroot_deploy_tree (sysroot, opt_osname, revision, origin, merge_deployment,
|
||||
kargs_strv, &new_deployment, cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
if (!ostree_sysroot_deploy_tree (sysroot,
|
||||
opt_osname, revision, origin,
|
||||
merge_deployment, kargs_strv,
|
||||
&new_deployment,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
}
|
||||
OstreeSysrootSimpleWriteDeploymentFlags flags = 0;
|
||||
if (opt_retain)
|
||||
flags |= OSTREE_SYSROOT_SIMPLE_WRITE_DEPLOYMENT_FLAGS_RETAIN;
|
||||
else
|
||||
{
|
||||
if (opt_retain_pending)
|
||||
flags |= OSTREE_SYSROOT_SIMPLE_WRITE_DEPLOYMENT_FLAGS_RETAIN_PENDING;
|
||||
if (opt_retain_rollback)
|
||||
flags |= OSTREE_SYSROOT_SIMPLE_WRITE_DEPLOYMENT_FLAGS_RETAIN_ROLLBACK;
|
||||
}
|
||||
|
||||
if (!ostree_sysroot_simple_write_deployment (sysroot, opt_osname,
|
||||
new_deployment, merge_deployment,
|
||||
opt_retain ? OSTREE_SYSROOT_SIMPLE_WRITE_DEPLOYMENT_FLAGS_RETAIN : 0,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
if (opt_not_as_default)
|
||||
flags |= OSTREE_SYSROOT_SIMPLE_WRITE_DEPLOYMENT_FLAGS_NOT_DEFAULT;
|
||||
|
||||
ret = TRUE;
|
||||
out:
|
||||
return ret;
|
||||
if (!ostree_sysroot_simple_write_deployment (sysroot, opt_osname, new_deployment,
|
||||
merge_deployment, flags, cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
@@ -63,9 +63,6 @@ ot_admin_builtin_diff (int argc, char **argv, GCancellable *cancellable, GError
|
||||
OSTREE_ADMIN_BUILTIN_FLAG_SUPERUSER | OSTREE_ADMIN_BUILTIN_FLAG_UNLOCKED,
|
||||
&sysroot, cancellable, error))
|
||||
goto out;
|
||||
|
||||
if (!ostree_sysroot_load (sysroot, cancellable, error))
|
||||
goto out;
|
||||
|
||||
if (!ot_admin_require_booted_deployment_or_osname (sysroot, opt_osname,
|
||||
cancellable, error))
|
||||
|
||||
@@ -41,57 +41,51 @@ static GOptionEntry options[] = {
|
||||
gboolean
|
||||
ot_admin_builtin_init_fs (int argc, char **argv, GCancellable *cancellable, GError **error)
|
||||
{
|
||||
g_autoptr(GOptionContext) context = NULL;
|
||||
g_autoptr(OstreeSysroot) sysroot = NULL;
|
||||
gboolean ret = FALSE;
|
||||
glnx_fd_close int root_dfd = -1;
|
||||
g_autoptr(OstreeSysroot) target_sysroot = NULL;
|
||||
guint i;
|
||||
const char *normal_toplevels[] = {"boot", "dev", "home", "proc", "run", "sys"};
|
||||
|
||||
context = g_option_context_new ("PATH - Initialize a root filesystem");
|
||||
g_autoptr(GOptionContext) context = g_option_context_new ("PATH - Initialize a root filesystem");
|
||||
|
||||
if (!ostree_admin_option_context_parse (context, options, &argc, &argv,
|
||||
OSTREE_ADMIN_BUILTIN_FLAG_SUPERUSER | OSTREE_ADMIN_BUILTIN_FLAG_UNLOCKED,
|
||||
&sysroot, cancellable, error))
|
||||
goto out;
|
||||
OSTREE_ADMIN_BUILTIN_FLAG_SUPERUSER |
|
||||
OSTREE_ADMIN_BUILTIN_FLAG_UNLOCKED |
|
||||
OSTREE_ADMIN_BUILTIN_FLAG_NO_SYSROOT,
|
||||
NULL, cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
if (argc < 2)
|
||||
{
|
||||
ot_util_usage_error (context, "PATH must be specified", error);
|
||||
goto out;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!glnx_opendirat (AT_FDCWD, argv[1], TRUE, &root_dfd, error))
|
||||
goto out;
|
||||
{ g_autoptr(GFile) dir = g_file_new_for_path (argv[1]);
|
||||
target_sysroot = ostree_sysroot_new (dir);
|
||||
}
|
||||
const char *sysroot_path = argv[1];
|
||||
|
||||
for (i = 0; i < G_N_ELEMENTS(normal_toplevels); i++)
|
||||
glnx_fd_close int root_dfd = -1;
|
||||
if (!glnx_opendirat (AT_FDCWD, sysroot_path, TRUE, &root_dfd, error))
|
||||
return FALSE;
|
||||
|
||||
const char *normal_toplevels[] = {"boot", "dev", "home", "proc", "run", "sys"};
|
||||
for (guint i = 0; i < G_N_ELEMENTS (normal_toplevels); i++)
|
||||
{
|
||||
if (!glnx_shutil_mkdir_p_at (root_dfd, normal_toplevels[i], 0755,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
if (!glnx_shutil_mkdir_p_at (root_dfd, "root", 0700,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
return FALSE;
|
||||
|
||||
if (!glnx_shutil_mkdir_p_at (root_dfd, "tmp", 01777,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
return FALSE;
|
||||
if (fchmodat (root_dfd, "tmp", 01777, 0) == -1)
|
||||
{
|
||||
glnx_set_prefix_error_from_errno (error, "chmod: %s", "tmp");
|
||||
goto out;
|
||||
return FALSE;
|
||||
}
|
||||
g_autoptr(GFile) dir = g_file_new_for_path (sysroot_path);
|
||||
g_autoptr(OstreeSysroot) sysroot = ostree_sysroot_new (dir);
|
||||
if (!ostree_sysroot_ensure_initialized (sysroot, cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
if (!ostree_sysroot_ensure_initialized (target_sysroot, cancellable, error))
|
||||
goto out;
|
||||
|
||||
ret = TRUE;
|
||||
out:
|
||||
return ret;
|
||||
return TRUE;
|
||||
}
|
||||
@@ -65,10 +65,7 @@ ostree_admin_instutil_option_context_new_with_commands (void)
|
||||
gboolean
|
||||
ot_admin_builtin_instutil (int argc, char **argv, GCancellable *cancellable, GError **error)
|
||||
{
|
||||
gboolean ret = FALSE;
|
||||
OstreeAdminInstUtilCommand *subcommand;
|
||||
const char *subcommand_name = NULL;
|
||||
g_autofree char *prgname = NULL;
|
||||
int in, out;
|
||||
|
||||
for (in = 1, out = 1; in < argc; in++, out++)
|
||||
@@ -94,7 +91,7 @@ ot_admin_builtin_instutil (int argc, char **argv, GCancellable *cancellable, GEr
|
||||
|
||||
argc = out;
|
||||
|
||||
subcommand = admin_instutil_subcommands;
|
||||
OstreeAdminInstUtilCommand *subcommand = admin_instutil_subcommands;
|
||||
while (subcommand->name)
|
||||
{
|
||||
if (g_strcmp0 (subcommand_name, subcommand->name) == 0)
|
||||
@@ -104,14 +101,12 @@ ot_admin_builtin_instutil (int argc, char **argv, GCancellable *cancellable, GEr
|
||||
|
||||
if (!subcommand->name)
|
||||
{
|
||||
g_autoptr(GOptionContext) context = NULL;
|
||||
g_autofree char *help;
|
||||
|
||||
context = ostree_admin_instutil_option_context_new_with_commands ();
|
||||
g_autoptr(GOptionContext) context =
|
||||
ostree_admin_instutil_option_context_new_with_commands ();
|
||||
|
||||
/* This will not return for some options (e.g. --version). */
|
||||
if (ostree_admin_option_context_parse (context, NULL, &argc, &argv,
|
||||
OSTREE_ADMIN_BUILTIN_FLAG_SUPERUSER | OSTREE_ADMIN_BUILTIN_FLAG_UNLOCKED,
|
||||
OSTREE_ADMIN_BUILTIN_FLAG_NO_SYSROOT,
|
||||
NULL, cancellable, error))
|
||||
{
|
||||
if (subcommand_name == NULL)
|
||||
@@ -126,19 +121,16 @@ ot_admin_builtin_instutil (int argc, char **argv, GCancellable *cancellable, GEr
|
||||
}
|
||||
}
|
||||
|
||||
help = g_option_context_get_help (context, FALSE, NULL);
|
||||
g_autofree char *help = g_option_context_get_help (context, FALSE, NULL);
|
||||
g_printerr ("%s", help);
|
||||
|
||||
goto out;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
prgname = g_strdup_printf ("%s %s", g_get_prgname (), subcommand_name);
|
||||
g_autofree char *prgname = g_strdup_printf ("%s %s", g_get_prgname (), subcommand_name);
|
||||
g_set_prgname (prgname);
|
||||
|
||||
if (!subcommand->fn (argc, argv, cancellable, error))
|
||||
goto out;
|
||||
|
||||
ret = TRUE;
|
||||
out:
|
||||
return ret;
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
@@ -75,9 +75,6 @@ ot_admin_builtin_set_origin (int argc, char **argv, GCancellable *cancellable, G
|
||||
if (argc > 3)
|
||||
branch = argv[3];
|
||||
|
||||
if (!ostree_sysroot_load (sysroot, cancellable, error))
|
||||
goto out;
|
||||
|
||||
if (!ostree_sysroot_get_repo (sysroot, &repo, cancellable, error))
|
||||
goto out;
|
||||
|
||||
|
||||
@@ -108,9 +108,6 @@ ot_admin_builtin_status (int argc, char **argv, GCancellable *cancellable, GErro
|
||||
&sysroot, cancellable, error))
|
||||
goto out;
|
||||
|
||||
if (!ostree_sysroot_load (sysroot, cancellable, error))
|
||||
goto out;
|
||||
|
||||
if (!ostree_sysroot_get_repo (sysroot, &repo, cancellable, error))
|
||||
goto out;
|
||||
|
||||
|
||||
@@ -47,54 +47,38 @@ static GOptionEntry options[] = {
|
||||
gboolean
|
||||
ot_admin_builtin_switch (int argc, char **argv, GCancellable *cancellable, GError **error)
|
||||
{
|
||||
gboolean ret = FALSE;
|
||||
g_autoptr(GOptionContext) context = NULL;
|
||||
g_autoptr(GOptionContext) context =
|
||||
g_option_context_new ("REF - Construct new tree from REF and deploy it");
|
||||
g_autoptr(OstreeSysroot) sysroot = NULL;
|
||||
const char *new_provided_refspec = NULL;
|
||||
g_autoptr(OstreeRepo) repo = NULL;
|
||||
g_autofree char *origin_refspec = NULL;
|
||||
g_autofree char *origin_remote = NULL;
|
||||
g_autofree char *origin_ref = NULL;
|
||||
g_autofree char *new_remote = NULL;
|
||||
g_autofree char *new_ref = NULL;
|
||||
g_autofree char *new_refspec = NULL;
|
||||
const char* remote;
|
||||
g_autoptr(OstreeSysrootUpgrader) upgrader = NULL;
|
||||
g_autoptr(OstreeAsyncProgress) progress = NULL;
|
||||
gboolean changed;
|
||||
GKeyFile *old_origin;
|
||||
g_autoptr(GKeyFile) new_origin = NULL;
|
||||
|
||||
context = g_option_context_new ("REF - Construct new tree from REF and deploy it");
|
||||
|
||||
if (!ostree_admin_option_context_parse (context, options, &argc, &argv,
|
||||
OSTREE_ADMIN_BUILTIN_FLAG_SUPERUSER,
|
||||
&sysroot, cancellable, error))
|
||||
goto out;
|
||||
return FALSE;
|
||||
|
||||
if (argc < 2)
|
||||
{
|
||||
ot_util_usage_error (context, "REF must be specified", error);
|
||||
goto out;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
new_provided_refspec = argv[1];
|
||||
const char *new_provided_refspec = argv[1];
|
||||
|
||||
if (!ostree_sysroot_load (sysroot, cancellable, error))
|
||||
goto out;
|
||||
|
||||
upgrader = ostree_sysroot_upgrader_new_for_os_with_flags (sysroot, opt_osname,
|
||||
OSTREE_SYSROOT_UPGRADER_FLAGS_IGNORE_UNCONFIGURED,
|
||||
cancellable, error);
|
||||
g_autoptr(OstreeSysrootUpgrader) upgrader =
|
||||
ostree_sysroot_upgrader_new_for_os_with_flags (sysroot, opt_osname,
|
||||
OSTREE_SYSROOT_UPGRADER_FLAGS_IGNORE_UNCONFIGURED,
|
||||
cancellable, error);
|
||||
if (!upgrader)
|
||||
goto out;
|
||||
return FALSE;
|
||||
|
||||
old_origin = ostree_sysroot_upgrader_get_origin (upgrader);
|
||||
origin_refspec = g_key_file_get_string (old_origin, "origin", "refspec", NULL);
|
||||
|
||||
GKeyFile *old_origin = ostree_sysroot_upgrader_get_origin (upgrader);
|
||||
g_autofree char *origin_refspec = g_key_file_get_string (old_origin, "origin", "refspec", NULL);
|
||||
g_autofree char *origin_remote = NULL;
|
||||
g_autofree char *origin_ref = NULL;
|
||||
if (!ostree_parse_refspec (origin_refspec, &origin_remote, &origin_ref, error))
|
||||
goto out;
|
||||
return FALSE;
|
||||
|
||||
g_autofree char *new_remote = NULL;
|
||||
g_autofree char *new_ref = NULL;
|
||||
/* Allow just switching remotes */
|
||||
if (g_str_has_suffix (new_provided_refspec, ":"))
|
||||
{
|
||||
@@ -105,14 +89,11 @@ ot_admin_builtin_switch (int argc, char **argv, GCancellable *cancellable, GErro
|
||||
else
|
||||
{
|
||||
if (!ostree_parse_refspec (new_provided_refspec, &new_remote, &new_ref, error))
|
||||
goto out;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!new_remote)
|
||||
remote = origin_remote;
|
||||
else
|
||||
remote = new_remote;
|
||||
|
||||
const char* remote = new_remote ?: origin_remote;
|
||||
g_autofree char *new_refspec = NULL;
|
||||
if (remote)
|
||||
new_refspec = g_strconcat (remote, ":", new_ref, NULL);
|
||||
else
|
||||
@@ -122,54 +103,52 @@ ot_admin_builtin_switch (int argc, char **argv, GCancellable *cancellable, GErro
|
||||
{
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||
"Old and new refs are equal: %s", new_refspec);
|
||||
goto out;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
new_origin = ostree_sysroot_origin_new_from_refspec (sysroot, new_refspec);
|
||||
g_autoptr(GKeyFile) new_origin = ostree_sysroot_origin_new_from_refspec (sysroot, new_refspec);
|
||||
if (!ostree_sysroot_upgrader_set_origin (upgrader, new_origin, cancellable, error))
|
||||
goto out;
|
||||
return FALSE;
|
||||
|
||||
{ g_auto(GLnxConsoleRef) console = { 0, };
|
||||
glnx_console_lock (&console);
|
||||
|
||||
g_autoptr(OstreeAsyncProgress) progress = NULL;
|
||||
if (console.is_tty)
|
||||
progress = ostree_async_progress_new_and_connect (ostree_repo_pull_default_console_progress_changed, &console);
|
||||
|
||||
/* Always allow older...there's not going to be a chronological
|
||||
* relationship necessarily.
|
||||
*/
|
||||
gboolean changed;
|
||||
if (!ostree_sysroot_upgrader_pull (upgrader, 0,
|
||||
OSTREE_SYSROOT_UPGRADER_PULL_FLAGS_ALLOW_OLDER,
|
||||
progress, &changed,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
return FALSE;
|
||||
|
||||
if (progress)
|
||||
ostree_async_progress_finish (progress);
|
||||
}
|
||||
|
||||
if (!ostree_sysroot_upgrader_deploy (upgrader, cancellable, error))
|
||||
goto out;
|
||||
|
||||
if (!ostree_sysroot_get_repo (sysroot, &repo, cancellable, error))
|
||||
goto out;
|
||||
return FALSE;
|
||||
|
||||
OstreeRepo *repo = ostree_sysroot_repo (sysroot);
|
||||
if (!ostree_repo_prepare_transaction (repo, NULL, cancellable, error))
|
||||
goto out;
|
||||
return FALSE;
|
||||
|
||||
g_print ("Deleting ref '%s:%s'\n", origin_remote, origin_ref);
|
||||
ostree_repo_transaction_set_ref (repo, origin_remote, origin_ref, NULL);
|
||||
|
||||
|
||||
if (!ostree_repo_commit_transaction (repo, NULL, cancellable, error))
|
||||
goto out;
|
||||
|
||||
return FALSE;
|
||||
|
||||
if (opt_reboot)
|
||||
{
|
||||
if (!ot_admin_execve_reboot (sysroot, error))
|
||||
goto out;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
ret = TRUE;
|
||||
out:
|
||||
return ret;
|
||||
return TRUE;
|
||||
}
|
||||
@@ -60,8 +60,6 @@ ot_admin_builtin_undeploy (int argc, char **argv, GCancellable *cancellable, GEr
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!ostree_sysroot_load (sysroot, cancellable, error))
|
||||
return FALSE;
|
||||
current_deployments = ostree_sysroot_get_deployments (sysroot);
|
||||
|
||||
deploy_index_str = argv[1];
|
||||
|
||||
@@ -65,9 +65,6 @@ ot_admin_builtin_unlock (int argc, char **argv, GCancellable *cancellable, GErro
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!ostree_sysroot_load (sysroot, cancellable, error))
|
||||
goto out;
|
||||
|
||||
booted_deployment = ostree_sysroot_get_booted_deployment (sysroot);
|
||||
if (!booted_deployment)
|
||||
{
|
||||
|
||||
@@ -57,44 +57,34 @@ static GOptionEntry options[] = {
|
||||
gboolean
|
||||
ot_admin_builtin_upgrade (int argc, char **argv, GCancellable *cancellable, GError **error)
|
||||
{
|
||||
gboolean ret = FALSE;
|
||||
g_autoptr(GOptionContext) context = NULL;
|
||||
g_autoptr(GOptionContext) context = g_option_context_new ("Construct new tree from current origin and deploy it, if it changed");
|
||||
|
||||
g_autoptr(OstreeSysroot) sysroot = NULL;
|
||||
g_autoptr(OstreeSysrootUpgrader) upgrader = NULL;
|
||||
g_autoptr(GKeyFile) origin = NULL;
|
||||
g_autoptr(OstreeAsyncProgress) progress = NULL;
|
||||
gboolean changed;
|
||||
OstreeSysrootUpgraderPullFlags upgraderpullflags = 0;
|
||||
|
||||
context = g_option_context_new ("Construct new tree from current origin and deploy it, if it changed");
|
||||
|
||||
if (!ostree_admin_option_context_parse (context, options, &argc, &argv,
|
||||
OSTREE_ADMIN_BUILTIN_FLAG_SUPERUSER,
|
||||
&sysroot, cancellable, error))
|
||||
goto out;
|
||||
return FALSE;
|
||||
|
||||
if (opt_pull_only && opt_deploy_only)
|
||||
{
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||
"Cannot simultaneously specify --pull-only and --deploy-only");
|
||||
goto out;
|
||||
return FALSE;
|
||||
}
|
||||
else if (opt_pull_only && opt_reboot)
|
||||
{
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||
"Cannot simultaneously specify --pull-only and --reboot");
|
||||
goto out;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!ostree_sysroot_load (sysroot, cancellable, error))
|
||||
goto out;
|
||||
|
||||
upgrader = ostree_sysroot_upgrader_new_for_os (sysroot, opt_osname,
|
||||
cancellable, error);
|
||||
g_autoptr(OstreeSysrootUpgrader) upgrader =
|
||||
ostree_sysroot_upgrader_new_for_os (sysroot, opt_osname,
|
||||
cancellable, error);
|
||||
if (!upgrader)
|
||||
goto out;
|
||||
return FALSE;
|
||||
|
||||
origin = ostree_sysroot_upgrader_dup_origin (upgrader);
|
||||
g_autoptr(GKeyFile) origin = ostree_sysroot_upgrader_dup_origin (upgrader);
|
||||
if (origin != NULL)
|
||||
{
|
||||
gboolean origin_changed = FALSE;
|
||||
@@ -122,16 +112,19 @@ ot_admin_builtin_upgrade (int argc, char **argv, GCancellable *cancellable, GErr
|
||||
{
|
||||
/* XXX GCancellable parameter is not used. */
|
||||
if (!ostree_sysroot_upgrader_set_origin (upgrader, origin, NULL, error))
|
||||
goto out;
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
gboolean changed;
|
||||
OstreeSysrootUpgraderPullFlags upgraderpullflags = 0;
|
||||
if (opt_deploy_only)
|
||||
upgraderpullflags |= OSTREE_SYSROOT_UPGRADER_PULL_FLAGS_SYNTHETIC;
|
||||
|
||||
{ g_auto(GLnxConsoleRef) console = { 0, };
|
||||
glnx_console_lock (&console);
|
||||
|
||||
g_autoptr(OstreeAsyncProgress) progress = NULL;
|
||||
if (console.is_tty)
|
||||
progress = ostree_async_progress_new_and_connect (ostree_repo_pull_default_console_progress_changed, &console);
|
||||
|
||||
@@ -152,7 +145,7 @@ ot_admin_builtin_upgrade (int argc, char **argv, GCancellable *cancellable, GErr
|
||||
*/
|
||||
if (opt_pull_only)
|
||||
(void) ostree_sysroot_cleanup (sysroot, NULL, NULL);
|
||||
goto out;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (progress)
|
||||
@@ -168,17 +161,15 @@ ot_admin_builtin_upgrade (int argc, char **argv, GCancellable *cancellable, GErr
|
||||
if (!opt_pull_only)
|
||||
{
|
||||
if (!ostree_sysroot_upgrader_deploy (upgrader, cancellable, error))
|
||||
goto out;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (opt_reboot)
|
||||
{
|
||||
if (!ot_admin_execve_reboot (sysroot, error))
|
||||
goto out;
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
ret = TRUE;
|
||||
out:
|
||||
return ret;
|
||||
return TRUE;
|
||||
}
|
||||
@@ -151,13 +151,15 @@ ot_admin_sysroot_lock (OstreeSysroot *sysroot,
|
||||
gboolean
|
||||
ot_admin_execve_reboot (OstreeSysroot *sysroot, GError **error)
|
||||
{
|
||||
g_autoptr(GFile) real_sysroot = g_file_new_for_path ("/");
|
||||
OstreeDeployment *booted = ostree_sysroot_get_booted_deployment (sysroot);
|
||||
|
||||
if (g_file_equal (ostree_sysroot_get_path (sysroot), real_sysroot))
|
||||
{
|
||||
if (execlp ("systemctl", "systemctl", "reboot", NULL) < 0)
|
||||
return glnx_throw_errno (error);
|
||||
}
|
||||
/* If the sysroot isn't booted, we shouldn't reboot, even if somehow the user
|
||||
* asked for it; might accidentally be specified in a build script, etc.
|
||||
*/
|
||||
if (!booted)
|
||||
return TRUE;
|
||||
|
||||
if (execlp ("systemctl", "systemctl", "reboot", NULL) < 0)
|
||||
return glnx_throw_errno_prefix (error, "execve(systemctl reboot)");
|
||||
return TRUE;
|
||||
}
|
||||
@@ -53,9 +53,6 @@ ot_admin_instutil_builtin_grub2_generate (int argc, char **argv, GCancellable *c
|
||||
&sysroot, cancellable, error))
|
||||
goto out;
|
||||
|
||||
if (!ostree_sysroot_load (sysroot, cancellable, error))
|
||||
goto out;
|
||||
|
||||
if (argc >= 2)
|
||||
{
|
||||
bootversion = (guint) g_ascii_strtoull (argv[1], NULL, 10);
|
||||
|
||||
@@ -204,9 +204,6 @@ ot_admin_instutil_builtin_selinux_ensure_labeled (int argc, char **argv, GCancel
|
||||
&sysroot, cancellable, error))
|
||||
goto out;
|
||||
|
||||
if (!ostree_sysroot_load (sysroot, cancellable, error))
|
||||
goto out;
|
||||
|
||||
deployments = ostree_sysroot_get_deployments (sysroot);
|
||||
if (deployments->len == 0)
|
||||
{
|
||||
|
||||
@@ -66,9 +66,6 @@ ot_admin_instutil_builtin_set_kargs (int argc, char **argv, GCancellable *cancel
|
||||
&sysroot, cancellable, error))
|
||||
goto out;
|
||||
|
||||
if (!ostree_sysroot_load (sysroot, cancellable, error))
|
||||
goto out;
|
||||
|
||||
deployments = ostree_sysroot_get_deployments (sysroot);
|
||||
if (deployments->len == 0)
|
||||
{
|
||||
|
||||
@@ -126,7 +126,7 @@ ostree_builtin_admin (int argc, char **argv, GCancellable *cancellable, GError *
|
||||
|
||||
/* This will not return for some options (e.g. --version). */
|
||||
if (ostree_admin_option_context_parse (context, NULL, &argc, &argv,
|
||||
OSTREE_ADMIN_BUILTIN_FLAG_NONE | OSTREE_ADMIN_BUILTIN_FLAG_UNLOCKED,
|
||||
OSTREE_ADMIN_BUILTIN_FLAG_NO_SYSROOT,
|
||||
NULL, cancellable, error))
|
||||
{
|
||||
if (subcommand_name == NULL)
|
||||
|
||||
@@ -37,6 +37,7 @@ static gboolean opt_disable_cache;
|
||||
static char *opt_subpath;
|
||||
static gboolean opt_union;
|
||||
static gboolean opt_union_add;
|
||||
static gboolean opt_union_identical;
|
||||
static gboolean opt_whiteouts;
|
||||
static gboolean opt_from_stdin;
|
||||
static char *opt_from_file;
|
||||
@@ -72,6 +73,7 @@ static GOptionEntry options[] = {
|
||||
{ "subpath", 0, 0, G_OPTION_ARG_FILENAME, &opt_subpath, "Checkout sub-directory PATH", "PATH" },
|
||||
{ "union", 0, 0, G_OPTION_ARG_NONE, &opt_union, "Keep existing directories, overwrite existing files", NULL },
|
||||
{ "union-add", 0, 0, G_OPTION_ARG_NONE, &opt_union_add, "Keep existing files/directories, only add new", NULL },
|
||||
{ "union-identical", 0, 0, G_OPTION_ARG_NONE, &opt_union_identical, "When layering checkouts, error out if a file would be replaced with a different version, but add new files and directories", NULL },
|
||||
{ "whiteouts", 0, 0, G_OPTION_ARG_NONE, &opt_whiteouts, "Process 'whiteout' (Docker style) entries", NULL },
|
||||
{ "allow-noent", 0, 0, G_OPTION_ARG_NONE, &opt_allow_noent, "Do nothing if specified path does not exist", NULL },
|
||||
{ "from-stdin", 0, 0, G_OPTION_ARG_NONE, &opt_from_stdin, "Process many checkouts from standard input", NULL },
|
||||
@@ -99,7 +101,7 @@ 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_add || opt_force_copy || opt_bareuseronly_dirs || opt_union_identical)
|
||||
{
|
||||
OstreeRepoCheckoutAtOptions options = { 0, };
|
||||
|
||||
@@ -112,6 +114,18 @@ process_one_checkout (OstreeRepo *repo,
|
||||
"Cannot specify both --union and --union-add");
|
||||
goto out;
|
||||
}
|
||||
if (opt_union && opt_union_identical)
|
||||
{
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||
"Cannot specify both --union and --union-identical");
|
||||
goto out;
|
||||
}
|
||||
if (opt_union_add && opt_union_identical)
|
||||
{
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||
"Cannot specify both --union-add and --union-identical ");
|
||||
goto out;
|
||||
}
|
||||
if (opt_require_hardlinks && opt_force_copy)
|
||||
{
|
||||
glnx_throw (error, "Cannot specify both --require-hardlinks and --force-copy");
|
||||
@@ -121,6 +135,16 @@ process_one_checkout (OstreeRepo *repo,
|
||||
options.overwrite_mode = OSTREE_REPO_CHECKOUT_OVERWRITE_UNION_FILES;
|
||||
else if (opt_union_add)
|
||||
options.overwrite_mode = OSTREE_REPO_CHECKOUT_OVERWRITE_ADD_FILES;
|
||||
else if (opt_union_identical)
|
||||
{
|
||||
if (!opt_require_hardlinks)
|
||||
{
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||
"--union-identical requires --require-hardlinks");
|
||||
goto out;
|
||||
}
|
||||
options.overwrite_mode = OSTREE_REPO_CHECKOUT_OVERWRITE_UNION_IDENTICAL;
|
||||
}
|
||||
if (opt_whiteouts)
|
||||
options.process_whiteouts = TRUE;
|
||||
if (subpath)
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include "ot-tool-util.h"
|
||||
#include "parse-datetime.h"
|
||||
#include "ostree-repo-private.h"
|
||||
#include "ostree-libarchive-private.h"
|
||||
|
||||
static char *opt_subject;
|
||||
static char *opt_body;
|
||||
@@ -46,7 +47,9 @@ static char **opt_detached_metadata_strings;
|
||||
static gboolean opt_link_checkout_speedup;
|
||||
static gboolean opt_skip_if_unchanged;
|
||||
static gboolean opt_tar_autocreate_parents;
|
||||
static char *opt_tar_pathname_filter;
|
||||
static gboolean opt_no_xattrs;
|
||||
static char *opt_selinux_policy;
|
||||
static gboolean opt_canonical_permissions;
|
||||
static char **opt_trees;
|
||||
static gint opt_owner_uid = -1;
|
||||
@@ -93,8 +96,10 @@ static GOptionEntry options[] = {
|
||||
{ "owner-gid", 0, 0, G_OPTION_ARG_INT, &opt_owner_gid, "Set file ownership group id", "GID" },
|
||||
{ "canonical-permissions", 0, 0, G_OPTION_ARG_NONE, &opt_canonical_permissions, "Canonicalize permissions in the same way bare-user does for hardlinked files", NULL },
|
||||
{ "no-xattrs", 0, 0, G_OPTION_ARG_NONE, &opt_no_xattrs, "Do not import extended attributes", NULL },
|
||||
{ "selinux-policy", 0, 0, G_OPTION_ARG_FILENAME, &opt_selinux_policy, "Set SELinux labels based on policy in root filesystem PATH (may be /)", "PATH" },
|
||||
{ "link-checkout-speedup", 0, 0, G_OPTION_ARG_NONE, &opt_link_checkout_speedup, "Optimize for commits of trees composed of hardlinks into the repository", NULL },
|
||||
{ "tar-autocreate-parents", 0, 0, G_OPTION_ARG_NONE, &opt_tar_autocreate_parents, "When loading tar archives, automatically create parent directories as needed", NULL },
|
||||
{ "tar-pathname-filter", 0, 0, G_OPTION_ARG_STRING, &opt_tar_pathname_filter, "When loading tar archives, use REGEX,REPLACEMENT against path names", "REGEX,REPLACEMENT" },
|
||||
{ "skip-if-unchanged", 0, 0, G_OPTION_ARG_NONE, &opt_skip_if_unchanged, "If the contents are unchanged from previous commit, do nothing", NULL },
|
||||
{ "statoverride", 0, 0, G_OPTION_ARG_FILENAME, &opt_statoverride_file, "File containing list of modifications to make to permissions", "PATH" },
|
||||
{ "skip-list", 0, 0, G_OPTION_ARG_FILENAME, &opt_skiplist_file, "File containing list of files to skip", "PATH" },
|
||||
@@ -219,6 +224,28 @@ commit_filter (OstreeRepo *self,
|
||||
return OSTREE_REPO_COMMIT_FILTER_ALLOW;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
GRegex *regex;
|
||||
const char *replacement;
|
||||
} TranslatePathnameData;
|
||||
|
||||
/* Implement --tar-pathname-filter */
|
||||
static char *
|
||||
handle_translate_pathname (OstreeRepo *repo,
|
||||
const struct stat *stbuf,
|
||||
const char *path,
|
||||
gpointer user_data)
|
||||
{
|
||||
TranslatePathnameData *tpdata = user_data;
|
||||
g_autoptr(GError) tmp_error = NULL;
|
||||
char *ret =
|
||||
g_regex_replace (tpdata->regex, path, -1, 0,
|
||||
tpdata->replacement, 0, &tmp_error);
|
||||
g_assert_no_error (tmp_error);
|
||||
g_assert (ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
commit_editor (OstreeRepo *repo,
|
||||
const char *branch,
|
||||
@@ -395,6 +422,7 @@ ostree_builtin_commit (int argc, char **argv, GCancellable *cancellable, GError
|
||||
g_autoptr(GHashTable) mode_overrides = NULL;
|
||||
g_autoptr(GHashTable) skip_list = NULL;
|
||||
OstreeRepoCommitModifierFlags flags = 0;
|
||||
g_autoptr(OstreeSePolicy) policy = NULL;
|
||||
OstreeRepoCommitModifier *modifier = NULL;
|
||||
OstreeRepoTransactionStats stats;
|
||||
struct CommitFilterData filter_data = { 0, };
|
||||
@@ -459,12 +487,26 @@ ostree_builtin_commit (int argc, char **argv, GCancellable *cancellable, GError
|
||||
|| opt_owner_gid >= 0
|
||||
|| opt_statoverride_file != NULL
|
||||
|| opt_skiplist_file != NULL
|
||||
|| opt_no_xattrs)
|
||||
|| opt_no_xattrs
|
||||
|| opt_selinux_policy)
|
||||
{
|
||||
filter_data.mode_adds = mode_adds;
|
||||
filter_data.skip_list = skip_list;
|
||||
modifier = ostree_repo_commit_modifier_new (flags, commit_filter,
|
||||
&filter_data, NULL);
|
||||
if (opt_selinux_policy)
|
||||
{
|
||||
glnx_fd_close int rootfs_dfd = -1;
|
||||
if (!glnx_opendirat (AT_FDCWD, opt_selinux_policy, TRUE, &rootfs_dfd, error))
|
||||
{
|
||||
g_prefix_error (error, "selinux-policy: ");
|
||||
goto out;
|
||||
}
|
||||
policy = ostree_sepolicy_new_at (rootfs_dfd, cancellable, error);
|
||||
if (!policy)
|
||||
goto out;
|
||||
ostree_repo_commit_modifier_set_sepolicy (modifier, policy);
|
||||
}
|
||||
}
|
||||
|
||||
if (opt_parent)
|
||||
@@ -551,11 +593,50 @@ ostree_builtin_commit (int argc, char **argv, GCancellable *cancellable, GError
|
||||
}
|
||||
else if (strcmp (tree_type, "tar") == 0)
|
||||
{
|
||||
object_to_commit = g_file_new_for_path (tree);
|
||||
if (!ostree_repo_write_archive_to_mtree (repo, object_to_commit, mtree, modifier,
|
||||
opt_tar_autocreate_parents,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
if (!opt_tar_pathname_filter)
|
||||
{
|
||||
object_to_commit = g_file_new_for_path (tree);
|
||||
if (!ostree_repo_write_archive_to_mtree (repo, object_to_commit, mtree, modifier,
|
||||
opt_tar_autocreate_parents,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef HAVE_LIBARCHIVE
|
||||
const char *comma = strchr (opt_tar_pathname_filter, ',');
|
||||
if (!comma)
|
||||
{
|
||||
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||
"Missing ',' in --tar-pathname-filter");
|
||||
goto out;
|
||||
}
|
||||
const char *replacement = comma + 1;
|
||||
g_autofree char *regexp_text = g_strndup (opt_tar_pathname_filter, comma - opt_tar_pathname_filter);
|
||||
/* Use new API if we have a pathname filter */
|
||||
OstreeRepoImportArchiveOptions opts = { 0, };
|
||||
opts.autocreate_parents = opt_tar_autocreate_parents;
|
||||
opts.translate_pathname = handle_translate_pathname;
|
||||
g_autoptr(GRegex) regexp = g_regex_new (regexp_text, 0, 0, error);
|
||||
TranslatePathnameData tpdata = { regexp, replacement };
|
||||
if (!regexp)
|
||||
{
|
||||
g_prefix_error (error, "--tar-pathname-filter: ");
|
||||
goto out;
|
||||
}
|
||||
opts.translate_pathname_user_data = &tpdata;
|
||||
g_autoptr(OtAutoArchiveRead) archive = ot_open_archive_read (tree, error);
|
||||
if (!archive)
|
||||
goto out;
|
||||
if (!ostree_repo_import_archive_to_mtree (repo, &opts, archive, mtree,
|
||||
modifier, cancellable, error))
|
||||
goto out;
|
||||
}
|
||||
#else
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
|
||||
"This version of ostree is not compiled with libarchive support");
|
||||
return FALSE;
|
||||
#endif
|
||||
}
|
||||
else if (strcmp (tree_type, "ref") == 0)
|
||||
{
|
||||
@@ -699,7 +780,7 @@ ostree_builtin_commit (int argc, char **argv, GCancellable *cancellable, GError
|
||||
if (!ostree_repo_commit_transaction (repo, &stats, cancellable, error))
|
||||
goto out;
|
||||
|
||||
/* The default for this option is FALSE, even for archive-z2 repos,
|
||||
/* The default for this option is FALSE, even for archive repos,
|
||||
* because ostree supports multiple processes committing to the same
|
||||
* repo (but different refs) concurrently, and in fact gnome-continuous
|
||||
* actually does this. In that context it's best to update the summary
|
||||
|
||||
@@ -69,7 +69,7 @@ ostree_builtin_config (int argc, char **argv, GCancellable *cancellable, GError
|
||||
g_autofree char *key = NULL;
|
||||
GKeyFile *config = NULL;
|
||||
|
||||
context = g_option_context_new ("- Change configuration settings");
|
||||
context = g_option_context_new ("(get KEY|set KEY VALUE) - Change repo configuration settings");
|
||||
|
||||
if (!ostree_option_context_parse (context, options, &argc, &argv, OSTREE_BUILTIN_FLAG_NONE, &repo, cancellable, error))
|
||||
goto out;
|
||||
|
||||
@@ -252,6 +252,7 @@ ostree_builtin_fsck (int argc, char **argv, GCancellable *cancellable, GError **
|
||||
|
||||
g_autoptr(GHashTable) all_collection_refs = NULL; /* (element-type OstreeCollectionRef utf8) */
|
||||
if (!ostree_repo_list_collection_refs (repo, NULL, &all_collection_refs,
|
||||
OSTREE_REPO_LIST_REFS_EXT_EXCLUDE_REMOTES,
|
||||
cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ static char *opt_collection_id = NULL;
|
||||
*/
|
||||
|
||||
static GOptionEntry options[] = {
|
||||
{ "mode", 0, 0, G_OPTION_ARG_STRING, &opt_mode, "Initialize repository in given mode (bare, archive-z2)", NULL },
|
||||
{ "mode", 0, 0, G_OPTION_ARG_STRING, &opt_mode, "Initialize repository in given mode (bare, archive)", NULL },
|
||||
#ifdef OSTREE_ENABLE_EXPERIMENTAL_API
|
||||
{ "collection-id", 0, 0, G_OPTION_ARG_STRING, &opt_collection_id,
|
||||
"Globally unique ID for this repository as an collection of refs for redistribution to other repositories", "COLLECTION-ID" },
|
||||
|
||||
@@ -59,57 +59,40 @@ delete_commit (OstreeRepo *repo, const char *commit_to_delete, GCancellable *can
|
||||
#ifdef OSTREE_ENABLE_EXPERIMENTAL_API
|
||||
g_autoptr(GHashTable) collection_refs = NULL; /* (element-type OstreeCollectionRef utf8) */
|
||||
#endif /* OSTREE_ENABLE_EXPERIMENTAL_API */
|
||||
GHashTableIter hashiter;
|
||||
gpointer hashkey, hashvalue;
|
||||
gboolean ret = FALSE;
|
||||
|
||||
/* Check refs which are not in a collection. */
|
||||
if (!ostree_repo_list_refs (repo, NULL, &refs, cancellable, error))
|
||||
goto out;
|
||||
return FALSE;
|
||||
|
||||
g_hash_table_iter_init (&hashiter, refs);
|
||||
while (g_hash_table_iter_next (&hashiter, &hashkey, &hashvalue))
|
||||
GLNX_HASH_TABLE_FOREACH_KV(refs, const char *, ref, const char *, commit)
|
||||
{
|
||||
const char *ref = hashkey;
|
||||
const char *commit = hashvalue;
|
||||
if (g_strcmp0 (commit_to_delete, commit) == 0)
|
||||
{
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||
"Commit '%s' is referenced by '%s'", commit_to_delete, ref);
|
||||
goto out;
|
||||
}
|
||||
return glnx_throw (error, "Commit '%s' is referenced by '%s'", commit_to_delete, ref);
|
||||
}
|
||||
|
||||
#ifdef OSTREE_ENABLE_EXPERIMENTAL_API
|
||||
/* And check refs which *are* in a collection. */
|
||||
if (!ostree_repo_list_collection_refs (repo, NULL, &collection_refs, cancellable, error))
|
||||
goto out;
|
||||
if (!ostree_repo_list_collection_refs (repo, NULL, &collection_refs,
|
||||
OSTREE_REPO_LIST_REFS_EXT_EXCLUDE_REMOTES,
|
||||
cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
g_hash_table_iter_init (&hashiter, collection_refs);
|
||||
while (g_hash_table_iter_next (&hashiter, &hashkey, &hashvalue))
|
||||
GLNX_HASH_TABLE_FOREACH_KV (collection_refs, const OstreeCollectionRef*, ref,
|
||||
const char *, commit)
|
||||
{
|
||||
const OstreeCollectionRef *ref = hashkey;
|
||||
const char *commit = hashvalue;
|
||||
if (g_strcmp0 (commit_to_delete, commit) == 0)
|
||||
{
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||
"Commit '%s' is referenced by (%s, %s)",
|
||||
commit_to_delete, ref->collection_id, ref->ref_name);
|
||||
goto out;
|
||||
}
|
||||
return glnx_throw (error, "Commit '%s' is referenced by (%s, %s)",
|
||||
commit_to_delete, ref->collection_id, ref->ref_name);
|
||||
}
|
||||
#endif /* OSTREE_ENABLE_EXPERIMENTAL_API */
|
||||
|
||||
if (!ot_enable_tombstone_commits (repo, error))
|
||||
goto out;
|
||||
return FALSE;
|
||||
|
||||
if (!ostree_repo_delete_object (repo, OSTREE_OBJECT_TYPE_COMMIT, commit_to_delete, cancellable, error))
|
||||
goto out;
|
||||
return FALSE;
|
||||
|
||||
ret = TRUE;
|
||||
|
||||
out:
|
||||
return ret;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
@@ -119,7 +102,6 @@ traverse_keep_younger_than (OstreeRepo *repo, const char *checksum,
|
||||
GCancellable *cancellable, GError **error)
|
||||
{
|
||||
g_autofree char *next_checksum = g_strdup (checksum);
|
||||
g_autoptr(GVariant) commit = NULL;
|
||||
|
||||
/* This is the first commit in our loop, which has a ref pointing to it. We
|
||||
* don't want to auto-prune it.
|
||||
@@ -130,16 +112,14 @@ traverse_keep_younger_than (OstreeRepo *repo, const char *checksum,
|
||||
|
||||
while (TRUE)
|
||||
{
|
||||
guint64 commit_timestamp;
|
||||
|
||||
g_autoptr(GVariant) commit = NULL;
|
||||
if (!ostree_repo_load_variant_if_exists (repo, OSTREE_OBJECT_TYPE_COMMIT, next_checksum,
|
||||
&commit, error))
|
||||
return FALSE;
|
||||
|
||||
if (!commit)
|
||||
break; /* This commit was pruned, so we're done */
|
||||
|
||||
commit_timestamp = ostree_commit_get_timestamp (commit);
|
||||
guint64 commit_timestamp = ostree_commit_get_timestamp (commit);
|
||||
/* Is this commit newer than our --keep-younger-than spec? */
|
||||
if (commit_timestamp >= ts->tv_sec)
|
||||
{
|
||||
@@ -165,22 +145,13 @@ traverse_keep_younger_than (OstreeRepo *repo, const char *checksum,
|
||||
gboolean
|
||||
ostree_builtin_prune (int argc, char **argv, GCancellable *cancellable, GError **error)
|
||||
{
|
||||
gboolean ret = FALSE;
|
||||
g_autoptr(GOptionContext) context = NULL;
|
||||
g_autoptr(GOptionContext) context = g_option_context_new ("- Search for unreachable objects");
|
||||
g_autoptr(OstreeRepo) repo = NULL;
|
||||
g_autofree char *formatted_freed_size = NULL;
|
||||
OstreeRepoPruneFlags pruneflags = 0;
|
||||
gint n_objects_total;
|
||||
gint n_objects_pruned;
|
||||
guint64 objsize_total;
|
||||
|
||||
context = g_option_context_new ("- Search for unreachable objects");
|
||||
|
||||
if (!ostree_option_context_parse (context, options, &argc, &argv, OSTREE_BUILTIN_FLAG_NONE, &repo, cancellable, error))
|
||||
goto out;
|
||||
return FALSE;
|
||||
|
||||
if (!opt_no_prune && !ostree_ensure_repo_writable (repo, error))
|
||||
goto out;
|
||||
return FALSE;
|
||||
|
||||
/* Special handling for explicit commit deletion here - we do this
|
||||
* first.
|
||||
@@ -190,17 +161,18 @@ ostree_builtin_prune (int argc, char **argv, GCancellable *cancellable, GError *
|
||||
if (opt_no_prune)
|
||||
{
|
||||
ot_util_usage_error (context, "Cannot specify both --delete-commit and --no-prune", error);
|
||||
goto out;
|
||||
return FALSE;
|
||||
}
|
||||
if (opt_static_deltas_only)
|
||||
{
|
||||
if(!ostree_repo_prune_static_deltas (repo, opt_delete_commit, cancellable, error))
|
||||
goto out;
|
||||
return FALSE;
|
||||
}
|
||||
else if (!delete_commit (repo, opt_delete_commit, cancellable, error))
|
||||
goto out;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
OstreeRepoPruneFlags pruneflags = 0;
|
||||
if (opt_refs_only)
|
||||
pruneflags |= OSTREE_REPO_PRUNE_FLAGS_REFS_ONLY;
|
||||
if (opt_no_prune)
|
||||
@@ -210,12 +182,15 @@ ostree_builtin_prune (int argc, char **argv, GCancellable *cancellable, GError *
|
||||
* prune API - both to avoid code duplication, and to keep it run from the
|
||||
* test suite.
|
||||
*/
|
||||
gint n_objects_total;
|
||||
gint n_objects_pruned;
|
||||
guint64 objsize_total;
|
||||
if (!(opt_retain_branch_depth || opt_keep_younger_than))
|
||||
{
|
||||
if (!ostree_repo_prune (repo, pruneflags, opt_depth,
|
||||
&n_objects_total, &n_objects_pruned, &objsize_total,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
return FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -232,11 +207,7 @@ ostree_builtin_prune (int argc, char **argv, GCancellable *cancellable, GError *
|
||||
if (opt_keep_younger_than)
|
||||
{
|
||||
if (!parse_datetime (&keep_younger_than_ts, opt_keep_younger_than, NULL))
|
||||
{
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||
"Could not parse '%s'", opt_keep_younger_than);
|
||||
goto out;
|
||||
}
|
||||
return glnx_throw (error, "Could not parse '%s'", opt_keep_younger_than);
|
||||
}
|
||||
|
||||
for (char **iter = opt_retain_branch_depth; iter && *iter; iter++)
|
||||
@@ -244,34 +215,19 @@ ostree_builtin_prune (int argc, char **argv, GCancellable *cancellable, GError *
|
||||
/* bd should look like BRANCH=DEPTH where DEPTH is an int */
|
||||
const char *bd = *iter;
|
||||
const char *eq = strchr (bd, '=');
|
||||
const char *depthstr;
|
||||
gint64 depth;
|
||||
char *endptr;
|
||||
|
||||
if (!eq)
|
||||
{
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||
"Invalid value %s, must specify BRANCH=DEPTH",
|
||||
bd);
|
||||
goto out;
|
||||
}
|
||||
depthstr = eq + 1;
|
||||
return glnx_throw (error, "Invalid value %s, must specify BRANCH=DEPTH", bd);
|
||||
|
||||
const char *depthstr = eq + 1;
|
||||
errno = EPERM;
|
||||
depth = g_ascii_strtoll (depthstr, &endptr, 10);
|
||||
char *endptr;
|
||||
gint64 depth = g_ascii_strtoll (depthstr, &endptr, 10);
|
||||
if (depth == 0)
|
||||
{
|
||||
if (errno == EINVAL)
|
||||
{
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||
"Out of range depth %s", depthstr);
|
||||
goto out;
|
||||
}
|
||||
return glnx_throw (error, "Out of range depth %s", depthstr);
|
||||
else if (endptr == depthstr)
|
||||
{
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||
"Invalid depth %s", depthstr);
|
||||
goto out;
|
||||
}
|
||||
return glnx_throw (error, "Invalid depth %s", depthstr);
|
||||
}
|
||||
g_hash_table_insert (retain_branch_depth, g_strndup (bd, eq - bd),
|
||||
GINT_TO_POINTER ((int)depth));
|
||||
@@ -302,7 +258,7 @@ ostree_builtin_prune (int argc, char **argv, GCancellable *cancellable, GError *
|
||||
&keep_younger_than_ts,
|
||||
reachable,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
return FALSE;
|
||||
|
||||
/* Okay, we handled the younger-than case; the other
|
||||
* two fall through to plain depth-based handling below.
|
||||
@@ -325,12 +281,11 @@ ostree_builtin_prune (int argc, char **argv, GCancellable *cancellable, GError *
|
||||
&n_objects_pruned,
|
||||
&objsize_total,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
formatted_freed_size = g_format_size_full (objsize_total, 0);
|
||||
|
||||
g_autofree char *formatted_freed_size = g_format_size_full (objsize_total, 0);
|
||||
g_print ("Total objects: %u\n", n_objects_total);
|
||||
if (n_objects_pruned == 0)
|
||||
g_print ("No unreachable objects\n");
|
||||
@@ -341,7 +296,5 @@ ostree_builtin_prune (int argc, char **argv, GCancellable *cancellable, GError *
|
||||
g_print ("Deleted %u objects, %s freed\n",
|
||||
n_objects_pruned, formatted_freed_size);
|
||||
|
||||
ret = TRUE;
|
||||
out:
|
||||
return ret;
|
||||
return TRUE;
|
||||
}
|
||||
@@ -47,7 +47,7 @@ static int opt_depth = 0;
|
||||
static GOptionEntry options[] = {
|
||||
{ "remote", 0, 0, G_OPTION_ARG_STRING, &opt_remote, "Add REMOTE to refspec", "REMOTE" },
|
||||
{ "disable-fsync", 0, 0, G_OPTION_ARG_NONE, &opt_disable_fsync, "Do not invoke fsync()", NULL },
|
||||
{ "untrusted", 0, 0, G_OPTION_ARG_NONE, &opt_untrusted, "Do not trust source", NULL },
|
||||
{ "untrusted", 0, 0, G_OPTION_ARG_NONE, &opt_untrusted, "Do not verify checksums of local sources (always enabled for HTTP pulls)", NULL },
|
||||
{ "bareuseronly-files", 0, 0, G_OPTION_ARG_NONE, &opt_bareuseronly_files, "Reject regular files with mode outside of 0775 (world writable, suid, etc.)", NULL },
|
||||
{ "require-static-deltas", 0, 0, G_OPTION_ARG_NONE, &opt_require_static_deltas, "Require static deltas", NULL },
|
||||
{ "gpg-verify", 0, 0, G_OPTION_ARG_NONE, &opt_gpg_verify, "GPG verify commits (must specify --remote)", NULL },
|
||||
|
||||
@@ -34,6 +34,7 @@ static gboolean opt_dry_run;
|
||||
static gboolean opt_disable_static_deltas;
|
||||
static gboolean opt_require_static_deltas;
|
||||
static gboolean opt_untrusted;
|
||||
static gboolean opt_timestamp_check;
|
||||
static gboolean opt_bareuseronly_files;
|
||||
static char** opt_subpaths;
|
||||
static char** opt_http_headers;
|
||||
@@ -54,9 +55,9 @@ static GOptionEntry options[] = {
|
||||
{ "disable-fsync", 0, 0, G_OPTION_ARG_NONE, &opt_disable_fsync, "Do not invoke fsync()", NULL },
|
||||
{ "disable-static-deltas", 0, 0, G_OPTION_ARG_NONE, &opt_disable_static_deltas, "Do not use static deltas", NULL },
|
||||
{ "require-static-deltas", 0, 0, G_OPTION_ARG_NONE, &opt_require_static_deltas, "Require static deltas", NULL },
|
||||
{ "mirror", 0, 0, G_OPTION_ARG_NONE, &opt_mirror, "Write refs suitable for a mirror", NULL },
|
||||
{ "mirror", 0, 0, G_OPTION_ARG_NONE, &opt_mirror, "Write refs suitable for a mirror and fetches all refs if none provided", NULL },
|
||||
{ "subpath", 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &opt_subpaths, "Only pull the provided subpath(s)", NULL },
|
||||
{ "untrusted", 0, 0, G_OPTION_ARG_NONE, &opt_untrusted, "Do not trust (local) sources", NULL },
|
||||
{ "untrusted", 0, 0, G_OPTION_ARG_NONE, &opt_untrusted, "Do not verify checksums of local sources (always enabled for HTTP pulls)", NULL },
|
||||
{ "bareuseronly-files", 0, 0, G_OPTION_ARG_NONE, &opt_bareuseronly_files, "Reject regular files with mode outside of 0775 (world writable, suid, etc.)", NULL },
|
||||
{ "dry-run", 0, 0, G_OPTION_ARG_NONE, &opt_dry_run, "Only print information on what will be downloaded (requires static deltas)", NULL },
|
||||
{ "depth", 0, 0, G_OPTION_ARG_INT, &opt_depth, "Traverse DEPTH parents (-1=infinite) (default: 0)", "DEPTH" },
|
||||
@@ -64,6 +65,7 @@ static GOptionEntry options[] = {
|
||||
{ "http-header", 0, 0, G_OPTION_ARG_STRING_ARRAY, &opt_http_headers, "Add NAME=VALUE as HTTP header to all requests", "NAME=VALUE" },
|
||||
{ "update-frequency", 0, 0, G_OPTION_ARG_INT, &opt_frequency, "Sets the update frequency, in milliseconds (0=1000ms) (default: 0)", "FREQUENCY" },
|
||||
{ "localcache-repo", 'L', 0, G_OPTION_ARG_FILENAME_ARRAY, &opt_localcache_repos, "Add REPO as local cache source for objects during this pull", "REPO" },
|
||||
{ "timestamp-check", 'T', 0, G_OPTION_ARG_NONE, &opt_timestamp_check, "Require fetched commits to have newer timestamps", NULL },
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
@@ -288,6 +290,9 @@ ostree_builtin_pull (int argc, char **argv, GCancellable *cancellable, GError **
|
||||
|
||||
g_variant_builder_add (&builder, "{s@v}", "dry-run",
|
||||
g_variant_new_variant (g_variant_new_boolean (opt_dry_run)));
|
||||
if (opt_timestamp_check)
|
||||
g_variant_builder_add (&builder, "{s@v}", "timestamp-check",
|
||||
g_variant_new_variant (g_variant_new_boolean (opt_timestamp_check)));
|
||||
|
||||
if (override_commit_ids)
|
||||
g_variant_builder_add (&builder, "{s@v}", "override-commit-ids",
|
||||
|
||||
@@ -64,7 +64,8 @@ do_ref_with_collections (OstreeRepo *repo,
|
||||
|
||||
if (!ostree_repo_list_collection_refs (repo,
|
||||
(!opt_create) ? refspec_prefix : NULL,
|
||||
&refs, cancellable, error))
|
||||
&refs, OSTREE_REPO_LIST_REFS_EXT_NONE,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
|
||||
if (!opt_delete && !opt_create)
|
||||
|
||||
@@ -115,7 +115,8 @@ ostree_builtin_remote (int argc, char **argv, GCancellable *cancellable, GError
|
||||
|
||||
/* This will not return for some options (e.g. --version). */
|
||||
if (ostree_option_context_parse (context, NULL, &argc, &argv,
|
||||
OSTREE_BUILTIN_FLAG_NONE, NULL, cancellable, error))
|
||||
OSTREE_BUILTIN_FLAG_NO_REPO, NULL, cancellable,
|
||||
error))
|
||||
{
|
||||
if (subcommand_name == NULL)
|
||||
{
|
||||
|
||||
@@ -100,48 +100,41 @@ static_delta_usage (char **argv,
|
||||
else
|
||||
print_func = g_print;
|
||||
|
||||
print_func ("usage: ostree static-delta\n");
|
||||
print_func ("Builtin commands:\n");
|
||||
print_func ("Usage:\n");
|
||||
print_func (" ostree static-delta [OPTION...] COMMAND\n\n");
|
||||
print_func ("Builtin \"static-delta\" Commands:\n");
|
||||
|
||||
while (command->name)
|
||||
{
|
||||
print_func (" %s\n", command->name);
|
||||
command++;
|
||||
}
|
||||
|
||||
print_func ("\n");
|
||||
}
|
||||
|
||||
static gboolean
|
||||
ot_static_delta_builtin_list (int argc, char **argv, GCancellable *cancellable, GError **error)
|
||||
{
|
||||
gboolean ret = FALSE;
|
||||
g_autoptr(GPtrArray) delta_names = NULL;
|
||||
guint i;
|
||||
g_autoptr(GOptionContext) context = NULL;
|
||||
g_autoptr(OstreeRepo) repo = NULL;
|
||||
g_autoptr(GOptionContext) context = g_option_context_new ("- list static delta files");
|
||||
if (!ostree_option_context_parse (context, list_options, &argc, &argv,
|
||||
OSTREE_BUILTIN_FLAG_NONE, &repo, cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
context = g_option_context_new ("LIST - list static delta files");
|
||||
|
||||
if (!ostree_option_context_parse (context, list_options, &argc, &argv, OSTREE_BUILTIN_FLAG_NONE, &repo, cancellable, error))
|
||||
goto out;
|
||||
|
||||
g_autoptr(GPtrArray) delta_names = NULL;
|
||||
if (!ostree_repo_list_static_delta_names (repo, &delta_names, cancellable, error))
|
||||
goto out;
|
||||
|
||||
return FALSE;
|
||||
|
||||
if (delta_names->len == 0)
|
||||
{
|
||||
g_print ("(No static deltas)\n");
|
||||
}
|
||||
g_print ("(No static deltas)\n");
|
||||
else
|
||||
{
|
||||
for (i = 0; i < delta_names->len; i++)
|
||||
{
|
||||
g_print ("%s\n", (char*)delta_names->pdata[i]);
|
||||
}
|
||||
for (guint i = 0; i < delta_names->len; i++)
|
||||
g_print ("%s\n", (char*)delta_names->pdata[i]);
|
||||
}
|
||||
|
||||
ret = TRUE;
|
||||
out:
|
||||
return ret;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
@@ -152,7 +145,7 @@ ot_static_delta_builtin_show (int argc, char **argv, GCancellable *cancellable,
|
||||
g_autoptr(OstreeRepo) repo = NULL;
|
||||
const char *delta_id = NULL;
|
||||
|
||||
context = g_option_context_new ("SHOW - Dump information on a delta");
|
||||
context = g_option_context_new ("- Dump information on a delta");
|
||||
|
||||
if (!ostree_option_context_parse (context, list_options, &argc, &argv, OSTREE_BUILTIN_FLAG_NONE, &repo, cancellable, error))
|
||||
goto out;
|
||||
@@ -182,7 +175,7 @@ ot_static_delta_builtin_delete (int argc, char **argv, GCancellable *cancellable
|
||||
g_autoptr(OstreeRepo) repo = NULL;
|
||||
const char *delta_id = NULL;
|
||||
|
||||
context = g_option_context_new ("DELETE - Remove a delta");
|
||||
context = g_option_context_new ("- Remove a delta");
|
||||
|
||||
if (!ostree_option_context_parse (context, list_options, &argc, &argv, OSTREE_BUILTIN_FLAG_NONE, &repo, cancellable, error))
|
||||
goto out;
|
||||
@@ -212,7 +205,7 @@ ot_static_delta_builtin_generate (int argc, char **argv, GCancellable *cancellab
|
||||
g_autoptr(GOptionContext) context = NULL;
|
||||
g_autoptr(OstreeRepo) repo = NULL;
|
||||
|
||||
context = g_option_context_new ("GENERATE [TO] - Generate static delta files");
|
||||
context = g_option_context_new ("[TO] - Generate static delta files");
|
||||
if (!ostree_option_context_parse (context, generate_options, &argc, &argv, OSTREE_BUILTIN_FLAG_NONE, &repo, cancellable, error))
|
||||
goto out;
|
||||
|
||||
@@ -363,7 +356,7 @@ ot_static_delta_builtin_apply_offline (int argc, char **argv, GCancellable *canc
|
||||
g_autoptr(GOptionContext) context = NULL;
|
||||
g_autoptr(OstreeRepo) repo = NULL;
|
||||
|
||||
context = g_option_context_new ("APPLY-OFFLINE - Apply static delta file");
|
||||
context = g_option_context_new ("- Apply static delta file");
|
||||
if (!ostree_option_context_parse (context, apply_offline_options, &argc, &argv, OSTREE_BUILTIN_FLAG_NONE, &repo, cancellable, error))
|
||||
goto out;
|
||||
|
||||
@@ -397,13 +390,9 @@ ot_static_delta_builtin_apply_offline (int argc, char **argv, GCancellable *canc
|
||||
gboolean
|
||||
ostree_builtin_static_delta (int argc, char **argv, GCancellable *cancellable, GError **error)
|
||||
{
|
||||
gboolean ret = FALSE;
|
||||
OstreeCommand *command = NULL;
|
||||
const char *cmdname = NULL;
|
||||
int i;
|
||||
gboolean want_help = FALSE;
|
||||
|
||||
for (i = 1; i < argc; i++)
|
||||
const char *cmdname = NULL;
|
||||
for (int i = 1; i < argc; i++)
|
||||
{
|
||||
if (argv[i][0] != '-')
|
||||
{
|
||||
@@ -420,11 +409,10 @@ ostree_builtin_static_delta (int argc, char **argv, GCancellable *cancellable, G
|
||||
if (!cmdname && !want_help)
|
||||
{
|
||||
static_delta_usage (argv, TRUE);
|
||||
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||
"No command specified");
|
||||
goto out;
|
||||
return glnx_throw (error, "No command specified");
|
||||
}
|
||||
|
||||
OstreeCommand *command = NULL;
|
||||
if (cmdname)
|
||||
{
|
||||
command = static_delta_subcommands;
|
||||
@@ -439,22 +427,17 @@ ostree_builtin_static_delta (int argc, char **argv, GCancellable *cancellable, G
|
||||
if (want_help && command == NULL)
|
||||
{
|
||||
static_delta_usage (argv, FALSE);
|
||||
ret = TRUE;
|
||||
goto out;
|
||||
return TRUE; /* Note early return */
|
||||
}
|
||||
|
||||
if (!command->fn)
|
||||
{
|
||||
g_autofree char *msg = g_strdup_printf ("Unknown command '%s'", cmdname);
|
||||
static_delta_usage (argv, TRUE);
|
||||
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, msg);
|
||||
goto out;
|
||||
return glnx_throw (error, "Unknown \"static-delta\" subcommand '%s'", cmdname);
|
||||
}
|
||||
|
||||
if (!command->fn (argc, argv, cancellable, error))
|
||||
goto out;
|
||||
g_autofree char *prgname = g_strdup_printf ("%s %s", g_get_prgname (), cmdname);
|
||||
g_set_prgname (prgname);
|
||||
|
||||
ret = TRUE;
|
||||
out:
|
||||
return ret;
|
||||
return command->fn (argc, argv, cancellable, error);
|
||||
}
|
||||
+28
-25
@@ -78,15 +78,11 @@ int
|
||||
ostree_usage (OstreeCommand *commands,
|
||||
gboolean is_error)
|
||||
{
|
||||
g_autoptr(GOptionContext) context = NULL;
|
||||
g_autofree char *help;
|
||||
|
||||
context = ostree_option_context_new_with_commands (commands);
|
||||
|
||||
g_autoptr(GOptionContext) context =
|
||||
ostree_option_context_new_with_commands (commands);
|
||||
g_option_context_add_main_entries (context, global_entries, NULL);
|
||||
|
||||
help = g_option_context_get_help (context, FALSE, NULL);
|
||||
|
||||
g_autofree char *help = g_option_context_get_help (context, FALSE, NULL);
|
||||
if (is_error)
|
||||
g_printerr ("%s", help);
|
||||
else
|
||||
@@ -185,13 +181,10 @@ ostree_run (int argc,
|
||||
{
|
||||
g_set_error (&error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||
"Unknown command '%s'", command_name);
|
||||
ostree_usage (commands, TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
help = g_option_context_get_help (context, FALSE, NULL);
|
||||
g_printerr ("%s", help);
|
||||
|
||||
ostree_usage (commands, TRUE);
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -381,9 +374,17 @@ ostree_admin_option_context_parse (GOptionContext *context,
|
||||
|
||||
g_option_context_add_main_entries (context, global_admin_entries, NULL);
|
||||
|
||||
if (!ostree_option_context_parse (context, main_entries, argc, argv, OSTREE_BUILTIN_FLAG_NO_REPO, NULL, cancellable, error))
|
||||
if (!ostree_option_context_parse (context, main_entries, argc, argv,
|
||||
OSTREE_BUILTIN_FLAG_NO_REPO, NULL, cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
if (!opt_print_current_dir && (flags & OSTREE_ADMIN_BUILTIN_FLAG_NO_SYSROOT))
|
||||
{
|
||||
g_assert_null (out_sysroot);
|
||||
/* Early return if no sysroot is requested */
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
g_autoptr(GFile) sysroot_path = NULL;
|
||||
if (opt_sysroot != NULL)
|
||||
sysroot_path = g_file_new_for_path (opt_sysroot);
|
||||
@@ -391,12 +392,24 @@ ostree_admin_option_context_parse (GOptionContext *context,
|
||||
g_autoptr(OstreeSysroot) sysroot = ostree_sysroot_new (sysroot_path);
|
||||
g_signal_connect (sysroot, "journal-msg", G_CALLBACK (on_sysroot_journal_msg), NULL);
|
||||
|
||||
if ((flags & OSTREE_ADMIN_BUILTIN_FLAG_UNLOCKED) == 0)
|
||||
{
|
||||
/* Released when sysroot is finalized, or on process exit */
|
||||
if (!ot_admin_sysroot_lock (sysroot, error))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!ostree_sysroot_load (sysroot, cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
if (flags & OSTREE_ADMIN_BUILTIN_FLAG_SUPERUSER)
|
||||
{
|
||||
GFile *path = ostree_sysroot_get_path (sysroot);
|
||||
OstreeDeployment *booted = ostree_sysroot_get_booted_deployment (sysroot);
|
||||
|
||||
/* If sysroot path is "/" then user must be root. */
|
||||
if (!g_file_has_parent (path, NULL) && getuid () != 0)
|
||||
/* Only require root if we're manipulating a booted sysroot. (Mostly
|
||||
* useful for the test suite)
|
||||
*/
|
||||
if (booted && getuid () != 0)
|
||||
{
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED,
|
||||
"You must be root to perform this command");
|
||||
@@ -411,9 +424,6 @@ ostree_admin_option_context_parse (GOptionContext *context,
|
||||
g_autoptr(GFile) deployment_file = NULL;
|
||||
g_autofree char *deployment_path = NULL;
|
||||
|
||||
if (!ostree_sysroot_load (sysroot, cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
deployments = ostree_sysroot_get_deployments (sysroot);
|
||||
if (deployments->len == 0)
|
||||
return glnx_throw (error, "Unable to find a deployment in sysroot");
|
||||
@@ -433,13 +443,6 @@ ostree_admin_option_context_parse (GOptionContext *context,
|
||||
exit (EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
if ((flags & OSTREE_ADMIN_BUILTIN_FLAG_UNLOCKED) == 0)
|
||||
{
|
||||
/* Released when sysroot is finalized, or on process exit */
|
||||
if (!ot_admin_sysroot_lock (sysroot, error))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (out_sysroot)
|
||||
*out_sysroot = g_steal_pointer (&sysroot);
|
||||
|
||||
|
||||
@@ -33,8 +33,9 @@ typedef enum {
|
||||
|
||||
typedef enum {
|
||||
OSTREE_ADMIN_BUILTIN_FLAG_NONE = 0,
|
||||
OSTREE_ADMIN_BUILTIN_FLAG_SUPERUSER = 1 << 0,
|
||||
OSTREE_ADMIN_BUILTIN_FLAG_UNLOCKED = 1 << 1
|
||||
OSTREE_ADMIN_BUILTIN_FLAG_SUPERUSER = (1 << 0),
|
||||
OSTREE_ADMIN_BUILTIN_FLAG_UNLOCKED = (1 << 1),
|
||||
OSTREE_ADMIN_BUILTIN_FLAG_NO_SYSROOT = (1 << 2),
|
||||
} OstreeAdminBuiltinFlags;
|
||||
|
||||
typedef struct {
|
||||
|
||||
+33
-12
@@ -198,26 +198,42 @@ callback_link (const char *from, const char *to)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
stbuf_is_regfile_hardlinked (struct stat *stbuf)
|
||||
/* Check whether @stbuf refers to a hardlinked regfile or symlink, and if so
|
||||
* return -EROFS. Otherwise return 0.
|
||||
*/
|
||||
static int
|
||||
can_write_stbuf (struct stat *stbuf)
|
||||
{
|
||||
return S_ISREG (stbuf->st_mode) && stbuf->st_nlink > 1;
|
||||
/* If it's not a regular file or symlink, ostree won't hardlink it, so allow
|
||||
* writes - it might be a FIFO or device that somehow
|
||||
* ended up underneath our mount.
|
||||
*/
|
||||
if (!(S_ISREG (stbuf->st_mode) || S_ISLNK (stbuf->st_mode)))
|
||||
return 0;
|
||||
/* If the object isn't hardlinked, it's OK to write */
|
||||
if (stbuf->st_nlink <= 1)
|
||||
return 0;
|
||||
/* Otherwise, it's a hardlinked file or symlink; it must be
|
||||
* immutable.
|
||||
*/
|
||||
return -EROFS;
|
||||
}
|
||||
|
||||
/* Check whether @path refers to a hardlinked regfile or symlink, and if so
|
||||
* return -EROFS. Otherwise return 0.
|
||||
*/
|
||||
static int
|
||||
can_write (const char *path)
|
||||
{
|
||||
struct stat stbuf;
|
||||
if (fstatat (basefd, path, &stbuf, 0) == -1)
|
||||
if (fstatat (basefd, path, &stbuf, AT_SYMLINK_NOFOLLOW) == -1)
|
||||
{
|
||||
if (errno == ENOENT)
|
||||
return 0;
|
||||
else
|
||||
return -errno;
|
||||
}
|
||||
if (stbuf_is_regfile_hardlinked (&stbuf))
|
||||
return -EROFS;
|
||||
return 0;
|
||||
return can_write_stbuf (&stbuf);
|
||||
}
|
||||
|
||||
#define VERIFY_WRITE(path) do { \
|
||||
@@ -231,6 +247,10 @@ callback_chmod (const char *path, mode_t mode)
|
||||
{
|
||||
path = ENSURE_RELPATH (path);
|
||||
VERIFY_WRITE(path);
|
||||
/* Note we can't use AT_SYMLINK_NOFOLLOW yet;
|
||||
* https://marc.info/?l=linux-kernel&m=148830147803162&w=2
|
||||
* https://marc.info/?l=linux-fsdevel&m=149193779929561&w=2
|
||||
*/
|
||||
if (fchmodat (basefd, path, mode, 0) != 0)
|
||||
return -errno;
|
||||
return 0;
|
||||
@@ -241,7 +261,7 @@ callback_chown (const char *path, uid_t uid, gid_t gid)
|
||||
{
|
||||
path = ENSURE_RELPATH (path);
|
||||
VERIFY_WRITE(path);
|
||||
if (fchownat (basefd, path, uid, gid, 0) != 0)
|
||||
if (fchownat (basefd, path, uid, gid, AT_SYMLINK_NOFOLLOW) != 0)
|
||||
return -errno;
|
||||
return 0;
|
||||
}
|
||||
@@ -254,7 +274,7 @@ callback_truncate (const char *path, off_t size)
|
||||
path = ENSURE_RELPATH (path);
|
||||
VERIFY_WRITE(path);
|
||||
|
||||
fd = openat (basefd, path, O_WRONLY);
|
||||
fd = openat (basefd, path, O_NOFOLLOW|O_WRONLY);
|
||||
if (fd == -1)
|
||||
return -errno;
|
||||
|
||||
@@ -312,10 +332,11 @@ do_open (const char *path, mode_t mode, struct fuse_file_info *finfo)
|
||||
return -errno;
|
||||
}
|
||||
|
||||
if (stbuf_is_regfile_hardlinked (&stbuf))
|
||||
int r = can_write_stbuf (&stbuf);
|
||||
if (r != 0)
|
||||
{
|
||||
(void) close (fd);
|
||||
return -EROFS;
|
||||
return r;
|
||||
}
|
||||
|
||||
/* Handle O_TRUNC here only after verifying hardlink state */
|
||||
@@ -433,7 +454,7 @@ callback_access (const char *path, int mode)
|
||||
* before trying to do an unlink. So...we'll just lie about
|
||||
* writable access here.
|
||||
*/
|
||||
if (faccessat (basefd, path, mode, 0) == -1)
|
||||
if (faccessat (basefd, path, mode, AT_SYMLINK_NOFOLLOW) == -1)
|
||||
return -errno;
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user