New upstream version 2017.11

This commit is contained in:
Simon McVittie committed 2017-09-15 11:18:25 +01:00
commit 573491dc3d
154 files changed
+3866 -2564

No files matched your search

+3
View File
@@ -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
+3
View File
@@ -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.
*/
+57 -70
View 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
+58 -102
View File
@@ -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
+63 -51
View File
@@ -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;
}
}
}
+7
View File
@@ -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"
+33 -1
View File
@@ -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)
+10 -22
View File
@@ -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*
+37 -15
View File
@@ -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", &timestamp);
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,
+7 -1
View File
@@ -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
+22
View File
@@ -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
+131 -51
View File
@@ -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;
}
}
}
+40 -45
View File
@@ -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
{
+3 -1
View File
@@ -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);
+42 -31
View File
@@ -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))
+28 -5
View File
@@ -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,
+4 -10
View File
@@ -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 -88
View File
@@ -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);
+103 -64
View File
@@ -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
View File
@@ -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 */
+47 -8
View File
@@ -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 */
+1 -1
View File
@@ -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
{
+87 -121
View File
@@ -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;
}
/**
+301 -97
View File
@@ -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.
+22 -23
View File
@@ -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)
+79 -45
View File
@@ -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");
+2
View File
@@ -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
+3 -3
View File
@@ -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))