New upstream version 2022.2
This commit is contained in:
commit
c27a45a449
134 files changed
+5374
-4216
No files matched your search
@@ -20,6 +20,8 @@
|
||||
- uncomment the include in Makefile-libostree.am
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/* Stub section for the stable release *after* this development one; don't
|
||||
* edit this other than to update the year. This is just a copy/paste
|
||||
* source. Replace $LASTSTABLE with the last stable version, and $NEWVERSION
|
||||
|
||||
@@ -672,6 +672,11 @@ global:
|
||||
ostree_mutable_tree_new_from_commit;
|
||||
} LIBOSTREE_2021.4;
|
||||
|
||||
LIBOSTREE_2022.2 {
|
||||
global:
|
||||
ostree_repo_traverse_commit_with_flags;
|
||||
} LIBOSTREE_2021.5;
|
||||
|
||||
/* NOTE: Only add more content here in release commits! See the
|
||||
* comments at the top of this file.
|
||||
*/
|
||||
@@ -3,6 +3,7 @@ exec_prefix=@exec_prefix@
|
||||
libdir=@libdir@
|
||||
includedir=@includedir@
|
||||
features=@OSTREE_FEATURES@
|
||||
cliextdir=@pkglibexecdir@/ext
|
||||
|
||||
Name: OSTree
|
||||
Description: Git for operating system binaries
|
||||
|
||||
@@ -19,10 +19,17 @@
|
||||
|
||||
#include "ostree-sysroot-private.h"
|
||||
#include "ostree-bootloader-zipl.h"
|
||||
#include "ostree-deployment-private.h"
|
||||
#include "otutil.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#define SECURE_EXECUTION_BOOT_IMAGE "/boot/sd-boot"
|
||||
#define SECURE_EXECUTION_HOSTKEY_PATH "/etc/se-hostkeys/"
|
||||
#define SECURE_EXECUTION_HOSTKEY_PREFIX "ibm-z-hostkey"
|
||||
#define SECURE_EXECUTION_LUKS_ROOT_KEY "/etc/luks/root"
|
||||
#define SECURE_EXECUTION_LUKS_CONFIG "/etc/crypttab"
|
||||
#define SECURE_EXECUTION_RAMDISK_TOOL PKGLIBEXECDIR "/s390x-se-luks-gencpio"
|
||||
|
||||
/* This is specific to zipl today, but in the future we could also
|
||||
* use it for the grub2-mkconfig case.
|
||||
*/
|
||||
@@ -78,8 +85,206 @@ _ostree_bootloader_zipl_write_config (OstreeBootloader *bootloader,
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
_ostree_secure_execution_get_keys (GPtrArray **keys,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
g_auto (GLnxDirFdIterator) it = { 0,};
|
||||
if ( !glnx_dirfd_iterator_init_at (-1, SECURE_EXECUTION_HOSTKEY_PATH, TRUE, &it, error))
|
||||
return glnx_prefix_error (error, "s390x SE: looking for SE keys");
|
||||
|
||||
g_autoptr(GPtrArray) ret_keys = g_ptr_array_new_with_free_func (g_free);
|
||||
while (TRUE)
|
||||
{
|
||||
struct dirent *dent = NULL;
|
||||
if (!glnx_dirfd_iterator_next_dent (&it, &dent, cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
if (!dent)
|
||||
break;
|
||||
|
||||
if (g_str_has_prefix (dent->d_name, SECURE_EXECUTION_HOSTKEY_PREFIX))
|
||||
g_ptr_array_add (ret_keys, g_build_filename (SECURE_EXECUTION_HOSTKEY_PATH, dent->d_name, NULL));
|
||||
}
|
||||
|
||||
*keys = g_steal_pointer (&ret_keys);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
_ostree_secure_execution_get_bls_config (OstreeBootloaderZipl *self,
|
||||
int bootversion,
|
||||
gchar **vmlinuz,
|
||||
gchar **initramfs,
|
||||
gchar **options,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
g_autoptr (GPtrArray) configs = NULL;
|
||||
if ( !_ostree_sysroot_read_boot_loader_configs (self->sysroot, bootversion, &configs, cancellable, error))
|
||||
return glnx_prefix_error (error, "s390x SE: loading bls configs");
|
||||
|
||||
if (!configs || configs->len == 0)
|
||||
return glnx_throw (error, "s390x SE: no bls config");
|
||||
|
||||
OstreeBootconfigParser *parser = (OstreeBootconfigParser *) g_ptr_array_index (configs, 0);
|
||||
const gchar *val = NULL;
|
||||
|
||||
val = ostree_bootconfig_parser_get (parser, "linux");
|
||||
if (!val)
|
||||
return glnx_throw (error, "s390x SE: no \"linux\" key in bootloader config");
|
||||
*vmlinuz = g_build_filename ("/boot", val, NULL);
|
||||
|
||||
val = ostree_bootconfig_parser_get (parser, "initrd");
|
||||
if (!val)
|
||||
return glnx_throw (error, "s390x SE: no \"initrd\" key in bootloader config");
|
||||
*initramfs = g_build_filename ("/boot", val, NULL);
|
||||
|
||||
val = ostree_bootconfig_parser_get (parser, "options");
|
||||
if (!val)
|
||||
return glnx_throw (error, "s390x SE: no \"options\" key in bootloader config");
|
||||
*options = g_strdup(val);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
_ostree_secure_execution_luks_key_exists (void)
|
||||
{
|
||||
return (access(SECURE_EXECUTION_LUKS_ROOT_KEY, F_OK) == 0 &&
|
||||
access(SECURE_EXECUTION_LUKS_CONFIG, F_OK) == 0);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
_ostree_secure_execution_enable_luks(const gchar *oldramfs,
|
||||
const gchar *newramfs,
|
||||
GError **error)
|
||||
{
|
||||
const char *const argv[] = {SECURE_EXECUTION_RAMDISK_TOOL, oldramfs, newramfs, NULL};
|
||||
g_autofree gchar *out = NULL;
|
||||
g_autofree gchar *err = NULL;
|
||||
int status = 0;
|
||||
if (!g_spawn_sync (NULL, (char**)argv, NULL, G_SPAWN_SEARCH_PATH,
|
||||
NULL, NULL, &out, &err, &status, error))
|
||||
return glnx_prefix_error(error, "s390x SE: spawning %s", SECURE_EXECUTION_RAMDISK_TOOL);
|
||||
|
||||
if (!g_spawn_check_exit_status (status, error))
|
||||
{
|
||||
g_printerr("s390x SE: `%s` stdout: %s\n", SECURE_EXECUTION_RAMDISK_TOOL, out);
|
||||
g_printerr("s390x SE: `%s` stderr: %s\n", SECURE_EXECUTION_RAMDISK_TOOL, err);
|
||||
return glnx_prefix_error(error, "s390x SE: `%s` failed", SECURE_EXECUTION_RAMDISK_TOOL);
|
||||
}
|
||||
|
||||
ot_journal_print(LOG_INFO, "s390x SE: luks key added to initrd");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
_ostree_secure_execution_generate_sdboot (gchar *vmlinuz,
|
||||
gchar *initramfs,
|
||||
gchar *options,
|
||||
GPtrArray *keys,
|
||||
GError **error)
|
||||
{
|
||||
g_assert (vmlinuz && initramfs && options && keys && keys->len);
|
||||
ot_journal_print(LOG_INFO, "s390x SE: kernel: %s", vmlinuz);
|
||||
ot_journal_print(LOG_INFO, "s390x SE: initrd: %s", initramfs);
|
||||
ot_journal_print(LOG_INFO, "s390x SE: kargs: %s", options);
|
||||
|
||||
pid_t self = getpid();
|
||||
|
||||
// Store kernel options to temp file, so `genprotimg` can later embed it
|
||||
g_auto(GLnxTmpfile) cmdline = { 0, };
|
||||
if (!glnx_open_anonymous_tmpfile (O_RDWR | O_CLOEXEC, &cmdline, error))
|
||||
return glnx_prefix_error(error, "s390x SE: opening cmdline file");
|
||||
if (glnx_loop_write (cmdline.fd, options, strlen (options)) < 0)
|
||||
return glnx_throw_errno_prefix (error, "s390x SE: writting cmdline file");
|
||||
g_autofree gchar *cmdline_filename = g_strdup_printf ("/proc/%d/fd/%d", self, cmdline.fd);
|
||||
|
||||
// Copy initramfs to temp file and embed LUKS key and config into it
|
||||
g_auto(GLnxTmpfile) ramdisk = { 0, };
|
||||
g_autofree gchar *ramdisk_filename = NULL;
|
||||
if (_ostree_secure_execution_luks_key_exists ())
|
||||
{
|
||||
if (!glnx_open_anonymous_tmpfile (O_RDWR | O_CLOEXEC, &ramdisk, error))
|
||||
return glnx_prefix_error(error, "s390x SE: creating new ramdisk");
|
||||
ramdisk_filename = g_strdup_printf ("/proc/%d/fd/%d", self, ramdisk.fd);
|
||||
if (!_ostree_secure_execution_enable_luks (initramfs, ramdisk_filename, error))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
g_autoptr(GPtrArray) argv = g_ptr_array_new ();
|
||||
g_ptr_array_add (argv, "genprotimg");
|
||||
g_ptr_array_add (argv, "-i");
|
||||
g_ptr_array_add (argv, vmlinuz);
|
||||
g_ptr_array_add (argv, "-r");
|
||||
g_ptr_array_add (argv, (ramdisk_filename == NULL) ? initramfs: ramdisk_filename);
|
||||
g_ptr_array_add (argv, "-p");
|
||||
g_ptr_array_add (argv, cmdline_filename);
|
||||
for (guint i = 0; i < keys->len; ++i)
|
||||
{
|
||||
gchar *key = g_ptr_array_index (keys, i);
|
||||
g_ptr_array_add (argv, "-k");
|
||||
g_ptr_array_add (argv, key);
|
||||
ot_journal_print(LOG_INFO, "s390x SE: key[%d]: %s", i + 1, key);
|
||||
}
|
||||
g_ptr_array_add (argv, "--no-verify");
|
||||
g_ptr_array_add (argv, "-o");
|
||||
g_ptr_array_add (argv, SECURE_EXECUTION_BOOT_IMAGE);
|
||||
g_ptr_array_add (argv, NULL);
|
||||
|
||||
gint status = 0;
|
||||
if (!g_spawn_sync (NULL, (char**)argv->pdata, NULL, G_SPAWN_SEARCH_PATH,
|
||||
NULL, NULL, NULL, NULL, &status, error))
|
||||
return glnx_prefix_error(error, "s390x SE: spawning genprotimg");
|
||||
|
||||
if (!g_spawn_check_exit_status (status, error))
|
||||
return glnx_prefix_error(error, "s390x SE: `genprotimg` failed");
|
||||
|
||||
ot_journal_print(LOG_INFO, "s390x SE: `%s` generated", SECURE_EXECUTION_BOOT_IMAGE);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
_ostree_secure_execution_call_zipl (GError **error)
|
||||
{
|
||||
int status = 0;
|
||||
const char *const zipl_argv[] = {"zipl", "-V", "-t", "/boot", "-i", SECURE_EXECUTION_BOOT_IMAGE, NULL};
|
||||
if (!g_spawn_sync (NULL, (char**)zipl_argv, NULL, G_SPAWN_SEARCH_PATH,
|
||||
NULL, NULL, NULL, NULL, &status, error))
|
||||
return glnx_prefix_error(error, "s390x SE: spawning zipl");
|
||||
|
||||
if (!g_spawn_check_exit_status (status, error))
|
||||
return glnx_prefix_error(error, "s390x SE: `zipl` failed");
|
||||
|
||||
ot_journal_print(LOG_INFO, "s390x SE: `sd-boot` zipled");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
_ostree_secure_execution_enable (OstreeBootloaderZipl *self,
|
||||
int bootversion,
|
||||
GPtrArray *keys,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
g_autofree gchar* vmlinuz = NULL;
|
||||
g_autofree gchar* initramfs = NULL;
|
||||
g_autofree gchar* options = NULL;
|
||||
|
||||
gboolean rc =
|
||||
_ostree_secure_execution_get_bls_config (self, bootversion, &vmlinuz, &initramfs, &options, cancellable, error) &&
|
||||
_ostree_secure_execution_generate_sdboot (vmlinuz, initramfs, options, keys, error) &&
|
||||
_ostree_secure_execution_call_zipl (error);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
static gboolean
|
||||
_ostree_bootloader_zipl_post_bls_sync (OstreeBootloader *bootloader,
|
||||
int bootversion,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
@@ -97,6 +302,14 @@ _ostree_bootloader_zipl_post_bls_sync (OstreeBootloader *bootloader,
|
||||
if (errno == ENOENT)
|
||||
return TRUE;
|
||||
|
||||
/* Try with Secure Execution */
|
||||
g_autoptr(GPtrArray) keys = NULL;
|
||||
if (!_ostree_secure_execution_get_keys (&keys, cancellable, error))
|
||||
return FALSE;
|
||||
if (keys && keys->len)
|
||||
return _ostree_secure_execution_enable (self, bootversion, keys, cancellable, error);
|
||||
|
||||
/* Fallback to non-SE setup */
|
||||
const char *const zipl_argv[] = {"zipl", NULL};
|
||||
int estatus;
|
||||
if (!g_spawn_sync (NULL, (char**)zipl_argv, NULL, G_SPAWN_SEARCH_PATH,
|
||||
|
||||
@@ -30,5 +30,4 @@ typedef struct _OstreeBootloaderZipl OstreeBootloaderZipl;
|
||||
GType _ostree_bootloader_zipl_get_type (void) G_GNUC_CONST;
|
||||
|
||||
OstreeBootloaderZipl * _ostree_bootloader_zipl_new (OstreeSysroot *sysroot);
|
||||
|
||||
G_END_DECLS
|
||||
@@ -65,13 +65,14 @@ _ostree_bootloader_write_config (OstreeBootloader *self,
|
||||
|
||||
gboolean
|
||||
_ostree_bootloader_post_bls_sync (OstreeBootloader *self,
|
||||
int bootversion,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
g_return_val_if_fail (OSTREE_IS_BOOTLOADER (self), FALSE);
|
||||
|
||||
if (OSTREE_BOOTLOADER_GET_IFACE (self)->post_bls_sync)
|
||||
return OSTREE_BOOTLOADER_GET_IFACE (self)->post_bls_sync (self, cancellable, error);
|
||||
return OSTREE_BOOTLOADER_GET_IFACE (self)->post_bls_sync (self, bootversion, cancellable, error);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@@ -46,6 +46,7 @@ struct _OstreeBootloaderInterface
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
gboolean (* post_bls_sync) (OstreeBootloader *self,
|
||||
int bootversion,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
gboolean (* is_atomic) (OstreeBootloader *self);
|
||||
@@ -68,6 +69,7 @@ gboolean _ostree_bootloader_write_config (OstreeBootloader *self,
|
||||
GError **error);
|
||||
|
||||
gboolean _ostree_bootloader_post_bls_sync (OstreeBootloader *self,
|
||||
int bootversion,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
|
||||
@@ -197,7 +197,8 @@ _ostree_repo_mode_is_bare (OstreeRepoMode mode)
|
||||
return
|
||||
mode == OSTREE_REPO_MODE_BARE ||
|
||||
mode == OSTREE_REPO_MODE_BARE_USER ||
|
||||
mode == OSTREE_REPO_MODE_BARE_USER_ONLY;
|
||||
mode == OSTREE_REPO_MODE_BARE_USER_ONLY ||
|
||||
mode == OSTREE_REPO_MODE_BARE_SPLIT_XATTRS;
|
||||
}
|
||||
|
||||
#ifndef OSTREE_DISABLE_GPGME
|
||||
|
||||
@@ -39,6 +39,7 @@ G_STATIC_ASSERT(OSTREE_REPO_MODE_ARCHIVE_Z2 == 1);
|
||||
G_STATIC_ASSERT(OSTREE_REPO_MODE_ARCHIVE == OSTREE_REPO_MODE_ARCHIVE_Z2);
|
||||
G_STATIC_ASSERT(OSTREE_REPO_MODE_BARE_USER == 2);
|
||||
G_STATIC_ASSERT(OSTREE_REPO_MODE_BARE_USER_ONLY == 3);
|
||||
G_STATIC_ASSERT(OSTREE_REPO_MODE_BARE_SPLIT_XATTRS == 4);
|
||||
|
||||
static GBytes *variant_to_lenprefixed_buffer (GVariant *variant);
|
||||
|
||||
@@ -1141,7 +1142,7 @@ _ostree_compare_object_checksum (OstreeObjectType objtype,
|
||||
* @dir_info: a #GFileInfo containing directory information
|
||||
* @xattrs: (allow-none): Optional extended attributes
|
||||
*
|
||||
* Returns: (transfer full): A new #GVariant containing %OSTREE_OBJECT_TYPE_DIR_META
|
||||
* Returns: (transfer full) (not nullable): A new #GVariant containing %OSTREE_OBJECT_TYPE_DIR_META
|
||||
*/
|
||||
GVariant *
|
||||
ostree_create_directory_metadata (GFileInfo *dir_info,
|
||||
@@ -1228,6 +1229,10 @@ ostree_object_type_to_string (OstreeObjectType objtype)
|
||||
return "commitmeta";
|
||||
case OSTREE_OBJECT_TYPE_PAYLOAD_LINK:
|
||||
return "payload-link";
|
||||
case OSTREE_OBJECT_TYPE_FILE_XATTRS:
|
||||
return "file-xattrs";
|
||||
case OSTREE_OBJECT_TYPE_FILE_XATTRS_LINK:
|
||||
return "file-xattrs-link";
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
return NULL;
|
||||
@@ -1257,6 +1262,10 @@ ostree_object_type_from_string (const char *str)
|
||||
return OSTREE_OBJECT_TYPE_COMMIT_META;
|
||||
else if (!strcmp (str, "payload-link"))
|
||||
return OSTREE_OBJECT_TYPE_PAYLOAD_LINK;
|
||||
else if (!strcmp (str, "file-xattrs"))
|
||||
return OSTREE_OBJECT_TYPE_FILE_XATTRS;
|
||||
else if (!strcmp (str, "file-xattrs-link"))
|
||||
return OSTREE_OBJECT_TYPE_FILE_XATTRS_LINK;
|
||||
g_assert_not_reached ();
|
||||
return 0;
|
||||
}
|
||||
@@ -2141,6 +2150,8 @@ _ostree_validate_structureof_metadata (OstreeObjectType objtype,
|
||||
/* TODO */
|
||||
break;
|
||||
case OSTREE_OBJECT_TYPE_FILE:
|
||||
case OSTREE_OBJECT_TYPE_FILE_XATTRS:
|
||||
case OSTREE_OBJECT_TYPE_FILE_XATTRS_LINK:
|
||||
g_assert_not_reached ();
|
||||
break;
|
||||
}
|
||||
@@ -2197,6 +2208,19 @@ ostree_validate_structureof_commit (GVariant *commit,
|
||||
if (!validate_variant (commit, OSTREE_COMMIT_GVARIANT_FORMAT, error))
|
||||
return FALSE;
|
||||
|
||||
g_autoptr(GVariant) metadata = NULL;
|
||||
g_variant_get_child (commit, 0, "@a{sv}", &metadata);
|
||||
g_assert (metadata != NULL);
|
||||
g_autoptr(GVariantIter) metadata_iter = g_variant_iter_new (metadata);
|
||||
g_assert (metadata_iter != NULL);
|
||||
g_autoptr(GVariant) metadata_entry = NULL;
|
||||
const gchar *metadata_key = NULL;
|
||||
while (g_variant_iter_loop (metadata_iter, "{sv}", &metadata_key, NULL))
|
||||
{
|
||||
if (metadata_key == NULL || strlen (metadata_key) == 0)
|
||||
return glnx_throw (error, "Empty metadata key");
|
||||
}
|
||||
|
||||
g_autoptr(GVariant) parent_csum_v = NULL;
|
||||
g_variant_get_child (commit, 1, "@ay", &parent_csum_v);
|
||||
gsize n_elts;
|
||||
|
||||
@@ -67,6 +67,8 @@ G_BEGIN_DECLS
|
||||
* @OSTREE_OBJECT_TYPE_TOMBSTONE_COMMIT: Toplevel object, refers to a deleted commit
|
||||
* @OSTREE_OBJECT_TYPE_COMMIT_META: Detached metadata for a commit
|
||||
* @OSTREE_OBJECT_TYPE_PAYLOAD_LINK: Symlink to a .file given its checksum on the payload only.
|
||||
* @OSTREE_OBJECT_TYPE_FILE_XATTRS: Detached xattrs content, for 'bare-split-xattrs' mode.
|
||||
* @OSTREE_OBJECT_TYPE_FILE_XATTRS_LINK: Hardlink to a .file-xattrs given the checksum of its .file object.
|
||||
*
|
||||
* Enumeration for core object types; %OSTREE_OBJECT_TYPE_FILE is for
|
||||
* content, the other types are metadata.
|
||||
@@ -78,7 +80,9 @@ typedef enum {
|
||||
OSTREE_OBJECT_TYPE_COMMIT = 4, /* .commit */
|
||||
OSTREE_OBJECT_TYPE_TOMBSTONE_COMMIT = 5, /* .commit-tombstone */
|
||||
OSTREE_OBJECT_TYPE_COMMIT_META = 6, /* .commitmeta */
|
||||
OSTREE_OBJECT_TYPE_PAYLOAD_LINK = 7, /* .payload-link */
|
||||
OSTREE_OBJECT_TYPE_PAYLOAD_LINK = 7, /* .payload-link */
|
||||
OSTREE_OBJECT_TYPE_FILE_XATTRS = 8, /* .file-xattrs */
|
||||
OSTREE_OBJECT_TYPE_FILE_XATTRS_LINK = 9, /* .file-xattrs-link */
|
||||
} OstreeObjectType;
|
||||
|
||||
/**
|
||||
@@ -94,7 +98,7 @@ typedef enum {
|
||||
*
|
||||
* Last valid object type; use this to validate ranges.
|
||||
*/
|
||||
#define OSTREE_OBJECT_TYPE_LAST OSTREE_OBJECT_TYPE_PAYLOAD_LINK
|
||||
#define OSTREE_OBJECT_TYPE_LAST OSTREE_OBJECT_TYPE_FILE_XATTRS_LINK
|
||||
|
||||
/**
|
||||
* OSTREE_DIRMETA_GVARIANT_FORMAT:
|
||||
@@ -164,6 +168,8 @@ typedef enum {
|
||||
* The currently defined keys for the `a{sv}` of additional metadata for each commit are:
|
||||
* - key: `ostree.commit.timestamp`, value: `t`, timestamp (seconds since the
|
||||
* Unix epoch in UTC, big-endian) when the commit was committed
|
||||
* - key: `ostree.commit.version`, value: `s`, the `version` value from the
|
||||
* commit's metadata if it was defined. Since: 2022.2
|
||||
*/
|
||||
#define OSTREE_SUMMARY_GVARIANT_STRING "(a(s(taya{sv}))a{sv})"
|
||||
#define OSTREE_SUMMARY_GVARIANT_FORMAT G_VARIANT_TYPE (OSTREE_SUMMARY_GVARIANT_STRING)
|
||||
@@ -187,6 +193,7 @@ typedef enum {
|
||||
* @OSTREE_REPO_MODE_ARCHIVE_Z2: Legacy alias for `OSTREE_REPO_MODE_ARCHIVE`
|
||||
* @OSTREE_REPO_MODE_BARE_USER: Files are stored as themselves, except ownership; can be written by user. Hardlinks work only in user checkouts.
|
||||
* @OSTREE_REPO_MODE_BARE_USER_ONLY: Same as BARE_USER, but all metadata is not stored, so it can only be used for user checkouts. Does not need xattrs.
|
||||
* @OSTREE_REPO_MODE_BARE_SPLIT_XATTRS: Same as BARE_USER, but xattrs are stored separately from file content, with dedicated object types.
|
||||
*
|
||||
* See the documentation of #OstreeRepo for more information about the
|
||||
* possible modes.
|
||||
@@ -197,6 +204,7 @@ typedef enum {
|
||||
OSTREE_REPO_MODE_ARCHIVE_Z2 = OSTREE_REPO_MODE_ARCHIVE,
|
||||
OSTREE_REPO_MODE_BARE_USER,
|
||||
OSTREE_REPO_MODE_BARE_USER_ONLY,
|
||||
OSTREE_REPO_MODE_BARE_SPLIT_XATTRS,
|
||||
} OstreeRepoMode;
|
||||
|
||||
/**
|
||||
|
||||
@@ -143,7 +143,7 @@ ostree_gpg_verify_result_lookup (OstreeGpgVerifyResult *result,
|
||||
* an invalid @signature_index. Use ostree_gpg_verify_result_count_all() to
|
||||
* find the number of signatures in @result.
|
||||
*
|
||||
* Returns: a new, floating, #GVariant tuple
|
||||
* Returns: (transfer floating): a new, floating, #GVariant tuple
|
||||
**/
|
||||
GVariant *
|
||||
ostree_gpg_verify_result_get (OstreeGpgVerifyResult *result,
|
||||
@@ -184,7 +184,7 @@ ostree_gpg_verify_result_get (OstreeGpgVerifyResult *result,
|
||||
* ostree_gpg_verify_result_count_all() to find the number of signatures in
|
||||
* @result.
|
||||
*
|
||||
* Returns: a new, floating, #GVariant tuple
|
||||
* Returns: (transfer floating): a new, floating, #GVariant tuple
|
||||
**/
|
||||
GVariant *
|
||||
ostree_gpg_verify_result_get_all (OstreeGpgVerifyResult *result,
|
||||
|
||||
@@ -298,7 +298,7 @@ ostree_gpg_verify_result_lookup (OstreeGpgVerifyResult *result,
|
||||
* an invalid @signature_index. Use ostree_gpg_verify_result_count_all() to
|
||||
* find the number of signatures in @result.
|
||||
*
|
||||
* Returns: a new, floating, #GVariant tuple
|
||||
* Returns: (transfer floating): a new, floating, #GVariant tuple
|
||||
**/
|
||||
GVariant *
|
||||
ostree_gpg_verify_result_get (OstreeGpgVerifyResult *result,
|
||||
@@ -490,7 +490,7 @@ ostree_gpg_verify_result_get (OstreeGpgVerifyResult *result,
|
||||
* ostree_gpg_verify_result_count_all() to find the number of signatures in
|
||||
* @result.
|
||||
*
|
||||
* Returns: a new, floating, #GVariant tuple
|
||||
* Returns: (transfer floating): a new, floating, #GVariant tuple
|
||||
**/
|
||||
GVariant *
|
||||
ostree_gpg_verify_result_get_all (OstreeGpgVerifyResult *result,
|
||||
|
||||
@@ -463,13 +463,11 @@ ostree_mutable_tree_ensure_parent_dirs (OstreeMutableTree *self,
|
||||
OstreeMutableTree *subdir = self; /* nofree */
|
||||
for (guint i = 0; i+1 < split_path->len; i++)
|
||||
{
|
||||
OstreeMutableTree *next;
|
||||
const char *name = split_path->pdata[i];
|
||||
|
||||
if (g_hash_table_lookup (subdir->files, name))
|
||||
return glnx_throw (error, "Can't replace file with directory: %s", name);
|
||||
|
||||
next = g_hash_table_lookup (subdir->subdirs, name);
|
||||
OstreeMutableTree *next = g_hash_table_lookup (subdir->subdirs, name);
|
||||
if (!next)
|
||||
{
|
||||
invalidate_contents_checksum (subdir);
|
||||
@@ -479,6 +477,9 @@ ostree_mutable_tree_ensure_parent_dirs (OstreeMutableTree *self,
|
||||
}
|
||||
|
||||
subdir = next;
|
||||
g_assert (subdir);
|
||||
if (!_ostree_mutable_tree_make_whole (subdir, NULL, error))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (out_parent)
|
||||
@@ -580,11 +581,9 @@ ostree_mutable_tree_walk (OstreeMutableTree *self,
|
||||
}
|
||||
else
|
||||
{
|
||||
OstreeMutableTree *subdir;
|
||||
if (!_ostree_mutable_tree_make_whole (self, NULL, error))
|
||||
return FALSE;
|
||||
|
||||
subdir = g_hash_table_lookup (self->subdirs, split_path->pdata[start]);
|
||||
OstreeMutableTree *subdir = g_hash_table_lookup (self->subdirs, split_path->pdata[start]);
|
||||
if (!subdir)
|
||||
return set_error_noent (error, (char*)split_path->pdata[start]);
|
||||
|
||||
|
||||
@@ -912,13 +912,17 @@ write_content_object (OstreeRepo *self,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
g_assert (expected_checksum != NULL || out_csum != NULL);
|
||||
|
||||
GLNX_AUTO_PREFIX_ERROR ("Writing content object", error);
|
||||
g_return_val_if_fail (expected_checksum || out_csum, FALSE);
|
||||
|
||||
if (g_cancellable_set_error_if_cancelled (cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
OstreeRepoMode repo_mode = ostree_repo_get_mode (self);
|
||||
if (repo_mode == OSTREE_REPO_MODE_BARE_SPLIT_XATTRS &&
|
||||
g_getenv ("OSTREE_EXP_WRITE_BARE_SPLIT_XATTRS") == NULL)
|
||||
return glnx_throw (error, "Not allowed due to repo mode");
|
||||
|
||||
GInputStream *file_input; /* Unowned alias */
|
||||
g_autoptr(GInputStream) file_input_owned = NULL; /* We need a temporary for bare-user symlinks */
|
||||
@@ -1244,9 +1248,10 @@ adopt_and_commit_regfile (OstreeRepo *self,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
g_assert (G_IN_SET (self->mode, OSTREE_REPO_MODE_BARE, OSTREE_REPO_MODE_BARE_USER_ONLY));
|
||||
|
||||
GLNX_AUTO_PREFIX_ERROR ("Commit regfile (adopt)", error);
|
||||
|
||||
g_assert (G_IN_SET (self->mode, OSTREE_REPO_MODE_BARE, OSTREE_REPO_MODE_BARE_USER_ONLY));
|
||||
g_autoptr(GBytes) header = _ostree_file_header_new (finfo, xattrs);
|
||||
|
||||
g_auto(OtChecksum) hasher = { 0, };
|
||||
@@ -1341,9 +1346,9 @@ write_metadata_object (OstreeRepo *self,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
GLNX_AUTO_PREFIX_ERROR ("Writing metadata object", error);
|
||||
g_assert (expected_checksum != NULL || out_csum != NULL);
|
||||
|
||||
g_return_val_if_fail (expected_checksum || out_csum, FALSE);
|
||||
GLNX_AUTO_PREFIX_ERROR ("Writing metadata object", error);
|
||||
|
||||
if (g_cancellable_set_error_if_cancelled (cancellable, error))
|
||||
return FALSE;
|
||||
@@ -1629,7 +1634,11 @@ ostree_repo_scan_hardlinks (OstreeRepo *self,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
g_return_val_if_fail (self->in_transaction == TRUE, FALSE);
|
||||
g_assert (self != NULL);
|
||||
g_assert (OSTREE_IS_REPO (self));
|
||||
|
||||
if (!self->in_transaction)
|
||||
return glnx_throw (error, "Failed to scan hardlinks, not in a transaction");
|
||||
|
||||
if (!self->loose_object_devino_hash)
|
||||
self->loose_object_devino_hash = (GHashTable*)ostree_repo_devino_cache_new ();
|
||||
@@ -1671,10 +1680,10 @@ ostree_repo_prepare_transaction (OstreeRepo *self,
|
||||
GError **error)
|
||||
{
|
||||
g_assert (self != NULL);
|
||||
g_assert (OSTREE_IS_REPO (self));
|
||||
|
||||
guint64 reserved_bytes = 0;
|
||||
|
||||
g_return_val_if_fail (self->in_transaction == FALSE, FALSE);
|
||||
if (self->in_transaction)
|
||||
return glnx_throw (error, "Failed to prepare transaction, another transaction is in progress");
|
||||
|
||||
g_debug ("Preparing transaction in repository %p", self);
|
||||
|
||||
@@ -1700,6 +1709,7 @@ ostree_repo_prepare_transaction (OstreeRepo *self,
|
||||
|
||||
g_mutex_lock (&self->txn_lock);
|
||||
self->txn.blocksize = stvfsbuf.f_bsize;
|
||||
guint64 reserved_bytes = 0;
|
||||
if (!ostree_repo_get_min_free_space_bytes (self, &reserved_bytes, error))
|
||||
{
|
||||
g_mutex_unlock (&self->txn_lock);
|
||||
@@ -2077,7 +2087,9 @@ ostree_repo_transaction_set_refspec (OstreeRepo *self,
|
||||
const char *refspec,
|
||||
const char *checksum)
|
||||
{
|
||||
g_return_if_fail (self->in_transaction == TRUE);
|
||||
g_assert (self != NULL);
|
||||
g_assert (OSTREE_IS_REPO (self));
|
||||
g_assert (self->in_transaction == TRUE);
|
||||
|
||||
g_mutex_lock (&self->txn_lock);
|
||||
ensure_txn_refs (self);
|
||||
@@ -2120,7 +2132,9 @@ ostree_repo_transaction_set_ref (OstreeRepo *self,
|
||||
const char *ref,
|
||||
const char *checksum)
|
||||
{
|
||||
g_return_if_fail (self->in_transaction == TRUE);
|
||||
g_assert (self != NULL);
|
||||
g_assert (OSTREE_IS_REPO (self));
|
||||
g_assert (self->in_transaction == TRUE);
|
||||
|
||||
char *refspec;
|
||||
if (remote)
|
||||
@@ -2160,9 +2174,13 @@ ostree_repo_transaction_set_collection_ref (OstreeRepo *self,
|
||||
const OstreeCollectionRef *ref,
|
||||
const char *checksum)
|
||||
{
|
||||
g_return_if_fail (OSTREE_IS_REPO (self));
|
||||
g_return_if_fail (self->in_transaction == TRUE);
|
||||
g_return_if_fail (ref != NULL);
|
||||
g_assert (self != NULL);
|
||||
g_assert (OSTREE_IS_REPO (self));
|
||||
g_assert (self->in_transaction == TRUE);
|
||||
g_assert (ref != NULL);
|
||||
|
||||
// TODO(lucab): introduce a method with error-returning in order to deprecate
|
||||
// this one, because it can silently fail.
|
||||
g_return_if_fail (checksum == NULL || ostree_validate_checksum_string (checksum, NULL));
|
||||
|
||||
g_mutex_lock (&self->txn_lock);
|
||||
@@ -2248,11 +2266,13 @@ ostree_repo_set_collection_ref_immediate (OstreeRepo *self,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
g_return_val_if_fail (OSTREE_IS_REPO (self), FALSE);
|
||||
g_return_val_if_fail (ref != NULL, FALSE);
|
||||
g_return_val_if_fail (checksum == NULL || ostree_validate_checksum_string (checksum, NULL), FALSE);
|
||||
g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE);
|
||||
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
|
||||
g_assert (self != NULL);
|
||||
g_assert (OSTREE_IS_REPO (self));
|
||||
g_assert (ref != NULL);
|
||||
|
||||
/* If a checksum was provided, validate it upfront. */
|
||||
if (checksum != NULL && !ostree_validate_checksum_string (checksum, error))
|
||||
return FALSE;
|
||||
|
||||
return _ostree_repo_write_ref (self, NULL, ref, checksum, NULL,
|
||||
cancellable, error);
|
||||
@@ -2283,7 +2303,11 @@ ostree_repo_commit_transaction (OstreeRepo *self,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
g_return_val_if_fail (self->in_transaction == TRUE, FALSE);
|
||||
g_assert (self != NULL);
|
||||
g_assert (OSTREE_IS_REPO (self));
|
||||
|
||||
if (!self->in_transaction)
|
||||
return glnx_throw (error, "Failed to commit transaction, no transaction in progress");
|
||||
|
||||
g_debug ("Committing transaction in repository %p", self);
|
||||
|
||||
@@ -2370,6 +2394,9 @@ ostree_repo_abort_transaction (OstreeRepo *self,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
g_assert (self != NULL);
|
||||
g_assert (OSTREE_IS_REPO (self));
|
||||
|
||||
g_autoptr(GError) cleanup_error = NULL;
|
||||
|
||||
/* Always ignore the cancellable to avoid the chance that, if it gets
|
||||
@@ -2478,12 +2505,8 @@ ostree_repo_write_metadata (OstreeRepo *self,
|
||||
normalized = g_variant_get_normal_form (object);
|
||||
}
|
||||
|
||||
/* For untrusted objects, verify their structure here */
|
||||
if (expected_checksum)
|
||||
{
|
||||
if (!_ostree_validate_structureof_metadata (objtype, object, error))
|
||||
return FALSE;
|
||||
}
|
||||
if (!_ostree_validate_structureof_metadata (objtype, object, error))
|
||||
return FALSE;
|
||||
|
||||
g_autoptr(GBytes) vdata = g_variant_get_data_as_bytes (normalized);
|
||||
if (!write_metadata_object (self, objtype, expected_checksum,
|
||||
|
||||
@@ -364,6 +364,7 @@ aic_ensure_parent_dir (OstreeRepoArchiveImportContext *ctx,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
GLNX_AUTO_PREFIX_ERROR ("ostree-tar: Failed to create parent", error);
|
||||
/* Who should own the parent dir? Since it's not in the archive, it's up to
|
||||
* us. Here, we use the heuristic of simply creating it as the same user as
|
||||
* the owner of the archive entry for which we're creating the dir. This is OK
|
||||
@@ -452,6 +453,7 @@ aic_get_xattrs (OstreeRepoArchiveImportContext *ctx,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
GLNX_AUTO_PREFIX_ERROR ("ostree-tar: Failed to get xattrs", error);
|
||||
g_autofree char *abspath = g_build_filename ("/", path, NULL);
|
||||
g_autoptr(GVariant) xattrs = NULL;
|
||||
const char *cb_path = abspath;
|
||||
@@ -526,6 +528,7 @@ aic_handle_dir (OstreeRepoArchiveImportContext *ctx,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
GLNX_AUTO_PREFIX_ERROR ("ostree-tar: Failed to handle directory", error);
|
||||
const char *name = glnx_basename (path);
|
||||
g_autoptr(GVariant) xattrs = NULL;
|
||||
|
||||
@@ -575,6 +578,7 @@ aic_import_file (OstreeRepoArchiveImportContext *ctx,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
GLNX_AUTO_PREFIX_ERROR ("ostree-tar: Failed to import file", error);
|
||||
const char *name = glnx_basename (path);
|
||||
g_autoptr(GVariant) xattrs = NULL;
|
||||
g_autofree char *csum = NULL;
|
||||
@@ -631,6 +635,7 @@ aic_handle_file (OstreeRepoArchiveImportContext *ctx,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
GLNX_AUTO_PREFIX_ERROR ("ostree-tar: Failed to handle file", error);
|
||||
/* The wonderful world of hardlinks and archives. We have to be very careful
|
||||
* here. Do not assume that if a file is a hardlink, it will have size 0 (e.g.
|
||||
* cpio). Do not assume that if a file will have hardlinks to it, it will have
|
||||
@@ -784,10 +789,10 @@ aic_import_deferred_hardlinks_for (OstreeRepoArchiveImportContext *ctx,
|
||||
/* rewrite the target so it points to the csum of the payload hardlink */
|
||||
if (payload)
|
||||
if (!aic_import_from_hardlink (ctx, target, payload->data, error))
|
||||
return FALSE;
|
||||
return glnx_prefix_error (error, "Failed importing hardlink");
|
||||
|
||||
if (!aic_lookup_file_csum (ctx, target, &csum, error))
|
||||
return FALSE;
|
||||
return glnx_prefix_error (error, "Failed to find object");
|
||||
|
||||
/* import all the hardlinks */
|
||||
for (GSList *hl = hardlinks; hl != NULL; hl = g_slist_next (hl))
|
||||
@@ -799,7 +804,7 @@ aic_import_deferred_hardlinks_for (OstreeRepoArchiveImportContext *ctx,
|
||||
continue; /* small optimization; no need to redo this one */
|
||||
|
||||
if (!ostree_mutable_tree_replace_file (df->parent, name, csum, error))
|
||||
return FALSE;
|
||||
return glnx_prefix_error (error, "Failed to replace file");
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
@@ -813,7 +818,7 @@ aic_import_deferred_hardlinks (OstreeRepoArchiveImportContext *ctx,
|
||||
GLNX_HASH_TABLE_FOREACH_KV (ctx->deferred_hardlinks, const char*, target, GSList*, links)
|
||||
{
|
||||
if (!aic_import_deferred_hardlinks_for (ctx, target, links, error))
|
||||
return FALSE;
|
||||
return glnx_prefix_error (error, "ostree-tar: Processing deferred hardlink %s", target);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@@ -63,6 +63,7 @@ G_BEGIN_DECLS
|
||||
/* Well-known keys for the additional metadata field in a commit in a ref entry
|
||||
* in a summary file. */
|
||||
#define OSTREE_COMMIT_TIMESTAMP "ostree.commit.timestamp"
|
||||
#define OSTREE_COMMIT_VERSION "ostree.commit.version"
|
||||
|
||||
typedef enum {
|
||||
OSTREE_REPO_TEST_ERROR_PRE_COMMIT = (1 << 0),
|
||||
|
||||
@@ -48,6 +48,10 @@ maybe_prune_loose_object (OtPruneData *data,
|
||||
OstreeObjectType objtype;
|
||||
|
||||
ostree_object_name_deserialize (key, &checksum, &objtype);
|
||||
/* Return if we only want to delete commits and this object is not a commit object. */
|
||||
gboolean commit_only = flags & OSTREE_REPO_PRUNE_FLAGS_COMMIT_ONLY;
|
||||
if (commit_only && (objtype != OSTREE_OBJECT_TYPE_COMMIT))
|
||||
goto exit;
|
||||
|
||||
if (g_hash_table_lookup_extended (data->reachable, key, NULL, NULL))
|
||||
reachable = TRUE;
|
||||
@@ -125,7 +129,11 @@ maybe_prune_loose_object (OtPruneData *data,
|
||||
else
|
||||
data->n_reachable_content++;
|
||||
}
|
||||
|
||||
if (commit_only && (objtype != OSTREE_OBJECT_TYPE_COMMIT))
|
||||
{
|
||||
g_debug ("Keeping object (not commit) %s.%s", checksum,
|
||||
ostree_object_type_to_string (objtype));
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@@ -299,6 +307,52 @@ repo_prune_internal (OstreeRepo *self,
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
traverse_reachable_internal (OstreeRepo *self,
|
||||
OstreeRepoCommitTraverseFlags flags,
|
||||
guint depth,
|
||||
GHashTable *reachable,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
g_autoptr(OstreeRepoAutoLock) lock =
|
||||
ostree_repo_auto_lock_push (self, OSTREE_REPO_LOCK_SHARED, cancellable, error);
|
||||
if (!lock)
|
||||
return FALSE;
|
||||
|
||||
/* Ignoring collections. */
|
||||
g_autoptr(GHashTable) all_refs = NULL; /* (element-type utf8 utf8) */
|
||||
|
||||
if (!ostree_repo_list_refs (self, NULL, &all_refs,
|
||||
cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
GLNX_HASH_TABLE_FOREACH_V (all_refs, const char*, checksum)
|
||||
{
|
||||
g_debug ("Finding objects to keep for commit %s", checksum);
|
||||
if (!ostree_repo_traverse_commit_with_flags (self, flags, checksum, depth, reachable,
|
||||
NULL, cancellable, error))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Using collections. */
|
||||
g_autoptr(GHashTable) all_collection_refs = NULL; /* (element-type OstreeChecksumRef utf8) */
|
||||
|
||||
if (!ostree_repo_list_collection_refs (self, NULL, &all_collection_refs,
|
||||
OSTREE_REPO_LIST_REFS_EXT_EXCLUDE_REMOTES, cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
GLNX_HASH_TABLE_FOREACH_V (all_collection_refs, const char*, checksum)
|
||||
{
|
||||
g_debug ("Finding objects to keep for commit %s", checksum);
|
||||
if (!ostree_repo_traverse_commit_with_flags (self, flags, checksum, depth, reachable,
|
||||
NULL, cancellable, error))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* ostree_repo_traverse_reachable_refs:
|
||||
* @self: Repo
|
||||
@@ -319,42 +373,10 @@ ostree_repo_traverse_reachable_refs (OstreeRepo *self,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
g_autoptr(OstreeRepoAutoLock) lock =
|
||||
ostree_repo_auto_lock_push (self, OSTREE_REPO_LOCK_SHARED, cancellable, error);
|
||||
if (!lock)
|
||||
return FALSE;
|
||||
|
||||
/* Ignoring collections. */
|
||||
g_autoptr(GHashTable) all_refs = NULL; /* (element-type utf8 utf8) */
|
||||
|
||||
if (!ostree_repo_list_refs (self, NULL, &all_refs,
|
||||
cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
GLNX_HASH_TABLE_FOREACH_V (all_refs, const char*, checksum)
|
||||
{
|
||||
g_debug ("Finding objects to keep for commit %s", checksum);
|
||||
if (!ostree_repo_traverse_commit_union (self, checksum, depth, reachable,
|
||||
cancellable, error))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Using collections. */
|
||||
g_autoptr(GHashTable) all_collection_refs = NULL; /* (element-type OstreeChecksumRef utf8) */
|
||||
|
||||
if (!ostree_repo_list_collection_refs (self, NULL, &all_collection_refs,
|
||||
OSTREE_REPO_LIST_REFS_EXT_EXCLUDE_REMOTES, cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
GLNX_HASH_TABLE_FOREACH_V (all_collection_refs, const char*, checksum)
|
||||
{
|
||||
g_debug ("Finding objects to keep for commit %s", checksum);
|
||||
if (!ostree_repo_traverse_commit_union (self, checksum, depth, reachable,
|
||||
cancellable, error))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
return traverse_reachable_internal (self,
|
||||
OSTREE_REPO_COMMIT_TRAVERSE_FLAG_NONE,
|
||||
depth, reachable,
|
||||
cancellable, error);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -401,6 +423,7 @@ ostree_repo_prune (OstreeRepo *self,
|
||||
|
||||
g_autoptr(GHashTable) objects = NULL;
|
||||
gboolean refs_only = flags & OSTREE_REPO_PRUNE_FLAGS_REFS_ONLY;
|
||||
gboolean commit_only = flags & OSTREE_REPO_PRUNE_FLAGS_COMMIT_ONLY;
|
||||
|
||||
g_autoptr(GHashTable) reachable = ostree_repo_traverse_new_reachable ();
|
||||
|
||||
@@ -409,9 +432,15 @@ ostree_repo_prune (OstreeRepo *self,
|
||||
* the deletion.
|
||||
*/
|
||||
|
||||
OstreeRepoCommitTraverseFlags traverse_flags = OSTREE_REPO_COMMIT_TRAVERSE_FLAG_NONE;
|
||||
if (commit_only)
|
||||
traverse_flags |= OSTREE_REPO_COMMIT_TRAVERSE_FLAG_COMMIT_ONLY;
|
||||
|
||||
if (refs_only)
|
||||
{
|
||||
if (!ostree_repo_traverse_reachable_refs (self, depth, reachable, cancellable, error))
|
||||
if (!traverse_reachable_internal (self, traverse_flags,
|
||||
depth, reachable,
|
||||
cancellable, error))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
@@ -432,8 +461,8 @@ ostree_repo_prune (OstreeRepo *self,
|
||||
continue;
|
||||
|
||||
g_debug ("Finding objects to keep for commit %s", checksum);
|
||||
if (!ostree_repo_traverse_commit_union (self, checksum, depth, reachable,
|
||||
cancellable, error))
|
||||
if (!ostree_repo_traverse_commit_with_flags (self, traverse_flags, checksum, depth, reachable,
|
||||
NULL, cancellable, error))
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2244,6 +2244,9 @@ process_one_static_delta (OtPullData *pull_data,
|
||||
ref, cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
if (!ostree_repo_mark_commit_partial (pull_data->repo, to_revision, TRUE, error))
|
||||
return FALSE;
|
||||
|
||||
if (detached_data && !ostree_repo_write_commit_detached_metadata (pull_data->repo,
|
||||
to_revision,
|
||||
detached_data,
|
||||
|
||||
@@ -396,7 +396,6 @@ _ostree_repo_resolve_rev_internal (OstreeRepo *self,
|
||||
{
|
||||
ret_rev = g_strdup (refspec);
|
||||
}
|
||||
|
||||
else if (!ostree_repo_resolve_partial_checksum (self, refspec, &ret_rev, error))
|
||||
return FALSE;
|
||||
|
||||
|
||||
@@ -542,8 +542,9 @@ traverse_dirtree (OstreeRepo *repo,
|
||||
}
|
||||
|
||||
/**
|
||||
* ostree_repo_traverse_commit_union_with_parents: (skip)
|
||||
* ostree_repo_traverse_commit_with_flags: (skip)
|
||||
* @repo: Repo
|
||||
* @flags: change traversal behaviour according to these flags
|
||||
* @commit_checksum: ASCII SHA256 checksum
|
||||
* @maxdepth: Traverse this many parent commits, -1 for unlimited
|
||||
* @inout_reachable: Set of reachable objects
|
||||
@@ -561,15 +562,17 @@ traverse_dirtree (OstreeRepo *repo,
|
||||
* Since: 2018.5
|
||||
*/
|
||||
gboolean
|
||||
ostree_repo_traverse_commit_union_with_parents (OstreeRepo *repo,
|
||||
const char *commit_checksum,
|
||||
int maxdepth,
|
||||
GHashTable *inout_reachable,
|
||||
GHashTable *inout_parents,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
ostree_repo_traverse_commit_with_flags (OstreeRepo *repo,
|
||||
OstreeRepoCommitTraverseFlags flags,
|
||||
const char *commit_checksum,
|
||||
int maxdepth,
|
||||
GHashTable *inout_reachable,
|
||||
GHashTable *inout_parents,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
g_autofree char *tmp_checksum = NULL;
|
||||
gboolean commit_only = flags & OSTREE_REPO_COMMIT_TRAVERSE_FLAG_COMMIT_ONLY;
|
||||
|
||||
while (TRUE)
|
||||
{
|
||||
@@ -603,16 +606,20 @@ ostree_repo_traverse_commit_union_with_parents (OstreeRepo *repo,
|
||||
|
||||
g_hash_table_add (inout_reachable, g_variant_ref (key));
|
||||
|
||||
g_debug ("Traversing commit %s", commit_checksum);
|
||||
ostree_cleanup_repo_commit_traverse_iter
|
||||
OstreeRepoCommitTraverseIter iter = { 0, };
|
||||
if (!ostree_repo_commit_traverse_iter_init_commit (&iter, repo, commit,
|
||||
OSTREE_REPO_COMMIT_TRAVERSE_FLAG_NONE,
|
||||
error))
|
||||
return FALSE;
|
||||
/* Save time by skipping traversal of non-commit objects */
|
||||
if (!commit_only)
|
||||
{
|
||||
g_debug ("Traversing commit %s", commit_checksum);
|
||||
ostree_cleanup_repo_commit_traverse_iter
|
||||
OstreeRepoCommitTraverseIter iter = { 0, };
|
||||
if (!ostree_repo_commit_traverse_iter_init_commit (&iter, repo, commit,
|
||||
OSTREE_REPO_COMMIT_TRAVERSE_FLAG_NONE,
|
||||
error))
|
||||
return FALSE;
|
||||
|
||||
if (!traverse_iter (repo, &iter, key, inout_reachable, inout_parents, ignore_missing_dirs, cancellable, error))
|
||||
return FALSE;
|
||||
if (!traverse_iter (repo, &iter, key, inout_reachable, inout_parents, ignore_missing_dirs, cancellable, error))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
gboolean recurse = FALSE;
|
||||
if (maxdepth == -1 || maxdepth > 0)
|
||||
@@ -634,6 +641,39 @@ ostree_repo_traverse_commit_union_with_parents (OstreeRepo *repo,
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* ostree_repo_traverse_commit_union_with_parents: (skip)
|
||||
* @repo: Repo
|
||||
* @commit_checksum: ASCII SHA256 checksum
|
||||
* @maxdepth: Traverse this many parent commits, -1 for unlimited
|
||||
* @inout_reachable: Set of reachable objects
|
||||
* @inout_parents: Map from object to parent object
|
||||
* @cancellable: Cancellable
|
||||
* @error: Error
|
||||
*
|
||||
* Update the set @inout_reachable containing all objects reachable
|
||||
* from @commit_checksum, traversing @maxdepth parent commits.
|
||||
*
|
||||
* Additionally this constructs a mapping from each object to the parents
|
||||
* of the object, which can be used to track which commits an object
|
||||
* belongs to.
|
||||
*
|
||||
* Since: 2018.5
|
||||
*/
|
||||
gboolean
|
||||
ostree_repo_traverse_commit_union_with_parents (OstreeRepo *repo,
|
||||
const char *commit_checksum,
|
||||
int maxdepth,
|
||||
GHashTable *inout_reachable,
|
||||
GHashTable *inout_parents,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
return ostree_repo_traverse_commit_with_flags(repo, OSTREE_REPO_COMMIT_TRAVERSE_FLAG_NONE,
|
||||
commit_checksum, maxdepth, inout_reachable, inout_parents,
|
||||
cancellable, error);
|
||||
}
|
||||
|
||||
/**
|
||||
* ostree_repo_traverse_commit_union: (skip)
|
||||
* @repo: Repo
|
||||
|
||||
@@ -2704,6 +2704,9 @@ ostree_repo_mode_to_string (OstreeRepoMode mode,
|
||||
/* Legacy alias */
|
||||
ret_mode ="archive-z2";
|
||||
break;
|
||||
case OSTREE_REPO_MODE_BARE_SPLIT_XATTRS:
|
||||
ret_mode = "bare-split-xattrs";
|
||||
break;
|
||||
default:
|
||||
return glnx_throw (error, "Invalid mode '%d'", mode);
|
||||
}
|
||||
@@ -2734,6 +2737,8 @@ ostree_repo_mode_from_string (const char *mode,
|
||||
else if (strcmp (mode, "archive-z2") == 0 ||
|
||||
strcmp (mode, "archive") == 0)
|
||||
ret_mode = OSTREE_REPO_MODE_ARCHIVE;
|
||||
else if (strcmp (mode, "bare-split-xattrs") == 0)
|
||||
ret_mode = OSTREE_REPO_MODE_BARE_SPLIT_XATTRS;
|
||||
else
|
||||
return glnx_throw (error, "Invalid mode '%s' in repository configuration", mode);
|
||||
|
||||
@@ -4171,6 +4176,32 @@ repo_load_file_archive (OstreeRepo *self,
|
||||
}
|
||||
}
|
||||
|
||||
static GVariant *
|
||||
_ostree_repo_read_xattrs_file_link (OstreeRepo *self,
|
||||
const char *checksum,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
g_assert (self != NULL);
|
||||
g_assert (checksum != NULL);
|
||||
|
||||
char xattr_path[_OSTREE_LOOSE_PATH_MAX];
|
||||
_ostree_loose_path (xattr_path, checksum, OSTREE_OBJECT_TYPE_FILE_XATTRS_LINK, self->mode);
|
||||
|
||||
g_autoptr(GVariant) xattrs = NULL;
|
||||
glnx_autofd int fd = -1;
|
||||
if (!glnx_openat_rdonly (self->objects_dir_fd, xattr_path, FALSE, &fd, error))
|
||||
return FALSE;
|
||||
|
||||
g_assert (fd >= 0);
|
||||
if (!ot_variant_read_fd (fd, 0, G_VARIANT_TYPE ("a(ayay)"), TRUE,
|
||||
&xattrs, error))
|
||||
return glnx_prefix_error_null (error, "Deserializing xattrs content");
|
||||
|
||||
g_assert (xattrs != NULL);
|
||||
return g_steal_pointer (&xattrs);
|
||||
}
|
||||
|
||||
gboolean
|
||||
_ostree_repo_load_file_bare (OstreeRepo *self,
|
||||
const char *checksum,
|
||||
@@ -4218,8 +4249,9 @@ _ostree_repo_load_file_bare (OstreeRepo *self,
|
||||
cancellable, error);
|
||||
}
|
||||
|
||||
const gboolean need_open =
|
||||
(out_fd || out_xattrs || self->mode == OSTREE_REPO_MODE_BARE_USER);
|
||||
const gboolean need_open = (out_fd ||
|
||||
(out_xattrs && self->mode == OSTREE_REPO_MODE_BARE) ||
|
||||
self->mode == OSTREE_REPO_MODE_BARE_USER);
|
||||
/* If it's a regular file and we're requested to return the fd, do it now. As
|
||||
* a special case in bare-user, we always do an open, since the stat() metadata
|
||||
* lives there.
|
||||
@@ -4284,10 +4316,8 @@ _ostree_repo_load_file_bare (OstreeRepo *self,
|
||||
ret_xattrs = g_variant_ref_sink (g_variant_builder_end (&builder));
|
||||
}
|
||||
}
|
||||
else
|
||||
else if (self->mode == OSTREE_REPO_MODE_BARE)
|
||||
{
|
||||
g_assert (self->mode == OSTREE_REPO_MODE_BARE);
|
||||
|
||||
if (S_ISREG (stbuf.st_mode) && out_xattrs)
|
||||
{
|
||||
if (self->disable_xattrs)
|
||||
@@ -4306,6 +4336,19 @@ _ostree_repo_load_file_bare (OstreeRepo *self,
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
else if (self->mode == OSTREE_REPO_MODE_BARE_SPLIT_XATTRS)
|
||||
{
|
||||
if (out_xattrs)
|
||||
{
|
||||
ret_xattrs = _ostree_repo_read_xattrs_file_link(self, checksum, cancellable, error);
|
||||
if (ret_xattrs == NULL)
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
|
||||
if (out_fd)
|
||||
*out_fd = glnx_steal_fd (&fd);
|
||||
@@ -6064,10 +6107,11 @@ summary_add_ref_entry (OstreeRepo *self,
|
||||
g_autoptr(GVariant) commit_obj = NULL;
|
||||
if (!ostree_repo_load_variant (self, OSTREE_OBJECT_TYPE_COMMIT, checksum, &commit_obj, error))
|
||||
return FALSE;
|
||||
g_autoptr(GVariant) orig_metadata = g_variant_get_child_value (commit_obj, 0);
|
||||
|
||||
g_variant_dict_init (&commit_metadata_builder, NULL);
|
||||
|
||||
/* Forward the commit’s timestamp if it’s valid. */
|
||||
/* Forward the commit’s timestamp and version if they're valid. */
|
||||
guint64 commit_timestamp = ostree_commit_get_timestamp (commit_obj);
|
||||
g_autoptr(GDateTime) dt = g_date_time_new_from_unix_utc (commit_timestamp);
|
||||
|
||||
@@ -6075,6 +6119,10 @@ summary_add_ref_entry (OstreeRepo *self,
|
||||
g_variant_dict_insert_value (&commit_metadata_builder, OSTREE_COMMIT_TIMESTAMP,
|
||||
g_variant_new_uint64 (GUINT64_TO_BE (commit_timestamp)));
|
||||
|
||||
const char *version = NULL;
|
||||
if (g_variant_lookup (orig_metadata, OSTREE_COMMIT_META_KEY_VERSION, "&s", &version))
|
||||
g_variant_dict_insert (&commit_metadata_builder, OSTREE_COMMIT_VERSION, "s", version);
|
||||
|
||||
g_variant_builder_add_value (refs_builder,
|
||||
g_variant_new ("(s(t@ay@a{sv}))", ref,
|
||||
(guint64) g_variant_get_size (commit_obj),
|
||||
|
||||
@@ -1118,6 +1118,16 @@ typedef enum {
|
||||
OSTREE_STATIC_DELTA_INDEX_FLAGS_NONE = 0,
|
||||
} OstreeStaticDeltaIndexFlags;
|
||||
|
||||
/**
|
||||
* OstreeRepoCommitTraverseFlags:
|
||||
* @OSTREE_REPO_COMMIT_TRAVERSE_FLAG_NONE: No special options for traverse
|
||||
* @OSTREE_REPO_COMMIT_TRAVERSE_FLAG_COMMIT_ONLY: Traverse and retrieve only commit objects. (Since: 2022.2)
|
||||
*/
|
||||
typedef enum {
|
||||
OSTREE_REPO_COMMIT_TRAVERSE_FLAG_NONE = (1 << 0),
|
||||
OSTREE_REPO_COMMIT_TRAVERSE_FLAG_COMMIT_ONLY = (1 << 1),
|
||||
} OstreeRepoCommitTraverseFlags;
|
||||
|
||||
_OSTREE_PUBLIC
|
||||
gboolean ostree_repo_static_delta_reindex (OstreeRepo *repo,
|
||||
OstreeStaticDeltaIndexFlags flags,
|
||||
@@ -1171,6 +1181,7 @@ gboolean ostree_repo_traverse_commit_union (OstreeRepo *repo,
|
||||
GHashTable *inout_reachable,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
_OSTREE_PUBLIC
|
||||
gboolean ostree_repo_traverse_commit_union_with_parents (OstreeRepo *repo,
|
||||
const char *commit_checksum,
|
||||
@@ -1180,6 +1191,16 @@ gboolean ostree_repo_traverse_commit_union_with_parents (OstreeRepo *rep
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
_OSTREE_PUBLIC
|
||||
gboolean ostree_repo_traverse_commit_with_flags (OstreeRepo *repo,
|
||||
OstreeRepoCommitTraverseFlags flags,
|
||||
const char *commit_checksum,
|
||||
int maxdepth,
|
||||
GHashTable *inout_reachable,
|
||||
GHashTable *inout_parents,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
struct _OstreeRepoCommitTraverseIter {
|
||||
gboolean initialized;
|
||||
/* 4 byte hole on 64 bit */
|
||||
@@ -1189,10 +1210,6 @@ struct _OstreeRepoCommitTraverseIter {
|
||||
|
||||
typedef struct _OstreeRepoCommitTraverseIter OstreeRepoCommitTraverseIter;
|
||||
|
||||
typedef enum {
|
||||
OSTREE_REPO_COMMIT_TRAVERSE_FLAG_NONE = (1 << 0)
|
||||
} OstreeRepoCommitTraverseFlags;
|
||||
|
||||
_OSTREE_PUBLIC
|
||||
gboolean
|
||||
ostree_repo_commit_traverse_iter_init_commit (OstreeRepoCommitTraverseIter *iter,
|
||||
@@ -1245,11 +1262,13 @@ void ostree_repo_commit_traverse_iter_cleanup (void *p);
|
||||
* @OSTREE_REPO_PRUNE_FLAGS_NONE: No special options for pruning
|
||||
* @OSTREE_REPO_PRUNE_FLAGS_NO_PRUNE: Don't actually delete objects
|
||||
* @OSTREE_REPO_PRUNE_FLAGS_REFS_ONLY: Do not traverse individual commit objects, only follow refs
|
||||
* @OSTREE_REPO_PRUNE_FLAGS_COMMIT_ONLY: Only traverse commit objects. (Since 2022.2)
|
||||
*/
|
||||
typedef enum {
|
||||
OSTREE_REPO_PRUNE_FLAGS_NONE = 0,
|
||||
OSTREE_REPO_PRUNE_FLAGS_NO_PRUNE = (1 << 0),
|
||||
OSTREE_REPO_PRUNE_FLAGS_REFS_ONLY = (1 << 1),
|
||||
OSTREE_REPO_PRUNE_FLAGS_COMMIT_ONLY = (1 << 2),
|
||||
} OstreeRepoPruneFlags;
|
||||
|
||||
_OSTREE_PUBLIC
|
||||
|
||||
@@ -422,7 +422,6 @@ initable_init (GInitable *initable,
|
||||
{
|
||||
const char *policy_rootpath = gs_file_get_path_cached (policy_root);
|
||||
|
||||
g_setenv ("LIBSELINUX_DISABLE_PCRE_PRECOMPILED", "1", FALSE);
|
||||
if (selinux_set_policy_root (policy_rootpath) != 0)
|
||||
return glnx_throw_errno_prefix (error, "selinux_set_policy_root(%s)", policy_rootpath);
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include <sys/statvfs.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <stdbool.h>
|
||||
#include <sys/poll.h>
|
||||
#include <linux/fs.h>
|
||||
#include <err.h>
|
||||
@@ -1557,6 +1558,39 @@ typedef struct {
|
||||
guint64 extra_syncfs_msec;
|
||||
} SyncStats;
|
||||
|
||||
typedef struct {
|
||||
int ref_count; /* atomic */
|
||||
bool success;
|
||||
GMutex mutex;
|
||||
GCond cond;
|
||||
} SyncData;
|
||||
|
||||
static void
|
||||
sync_data_unref (SyncData *data)
|
||||
{
|
||||
if (!g_atomic_int_dec_and_test (&data->ref_count))
|
||||
return;
|
||||
g_mutex_clear (&data->mutex);
|
||||
g_cond_clear (&data->cond);
|
||||
g_free (data);
|
||||
}
|
||||
|
||||
// An asynchronous sync() call. See below.
|
||||
static void *
|
||||
sync_in_thread (void *ptr)
|
||||
{
|
||||
SyncData *syncdata = ptr;
|
||||
// Ensure that the caller is blocked waiting
|
||||
g_mutex_lock (&syncdata->mutex);
|
||||
sync ();
|
||||
// Signal success
|
||||
syncdata->success = true;
|
||||
g_cond_broadcast (&syncdata->cond);
|
||||
g_mutex_unlock (&syncdata->mutex);
|
||||
sync_data_unref (syncdata);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* First, sync the root directory as well as /var and /boot which may
|
||||
* be separate mount points. Then *in addition*, do a global
|
||||
* `sync()`.
|
||||
@@ -1589,9 +1623,44 @@ full_system_sync (OstreeSysroot *self,
|
||||
* actually get error codes out of that API, and we more clearly
|
||||
* delineate what we actually want to sync in the future when this
|
||||
* global sync call is removed.
|
||||
*
|
||||
* Due to https://bugzilla.redhat.com/show_bug.cgi?id=2003532 which is
|
||||
* a bug in the interaction between Ceph and systemd, we have a capped
|
||||
* timeout of 5s on the sync here. In general, we don't actually want
|
||||
* to "own" flushing data on *all* mounted filesystems as part of
|
||||
* our process. Again, we're only keeping the `sync` here out of
|
||||
* conservatisim. We could likely just remove it and e.g. systemd
|
||||
* would take care of sync as part of unmounting individual filesystems.
|
||||
*/
|
||||
start_msec = g_get_monotonic_time () / 1000;
|
||||
sync ();
|
||||
if (!(self->opt_flags & OSTREE_SYSROOT_GLOBAL_OPT_SKIP_SYNC))
|
||||
{
|
||||
SyncData *syncdata = g_new (SyncData, 1);
|
||||
syncdata->ref_count = 2; // One for us, one for thread
|
||||
syncdata->success = FALSE;
|
||||
g_mutex_init (&syncdata->mutex);
|
||||
g_cond_init (&syncdata->cond);
|
||||
g_mutex_lock (&syncdata->mutex);
|
||||
GThread *worker = g_thread_new ("ostree sync", sync_in_thread, syncdata);
|
||||
// Wait up to 5 seconds for sync() to finish
|
||||
gint64 end_time = g_get_monotonic_time () + (5 * G_TIME_SPAN_SECOND);
|
||||
while (!syncdata->success)
|
||||
{
|
||||
if (!g_cond_wait_until (&syncdata->cond, &syncdata->mutex, end_time))
|
||||
{
|
||||
ot_journal_print (LOG_INFO, "Timed out waiting for global sync()");
|
||||
break;
|
||||
}
|
||||
}
|
||||
g_mutex_unlock (&syncdata->mutex);
|
||||
sync_data_unref (syncdata);
|
||||
// We can't join the thread here, for two reasons. First, the whole
|
||||
// point here is to avoid blocking on `sync`. The other reason is
|
||||
// today there is no return value on success from `sync`, so there's
|
||||
// no point to joining the thread even if we had a nonblocking mechanism
|
||||
// to do so.
|
||||
g_thread_unref (worker);
|
||||
}
|
||||
end_msec = g_get_monotonic_time () / 1000;
|
||||
out_stats->extra_syncfs_msec = (end_msec - start_msec);
|
||||
|
||||
@@ -2097,7 +2166,7 @@ swap_bootloader (OstreeSysroot *sysroot,
|
||||
**/
|
||||
if (bootloader)
|
||||
{
|
||||
if (!_ostree_bootloader_post_bls_sync (bootloader, cancellable, error))
|
||||
if (!_ostree_bootloader_post_bls_sync (bootloader, new_bootversion, cancellable, error))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
@@ -2424,6 +2493,10 @@ ostree_sysroot_write_deployments_with_options (OstreeSysroot *self,
|
||||
if (!_ostree_sysroot_rmrf_deployment (self, self->staged_deployment, cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
/* Delete the lock if there was any. */
|
||||
if (!ot_ensure_unlinked_at (AT_FDCWD, _OSTREE_SYSROOT_RUNSTATE_STAGED_LOCKED, error))
|
||||
return FALSE;
|
||||
|
||||
/* Clear it out of the *current* deployments list to maintain invariants */
|
||||
self->staged_deployment = NULL;
|
||||
g_ptr_array_remove_index (self->deployments, 0);
|
||||
|
||||
@@ -38,6 +38,11 @@ typedef enum {
|
||||
OSTREE_SYSROOT_DEBUG_TEST_NO_DTB = 1 << 3, /* https://github.com/ostreedev/ostree/issues/2154 */
|
||||
} OstreeSysrootDebugFlags;
|
||||
|
||||
typedef enum {
|
||||
/* Skip invoking `sync()` */
|
||||
OSTREE_SYSROOT_GLOBAL_OPT_SKIP_SYNC = 1 << 0,
|
||||
} OstreeSysrootGlobalOptFlags;
|
||||
|
||||
typedef enum {
|
||||
OSTREE_SYSROOT_LOAD_STATE_NONE, /* ostree_sysroot_new() was called */
|
||||
OSTREE_SYSROOT_LOAD_STATE_INIT, /* We've loaded basic sysroot state and have an fd */
|
||||
@@ -75,6 +80,7 @@ struct OstreeSysroot {
|
||||
/* Only access through ostree_sysroot_[_get]repo() */
|
||||
OstreeRepo *repo;
|
||||
|
||||
OstreeSysrootGlobalOptFlags opt_flags;
|
||||
OstreeSysrootDebugFlags debug_flags;
|
||||
};
|
||||
|
||||
|
||||
@@ -184,6 +184,9 @@ ostree_sysroot_class_init (OstreeSysrootClass *klass)
|
||||
static void
|
||||
ostree_sysroot_init (OstreeSysroot *self)
|
||||
{
|
||||
const GDebugKey globalopt_keys[] = {
|
||||
{ "skip-sync", OSTREE_SYSROOT_GLOBAL_OPT_SKIP_SYNC },
|
||||
};
|
||||
const GDebugKey keys[] = {
|
||||
{ "mutable-deployments", OSTREE_SYSROOT_DEBUG_MUTABLE_DEPLOYMENTS },
|
||||
{ "test-fifreeze", OSTREE_SYSROOT_DEBUG_TEST_FIFREEZE },
|
||||
@@ -191,6 +194,8 @@ ostree_sysroot_init (OstreeSysroot *self)
|
||||
{ "no-dtb", OSTREE_SYSROOT_DEBUG_TEST_NO_DTB },
|
||||
};
|
||||
|
||||
self->opt_flags = g_parse_debug_string (g_getenv ("OSTREE_SYSROOT_OPTS"),
|
||||
globalopt_keys, G_N_ELEMENTS (globalopt_keys));
|
||||
self->debug_flags = g_parse_debug_string (g_getenv ("OSTREE_SYSROOT_DEBUG"),
|
||||
keys, G_N_ELEMENTS (keys));
|
||||
|
||||
@@ -973,7 +978,7 @@ ostree_sysroot_initialize (OstreeSysroot *self,
|
||||
* we'll use it to sanity check that we found a booted deployment for example.
|
||||
* Second, we also find out whether sysroot == /.
|
||||
*/
|
||||
if (!glnx_fstatat_allow_noent (AT_FDCWD, "/run/ostree-booted", NULL, 0, error))
|
||||
if (!glnx_fstatat_allow_noent (AT_FDCWD, OSTREE_PATH_BOOTED, NULL, 0, error))
|
||||
return FALSE;
|
||||
const gboolean ostree_booted = (errno == 0);
|
||||
|
||||
@@ -1106,11 +1111,11 @@ sysroot_load_from_bootloader_configs (OstreeSysroot *self,
|
||||
return FALSE;
|
||||
if (errno == ENOENT)
|
||||
{
|
||||
return glnx_throw (error, "Unexpected state: /run/ostree-booted found, but no /boot/loader directory");
|
||||
return glnx_throw (error, "Unexpected state: %s found, but no /boot/loader directory", OSTREE_PATH_BOOTED);
|
||||
}
|
||||
else
|
||||
{
|
||||
return glnx_throw (error, "Unexpected state: /run/ostree-booted found and in / sysroot, but bootloader entry not found");
|
||||
return glnx_throw (error, "Unexpected state: %s found and in / sysroot, but bootloader entry not found", OSTREE_PATH_BOOTED);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,14 @@
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
/**
|
||||
* OSTREE_PATH_BOOTED:
|
||||
* Filesystem path that is created on an ostree-booted system.
|
||||
*
|
||||
* Since: 2022.2
|
||||
*/
|
||||
#define OSTREE_PATH_BOOTED "/run/ostree-booted"
|
||||
|
||||
#define OSTREE_TYPE_SYSROOT ostree_sysroot_get_type()
|
||||
#define OSTREE_SYSROOT(obj) \
|
||||
(G_TYPE_CHECK_INSTANCE_CAST ((obj), OSTREE_TYPE_SYSROOT, OstreeSysroot))
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
*
|
||||
* Since: 2017.4
|
||||
*/
|
||||
#define OSTREE_RELEASE_VERSION (1)
|
||||
#define OSTREE_RELEASE_VERSION (2)
|
||||
|
||||
/**
|
||||
* OSTREE_VERSION
|
||||
@@ -50,7 +50,7 @@
|
||||
*
|
||||
* Since: 2017.4
|
||||
*/
|
||||
#define OSTREE_VERSION (2022.1)
|
||||
#define OSTREE_VERSION (2022.2)
|
||||
|
||||
/**
|
||||
* OSTREE_VERSION_S:
|
||||
@@ -60,7 +60,7 @@
|
||||
*
|
||||
* Since: 2017.4
|
||||
*/
|
||||
#define OSTREE_VERSION_S "2022.1"
|
||||
#define OSTREE_VERSION_S "2022.2"
|
||||
|
||||
#define OSTREE_ENCODE_VERSION(year,release) \
|
||||
((year) << 16 | (release))
|
||||
@@ -98,4 +98,4 @@
|
||||
*
|
||||
* Since: 2019.3
|
||||
*/
|
||||
#define OSTREE_BUILT_FEATURES "libsoup gpgme ex-fsverity libarchive selinux avahi libmount systemd release p2p"
|
||||
#define OSTREE_BUILT_FEATURES "libsoup gpgme ex-fsverity libarchive selinux libmount systemd release p2p"
|
||||
Executable
+22
@@ -0,0 +1,22 @@
|
||||
#!/usr/bin/bash
|
||||
# This script creates new initramdisk with LUKS config within
|
||||
set -euo pipefail
|
||||
|
||||
old_initrd=$1
|
||||
new_initrd=$2
|
||||
|
||||
# Unpacking existing initramdisk
|
||||
workdir=$(mktemp -d -p /tmp se-initramfs-XXXXXX)
|
||||
cd ${workdir}
|
||||
gzip -cd ${old_initrd} | cpio -imd --quiet
|
||||
|
||||
# Adding LUKS root key and crypttab config
|
||||
mkdir -p etc/luks
|
||||
cp -f /etc/luks/root etc/luks/
|
||||
cp -f /etc/crypttab etc/
|
||||
|
||||
# Creating new initramdisk image
|
||||
find . | cpio --quiet -H newc -o | gzip -9 -n >> ${new_initrd}
|
||||
|
||||
# Cleanup
|
||||
rm -rf ${workdir}
|
||||
@@ -760,10 +760,10 @@ ot_variant_builder_info_new (OtVariantBuilder *builder, const GVariantType *type
|
||||
{
|
||||
OtVariantBuilderInfo *info;
|
||||
|
||||
info = (OtVariantBuilderInfo *) g_slice_new0 (OtVariantBuilderInfo);
|
||||
|
||||
g_return_val_if_fail (g_variant_type_is_container (type), NULL);
|
||||
|
||||
info = (OtVariantBuilderInfo *) g_slice_new0 (OtVariantBuilderInfo);
|
||||
|
||||
info->builder = builder;
|
||||
info->type = g_variant_type_copy (type);
|
||||
info->type_info = g_variant_type_info_get (type);
|
||||
@@ -830,7 +830,7 @@ static void
|
||||
ot_variant_builder_info_free (OtVariantBuilderInfo *info)
|
||||
{
|
||||
if (info->parent)
|
||||
ot_variant_builder_info_free (info);
|
||||
ot_variant_builder_info_free (info->parent);
|
||||
|
||||
g_variant_type_free (info->type);
|
||||
g_array_unref (info->child_ends);
|
||||
@@ -845,10 +845,10 @@ ot_variant_builder_new (const GVariantType *type,
|
||||
{
|
||||
OtVariantBuilder *builder;
|
||||
|
||||
builder = (OtVariantBuilder *) g_slice_new0 (OtVariantBuilder);
|
||||
|
||||
g_return_val_if_fail (g_variant_type_is_container (type), NULL);
|
||||
|
||||
builder = (OtVariantBuilder *) g_slice_new0 (OtVariantBuilder);
|
||||
|
||||
builder->head = ot_variant_builder_info_new (builder, type);
|
||||
builder->ref_count = 1;
|
||||
builder->fd = fd;
|
||||
|
||||
@@ -23,8 +23,13 @@
|
||||
|
||||
#include <gio/gio.h>
|
||||
#include <string.h> /* Yeah...let's just do that here. */
|
||||
#include <syslog.h>
|
||||
#include <libglnx.h>
|
||||
|
||||
#ifdef HAVE_LIBSYSTEMD
|
||||
#include <systemd/sd-journal.h>
|
||||
#endif
|
||||
|
||||
/* https://bugzilla.gnome.org/show_bug.cgi?id=766370 */
|
||||
#if !GLIB_CHECK_VERSION(2, 49, 3)
|
||||
#define OT_VARIANT_BUILDER_INITIALIZER {{0,}}
|
||||
|
||||
@@ -21,6 +21,8 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "ostree-sysroot-private.h"
|
||||
|
||||
#include "ot-main.h"
|
||||
#include "ot-admin-builtins.h"
|
||||
#include "ot-admin-functions.h"
|
||||
@@ -31,6 +33,7 @@
|
||||
|
||||
static gboolean opt_retain;
|
||||
static gboolean opt_stage;
|
||||
static gboolean opt_lock_finalization;
|
||||
static gboolean opt_retain_pending;
|
||||
static gboolean opt_retain_rollback;
|
||||
static gboolean opt_not_as_default;
|
||||
@@ -51,6 +54,7 @@ static GOptionEntry options[] = {
|
||||
{ "no-merge", 0, 0, G_OPTION_ARG_NONE, &opt_no_merge, "Do not apply configuration (/etc and kernel arguments) from booted deployment", NULL},
|
||||
{ "retain", 0, 0, G_OPTION_ARG_NONE, &opt_retain, "Do not delete previous deployments", NULL },
|
||||
{ "stage", 0, 0, G_OPTION_ARG_NONE, &opt_stage, "Complete deployment at OS shutdown", NULL },
|
||||
{ "lock-finalization", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &opt_lock_finalization, "Prevent automatic deployment finalization on shutdown", NULL },
|
||||
{ "retain-pending", 0, 0, G_OPTION_ARG_NONE, &opt_retain_pending, "Do not delete pending deployments", NULL },
|
||||
{ "retain-rollback", 0, 0, G_OPTION_ARG_NONE, &opt_retain_rollback, "Do not delete rollback deployments", NULL },
|
||||
{ "not-as-default", 0, 0, G_OPTION_ARG_NONE, &opt_not_as_default, "Append rather than prepend new deployment", NULL },
|
||||
@@ -202,6 +206,20 @@ ot_admin_builtin_deploy (int argc, char **argv, OstreeCommandInvocation *invocat
|
||||
return glnx_throw (error, "--stage cannot currently be combined with --retain arguments");
|
||||
if (opt_not_as_default)
|
||||
return glnx_throw (error, "--stage cannot currently be combined with --not-as-default");
|
||||
/* touch file *before* we stage to avoid races */
|
||||
if (opt_lock_finalization)
|
||||
{
|
||||
if (!glnx_shutil_mkdir_p_at (AT_FDCWD,
|
||||
dirname (strdupa (_OSTREE_SYSROOT_RUNSTATE_STAGED_LOCKED)),
|
||||
0755, cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
glnx_autofd int fd = open (_OSTREE_SYSROOT_RUNSTATE_STAGED_LOCKED,
|
||||
O_CREAT | O_WRONLY | O_NOCTTY | O_CLOEXEC, 0640);
|
||||
if (fd == -1)
|
||||
return glnx_throw_errno_prefix (error, "touch(%s)",
|
||||
_OSTREE_SYSROOT_RUNSTATE_STAGED_LOCKED);
|
||||
}
|
||||
/* use old API if we can to exercise it in CI */
|
||||
if (!overlay_initrd_chksums)
|
||||
{
|
||||
|
||||
@@ -45,7 +45,7 @@ ot_admin_builtin_finalize_staged (int argc, char **argv, OstreeCommandInvocation
|
||||
/* Just a sanity check; we shouldn't be called outside of the service though.
|
||||
*/
|
||||
struct stat stbuf;
|
||||
if (fstatat (AT_FDCWD, "/run/ostree-booted", &stbuf, 0) < 0)
|
||||
if (fstatat (AT_FDCWD, OSTREE_PATH_BOOTED, &stbuf, 0) < 0)
|
||||
return TRUE;
|
||||
|
||||
g_autoptr(GOptionContext) context = g_option_context_new ("");
|
||||
|
||||
@@ -335,17 +335,18 @@ parse_keyvalue_strings (GVariantBuilder *builder,
|
||||
if (!eq)
|
||||
return glnx_throw (error, "Missing '=' in KEY=VALUE metadata '%s'", s);
|
||||
g_autofree char *key = g_strndup (s, eq - s);
|
||||
const char *value = eq + 1;
|
||||
if (is_gvariant_print)
|
||||
{
|
||||
g_autoptr(GVariant) value = g_variant_parse (NULL, eq + 1, NULL, NULL, error);
|
||||
if (!value)
|
||||
g_autoptr(GVariant) variant = g_variant_parse (NULL, value, NULL, NULL, error);
|
||||
if (!variant)
|
||||
return glnx_prefix_error (error, "Parsing %s", s);
|
||||
|
||||
g_variant_builder_add (builder, "{sv}", key, value);
|
||||
g_variant_builder_add (builder, "{sv}", key, variant);
|
||||
}
|
||||
else
|
||||
g_variant_builder_add (builder, "{sv}", key,
|
||||
g_variant_new_string (eq + 1));
|
||||
g_variant_new_string (value));
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
|
||||
+66
-60
@@ -47,17 +47,19 @@ static GOptionEntry options[] = {
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
static void
|
||||
print_one_file_text (GFile *f,
|
||||
GFileInfo *file_info)
|
||||
static gboolean
|
||||
print_one_file_text (GFile *f,
|
||||
GFileInfo *file_info,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
g_autoptr(GString) buf = g_string_new ("");
|
||||
char type_c;
|
||||
guint32 mode;
|
||||
guint32 type;
|
||||
|
||||
if (!ostree_repo_file_ensure_resolved ((OstreeRepoFile*)f, NULL))
|
||||
g_assert_not_reached ();
|
||||
if (!ostree_repo_file_ensure_resolved ((OstreeRepoFile*)f, error))
|
||||
return FALSE;
|
||||
|
||||
type_c = '?';
|
||||
mode = g_file_info_get_attribute_uint32 (file_info, "unix::mode");
|
||||
@@ -82,8 +84,7 @@ print_one_file_text (GFile *f,
|
||||
case G_FILE_TYPE_UNKNOWN:
|
||||
case G_FILE_TYPE_SHORTCUT:
|
||||
case G_FILE_TYPE_MOUNTABLE:
|
||||
g_assert_not_reached ();
|
||||
break;
|
||||
return glnx_throw (error, "Invalid file type");
|
||||
}
|
||||
g_string_append_c (buf, type_c);
|
||||
g_string_append_printf (buf, "0%04o %u %u %6" G_GUINT64_FORMAT " ",
|
||||
@@ -104,8 +105,8 @@ print_one_file_text (GFile *f,
|
||||
GVariant *xattrs;
|
||||
char *formatted;
|
||||
|
||||
if (!ostree_repo_file_get_xattrs ((OstreeRepoFile*)f, &xattrs, NULL, NULL))
|
||||
g_assert_not_reached ();
|
||||
if (!ostree_repo_file_get_xattrs ((OstreeRepoFile*)f, &xattrs, cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
formatted = g_variant_print (xattrs, TRUE);
|
||||
g_string_append (buf, "{ ");
|
||||
@@ -121,39 +122,47 @@ print_one_file_text (GFile *f,
|
||||
g_string_append_printf (buf, " -> %s", g_file_info_get_attribute_byte_string (file_info, "standard::symlink-target"));
|
||||
|
||||
g_print ("%s\n", buf->str);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
print_one_file_binary (GFile *f,
|
||||
GFileInfo *file_info)
|
||||
static gboolean
|
||||
print_one_file_binary (GFile *f,
|
||||
GFileInfo *file_info,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
const char *path;
|
||||
|
||||
if (!ostree_repo_file_ensure_resolved ((OstreeRepoFile*)f, NULL))
|
||||
g_assert_not_reached ();
|
||||
if (!ostree_repo_file_ensure_resolved ((OstreeRepoFile*)f, error))
|
||||
return FALSE;
|
||||
|
||||
path = gs_file_get_path_cached (f);
|
||||
|
||||
fwrite (path, 1, strlen (path), stdout);
|
||||
fwrite ("\0", 1, 1, stdout);
|
||||
}
|
||||
|
||||
static void
|
||||
print_one_file (GFile *f,
|
||||
GFileInfo *file_info)
|
||||
{
|
||||
if (opt_nul_filenames_only)
|
||||
print_one_file_binary (f, file_info);
|
||||
else
|
||||
print_one_file_text (f, file_info);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
print_directory_recurse (GFile *f,
|
||||
int depth,
|
||||
GError **error)
|
||||
print_one_file (GFile *f,
|
||||
GFileInfo *file_info,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
if (opt_nul_filenames_only)
|
||||
return print_one_file_binary (f, file_info, cancellable, error);
|
||||
else
|
||||
return print_one_file_text (f, file_info, cancellable, error);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
print_directory_recurse (GFile *f,
|
||||
int depth,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
gboolean ret = FALSE;
|
||||
g_autoptr(GFileEnumerator) dir_enum = NULL;
|
||||
g_autoptr(GFile) child = NULL;
|
||||
g_autoptr(GFileInfo) child_info = NULL;
|
||||
@@ -170,20 +179,21 @@ print_directory_recurse (GFile *f,
|
||||
G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
|
||||
NULL,
|
||||
error);
|
||||
if (!dir_enum)
|
||||
goto out;
|
||||
if (dir_enum == NULL)
|
||||
return FALSE;
|
||||
|
||||
while ((child_info = g_file_enumerator_next_file (dir_enum, NULL, &temp_error)) != NULL)
|
||||
{
|
||||
g_clear_object (&child);
|
||||
child = g_file_get_child (f, g_file_info_get_name (child_info));
|
||||
|
||||
print_one_file (child, child_info);
|
||||
if (!print_one_file (child, child_info, cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
if (g_file_info_get_file_type (child_info) == G_FILE_TYPE_DIRECTORY)
|
||||
{
|
||||
if (!print_directory_recurse (child, depth, error))
|
||||
goto out;
|
||||
if (!print_directory_recurse (child, depth, cancellable, error))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
g_clear_object (&child_info);
|
||||
@@ -191,12 +201,10 @@ print_directory_recurse (GFile *f,
|
||||
if (temp_error)
|
||||
{
|
||||
g_propagate_error (error, temp_error);
|
||||
goto out;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
ret = TRUE;
|
||||
out:
|
||||
return ret;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
@@ -206,37 +214,38 @@ print_one_argument (OstreeRepo *repo,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
gboolean ret = FALSE;
|
||||
g_autoptr(GFile) f = NULL;
|
||||
g_autoptr(GFileInfo) file_info = NULL;
|
||||
g_assert (root != NULL);
|
||||
g_assert (arg != NULL);
|
||||
|
||||
f = g_file_resolve_relative_path (root, arg);
|
||||
|
||||
g_autoptr(GFile) f = g_file_resolve_relative_path (root, arg);
|
||||
if (f == NULL)
|
||||
return glnx_throw (error, "Failed to resolve path '%s'", arg);
|
||||
|
||||
g_autoptr(GFileInfo) file_info = NULL;
|
||||
file_info = g_file_query_info (f, OSTREE_GIO_FAST_QUERYINFO,
|
||||
G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
|
||||
cancellable, error);
|
||||
if (!file_info)
|
||||
goto out;
|
||||
if (file_info == NULL)
|
||||
return FALSE;
|
||||
|
||||
print_one_file (f, file_info);
|
||||
if (!print_one_file (f, file_info, cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
if (g_file_info_get_file_type (file_info) == G_FILE_TYPE_DIRECTORY)
|
||||
{
|
||||
if (opt_recursive)
|
||||
{
|
||||
if (!print_directory_recurse (f, -1, error))
|
||||
goto out;
|
||||
if (!print_directory_recurse (f, -1, cancellable, error))
|
||||
return FALSE;
|
||||
}
|
||||
else if (!opt_dironly)
|
||||
{
|
||||
if (!print_directory_recurse (f, 1, error))
|
||||
goto out;
|
||||
if (!print_directory_recurse (f, 1, cancellable, error))
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
ret = TRUE;
|
||||
out:
|
||||
return ret;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
@@ -244,7 +253,6 @@ ostree_builtin_ls (int argc, char **argv, OstreeCommandInvocation *invocation, G
|
||||
{
|
||||
g_autoptr(GOptionContext) context = NULL;
|
||||
g_autoptr(OstreeRepo) repo = NULL;
|
||||
gboolean ret = FALSE;
|
||||
const char *rev;
|
||||
int i;
|
||||
g_autoptr(GFile) root = NULL;
|
||||
@@ -252,33 +260,31 @@ ostree_builtin_ls (int argc, char **argv, OstreeCommandInvocation *invocation, G
|
||||
context = g_option_context_new ("COMMIT [PATH...]");
|
||||
|
||||
if (!ostree_option_context_parse (context, options, &argc, &argv, invocation, &repo, cancellable, error))
|
||||
goto out;
|
||||
return FALSE;
|
||||
|
||||
if (argc <= 1)
|
||||
{
|
||||
ot_util_usage_error (context, "An COMMIT argument is required", error);
|
||||
goto out;
|
||||
return FALSE;
|
||||
}
|
||||
rev = argv[1];
|
||||
|
||||
if (!ostree_repo_read_commit (repo, rev, &root, NULL, cancellable, error))
|
||||
goto out;
|
||||
return FALSE;
|
||||
|
||||
if (argc > 2)
|
||||
{
|
||||
for (i = 2; i < argc; i++)
|
||||
{
|
||||
if (!print_one_argument (repo, root, argv[i], cancellable, error))
|
||||
goto out;
|
||||
return glnx_prefix_error (error, "Inspecting path '%s'", argv[i]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!print_one_argument (repo, root, "/", cancellable, error))
|
||||
goto out;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
ret = TRUE;
|
||||
out:
|
||||
return ret;
|
||||
return TRUE;
|
||||
}
|
||||
@@ -35,6 +35,7 @@ static char *opt_delete_commit;
|
||||
static char *opt_keep_younger_than;
|
||||
static char **opt_retain_branch_depth;
|
||||
static char **opt_only_branches;
|
||||
static gboolean opt_commit_only;
|
||||
|
||||
/* ATTENTION:
|
||||
* Please remember to update the bash-completion script (bash/ostree) and
|
||||
@@ -50,6 +51,7 @@ static GOptionEntry options[] = {
|
||||
{ "static-deltas-only", 0, 0, G_OPTION_ARG_NONE, &opt_static_deltas_only, "Change the behavior of delete-commit and keep-younger-than to prune only static deltas" },
|
||||
{ "retain-branch-depth", 0, 0, G_OPTION_ARG_STRING_ARRAY, &opt_retain_branch_depth, "Additionally retain BRANCH=DEPTH commits", "BRANCH=DEPTH" },
|
||||
{ "only-branch", 0, 0, G_OPTION_ARG_STRING_ARRAY, &opt_only_branches, "Only prune BRANCH (may be specified multiple times)", "BRANCH" },
|
||||
{ "commit-only", 0, 0, G_OPTION_ARG_NONE, &opt_commit_only, "Only traverse and delete commit objects.", NULL },
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
@@ -99,12 +101,15 @@ traverse_keep_younger_than (OstreeRepo *repo, const char *checksum,
|
||||
GCancellable *cancellable, GError **error)
|
||||
{
|
||||
g_autofree char *next_checksum = g_strdup (checksum);
|
||||
OstreeRepoCommitTraverseFlags traverse_flags = OSTREE_REPO_COMMIT_TRAVERSE_FLAG_NONE;
|
||||
if (opt_commit_only)
|
||||
traverse_flags |= OSTREE_REPO_COMMIT_TRAVERSE_FLAG_COMMIT_ONLY;
|
||||
|
||||
/* This is the first commit in our loop, which has a ref pointing to it. We
|
||||
* don't want to auto-prune it.
|
||||
*/
|
||||
if (!ostree_repo_traverse_commit_union (repo, checksum, 0, reachable,
|
||||
cancellable, error))
|
||||
if (!ostree_repo_traverse_commit_with_flags (repo, traverse_flags, checksum, 0, reachable,
|
||||
NULL, cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
while (TRUE)
|
||||
@@ -121,8 +126,8 @@ traverse_keep_younger_than (OstreeRepo *repo, const char *checksum,
|
||||
if (commit_timestamp >= ts->tv_sec)
|
||||
{
|
||||
/* It's newer, traverse it */
|
||||
if (!ostree_repo_traverse_commit_union (repo, next_checksum, 0, reachable,
|
||||
cancellable, error))
|
||||
if (!ostree_repo_traverse_commit_with_flags (repo, traverse_flags, next_checksum, 0, reachable,
|
||||
NULL, cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
g_free (next_checksum);
|
||||
@@ -183,6 +188,8 @@ ostree_builtin_prune (int argc, char **argv, OstreeCommandInvocation *invocation
|
||||
pruneflags |= OSTREE_REPO_PRUNE_FLAGS_REFS_ONLY;
|
||||
if (opt_no_prune)
|
||||
pruneflags |= OSTREE_REPO_PRUNE_FLAGS_NO_PRUNE;
|
||||
if (opt_commit_only)
|
||||
pruneflags |= OSTREE_REPO_PRUNE_FLAGS_COMMIT_ONLY;
|
||||
|
||||
/* If no newer more complex options are specified, drop down to the original
|
||||
* prune API - both to avoid code duplication, and to keep it run from the
|
||||
@@ -285,6 +292,10 @@ ostree_builtin_prune (int argc, char **argv, OstreeCommandInvocation *invocation
|
||||
/* Traverse each ref, and gather all objects pointed to by it up to a
|
||||
* specific depth (if configured).
|
||||
*/
|
||||
OstreeRepoCommitTraverseFlags traverse_flags = OSTREE_REPO_COMMIT_TRAVERSE_FLAG_NONE;
|
||||
if (opt_commit_only)
|
||||
/** We can avoid looking at all objects if --commit-only is specified **/
|
||||
traverse_flags |= OSTREE_REPO_COMMIT_TRAVERSE_FLAG_COMMIT_ONLY;
|
||||
g_hash_table_iter_init (&hash_iter, all_refs);
|
||||
while (g_hash_table_iter_next (&hash_iter, &key, &value))
|
||||
{
|
||||
@@ -316,8 +327,8 @@ ostree_builtin_prune (int argc, char **argv, OstreeCommandInvocation *invocation
|
||||
the global default */
|
||||
|
||||
g_debug ("Finding objects to keep for commit %s", checksum);
|
||||
if (!ostree_repo_traverse_commit_union (repo, checksum, depth, reachable,
|
||||
cancellable, error))
|
||||
if (!ostree_repo_traverse_commit_with_flags (repo, traverse_flags, checksum, depth, reachable,
|
||||
NULL, cancellable, error))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
@@ -333,7 +344,10 @@ ostree_builtin_prune (int argc, char **argv, OstreeCommandInvocation *invocation
|
||||
}
|
||||
|
||||
g_autofree char *formatted_freed_size = g_format_size_full (objsize_total, 0);
|
||||
g_print ("Total objects: %u\n", n_objects_total);
|
||||
if (opt_commit_only)
|
||||
g_print("Total (commit only) objects: %u\n", n_objects_total);
|
||||
else
|
||||
g_print ("Total objects: %u\n", n_objects_total);
|
||||
if (n_objects_pruned == 0)
|
||||
g_print ("No unreachable objects\n");
|
||||
else if (pruneflags & OSTREE_REPO_PRUNE_FLAGS_NO_PRUNE)
|
||||
|
||||
@@ -249,6 +249,11 @@ dump_summary_ref (const char *collection_id,
|
||||
pretty_key = "Timestamp";
|
||||
value_str = uint64_secs_to_iso8601 (GUINT64_FROM_BE (g_variant_get_uint64 (value)));
|
||||
}
|
||||
else if (g_strcmp0 (key, OSTREE_COMMIT_VERSION) == 0)
|
||||
{
|
||||
pretty_key = "Version";
|
||||
value_str = g_strdup (g_variant_get_string (value, NULL));
|
||||
}
|
||||
else
|
||||
{
|
||||
value_str = g_variant_print (value, FALSE);
|
||||
|
||||
+20
-4
@@ -41,6 +41,10 @@ static gboolean opt_verbose;
|
||||
static gboolean opt_version;
|
||||
static gboolean opt_print_current_dir;
|
||||
|
||||
// TODO: make this public? But no one sane wants to use our C headers
|
||||
// to find where to put files. Maybe we can make it printed by the CLI?
|
||||
#define _OSTREE_EXT_DIR PKGLIBEXECDIR "/ext"
|
||||
|
||||
static GOptionEntry global_entries[] = {
|
||||
{ "verbose", 'v', 0, G_OPTION_ARG_NONE, &opt_verbose, "Print debug information during command processing", NULL },
|
||||
{ "version", 0, 0, G_OPTION_ARG_NONE, &opt_version, "Print version information and exit", NULL },
|
||||
@@ -116,7 +120,7 @@ maybe_setup_mount_namespace (gboolean *out_ns,
|
||||
return TRUE;
|
||||
|
||||
/* If the system isn't booted via libostree, also nothing to do */
|
||||
if (!glnx_fstatat_allow_noent (AT_FDCWD, "/run/ostree-booted", NULL, 0, error))
|
||||
if (!glnx_fstatat_allow_noent (AT_FDCWD, OSTREE_PATH_BOOTED, NULL, 0, error))
|
||||
return FALSE;
|
||||
if (errno == ENOENT)
|
||||
return TRUE;
|
||||
@@ -188,7 +192,7 @@ ostree_command_lookup_external (int argc,
|
||||
|
||||
// Find the first verb (ignoring all earlier flags), then
|
||||
// check if it is a known native command. Otherwise, try to look it
|
||||
// up in $PATH.
|
||||
// up in /usr/lib/ostree/ostree-$cmd or $PATH.
|
||||
// We ignore argv[0] here, the ostree binary itself is not multicall.
|
||||
for (guint arg_index = 1; arg_index < argc; arg_index++)
|
||||
{
|
||||
@@ -204,10 +208,18 @@ ostree_command_lookup_external (int argc,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
g_autofree gchar *ext_command = g_strdup_printf ("ostree-%s", current_arg);
|
||||
|
||||
/* First, search in our libdir /usr/lib/ostree/ostree-$cmd */
|
||||
g_autofree char *ext_lib = g_strconcat (_OSTREE_EXT_DIR, "/", ext_command, NULL);
|
||||
struct stat stbuf;
|
||||
if (stat (ext_lib, &stbuf) == 0)
|
||||
return g_steal_pointer (&ext_lib);
|
||||
|
||||
/* Otherwise, look in $PATH */
|
||||
if (g_find_program_in_path (ext_command) == NULL)
|
||||
return NULL;
|
||||
|
||||
return g_steal_pointer (&ext_command);
|
||||
}
|
||||
|
||||
@@ -253,7 +265,11 @@ ostree_run (int argc,
|
||||
int in, out;
|
||||
|
||||
/* avoid gvfs (http://bugzilla.gnome.org/show_bug.cgi?id=526454) */
|
||||
g_setenv ("GIO_USE_VFS", "local", TRUE);
|
||||
if (!g_setenv ("GIO_USE_VFS", "local", TRUE))
|
||||
{
|
||||
(void) glnx_throw (res_error, "Failed to set environment variable GIO_USE_FVS");
|
||||
return 1;
|
||||
}
|
||||
|
||||
g_log_set_handler (G_LOG_DOMAIN, G_LOG_LEVEL_MESSAGE, message_handler, NULL);
|
||||
|
||||
|
||||
Reference in new issue
Block a user