New upstream version 2017.15
This commit is contained in:
commit
ef9ffc09f4
66 files changed
+2620
-638
No files matched your search
@@ -18,9 +18,8 @@
|
||||
***/
|
||||
|
||||
/* Add new symbols here. Release commits should copy this section into -released.sym. */
|
||||
|
||||
LIBOSTREE_2017.15 {
|
||||
} LIBOSTREE_2017.14;
|
||||
LIBOSTREE_2017.16 {
|
||||
} LIBOSTREE_2017.15;
|
||||
|
||||
/* Stub section for the stable release *after* this development one; don't
|
||||
* edit this other than to update the last number. This is just a copy/paste
|
||||
|
||||
@@ -94,4 +94,8 @@ LIBOSTREE_2017.14_EXPERIMENTAL {
|
||||
global:
|
||||
ostree_remote_get_type;
|
||||
ostree_remote_get_url;
|
||||
ostree_repo_auto_lock_cleanup;
|
||||
ostree_repo_auto_lock_push;
|
||||
ostree_repo_lock_pop;
|
||||
ostree_repo_lock_push;
|
||||
} LIBOSTREE_2017.13_EXPERIMENTAL;
|
||||
@@ -445,6 +445,12 @@ global:
|
||||
LIBOSTREE_2017.14 {
|
||||
} LIBOSTREE_2017.13;
|
||||
|
||||
LIBOSTREE_2017.15 {
|
||||
ostree_repo_fsck_object;
|
||||
ostree_repo_mark_commit_partial;
|
||||
ostree_break_hardlink;
|
||||
} LIBOSTREE_2017.14;
|
||||
|
||||
/* NOTE: Only add more content here in release commits! See the
|
||||
* comments at the top of this file.
|
||||
*/
|
||||
@@ -59,6 +59,7 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC (OstreeSysrootUpgrader, g_object_unref)
|
||||
G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC (OstreeRepoCommitTraverseIter, ostree_repo_commit_traverse_iter_clear)
|
||||
|
||||
#ifdef OSTREE_ENABLE_EXPERIMENTAL_API
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC (OstreeRepoAutoLock, ostree_repo_auto_lock_cleanup)
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC (OstreeCollectionRef, ostree_collection_ref_free)
|
||||
G_DEFINE_AUTO_CLEANUP_FREE_FUNC (OstreeCollectionRefv, ostree_collection_ref_freev, NULL)
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC (OstreeRemote, ostree_remote_unref)
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "ostree-cmdprivate.h"
|
||||
#include "ostree-repo-private.h"
|
||||
#include "ostree-core-private.h"
|
||||
#include "ostree-repo-pull-private.h"
|
||||
#include "ostree-repo-static-delta-private.h"
|
||||
#include "ostree-sysroot.h"
|
||||
#include "ostree-bootloader-grub2.h"
|
||||
@@ -48,7 +49,8 @@ ostree_cmd__private__ (void)
|
||||
impl_ostree_generate_grub2_config,
|
||||
_ostree_repo_static_delta_dump,
|
||||
_ostree_repo_static_delta_query_exists,
|
||||
_ostree_repo_static_delta_delete
|
||||
_ostree_repo_static_delta_delete,
|
||||
_ostree_repo_verify_bindings
|
||||
};
|
||||
|
||||
return &table;
|
||||
|
||||
@@ -31,6 +31,7 @@ typedef struct {
|
||||
gboolean (* ostree_static_delta_dump) (OstreeRepo *repo, const char *delta_id, GCancellable *cancellable, GError **error);
|
||||
gboolean (* ostree_static_delta_query_exists) (OstreeRepo *repo, const char *delta_id, gboolean *out_exists, GCancellable *cancellable, GError **error);
|
||||
gboolean (* ostree_static_delta_delete) (OstreeRepo *repo, const char *delta_id, GCancellable *cancellable, GError **error);
|
||||
gboolean (* ostree_repo_verify_bindings) (const char *collection_id, const char *ref_name, GVariant *commit, GError **error);
|
||||
} OstreeCmdPrivateVTable;
|
||||
|
||||
/* Note this not really "public", we just export the symbol, but not the header */
|
||||
|
||||
@@ -749,6 +749,107 @@ ostree_content_file_parse (gboolean compressed,
|
||||
cancellable, error);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
break_symhardlink (int dfd,
|
||||
const char *path,
|
||||
struct stat *stbuf,
|
||||
GLnxFileCopyFlags copyflags,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
guint count;
|
||||
gboolean copy_success = FALSE;
|
||||
char *path_tmp = glnx_strjoina (path, ".XXXXXX");
|
||||
|
||||
for (count = 0; count < 100; count++)
|
||||
{
|
||||
g_autoptr(GError) tmp_error = NULL;
|
||||
|
||||
glnx_gen_temp_name (path_tmp);
|
||||
|
||||
if (!glnx_file_copy_at (dfd, path, stbuf, dfd, path_tmp, copyflags,
|
||||
cancellable, &tmp_error))
|
||||
{
|
||||
if (g_error_matches (tmp_error, G_IO_ERROR, G_IO_ERROR_EXISTS))
|
||||
continue;
|
||||
g_propagate_error (error, g_steal_pointer (&tmp_error));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
copy_success = TRUE;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!copy_success)
|
||||
{
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_EXISTS,
|
||||
"Exceeded limit of %u file creation attempts", count);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!glnx_renameat (dfd, path_tmp, dfd, path, error))
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* ostree_break_hardlink:
|
||||
* @dfd: Directory fd
|
||||
* @path: Path relative to @dfd
|
||||
* @skip_xattrs: Do not copy extended attributes
|
||||
* @error: error
|
||||
*
|
||||
* In many cases using libostree, a program may need to "break"
|
||||
* hardlinks by performing a copy. For example, in order to
|
||||
* logically append to a file.
|
||||
*
|
||||
* This function performs full copying, including e.g. extended
|
||||
* attributes and permissions of both regular files and symbolic links.
|
||||
*
|
||||
* If the file is not hardlinked, this function does nothing and
|
||||
* returns successfully.
|
||||
*
|
||||
* This function does not perform synchronization via `fsync()` or
|
||||
* `fdatasync()`; the idea is this will commonly be done as part
|
||||
* of an `ostree_repo_commit_transaction()`, which itself takes
|
||||
* care of synchronization.
|
||||
*
|
||||
* Since: 2017.15
|
||||
*/
|
||||
gboolean ostree_break_hardlink (int dfd,
|
||||
const char *path,
|
||||
gboolean skip_xattrs,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
struct stat stbuf;
|
||||
|
||||
if (!glnx_fstatat (dfd, path, &stbuf, AT_SYMLINK_NOFOLLOW, error))
|
||||
return FALSE;
|
||||
|
||||
if (stbuf.st_nlink <= 1)
|
||||
return TRUE; /* Note early return */
|
||||
|
||||
const GLnxFileCopyFlags copyflags = skip_xattrs ? GLNX_FILE_COPY_NOXATTRS : 0;
|
||||
|
||||
if (S_ISREG (stbuf.st_mode))
|
||||
/* Note it's now completely safe to copy a file to itself,
|
||||
* as glnx_file_copy_at() is documented to do an O_TMPFILE + rename()
|
||||
* with GLNX_FILE_COPY_OVERWRITE.
|
||||
*/
|
||||
return glnx_file_copy_at (dfd, path, &stbuf, dfd, path,
|
||||
copyflags | GLNX_FILE_COPY_OVERWRITE,
|
||||
cancellable, error);
|
||||
else if (S_ISLNK (stbuf.st_mode))
|
||||
return break_symhardlink (dfd, path, &stbuf, copyflags,
|
||||
cancellable, error);
|
||||
else
|
||||
return glnx_throw (error, "Unsupported type for entry '%s'", path);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* ostree_checksum_file_from_input:
|
||||
* @file_info: File information
|
||||
|
||||
@@ -438,6 +438,13 @@ gboolean ostree_checksum_file (GFile *f,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
_OSTREE_PUBLIC
|
||||
gboolean ostree_break_hardlink (int dfd,
|
||||
const char *path,
|
||||
gboolean skip_xattrs,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
/**
|
||||
* OstreeChecksumFlags:
|
||||
*
|
||||
|
||||
@@ -771,13 +771,18 @@ initiate_next_curl_request (FetcherRequest *req,
|
||||
* there are numerous HTTP/2 fixes since the original version in
|
||||
* libcurl 7.43.0.
|
||||
*/
|
||||
#ifdef BUILDOPT_HTTP2
|
||||
if (!(self->config_flags & OSTREE_FETCHER_FLAGS_DISABLE_HTTP2))
|
||||
{
|
||||
#if CURL_AT_LEAST_VERSION(7, 51, 0)
|
||||
curl_easy_setopt (req->easy, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_0);
|
||||
curl_easy_setopt (req->easy, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_0);
|
||||
#endif
|
||||
/* https://github.com/curl/curl/blob/curl-7_53_0/docs/examples/http2-download.c */
|
||||
/* https://github.com/curl/curl/blob/curl-7_53_0/docs/examples/http2-download.c */
|
||||
#if (CURLPIPE_MULTIPLEX > 0)
|
||||
/* wait for pipe connection to confirm */
|
||||
curl_easy_setopt (req->easy, CURLOPT_PIPEWAIT, 1L);
|
||||
/* wait for pipe connection to confirm */
|
||||
curl_easy_setopt (req->easy, CURLOPT_PIPEWAIT, 1L);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
curl_easy_setopt (req->easy, CURLOPT_WRITEFUNCTION, write_cb);
|
||||
if (g_getenv ("OSTREE_DEBUG_HTTP"))
|
||||
|
||||
@@ -50,7 +50,8 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC(OstreeFetcher, g_object_unref)
|
||||
typedef enum {
|
||||
OSTREE_FETCHER_FLAGS_NONE = 0,
|
||||
OSTREE_FETCHER_FLAGS_TLS_PERMISSIVE = (1 << 0),
|
||||
OSTREE_FETCHER_FLAGS_TRANSFER_GZIP = (1 << 1)
|
||||
OSTREE_FETCHER_FLAGS_TRANSFER_GZIP = (1 << 1),
|
||||
OSTREE_FETCHER_FLAGS_DISABLE_HTTP2 = (1 << 2),
|
||||
} OstreeFetcherConfigFlags;
|
||||
|
||||
typedef enum {
|
||||
|
||||
+451
-256
File diff suppressed because it is too large.
Load diff
@@ -508,7 +508,7 @@ ostree_repo_file_get_parent (GFile *file)
|
||||
{
|
||||
OstreeRepoFile *self = OSTREE_REPO_FILE (file);
|
||||
|
||||
return g_object_ref (self->parent);
|
||||
return (GFile*)g_object_ref (self->parent);
|
||||
}
|
||||
|
||||
static GFile *
|
||||
@@ -621,7 +621,7 @@ ostree_repo_file_resolve_relative_path (GFile *file,
|
||||
g_assert (*relative_path == '/');
|
||||
|
||||
if (strcmp (relative_path, "/") == 0)
|
||||
return g_object_ref (ostree_repo_file_get_root (self));
|
||||
return (GFile*)g_object_ref (ostree_repo_file_get_root (self));
|
||||
|
||||
if (self->parent)
|
||||
return ostree_repo_file_resolve_relative_path ((GFile*)ostree_repo_file_get_root (self),
|
||||
|
||||
@@ -82,6 +82,15 @@ typedef enum {
|
||||
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;
|
||||
|
||||
/**
|
||||
* OstreeRepo:
|
||||
*
|
||||
@@ -109,13 +118,9 @@ struct OstreeRepo {
|
||||
GWeakRef sysroot; /* Weak to avoid a circular ref; see also `is_system` */
|
||||
char *remotes_config_dir;
|
||||
|
||||
GHashTable *txn_refs; /* (element-type utf8 utf8) */
|
||||
GHashTable *txn_collection_refs; /* (element-type OstreeCollectionRef utf8) */
|
||||
GMutex txn_stats_lock;
|
||||
OstreeRepoTransactionStats txn_stats;
|
||||
/* Implementation of min-free-space-percent */
|
||||
gulong txn_blocksize;
|
||||
fsblkcnt_t max_txn_blocks;
|
||||
GMutex txn_lock;
|
||||
OstreeRepoTxn txn;
|
||||
gboolean txn_locked;
|
||||
|
||||
GMutex cache_lock;
|
||||
guint dirmeta_cache_refcount;
|
||||
@@ -153,6 +158,7 @@ struct OstreeRepo {
|
||||
guint64 tmp_expiry_seconds;
|
||||
gchar *collection_id;
|
||||
gboolean add_remotes_config_dir; /* Add new remotes in remotes.d dir */
|
||||
gint lock_timeout_seconds;
|
||||
|
||||
OstreeRepo *parent_repo;
|
||||
};
|
||||
@@ -432,6 +438,32 @@ _ostree_repo_get_remote_inherited (OstreeRepo *self,
|
||||
|
||||
#ifndef OSTREE_ENABLE_EXPERIMENTAL_API
|
||||
|
||||
/* All the locking APIs below are duplicated in ostree-repo.h. Remove the ones
|
||||
* here once it's no longer experimental.
|
||||
*/
|
||||
|
||||
typedef enum {
|
||||
OSTREE_REPO_LOCK_SHARED,
|
||||
OSTREE_REPO_LOCK_EXCLUSIVE
|
||||
} OstreeRepoLockType;
|
||||
|
||||
gboolean ostree_repo_lock_push (OstreeRepo *self,
|
||||
OstreeRepoLockType lock_type,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
gboolean ostree_repo_lock_pop (OstreeRepo *self,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
typedef OstreeRepo OstreeRepoAutoLock;
|
||||
|
||||
OstreeRepoAutoLock * ostree_repo_auto_lock_push (OstreeRepo *self,
|
||||
OstreeRepoLockType lock_type,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
void ostree_repo_auto_lock_cleanup (OstreeRepoAutoLock *lock);
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC (OstreeRepoAutoLock, ostree_repo_auto_lock_cleanup)
|
||||
|
||||
const gchar * ostree_repo_get_collection_id (OstreeRepo *self);
|
||||
gboolean ostree_repo_set_collection_id (OstreeRepo *self,
|
||||
const gchar *collection_id,
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
|
||||
#include "ostree-core-private.h"
|
||||
#include "ostree-repo-private.h"
|
||||
#include "ostree-autocleanups.h"
|
||||
#include "otutil.h"
|
||||
|
||||
typedef struct {
|
||||
@@ -35,16 +36,6 @@ typedef struct {
|
||||
guint64 freed_bytes;
|
||||
} OtPruneData;
|
||||
|
||||
static gboolean
|
||||
prune_commitpartial_file (OstreeRepo *repo,
|
||||
const char *checksum,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
g_autofree char *path = _ostree_get_commitpartial_path (checksum);
|
||||
return ot_ensure_unlinked_at (repo->repo_dir_fd, path, error);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
maybe_prune_loose_object (OtPruneData *data,
|
||||
OstreeRepoPruneFlags flags,
|
||||
@@ -67,7 +58,7 @@ maybe_prune_loose_object (OtPruneData *data,
|
||||
|
||||
if (objtype == OSTREE_OBJECT_TYPE_COMMIT)
|
||||
{
|
||||
if (!prune_commitpartial_file (data->repo, checksum, cancellable, error))
|
||||
if (!ostree_repo_mark_commit_partial (data->repo, checksum, FALSE, error))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
@@ -160,12 +151,20 @@ _ostree_repo_prune_tmp (OstreeRepo *self,
|
||||
* Prune static deltas, if COMMIT is specified then delete static delta files only
|
||||
* targeting that commit; otherwise any static delta of non existing commits are
|
||||
* deleted.
|
||||
*
|
||||
* This function takes an exclusive lock on the @self repository.
|
||||
*/
|
||||
gboolean
|
||||
ostree_repo_prune_static_deltas (OstreeRepo *self, const char *commit,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
g_autoptr(OstreeRepoAutoLock) lock =
|
||||
ostree_repo_auto_lock_push (self, OSTREE_REPO_LOCK_EXCLUSIVE, cancellable,
|
||||
error);
|
||||
if (!lock)
|
||||
return FALSE;
|
||||
|
||||
g_autoptr(GPtrArray) deltas = NULL;
|
||||
if (!ostree_repo_list_static_delta_names (self, &deltas,
|
||||
cancellable, error))
|
||||
@@ -286,6 +285,8 @@ repo_prune_internal (OstreeRepo *self,
|
||||
* Use the %OSTREE_REPO_PRUNE_FLAGS_NO_PRUNE to just determine
|
||||
* statistics on objects that would be deleted, without actually
|
||||
* deleting them.
|
||||
*
|
||||
* This function takes an exclusive lock on the @self repository.
|
||||
*/
|
||||
gboolean
|
||||
ostree_repo_prune (OstreeRepo *self,
|
||||
@@ -297,6 +298,12 @@ ostree_repo_prune (OstreeRepo *self,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
g_autoptr(OstreeRepoAutoLock) lock =
|
||||
ostree_repo_auto_lock_push (self, OSTREE_REPO_LOCK_EXCLUSIVE, cancellable,
|
||||
error);
|
||||
if (!lock)
|
||||
return FALSE;
|
||||
|
||||
g_autoptr(GHashTable) objects = NULL;
|
||||
gboolean refs_only = flags & OSTREE_REPO_PRUNE_FLAGS_REFS_ONLY;
|
||||
|
||||
@@ -391,6 +398,8 @@ ostree_repo_prune (OstreeRepo *self,
|
||||
*
|
||||
* The %OSTREE_REPO_PRUNE_FLAGS_NO_PRUNE flag may be specified to just determine
|
||||
* statistics on objects that would be deleted, without actually deleting them.
|
||||
*
|
||||
* This function takes an exclusive lock on the @self repository.
|
||||
*/
|
||||
gboolean
|
||||
ostree_repo_prune_from_reachable (OstreeRepo *self,
|
||||
@@ -401,6 +410,12 @@ ostree_repo_prune_from_reachable (OstreeRepo *self,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
g_autoptr(OstreeRepoAutoLock) lock =
|
||||
ostree_repo_auto_lock_push (self, OSTREE_REPO_LOCK_EXCLUSIVE, cancellable,
|
||||
error);
|
||||
if (!lock)
|
||||
return FALSE;
|
||||
|
||||
g_autoptr(GHashTable) objects = NULL;
|
||||
|
||||
if (!ostree_repo_list_objects (self, OSTREE_REPO_LIST_OBJECTS_ALL | OSTREE_REPO_LIST_OBJECTS_NO_PARENTS,
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright © 2017 Endless Mobile, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ostree-core.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
gboolean
|
||||
_ostree_repo_verify_bindings (const char *collection_id,
|
||||
const char *ref_name,
|
||||
GVariant *commit,
|
||||
GError **error);
|
||||
|
||||
G_END_DECLS
|
||||
@@ -32,6 +32,7 @@
|
||||
|
||||
#include "ostree-core-private.h"
|
||||
#include "ostree-repo-private.h"
|
||||
#include "ostree-repo-pull-private.h"
|
||||
#include "ostree-repo-static-delta-private.h"
|
||||
#include "ostree-metalink.h"
|
||||
#include "ostree-fetcher-util.h"
|
||||
@@ -557,21 +558,6 @@ fetch_uri_contents_utf8_sync (OstreeFetcher *fetcher,
|
||||
cancellable, error);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
write_commitpartial_for (OtPullData *pull_data,
|
||||
const char *checksum,
|
||||
GError **error)
|
||||
{
|
||||
g_autofree char *commitpartial_path = _ostree_get_commitpartial_path (checksum);
|
||||
glnx_autofd int fd = openat (pull_data->repo->repo_dir_fd, commitpartial_path, O_EXCL | O_CREAT | O_WRONLY | O_CLOEXEC | O_NOCTTY, 0644);
|
||||
if (fd == -1)
|
||||
{
|
||||
if (errno != EEXIST)
|
||||
return glnx_throw_errno_prefix (error, "open(%s)", commitpartial_path);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
enqueue_one_object_request (OtPullData *pull_data,
|
||||
const char *checksum,
|
||||
@@ -1266,7 +1252,7 @@ meta_fetch_on_complete (GObject *object,
|
||||
pull_data->cancellable, error))
|
||||
goto out;
|
||||
|
||||
if (!write_commitpartial_for (pull_data, checksum, error))
|
||||
if (!ostree_repo_mark_commit_partial (pull_data->repo, checksum, TRUE, error))
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -1475,30 +1461,40 @@ get_remote_repo_collection_id (OtPullData *pull_data)
|
||||
}
|
||||
#endif /* OSTREE_ENABLE_EXPERIMENTAL_API */
|
||||
|
||||
/* Verify the ref and collection bindings.
|
||||
#endif /* HAVE_LIBCURL_OR_LIBSOUP */
|
||||
|
||||
/**
|
||||
* _ostree_repo_verify_bindings:
|
||||
* @collection_id: (nullable): Locally specified collection ID for the remote
|
||||
* the @commit was retrieved from, or %NULL if none is configured
|
||||
* @ref_name: (nullable): Ref name the commit was retrieved using, or %NULL if
|
||||
* the commit was retrieved by checksum
|
||||
* @commit: Commit data to check
|
||||
* @error: Return location for a #GError, or %NULL
|
||||
*
|
||||
* Verify the ref and collection bindings.
|
||||
*
|
||||
* The ref binding is verified only if it exists. But if we have the
|
||||
* collection ID specified in the remote configuration then the ref
|
||||
* binding must exist, otherwise the verification will fail. Parts of
|
||||
* the verification can be skipped by passing NULL to the requested_ref
|
||||
* parameter (in case we requested a checksum directly, without looking it up
|
||||
* from a ref).
|
||||
* collection ID specified in the remote configuration (@collection_id is
|
||||
* non-%NULL) then the ref binding must exist, otherwise the verification will
|
||||
* fail. Parts of the verification can be skipped by passing %NULL to the
|
||||
* @ref_name parameter (in case we requested a checksum directly, without
|
||||
* looking it up from a ref).
|
||||
*
|
||||
* The collection binding is verified only when we have collection ID
|
||||
* specified in the remote configuration. If it is specified, then the
|
||||
* binding must exist and must be equal to the remote repository
|
||||
* collection ID.
|
||||
*
|
||||
* Returns: %TRUE if bindings are correct, %FALSE otherwise
|
||||
* Since: 2017.14
|
||||
*/
|
||||
static gboolean
|
||||
verify_bindings (OtPullData *pull_data,
|
||||
GVariant *commit,
|
||||
const OstreeCollectionRef *requested_ref,
|
||||
GError **error)
|
||||
gboolean
|
||||
_ostree_repo_verify_bindings (const char *collection_id,
|
||||
const char *ref_name,
|
||||
GVariant *commit,
|
||||
GError **error)
|
||||
{
|
||||
g_autofree char *remote_collection_id = NULL;
|
||||
#ifdef OSTREE_ENABLE_EXPERIMENTAL_API
|
||||
remote_collection_id = get_remote_repo_collection_id (pull_data);
|
||||
#endif /* OSTREE_ENABLE_EXPERIMENTAL_API */
|
||||
g_autoptr(GVariant) metadata = g_variant_get_child_value (commit, 0);
|
||||
g_autofree const char **refs = NULL;
|
||||
if (!g_variant_lookup (metadata,
|
||||
@@ -1510,17 +1506,17 @@ verify_bindings (OtPullData *pull_data,
|
||||
* we certainly will not verify the collection binding in the
|
||||
* commit.
|
||||
*/
|
||||
if (remote_collection_id == NULL)
|
||||
if (collection_id == NULL)
|
||||
return TRUE;
|
||||
|
||||
return glnx_throw (error,
|
||||
"expected commit metadata to have ref "
|
||||
"Expected commit metadata to have ref "
|
||||
"binding information, found none");
|
||||
}
|
||||
|
||||
if (requested_ref != NULL)
|
||||
if (ref_name != NULL)
|
||||
{
|
||||
if (!g_strv_contains ((const char *const *) refs, requested_ref->ref_name))
|
||||
if (!g_strv_contains ((const char *const *) refs, ref_name))
|
||||
{
|
||||
g_autoptr(GString) refs_dump = g_string_new (NULL);
|
||||
const char *refs_str;
|
||||
@@ -1543,35 +1539,37 @@ verify_bindings (OtPullData *pull_data,
|
||||
refs_str = "no refs";
|
||||
}
|
||||
|
||||
return glnx_throw (error, "commit has no requested ref ‘%s’ "
|
||||
return glnx_throw (error, "Commit has no requested ref ‘%s’ "
|
||||
"in ref binding metadata (%s)",
|
||||
requested_ref->ref_name, refs_str);
|
||||
ref_name, refs_str);
|
||||
}
|
||||
}
|
||||
|
||||
if (remote_collection_id != NULL)
|
||||
if (collection_id != NULL)
|
||||
{
|
||||
#ifdef OSTREE_ENABLE_EXPERIMENTAL_API
|
||||
const char *collection_id;
|
||||
const char *collection_id_binding;
|
||||
if (!g_variant_lookup (metadata,
|
||||
OSTREE_COMMIT_META_KEY_COLLECTION_BINDING,
|
||||
"&s",
|
||||
&collection_id))
|
||||
&collection_id_binding))
|
||||
return glnx_throw (error,
|
||||
"expected commit metadata to have collection ID "
|
||||
"Expected commit metadata to have collection ID "
|
||||
"binding information, found none");
|
||||
if (!g_str_equal (collection_id, remote_collection_id))
|
||||
if (!g_str_equal (collection_id_binding, collection_id))
|
||||
return glnx_throw (error,
|
||||
"commit has collection ID ‘%s’ in collection binding "
|
||||
"Commit has collection ID ‘%s’ in collection binding "
|
||||
"metadata, while the remote it came from has "
|
||||
"collection ID ‘%s’",
|
||||
collection_id, remote_collection_id);
|
||||
collection_id_binding, collection_id);
|
||||
#endif
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
#ifdef HAVE_LIBCURL_OR_LIBSOUP
|
||||
|
||||
/* Look at a commit object, and determine whether there are
|
||||
* more things to fetch.
|
||||
*/
|
||||
@@ -1626,7 +1624,13 @@ scan_commit_object (OtPullData *pull_data,
|
||||
/* If ref is non-NULL then the commit we fetched was requested through the
|
||||
* branch, otherwise we requested a commit checksum without specifying a branch.
|
||||
*/
|
||||
if (!verify_bindings (pull_data, commit, ref, error))
|
||||
g_autofree char *remote_collection_id = NULL;
|
||||
#ifdef OSTREE_ENABLE_EXPERIMENTAL_API
|
||||
remote_collection_id = get_remote_repo_collection_id (pull_data);
|
||||
#endif /* OSTREE_ENABLE_EXPERIMENTAL_API */
|
||||
if (!_ostree_repo_verify_bindings (remote_collection_id,
|
||||
(ref != NULL) ? ref->ref_name : NULL,
|
||||
commit, error))
|
||||
return glnx_prefix_error (error, "Commit %s", checksum);
|
||||
|
||||
if (pull_data->timestamp_check)
|
||||
@@ -1802,7 +1806,7 @@ scan_one_metadata_object (OtPullData *pull_data,
|
||||
if (objtype == OSTREE_OBJECT_TYPE_COMMIT)
|
||||
{
|
||||
/* mark as partial to ensure we scan the commit below */
|
||||
if (!write_commitpartial_for (pull_data, checksum, error))
|
||||
if (!ostree_repo_mark_commit_partial (pull_data->repo, checksum, TRUE, error))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
@@ -1835,7 +1839,7 @@ scan_one_metadata_object (OtPullData *pull_data,
|
||||
if (objtype == OSTREE_OBJECT_TYPE_COMMIT)
|
||||
{
|
||||
/* mark as partial to ensure we scan the commit below */
|
||||
if (!write_commitpartial_for (pull_data, checksum, error))
|
||||
if (!ostree_repo_mark_commit_partial (pull_data->repo, checksum, TRUE, error))
|
||||
return FALSE;
|
||||
}
|
||||
if (!_ostree_repo_import_object (pull_data->repo, refd_repo,
|
||||
@@ -2690,6 +2694,15 @@ _ostree_repo_remote_new_fetcher (OstreeRepo *self,
|
||||
if (gzip)
|
||||
fetcher_flags |= OSTREE_FETCHER_FLAGS_TRANSFER_GZIP;
|
||||
|
||||
{ gboolean http2 = TRUE;
|
||||
if (!ostree_repo_get_remote_boolean_option (self, remote_name,
|
||||
"http2", TRUE,
|
||||
&http2, error))
|
||||
goto out;
|
||||
if (!http2)
|
||||
fetcher_flags |= OSTREE_FETCHER_FLAGS_DISABLE_HTTP2;
|
||||
}
|
||||
|
||||
fetcher = _ostree_fetcher_new (self->tmp_dir_fd, remote_name, fetcher_flags);
|
||||
|
||||
{
|
||||
@@ -4312,15 +4325,13 @@ ostree_repo_pull_with_options (OstreeRepo *self,
|
||||
{
|
||||
GLNX_HASH_TABLE_FOREACH_V (requested_refs_to_fetch, const char*, checksum)
|
||||
{
|
||||
g_autofree char *commitpartial_path = _ostree_get_commitpartial_path (checksum);
|
||||
if (!ot_ensure_unlinked_at (pull_data->repo->repo_dir_fd, commitpartial_path, 0))
|
||||
if (!ostree_repo_mark_commit_partial (pull_data->repo, checksum, FALSE, error))
|
||||
goto out;
|
||||
}
|
||||
|
||||
GLNX_HASH_TABLE_FOREACH_V (commits_to_fetch, const char*, commit)
|
||||
{
|
||||
g_autofree char *commitpartial_path = _ostree_get_commitpartial_path (commit);
|
||||
if (!ot_ensure_unlinked_at (pull_data->repo->repo_dir_fd, commitpartial_path, 0))
|
||||
if (!ostree_repo_mark_commit_partial (pull_data->repo, commit, FALSE, error))
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -726,13 +726,17 @@ _ostree_repo_list_refs_internal (OstreeRepo *self,
|
||||
* @self: Repo
|
||||
* @refspec_prefix: (allow-none): Only list refs which match this prefix
|
||||
* @out_all_refs: (out) (element-type utf8 utf8) (transfer container):
|
||||
* Mapping from ref to checksum
|
||||
* Mapping from refspec to checksum
|
||||
* @cancellable: Cancellable
|
||||
* @error: Error
|
||||
*
|
||||
* If @refspec_prefix is %NULL, list all local and remote refspecs,
|
||||
* with their current values in @out_all_refs. Otherwise, only list
|
||||
* refspecs which have @refspec_prefix as a prefix.
|
||||
*
|
||||
* @out_all_refs will be returned as a mapping from refspecs (including the
|
||||
* remote name) to checksums. If @refspec_prefix is non-%NULL, it will be
|
||||
* removed as a prefix from the hash table keys.
|
||||
*/
|
||||
gboolean
|
||||
ostree_repo_list_refs (OstreeRepo *self,
|
||||
@@ -752,16 +756,18 @@ ostree_repo_list_refs (OstreeRepo *self,
|
||||
* @self: Repo
|
||||
* @refspec_prefix: (allow-none): Only list refs which match this prefix
|
||||
* @out_all_refs: (out) (element-type utf8 utf8) (transfer container):
|
||||
* Mapping from ref to checksum
|
||||
* Mapping from refspec to checksum
|
||||
* @flags: Options controlling listing behavior
|
||||
* @cancellable: Cancellable
|
||||
* @error: Error
|
||||
*
|
||||
* If @refspec_prefix is %NULL, list all local and remote refspecs,
|
||||
* with their current values in @out_all_refs. Otherwise, only list
|
||||
* refspecs which have @refspec_prefix as a prefix. Differently from
|
||||
* ostree_repo_list_refs(), the prefix will not be removed from the ref
|
||||
* name.
|
||||
* refspecs which have @refspec_prefix as a prefix.
|
||||
*
|
||||
* @out_all_refs will be returned as a mapping from refspecs (including the
|
||||
* remote name) to checksums. Differently from ostree_repo_list_refs(), the
|
||||
* @refspec_prefix will not be removed from the refspecs in the hash table.
|
||||
*/
|
||||
gboolean
|
||||
ostree_repo_list_refs_ext (OstreeRepo *self,
|
||||
|
||||
@@ -128,11 +128,6 @@ _ostree_static_delta_part_open (GInputStream *part_in,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
gboolean _ostree_static_delta_dump (OstreeRepo *repo,
|
||||
const char *delta_id,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
typedef struct {
|
||||
guint n_ops_executed[OSTREE_STATIC_DELTA_N_OPS];
|
||||
} OstreeDeltaExecuteStats;
|
||||
|
||||
+667
-5
@@ -46,6 +46,9 @@
|
||||
#include <sys/file.h>
|
||||
#include <sys/statvfs.h>
|
||||
|
||||
#define REPO_LOCK_DISABLED (-2)
|
||||
#define REPO_LOCK_BLOCKING (-1)
|
||||
|
||||
/* ABI Size checks for ostree-repo.h, only for LP64 systems;
|
||||
* https://en.wikipedia.org/wiki/64-bit_computing#64-bit_data_models
|
||||
*
|
||||
@@ -156,6 +159,526 @@ G_DEFINE_TYPE (OstreeRepo, ostree_repo, G_TYPE_OBJECT)
|
||||
|
||||
#define SYSCONF_REMOTES SHORTENED_SYSCONFDIR "/ostree/remotes.d"
|
||||
|
||||
/* Repository locking
|
||||
*
|
||||
* To guard against objects being deleted (e.g., prune) while they're in
|
||||
* use by another operation is accessing them (e.g., commit), the
|
||||
* repository must be locked by concurrent writers.
|
||||
*
|
||||
* The locking is implemented by maintaining a thread local table of
|
||||
* lock stacks per repository. This allows thread safe locking since
|
||||
* each thread maintains its own lock stack. See the OstreeRepoLock type
|
||||
* below.
|
||||
*
|
||||
* The actual locking is done using either open file descriptor locks or
|
||||
* flock locks. This allows the locking to work with concurrent
|
||||
* processes. The lock file is held on the ".lock" file within the
|
||||
* repository.
|
||||
*
|
||||
* The intended usage is to take a shared lock when writing objects or
|
||||
* reading objects in critical sections. Exclusive locks are taken when
|
||||
* deleting objects.
|
||||
*
|
||||
* To allow fine grained locking within libostree, the lock is
|
||||
* maintained as a stack. The core APIs then push or pop from the stack.
|
||||
* When pushing or popping a lock state identical to the existing or
|
||||
* next state, the stack is simply updated. Only when upgrading or
|
||||
* downgrading the lock (changing to/from unlocked, pushing exclusive on
|
||||
* shared or popping exclusive to shared) are actual locking operations
|
||||
* performed.
|
||||
*/
|
||||
|
||||
static void
|
||||
free_repo_lock_table (gpointer data)
|
||||
{
|
||||
GHashTable *lock_table = data;
|
||||
|
||||
if (lock_table != NULL)
|
||||
{
|
||||
g_debug ("Free lock table");
|
||||
g_hash_table_destroy (lock_table);
|
||||
}
|
||||
}
|
||||
|
||||
static GPrivate repo_lock_table = G_PRIVATE_INIT (free_repo_lock_table);
|
||||
|
||||
typedef struct {
|
||||
int fd;
|
||||
GQueue stack;
|
||||
} OstreeRepoLock;
|
||||
|
||||
typedef struct {
|
||||
guint len;
|
||||
int state;
|
||||
const char *name;
|
||||
} OstreeRepoLockInfo;
|
||||
|
||||
static void
|
||||
repo_lock_info (OstreeRepoLock *lock, OstreeRepoLockInfo *out_info)
|
||||
{
|
||||
g_assert (lock != NULL);
|
||||
g_assert (out_info != NULL);
|
||||
|
||||
OstreeRepoLockInfo info;
|
||||
info.len = g_queue_get_length (&lock->stack);
|
||||
if (info.len == 0)
|
||||
{
|
||||
info.state = LOCK_UN;
|
||||
info.name = "unlocked";
|
||||
}
|
||||
else
|
||||
{
|
||||
info.state = GPOINTER_TO_INT (g_queue_peek_head (&lock->stack));
|
||||
info.name = (info.state == LOCK_EX) ? "exclusive" : "shared";
|
||||
}
|
||||
|
||||
*out_info = info;
|
||||
}
|
||||
|
||||
static void
|
||||
free_repo_lock (gpointer data)
|
||||
{
|
||||
OstreeRepoLock *lock = data;
|
||||
|
||||
if (lock != NULL)
|
||||
{
|
||||
OstreeRepoLockInfo info;
|
||||
repo_lock_info (lock, &info);
|
||||
|
||||
g_debug ("Free lock: state=%s, depth=%u", info.name, info.len);
|
||||
g_queue_clear (&lock->stack);
|
||||
if (lock->fd >= 0)
|
||||
{
|
||||
g_debug ("Closing repo lock file");
|
||||
(void) close (lock->fd);
|
||||
}
|
||||
g_free (lock);
|
||||
}
|
||||
}
|
||||
|
||||
/* Wrapper to handle flock vs OFD locking based on GLnxLockFile */
|
||||
static gboolean
|
||||
do_repo_lock (int fd,
|
||||
int flags)
|
||||
{
|
||||
int res;
|
||||
|
||||
#ifdef F_OFD_SETLK
|
||||
struct flock fl = {
|
||||
.l_type = (flags & ~LOCK_NB) == LOCK_EX ? F_WRLCK : F_RDLCK,
|
||||
.l_whence = SEEK_SET,
|
||||
.l_start = 0,
|
||||
.l_len = 0,
|
||||
};
|
||||
|
||||
res = TEMP_FAILURE_RETRY (fcntl (fd, (flags & LOCK_NB) ? F_OFD_SETLK : F_OFD_SETLKW, &fl));
|
||||
#else
|
||||
res = -1;
|
||||
errno = EINVAL;
|
||||
#endif
|
||||
|
||||
/* Fallback to flock when OFD locks not available */
|
||||
if (res < 0)
|
||||
{
|
||||
if (errno == EINVAL)
|
||||
res = TEMP_FAILURE_RETRY (flock (fd, flags));
|
||||
if (res < 0)
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* Wrapper to handle flock vs OFD unlocking based on GLnxLockFile */
|
||||
static gboolean
|
||||
do_repo_unlock (int fd,
|
||||
int flags)
|
||||
{
|
||||
int res;
|
||||
|
||||
#ifdef F_OFD_SETLK
|
||||
struct flock fl = {
|
||||
.l_type = F_UNLCK,
|
||||
.l_whence = SEEK_SET,
|
||||
.l_start = 0,
|
||||
.l_len = 0,
|
||||
};
|
||||
|
||||
res = TEMP_FAILURE_RETRY (fcntl (fd, (flags & LOCK_NB) ? F_OFD_SETLK : F_OFD_SETLKW, &fl));
|
||||
#else
|
||||
res = -1;
|
||||
errno = EINVAL;
|
||||
#endif
|
||||
|
||||
/* Fallback to flock when OFD locks not available */
|
||||
if (res < 0)
|
||||
{
|
||||
if (errno == EINVAL)
|
||||
res = TEMP_FAILURE_RETRY (flock (fd, LOCK_UN | flags));
|
||||
if (res < 0)
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
push_repo_lock (OstreeRepo *self,
|
||||
OstreeRepoLockType lock_type,
|
||||
gboolean blocking,
|
||||
GError **error)
|
||||
{
|
||||
int flags = (lock_type == OSTREE_REPO_LOCK_EXCLUSIVE) ? LOCK_EX : LOCK_SH;
|
||||
if (!blocking)
|
||||
flags |= LOCK_NB;
|
||||
|
||||
GHashTable *lock_table = g_private_get (&repo_lock_table);
|
||||
if (lock_table == NULL)
|
||||
{
|
||||
g_debug ("Creating repo lock table");
|
||||
lock_table = g_hash_table_new_full (NULL, NULL, NULL,
|
||||
(GDestroyNotify)free_repo_lock);
|
||||
g_private_set (&repo_lock_table, lock_table);
|
||||
}
|
||||
|
||||
OstreeRepoLock *lock = g_hash_table_lookup (lock_table, self);
|
||||
if (lock == NULL)
|
||||
{
|
||||
lock = g_new0 (OstreeRepoLock, 1);
|
||||
g_queue_init (&lock->stack);
|
||||
g_debug ("Opening repo lock file");
|
||||
lock->fd = TEMP_FAILURE_RETRY (openat (self->repo_dir_fd, ".lock",
|
||||
O_CREAT | O_RDWR | O_CLOEXEC,
|
||||
0600));
|
||||
if (lock->fd < 0)
|
||||
{
|
||||
free_repo_lock (lock);
|
||||
return glnx_throw_errno_prefix (error,
|
||||
"Opening lock file %s/.lock failed",
|
||||
gs_file_get_path_cached (self->repodir));
|
||||
}
|
||||
g_hash_table_insert (lock_table, self, lock);
|
||||
}
|
||||
|
||||
OstreeRepoLockInfo info;
|
||||
repo_lock_info (lock, &info);
|
||||
g_debug ("Push lock: state=%s, depth=%u", info.name, info.len);
|
||||
|
||||
if (info.state == LOCK_EX)
|
||||
{
|
||||
g_debug ("Repo already locked exclusively, extending stack");
|
||||
g_queue_push_head (&lock->stack, GINT_TO_POINTER (LOCK_EX));
|
||||
}
|
||||
else
|
||||
{
|
||||
int next_state = (flags & LOCK_EX) ? LOCK_EX : LOCK_SH;
|
||||
const char *next_state_name = (flags & LOCK_EX) ? "exclusive" : "shared";
|
||||
|
||||
g_debug ("Locking repo %s", next_state_name);
|
||||
if (!do_repo_lock (lock->fd, flags))
|
||||
return glnx_throw_errno_prefix (error, "Locking repo %s failed",
|
||||
next_state_name);
|
||||
|
||||
g_queue_push_head (&lock->stack, GINT_TO_POINTER (next_state));
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
pop_repo_lock (OstreeRepo *self,
|
||||
gboolean blocking,
|
||||
GError **error)
|
||||
{
|
||||
int flags = blocking ? 0 : LOCK_NB;
|
||||
|
||||
GHashTable *lock_table = g_private_get (&repo_lock_table);
|
||||
g_return_val_if_fail (lock_table != NULL, FALSE);
|
||||
|
||||
OstreeRepoLock *lock = g_hash_table_lookup (lock_table, self);
|
||||
g_return_val_if_fail (lock != NULL, FALSE);
|
||||
g_return_val_if_fail (lock->fd != -1, FALSE);
|
||||
|
||||
OstreeRepoLockInfo info;
|
||||
repo_lock_info (lock, &info);
|
||||
g_return_val_if_fail (info.len > 0, FALSE);
|
||||
|
||||
g_debug ("Pop lock: state=%s, depth=%u", info.name, info.len);
|
||||
if (info.len > 1)
|
||||
{
|
||||
int next_state = GPOINTER_TO_INT (g_queue_peek_nth (&lock->stack, 1));
|
||||
|
||||
/* Drop back to the previous lock state if it differs */
|
||||
if (next_state != info.state)
|
||||
{
|
||||
/* We should never drop from shared to exclusive */
|
||||
g_return_val_if_fail (next_state == LOCK_SH, FALSE);
|
||||
g_debug ("Returning lock state to shared");
|
||||
if (!do_repo_lock (lock->fd, next_state | flags))
|
||||
return glnx_throw_errno_prefix (error,
|
||||
"Setting repo lock to shared failed");
|
||||
}
|
||||
else
|
||||
g_debug ("Maintaining lock state as %s", info.name);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Lock stack will be empty, unlock */
|
||||
g_debug ("Unlocking repo");
|
||||
if (!do_repo_unlock (lock->fd, flags))
|
||||
return glnx_throw_errno_prefix (error, "Unlocking repo failed");
|
||||
}
|
||||
|
||||
g_queue_pop_head (&lock->stack);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* ostree_repo_lock_push:
|
||||
* @self: a #OstreeRepo
|
||||
* @lock_type: the type of lock to acquire
|
||||
* @cancellable: a #GCancellable
|
||||
* @error: a #GError
|
||||
*
|
||||
* Takes a lock on the repository and adds it to the lock stack. If @lock_type
|
||||
* is %OSTREE_REPO_LOCK_SHARED, a shared lock is taken. If @lock_type is
|
||||
* %OSTREE_REPO_LOCK_EXCLUSIVE, an exclusive lock is taken. The actual lock
|
||||
* state is only changed when locking a previously unlocked repository or
|
||||
* upgrading the lock from shared to exclusive. If the requested lock state is
|
||||
* unchanged or would represent a downgrade (exclusive to shared), the lock
|
||||
* state is not changed and the stack is simply updated.
|
||||
*
|
||||
* ostree_repo_lock_push() waits for the lock depending on the repository's
|
||||
* lock-timeout configuration. When lock-timeout is -1, a blocking lock is
|
||||
* attempted. Otherwise, the lock is taken non-blocking and
|
||||
* ostree_repo_lock_push() will sleep synchronously up to lock-timeout seconds
|
||||
* attempting to acquire the lock. If the lock cannot be acquired within the
|
||||
* timeout, a %G_IO_ERROR_WOULD_BLOCK error is returned.
|
||||
*
|
||||
* If @self is not writable by the user, then no locking is attempted and
|
||||
* %TRUE is returned.
|
||||
*
|
||||
* Returns: %TRUE on success, otherwise %FALSE with @error set
|
||||
* Since: 2017.14
|
||||
*/
|
||||
gboolean
|
||||
ostree_repo_lock_push (OstreeRepo *self,
|
||||
OstreeRepoLockType lock_type,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
g_return_val_if_fail (self != NULL, FALSE);
|
||||
g_return_val_if_fail (OSTREE_IS_REPO (self), FALSE);
|
||||
g_return_val_if_fail (self->inited, FALSE);
|
||||
g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE);
|
||||
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
|
||||
|
||||
if (!self->writable)
|
||||
return TRUE;
|
||||
|
||||
g_assert (self->lock_timeout_seconds >= REPO_LOCK_DISABLED);
|
||||
if (self->lock_timeout_seconds == REPO_LOCK_DISABLED)
|
||||
return TRUE; /* No locking */
|
||||
else if (self->lock_timeout_seconds == REPO_LOCK_BLOCKING)
|
||||
{
|
||||
g_debug ("Pushing lock blocking");
|
||||
return push_repo_lock (self, lock_type, TRUE, error);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Convert to unsigned to guard against negative values */
|
||||
guint lock_timeout_seconds = self->lock_timeout_seconds;
|
||||
guint waited = 0;
|
||||
g_debug ("Pushing lock non-blocking with timeout %u",
|
||||
lock_timeout_seconds);
|
||||
for (;;)
|
||||
{
|
||||
if (g_cancellable_set_error_if_cancelled (cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
g_autoptr(GError) local_error = NULL;
|
||||
if (push_repo_lock (self, lock_type, FALSE, &local_error))
|
||||
return TRUE;
|
||||
|
||||
if (!g_error_matches (local_error, G_IO_ERROR,
|
||||
G_IO_ERROR_WOULD_BLOCK))
|
||||
{
|
||||
g_propagate_error (error, g_steal_pointer (&local_error));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (waited >= lock_timeout_seconds)
|
||||
{
|
||||
g_debug ("Push lock: Could not acquire lock within %u seconds",
|
||||
lock_timeout_seconds);
|
||||
g_propagate_error (error, g_steal_pointer (&local_error));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Sleep 1 second and try again */
|
||||
if (waited % 60 == 0)
|
||||
{
|
||||
guint remaining = lock_timeout_seconds - waited;
|
||||
g_debug ("Push lock: Waiting %u more second%s to acquire lock",
|
||||
remaining, (remaining == 1) ? "" : "s");
|
||||
}
|
||||
waited++;
|
||||
sleep (1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ostree_repo_lock_pop:
|
||||
* @self: a #OstreeRepo
|
||||
* @cancellable: a #GCancellable
|
||||
* @error: a #GError
|
||||
*
|
||||
* Remove the current repository lock state from the lock stack. If the lock
|
||||
* stack becomes empty, the repository is unlocked. Otherwise, the lock state
|
||||
* only changes when transitioning from an exclusive lock back to a shared
|
||||
* lock.
|
||||
*
|
||||
* ostree_repo_lock_pop() waits for the lock depending on the repository's
|
||||
* lock-timeout configuration. When lock-timeout is -1, a blocking lock is
|
||||
* attempted. Otherwise, the lock is removed non-blocking and
|
||||
* ostree_repo_lock_pop() will sleep synchronously up to lock-timeout seconds
|
||||
* attempting to remove the lock. If the lock cannot be removed within the
|
||||
* timeout, a %G_IO_ERROR_WOULD_BLOCK error is returned.
|
||||
*
|
||||
* If @self is not writable by the user, then no unlocking is attempted and
|
||||
* %TRUE is returned.
|
||||
*
|
||||
* Returns: %TRUE on success, otherwise %FALSE with @error set
|
||||
* Since: 2017.14
|
||||
*/
|
||||
gboolean
|
||||
ostree_repo_lock_pop (OstreeRepo *self,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
g_return_val_if_fail (self != NULL, FALSE);
|
||||
g_return_val_if_fail (OSTREE_IS_REPO (self), FALSE);
|
||||
g_return_val_if_fail (self->inited, FALSE);
|
||||
g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE);
|
||||
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
|
||||
|
||||
if (!self->writable)
|
||||
return TRUE;
|
||||
|
||||
g_assert (self->lock_timeout_seconds >= REPO_LOCK_DISABLED);
|
||||
if (self->lock_timeout_seconds == REPO_LOCK_DISABLED)
|
||||
return TRUE;
|
||||
else if (self->lock_timeout_seconds == REPO_LOCK_BLOCKING)
|
||||
{
|
||||
g_debug ("Popping lock blocking");
|
||||
return pop_repo_lock (self, TRUE, error);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Convert to unsigned to guard against negative values */
|
||||
guint lock_timeout_seconds = self->lock_timeout_seconds;
|
||||
guint waited = 0;
|
||||
g_debug ("Popping lock non-blocking with timeout %u",
|
||||
lock_timeout_seconds);
|
||||
for (;;)
|
||||
{
|
||||
if (g_cancellable_set_error_if_cancelled (cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
g_autoptr(GError) local_error = NULL;
|
||||
if (pop_repo_lock (self, FALSE, &local_error))
|
||||
return TRUE;
|
||||
|
||||
if (!g_error_matches (local_error, G_IO_ERROR,
|
||||
G_IO_ERROR_WOULD_BLOCK))
|
||||
{
|
||||
g_propagate_error (error, g_steal_pointer (&local_error));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (waited >= lock_timeout_seconds)
|
||||
{
|
||||
g_debug ("Pop lock: Could not remove lock within %u seconds",
|
||||
lock_timeout_seconds);
|
||||
g_propagate_error (error, g_steal_pointer (&local_error));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Sleep 1 second and try again */
|
||||
if (waited % 60 == 0)
|
||||
{
|
||||
guint remaining = lock_timeout_seconds - waited;
|
||||
g_debug ("Pop lock: Waiting %u more second%s to remove lock",
|
||||
remaining, (remaining == 1) ? "" : "s");
|
||||
}
|
||||
waited++;
|
||||
sleep (1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ostree_repo_auto_lock_push: (skip)
|
||||
* @self: a #OstreeRepo
|
||||
* @lock_type: the type of lock to acquire
|
||||
* @cancellable: a #GCancellable
|
||||
* @error: a #GError
|
||||
*
|
||||
* Like ostree_repo_lock_push(), but for usage with #OstreeRepoAutoLock.
|
||||
* The intended usage is to declare the #OstreeRepoAutoLock with
|
||||
* g_autoptr() so that ostree_repo_auto_lock_cleanup() is called when it
|
||||
* goes out of scope. This will automatically pop the lock status off
|
||||
* the stack if it was acquired successfully.
|
||||
*
|
||||
* |[<!-- language="C" -->
|
||||
* g_autoptr(OstreeRepoAutoLock) lock = NULL;
|
||||
* lock = ostree_repo_auto_lock_push (repo, lock_type, cancellable, error);
|
||||
* if (!lock)
|
||||
* return FALSE;
|
||||
* ]|
|
||||
*
|
||||
* Returns: @self on success, otherwise %NULL with @error set
|
||||
* Since: 2017.14
|
||||
*/
|
||||
OstreeRepoAutoLock *
|
||||
ostree_repo_auto_lock_push (OstreeRepo *self,
|
||||
OstreeRepoLockType lock_type,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
if (!ostree_repo_lock_push (self, lock_type, cancellable, error))
|
||||
return NULL;
|
||||
return (OstreeRepoAutoLock *)self;
|
||||
}
|
||||
|
||||
/**
|
||||
* ostree_repo_auto_lock_cleanup: (skip)
|
||||
* @lock: a #OstreeRepoAutoLock
|
||||
*
|
||||
* A cleanup handler for use with ostree_repo_auto_lock_push(). If @lock is
|
||||
* not %NULL, ostree_repo_lock_pop() will be called on it. If
|
||||
* ostree_repo_lock_pop() fails, a critical warning will be emitted.
|
||||
*
|
||||
* Since: 2017.14
|
||||
*/
|
||||
void
|
||||
ostree_repo_auto_lock_cleanup (OstreeRepoAutoLock *lock)
|
||||
{
|
||||
OstreeRepo *repo = lock;
|
||||
if (repo)
|
||||
{
|
||||
g_autoptr(GError) error = NULL;
|
||||
int errsv = errno;
|
||||
|
||||
if (!ostree_repo_lock_pop (repo, NULL, &error))
|
||||
g_critical ("Cleanup repo lock failed: %s", error->message);
|
||||
|
||||
errno = errsv;
|
||||
}
|
||||
}
|
||||
|
||||
static GFile *
|
||||
get_remotes_d_dir (OstreeRepo *self,
|
||||
GFile *sysroot);
|
||||
@@ -505,18 +1028,26 @@ ostree_repo_finalize (GObject *object)
|
||||
g_hash_table_destroy (self->updated_uncompressed_dirs);
|
||||
if (self->config)
|
||||
g_key_file_free (self->config);
|
||||
g_clear_pointer (&self->txn_refs, g_hash_table_destroy);
|
||||
g_clear_pointer (&self->txn_collection_refs, g_hash_table_destroy);
|
||||
g_clear_pointer (&self->txn.refs, g_hash_table_destroy);
|
||||
g_clear_pointer (&self->txn.collection_refs, g_hash_table_destroy);
|
||||
g_clear_error (&self->writable_error);
|
||||
g_clear_pointer (&self->object_sizes, (GDestroyNotify) g_hash_table_unref);
|
||||
g_clear_pointer (&self->dirmeta_cache, (GDestroyNotify) g_hash_table_unref);
|
||||
g_mutex_clear (&self->cache_lock);
|
||||
g_mutex_clear (&self->txn_stats_lock);
|
||||
g_mutex_clear (&self->txn_lock);
|
||||
g_free (self->collection_id);
|
||||
|
||||
g_clear_pointer (&self->remotes, g_hash_table_destroy);
|
||||
g_mutex_clear (&self->remotes_lock);
|
||||
|
||||
GHashTable *lock_table = g_private_get (&repo_lock_table);
|
||||
if (lock_table)
|
||||
{
|
||||
g_hash_table_remove (lock_table, self);
|
||||
if (g_hash_table_size (lock_table) == 0)
|
||||
g_private_replace (&repo_lock_table, NULL);
|
||||
}
|
||||
|
||||
G_OBJECT_CLASS (ostree_repo_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
@@ -672,7 +1203,7 @@ ostree_repo_init (OstreeRepo *self)
|
||||
test_error_keys, G_N_ELEMENTS (test_error_keys));
|
||||
|
||||
g_mutex_init (&self->cache_lock);
|
||||
g_mutex_init (&self->txn_stats_lock);
|
||||
g_mutex_init (&self->txn_lock);
|
||||
|
||||
self->remotes = g_hash_table_new_full (g_str_hash, g_str_equal,
|
||||
(GDestroyNotify) NULL,
|
||||
@@ -1266,7 +1797,7 @@ _ostree_repo_remote_list (OstreeRepo *self,
|
||||
g_mutex_unlock (&self->remotes_lock);
|
||||
|
||||
if (self->parent_repo)
|
||||
_ostree_repo_remote_list (self, out);
|
||||
_ostree_repo_remote_list (self->parent_repo, out);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1797,6 +2328,7 @@ repo_create_at_internal (int dfd,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
GLNX_AUTO_PREFIX_ERROR ("Creating repo", error);
|
||||
struct stat stbuf;
|
||||
/* We do objects/ last - if it exists we do nothing and exit successfully */
|
||||
const char *state_dirs[] = { "tmp", "extensions", "state",
|
||||
@@ -2208,6 +2740,27 @@ reload_core_config (OstreeRepo *self,
|
||||
self->tmp_expiry_seconds = g_ascii_strtoull (tmp_expiry_seconds, NULL, 10);
|
||||
}
|
||||
|
||||
/* Disable locking by default for now */
|
||||
{ gboolean locking;
|
||||
if (!ot_keyfile_get_boolean_with_default (self->config, "core", "locking",
|
||||
FALSE, &locking, error))
|
||||
return FALSE;
|
||||
if (!locking)
|
||||
{
|
||||
self->lock_timeout_seconds = REPO_LOCK_DISABLED;
|
||||
}
|
||||
else
|
||||
{
|
||||
g_autofree char *lock_timeout_seconds = NULL;
|
||||
|
||||
if (!ot_keyfile_get_value_with_default (self->config, "core", "lock-timeout-secs", "30",
|
||||
&lock_timeout_seconds, error))
|
||||
return FALSE;
|
||||
|
||||
self->lock_timeout_seconds = g_ascii_strtoull (lock_timeout_seconds, NULL, 10);
|
||||
}
|
||||
}
|
||||
|
||||
{ g_autofree char *compression_level_str = NULL;
|
||||
|
||||
/* gzip defaults to 6 */
|
||||
@@ -3388,6 +3941,115 @@ ostree_repo_delete_object (OstreeRepo *self,
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
fsck_metadata_object (OstreeRepo *self,
|
||||
OstreeObjectType objtype,
|
||||
const char *sha256,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
const char *errmsg = glnx_strjoina ("fsck ", sha256, ".", ostree_object_type_to_string (objtype));
|
||||
GLNX_AUTO_PREFIX_ERROR (errmsg, error);
|
||||
g_autoptr(GVariant) metadata = NULL;
|
||||
if (!load_metadata_internal (self, objtype, sha256, TRUE,
|
||||
&metadata, NULL, NULL, NULL,
|
||||
cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
g_auto(OtChecksum) hasher = { 0, };
|
||||
ot_checksum_init (&hasher);
|
||||
ot_checksum_update (&hasher, g_variant_get_data (metadata), g_variant_get_size (metadata));
|
||||
|
||||
char actual_checksum[OSTREE_SHA256_STRING_LEN+1];
|
||||
ot_checksum_get_hexdigest (&hasher, actual_checksum, sizeof (actual_checksum));
|
||||
if (!_ostree_compare_object_checksum (objtype, sha256, actual_checksum, error))
|
||||
return FALSE;
|
||||
|
||||
switch (objtype)
|
||||
{
|
||||
case OSTREE_OBJECT_TYPE_COMMIT:
|
||||
if (!ostree_validate_structureof_commit (metadata, error))
|
||||
return FALSE;
|
||||
break;
|
||||
case OSTREE_OBJECT_TYPE_DIR_TREE:
|
||||
if (!ostree_validate_structureof_dirtree (metadata, error))
|
||||
return FALSE;
|
||||
break;
|
||||
case OSTREE_OBJECT_TYPE_DIR_META:
|
||||
if (!ostree_validate_structureof_dirmeta (metadata, error))
|
||||
return FALSE;
|
||||
break;
|
||||
case OSTREE_OBJECT_TYPE_TOMBSTONE_COMMIT:
|
||||
case OSTREE_OBJECT_TYPE_COMMIT_META:
|
||||
/* TODO */
|
||||
break;
|
||||
case OSTREE_OBJECT_TYPE_FILE:
|
||||
g_assert_not_reached ();
|
||||
break;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
fsck_content_object (OstreeRepo *self,
|
||||
const char *sha256,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
const char *errmsg = glnx_strjoina ("fsck content object ", sha256);
|
||||
GLNX_AUTO_PREFIX_ERROR (errmsg, error);
|
||||
g_autoptr(GInputStream) input = NULL;
|
||||
g_autoptr(GFileInfo) file_info = NULL;
|
||||
g_autoptr(GVariant) xattrs = NULL;
|
||||
|
||||
if (!ostree_repo_load_file (self, sha256, &input, &file_info, &xattrs,
|
||||
cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
/* TODO more consistency checks here */
|
||||
const guint32 mode = g_file_info_get_attribute_uint32 (file_info, "unix::mode");
|
||||
if (!ostree_validate_structureof_file_mode (mode, error))
|
||||
return FALSE;
|
||||
|
||||
g_autofree guchar *computed_csum = NULL;
|
||||
if (!ostree_checksum_file_from_input (file_info, xattrs, input,
|
||||
OSTREE_OBJECT_TYPE_FILE, &computed_csum,
|
||||
cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
char actual_checksum[OSTREE_SHA256_STRING_LEN+1];
|
||||
ostree_checksum_inplace_from_bytes (computed_csum, actual_checksum);
|
||||
return _ostree_compare_object_checksum (OSTREE_OBJECT_TYPE_FILE, sha256, actual_checksum, error);
|
||||
}
|
||||
|
||||
/**
|
||||
* ostree_repo_fsck_object:
|
||||
* @self: Repo
|
||||
* @objtype: Object type
|
||||
* @sha256: Checksum
|
||||
* @cancellable: Cancellable
|
||||
* @error: Error
|
||||
*
|
||||
* Verify consistency of the object; this performs checks only relevant to the
|
||||
* immediate object itself, such as checksumming. This API call will not itself
|
||||
* traverse metadata objects for example.
|
||||
*
|
||||
* Since: 2017.15
|
||||
*/
|
||||
gboolean
|
||||
ostree_repo_fsck_object (OstreeRepo *self,
|
||||
OstreeObjectType objtype,
|
||||
const char *sha256,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
if (OSTREE_OBJECT_TYPE_IS_META (objtype))
|
||||
return fsck_metadata_object (self, objtype, sha256, cancellable, error);
|
||||
else
|
||||
return fsck_content_object (self, sha256, cancellable, error);
|
||||
}
|
||||
|
||||
/**
|
||||
* ostree_repo_import_object_from:
|
||||
* @self: Destination repo
|
||||
|
||||
@@ -107,6 +107,48 @@ OstreeRepo * ostree_repo_create_at (int dfd,
|
||||
|
||||
#ifdef OSTREE_ENABLE_EXPERIMENTAL_API
|
||||
|
||||
/**
|
||||
* OstreeRepoLockType:
|
||||
* @OSTREE_REPO_LOCK_SHARED: A shared lock
|
||||
* @OSTREE_REPO_LOCK_EXCLUSIVE: An exclusive lock
|
||||
*
|
||||
* The type of repository lock to acquire.
|
||||
*
|
||||
* Since: 2017.14
|
||||
*/
|
||||
typedef enum {
|
||||
OSTREE_REPO_LOCK_SHARED,
|
||||
OSTREE_REPO_LOCK_EXCLUSIVE
|
||||
} OstreeRepoLockType;
|
||||
|
||||
_OSTREE_PUBLIC
|
||||
gboolean ostree_repo_lock_push (OstreeRepo *self,
|
||||
OstreeRepoLockType lock_type,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
_OSTREE_PUBLIC
|
||||
gboolean ostree_repo_lock_pop (OstreeRepo *self,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
/**
|
||||
* OstreeRepoAutoLock: (skip)
|
||||
*
|
||||
* This is simply an alias to #OstreeRepo used for automatic lock cleanup.
|
||||
* See ostree_repo_auto_lock_push() for its intended usage.
|
||||
*
|
||||
* Since: 2017.14
|
||||
*/
|
||||
typedef OstreeRepo OstreeRepoAutoLock;
|
||||
|
||||
_OSTREE_PUBLIC
|
||||
OstreeRepoAutoLock * ostree_repo_auto_lock_push (OstreeRepo *self,
|
||||
OstreeRepoLockType lock_type,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
_OSTREE_PUBLIC
|
||||
void ostree_repo_auto_lock_cleanup (OstreeRepoAutoLock *lock);
|
||||
|
||||
_OSTREE_PUBLIC
|
||||
const gchar * ostree_repo_get_collection_id (OstreeRepo *self);
|
||||
_OSTREE_PUBLIC
|
||||
@@ -320,6 +362,12 @@ gboolean ostree_repo_abort_transaction (OstreeRepo *self,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
_OSTREE_PUBLIC
|
||||
gboolean ostree_repo_mark_commit_partial (OstreeRepo *self,
|
||||
const char *checksum,
|
||||
gboolean is_partial,
|
||||
GError **error);
|
||||
|
||||
_OSTREE_PUBLIC
|
||||
void ostree_repo_transaction_set_refspec (OstreeRepo *self,
|
||||
const char *refspec,
|
||||
@@ -608,10 +656,17 @@ gboolean ostree_repo_import_object_from_with_trust (OstreeRepo *s
|
||||
_OSTREE_PUBLIC
|
||||
gboolean ostree_repo_delete_object (OstreeRepo *self,
|
||||
OstreeObjectType objtype,
|
||||
const char *sha256,
|
||||
const char *sha256,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
_OSTREE_PUBLIC
|
||||
gboolean ostree_repo_fsck_object (OstreeRepo *self,
|
||||
OstreeObjectType objtype,
|
||||
const char *sha256,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
/**
|
||||
* OstreeRepoCommitFilterResult:
|
||||
* @OSTREE_REPO_COMMIT_FILTER_ALLOW: Do commit this object
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
*
|
||||
* Since: 2017.4
|
||||
*/
|
||||
#define OSTREE_RELEASE_VERSION (14)
|
||||
#define OSTREE_RELEASE_VERSION (15)
|
||||
|
||||
/**
|
||||
* OSTREE_VERSION
|
||||
@@ -52,7 +52,7 @@
|
||||
*
|
||||
* Since: 2017.4
|
||||
*/
|
||||
#define OSTREE_VERSION (2017.14)
|
||||
#define OSTREE_VERSION (2017.15)
|
||||
|
||||
/**
|
||||
* OSTREE_VERSION_S:
|
||||
@@ -62,7 +62,7 @@
|
||||
*
|
||||
* Since: 2017.4
|
||||
*/
|
||||
#define OSTREE_VERSION_S "2017.14"
|
||||
#define OSTREE_VERSION_S "2017.15"
|
||||
|
||||
#define OSTREE_ENCODE_VERSION(year,release) \
|
||||
((year) << 16 | (release))
|
||||
|
||||
@@ -65,6 +65,16 @@ ot_checksum_instream_init (OtChecksumInstream *self)
|
||||
OtChecksumInstream *
|
||||
ot_checksum_instream_new (GInputStream *base,
|
||||
GChecksumType checksum_type)
|
||||
{
|
||||
return ot_checksum_instream_new_with_start (base, checksum_type, NULL, 0);
|
||||
}
|
||||
|
||||
/* Initialize a checksum stream with starting state from data */
|
||||
OtChecksumInstream *
|
||||
ot_checksum_instream_new_with_start (GInputStream *base,
|
||||
GChecksumType checksum_type,
|
||||
const guint8 *buf,
|
||||
size_t len)
|
||||
{
|
||||
OtChecksumInstream *stream;
|
||||
|
||||
@@ -77,6 +87,8 @@ ot_checksum_instream_new (GInputStream *base,
|
||||
/* For now */
|
||||
g_assert (checksum_type == G_CHECKSUM_SHA256);
|
||||
ot_checksum_init (&stream->priv->checksum);
|
||||
if (buf)
|
||||
ot_checksum_update (&stream->priv->checksum, buf, len);
|
||||
|
||||
return (OtChecksumInstream*) (stream);
|
||||
}
|
||||
|
||||
@@ -51,6 +51,8 @@ struct _OtChecksumInstreamClass
|
||||
GType ot_checksum_instream_get_type (void) G_GNUC_CONST;
|
||||
|
||||
OtChecksumInstream * ot_checksum_instream_new (GInputStream *stream, GChecksumType checksum);
|
||||
OtChecksumInstream * ot_checksum_instream_new_with_start (GInputStream *stream, GChecksumType checksum,
|
||||
const guint8 *buf, size_t len);
|
||||
|
||||
char * ot_checksum_instream_get_string (OtChecksumInstream *stream);
|
||||
|
||||
|
||||
@@ -37,11 +37,13 @@ static char *opt_body_file;
|
||||
static gboolean opt_editor;
|
||||
static char *opt_parent;
|
||||
static gboolean opt_orphan;
|
||||
static gboolean opt_no_bindings;
|
||||
static char **opt_bind_refs;
|
||||
static char *opt_branch;
|
||||
static char *opt_statoverride_file;
|
||||
static char *opt_skiplist_file;
|
||||
static char **opt_metadata_strings;
|
||||
static char **opt_metadata_variants;
|
||||
static char **opt_detached_metadata_strings;
|
||||
static gboolean opt_link_checkout_speedup;
|
||||
static gboolean opt_skip_if_unchanged;
|
||||
@@ -89,9 +91,11 @@ static GOptionEntry options[] = {
|
||||
{ "editor", 'e', 0, G_OPTION_ARG_NONE, &opt_editor, "Use an editor to write the commit message", NULL },
|
||||
{ "branch", 'b', 0, G_OPTION_ARG_STRING, &opt_branch, "Branch", "BRANCH" },
|
||||
{ "orphan", 0, 0, G_OPTION_ARG_NONE, &opt_orphan, "Create a commit without writing a ref", NULL },
|
||||
{ "no-bindings", 0, 0, G_OPTION_ARG_NONE, &opt_no_bindings, "Do not write any ref bindings", NULL },
|
||||
{ "bind-ref", 0, 0, G_OPTION_ARG_STRING_ARRAY, &opt_bind_refs, "Add a ref to ref binding commit metadata", "BRANCH" },
|
||||
{ "tree", 0, 0, G_OPTION_ARG_STRING_ARRAY, &opt_trees, "Overlay the given argument as a tree", "dir=PATH or tar=TARFILE or ref=COMMIT" },
|
||||
{ "add-metadata-string", 0, 0, G_OPTION_ARG_STRING_ARRAY, &opt_metadata_strings, "Add a key/value pair to metadata", "KEY=VALUE" },
|
||||
{ "add-metadata", 0, 0, G_OPTION_ARG_STRING_ARRAY, &opt_metadata_variants, "Add a key/value pair to metadata, where the KEY is a string, an VALUE is g_variant_parse() formatted", "KEY=VALUE" },
|
||||
{ "add-detached-metadata-string", 0, 0, G_OPTION_ARG_STRING_ARRAY, &opt_detached_metadata_strings, "Add a key/value pair to detached metadata", "KEY=VALUE" },
|
||||
{ "owner-uid", 0, 0, G_OPTION_ARG_INT, &opt_owner_uid, "Set file ownership user id", "UID" },
|
||||
{ "owner-gid", 0, 0, G_OPTION_ARG_INT, &opt_owner_gid, "Set file ownership group id", "GID" },
|
||||
@@ -321,13 +325,11 @@ commit_editor (OstreeRepo *repo,
|
||||
}
|
||||
|
||||
static gboolean
|
||||
parse_keyvalue_strings (char **strings,
|
||||
GVariant **out_metadata,
|
||||
parse_keyvalue_strings (GVariantBuilder *builder,
|
||||
char **strings,
|
||||
gboolean is_gvariant_print,
|
||||
GError **error)
|
||||
{
|
||||
g_autoptr(GVariantBuilder) builder =
|
||||
g_variant_builder_new (G_VARIANT_TYPE ("a{sv}"));
|
||||
|
||||
for (char ** iter = strings; *iter; iter++)
|
||||
{
|
||||
const char *s = *iter;
|
||||
@@ -335,11 +337,19 @@ parse_keyvalue_strings (char **strings,
|
||||
if (!eq)
|
||||
return glnx_throw (error, "Missing '=' in KEY=VALUE metadata '%s'", s);
|
||||
g_autofree char *key = g_strndup (s, eq - s);
|
||||
g_variant_builder_add (builder, "{sv}", key,
|
||||
g_variant_new_string (eq + 1));
|
||||
if (is_gvariant_print)
|
||||
{
|
||||
g_autoptr(GVariant) value = g_variant_parse (NULL, eq + 1, NULL, NULL, error);
|
||||
if (!value)
|
||||
return glnx_prefix_error (error, "Parsing %s", s);
|
||||
|
||||
g_variant_builder_add (builder, "{sv}", key, value);
|
||||
}
|
||||
else
|
||||
g_variant_builder_add (builder, "{sv}", key,
|
||||
g_variant_new_string (eq + 1));
|
||||
}
|
||||
|
||||
*out_metadata = g_variant_ref_sink (g_variant_builder_end (builder));
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@@ -370,13 +380,11 @@ compare_strings (gconstpointer a, gconstpointer b)
|
||||
static void
|
||||
add_ref_binding (GVariantBuilder *metadata_builder)
|
||||
{
|
||||
if (opt_orphan)
|
||||
return;
|
||||
|
||||
g_assert_nonnull (opt_branch);
|
||||
g_assert (opt_branch != NULL || opt_orphan);
|
||||
|
||||
g_autoptr(GPtrArray) refs = g_ptr_array_new ();
|
||||
g_ptr_array_add (refs, opt_branch);
|
||||
if (opt_branch != NULL)
|
||||
g_ptr_array_add (refs, opt_branch);
|
||||
for (char **iter = opt_bind_refs; iter != NULL && *iter != NULL; ++iter)
|
||||
g_ptr_array_add (refs, *iter);
|
||||
g_ptr_array_sort (refs, compare_strings);
|
||||
@@ -458,17 +466,31 @@ ostree_builtin_commit (int argc, char **argv, OstreeCommandInvocation *invocatio
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (opt_metadata_strings)
|
||||
if (opt_metadata_strings || opt_metadata_variants)
|
||||
{
|
||||
if (!parse_keyvalue_strings (opt_metadata_strings,
|
||||
&metadata, error))
|
||||
g_autoptr(GVariantBuilder) builder =
|
||||
g_variant_builder_new (G_VARIANT_TYPE ("a{sv}"));
|
||||
|
||||
if (opt_metadata_strings &&
|
||||
!parse_keyvalue_strings (builder, opt_metadata_strings, FALSE, error))
|
||||
goto out;
|
||||
|
||||
if (opt_metadata_variants &&
|
||||
!parse_keyvalue_strings (builder, opt_metadata_variants, TRUE, error))
|
||||
goto out;
|
||||
|
||||
metadata = g_variant_ref_sink (g_variant_builder_end (builder));
|
||||
}
|
||||
|
||||
if (opt_detached_metadata_strings)
|
||||
{
|
||||
if (!parse_keyvalue_strings (opt_detached_metadata_strings,
|
||||
&detached_metadata, error))
|
||||
g_autoptr(GVariantBuilder) builder =
|
||||
g_variant_builder_new (G_VARIANT_TYPE ("a{sv}"));
|
||||
|
||||
if (!parse_keyvalue_strings (builder, opt_detached_metadata_strings, FALSE, error))
|
||||
goto out;
|
||||
|
||||
detached_metadata = g_variant_ref_sink (g_variant_builder_end (builder));
|
||||
}
|
||||
|
||||
if (!(opt_branch || opt_orphan))
|
||||
@@ -725,9 +747,12 @@ ostree_builtin_commit (int argc, char **argv, OstreeCommandInvocation *invocatio
|
||||
{
|
||||
gboolean update_summary;
|
||||
guint64 timestamp;
|
||||
g_autoptr(GVariant) old_metadata = g_steal_pointer (&metadata);
|
||||
|
||||
fill_bindings (repo, old_metadata, &metadata);
|
||||
if (!opt_no_bindings)
|
||||
{
|
||||
g_autoptr(GVariant) old_metadata = g_steal_pointer (&metadata);
|
||||
fill_bindings (repo, old_metadata, &metadata);
|
||||
}
|
||||
|
||||
if (!opt_timestamp)
|
||||
{
|
||||
|
||||
+149
-115
@@ -30,6 +30,8 @@
|
||||
static gboolean opt_quiet;
|
||||
static gboolean opt_delete;
|
||||
static gboolean opt_add_tombstones;
|
||||
static gboolean opt_verify_bindings;
|
||||
static gboolean opt_verify_back_refs;
|
||||
|
||||
/* ATTENTION:
|
||||
* Please remember to update the bash-completion script (bash/ostree) and
|
||||
@@ -40,122 +42,42 @@ static GOptionEntry options[] = {
|
||||
{ "add-tombstones", 0, 0, G_OPTION_ARG_NONE, &opt_add_tombstones, "Add tombstones for missing commits", NULL },
|
||||
{ "quiet", 'q', 0, G_OPTION_ARG_NONE, &opt_quiet, "Only print error messages", NULL },
|
||||
{ "delete", 0, 0, G_OPTION_ARG_NONE, &opt_delete, "Remove corrupted objects", NULL },
|
||||
{ "verify-bindings", 0, 0, G_OPTION_ARG_NONE, &opt_verify_bindings, "Verify ref bindings", NULL },
|
||||
{ "verify-back-refs", 0, 0, G_OPTION_ARG_NONE, &opt_verify_back_refs, "Verify back-references (implies --verify-bindings)", NULL },
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
static gboolean
|
||||
load_and_fsck_one_object (OstreeRepo *repo,
|
||||
const char *checksum,
|
||||
OstreeObjectType objtype,
|
||||
gboolean *out_found_corruption,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
fsck_one_object (OstreeRepo *repo,
|
||||
const char *checksum,
|
||||
OstreeObjectType objtype,
|
||||
gboolean *out_found_corruption,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
gboolean missing = FALSE;
|
||||
g_autoptr(GVariant) metadata = NULL;
|
||||
g_autoptr(GInputStream) input = NULL;
|
||||
g_autoptr(GFileInfo) file_info = NULL;
|
||||
g_autoptr(GVariant) xattrs = NULL;
|
||||
g_autoptr(GError) temp_error = NULL;
|
||||
|
||||
if (OSTREE_OBJECT_TYPE_IS_META (objtype))
|
||||
if (!ostree_repo_fsck_object (repo, objtype, checksum, cancellable, &temp_error))
|
||||
{
|
||||
if (!ostree_repo_load_variant (repo, objtype,
|
||||
checksum, &metadata, &temp_error))
|
||||
if (g_error_matches (temp_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
|
||||
{
|
||||
if (g_error_matches (temp_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
|
||||
{
|
||||
g_clear_error (&temp_error);
|
||||
g_printerr ("Object missing: %s.%s\n", checksum,
|
||||
ostree_object_type_to_string (objtype));
|
||||
missing = TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
g_propagate_error (error, g_steal_pointer (&temp_error));
|
||||
return glnx_prefix_error (error, "Loading metadata object %s", checksum);
|
||||
}
|
||||
g_clear_error (&temp_error);
|
||||
g_printerr ("Object missing: %s.%s\n", checksum,
|
||||
ostree_object_type_to_string (objtype));
|
||||
*out_found_corruption = TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (objtype == OSTREE_OBJECT_TYPE_COMMIT)
|
||||
{
|
||||
if (!ostree_validate_structureof_commit (metadata, error))
|
||||
return glnx_prefix_error (error, "While validating commit metadata '%s'", checksum);
|
||||
}
|
||||
else if (objtype == OSTREE_OBJECT_TYPE_DIR_TREE)
|
||||
{
|
||||
if (!ostree_validate_structureof_dirtree (metadata, error))
|
||||
return glnx_prefix_error (error, "While validating directory tree '%s'", checksum);
|
||||
}
|
||||
else if (objtype == OSTREE_OBJECT_TYPE_DIR_META)
|
||||
{
|
||||
if (!ostree_validate_structureof_dirmeta (metadata, error))
|
||||
return glnx_prefix_error (error, "While validating directory metadata '%s'", checksum);
|
||||
}
|
||||
|
||||
input = g_memory_input_stream_new_from_data (g_variant_get_data (metadata),
|
||||
g_variant_get_size (metadata),
|
||||
NULL);
|
||||
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
guint32 mode;
|
||||
g_assert (objtype == OSTREE_OBJECT_TYPE_FILE);
|
||||
if (!ostree_repo_load_file (repo, checksum, &input, &file_info,
|
||||
&xattrs, cancellable, &temp_error))
|
||||
{
|
||||
if (g_error_matches (temp_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
|
||||
{
|
||||
g_clear_error (&temp_error);
|
||||
g_printerr ("Object missing: %s.%s\n", checksum,
|
||||
ostree_object_type_to_string (objtype));
|
||||
missing = TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
g_propagate_error (error, g_steal_pointer (&temp_error));
|
||||
return glnx_prefix_error (error, "Loading file object %s", checksum);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
mode = g_file_info_get_attribute_uint32 (file_info, "unix::mode");
|
||||
if (!ostree_validate_structureof_file_mode (mode, error))
|
||||
return glnx_prefix_error (error, "While validating file '%s'", checksum);
|
||||
}
|
||||
}
|
||||
|
||||
if (missing)
|
||||
{
|
||||
*out_found_corruption = TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
g_autofree guchar *computed_csum = NULL;
|
||||
g_autofree char *tmp_checksum = NULL;
|
||||
|
||||
if (!ostree_checksum_file_from_input (file_info, xattrs, input,
|
||||
objtype, &computed_csum,
|
||||
cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
tmp_checksum = ostree_checksum_from_bytes (computed_csum);
|
||||
if (strcmp (checksum, tmp_checksum) != 0)
|
||||
{
|
||||
g_autofree char *msg = g_strdup_printf ("corrupted object %s.%s; actual checksum: %s",
|
||||
checksum, ostree_object_type_to_string (objtype),
|
||||
tmp_checksum);
|
||||
if (opt_delete)
|
||||
{
|
||||
g_printerr ("%s\n", msg);
|
||||
g_printerr ("%s\n", temp_error->message);
|
||||
(void) ostree_repo_delete_object (repo, objtype, checksum, cancellable, NULL);
|
||||
*out_found_corruption = TRUE;
|
||||
}
|
||||
else
|
||||
return glnx_throw (error, "%s", msg);
|
||||
{
|
||||
g_propagate_error (error, g_steal_pointer (&temp_error));
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -189,8 +111,10 @@ fsck_reachable_objects_from_commits (OstreeRepo *repo,
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
g_auto(GLnxConsoleRef) console = { 0, };
|
||||
glnx_console_lock (&console);
|
||||
|
||||
const guint count = g_hash_table_size (reachable_objects);
|
||||
const guint mod = count / 10;
|
||||
guint i = 0;
|
||||
g_hash_table_iter_init (&hash_iter, reachable_objects);
|
||||
while (g_hash_table_iter_next (&hash_iter, &key, &value))
|
||||
@@ -201,13 +125,50 @@ fsck_reachable_objects_from_commits (OstreeRepo *repo,
|
||||
|
||||
ostree_object_name_deserialize (serialized_key, &checksum, &objtype);
|
||||
|
||||
if (!load_and_fsck_one_object (repo, checksum, objtype, out_found_corruption,
|
||||
cancellable, error))
|
||||
if (!fsck_one_object (repo, checksum, objtype, out_found_corruption,
|
||||
cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
if (mod == 0 || (i % mod == 0))
|
||||
g_print ("%u/%u objects\n", i + 1, count);
|
||||
i++;
|
||||
glnx_console_progress_n_items ("fsck objects", i, count);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* Check that a given commit object is valid for the ref it was looked up via.
|
||||
* @collection_id will be %NULL for normal refs, and non-%NULL for collection–refs. */
|
||||
static gboolean
|
||||
fsck_commit_for_ref (OstreeRepo *repo,
|
||||
const char *checksum,
|
||||
const char *collection_id,
|
||||
const char *ref_name,
|
||||
gboolean *found_corruption,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
if (!fsck_one_object (repo, checksum, OSTREE_OBJECT_TYPE_COMMIT,
|
||||
found_corruption,
|
||||
cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
/* Check the commit exists. */
|
||||
g_autoptr(GVariant) commit = NULL;
|
||||
if (!ostree_repo_load_variant (repo, OSTREE_OBJECT_TYPE_COMMIT,
|
||||
checksum, &commit, error))
|
||||
{
|
||||
if (collection_id != NULL)
|
||||
return glnx_prefix_error (error, "Loading commit for ref (%s, %s)",
|
||||
collection_id, ref_name);
|
||||
else
|
||||
return glnx_prefix_error (error, "Loading commit for ref %s", ref_name);
|
||||
}
|
||||
|
||||
/* Check its bindings. */
|
||||
if (opt_verify_bindings)
|
||||
{
|
||||
if (!ostree_cmd__private__ ()->ostree_repo_verify_bindings (collection_id, ref_name, commit, error))
|
||||
return glnx_prefix_error (error, "Commit %s", checksum);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
@@ -237,12 +198,14 @@ ostree_builtin_fsck (int argc, char **argv, OstreeCommandInvocation *invocation,
|
||||
g_hash_table_iter_init (&hash_iter, all_refs);
|
||||
while (g_hash_table_iter_next (&hash_iter, &key, &value))
|
||||
{
|
||||
const char *refname = key;
|
||||
const char *refspec = key;
|
||||
const char *checksum = value;
|
||||
g_autoptr(GVariant) commit = NULL;
|
||||
if (!ostree_repo_load_variant (repo, OSTREE_OBJECT_TYPE_COMMIT,
|
||||
checksum, &commit, error))
|
||||
return glnx_prefix_error (error, "Loading commit for ref %s", refname);
|
||||
g_autofree char *ref_name = NULL;
|
||||
if (!ostree_parse_refspec (refspec, NULL, &ref_name, error))
|
||||
return FALSE;
|
||||
if (!fsck_commit_for_ref (repo, checksum, NULL, ref_name,
|
||||
&found_corruption, cancellable, error))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
#ifdef OSTREE_ENABLE_EXPERIMENTAL_API
|
||||
@@ -259,12 +222,9 @@ ostree_builtin_fsck (int argc, char **argv, OstreeCommandInvocation *invocation,
|
||||
while (g_hash_table_iter_next (&hash_iter, &key, &value))
|
||||
{
|
||||
const OstreeCollectionRef *ref = key;
|
||||
const char *checksum = value;
|
||||
g_autoptr(GVariant) commit = NULL;
|
||||
if (!ostree_repo_load_variant (repo, OSTREE_OBJECT_TYPE_COMMIT,
|
||||
checksum, &commit, error))
|
||||
return glnx_prefix_error (error, "Loading commit for ref (%s, %s)",
|
||||
ref->collection_id, ref->ref_name);
|
||||
if (!fsck_commit_for_ref (repo, value, ref->collection_id, ref->ref_name,
|
||||
&found_corruption, cancellable, error))
|
||||
return FALSE;
|
||||
}
|
||||
#endif /* OSTREE_ENABLE_EXPERIMENTAL_API */
|
||||
|
||||
@@ -284,6 +244,9 @@ ostree_builtin_fsck (int argc, char **argv, OstreeCommandInvocation *invocation,
|
||||
if (opt_add_tombstones)
|
||||
tombstones = g_ptr_array_new_with_free_func (g_free);
|
||||
|
||||
if (opt_verify_back_refs)
|
||||
opt_verify_bindings = TRUE;
|
||||
|
||||
guint n_partial = 0;
|
||||
g_hash_table_iter_init (&hash_iter, objects);
|
||||
while (g_hash_table_iter_next (&hash_iter, &key, &value))
|
||||
@@ -301,6 +264,77 @@ ostree_builtin_fsck (int argc, char **argv, OstreeCommandInvocation *invocation,
|
||||
if (!ostree_repo_load_commit (repo, checksum, &commit, &commitstate, error))
|
||||
return FALSE;
|
||||
|
||||
/* If requested, check that all the refs listed in the ref-bindings
|
||||
* for this commit resolve back to this commit. */
|
||||
if (opt_verify_back_refs)
|
||||
{
|
||||
g_autoptr(GVariant) metadata = g_variant_get_child_value (commit, 0);
|
||||
|
||||
const char *collection_id = NULL;
|
||||
#ifdef OSTREE_ENABLE_EXPERIMENTAL_API
|
||||
if (!g_variant_lookup (metadata,
|
||||
OSTREE_COMMIT_META_KEY_COLLECTION_BINDING,
|
||||
"&s",
|
||||
&collection_id))
|
||||
collection_id = NULL;
|
||||
#endif /* OSTREE_ENABLE_EXPERIMENTAL_API */
|
||||
|
||||
g_autofree const char **refs = NULL;
|
||||
if (g_variant_lookup (metadata,
|
||||
OSTREE_COMMIT_META_KEY_REF_BINDING,
|
||||
"^a&s",
|
||||
&refs))
|
||||
{
|
||||
for (const char **iter = refs; *iter != NULL; ++iter)
|
||||
{
|
||||
g_autofree char *checksum_for_ref = NULL;
|
||||
|
||||
#ifdef OSTREE_ENABLE_EXPERIMENTAL_API
|
||||
if (collection_id != NULL)
|
||||
{
|
||||
const OstreeCollectionRef collection_ref = { (char *) collection_id, (char *) *iter };
|
||||
if (!ostree_repo_resolve_collection_ref (repo, &collection_ref,
|
||||
TRUE,
|
||||
OSTREE_REPO_RESOLVE_REV_EXT_NONE,
|
||||
&checksum_for_ref,
|
||||
cancellable,
|
||||
error))
|
||||
return FALSE;
|
||||
}
|
||||
else
|
||||
#endif /* OSTREE_ENABLE_EXPERIMENTAL_API */
|
||||
{
|
||||
if (!ostree_repo_resolve_rev (repo, *iter, TRUE,
|
||||
&checksum_for_ref, error))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (checksum_for_ref == NULL)
|
||||
{
|
||||
if (collection_id != NULL)
|
||||
return glnx_throw (error,
|
||||
"Collection–ref (%s, %s) in bindings for commit %s does not exist",
|
||||
collection_id, *iter, checksum);
|
||||
else
|
||||
return glnx_throw (error,
|
||||
"Ref ‘%s’ in bindings for commit %s does not exist",
|
||||
*iter, checksum);
|
||||
}
|
||||
else if (g_strcmp0 (checksum_for_ref, checksum) != 0)
|
||||
{
|
||||
if (collection_id != NULL)
|
||||
return glnx_throw (error,
|
||||
"Collection–ref (%s, %s) in bindings for commit %s does not resolve to that commit",
|
||||
collection_id, *iter, checksum);
|
||||
else
|
||||
return glnx_throw (error,
|
||||
"Ref ‘%s’ in bindings for commit %s does not resolve to that commit",
|
||||
*iter, checksum);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (opt_add_tombstones)
|
||||
{
|
||||
GError *local_error = NULL;
|
||||
|
||||
@@ -223,6 +223,8 @@ static gboolean do_ref (OstreeRepo *repo, const char *refspec_prefix, GCancellab
|
||||
|
||||
if (opt_alias)
|
||||
{
|
||||
if (remote)
|
||||
return glnx_throw (error, "Cannot create alias to remote ref: %s", remote);
|
||||
if (!ostree_repo_set_alias_ref_immediate (repo, remote, ref, refspec_prefix,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
|
||||
@@ -32,6 +32,7 @@ static char* opt_print_variant_type;
|
||||
static char* opt_print_metadata_key;
|
||||
static char* opt_print_detached_metadata_key;
|
||||
static gboolean opt_raw;
|
||||
static gboolean opt_no_byteswap;
|
||||
static char *opt_gpg_homedir;
|
||||
static char *opt_gpg_verify_remote;
|
||||
|
||||
@@ -46,6 +47,7 @@ static GOptionEntry options[] = {
|
||||
{ "print-metadata-key", 0, 0, G_OPTION_ARG_STRING, &opt_print_metadata_key, "Print string value of metadata key", "KEY" },
|
||||
{ "print-detached-metadata-key", 0, 0, G_OPTION_ARG_STRING, &opt_print_detached_metadata_key, "Print string value of detached metadata key", "KEY" },
|
||||
{ "raw", 0, 0, G_OPTION_ARG_NONE, &opt_raw, "Show raw variant data" },
|
||||
{ "no-byteswap", 'B', 0, G_OPTION_ARG_NONE, &opt_no_byteswap, "Do not automatically convert variant data from big endian" },
|
||||
{ "gpg-homedir", 0, 0, G_OPTION_ARG_FILENAME, &opt_gpg_homedir, "GPG Homedir to use when looking for keyrings", "HOMEDIR"},
|
||||
{ "gpg-verify-remote", 0, 0, G_OPTION_ARG_STRING, &opt_gpg_verify_remote, "Use REMOTE name for GPG configuration", "REMOTE"},
|
||||
{ NULL }
|
||||
@@ -132,7 +134,13 @@ do_print_metadata_key (OstreeRepo *repo,
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
ot_dump_variant (value);
|
||||
if (opt_no_byteswap)
|
||||
{
|
||||
g_autofree char *formatted = g_variant_print (value, TRUE);
|
||||
g_print ("%s\n", formatted);
|
||||
}
|
||||
else
|
||||
ot_dump_variant (value);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@@ -150,6 +158,8 @@ print_object (OstreeRepo *repo,
|
||||
return FALSE;
|
||||
if (opt_raw)
|
||||
flags |= OSTREE_DUMP_RAW;
|
||||
if (opt_no_byteswap)
|
||||
flags |= OSTREE_DUMP_UNSWAPPED;
|
||||
ot_dump_object (objtype, checksum, variant, flags);
|
||||
|
||||
if (objtype == OSTREE_OBJECT_TYPE_COMMIT)
|
||||
|
||||
@@ -157,7 +157,12 @@ ot_dump_object (OstreeObjectType objtype,
|
||||
{
|
||||
g_print ("%s %s\n", ostree_object_type_to_string (objtype), checksum);
|
||||
|
||||
if (flags & OSTREE_DUMP_RAW)
|
||||
if (flags & OSTREE_DUMP_UNSWAPPED)
|
||||
{
|
||||
g_autofree char *formatted = g_variant_print (variant, TRUE);
|
||||
g_print ("%s\n", formatted);
|
||||
}
|
||||
else if (flags & OSTREE_DUMP_RAW)
|
||||
{
|
||||
ot_dump_variant (variant);
|
||||
return;
|
||||
|
||||
@@ -26,8 +26,9 @@
|
||||
#include "ostree-core.h"
|
||||
|
||||
typedef enum {
|
||||
OSTREE_DUMP_NONE = 0,
|
||||
OSTREE_DUMP_RAW = 1,
|
||||
OSTREE_DUMP_NONE = (1 << 0),
|
||||
OSTREE_DUMP_RAW = (1 << 1),
|
||||
OSTREE_DUMP_UNSWAPPED = (1 << 2),
|
||||
} OstreeDumpFlags;
|
||||
|
||||
void ot_dump_variant (GVariant *variant);
|
||||
|
||||
Reference in new issue
Block a user