569 lines
21 KiB
C
569 lines
21 KiB
C
/*
|
|
* Copyright (C) 2011,2013 Colin Walters <walters@verbum.org>
|
|
*
|
|
* SPDX-License-Identifier: LGPL-2.0+
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library. If not, see <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include <sys/statvfs.h>
|
|
#include "config.h"
|
|
#include "otutil.h"
|
|
#include "ostree-ref.h"
|
|
#include "ostree-repo.h"
|
|
#include "ostree-remote-private.h"
|
|
|
|
G_BEGIN_DECLS
|
|
|
|
#define OSTREE_DELTAPART_VERSION (0)
|
|
|
|
#define _OSTREE_SUMMARY_CACHE_DIR "summaries"
|
|
#define _OSTREE_CACHE_DIR "cache"
|
|
|
|
#define _OSTREE_MAX_OUTSTANDING_FETCHER_REQUESTS 8
|
|
#define _OSTREE_MAX_OUTSTANDING_DELTAPART_REQUESTS 2
|
|
|
|
/* We want some parallelism with disk writes, but we also
|
|
* want to avoid starting tens or hundreds of threads
|
|
* (via GTask) all writing to disk. Eventually we may
|
|
* use io_uring which handles backpressure correctly.
|
|
* Also, in "immediate fsync" mode, this helps provide
|
|
* much more backpressure, helping our I/O patterns
|
|
* be nicer for any concurrent processes, such as etcd
|
|
* or other databases.
|
|
* https://github.com/openshift/machine-config-operator/issues/1897
|
|
* */
|
|
#define _OSTREE_MAX_OUTSTANDING_WRITE_REQUESTS 3
|
|
|
|
/* Well-known keys for the additional metadata field in a summary file. */
|
|
#define OSTREE_SUMMARY_LAST_MODIFIED "ostree.summary.last-modified"
|
|
#define OSTREE_SUMMARY_EXPIRES "ostree.summary.expires"
|
|
#define OSTREE_SUMMARY_COLLECTION_ID "ostree.summary.collection-id"
|
|
#define OSTREE_SUMMARY_COLLECTION_MAP "ostree.summary.collection-map"
|
|
#define OSTREE_SUMMARY_MODE "ostree.summary.mode"
|
|
#define OSTREE_SUMMARY_TOMBSTONE_COMMITS "ostree.summary.tombstone-commits"
|
|
#define OSTREE_SUMMARY_INDEXED_DELTAS "ostree.summary.indexed-deltas"
|
|
|
|
#define _OSTREE_PAYLOAD_LINK_PREFIX "../"
|
|
#define _OSTREE_PAYLOAD_LINK_PREFIX_LEN (sizeof (_OSTREE_PAYLOAD_LINK_PREFIX) - 1)
|
|
|
|
/* 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),
|
|
OSTREE_REPO_TEST_ERROR_INVALID_CACHE = (1 << 1),
|
|
} OstreeRepoTestErrorFlags;
|
|
|
|
struct OstreeRepoCommitModifier {
|
|
gint refcount; /* atomic */
|
|
|
|
OstreeRepoCommitModifierFlags flags;
|
|
OstreeRepoCommitFilter filter;
|
|
gpointer user_data;
|
|
GDestroyNotify destroy_notify;
|
|
|
|
OstreeRepoCommitModifierXattrCallback xattr_callback;
|
|
GDestroyNotify xattr_destroy;
|
|
gpointer xattr_user_data;
|
|
|
|
GLnxTmpDir sepolicy_tmpdir;
|
|
OstreeSePolicy *sepolicy;
|
|
GHashTable *devino_cache;
|
|
};
|
|
|
|
typedef enum {
|
|
OSTREE_REPO_SYSROOT_KIND_UNKNOWN,
|
|
OSTREE_REPO_SYSROOT_KIND_NO, /* Not a system repo */
|
|
OSTREE_REPO_SYSROOT_KIND_VIA_SYSROOT, /* Constructed via ostree_sysroot_get_repo() */
|
|
OSTREE_REPO_SYSROOT_KIND_IS_SYSROOT_OSTREE, /* We match /ostree/repo */
|
|
} OstreeRepoSysrootKind;
|
|
|
|
typedef struct {
|
|
GHashTable *refs; /* (element-type utf8 utf8) */
|
|
GHashTable *collection_refs; /* (element-type OstreeCollectionRef utf8) */
|
|
OstreeRepoTransactionStats stats;
|
|
/* Implementation of min-free-space-percent */
|
|
gulong blocksize;
|
|
fsblkcnt_t max_blocks;
|
|
} OstreeRepoTxn;
|
|
|
|
typedef struct {
|
|
GMutex mutex; /* All other members should only be accessed with this held */
|
|
int fd; /* The open file or flock file descriptor */
|
|
guint shared; /* Number of shared locks curently held */
|
|
guint exclusive; /* Number of exclusive locks currently held */
|
|
} OstreeRepoLock;
|
|
|
|
typedef enum {
|
|
_OSTREE_FEATURE_NO,
|
|
_OSTREE_FEATURE_MAYBE,
|
|
_OSTREE_FEATURE_YES,
|
|
} _OstreeFeatureSupport;
|
|
|
|
/* Possible values for the sysroot.bootloader configuration variable */
|
|
typedef enum {
|
|
CFG_SYSROOT_BOOTLOADER_OPT_AUTO = 0,
|
|
CFG_SYSROOT_BOOTLOADER_OPT_NONE,
|
|
CFG_SYSROOT_BOOTLOADER_OPT_GRUB2,
|
|
CFG_SYSROOT_BOOTLOADER_OPT_SYSLINUX,
|
|
CFG_SYSROOT_BOOTLOADER_OPT_UBOOT,
|
|
CFG_SYSROOT_BOOTLOADER_OPT_ZIPL,
|
|
/* Non-exhaustive */
|
|
} OstreeCfgSysrootBootloaderOpt;
|
|
|
|
static const char* const CFG_SYSROOT_BOOTLOADER_OPTS_STR[] = {
|
|
/* This must be kept in the same order as the enum */
|
|
"auto",
|
|
"none",
|
|
"grub2",
|
|
"syslinux",
|
|
"uboot",
|
|
"zipl",
|
|
NULL,
|
|
};
|
|
|
|
/**
|
|
* OstreeRepo:
|
|
*
|
|
* Private instance structure.
|
|
*/
|
|
struct OstreeRepo {
|
|
GObject parent;
|
|
|
|
char *stagedir_prefix;
|
|
GLnxTmpDir commit_stagedir;
|
|
GLnxLockFile commit_stagedir_lock;
|
|
|
|
/* A cached fd-relative version, distinct from the case where we may have a
|
|
* user-provided absolute path.
|
|
*/
|
|
GFile *repodir_fdrel;
|
|
GFile *repodir; /* May be %NULL if we were opened via ostree_repo_open_at() */
|
|
int repo_dir_fd;
|
|
int tmp_dir_fd;
|
|
int cache_dir_fd;
|
|
char *cache_dir;
|
|
int objects_dir_fd;
|
|
int uncompressed_objects_dir_fd;
|
|
GFile *sysroot_dir;
|
|
GWeakRef sysroot; /* Weak to avoid a circular ref; see also `is_system` */
|
|
char *remotes_config_dir;
|
|
|
|
OstreeRepoLock lock;
|
|
|
|
GMutex txn_lock;
|
|
OstreeRepoTxn txn;
|
|
gboolean txn_locked;
|
|
_OstreeFeatureSupport fs_verity_wanted;
|
|
_OstreeFeatureSupport fs_verity_supported;
|
|
|
|
GMutex cache_lock;
|
|
guint dirmeta_cache_refcount;
|
|
/* char * checksum → GVariant * for dirmeta objects, used in the checkout path */
|
|
GHashTable *dirmeta_cache;
|
|
|
|
gboolean inited;
|
|
gboolean writable;
|
|
OstreeRepoSysrootKind sysroot_kind;
|
|
GError *writable_error;
|
|
gboolean in_transaction;
|
|
gboolean disable_fsync;
|
|
gboolean per_object_fsync;
|
|
gboolean disable_xattrs;
|
|
guint zlib_compression_level;
|
|
GHashTable *loose_object_devino_hash;
|
|
GHashTable *updated_uncompressed_dirs;
|
|
|
|
/* FIXME: The object sizes hash table is really per-commit state, not repo
|
|
* state. Using a single table for the repo means that commits cannot be
|
|
* built simultaneously if they're adding size information. This data should
|
|
* probably be in OstreeMutableTree, but that's gone by the time the actual
|
|
* commit is constructed. At that point the only commit state is in the root
|
|
* OstreeRepoFile.
|
|
*/
|
|
GHashTable *object_sizes;
|
|
|
|
/* Cache the repo's device/inode to use for comparisons elsewhere */
|
|
dev_t device;
|
|
ino_t inode;
|
|
uid_t owner_uid; /* Cache of repo's owner uid */
|
|
guint min_free_space_percent; /* See the min-free-space-percent config option */
|
|
guint64 min_free_space_mb; /* See the min-free-space-size config option */
|
|
guint64 reserved_blocks;
|
|
gboolean cleanup_stagedir;
|
|
|
|
guint test_error_flags; /* OstreeRepoTestErrorFlags */
|
|
|
|
GKeyFile *config;
|
|
GHashTable *remotes;
|
|
GMutex remotes_lock;
|
|
OstreeRepoMode mode;
|
|
gboolean enable_uncompressed_cache;
|
|
gboolean generate_sizes;
|
|
guint64 tmp_expiry_seconds;
|
|
gchar *collection_id;
|
|
gboolean add_remotes_config_dir; /* Add new remotes in remotes.d dir */
|
|
gint lock_timeout_seconds;
|
|
guint64 payload_link_threshold;
|
|
gint fs_support_reflink; /* The underlying filesystem has support for ioctl (FICLONE..) */
|
|
gchar **repo_finders;
|
|
OstreeCfgSysrootBootloaderOpt bootloader; /* Configure which bootloader to use. */
|
|
GHashTable *bls_append_values; /* Parsed key-values from bls-append-except-default key in config. */
|
|
|
|
OstreeRepo *parent_repo;
|
|
};
|
|
|
|
typedef struct {
|
|
dev_t dev;
|
|
ino_t ino;
|
|
char checksum[OSTREE_SHA256_STRING_LEN+1];
|
|
} OstreeDevIno;
|
|
|
|
/* A MemoryCacheRef is an in-memory cache of objects (currently just DIRMETA). This can
|
|
* be used when performing an operation that traverses a repository in someway. Currently,
|
|
* the primary use case is ostree_repo_checkout_at() avoiding lots of duplicate dirmeta
|
|
* lookups.
|
|
*/
|
|
typedef struct {
|
|
OstreeRepo *repo;
|
|
} OstreeRepoMemoryCacheRef;
|
|
|
|
|
|
void
|
|
_ostree_repo_memory_cache_ref_init (OstreeRepoMemoryCacheRef *state,
|
|
OstreeRepo *repo);
|
|
|
|
void
|
|
_ostree_repo_memory_cache_ref_destroy (OstreeRepoMemoryCacheRef *state);
|
|
G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(OstreeRepoMemoryCacheRef, _ostree_repo_memory_cache_ref_destroy)
|
|
|
|
#define OSTREE_REPO_TMPDIR_STAGING "staging-"
|
|
|
|
gboolean
|
|
_ostree_repo_allocate_tmpdir (int tmpdir_dfd,
|
|
const char *tmpdir_prefix,
|
|
GLnxTmpDir *tmpdir_out,
|
|
GLnxLockFile *file_lock_out,
|
|
gboolean * reusing_dir_out,
|
|
GCancellable *cancellable,
|
|
GError **error);
|
|
|
|
gboolean
|
|
_ostree_repo_has_staging_prefix (const char *filename);
|
|
|
|
gboolean
|
|
_ostree_repo_try_lock_tmpdir (int tmpdir_dfd,
|
|
const char *tmpdir_name,
|
|
GLnxLockFile *file_lock_out,
|
|
gboolean *out_did_lock,
|
|
GError **error);
|
|
|
|
gboolean
|
|
_ostree_repo_ensure_loose_objdir_at (int dfd,
|
|
const char *loose_path,
|
|
GCancellable *cancellable,
|
|
GError **error);
|
|
|
|
GFile *
|
|
_ostree_repo_get_commit_metadata_loose_path (OstreeRepo *self,
|
|
const char *checksum);
|
|
|
|
gboolean
|
|
_ostree_repo_has_loose_object (OstreeRepo *self,
|
|
const char *checksum,
|
|
OstreeObjectType objtype,
|
|
gboolean *out_is_stored,
|
|
GCancellable *cancellable,
|
|
GError **error);
|
|
|
|
gboolean
|
|
_ostree_write_bareuser_metadata (int fd,
|
|
guint32 uid,
|
|
guint32 gid,
|
|
guint32 mode,
|
|
GVariant *xattrs,
|
|
GError **error);
|
|
|
|
gboolean
|
|
_ostree_repo_write_directory_meta (OstreeRepo *self,
|
|
GFileInfo *file_info,
|
|
GVariant *xattrs,
|
|
guchar **out_csum,
|
|
GCancellable *cancellable,
|
|
GError **error);
|
|
gboolean
|
|
_ostree_repo_update_refs (OstreeRepo *self,
|
|
GHashTable *refs,
|
|
GCancellable *cancellable,
|
|
GError **error);
|
|
|
|
gboolean
|
|
_ostree_repo_update_collection_refs (OstreeRepo *self,
|
|
GHashTable *refs,
|
|
GCancellable *cancellable,
|
|
GError **error);
|
|
|
|
gboolean
|
|
_ostree_repo_file_replace_contents (OstreeRepo *self,
|
|
int dfd,
|
|
const char *path,
|
|
const guint8 *buf,
|
|
gsize len,
|
|
GCancellable *cancellable,
|
|
GError **error);
|
|
|
|
gboolean
|
|
_ostree_repo_write_ref (OstreeRepo *self,
|
|
const char *remote,
|
|
const OstreeCollectionRef *ref,
|
|
const char *rev,
|
|
const char *alias,
|
|
GCancellable *cancellable,
|
|
GError **error);
|
|
|
|
OstreeRepoFile *
|
|
_ostree_repo_file_new_for_commit (OstreeRepo *repo,
|
|
const char *commit,
|
|
GError **error);
|
|
|
|
OstreeRepoFile *
|
|
_ostree_repo_file_new_root (OstreeRepo *repo,
|
|
const char *contents_checksum,
|
|
const char *metadata_checksum);
|
|
|
|
gboolean
|
|
_ostree_repo_traverse_dirtree_internal (OstreeRepo *repo,
|
|
const char *dirtree_checksum,
|
|
int recursion_depth,
|
|
GHashTable *inout_reachable,
|
|
GHashTable *inout_content_names,
|
|
GCancellable *cancellable,
|
|
GError **error);
|
|
|
|
OstreeRepoCommitFilterResult
|
|
_ostree_repo_commit_modifier_apply (OstreeRepo *self,
|
|
OstreeRepoCommitModifier *modifier,
|
|
const char *path,
|
|
GFileInfo *file_info,
|
|
GFileInfo **out_modified_info);
|
|
|
|
void
|
|
_ostree_repo_setup_generate_sizes (OstreeRepo *self,
|
|
OstreeRepoCommitModifier *modifier);
|
|
|
|
gboolean
|
|
_ostree_repo_remote_name_is_file (const char *remote_name);
|
|
|
|
#ifndef OSTREE_DISABLE_GPGME
|
|
OstreeGpgVerifyResult *
|
|
_ostree_repo_gpg_verify_with_metadata (OstreeRepo *self,
|
|
GBytes *signed_data,
|
|
GVariant *metadata,
|
|
const char *remote_name,
|
|
GFile *keyringdir,
|
|
GFile *extra_keyring,
|
|
GCancellable *cancellable,
|
|
GError **error);
|
|
|
|
OstreeGpgVerifyResult *
|
|
_ostree_repo_verify_commit_internal (OstreeRepo *self,
|
|
const char *commit_checksum,
|
|
const char *remote_name,
|
|
GFile *keyringdir,
|
|
GFile *extra_keyring,
|
|
GCancellable *cancellable,
|
|
GError **error);
|
|
#endif /* OSTREE_DISABLE_GPGME */
|
|
|
|
typedef enum {
|
|
_OSTREE_REPO_IMPORT_FLAGS_NONE = 0,
|
|
_OSTREE_REPO_IMPORT_FLAGS_TRUSTED = (1 << 0),
|
|
_OSTREE_REPO_IMPORT_FLAGS_VERIFY_BAREUSERONLY = (1 << 1),
|
|
} OstreeRepoImportFlags;
|
|
|
|
gboolean
|
|
_ostree_repo_import_object (OstreeRepo *self,
|
|
OstreeRepo *source,
|
|
OstreeObjectType objtype,
|
|
const char *checksum,
|
|
OstreeRepoImportFlags flags,
|
|
GCancellable *cancellable,
|
|
GError **error);
|
|
|
|
gboolean
|
|
_ostree_repo_commit_tmpf_final (OstreeRepo *self,
|
|
const char *checksum,
|
|
OstreeObjectType objtype,
|
|
GLnxTmpfile *tmpf,
|
|
GCancellable *cancellable,
|
|
GError **error);
|
|
|
|
typedef struct {
|
|
gboolean initialized;
|
|
gpointer opaque0[10];
|
|
guint opaque1[10];
|
|
} OstreeRepoBareContent;
|
|
void _ostree_repo_bare_content_cleanup (OstreeRepoBareContent *regwrite);
|
|
G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(OstreeRepoBareContent, _ostree_repo_bare_content_cleanup)
|
|
|
|
gboolean
|
|
_ostree_repo_bare_content_open (OstreeRepo *self,
|
|
const char *checksum,
|
|
guint64 content_len,
|
|
guint uid,
|
|
guint gid,
|
|
guint mode,
|
|
GVariant *xattrs,
|
|
OstreeRepoBareContent *out_regwrite,
|
|
GCancellable *cancellable,
|
|
GError **error);
|
|
|
|
gboolean
|
|
_ostree_repo_bare_content_write (OstreeRepo *repo,
|
|
OstreeRepoBareContent *barewrite,
|
|
const guint8 *buf,
|
|
size_t len,
|
|
GCancellable *cancellable,
|
|
GError **error);
|
|
|
|
gboolean
|
|
_ostree_repo_bare_content_commit (OstreeRepo *self,
|
|
OstreeRepoBareContent *barewrite,
|
|
char *checksum_buf,
|
|
size_t buflen,
|
|
GCancellable *cancellable,
|
|
GError **error);
|
|
|
|
OstreeContentWriter *
|
|
_ostree_content_writer_new (OstreeRepo *repo,
|
|
const char *checksum,
|
|
guint uid,
|
|
guint gid,
|
|
guint mode,
|
|
guint64 content_len,
|
|
GVariant *xattrs,
|
|
GError **error);
|
|
|
|
gboolean
|
|
_ostree_repo_load_file_bare (OstreeRepo *self,
|
|
const char *checksum,
|
|
int *out_fd,
|
|
struct stat *out_stbuf,
|
|
char **out_symlink,
|
|
GVariant **out_xattrs,
|
|
GCancellable *cancellable,
|
|
GError **error);
|
|
|
|
gboolean
|
|
_ostree_repo_update_mtime (OstreeRepo *self,
|
|
GError **error);
|
|
|
|
gboolean
|
|
_ostree_repo_add_remote (OstreeRepo *self,
|
|
OstreeRemote *remote);
|
|
gboolean
|
|
_ostree_repo_remove_remote (OstreeRepo *self,
|
|
OstreeRemote *remote);
|
|
OstreeRemote *
|
|
_ostree_repo_get_remote (OstreeRepo *self,
|
|
const char *name,
|
|
GError **error);
|
|
OstreeRemote *
|
|
_ostree_repo_get_remote_inherited (OstreeRepo *self,
|
|
const char *name,
|
|
GError **error);
|
|
|
|
gboolean
|
|
_ostree_repo_maybe_regenerate_summary (OstreeRepo *self,
|
|
GCancellable *cancellable,
|
|
GError **error);
|
|
|
|
|
|
gboolean _ostree_repo_parse_fsverity_config (OstreeRepo *self, GError **error);
|
|
|
|
gboolean
|
|
_ostree_tmpf_fsverity_core (GLnxTmpfile *tmpf,
|
|
_OstreeFeatureSupport fsverity_requested,
|
|
gboolean *supported,
|
|
GError **error);
|
|
|
|
gboolean
|
|
_ostree_tmpf_fsverity (OstreeRepo *self,
|
|
GLnxTmpfile *tmpf,
|
|
GError **error);
|
|
|
|
gboolean
|
|
_ostree_repo_verify_bindings (const char *collection_id,
|
|
const char *ref_name,
|
|
GVariant *commit,
|
|
GError **error);
|
|
|
|
GHashTable *
|
|
ostree_repo_list_objects_set (OstreeRepo *self,
|
|
OstreeRepoListObjectsFlags flags,
|
|
GCancellable *cancellable,
|
|
GError **error);
|
|
|
|
/**
|
|
* OstreeRepoAutoTransaction:
|
|
*
|
|
* A transaction guard for a specific #OstreeRepo. It can be explicitly
|
|
* completed through abort/commit. If the guard has not been completed
|
|
* beforehand, on cleanup it is automatically aborted.
|
|
*
|
|
* Taken from flatpak; may be made into public API later
|
|
*/
|
|
typedef struct
|
|
{
|
|
gint atomic_refcount;
|
|
OstreeRepo *repo;
|
|
} OstreeRepoAutoTransaction;
|
|
|
|
OstreeRepoAutoTransaction *
|
|
_ostree_repo_auto_transaction_start (OstreeRepo *repo,
|
|
GCancellable *cancellable,
|
|
GError **error);
|
|
|
|
gboolean
|
|
_ostree_repo_auto_transaction_abort (OstreeRepoAutoTransaction *txn,
|
|
GCancellable *cancellable,
|
|
GError **error);
|
|
|
|
gboolean
|
|
_ostree_repo_auto_transaction_commit (OstreeRepoAutoTransaction *txn,
|
|
OstreeRepoTransactionStats *out_stats,
|
|
GCancellable *cancellable,
|
|
GError **error);
|
|
|
|
OstreeRepoAutoTransaction *
|
|
_ostree_repo_auto_transaction_ref (OstreeRepoAutoTransaction *txn);
|
|
|
|
void
|
|
_ostree_repo_auto_transaction_unref (OstreeRepoAutoTransaction *txn);
|
|
|
|
GType _ostree_repo_auto_transaction_get_type (void);
|
|
|
|
G_DEFINE_AUTOPTR_CLEANUP_FUNC (OstreeRepoAutoTransaction, _ostree_repo_auto_transaction_unref);
|
|
|
|
/* Internal function to break a circular dependency:
|
|
* should not be made into public API, even if the rest is */
|
|
OstreeRepoAutoTransaction *_ostree_repo_auto_transaction_new (OstreeRepo *repo);
|
|
|
|
G_END_DECLS
|