New upstream version 2017.8
This commit is contained in:
commit
b6c0af922c
158 files changed
+12692
-2420
No files matched your search
@@ -408,6 +408,11 @@ global:
|
||||
ostree_sysroot_query_deployments_for;
|
||||
} LIBOSTREE_2017.6;
|
||||
|
||||
LIBOSTREE_2017.8 {
|
||||
global:
|
||||
ostree_validate_remote_name;
|
||||
} LIBOSTREE_2017.7;
|
||||
|
||||
/* NOTE: Only add more content here in release commits! See the
|
||||
* comments at the top of this file.
|
||||
*/
|
||||
@@ -60,7 +60,15 @@ 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 (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)
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC (OstreeRepoFinder, g_object_unref)
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC (OstreeRepoFinderAvahi, g_object_unref)
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC (OstreeRepoFinderConfig, g_object_unref)
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC (OstreeRepoFinderMount, g_object_unref)
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC (OstreeRepoFinderResult, ostree_repo_finder_result_free)
|
||||
G_DEFINE_AUTO_CLEANUP_FREE_FUNC (OstreeRepoFinderResultv, ostree_repo_finder_result_freev, NULL)
|
||||
#endif /* OSTREE_ENABLE_EXPERIMENTAL_API */
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Authors:
|
||||
* - Philip Withnall <withnall@endlessm.com>
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <gio/gio.h>
|
||||
#include <glib.h>
|
||||
#include <glib-object.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
/**
|
||||
* OstreeBloom:
|
||||
*
|
||||
* An implementation of a [bloom filter](https://en.wikipedia.org/wiki/Bloom_filter)
|
||||
* which is suitable for building a filter and looking keys up in an existing
|
||||
* filter.
|
||||
*
|
||||
* Since: 2017.8
|
||||
*/
|
||||
typedef struct _OstreeBloom OstreeBloom;
|
||||
|
||||
/**
|
||||
* OstreeBloomHashFunc:
|
||||
* @element: a pointer to the element to hash
|
||||
* @k: hash function parameter
|
||||
*
|
||||
* Function prototype for a
|
||||
* [universal hash function](https://en.wikipedia.org/wiki/Universal_hashing),
|
||||
* parameterised on @k, which hashes @element to a #guint64 hash value.
|
||||
*
|
||||
* It is up to the implementer of the hash function whether %NULL is valid for
|
||||
* @element.
|
||||
*
|
||||
* Since: 2017.8
|
||||
*/
|
||||
typedef guint64 (*OstreeBloomHashFunc) (gconstpointer element,
|
||||
guint8 k);
|
||||
|
||||
#define OSTREE_TYPE_BLOOM (ostree_bloom_get_type ())
|
||||
|
||||
G_GNUC_INTERNAL
|
||||
GType ostree_bloom_get_type (void);
|
||||
|
||||
G_GNUC_INTERNAL
|
||||
OstreeBloom *ostree_bloom_new (gsize n_bytes,
|
||||
guint8 k,
|
||||
OstreeBloomHashFunc hash_func);
|
||||
|
||||
G_GNUC_INTERNAL
|
||||
OstreeBloom *ostree_bloom_new_from_bytes (GBytes *bytes,
|
||||
guint8 k,
|
||||
OstreeBloomHashFunc hash_func);
|
||||
|
||||
G_GNUC_INTERNAL
|
||||
OstreeBloom *ostree_bloom_ref (OstreeBloom *bloom);
|
||||
G_GNUC_INTERNAL
|
||||
void ostree_bloom_unref (OstreeBloom *bloom);
|
||||
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC (OstreeBloom, ostree_bloom_unref)
|
||||
|
||||
G_GNUC_INTERNAL
|
||||
gboolean ostree_bloom_maybe_contains (OstreeBloom *bloom,
|
||||
gconstpointer element);
|
||||
|
||||
G_GNUC_INTERNAL
|
||||
GBytes *ostree_bloom_seal (OstreeBloom *bloom);
|
||||
|
||||
G_GNUC_INTERNAL
|
||||
void ostree_bloom_add_element (OstreeBloom *bloom,
|
||||
gconstpointer element);
|
||||
|
||||
G_GNUC_INTERNAL
|
||||
gsize ostree_bloom_get_size (OstreeBloom *bloom);
|
||||
G_GNUC_INTERNAL
|
||||
guint8 ostree_bloom_get_k (OstreeBloom *bloom);
|
||||
G_GNUC_INTERNAL
|
||||
OstreeBloomHashFunc ostree_bloom_get_hash_func (OstreeBloom *bloom);
|
||||
|
||||
G_GNUC_INTERNAL
|
||||
guint64 ostree_str_bloom_hash (gconstpointer element,
|
||||
guint8 k);
|
||||
|
||||
G_END_DECLS
|
||||
@@ -0,0 +1,603 @@
|
||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Authors:
|
||||
* - Philip Withnall <withnall@endlessm.com>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <gio/gio.h>
|
||||
#include <glib.h>
|
||||
#include <glib-object.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "ostree-bloom-private.h"
|
||||
|
||||
/**
|
||||
* SECTION:bloom
|
||||
* @title: Bloom filter
|
||||
* @short_description: Bloom filter implementation supporting building and
|
||||
* reading filters
|
||||
* @stability: Unstable
|
||||
* @include: libostree/ostree-bloom-private.h
|
||||
*
|
||||
* #OstreeBloom is an implementation of a bloom filter which supports writing to
|
||||
* and loading from a #GBytes bit array. The caller must store metadata about
|
||||
* the bloom filter (its hash function and `k` parameter value) separately, as
|
||||
* the same values must be used when reading from a serialised bit array as were
|
||||
* used to build the array in the first place.
|
||||
*
|
||||
* This is a standard implementation of a bloom filter, and background reading
|
||||
* on the theory can be
|
||||
* [found on Wikipedia](https://en.wikipedia.org/wiki/Bloom_filter). In
|
||||
* particular, a bloom filter is parameterised by `m` and `k` parameters: the
|
||||
* size of the bit array (in bits) is `m`, and the number of hash functions
|
||||
* applied to each element is `k`. Bloom filters require a universal hash
|
||||
* function which can be parameterised by `k`. We have #OstreeBloomHashFunc,
|
||||
* with ostree_str_bloom_hash() being an implementation for strings.
|
||||
*
|
||||
* The serialised output from a bloom filter is guaranteed to be stable across
|
||||
* versions of libostree as long as the same values for `k` and the hash
|
||||
* function are used.
|
||||
*
|
||||
* #OstreeBloom is mutable when constructed with ostree_bloom_new(), and elements
|
||||
* can be added to it using ostree_bloom_add_element(), until ostree_bloom_seal()
|
||||
* is called to serialise it and make it immutable. After then, the bloom filter
|
||||
* can only be queried using ostree_bloom_maybe_contains().
|
||||
*
|
||||
* If constructed with ostree_bloom_new_from_bytes(), the bloom filter is
|
||||
* immutable from construction, and can only be queried.
|
||||
*
|
||||
* Reference:
|
||||
* - https://en.wikipedia.org/wiki/Bloom_filter
|
||||
* - https://llimllib.github.io/bloomfilter-tutorial/
|
||||
*
|
||||
* Since: 2017.8
|
||||
*/
|
||||
|
||||
struct _OstreeBloom
|
||||
{
|
||||
guint ref_count;
|
||||
gsize n_bytes;
|
||||
gboolean is_mutable; /* determines which of [im]mutable_bytes is accessed */
|
||||
union
|
||||
{
|
||||
guint8 *mutable_bytes; /* owned; mutually exclusive */
|
||||
GBytes *immutable_bytes; /* owned; mutually exclusive */
|
||||
};
|
||||
guint8 k;
|
||||
OstreeBloomHashFunc hash_func;
|
||||
};
|
||||
|
||||
G_DEFINE_BOXED_TYPE (OstreeBloom, ostree_bloom, ostree_bloom_ref, ostree_bloom_unref)
|
||||
|
||||
/**
|
||||
* ostree_bloom_new:
|
||||
* @n_bytes: size to make the bloom filter, in bytes
|
||||
* @k: number of hash functions to use
|
||||
* @hash_func: universal hash function to use
|
||||
*
|
||||
* Create a new mutable #OstreeBloom filter, with all its bits initialised to
|
||||
* zero. Set elements in the filter using ostree_bloom_add_element(), and seal
|
||||
* it to return an immutable #GBytes using ostree_bloom_seal().
|
||||
*
|
||||
* To load an #OstreeBloom from an existing #GBytes, use
|
||||
* ostree_bloom_new_from_bytes().
|
||||
*
|
||||
* Note that @n_bytes is in bytes, so is 8 times smaller than the parameter `m`
|
||||
* which is used when describing bloom filters academically.
|
||||
*
|
||||
* Returns: (transfer full): a new mutable bloom filter
|
||||
*
|
||||
* Since: 2017.8
|
||||
*/
|
||||
OstreeBloom *
|
||||
ostree_bloom_new (gsize n_bytes,
|
||||
guint8 k,
|
||||
OstreeBloomHashFunc hash_func)
|
||||
{
|
||||
g_autoptr(OstreeBloom) bloom = NULL;
|
||||
|
||||
g_return_val_if_fail (n_bytes > 0, NULL);
|
||||
g_return_val_if_fail (k > 0, NULL);
|
||||
g_return_val_if_fail (hash_func != NULL, NULL);
|
||||
|
||||
bloom = g_new0 (OstreeBloom, 1);
|
||||
bloom->ref_count = 1;
|
||||
|
||||
bloom->is_mutable = TRUE;
|
||||
bloom->mutable_bytes = g_malloc0 (n_bytes);
|
||||
bloom->n_bytes = n_bytes;
|
||||
bloom->k = k;
|
||||
bloom->hash_func = hash_func;
|
||||
|
||||
return g_steal_pointer (&bloom);
|
||||
}
|
||||
|
||||
/**
|
||||
* ostree_bloom_new_from_bytes:
|
||||
* @bytes: array of bytes containing the filter data
|
||||
* @k: number of hash functions to use
|
||||
* @hash_func: universal hash function to use
|
||||
*
|
||||
* Load an immutable #OstreeBloom filter from the given @bytes. Check whether
|
||||
* elements are probably set in the filter using ostree_bloom_maybe_contains().
|
||||
*
|
||||
* To create a new mutable #OstreeBloom, use ostree_bloom_new().
|
||||
*
|
||||
* Note that all the bits in @bytes are loaded, so the parameter `m` for the
|
||||
* filter (as commonly used in academic literature) is always a multiple of 8.
|
||||
*
|
||||
* Returns: (transfer full): a new immutable bloom filter
|
||||
*
|
||||
* Since: 2017.8
|
||||
*/
|
||||
OstreeBloom *
|
||||
ostree_bloom_new_from_bytes (GBytes *bytes,
|
||||
guint8 k,
|
||||
OstreeBloomHashFunc hash_func)
|
||||
{
|
||||
g_autoptr(OstreeBloom) bloom = NULL;
|
||||
|
||||
g_return_val_if_fail (bytes != NULL, NULL);
|
||||
g_return_val_if_fail (g_bytes_get_size (bytes) > 0, NULL);
|
||||
g_return_val_if_fail (k > 0, NULL);
|
||||
g_return_val_if_fail (hash_func != NULL, NULL);
|
||||
|
||||
bloom = g_new0 (OstreeBloom, 1);
|
||||
bloom->ref_count = 1;
|
||||
|
||||
bloom->is_mutable = FALSE;
|
||||
bloom->immutable_bytes = g_bytes_ref (bytes);
|
||||
bloom->n_bytes = g_bytes_get_size (bytes);
|
||||
bloom->k = k;
|
||||
bloom->hash_func = hash_func;
|
||||
|
||||
return g_steal_pointer (&bloom);
|
||||
}
|
||||
|
||||
/**
|
||||
* ostree_bloom_ref:
|
||||
* @bloom: an #OstreeBloom
|
||||
*
|
||||
* Increase the reference count of @bloom.
|
||||
*
|
||||
* Returns: (transfer full): @bloom
|
||||
* Since: 2017.8
|
||||
*/
|
||||
OstreeBloom *
|
||||
ostree_bloom_ref (OstreeBloom *bloom)
|
||||
{
|
||||
g_return_val_if_fail (bloom != NULL, NULL);
|
||||
g_return_val_if_fail (bloom->ref_count >= 1, NULL);
|
||||
g_return_val_if_fail (bloom->ref_count == G_MAXUINT - 1, NULL);
|
||||
|
||||
bloom->ref_count++;
|
||||
|
||||
return bloom;
|
||||
}
|
||||
|
||||
/**
|
||||
* ostree_bloom_unref:
|
||||
* @bloom: (transfer full): an #OstreeBloom
|
||||
*
|
||||
* Decrement the reference count of @bloom. If it reaches zero, the filter
|
||||
* is destroyed.
|
||||
*
|
||||
* Since: 2017.8
|
||||
*/
|
||||
void
|
||||
ostree_bloom_unref (OstreeBloom *bloom)
|
||||
{
|
||||
g_return_if_fail (bloom != NULL);
|
||||
g_return_if_fail (bloom->ref_count >= 1);
|
||||
|
||||
bloom->ref_count--;
|
||||
|
||||
if (bloom->ref_count == 0)
|
||||
{
|
||||
if (bloom->is_mutable)
|
||||
g_clear_pointer (&bloom->mutable_bytes, g_free);
|
||||
else
|
||||
g_clear_pointer (&bloom->immutable_bytes, g_bytes_unref);
|
||||
bloom->n_bytes = 0;
|
||||
g_free (bloom);
|
||||
}
|
||||
}
|
||||
|
||||
/* @idx is in bits, not bytes. */
|
||||
static inline gboolean
|
||||
ostree_bloom_get_bit (OstreeBloom *bloom,
|
||||
gsize idx)
|
||||
{
|
||||
const guint8 *bytes;
|
||||
|
||||
if (bloom->is_mutable)
|
||||
bytes = bloom->mutable_bytes;
|
||||
else
|
||||
bytes = g_bytes_get_data (bloom->immutable_bytes, NULL);
|
||||
|
||||
g_assert (idx / 8 < bloom->n_bytes);
|
||||
return (bytes[idx / 8] & (1 << (idx % 8)));
|
||||
}
|
||||
|
||||
/* @idx is in bits, not bytes. */
|
||||
static inline void
|
||||
ostree_bloom_set_bit (OstreeBloom *bloom,
|
||||
gsize idx)
|
||||
{
|
||||
g_assert (bloom->is_mutable);
|
||||
g_assert (idx / 8 < bloom->n_bytes);
|
||||
bloom->mutable_bytes[idx / 8] |= (1 << (idx % 8));
|
||||
}
|
||||
|
||||
/**
|
||||
* ostree_bloom_maybe_contains:
|
||||
* @bloom: an #OstreeBloom
|
||||
* @element: (nullable): element to check for membership
|
||||
*
|
||||
* Check whether @element is potentially in @bloom, or whether it definitely
|
||||
* isn’t. @element may be %NULL only if the hash function passed to @bloom at
|
||||
* construction time supports %NULL elements.
|
||||
*
|
||||
* Returns: %TRUE if @element is potentially in @bloom; %FALSE if it definitely
|
||||
* isn’t
|
||||
* Since: 2017.8
|
||||
*/
|
||||
gboolean
|
||||
ostree_bloom_maybe_contains (OstreeBloom *bloom,
|
||||
gconstpointer element)
|
||||
{
|
||||
guint8 i;
|
||||
|
||||
g_return_val_if_fail (bloom != NULL, TRUE);
|
||||
g_return_val_if_fail (bloom->ref_count >= 1, TRUE);
|
||||
|
||||
for (i = 0; i < bloom->k; i++)
|
||||
{
|
||||
gsize idx;
|
||||
|
||||
idx = bloom->hash_func (element, i);
|
||||
|
||||
if (!ostree_bloom_get_bit (bloom, idx % (bloom->n_bytes * 8)))
|
||||
return FALSE; /* definitely not in the set */
|
||||
}
|
||||
|
||||
return TRUE; /* possibly in the set */
|
||||
}
|
||||
|
||||
/**
|
||||
* ostree_bloom_seal:
|
||||
* @bloom: an #OstreeBloom
|
||||
*
|
||||
* Seal a constructed bloom filter, so that elements may no longer be added to
|
||||
* it, and queries can now be performed against it. The serialised form of the
|
||||
* bloom filter is returned as a bit array. Note that this does not include
|
||||
* information about the filter hash function or parameters; the caller is
|
||||
* responsible for serialising those separately if appropriate.
|
||||
*
|
||||
* It is safe to call this function multiple times.
|
||||
*
|
||||
* Returns: (transfer full): a #GBytes containing the immutable filter data
|
||||
* Since: 2017.8
|
||||
*/
|
||||
GBytes *
|
||||
ostree_bloom_seal (OstreeBloom *bloom)
|
||||
{
|
||||
g_return_val_if_fail (bloom != NULL, NULL);
|
||||
g_return_val_if_fail (bloom->ref_count >= 1, NULL);
|
||||
|
||||
if (bloom->is_mutable)
|
||||
{
|
||||
bloom->is_mutable = FALSE;
|
||||
bloom->immutable_bytes = g_bytes_new_take (g_steal_pointer (&bloom->mutable_bytes), bloom->n_bytes);
|
||||
}
|
||||
|
||||
return g_bytes_ref (bloom->immutable_bytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* ostree_bloom_add_element:
|
||||
* @bloom: an #OstreeBloom
|
||||
* @element: (nullable): element to add to the filter
|
||||
*
|
||||
* Add the given @element to the bloom filter, which must not yet have been
|
||||
* sealed (ostree_bloom_seal()). @element may be %NULL if the hash function
|
||||
* passed to @bloom at construction time supports %NULL elements.
|
||||
*
|
||||
* Since: 2017.8
|
||||
*/
|
||||
void
|
||||
ostree_bloom_add_element (OstreeBloom *bloom,
|
||||
gconstpointer element)
|
||||
{
|
||||
guint8 i;
|
||||
|
||||
g_return_if_fail (bloom != NULL);
|
||||
g_return_if_fail (bloom->ref_count >= 1);
|
||||
g_return_if_fail (bloom->is_mutable);
|
||||
|
||||
for (i = 0; i < bloom->k; i++)
|
||||
{
|
||||
gsize idx = bloom->hash_func (element, i);
|
||||
ostree_bloom_set_bit (bloom, idx % (bloom->n_bytes * 8));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ostree_bloom_get_size:
|
||||
* @bloom: an #OstreeBloom
|
||||
*
|
||||
* Get the size of the #OstreeBloom filter, in bytes, as configured at
|
||||
* construction time.
|
||||
*
|
||||
* Returns: the bloom filter’s size in bytes, guaranteed to be >0
|
||||
* Since: 2017.8
|
||||
*/
|
||||
gsize
|
||||
ostree_bloom_get_size (OstreeBloom *bloom)
|
||||
{
|
||||
g_return_val_if_fail (bloom != NULL, 0);
|
||||
|
||||
return bloom->n_bytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* ostree_bloom_get_k:
|
||||
* @bloom: an #OstreeBloom
|
||||
*
|
||||
* Get the `k` value from the #OstreeBloom filter, as configured at
|
||||
* construction time.
|
||||
*
|
||||
* Returns: the bloom filter’s `k` value, guaranteed to be >0
|
||||
* Since: 2017.8
|
||||
*/
|
||||
guint8
|
||||
ostree_bloom_get_k (OstreeBloom *bloom)
|
||||
{
|
||||
g_return_val_if_fail (bloom != NULL, 0);
|
||||
|
||||
return bloom->k;
|
||||
}
|
||||
|
||||
/**
|
||||
* ostree_bloom_get_hash_func:
|
||||
* @bloom: an #OstreeBloom
|
||||
*
|
||||
* Get the #OstreeBloomHashFunc from the #OstreeBloom filter, as configured at
|
||||
* construction time.
|
||||
*
|
||||
* Returns: the bloom filter’s universal hash function
|
||||
* Since: 2017.8
|
||||
*/
|
||||
OstreeBloomHashFunc
|
||||
ostree_bloom_get_hash_func (OstreeBloom *bloom)
|
||||
{
|
||||
g_return_val_if_fail (bloom != NULL, NULL);
|
||||
|
||||
return bloom->hash_func;
|
||||
}
|
||||
|
||||
/* SipHash code adapted from https://github.com/veorq/SipHash/blob/master/siphash.c */
|
||||
|
||||
/*
|
||||
SipHash reference C implementation
|
||||
Copyright (c) 2012-2016 Jean-Philippe Aumasson
|
||||
<jeanphilippe.aumasson@gmail.com>
|
||||
Copyright (c) 2012-2014 Daniel J. Bernstein <djb@cr.yp.to>
|
||||
To the extent possible under law, the author(s) have dedicated all copyright
|
||||
and related and neighboring rights to this software to the public domain
|
||||
worldwide. This software is distributed without any warranty.
|
||||
You should have received a copy of the CC0 Public Domain Dedication along
|
||||
with
|
||||
this software. If not, see
|
||||
<http://creativecommons.org/publicdomain/zero/1.0/>.
|
||||
*/
|
||||
|
||||
/* default: SipHash-2-4 */
|
||||
#define cROUNDS 2
|
||||
#define dROUNDS 4
|
||||
|
||||
#define ROTL(x, b) (uint64_t)(((x) << (b)) | ((x) >> (64 - (b))))
|
||||
|
||||
#define U32TO8_LE(p, v) \
|
||||
(p)[0] = (uint8_t)((v)); \
|
||||
(p)[1] = (uint8_t)((v) >> 8); \
|
||||
(p)[2] = (uint8_t)((v) >> 16); \
|
||||
(p)[3] = (uint8_t)((v) >> 24);
|
||||
|
||||
#define U64TO8_LE(p, v) \
|
||||
U32TO8_LE((p), (uint32_t)((v))); \
|
||||
U32TO8_LE((p) + 4, (uint32_t)((v) >> 32));
|
||||
|
||||
#define U8TO64_LE(p) \
|
||||
(((uint64_t)((p)[0])) | ((uint64_t)((p)[1]) << 8) | \
|
||||
((uint64_t)((p)[2]) << 16) | ((uint64_t)((p)[3]) << 24) | \
|
||||
((uint64_t)((p)[4]) << 32) | ((uint64_t)((p)[5]) << 40) | \
|
||||
((uint64_t)((p)[6]) << 48) | ((uint64_t)((p)[7]) << 56))
|
||||
|
||||
#define SIPROUND \
|
||||
do { \
|
||||
v0 += v1; \
|
||||
v1 = ROTL(v1, 13); \
|
||||
v1 ^= v0; \
|
||||
v0 = ROTL(v0, 32); \
|
||||
v2 += v3; \
|
||||
v3 = ROTL(v3, 16); \
|
||||
v3 ^= v2; \
|
||||
v0 += v3; \
|
||||
v3 = ROTL(v3, 21); \
|
||||
v3 ^= v0; \
|
||||
v2 += v1; \
|
||||
v1 = ROTL(v1, 17); \
|
||||
v1 ^= v2; \
|
||||
v2 = ROTL(v2, 32); \
|
||||
} while (0)
|
||||
|
||||
#ifdef DEBUG
|
||||
#define TRACE \
|
||||
do { \
|
||||
printf("(%3d) v0 %08x %08x\n", (int)inlen, (uint32_t)(v0 >> 32), \
|
||||
(uint32_t)v0); \
|
||||
printf("(%3d) v1 %08x %08x\n", (int)inlen, (uint32_t)(v1 >> 32), \
|
||||
(uint32_t)v1); \
|
||||
printf("(%3d) v2 %08x %08x\n", (int)inlen, (uint32_t)(v2 >> 32), \
|
||||
(uint32_t)v2); \
|
||||
printf("(%3d) v3 %08x %08x\n", (int)inlen, (uint32_t)(v3 >> 32), \
|
||||
(uint32_t)v3); \
|
||||
} while (0)
|
||||
#else
|
||||
#define TRACE
|
||||
#endif
|
||||
|
||||
static int siphash(const uint8_t *in, const size_t inlen, const uint8_t *k,
|
||||
uint8_t *out, const size_t outlen) {
|
||||
|
||||
assert((outlen == 8) || (outlen == 16));
|
||||
uint64_t v0 = 0x736f6d6570736575ULL;
|
||||
uint64_t v1 = 0x646f72616e646f6dULL;
|
||||
uint64_t v2 = 0x6c7967656e657261ULL;
|
||||
uint64_t v3 = 0x7465646279746573ULL;
|
||||
uint64_t k0 = U8TO64_LE(k);
|
||||
uint64_t k1 = U8TO64_LE(k + 8);
|
||||
uint64_t m;
|
||||
int i;
|
||||
const uint8_t *end = in + inlen - (inlen % sizeof(uint64_t));
|
||||
const int left = inlen & 7;
|
||||
uint64_t b = ((uint64_t)inlen) << 56;
|
||||
v3 ^= k1;
|
||||
v2 ^= k0;
|
||||
v1 ^= k1;
|
||||
v0 ^= k0;
|
||||
|
||||
if (outlen == 16)
|
||||
v1 ^= 0xee;
|
||||
|
||||
for (; in != end; in += 8) {
|
||||
m = U8TO64_LE(in);
|
||||
v3 ^= m;
|
||||
|
||||
TRACE;
|
||||
for (i = 0; i < cROUNDS; ++i)
|
||||
SIPROUND;
|
||||
|
||||
v0 ^= m;
|
||||
}
|
||||
|
||||
switch (left) {
|
||||
case 7:
|
||||
b |= ((uint64_t)in[6]) << 48;
|
||||
case 6:
|
||||
b |= ((uint64_t)in[5]) << 40;
|
||||
case 5:
|
||||
b |= ((uint64_t)in[4]) << 32;
|
||||
case 4:
|
||||
b |= ((uint64_t)in[3]) << 24;
|
||||
case 3:
|
||||
b |= ((uint64_t)in[2]) << 16;
|
||||
case 2:
|
||||
b |= ((uint64_t)in[1]) << 8;
|
||||
case 1:
|
||||
b |= ((uint64_t)in[0]);
|
||||
break;
|
||||
case 0:
|
||||
break;
|
||||
}
|
||||
|
||||
v3 ^= b;
|
||||
|
||||
TRACE;
|
||||
for (i = 0; i < cROUNDS; ++i)
|
||||
SIPROUND;
|
||||
|
||||
v0 ^= b;
|
||||
|
||||
if (outlen == 16)
|
||||
v2 ^= 0xee;
|
||||
else
|
||||
v2 ^= 0xff;
|
||||
|
||||
TRACE;
|
||||
for (i = 0; i < dROUNDS; ++i)
|
||||
SIPROUND;
|
||||
|
||||
b = v0 ^ v1 ^ v2 ^ v3;
|
||||
U64TO8_LE(out, b);
|
||||
|
||||
if (outlen == 8)
|
||||
return 0;
|
||||
|
||||
v1 ^= 0xdd;
|
||||
|
||||
TRACE;
|
||||
for (i = 0; i < dROUNDS; ++i)
|
||||
SIPROUND;
|
||||
|
||||
b = v0 ^ v1 ^ v2 ^ v3;
|
||||
U64TO8_LE(out + 8, b);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* End SipHash copied code. */
|
||||
|
||||
/**
|
||||
* ostree_str_bloom_hash:
|
||||
* @element: element to calculate the hash for
|
||||
* @k: hash function index
|
||||
*
|
||||
* A universal hash function implementation for strings. It expects @element to
|
||||
* be a pointer to a string (i.e. @element has type `const gchar*`), and expects
|
||||
* @k to be in the range `[0, k_max)`, where `k_max` is the `k` value used to
|
||||
* construct the bloom filter. The output range from this hash function could be
|
||||
* any value in #guint64, and it handles input strings of any length.
|
||||
*
|
||||
* This function does not allow %NULL as a valid value for @element.
|
||||
*
|
||||
* Reference:
|
||||
* - https://www.131002.net/siphash/
|
||||
*
|
||||
* Returns: hash of the string at @element using parameter @k
|
||||
* Since: 2017.8
|
||||
*/
|
||||
guint64
|
||||
ostree_str_bloom_hash (gconstpointer element,
|
||||
guint8 k)
|
||||
{
|
||||
const gchar *str = element;
|
||||
gsize str_len;
|
||||
union
|
||||
{
|
||||
guint64 u64;
|
||||
guint8 u8[8];
|
||||
} out_le;
|
||||
guint8 k_array[16];
|
||||
gsize i;
|
||||
|
||||
str_len = strlen (str);
|
||||
for (i = 0; i < G_N_ELEMENTS (k_array); i++)
|
||||
k_array[i] = k;
|
||||
|
||||
siphash ((const guint8 *) str, str_len, k_array, out_le.u8, sizeof (out_le));
|
||||
|
||||
return le64toh (out_le.u64);
|
||||
}
|
||||
@@ -48,15 +48,11 @@ OstreeBootconfigParser *
|
||||
ostree_bootconfig_parser_clone (OstreeBootconfigParser *self)
|
||||
{
|
||||
OstreeBootconfigParser *parser = ostree_bootconfig_parser_new ();
|
||||
guint i;
|
||||
GHashTableIter hashiter;
|
||||
gpointer k, v;
|
||||
|
||||
for (i = 0; i < self->lines->len; i++)
|
||||
for (guint i = 0; i < self->lines->len; i++)
|
||||
g_ptr_array_add (parser->lines, g_variant_ref (self->lines->pdata[i]));
|
||||
|
||||
g_hash_table_iter_init (&hashiter, self->options);
|
||||
while (g_hash_table_iter_next (&hashiter, &k, &v))
|
||||
GLNX_HASH_TABLE_FOREACH_KV (self->options, const char*, k, const char*, v)
|
||||
g_hash_table_replace (parser->options, g_strdup (k), g_strdup (v));
|
||||
|
||||
return parser;
|
||||
@@ -183,14 +179,11 @@ ostree_bootconfig_parser_write_at (OstreeBootconfigParser *self,
|
||||
}
|
||||
}
|
||||
|
||||
GHashTableIter hashiter;
|
||||
gpointer hashkey, hashvalue;
|
||||
g_hash_table_iter_init (&hashiter, self->options);
|
||||
while (g_hash_table_iter_next (&hashiter, &hashkey, &hashvalue))
|
||||
GLNX_HASH_TABLE_FOREACH_KV (self->options, const char*, k, const char*, v)
|
||||
{
|
||||
if (g_hash_table_lookup (written_overrides, hashkey))
|
||||
if (g_hash_table_lookup (written_overrides, k))
|
||||
continue;
|
||||
write_key (self, buf, hashkey, hashvalue);
|
||||
write_key (self, buf, k, v);
|
||||
}
|
||||
|
||||
if (!glnx_file_replace_contents_at (dfd, path, (guint8*)buf->str, buf->len,
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "ostree-core.h"
|
||||
#include <sys/stat.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
@@ -88,7 +89,8 @@ _ostree_make_temporary_symlink_at (int tmp_dirfd,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
GFileInfo * _ostree_header_gfile_info_new (mode_t mode, uid_t uid, gid_t gid);
|
||||
GFileInfo * _ostree_stbuf_to_gfileinfo (const struct stat *stbuf);
|
||||
GFileInfo * _ostree_mode_uidgid_to_gfileinfo (mode_t mode, uid_t uid, gid_t gid);
|
||||
|
||||
static inline void
|
||||
_ostree_checksum_inplace_from_bytes_v (GVariant *csum_v, char *buf)
|
||||
@@ -173,6 +175,29 @@ _ostree_raw_file_to_archive_stream (GInputStream *input,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
#ifndef OSTREE_ENABLE_EXPERIMENTAL_API
|
||||
gboolean ostree_validate_collection_id (const char *collection_id, GError **error);
|
||||
#endif /* !OSTREE_ENABLE_EXPERIMENTAL_API */
|
||||
|
||||
#if (defined(OSTREE_COMPILATION) || GLIB_CHECK_VERSION(2, 44, 0)) && !defined(OSTREE_ENABLE_EXPERIMENTAL_API)
|
||||
#include <libglnx.h>
|
||||
#include "ostree-ref.h"
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC (OstreeCollectionRef, ostree_collection_ref_free)
|
||||
G_DEFINE_AUTO_CLEANUP_FREE_FUNC (OstreeCollectionRefv, ostree_collection_ref_freev, NULL)
|
||||
|
||||
#include "ostree-repo-finder.h"
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC (OstreeRepoFinder, g_object_unref)
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC (OstreeRepoFinderResult, ostree_repo_finder_result_free)
|
||||
G_DEFINE_AUTO_CLEANUP_FREE_FUNC (OstreeRepoFinderResultv, ostree_repo_finder_result_freev, NULL)
|
||||
|
||||
#include "ostree-repo-finder-avahi.h"
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC (OstreeRepoFinderAvahi, g_object_unref)
|
||||
|
||||
#include "ostree-repo-finder-config.h"
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC (OstreeRepoFinderConfig, g_object_unref)
|
||||
|
||||
#include "ostree-repo-finder-mount.h"
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC (OstreeRepoFinderMount, g_object_unref)
|
||||
#endif
|
||||
|
||||
G_END_DECLS
|
||||
+98
-13
@@ -101,6 +101,7 @@ ostree_validate_checksum_string (const char *sha256,
|
||||
|
||||
#define OSTREE_REF_FRAGMENT_REGEXP "[-._\\w\\d]+"
|
||||
#define OSTREE_REF_REGEXP "(?:" OSTREE_REF_FRAGMENT_REGEXP "/)*" OSTREE_REF_FRAGMENT_REGEXP
|
||||
#define OSTREE_REMOTE_NAME_REGEXP OSTREE_REF_FRAGMENT_REGEXP
|
||||
|
||||
/**
|
||||
* ostree_parse_refspec:
|
||||
@@ -125,7 +126,7 @@ ostree_parse_refspec (const char *refspec,
|
||||
static gsize regex_initialized;
|
||||
if (g_once_init_enter (®ex_initialized))
|
||||
{
|
||||
regex = g_regex_new ("^(" OSTREE_REF_FRAGMENT_REGEXP ":)?(" OSTREE_REF_REGEXP ")$", 0, 0, NULL);
|
||||
regex = g_regex_new ("^(" OSTREE_REMOTE_NAME_REGEXP ":)?(" OSTREE_REF_REGEXP ")$", 0, 0, NULL);
|
||||
g_assert (regex);
|
||||
g_once_init_leave (®ex_initialized, 1);
|
||||
}
|
||||
@@ -180,6 +181,67 @@ ostree_validate_rev (const char *rev,
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* ostree_validate_remote_name:
|
||||
* @remote_name: A remote name
|
||||
* @error: Error
|
||||
*
|
||||
* Returns: %TRUE if @remote_name is a valid remote name
|
||||
* Since: 2017.8
|
||||
*/
|
||||
gboolean
|
||||
ostree_validate_remote_name (const char *remote_name,
|
||||
GError **error)
|
||||
{
|
||||
g_autoptr(GMatchInfo) match = NULL;
|
||||
|
||||
static gsize regex_initialized;
|
||||
static GRegex *regex;
|
||||
if (g_once_init_enter (®ex_initialized))
|
||||
{
|
||||
regex = g_regex_new ("^" OSTREE_REMOTE_NAME_REGEXP "$", 0, 0, NULL);
|
||||
g_assert (regex);
|
||||
g_once_init_leave (®ex_initialized, 1);
|
||||
}
|
||||
|
||||
if (!g_regex_match (regex, remote_name, 0, &match))
|
||||
return glnx_throw (error, "Invalid remote name %s", remote_name);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* ostree_validate_collection_id:
|
||||
* @rev: (nullable): A collection ID
|
||||
* @error: Error
|
||||
*
|
||||
* Check whether the given @collection_id is valid. Return an error if it is
|
||||
* invalid or %NULL.
|
||||
*
|
||||
* Valid collection IDs are reverse DNS names:
|
||||
* * They are composed of 1 or more elements separated by a period (`.`) character.
|
||||
* All elements must contain at least one character.
|
||||
* * Each element must only contain the ASCII characters `[A-Z][a-z][0-9]_` and must not
|
||||
* begin with a digit.
|
||||
* * They must contain at least one `.` (period) character (and thus at least two elements).
|
||||
* * They must not begin with a `.` (period) character.
|
||||
* * They must not exceed 255 characters in length.
|
||||
*
|
||||
* (This makes their format identical to D-Bus interface names, for consistency.)
|
||||
*
|
||||
* Returns: %TRUE if @collection_id is a valid collection ID, %FALSE if it is invalid
|
||||
* or %NULL
|
||||
*/
|
||||
gboolean
|
||||
ostree_validate_collection_id (const char *collection_id, GError **error)
|
||||
{
|
||||
/* Abuse g_dbus_is_interface_name(), since collection IDs have the same format. */
|
||||
if (collection_id == NULL || !g_dbus_is_interface_name (collection_id))
|
||||
return glnx_throw (error, "Invalid collection ID %s", collection_id);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
GVariant *
|
||||
_ostree_file_header_new (GFileInfo *file_info,
|
||||
GVariant *xattrs)
|
||||
@@ -614,7 +676,7 @@ ostree_content_stream_parse (gboolean compressed,
|
||||
if (compressed)
|
||||
{
|
||||
if (!zlib_file_header_parse (file_header,
|
||||
out_file_info ? &ret_file_info : NULL,
|
||||
&ret_file_info,
|
||||
out_xattrs ? &ret_xattrs : NULL,
|
||||
error))
|
||||
return FALSE;
|
||||
@@ -622,12 +684,11 @@ ostree_content_stream_parse (gboolean compressed,
|
||||
else
|
||||
{
|
||||
if (!file_header_parse (file_header,
|
||||
out_file_info ? &ret_file_info : NULL,
|
||||
&ret_file_info,
|
||||
out_xattrs ? &ret_xattrs : NULL,
|
||||
error))
|
||||
return FALSE;
|
||||
if (ret_file_info)
|
||||
g_file_info_set_size (ret_file_info, input_length - archive_header_size - 8);
|
||||
g_file_info_set_size (ret_file_info, input_length - archive_header_size - 8);
|
||||
}
|
||||
|
||||
g_autoptr(GInputStream) ret_input = NULL;
|
||||
@@ -1432,7 +1493,7 @@ _ostree_loose_path (char *buf,
|
||||
}
|
||||
|
||||
/**
|
||||
* _ostree_header_gfile_info_new:
|
||||
* _ostree_stbuf_to_gfileinfo:
|
||||
* @mode: File mode
|
||||
* @uid: File uid
|
||||
* @gid: File gid
|
||||
@@ -1445,17 +1506,42 @@ _ostree_loose_path (char *buf,
|
||||
* Returns: (transfer full): A new #GFileInfo mapping a subset of @stbuf.
|
||||
*/
|
||||
GFileInfo *
|
||||
_ostree_header_gfile_info_new (mode_t mode, uid_t uid, gid_t gid)
|
||||
_ostree_stbuf_to_gfileinfo (const struct stat *stbuf)
|
||||
{
|
||||
GFileInfo *ret = g_file_info_new ();
|
||||
g_file_info_set_attribute_uint32 (ret, "standard::type", ot_gfile_type_for_mode (mode));
|
||||
GFileType ftype;
|
||||
const mode_t mode = stbuf->st_mode;
|
||||
if (S_ISDIR (mode))
|
||||
ftype = G_FILE_TYPE_DIRECTORY;
|
||||
else if (S_ISREG (mode))
|
||||
ftype = G_FILE_TYPE_REGULAR;
|
||||
else if (S_ISLNK (mode))
|
||||
ftype = G_FILE_TYPE_SYMBOLIC_LINK;
|
||||
else if (S_ISBLK (mode) || S_ISCHR(mode) || S_ISFIFO(mode))
|
||||
ftype = G_FILE_TYPE_SPECIAL;
|
||||
else
|
||||
ftype = G_FILE_TYPE_UNKNOWN;
|
||||
g_file_info_set_attribute_uint32 (ret, "standard::type", ftype);
|
||||
g_file_info_set_attribute_boolean (ret, "standard::is-symlink", S_ISLNK (mode));
|
||||
g_file_info_set_attribute_uint32 (ret, "unix::uid", uid);
|
||||
g_file_info_set_attribute_uint32 (ret, "unix::gid", gid);
|
||||
g_file_info_set_attribute_uint32 (ret, "unix::uid", stbuf->st_uid);
|
||||
g_file_info_set_attribute_uint32 (ret, "unix::gid", stbuf->st_gid);
|
||||
g_file_info_set_attribute_uint32 (ret, "unix::mode", mode);
|
||||
if (S_ISREG (mode))
|
||||
g_file_info_set_attribute_uint64 (ret, "standard::size", stbuf->st_size);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
GFileInfo *
|
||||
_ostree_mode_uidgid_to_gfileinfo (mode_t mode, uid_t uid, gid_t gid)
|
||||
{
|
||||
struct stat stbuf;
|
||||
stbuf.st_mode = mode;
|
||||
stbuf.st_uid = uid;
|
||||
stbuf.st_gid = gid;
|
||||
return _ostree_stbuf_to_gfileinfo (&stbuf);
|
||||
}
|
||||
|
||||
/*
|
||||
* _ostree_get_relative_object_path:
|
||||
* @checksum: ASCII checksum string
|
||||
@@ -1619,8 +1705,7 @@ file_header_parse (GVariant *metadata,
|
||||
uid = GUINT32_FROM_BE (uid);
|
||||
gid = GUINT32_FROM_BE (gid);
|
||||
mode = GUINT32_FROM_BE (mode);
|
||||
|
||||
g_autoptr(GFileInfo) ret_file_info = _ostree_header_gfile_info_new (mode, uid, gid);
|
||||
g_autoptr(GFileInfo) ret_file_info = _ostree_mode_uidgid_to_gfileinfo (mode, uid, gid);
|
||||
|
||||
if (S_ISREG (mode))
|
||||
{
|
||||
@@ -1670,7 +1755,7 @@ zlib_file_header_parse (GVariant *metadata,
|
||||
uid = GUINT32_FROM_BE (uid);
|
||||
gid = GUINT32_FROM_BE (gid);
|
||||
mode = GUINT32_FROM_BE (mode);
|
||||
g_autoptr(GFileInfo) ret_file_info = _ostree_header_gfile_info_new (mode, uid, gid);
|
||||
g_autoptr(GFileInfo) ret_file_info = _ostree_mode_uidgid_to_gfileinfo (mode, uid, gid);
|
||||
g_file_info_set_size (ret_file_info, GUINT64_FROM_BE (size));
|
||||
|
||||
if (S_ISREG (mode))
|
||||
|
||||
@@ -242,6 +242,14 @@ int ostree_cmp_checksum_bytes (const guchar *a, const guchar *b);
|
||||
_OSTREE_PUBLIC
|
||||
gboolean ostree_validate_rev (const char *rev, GError **error);
|
||||
|
||||
#ifdef OSTREE_ENABLE_EXPERIMENTAL_API
|
||||
_OSTREE_PUBLIC
|
||||
gboolean ostree_validate_collection_id (const char *collection_id, GError **error);
|
||||
#endif /* OSTREE_ENABLE_EXPERIMENTAL_API */
|
||||
|
||||
_OSTREE_PUBLIC
|
||||
gboolean ostree_validate_remote_name (const char *remote_name, GError **error);
|
||||
|
||||
_OSTREE_PUBLIC
|
||||
gboolean ostree_parse_refspec (const char *refspec,
|
||||
char **out_remote,
|
||||
|
||||
@@ -99,7 +99,7 @@ struct FetcherRequest {
|
||||
OstreeFetcherRequestFlags flags;
|
||||
gboolean is_membuf;
|
||||
GError *caught_write_error;
|
||||
OtTmpfile tmpf;
|
||||
GLnxTmpfile tmpf;
|
||||
GString *output_buf;
|
||||
|
||||
CURL *easy;
|
||||
@@ -270,9 +270,9 @@ ensure_tmpfile (FetcherRequest *req, GError **error)
|
||||
{
|
||||
if (!req->tmpf.initialized)
|
||||
{
|
||||
if (!ot_open_tmpfile_linkable_at (req->fetcher->tmpdir_dfd, ".",
|
||||
O_WRONLY | O_CLOEXEC, &req->tmpf,
|
||||
error))
|
||||
if (!glnx_open_tmpfile_linkable_at (req->fetcher->tmpdir_dfd, ".",
|
||||
O_WRONLY | O_CLOEXEC, &req->tmpf,
|
||||
error))
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
@@ -390,9 +390,9 @@ check_multi_info (OstreeFetcher *fetcher)
|
||||
glnx_set_error_from_errno (error);
|
||||
g_task_return_error (task, g_steal_pointer (&local_error));
|
||||
}
|
||||
else if (!ot_link_tmpfile_at (&req->tmpf, GLNX_LINK_TMPFILE_REPLACE,
|
||||
fetcher->tmpdir_dfd, tmpfile_path,
|
||||
error))
|
||||
else if (!glnx_link_tmpfile_at (&req->tmpf, GLNX_LINK_TMPFILE_REPLACE,
|
||||
fetcher->tmpdir_dfd, tmpfile_path,
|
||||
error))
|
||||
g_task_return_error (task, g_steal_pointer (&local_error));
|
||||
else
|
||||
{
|
||||
@@ -616,7 +616,7 @@ request_unref (FetcherRequest *req)
|
||||
g_ptr_array_unref (req->mirrorlist);
|
||||
g_free (req->filename);
|
||||
g_clear_error (&req->caught_write_error);
|
||||
ot_tmpfile_clear (&req->tmpf);
|
||||
glnx_tmpfile_clear (&req->tmpf);
|
||||
if (req->output_buf)
|
||||
g_string_free (req->output_buf, TRUE);
|
||||
curl_easy_cleanup (req->easy);
|
||||
@@ -690,9 +690,6 @@ static void
|
||||
adopt_steal_mainctx (OstreeFetcher *self,
|
||||
GMainContext *mainctx)
|
||||
{
|
||||
GHashTableIter hiter;
|
||||
gpointer key, value;
|
||||
|
||||
g_assert (self->mainctx == NULL);
|
||||
self->mainctx = mainctx; /* Transfer */
|
||||
|
||||
@@ -706,12 +703,8 @@ adopt_steal_mainctx (OstreeFetcher *self,
|
||||
update_timeout_cb (self->multi, timeout_micros / 1000, self);
|
||||
}
|
||||
|
||||
g_hash_table_iter_init (&hiter, self->sockets);
|
||||
while (g_hash_table_iter_next (&hiter, &key, &value))
|
||||
{
|
||||
SockInfo *fdp = key;
|
||||
setsock (fdp, fdp->sockfd, fdp->action, self);
|
||||
}
|
||||
GLNX_HASH_TABLE_FOREACH (self->sockets, SockInfo*, fdp)
|
||||
setsock (fdp, fdp->sockfd, fdp->action, self);
|
||||
}
|
||||
|
||||
static void
|
||||
|
||||
@@ -1323,24 +1323,18 @@ _ostree_fetcher_request_to_membuf_finish (OstreeFetcher *self,
|
||||
guint64
|
||||
_ostree_fetcher_bytes_transferred (OstreeFetcher *self)
|
||||
{
|
||||
GHashTableIter hiter;
|
||||
gpointer key, value;
|
||||
guint64 ret;
|
||||
|
||||
g_return_val_if_fail (OSTREE_IS_FETCHER (self), 0);
|
||||
|
||||
g_mutex_lock (&self->thread_closure->output_stream_set_lock);
|
||||
|
||||
ret = self->thread_closure->total_downloaded;
|
||||
guint64 ret = self->thread_closure->total_downloaded;
|
||||
|
||||
g_hash_table_iter_init (&hiter, self->thread_closure->output_stream_set);
|
||||
while (g_hash_table_iter_next (&hiter, &key, &value))
|
||||
GLNX_HASH_TABLE_FOREACH (self->thread_closure->output_stream_set,
|
||||
GFileOutputStream*, stream)
|
||||
{
|
||||
GFileOutputStream *stream = key;
|
||||
struct stat stbuf;
|
||||
|
||||
if (G_IS_FILE_DESCRIPTOR_BASED (stream))
|
||||
{
|
||||
struct stat stbuf;
|
||||
if (glnx_stream_fstat ((GFileDescriptorBased*)stream, &stbuf, NULL))
|
||||
ret += stbuf.st_size;
|
||||
}
|
||||
|
||||
@@ -172,12 +172,11 @@ _ostree_impl_system_generator (const char *ostree_cmdline,
|
||||
/* Generate our bind mount unit */
|
||||
const char *stateroot_var_path = glnx_strjoina ("/sysroot/ostree/deploy/", stateroot, "/var");
|
||||
|
||||
glnx_fd_close int tmpfd = -1;
|
||||
g_autofree char *tmppath = NULL;
|
||||
g_auto(GLnxTmpfile) tmpf = { 0, };
|
||||
if (!glnx_open_tmpfile_linkable_at (normal_dir_dfd, ".", O_WRONLY | O_CLOEXEC,
|
||||
&tmpfd, &tmppath, error))
|
||||
&tmpf, error))
|
||||
return FALSE;
|
||||
g_autoptr(GOutputStream) outstream = g_unix_output_stream_new (tmpfd, FALSE);
|
||||
g_autoptr(GOutputStream) outstream = g_unix_output_stream_new (tmpf.fd, FALSE);
|
||||
gsize bytes_written;
|
||||
/* This code is inspired by systemd's fstab-generator.c.
|
||||
*
|
||||
@@ -204,12 +203,11 @@ _ostree_impl_system_generator (const char *ostree_cmdline,
|
||||
return FALSE;
|
||||
g_clear_object (&outstream);
|
||||
/* It should be readable */
|
||||
if (fchmod (tmpfd, 0644) < 0)
|
||||
if (fchmod (tmpf.fd, 0644) < 0)
|
||||
return glnx_throw_errno_prefix (error, "fchmod");
|
||||
/* Error out if somehow it already exists, that'll help us debug conflicts */
|
||||
if (!glnx_link_tmpfile_at (normal_dir_dfd, GLNX_LINK_TMPFILE_NOREPLACE,
|
||||
tmpfd, tmppath, normal_dir_dfd,
|
||||
"var.mount", error))
|
||||
if (!glnx_link_tmpfile_at (&tmpf, GLNX_LINK_TMPFILE_NOREPLACE,
|
||||
normal_dir_dfd, "var.mount", error))
|
||||
return FALSE;
|
||||
|
||||
/* And ensure it's required; newer systemd will auto-inject fs dependencies
|
||||
|
||||
@@ -114,9 +114,6 @@ ostree_mutable_tree_set_contents_checksum (OstreeMutableTree *self,
|
||||
const char *
|
||||
ostree_mutable_tree_get_contents_checksum (OstreeMutableTree *self)
|
||||
{
|
||||
GHashTableIter iter;
|
||||
gpointer key, value;
|
||||
|
||||
if (!self->contents_checksum)
|
||||
return NULL;
|
||||
|
||||
@@ -127,10 +124,8 @@ ostree_mutable_tree_get_contents_checksum (OstreeMutableTree *self)
|
||||
*
|
||||
* However, we only call this function once right now.
|
||||
*/
|
||||
g_hash_table_iter_init (&iter, self->subdirs);
|
||||
while (g_hash_table_iter_next (&iter, &key, &value))
|
||||
GLNX_HASH_TABLE_FOREACH_V (self->subdirs, OstreeMutableTree*, subdir)
|
||||
{
|
||||
OstreeMutableTree *subdir = value;
|
||||
if (!ostree_mutable_tree_get_contents_checksum (subdir))
|
||||
{
|
||||
g_free (self->contents_checksum);
|
||||
|
||||
@@ -0,0 +1,193 @@
|
||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Authors:
|
||||
* - Philip Withnall <withnall@endlessm.com>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <gio/gio.h>
|
||||
#include <glib.h>
|
||||
#include <glib-object.h>
|
||||
#include <libglnx.h>
|
||||
|
||||
#include "ostree-autocleanups.h"
|
||||
#include "ostree-core.h"
|
||||
#include "ostree-core-private.h"
|
||||
#include "ostree-ref.h"
|
||||
|
||||
G_DEFINE_BOXED_TYPE (OstreeCollectionRef, ostree_collection_ref,
|
||||
ostree_collection_ref_dup, ostree_collection_ref_free)
|
||||
|
||||
/**
|
||||
* ostree_collection_ref_new:
|
||||
* @collection_id: (nullable): a collection ID, or %NULL for a plain ref
|
||||
* @ref_name: a ref name
|
||||
*
|
||||
* Create a new #OstreeCollectionRef containing (@collection_id, @ref_name). If
|
||||
* @collection_id is %NULL, this is equivalent to a plain ref name string (not a
|
||||
* refspec; no remote name is included), which can be used for non-P2P
|
||||
* operations.
|
||||
*
|
||||
* Returns: (transfer full): a new #OstreeCollectionRef
|
||||
* Since: 2017.8
|
||||
*/
|
||||
OstreeCollectionRef *
|
||||
ostree_collection_ref_new (const gchar *collection_id,
|
||||
const gchar *ref_name)
|
||||
{
|
||||
g_autoptr(OstreeCollectionRef) collection_ref = NULL;
|
||||
|
||||
g_return_val_if_fail (collection_id == NULL ||
|
||||
ostree_validate_collection_id (collection_id, NULL), NULL);
|
||||
g_return_val_if_fail (ostree_validate_rev (ref_name, NULL), NULL);
|
||||
|
||||
collection_ref = g_new0 (OstreeCollectionRef, 1);
|
||||
collection_ref->collection_id = g_strdup (collection_id);
|
||||
collection_ref->ref_name = g_strdup (ref_name);
|
||||
|
||||
return g_steal_pointer (&collection_ref);
|
||||
}
|
||||
|
||||
/**
|
||||
* ostree_collection_ref_dup:
|
||||
* @ref: an #OstreeCollectionRef
|
||||
*
|
||||
* Create a copy of the given @ref.
|
||||
*
|
||||
* Returns: (transfer full): a newly allocated copy of @ref
|
||||
* Since: 2017.8
|
||||
*/
|
||||
OstreeCollectionRef *
|
||||
ostree_collection_ref_dup (const OstreeCollectionRef *ref)
|
||||
{
|
||||
g_return_val_if_fail (ref != NULL, NULL);
|
||||
|
||||
return ostree_collection_ref_new (ref->collection_id, ref->ref_name);
|
||||
}
|
||||
|
||||
/**
|
||||
* ostree_collection_ref_free:
|
||||
* @ref: (transfer full): an #OstreeCollectionRef
|
||||
*
|
||||
* Free the given @ref.
|
||||
*
|
||||
* Since: 2017.8
|
||||
*/
|
||||
void
|
||||
ostree_collection_ref_free (OstreeCollectionRef *ref)
|
||||
{
|
||||
g_return_if_fail (ref != NULL);
|
||||
|
||||
g_free (ref->collection_id);
|
||||
g_free (ref->ref_name);
|
||||
g_free (ref);
|
||||
}
|
||||
|
||||
/**
|
||||
* ostree_collection_ref_hash:
|
||||
* @ref: an #OstreeCollectionRef
|
||||
*
|
||||
* Hash the given @ref. This function is suitable for use with #GHashTable.
|
||||
* @ref must be non-%NULL.
|
||||
*
|
||||
* Returns: hash value for @ref
|
||||
* Since: 2017.8
|
||||
*/
|
||||
guint
|
||||
ostree_collection_ref_hash (gconstpointer ref)
|
||||
{
|
||||
const OstreeCollectionRef *_ref = ref;
|
||||
|
||||
if (_ref->collection_id != NULL)
|
||||
return g_str_hash (_ref->collection_id) ^ g_str_hash (_ref->ref_name);
|
||||
else
|
||||
return g_str_hash (_ref->ref_name);
|
||||
}
|
||||
|
||||
/**
|
||||
* ostree_collection_ref_equal:
|
||||
* @ref1: an #OstreeCollectionRef
|
||||
* @ref2: another #OstreeCollectionRef
|
||||
*
|
||||
* Compare @ref1 and @ref2 and return %TRUE if they have the same collection ID and
|
||||
* ref name, and %FALSE otherwise. Both @ref1 and @ref2 must be non-%NULL.
|
||||
*
|
||||
* Returns: %TRUE if @ref1 and @ref2 are equal, %FALSE otherwise
|
||||
* Since: 2017.8
|
||||
*/
|
||||
gboolean
|
||||
ostree_collection_ref_equal (gconstpointer ref1,
|
||||
gconstpointer ref2)
|
||||
{
|
||||
const OstreeCollectionRef *_ref1 = ref1, *_ref2 = ref2;
|
||||
|
||||
return (g_strcmp0 (_ref1->collection_id, _ref2->collection_id) == 0 &&
|
||||
g_strcmp0 (_ref1->ref_name, _ref2->ref_name) == 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* ostree_collection_ref_dupv:
|
||||
* @refs: (array zero-terminated=1): %NULL-terminated array of #OstreeCollectionRefs
|
||||
*
|
||||
* Copy an array of #OstreeCollectionRefs, including deep copies of all its
|
||||
* elements. @refs must be %NULL-terminated; it may be empty, but must not be
|
||||
* %NULL.
|
||||
*
|
||||
* Returns: (transfer full) (array zero-terminated=1): a newly allocated copy of @refs
|
||||
* Since: 2017.8
|
||||
*/
|
||||
OstreeCollectionRef **
|
||||
ostree_collection_ref_dupv (const OstreeCollectionRef * const *refs)
|
||||
{
|
||||
gsize i, n_refs = g_strv_length ((gchar **) refs); /* hack */
|
||||
g_auto(OstreeCollectionRefv) new_refs = NULL;
|
||||
|
||||
g_return_val_if_fail (refs != NULL, NULL);
|
||||
|
||||
new_refs = g_new0 (OstreeCollectionRef*, n_refs + 1);
|
||||
|
||||
for (i = 0; i < n_refs; i++)
|
||||
new_refs[i] = ostree_collection_ref_dup (refs[i]);
|
||||
new_refs[i] = NULL;
|
||||
|
||||
return g_steal_pointer (&new_refs);
|
||||
}
|
||||
|
||||
/**
|
||||
* ostree_collection_ref_freev:
|
||||
* @refs: (transfer full) (array zero-terminated=1): an array of #OstreeCollectionRefs
|
||||
*
|
||||
* Free the given array of @refs, including freeing all its elements. @refs
|
||||
* must be %NULL-terminated; it may be empty, but must not be %NULL.
|
||||
*
|
||||
* Since: 2017.8
|
||||
*/
|
||||
void
|
||||
ostree_collection_ref_freev (OstreeCollectionRef **refs)
|
||||
{
|
||||
gsize i;
|
||||
|
||||
g_return_if_fail (refs != NULL);
|
||||
|
||||
for (i = 0; refs[i] != NULL; i++)
|
||||
ostree_collection_ref_free (refs[i]);
|
||||
g_free (refs);
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Authors:
|
||||
* - Philip Withnall <withnall@endlessm.com>
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <gio/gio.h>
|
||||
#include <glib.h>
|
||||
#include <glib-object.h>
|
||||
|
||||
#include "ostree-types.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
/**
|
||||
* OstreeCollectionRef:
|
||||
* @collection_id: (nullable): collection ID which provided the ref, or %NULL if there
|
||||
* is no associated collection
|
||||
* @ref_name: ref name
|
||||
*
|
||||
* A structure which globally uniquely identifies a ref as the tuple
|
||||
* (@collection_id, @ref_name). For backwards compatibility, @collection_id may be %NULL,
|
||||
* indicating a ref name which is not globally unique.
|
||||
*
|
||||
* Since: 2017.8
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
gchar *collection_id; /* (nullable) */
|
||||
gchar *ref_name; /* (not nullable) */
|
||||
} OstreeCollectionRef;
|
||||
|
||||
#ifndef __GI_SCANNER__
|
||||
_OSTREE_PUBLIC
|
||||
GType ostree_collection_ref_get_type (void);
|
||||
|
||||
_OSTREE_PUBLIC
|
||||
OstreeCollectionRef *ostree_collection_ref_new (const gchar *collection_id,
|
||||
const gchar *ref_name);
|
||||
_OSTREE_PUBLIC
|
||||
OstreeCollectionRef *ostree_collection_ref_dup (const OstreeCollectionRef *ref);
|
||||
_OSTREE_PUBLIC
|
||||
void ostree_collection_ref_free (OstreeCollectionRef *ref);
|
||||
#endif
|
||||
|
||||
_OSTREE_PUBLIC
|
||||
guint ostree_collection_ref_hash (gconstpointer ref);
|
||||
_OSTREE_PUBLIC
|
||||
gboolean ostree_collection_ref_equal (gconstpointer ref1,
|
||||
gconstpointer ref2);
|
||||
|
||||
_OSTREE_PUBLIC
|
||||
OstreeCollectionRef **ostree_collection_ref_dupv (const OstreeCollectionRef * const *refs);
|
||||
_OSTREE_PUBLIC
|
||||
void ostree_collection_ref_freev (OstreeCollectionRef **refs);
|
||||
|
||||
/**
|
||||
* OstreeCollectionRefv:
|
||||
*
|
||||
* A %NULL-terminated array of #OstreeCollectionRef instances, designed to
|
||||
* be used with g_auto():
|
||||
*
|
||||
* |[<!-- language="C" -->
|
||||
* g_auto(OstreeCollectionRefv) refs = NULL;
|
||||
* ]|
|
||||
*
|
||||
* Since: 2017.8
|
||||
*/
|
||||
typedef OstreeCollectionRef** OstreeCollectionRefv;
|
||||
|
||||
G_END_DECLS
|
||||
@@ -41,7 +41,7 @@ G_BEGIN_DECLS
|
||||
* remotes can only be passed around as (reference counted) opaque handles. In
|
||||
* future, more API may be added to create and interrogate them.
|
||||
*
|
||||
* Since: 2016.7
|
||||
* Since: 2017.6
|
||||
*/
|
||||
#ifndef OSTREE_ENABLE_EXPERIMENTAL_API
|
||||
/* This is in ostree-types.h otherwise */
|
||||
|
||||
@@ -60,9 +60,9 @@ checkout_object_for_uncompressed_cache (OstreeRepo *self,
|
||||
guint32 file_mode = g_file_info_get_attribute_uint32 (src_info, "unix::mode");
|
||||
file_mode &= ~(S_ISUID|S_ISGID);
|
||||
|
||||
g_auto(OtTmpfile) tmpf = { 0, };
|
||||
if (!ot_open_tmpfile_linkable_at (self->tmp_dir_fd, ".", O_WRONLY | O_CLOEXEC,
|
||||
&tmpf, error))
|
||||
g_auto(GLnxTmpfile) tmpf = { 0, };
|
||||
if (!glnx_open_tmpfile_linkable_at (self->tmp_dir_fd, ".", O_WRONLY | O_CLOEXEC,
|
||||
&tmpf, error))
|
||||
return FALSE;
|
||||
g_autoptr(GOutputStream) temp_out = g_unix_output_stream_new (tmpf.fd, FALSE);
|
||||
|
||||
@@ -89,9 +89,9 @@ checkout_object_for_uncompressed_cache (OstreeRepo *self,
|
||||
cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
if (!ot_link_tmpfile_at (&tmpf, GLNX_LINK_TMPFILE_NOREPLACE_IGNORE_EXIST,
|
||||
self->uncompressed_objects_dir_fd, loose_path,
|
||||
error))
|
||||
if (!glnx_link_tmpfile_at (&tmpf, GLNX_LINK_TMPFILE_NOREPLACE_IGNORE_EXIST,
|
||||
self->uncompressed_objects_dir_fd, loose_path,
|
||||
error))
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
@@ -254,11 +254,11 @@ create_file_copy_from_input_at (OstreeRepo *repo,
|
||||
}
|
||||
else if (g_file_info_get_file_type (file_info) == G_FILE_TYPE_REGULAR)
|
||||
{
|
||||
g_auto(OtTmpfile) tmpf = { 0, };
|
||||
g_auto(GLnxTmpfile) tmpf = { 0, };
|
||||
GLnxLinkTmpfileReplaceMode replace_mode;
|
||||
|
||||
if (!ot_open_tmpfile_linkable_at (destination_dfd, ".", O_WRONLY | O_CLOEXEC,
|
||||
&tmpf, error))
|
||||
if (!glnx_open_tmpfile_linkable_at (destination_dfd, ".", O_WRONLY | O_CLOEXEC,
|
||||
&tmpf, error))
|
||||
return FALSE;
|
||||
|
||||
if (sepolicy_enabled && options->mode != OSTREE_REPO_CHECKOUT_MODE_USER)
|
||||
@@ -285,9 +285,9 @@ create_file_copy_from_input_at (OstreeRepo *repo,
|
||||
else
|
||||
replace_mode = GLNX_LINK_TMPFILE_NOREPLACE;
|
||||
|
||||
if (!ot_link_tmpfile_at (&tmpf, replace_mode,
|
||||
destination_dfd, destination_name,
|
||||
error))
|
||||
if (!glnx_link_tmpfile_at (&tmpf, replace_mode,
|
||||
destination_dfd, destination_name,
|
||||
error))
|
||||
return FALSE;
|
||||
}
|
||||
else
|
||||
@@ -446,7 +446,7 @@ checkout_one_file_at (OstreeRepo *repo,
|
||||
/* But only under these conditions */
|
||||
if (is_bare || is_archive_z2_with_cache)
|
||||
{
|
||||
/* Override repo mode; for archive-z2 we're looking in
|
||||
/* Override repo mode; for archive we're looking in
|
||||
the cache, which is in "bare" form */
|
||||
_ostree_loose_path (loose_path_buf, checksum, OSTREE_OBJECT_TYPE_FILE, OSTREE_REPO_MODE_BARE);
|
||||
if (!checkout_file_hardlink (current_repo,
|
||||
@@ -488,7 +488,7 @@ checkout_one_file_at (OstreeRepo *repo,
|
||||
g_autoptr(GInputStream) input = NULL;
|
||||
g_autoptr(GVariant) xattrs = NULL;
|
||||
|
||||
/* Ok, if we're archive-z2 and we didn't find an object, uncompress
|
||||
/* Ok, if we're archive and we didn't find an object, uncompress
|
||||
* it now, stick it in the cache, and then hardlink to that.
|
||||
*/
|
||||
if (can_cache
|
||||
@@ -1088,13 +1088,12 @@ ostree_repo_checkout_gc (OstreeRepo *self,
|
||||
self->updated_uncompressed_dirs = g_hash_table_new (NULL, NULL);
|
||||
g_mutex_unlock (&self->cache_lock);
|
||||
|
||||
GHashTableIter iter;
|
||||
gpointer key, value;
|
||||
if (to_clean_dirs)
|
||||
g_hash_table_iter_init (&iter, to_clean_dirs);
|
||||
while (to_clean_dirs && g_hash_table_iter_next (&iter, &key, &value))
|
||||
if (!to_clean_dirs)
|
||||
return TRUE; /* Note early return */
|
||||
|
||||
GLNX_HASH_TABLE_FOREACH (to_clean_dirs, gpointer, prefix)
|
||||
{
|
||||
g_autofree char *objdir_name = g_strdup_printf ("%02x", GPOINTER_TO_UINT (key));
|
||||
g_autofree char *objdir_name = g_strdup_printf ("%02x", GPOINTER_TO_UINT (prefix));
|
||||
g_auto(GLnxDirFdIterator) dfd_iter = { 0, };
|
||||
|
||||
if (!glnx_dirfd_iterator_init_at (self->uncompressed_objects_dir_fd, objdir_name, FALSE,
|
||||
|
||||
+441
-407
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,137 @@
|
||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
|
||||
*
|
||||
* Copyright © 2016 Kinvolk GmbH
|
||||
* 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.
|
||||
*
|
||||
* Authors:
|
||||
* - Krzesimir Nowak <krzesimir@kinvolk.io>
|
||||
* - Philip Withnall <withnall@endlessm.com>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <avahi-common/strlst.h>
|
||||
#include <glib.h>
|
||||
#include <glib-object.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "ostree-autocleanups.h"
|
||||
#include "ostree-repo-finder-avahi.h"
|
||||
#include "ostree-repo-finder-avahi-private.h"
|
||||
|
||||
/* Reference: RFC 6763, §6. */
|
||||
static gboolean
|
||||
parse_txt_record (const guint8 *txt,
|
||||
gsize txt_len,
|
||||
const gchar **key,
|
||||
gsize *key_len,
|
||||
const guint8 **value,
|
||||
gsize *value_len)
|
||||
{
|
||||
gsize i;
|
||||
|
||||
g_return_val_if_fail (key != NULL, FALSE);
|
||||
g_return_val_if_fail (key_len != NULL, FALSE);
|
||||
g_return_val_if_fail (value != NULL, FALSE);
|
||||
g_return_val_if_fail (value_len != NULL, FALSE);
|
||||
|
||||
/* RFC 6763, §6.1. */
|
||||
if (txt_len > 8900)
|
||||
return FALSE;
|
||||
|
||||
*key = (const gchar *) txt;
|
||||
*key_len = 0;
|
||||
*value = NULL;
|
||||
*value_len = 0;
|
||||
|
||||
for (i = 0; i < txt_len; i++)
|
||||
{
|
||||
if (txt[i] >= 0x20 && txt[i] <= 0x7e && txt[i] != '=')
|
||||
{
|
||||
/* Key character. */
|
||||
*key_len = *key_len + 1;
|
||||
continue;
|
||||
}
|
||||
else if (*key_len > 0 && txt[i] == '=')
|
||||
{
|
||||
/* Separator. */
|
||||
*value = txt + (i + 1);
|
||||
*value_len = txt_len - (i + 1);
|
||||
return TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/* The entire TXT record is the key; there is no ‘=’ or value. */
|
||||
*value = NULL;
|
||||
*value_len = 0;
|
||||
|
||||
return (*key_len > 0);
|
||||
}
|
||||
|
||||
/* TODO: Docs. Return value is only valid as long as @txt is. Reference: RFC 6763, §6. */
|
||||
GHashTable *
|
||||
_ostree_txt_records_parse (AvahiStringList *txt)
|
||||
{
|
||||
AvahiStringList *l;
|
||||
g_autoptr(GHashTable) out = NULL;
|
||||
|
||||
out = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_bytes_unref);
|
||||
|
||||
for (l = txt; l != NULL; l = avahi_string_list_get_next (l))
|
||||
{
|
||||
const guint8 *txt;
|
||||
gsize txt_len;
|
||||
const gchar *key;
|
||||
const guint8 *value;
|
||||
gsize key_len, value_len;
|
||||
g_autofree gchar *key_allocated = NULL;
|
||||
g_autoptr(GBytes) value_allocated = NULL;
|
||||
|
||||
txt = avahi_string_list_get_text (l);
|
||||
txt_len = avahi_string_list_get_size (l);
|
||||
|
||||
if (!parse_txt_record (txt, txt_len, &key, &key_len, &value, &value_len))
|
||||
{
|
||||
g_debug ("Ignoring invalid TXT record of length %" G_GSIZE_FORMAT,
|
||||
txt_len);
|
||||
continue;
|
||||
}
|
||||
|
||||
key_allocated = g_ascii_strdown (key, key_len);
|
||||
|
||||
if (g_hash_table_lookup_extended (out, key_allocated, NULL, NULL))
|
||||
{
|
||||
g_debug ("Ignoring duplicate TXT record ‘%s’", key_allocated);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Distinguish between the case where the entire record is the key
|
||||
* (value == NULL) and the case where the record is the key + ‘=’ and the
|
||||
* value is empty (value != NULL && value_len == 0). */
|
||||
if (value != NULL)
|
||||
value_allocated = g_bytes_new_static (value, value_len);
|
||||
|
||||
g_hash_table_insert (out, g_steal_pointer (&key_allocated), g_steal_pointer (&value_allocated));
|
||||
}
|
||||
|
||||
return g_steal_pointer (&out);
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Authors:
|
||||
* - Philip Withnall <withnall@endlessm.com>
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <avahi-common/strlst.h>
|
||||
#include <gio/gio.h>
|
||||
#include <glib.h>
|
||||
#include <glib-object.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
GHashTable *_ostree_txt_records_parse (AvahiStringList *txt);
|
||||
|
||||
G_END_DECLS
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,62 @@
|
||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Authors:
|
||||
* - Philip Withnall <withnall@endlessm.com>
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <gio/gio.h>
|
||||
#include <glib.h>
|
||||
#include <glib-object.h>
|
||||
|
||||
#include "ostree-repo-finder.h"
|
||||
#include "ostree-types.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define OSTREE_TYPE_REPO_FINDER_AVAHI (ostree_repo_finder_avahi_get_type ())
|
||||
|
||||
/* Manually expanded version of the following, omitting autoptr support (for GLib < 2.44):
|
||||
_OSTREE_PUBLIC
|
||||
G_DECLARE_FINAL_TYPE (OstreeRepoFinderAvahi, ostree_repo_finder_avahi, OSTREE, REPO_FINDER_AVAHI, GObject) */
|
||||
|
||||
_OSTREE_PUBLIC
|
||||
GType ostree_repo_finder_avahi_get_type (void);
|
||||
|
||||
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
|
||||
typedef struct _OstreeRepoFinderAvahi OstreeRepoFinderAvahi;
|
||||
typedef struct { GObjectClass parent_class; } OstreeRepoFinderAvahiClass;
|
||||
|
||||
static inline OstreeRepoFinderAvahi *OSTREE_REPO_FINDER_AVAHI (gpointer ptr) { return G_TYPE_CHECK_INSTANCE_CAST (ptr, ostree_repo_finder_avahi_get_type (), OstreeRepoFinderAvahi); }
|
||||
static inline gboolean OSTREE_IS_REPO_FINDER_AVAHI (gpointer ptr) { return G_TYPE_CHECK_INSTANCE_TYPE (ptr, ostree_repo_finder_avahi_get_type ()); }
|
||||
G_GNUC_END_IGNORE_DEPRECATIONS
|
||||
|
||||
_OSTREE_PUBLIC
|
||||
OstreeRepoFinderAvahi *ostree_repo_finder_avahi_new (GMainContext *context);
|
||||
|
||||
_OSTREE_PUBLIC
|
||||
void ostree_repo_finder_avahi_start (OstreeRepoFinderAvahi *self,
|
||||
GError **error);
|
||||
|
||||
_OSTREE_PUBLIC
|
||||
void ostree_repo_finder_avahi_stop (OstreeRepoFinderAvahi *self);
|
||||
|
||||
G_END_DECLS
|
||||
@@ -0,0 +1,235 @@
|
||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Authors:
|
||||
* - Philip Withnall <withnall@endlessm.com>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <gio/gio.h>
|
||||
#include <glib.h>
|
||||
#include <glib-object.h>
|
||||
#include <libglnx.h>
|
||||
|
||||
#include "ostree-remote-private.h"
|
||||
#include "ostree-repo.h"
|
||||
#include "ostree-repo-private.h"
|
||||
#include "ostree-repo-finder.h"
|
||||
#include "ostree-repo-finder-config.h"
|
||||
|
||||
/**
|
||||
* SECTION:ostree-repo-finder-config
|
||||
* @title: OstreeRepoFinderConfig
|
||||
* @short_description: Finds remote repositories from ref names using the local
|
||||
* repository configuration files
|
||||
* @stability: Unstable
|
||||
* @include: libostree/ostree-repo-finder-config.h
|
||||
*
|
||||
* #OstreeRepoFinderConfig is an implementation of #OstreeRepoFinder which looks
|
||||
* refs up in locally configured remotes and returns remote URIs.
|
||||
* Duplicate remote URIs are combined into a single #OstreeRepoFinderResult
|
||||
* which lists multiple refs.
|
||||
*
|
||||
* For all the locally configured remotes which have an `collection-id` specified
|
||||
* (see [ostree.repo-config(5)](man:ostree.repo-config(5))), it finds the
|
||||
* intersection of their refs and the set of refs to resolve. If the
|
||||
* intersection is non-empty, that remote is returned as a result. Remotes which
|
||||
* do not have their `collection-id` key configured are ignored.
|
||||
*
|
||||
* Since: 2017.8
|
||||
*/
|
||||
|
||||
static void ostree_repo_finder_config_iface_init (OstreeRepoFinderInterface *iface);
|
||||
|
||||
struct _OstreeRepoFinderConfig
|
||||
{
|
||||
GObject parent_instance;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE_WITH_CODE (OstreeRepoFinderConfig, ostree_repo_finder_config, G_TYPE_OBJECT,
|
||||
G_IMPLEMENT_INTERFACE (OSTREE_TYPE_REPO_FINDER, ostree_repo_finder_config_iface_init))
|
||||
|
||||
static gint
|
||||
results_compare_cb (gconstpointer a,
|
||||
gconstpointer b)
|
||||
{
|
||||
const OstreeRepoFinderResult *result_a = *((const OstreeRepoFinderResult **) a);
|
||||
const OstreeRepoFinderResult *result_b = *((const OstreeRepoFinderResult **) b);
|
||||
|
||||
return ostree_repo_finder_result_compare (result_a, result_b);
|
||||
}
|
||||
|
||||
static void
|
||||
ostree_repo_finder_config_resolve_async (OstreeRepoFinder *finder,
|
||||
const OstreeCollectionRef * const *refs,
|
||||
OstreeRepo *parent_repo,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data)
|
||||
{
|
||||
g_autoptr(GTask) task = NULL;
|
||||
g_autoptr(GPtrArray) results = NULL;
|
||||
const gint priority = 100; /* arbitrarily chosen; lower than the others */
|
||||
gsize i, j;
|
||||
g_autoptr(GHashTable) repo_name_to_refs = NULL; /* (element-type utf8 GHashTable) */
|
||||
GHashTable *supported_ref_to_checksum; /* (element-type OstreeCollectionRef utf8) */
|
||||
GHashTableIter iter;
|
||||
const gchar *remote_name;
|
||||
g_auto(GStrv) remotes = NULL;
|
||||
gsize n_remotes = 0;
|
||||
|
||||
task = g_task_new (finder, cancellable, callback, user_data);
|
||||
g_task_set_source_tag (task, ostree_repo_finder_config_resolve_async);
|
||||
results = g_ptr_array_new_with_free_func ((GDestroyNotify) ostree_repo_finder_result_free);
|
||||
repo_name_to_refs = g_hash_table_new_full (g_str_hash, g_str_equal, NULL,
|
||||
(GDestroyNotify) g_hash_table_unref);
|
||||
|
||||
/* List all remotes in this #OstreeRepo and see which of their ref lists
|
||||
* intersect with @refs. */
|
||||
remotes = ostree_repo_remote_list (parent_repo, (guint *) &n_remotes);
|
||||
|
||||
g_debug ("%s: Checking %" G_GSIZE_FORMAT " remotes", G_STRFUNC, n_remotes);
|
||||
|
||||
for (i = 0; i < n_remotes; i++)
|
||||
{
|
||||
g_autoptr(GError) local_error = NULL;
|
||||
g_autoptr(GHashTable) remote_refs = NULL; /* (element-type utf8 utf8) */
|
||||
const gchar *checksum;
|
||||
g_autofree gchar *remote_collection_id = NULL;
|
||||
|
||||
remote_name = remotes[i];
|
||||
|
||||
if (!ostree_repo_get_remote_option (parent_repo, remote_name, "collection-id",
|
||||
NULL, &remote_collection_id, &local_error) ||
|
||||
!ostree_validate_collection_id (remote_collection_id, &local_error))
|
||||
{
|
||||
g_debug ("Ignoring remote ‘%s’ due to no valid collection ID being configured for it: %s",
|
||||
remote_name, local_error->message);
|
||||
g_clear_error (&local_error);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!ostree_repo_remote_list_refs (parent_repo, remote_name, &remote_refs,
|
||||
cancellable, &local_error))
|
||||
{
|
||||
g_debug ("Ignoring remote ‘%s’ due to error loading its refs: %s",
|
||||
remote_name, local_error->message);
|
||||
g_clear_error (&local_error);
|
||||
continue;
|
||||
}
|
||||
|
||||
for (j = 0; refs[j] != NULL; j++)
|
||||
{
|
||||
if (g_strcmp0 (refs[j]->collection_id, remote_collection_id) == 0 &&
|
||||
g_hash_table_lookup_extended (remote_refs, refs[j]->ref_name, NULL, (gpointer *) &checksum))
|
||||
{
|
||||
/* The requested ref is listed in the refs for this remote. Add
|
||||
* the remote to the results, and the ref to its
|
||||
* @supported_ref_to_checksum. */
|
||||
g_debug ("Resolved ref (%s, %s) to remote ‘%s’.",
|
||||
refs[j]->collection_id, refs[j]->ref_name, remote_name);
|
||||
|
||||
supported_ref_to_checksum = g_hash_table_lookup (repo_name_to_refs, remote_name);
|
||||
|
||||
if (supported_ref_to_checksum == NULL)
|
||||
{
|
||||
supported_ref_to_checksum = g_hash_table_new_full (ostree_collection_ref_hash,
|
||||
ostree_collection_ref_equal,
|
||||
NULL, g_free);
|
||||
g_hash_table_insert (repo_name_to_refs, (gpointer) remote_name, supported_ref_to_checksum /* transfer */);
|
||||
}
|
||||
|
||||
g_hash_table_insert (supported_ref_to_checksum,
|
||||
(gpointer) refs[j], g_strdup (checksum));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Aggregate the results. */
|
||||
g_hash_table_iter_init (&iter, repo_name_to_refs);
|
||||
|
||||
while (g_hash_table_iter_next (&iter, (gpointer *) &remote_name, (gpointer *) &supported_ref_to_checksum))
|
||||
{
|
||||
g_autoptr(GError) local_error = NULL;
|
||||
OstreeRemote *remote;
|
||||
|
||||
/* We don’t know what last-modified timestamp the remote has without
|
||||
* making expensive HTTP queries, so leave that information blank. We
|
||||
* assume that the configuration which says the refs and commits in
|
||||
* @supported_ref_to_checksum are in the repository is correct; the code
|
||||
* in ostree_repo_find_remotes_async() will check that. */
|
||||
remote = _ostree_repo_get_remote_inherited (parent_repo, remote_name, &local_error);
|
||||
if (remote == NULL)
|
||||
{
|
||||
g_debug ("Configuration for remote ‘%s’ could not be found. Ignoring.",
|
||||
remote_name);
|
||||
continue;
|
||||
}
|
||||
|
||||
g_ptr_array_add (results, ostree_repo_finder_result_new (remote, finder, priority, supported_ref_to_checksum, 0));
|
||||
}
|
||||
|
||||
g_ptr_array_sort (results, results_compare_cb);
|
||||
|
||||
g_task_return_pointer (task, g_steal_pointer (&results), (GDestroyNotify) g_ptr_array_unref);
|
||||
}
|
||||
|
||||
static GPtrArray *
|
||||
ostree_repo_finder_config_resolve_finish (OstreeRepoFinder *finder,
|
||||
GAsyncResult *result,
|
||||
GError **error)
|
||||
{
|
||||
g_return_val_if_fail (g_task_is_valid (result, finder), NULL);
|
||||
return g_task_propagate_pointer (G_TASK (result), error);
|
||||
}
|
||||
|
||||
static void
|
||||
ostree_repo_finder_config_init (OstreeRepoFinderConfig *self)
|
||||
{
|
||||
/* Nothing to see here. */
|
||||
}
|
||||
|
||||
static void
|
||||
ostree_repo_finder_config_class_init (OstreeRepoFinderConfigClass *klass)
|
||||
{
|
||||
/* Nothing to see here. */
|
||||
}
|
||||
|
||||
static void
|
||||
ostree_repo_finder_config_iface_init (OstreeRepoFinderInterface *iface)
|
||||
{
|
||||
iface->resolve_async = ostree_repo_finder_config_resolve_async;
|
||||
iface->resolve_finish = ostree_repo_finder_config_resolve_finish;
|
||||
}
|
||||
|
||||
/**
|
||||
* ostree_repo_finder_config_new:
|
||||
*
|
||||
* Create a new #OstreeRepoFinderConfig.
|
||||
*
|
||||
* Returns: (transfer full): a new #OstreeRepoFinderConfig
|
||||
* Since: 2017.8
|
||||
*/
|
||||
OstreeRepoFinderConfig *
|
||||
ostree_repo_finder_config_new (void)
|
||||
{
|
||||
return g_object_new (OSTREE_TYPE_REPO_FINDER_CONFIG, NULL);
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Authors:
|
||||
* - Philip Withnall <withnall@endlessm.com>
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <gio/gio.h>
|
||||
#include <glib.h>
|
||||
#include <glib-object.h>
|
||||
|
||||
#include "ostree-repo-finder.h"
|
||||
#include "ostree-types.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define OSTREE_TYPE_REPO_FINDER_CONFIG (ostree_repo_finder_config_get_type ())
|
||||
|
||||
/* Manually expanded version of the following, omitting autoptr support (for GLib < 2.44):
|
||||
_OSTREE_PUBLIC
|
||||
G_DECLARE_FINAL_TYPE (OstreeRepoFinderConfig, ostree_repo_finder_config, OSTREE, REPO_FINDER_CONFIG, GObject) */
|
||||
|
||||
_OSTREE_PUBLIC
|
||||
GType ostree_repo_finder_config_get_type (void);
|
||||
|
||||
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
|
||||
typedef struct _OstreeRepoFinderConfig OstreeRepoFinderConfig;
|
||||
typedef struct { GObjectClass parent_class; } OstreeRepoFinderConfigClass;
|
||||
|
||||
static inline OstreeRepoFinderConfig *OSTREE_REPO_FINDER_CONFIG (gpointer ptr) { return G_TYPE_CHECK_INSTANCE_CAST (ptr, ostree_repo_finder_config_get_type (), OstreeRepoFinderConfig); }
|
||||
static inline gboolean OSTREE_IS_REPO_FINDER_CONFIG (gpointer ptr) { return G_TYPE_CHECK_INSTANCE_TYPE (ptr, ostree_repo_finder_config_get_type ()); }
|
||||
G_GNUC_END_IGNORE_DEPRECATIONS
|
||||
|
||||
_OSTREE_PUBLIC
|
||||
OstreeRepoFinderConfig *ostree_repo_finder_config_new (void);
|
||||
|
||||
G_END_DECLS
|
||||
@@ -0,0 +1,547 @@
|
||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Authors:
|
||||
* - Philip Withnall <withnall@endlessm.com>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <gio/gio.h>
|
||||
#include <glib.h>
|
||||
#include <glib-object.h>
|
||||
#include <libglnx.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "ostree-autocleanups.h"
|
||||
#include "ostree-remote-private.h"
|
||||
#include "ostree-repo-finder.h"
|
||||
#include "ostree-repo-finder-mount.h"
|
||||
|
||||
/**
|
||||
* SECTION:ostree-repo-finder-mount
|
||||
* @title: OstreeRepoFinderMount
|
||||
* @short_description: Finds remote repositories from ref names by looking at
|
||||
* mounted removable volumes
|
||||
* @stability: Unstable
|
||||
* @include: libostree/ostree-repo-finder-mount.h
|
||||
*
|
||||
* #OstreeRepoFinderMount is an implementation of #OstreeRepoFinder which looks
|
||||
* refs up in well-known locations on any mounted removable volumes.
|
||||
*
|
||||
* For an #OstreeCollectionRef, (`C`, `R`), it checks whether `.ostree/repos/C/R`
|
||||
* exists and is an OSTree repository on each mounted removable volume. Collection
|
||||
* IDs and ref names are not escaped when building the path, so if either
|
||||
* contains `/` in its name, the repository will be checked for in a
|
||||
* subdirectory of `.ostree/repos`. Non-removable volumes are ignored.
|
||||
*
|
||||
* For each repository which is found, a result will be returned for the
|
||||
* intersection of the refs being searched for, and the refs in `refs/heads` and
|
||||
* `refs/mirrors` in the repository on the removable volume.
|
||||
*
|
||||
* Symlinks are followed when resolving the refs, so a volume might contain a
|
||||
* single OSTree at some arbitrary path, with a number of refs linking to it
|
||||
* from `.ostree/repos`. Any symlink which points outside the volume’s file
|
||||
* system will be ignored. Repositories are deduplicated in the results.
|
||||
*
|
||||
* The volume monitor used to find mounted volumes can be overridden by setting
|
||||
* #OstreeRepoFinderMount:monitor. By default, g_volume_monitor_get() is used.
|
||||
*
|
||||
* Since: 2017.8
|
||||
*/
|
||||
|
||||
typedef GList/*<owned GObject>*/ ObjectList;
|
||||
|
||||
static void
|
||||
object_list_free (ObjectList *list)
|
||||
{
|
||||
g_list_free_full (list, g_object_unref);
|
||||
}
|
||||
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC (ObjectList, object_list_free)
|
||||
|
||||
static void ostree_repo_finder_mount_iface_init (OstreeRepoFinderInterface *iface);
|
||||
|
||||
struct _OstreeRepoFinderMount
|
||||
{
|
||||
GObject parent_instance;
|
||||
|
||||
GVolumeMonitor *monitor; /* owned */
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE_WITH_CODE (OstreeRepoFinderMount, ostree_repo_finder_mount, G_TYPE_OBJECT,
|
||||
G_IMPLEMENT_INTERFACE (OSTREE_TYPE_REPO_FINDER, ostree_repo_finder_mount_iface_init))
|
||||
|
||||
typedef struct
|
||||
{
|
||||
gchar *uri;
|
||||
gchar *keyring;
|
||||
} UriAndKeyring;
|
||||
|
||||
static void
|
||||
uri_and_keyring_free (UriAndKeyring *data)
|
||||
{
|
||||
g_free (data->uri);
|
||||
g_free (data->keyring);
|
||||
g_free (data);
|
||||
}
|
||||
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC (UriAndKeyring, uri_and_keyring_free)
|
||||
|
||||
static UriAndKeyring *
|
||||
uri_and_keyring_new (const gchar *uri,
|
||||
const gchar *keyring)
|
||||
{
|
||||
g_autoptr(UriAndKeyring) data = NULL;
|
||||
|
||||
data = g_new0 (UriAndKeyring, 1);
|
||||
data->uri = g_strdup (uri);
|
||||
data->keyring = g_strdup (keyring);
|
||||
|
||||
return g_steal_pointer (&data);
|
||||
}
|
||||
|
||||
static guint
|
||||
uri_and_keyring_hash (gconstpointer key)
|
||||
{
|
||||
const UriAndKeyring *_key = key;
|
||||
|
||||
return g_str_hash (_key->uri) ^ g_str_hash (_key->keyring);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
uri_and_keyring_equal (gconstpointer a,
|
||||
gconstpointer b)
|
||||
{
|
||||
const UriAndKeyring *_a = a, *_b = b;
|
||||
|
||||
return g_str_equal (_a->uri, _b->uri) && g_str_equal (_a->keyring, _b->keyring);
|
||||
}
|
||||
|
||||
/* This must return a valid remote name (suitable for use in a refspec). */
|
||||
static gchar *
|
||||
uri_and_keyring_to_name (UriAndKeyring *data)
|
||||
{
|
||||
g_autofree gchar *escaped_uri = g_uri_escape_string (data->uri, NULL, FALSE);
|
||||
g_autofree gchar *escaped_keyring = g_uri_escape_string (data->keyring, NULL, FALSE);
|
||||
|
||||
/* FIXME: Need a better separator than `_`, since it’s not escaped in the input. */
|
||||
g_autofree gchar *out = g_strdup_printf ("%s_%s", escaped_uri, escaped_keyring);
|
||||
|
||||
for (gsize i = 0; out[i] != '\0'; i++)
|
||||
{
|
||||
if (out[i] == '%')
|
||||
out[i] = '_';
|
||||
}
|
||||
|
||||
g_return_val_if_fail (ostree_validate_remote_name (out, NULL), NULL);
|
||||
|
||||
return g_steal_pointer (&out);
|
||||
}
|
||||
|
||||
static gint
|
||||
results_compare_cb (gconstpointer a,
|
||||
gconstpointer b)
|
||||
{
|
||||
const OstreeRepoFinderResult *result_a = *((const OstreeRepoFinderResult **) a);
|
||||
const OstreeRepoFinderResult *result_b = *((const OstreeRepoFinderResult **) b);
|
||||
|
||||
return ostree_repo_finder_result_compare (result_a, result_b);
|
||||
}
|
||||
|
||||
static void
|
||||
ostree_repo_finder_mount_resolve_async (OstreeRepoFinder *finder,
|
||||
const OstreeCollectionRef * const *refs,
|
||||
OstreeRepo *parent_repo,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data)
|
||||
{
|
||||
OstreeRepoFinderMount *self = OSTREE_REPO_FINDER_MOUNT (finder);
|
||||
g_autoptr(GTask) task = NULL;
|
||||
g_autoptr(ObjectList) mounts = NULL;
|
||||
g_autoptr(GPtrArray) results = NULL; /* (element-type OstreeRepoFinderResult) */
|
||||
GList *l;
|
||||
const gint priority = 50; /* arbitrarily chosen */
|
||||
|
||||
task = g_task_new (finder, cancellable, callback, user_data);
|
||||
g_task_set_source_tag (task, ostree_repo_finder_mount_resolve_async);
|
||||
|
||||
mounts = g_volume_monitor_get_mounts (self->monitor);
|
||||
results = g_ptr_array_new_with_free_func ((GDestroyNotify) ostree_repo_finder_result_free);
|
||||
|
||||
g_debug ("%s: Found %u mounts", G_STRFUNC, g_list_length (mounts));
|
||||
|
||||
for (l = mounts; l != NULL; l = l->next)
|
||||
{
|
||||
GMount *mount = G_MOUNT (l->data);
|
||||
g_autofree gchar *mount_name = NULL;
|
||||
g_autoptr(GFile) mount_root = NULL;
|
||||
g_autofree gchar *mount_root_path = NULL;
|
||||
glnx_fd_close int mount_root_dfd = -1;
|
||||
struct stat mount_root_stbuf;
|
||||
glnx_fd_close int repos_dfd = -1;
|
||||
gsize i;
|
||||
g_autoptr(GHashTable) repo_to_refs = NULL; /* (element-type UriAndKeyring GHashTable) */
|
||||
GHashTable *supported_ref_to_checksum; /* (element-type OstreeCollectionRef utf8) */
|
||||
GHashTableIter iter;
|
||||
UriAndKeyring *repo;
|
||||
g_autoptr(GError) local_error = NULL;
|
||||
|
||||
mount_name = g_mount_get_name (mount);
|
||||
|
||||
/* Check the mount’s general properties. */
|
||||
if (g_mount_is_shadowed (mount))
|
||||
{
|
||||
g_debug ("Ignoring mount ‘%s’ as it’s shadowed.", mount_name);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Check if it contains a .ostree/repos directory. */
|
||||
mount_root = g_mount_get_root (mount);
|
||||
mount_root_path = g_file_get_path (mount_root);
|
||||
|
||||
if (!glnx_opendirat (AT_FDCWD, mount_root_path, TRUE, &mount_root_dfd, &local_error))
|
||||
{
|
||||
g_debug ("Ignoring mount ‘%s’ as ‘%s’ directory can’t be opened: %s",
|
||||
mount_name, mount_root_path, local_error->message);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!glnx_opendirat (mount_root_dfd, ".ostree/repos", TRUE, &repos_dfd, &local_error))
|
||||
{
|
||||
if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
|
||||
g_debug ("Ignoring mount ‘%s’ as ‘%s/.ostree/repos’ directory doesn’t exist.",
|
||||
mount_name, mount_root_path);
|
||||
else
|
||||
g_debug ("Ignoring mount ‘%s’ as ‘%s/.ostree/repos’ directory can’t be opened: %s",
|
||||
mount_name, mount_root_path, local_error->message);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
/* stat() the mount root so we can later check whether the resolved
|
||||
* repositories for individual refs are on the same device (to avoid the
|
||||
* symlinks for them pointing outside the mount root). */
|
||||
if (!glnx_fstat (mount_root_dfd, &mount_root_stbuf, &local_error))
|
||||
{
|
||||
g_debug ("Ignoring mount ‘%s’ as querying info of ‘%s’ failed: %s",
|
||||
mount_name, mount_root_path, local_error->message);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Check whether a subdirectory exists for any of the @refs we’re looking
|
||||
* for. If so, and it’s a symbolic link, dereference it so multiple links
|
||||
* to the same repository (containing multiple refs) are coalesced.
|
||||
* Otherwise, include it as a result by itself. */
|
||||
repo_to_refs = g_hash_table_new_full (uri_and_keyring_hash, uri_and_keyring_equal,
|
||||
(GDestroyNotify) uri_and_keyring_free, (GDestroyNotify) g_hash_table_unref);
|
||||
|
||||
for (i = 0; refs[i] != NULL; i++)
|
||||
{
|
||||
struct stat stbuf;
|
||||
g_autofree gchar *collection_and_ref = NULL;
|
||||
g_autofree gchar *repo_dir_path = NULL;
|
||||
g_autofree gchar *resolved_repo_uri = NULL;
|
||||
g_autofree gchar *keyring = NULL;
|
||||
g_autoptr(UriAndKeyring) resolved_repo = NULL;
|
||||
|
||||
collection_and_ref = g_build_filename (refs[i]->collection_id, refs[i]->ref_name, NULL);
|
||||
repo_dir_path = g_build_filename (mount_root_path, ".ostree", "repos",
|
||||
collection_and_ref, NULL);
|
||||
|
||||
if (!glnx_fstatat (repos_dfd, collection_and_ref, &stbuf, AT_NO_AUTOMOUNT, &local_error))
|
||||
{
|
||||
g_debug ("Ignoring ref (%s, %s) on mount ‘%s’ as querying info of ‘%s’ failed: %s",
|
||||
refs[i]->collection_id, refs[i]->ref_name, mount_name, repo_dir_path, local_error->message);
|
||||
g_clear_error (&local_error);
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((stbuf.st_mode & S_IFMT) != S_IFDIR)
|
||||
{
|
||||
g_debug ("Ignoring ref (%s, %s) on mount ‘%s’ as ‘%s’ is of type %u, not a directory.",
|
||||
refs[i]->collection_id, refs[i]->ref_name, mount_name, repo_dir_path, (stbuf.st_mode & S_IFMT));
|
||||
g_clear_error (&local_error);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Check the resolved repository path is below the mount point. Do not
|
||||
* allow ref symlinks to point somewhere outside of the mounted
|
||||
* volume. */
|
||||
if (stbuf.st_dev != mount_root_stbuf.st_dev)
|
||||
{
|
||||
g_debug ("Ignoring ref (%s, %s) on mount ‘%s’ as it’s on a different file system from the mount.",
|
||||
refs[i]->collection_id, refs[i]->ref_name, mount_name);
|
||||
g_clear_error (&local_error);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Exclude repositories which resolve to @parent_repo. */
|
||||
g_autofree char *canonical_repo_dir_path = realpath (repo_dir_path, NULL);
|
||||
g_autofree gchar *parent_repo_path = g_file_get_path (ostree_repo_get_path (parent_repo));
|
||||
g_autofree char *canonical_parent_repo_path = realpath (parent_repo_path, NULL);
|
||||
|
||||
if (g_strcmp0 (canonical_repo_dir_path, canonical_parent_repo_path) == 0)
|
||||
{
|
||||
g_debug ("Ignoring ref (%s, %s) on mount ‘%s’ as its repository was the one we are resolving for: %s",
|
||||
refs[i]->collection_id, refs[i]->ref_name, mount_name, canonical_parent_repo_path);
|
||||
g_clear_error (&local_error);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Grab the given ref and a checksum for it from the repo.
|
||||
* FIXME: Ideally, there would be some ostree_repo_open_at() which we
|
||||
* could use to keep the openat() chain going. See
|
||||
* https://github.com/ostreedev/ostree/pull/820. */
|
||||
g_autoptr(OstreeRepo) repo = NULL;
|
||||
g_autoptr(GFile) repo_dir_file = g_file_new_for_path (repo_dir_path);
|
||||
repo = ostree_repo_new (repo_dir_file);
|
||||
|
||||
if (!ostree_repo_open (repo, cancellable, &local_error))
|
||||
{
|
||||
g_debug ("Ignoring ref (%s, %s) on mount ‘%s’ as its repository could not be opened: %s",
|
||||
refs[i]->collection_id, refs[i]->ref_name, mount_name, local_error->message);
|
||||
g_clear_error (&local_error);
|
||||
continue;
|
||||
}
|
||||
|
||||
g_autoptr(GHashTable) repo_refs = NULL; /* (element-type OstreeCollectionRef utf8) */
|
||||
|
||||
if (!ostree_repo_list_collection_refs (repo, refs[i]->collection_id, &repo_refs, cancellable, &local_error))
|
||||
{
|
||||
g_debug ("Ignoring ref (%s, %s) on mount ‘%s’ as its refs could not be listed: %s",
|
||||
refs[i]->collection_id, refs[i]->ref_name, mount_name, local_error->message);
|
||||
g_clear_error (&local_error);
|
||||
continue;
|
||||
}
|
||||
|
||||
const gchar *checksum = g_hash_table_lookup (repo_refs, refs[i]);
|
||||
|
||||
if (checksum == NULL)
|
||||
{
|
||||
g_debug ("Ignoring ref (%s, %s) on mount ‘%s’ as its repository doesn’t contain the ref.",
|
||||
refs[i]->collection_id, refs[i]->ref_name, mount_name);
|
||||
g_clear_error (&local_error);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Finally, look up the GPG keyring for this ref. */
|
||||
keyring = ostree_repo_resolve_keyring_for_collection (parent_repo, refs[i]->collection_id,
|
||||
cancellable, &local_error);
|
||||
|
||||
if (keyring == NULL)
|
||||
{
|
||||
g_debug ("Ignoring ref (%s, %s) on mount ‘%s’ due to missing keyring: %s",
|
||||
refs[i]->collection_id, refs[i]->ref_name, mount_name, local_error->message);
|
||||
g_clear_error (&local_error);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* There is a valid repo at (or pointed to by)
|
||||
* $mount_root/.ostree/repos/$refs[i]->collection_id/$refs[i]->ref_name.
|
||||
* Add it to the results, keyed by the canonicalised repository URI
|
||||
* to deduplicate the results. */
|
||||
resolved_repo_uri = g_strconcat ("file://", canonical_repo_dir_path, NULL);
|
||||
g_debug ("Resolved ref (%s, %s) on mount ‘%s’ to repo URI ‘%s’ with keyring ‘%s’.",
|
||||
refs[i]->collection_id, refs[i]->ref_name, mount_name, resolved_repo_uri, keyring);
|
||||
|
||||
resolved_repo = uri_and_keyring_new (resolved_repo_uri, keyring);
|
||||
|
||||
supported_ref_to_checksum = g_hash_table_lookup (repo_to_refs, resolved_repo);
|
||||
|
||||
if (supported_ref_to_checksum == NULL)
|
||||
{
|
||||
supported_ref_to_checksum = g_hash_table_new_full (ostree_collection_ref_hash,
|
||||
ostree_collection_ref_equal,
|
||||
NULL, g_free);
|
||||
g_hash_table_insert (repo_to_refs, g_steal_pointer (&resolved_repo), supported_ref_to_checksum /* transfer */);
|
||||
}
|
||||
|
||||
g_hash_table_insert (supported_ref_to_checksum, (gpointer) refs[i], g_strdup (checksum));
|
||||
}
|
||||
|
||||
/* Aggregate the results. */
|
||||
g_hash_table_iter_init (&iter, repo_to_refs);
|
||||
|
||||
while (g_hash_table_iter_next (&iter, (gpointer *) &repo, (gpointer *) &supported_ref_to_checksum))
|
||||
{
|
||||
g_autoptr(OstreeRemote) remote = NULL;
|
||||
|
||||
/* Build an #OstreeRemote. Use the escaped URI, since remote->name
|
||||
* is used in file paths, so needs to not contain special characters. */
|
||||
g_autofree gchar *name = uri_and_keyring_to_name (repo);
|
||||
remote = ostree_remote_new (name);
|
||||
|
||||
g_clear_pointer (&remote->keyring, g_free);
|
||||
remote->keyring = g_strdup (repo->keyring);
|
||||
|
||||
g_key_file_set_string (remote->options, remote->group, "url", repo->uri);
|
||||
g_key_file_set_boolean (remote->options, remote->group, "gpg-verify", TRUE);
|
||||
g_key_file_set_boolean (remote->options, remote->group, "gpg-verify-summary", TRUE);
|
||||
|
||||
/* Set the timestamp in the #OstreeRepoFinderResult to 0 because
|
||||
* the code in ostree_repo_pull_from_remotes_async() will be able to
|
||||
* check it just as quickly as we can here; so don’t duplicate the
|
||||
* code. */
|
||||
g_ptr_array_add (results, ostree_repo_finder_result_new (remote, finder, priority, supported_ref_to_checksum, 0));
|
||||
}
|
||||
}
|
||||
|
||||
g_ptr_array_sort (results, results_compare_cb);
|
||||
|
||||
g_task_return_pointer (task, g_steal_pointer (&results), (GDestroyNotify) g_ptr_array_unref);
|
||||
}
|
||||
|
||||
static GPtrArray *
|
||||
ostree_repo_finder_mount_resolve_finish (OstreeRepoFinder *self,
|
||||
GAsyncResult *result,
|
||||
GError **error)
|
||||
{
|
||||
g_return_val_if_fail (g_task_is_valid (result, self), NULL);
|
||||
return g_task_propagate_pointer (G_TASK (result), error);
|
||||
}
|
||||
|
||||
static void
|
||||
ostree_repo_finder_mount_init (OstreeRepoFinderMount *self)
|
||||
{
|
||||
/* Nothing to see here. */
|
||||
}
|
||||
|
||||
static void
|
||||
ostree_repo_finder_mount_constructed (GObject *object)
|
||||
{
|
||||
OstreeRepoFinderMount *self = OSTREE_REPO_FINDER_MOUNT (object);
|
||||
|
||||
G_OBJECT_CLASS (ostree_repo_finder_mount_parent_class)->constructed (object);
|
||||
|
||||
if (self->monitor == NULL)
|
||||
self->monitor = g_volume_monitor_get ();
|
||||
}
|
||||
|
||||
typedef enum
|
||||
{
|
||||
PROP_MONITOR = 1,
|
||||
} OstreeRepoFinderMountProperty;
|
||||
|
||||
static void
|
||||
ostree_repo_finder_mount_get_property (GObject *object,
|
||||
guint property_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
OstreeRepoFinderMount *self = OSTREE_REPO_FINDER_MOUNT (object);
|
||||
|
||||
switch ((OstreeRepoFinderMountProperty) property_id)
|
||||
{
|
||||
case PROP_MONITOR:
|
||||
g_value_set_object (value, self->monitor);
|
||||
break;
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
ostree_repo_finder_mount_set_property (GObject *object,
|
||||
guint property_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
OstreeRepoFinderMount *self = OSTREE_REPO_FINDER_MOUNT (object);
|
||||
|
||||
switch ((OstreeRepoFinderMountProperty) property_id)
|
||||
{
|
||||
case PROP_MONITOR:
|
||||
/* Construct-only. */
|
||||
g_assert (self->monitor == NULL);
|
||||
self->monitor = g_value_dup_object (value);
|
||||
break;
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
ostree_repo_finder_mount_dispose (GObject *object)
|
||||
{
|
||||
OstreeRepoFinderMount *self = OSTREE_REPO_FINDER_MOUNT (object);
|
||||
|
||||
g_clear_object (&self->monitor);
|
||||
|
||||
G_OBJECT_CLASS (ostree_repo_finder_mount_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
ostree_repo_finder_mount_class_init (OstreeRepoFinderMountClass *klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
object_class->get_property = ostree_repo_finder_mount_get_property;
|
||||
object_class->set_property = ostree_repo_finder_mount_set_property;
|
||||
object_class->constructed = ostree_repo_finder_mount_constructed;
|
||||
object_class->dispose = ostree_repo_finder_mount_dispose;
|
||||
|
||||
/**
|
||||
* OstreeRepoFinderMount:monitor:
|
||||
*
|
||||
* Volume monitor to use to look up mounted volumes when queried.
|
||||
*
|
||||
* Since: 2017.8
|
||||
*/
|
||||
g_object_class_install_property (object_class, PROP_MONITOR,
|
||||
g_param_spec_object ("monitor",
|
||||
"Volume Monitor",
|
||||
"Volume monitor to use "
|
||||
"to look up mounted "
|
||||
"volumes when queried.",
|
||||
G_TYPE_VOLUME_MONITOR,
|
||||
G_PARAM_CONSTRUCT_ONLY |
|
||||
G_PARAM_READWRITE |
|
||||
G_PARAM_STATIC_STRINGS));
|
||||
}
|
||||
|
||||
static void
|
||||
ostree_repo_finder_mount_iface_init (OstreeRepoFinderInterface *iface)
|
||||
{
|
||||
iface->resolve_async = ostree_repo_finder_mount_resolve_async;
|
||||
iface->resolve_finish = ostree_repo_finder_mount_resolve_finish;
|
||||
}
|
||||
|
||||
/**
|
||||
* ostree_repo_finder_mount_new:
|
||||
* @monitor: (nullable) (transfer none): volume monitor to use, or %NULL to use
|
||||
* the system default
|
||||
*
|
||||
* Create a new #OstreeRepoFinderMount, using the given @monitor to look up
|
||||
* volumes. If @monitor is %NULL, the monitor from g_volume_monitor_get() will
|
||||
* be used.
|
||||
*
|
||||
* Returns: (transfer full): a new #OstreeRepoFinderMount
|
||||
* Since: 2017.8
|
||||
*/
|
||||
OstreeRepoFinderMount *
|
||||
ostree_repo_finder_mount_new (GVolumeMonitor *monitor)
|
||||
{
|
||||
g_return_val_if_fail (monitor == NULL || G_IS_VOLUME_MONITOR (monitor), NULL);
|
||||
|
||||
return g_object_new (OSTREE_TYPE_REPO_FINDER_MOUNT,
|
||||
"monitor", monitor,
|
||||
NULL);
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Authors:
|
||||
* - Philip Withnall <withnall@endlessm.com>
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <gio/gio.h>
|
||||
#include <glib.h>
|
||||
#include <glib-object.h>
|
||||
|
||||
#include "ostree-repo-finder.h"
|
||||
#include "ostree-types.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define OSTREE_TYPE_REPO_FINDER_MOUNT (ostree_repo_finder_mount_get_type ())
|
||||
|
||||
/* Manually expanded version of the following, omitting autoptr support (for GLib < 2.44):
|
||||
_OSTREE_PUBLIC
|
||||
G_DECLARE_FINAL_TYPE (OstreeRepoFinderMount, ostree_repo_finder_mount, OSTREE, REPO_FINDER_MOUNT, GObject) */
|
||||
|
||||
_OSTREE_PUBLIC
|
||||
GType ostree_repo_finder_mount_get_type (void);
|
||||
|
||||
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
|
||||
typedef struct _OstreeRepoFinderMount OstreeRepoFinderMount;
|
||||
typedef struct { GObjectClass parent_class; } OstreeRepoFinderMountClass;
|
||||
|
||||
static inline OstreeRepoFinderMount *OSTREE_REPO_FINDER_MOUNT (gpointer ptr) { return G_TYPE_CHECK_INSTANCE_CAST (ptr, ostree_repo_finder_mount_get_type (), OstreeRepoFinderMount); }
|
||||
static inline gboolean OSTREE_IS_REPO_FINDER_MOUNT (gpointer ptr) { return G_TYPE_CHECK_INSTANCE_TYPE (ptr, ostree_repo_finder_mount_get_type ()); }
|
||||
G_GNUC_END_IGNORE_DEPRECATIONS
|
||||
|
||||
_OSTREE_PUBLIC
|
||||
OstreeRepoFinderMount *ostree_repo_finder_mount_new (GVolumeMonitor *monitor);
|
||||
|
||||
G_END_DECLS
|
||||
@@ -0,0 +1,576 @@
|
||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Authors:
|
||||
* - Philip Withnall <withnall@endlessm.com>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <gio/gio.h>
|
||||
#include <glib.h>
|
||||
#include <glib-object.h>
|
||||
|
||||
#include "ostree-autocleanups.h"
|
||||
#include "ostree-core.h"
|
||||
#include "ostree-remote-private.h"
|
||||
#include "ostree-repo-finder.h"
|
||||
#include "ostree-repo.h"
|
||||
|
||||
static void ostree_repo_finder_default_init (OstreeRepoFinderInterface *iface);
|
||||
|
||||
G_DEFINE_INTERFACE (OstreeRepoFinder, ostree_repo_finder, G_TYPE_OBJECT)
|
||||
|
||||
static void
|
||||
ostree_repo_finder_default_init (OstreeRepoFinderInterface *iface)
|
||||
{
|
||||
/* Nothing to see here. */
|
||||
}
|
||||
|
||||
/* Validate the given struct contains a valid collection ID and ref name, and that
|
||||
* the collection ID is non-%NULL. */
|
||||
static gboolean
|
||||
is_valid_collection_ref (const OstreeCollectionRef *ref)
|
||||
{
|
||||
return (ref != NULL &&
|
||||
ostree_validate_rev (ref->ref_name, NULL) &&
|
||||
ostree_validate_collection_id (ref->collection_id, NULL));
|
||||
}
|
||||
|
||||
/* Validate @refs is non-%NULL, non-empty, and contains only valid collection
|
||||
* and ref names. */
|
||||
static gboolean
|
||||
is_valid_collection_ref_array (const OstreeCollectionRef * const *refs)
|
||||
{
|
||||
gsize i;
|
||||
|
||||
if (refs == NULL || *refs == NULL)
|
||||
return FALSE;
|
||||
|
||||
for (i = 0; refs[i] != NULL; i++)
|
||||
{
|
||||
if (!is_valid_collection_ref (refs[i]))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* Validate @ref_to_checksum is non-%NULL, non-empty, and contains only valid
|
||||
* OstreeCollectionRefs as keys and only valid commit checksums as values. */
|
||||
static gboolean
|
||||
is_valid_collection_ref_map (GHashTable *ref_to_checksum)
|
||||
{
|
||||
GHashTableIter iter;
|
||||
const OstreeCollectionRef *ref;
|
||||
const gchar *checksum;
|
||||
|
||||
if (ref_to_checksum == NULL || g_hash_table_size (ref_to_checksum) == 0)
|
||||
return FALSE;
|
||||
|
||||
g_hash_table_iter_init (&iter, ref_to_checksum);
|
||||
|
||||
while (g_hash_table_iter_next (&iter, (gpointer *) &ref, (gpointer *) &checksum))
|
||||
{
|
||||
g_assert (ref != NULL);
|
||||
g_assert (checksum != NULL);
|
||||
|
||||
if (!is_valid_collection_ref (ref))
|
||||
return FALSE;
|
||||
if (!ostree_validate_checksum_string (checksum, NULL))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void resolve_cb (GObject *obj,
|
||||
GAsyncResult *result,
|
||||
gpointer user_data);
|
||||
|
||||
/**
|
||||
* ostree_repo_finder_resolve_async:
|
||||
* @self: an #OstreeRepoFinder
|
||||
* @refs: (array zero-terminated=1): non-empty array of collection–ref pairs to find remotes for
|
||||
* @parent_repo: (transfer none): the local repository which the refs are being resolved for,
|
||||
* which provides configuration information and GPG keys
|
||||
* @cancellable: (nullable): a #GCancellable, or %NULL
|
||||
* @callback: asynchronous completion callback
|
||||
* @user_data: data to pass to @callback
|
||||
*
|
||||
* Find reachable remote URIs which claim to provide any of the given @refs. The
|
||||
* specific method for finding the remotes depends on the #OstreeRepoFinder
|
||||
* implementation.
|
||||
*
|
||||
* Any remote which is found and which claims to support any of the given @refs
|
||||
* will be returned in the results. It is possible that a remote claims to
|
||||
* support a given ref, but turns out not to — it is not possible to verify this
|
||||
* until ostree_repo_pull_from_remotes_async() is called.
|
||||
*
|
||||
* The returned results will be sorted with the most useful first — this is
|
||||
* typically the remote which claims to provide the most @refs, at the lowest
|
||||
* latency.
|
||||
*
|
||||
* Each result contains a mapping of @refs to the checksums of the commits
|
||||
* which the result provides. If the result provides the latest commit for a ref
|
||||
* across all of the results, the checksum will be set. Otherwise, if the
|
||||
* result provides an outdated commit, or doesn’t provide a given ref at all,
|
||||
* the ref will not be set. Results which provide none of the requested @refs
|
||||
* may be listed with an empty refs map.
|
||||
*
|
||||
* Pass the results to ostree_repo_pull_from_remotes_async() to pull the given
|
||||
* @refs from those remotes.
|
||||
*
|
||||
* Since: 2017.8
|
||||
*/
|
||||
void
|
||||
ostree_repo_finder_resolve_async (OstreeRepoFinder *self,
|
||||
const OstreeCollectionRef * const *refs,
|
||||
OstreeRepo *parent_repo,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data)
|
||||
{
|
||||
g_autoptr(GTask) task = NULL;
|
||||
OstreeRepoFinder *finders[2] = { NULL, };
|
||||
|
||||
g_return_if_fail (OSTREE_IS_REPO_FINDER (self));
|
||||
g_return_if_fail (is_valid_collection_ref_array (refs));
|
||||
g_return_if_fail (OSTREE_IS_REPO (parent_repo));
|
||||
g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
|
||||
|
||||
task = g_task_new (self, cancellable, callback, user_data);
|
||||
g_task_set_source_tag (task, ostree_repo_finder_resolve_async);
|
||||
|
||||
finders[0] = self;
|
||||
|
||||
ostree_repo_finder_resolve_all_async (finders, refs, parent_repo, cancellable,
|
||||
resolve_cb, g_steal_pointer (&task));
|
||||
}
|
||||
|
||||
static void
|
||||
resolve_cb (GObject *obj,
|
||||
GAsyncResult *result,
|
||||
gpointer user_data)
|
||||
{
|
||||
g_autoptr(GTask) task = NULL;
|
||||
g_autoptr(GPtrArray) results = NULL;
|
||||
g_autoptr(GError) local_error = NULL;
|
||||
|
||||
task = G_TASK (user_data);
|
||||
|
||||
results = ostree_repo_finder_resolve_all_finish (result, &local_error);
|
||||
|
||||
g_assert ((local_error == NULL) != (results == NULL));
|
||||
|
||||
if (local_error != NULL)
|
||||
g_task_return_error (task, g_steal_pointer (&local_error));
|
||||
else
|
||||
g_task_return_pointer (task, g_steal_pointer (&results), (GDestroyNotify) g_ptr_array_unref);
|
||||
}
|
||||
|
||||
/**
|
||||
* ostree_repo_finder_resolve_finish:
|
||||
* @self: an #OstreeRepoFinder
|
||||
* @result: #GAsyncResult from the callback
|
||||
* @error: return location for a #GError
|
||||
*
|
||||
* Get the results from a ostree_repo_finder_resolve_async() operation.
|
||||
*
|
||||
* Returns: (transfer full) (element-type OstreeRepoFinderResult): array of zero
|
||||
* or more results
|
||||
* Since: 2017.8
|
||||
*/
|
||||
GPtrArray *
|
||||
ostree_repo_finder_resolve_finish (OstreeRepoFinder *self,
|
||||
GAsyncResult *result,
|
||||
GError **error)
|
||||
{
|
||||
g_return_val_if_fail (OSTREE_IS_REPO_FINDER (self), NULL);
|
||||
g_return_val_if_fail (g_task_is_valid (result, self), NULL);
|
||||
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
|
||||
|
||||
return g_task_propagate_pointer (G_TASK (result), error);
|
||||
}
|
||||
|
||||
static gint
|
||||
sort_results_cb (gconstpointer a,
|
||||
gconstpointer b)
|
||||
{
|
||||
const OstreeRepoFinderResult *result_a = *((const OstreeRepoFinderResult **) a);
|
||||
const OstreeRepoFinderResult *result_b = *((const OstreeRepoFinderResult **) b);
|
||||
|
||||
return ostree_repo_finder_result_compare (result_a, result_b);
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
gsize n_finders_pending;
|
||||
GPtrArray *results;
|
||||
} ResolveAllData;
|
||||
|
||||
static void
|
||||
resolve_all_data_free (ResolveAllData *data)
|
||||
{
|
||||
g_assert (data->n_finders_pending == 0);
|
||||
g_clear_pointer (&data->results, g_ptr_array_unref);
|
||||
g_free (data);
|
||||
}
|
||||
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC (ResolveAllData, resolve_all_data_free)
|
||||
|
||||
static void resolve_all_cb (GObject *obj,
|
||||
GAsyncResult *result,
|
||||
gpointer user_data);
|
||||
static void resolve_all_finished_one (GTask *task);
|
||||
|
||||
/**
|
||||
* ostree_repo_finder_resolve_all_async:
|
||||
* @finders: (array zero-terminated=1): non-empty array of #OstreeRepoFinders
|
||||
* @refs: (array zero-terminated=1): non-empty array of collection–ref pairs to find remotes for
|
||||
* @parent_repo: (transfer none): the local repository which the refs are being resolved for,
|
||||
* which provides configuration information and GPG keys
|
||||
* @cancellable: (nullable): a #GCancellable, or %NULL
|
||||
* @callback: asynchronous completion callback
|
||||
* @user_data: data to pass to @callback
|
||||
*
|
||||
* A version of ostree_repo_finder_resolve_async() which queries one or more
|
||||
* @finders in parallel and combines the results.
|
||||
*
|
||||
* Since: 2017.8
|
||||
*/
|
||||
void
|
||||
ostree_repo_finder_resolve_all_async (OstreeRepoFinder * const *finders,
|
||||
const OstreeCollectionRef * const *refs,
|
||||
OstreeRepo *parent_repo,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data)
|
||||
{
|
||||
g_autoptr(GTask) task = NULL;
|
||||
g_autoptr(ResolveAllData) data = NULL;
|
||||
gsize i;
|
||||
g_autoptr(GString) refs_str = NULL;
|
||||
g_autoptr(GString) finders_str = NULL;
|
||||
|
||||
g_return_if_fail (finders != NULL && finders[0] != NULL);
|
||||
g_return_if_fail (is_valid_collection_ref_array (refs));
|
||||
g_return_if_fail (OSTREE_IS_REPO (parent_repo));
|
||||
g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
|
||||
|
||||
refs_str = g_string_new ("");
|
||||
for (i = 0; refs[i] != NULL; i++)
|
||||
{
|
||||
if (i != 0)
|
||||
g_string_append (refs_str, ", ");
|
||||
g_string_append_printf (refs_str, "(%s, %s)",
|
||||
refs[i]->collection_id, refs[i]->ref_name);
|
||||
}
|
||||
|
||||
finders_str = g_string_new ("");
|
||||
for (i = 0; finders[i] != NULL; i++)
|
||||
{
|
||||
if (i != 0)
|
||||
g_string_append (finders_str, ", ");
|
||||
g_string_append (finders_str, g_type_name (G_TYPE_FROM_INSTANCE (finders[i])));
|
||||
}
|
||||
|
||||
g_debug ("%s: Resolving refs [%s] with finders [%s]", G_STRFUNC,
|
||||
refs_str->str, finders_str->str);
|
||||
|
||||
task = g_task_new (NULL, cancellable, callback, user_data);
|
||||
g_task_set_source_tag (task, ostree_repo_finder_resolve_all_async);
|
||||
|
||||
data = g_new0 (ResolveAllData, 1);
|
||||
data->n_finders_pending = 1; /* while setting up the loop */
|
||||
data->results = g_ptr_array_new_with_free_func ((GDestroyNotify) ostree_repo_finder_result_free);
|
||||
g_task_set_task_data (task, data, (GDestroyNotify) resolve_all_data_free);
|
||||
|
||||
/* Start all the asynchronous queries in parallel. */
|
||||
for (i = 0; finders[i] != NULL; i++)
|
||||
{
|
||||
OstreeRepoFinder *finder = OSTREE_REPO_FINDER (finders[i]);
|
||||
OstreeRepoFinderInterface *iface;
|
||||
|
||||
iface = OSTREE_REPO_FINDER_GET_IFACE (finder);
|
||||
g_assert (iface->resolve_async != NULL);
|
||||
iface->resolve_async (finder, refs, parent_repo, cancellable, resolve_all_cb, g_object_ref (task));
|
||||
data->n_finders_pending++;
|
||||
}
|
||||
|
||||
resolve_all_finished_one (task);
|
||||
data = NULL; /* passed to the GTask above */
|
||||
}
|
||||
|
||||
/* Modifies both arrays in place. */
|
||||
static void
|
||||
array_concatenate_steal (GPtrArray *array,
|
||||
GPtrArray *to_concatenate) /* (transfer full) */
|
||||
{
|
||||
g_autoptr(GPtrArray) array_to_concatenate = to_concatenate;
|
||||
gsize i;
|
||||
|
||||
for (i = 0; i < array_to_concatenate->len; i++)
|
||||
{
|
||||
/* Sanity check that the arrays do not contain any %NULL elements
|
||||
* (particularly NULL terminators). */
|
||||
g_assert (g_ptr_array_index (array_to_concatenate, i) != NULL);
|
||||
g_ptr_array_add (array, g_steal_pointer (&g_ptr_array_index (array_to_concatenate, i)));
|
||||
}
|
||||
|
||||
g_ptr_array_set_free_func (array_to_concatenate, NULL);
|
||||
g_ptr_array_set_size (array_to_concatenate, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
resolve_all_cb (GObject *obj,
|
||||
GAsyncResult *result,
|
||||
gpointer user_data)
|
||||
{
|
||||
OstreeRepoFinder *finder;
|
||||
OstreeRepoFinderInterface *iface;
|
||||
g_autoptr(GTask) task = NULL;
|
||||
g_autoptr(GPtrArray) results = NULL;
|
||||
g_autoptr(GError) local_error = NULL;
|
||||
ResolveAllData *data;
|
||||
|
||||
finder = OSTREE_REPO_FINDER (obj);
|
||||
iface = OSTREE_REPO_FINDER_GET_IFACE (finder);
|
||||
task = G_TASK (user_data);
|
||||
data = g_task_get_task_data (task);
|
||||
results = iface->resolve_finish (finder, result, &local_error);
|
||||
|
||||
g_assert ((local_error == NULL) != (results == NULL));
|
||||
|
||||
if (local_error != NULL)
|
||||
g_debug ("Error resolving refs to repository URI using %s: %s",
|
||||
g_type_name (G_TYPE_FROM_INSTANCE (finder)), local_error->message);
|
||||
else
|
||||
array_concatenate_steal (data->results, g_steal_pointer (&results));
|
||||
|
||||
resolve_all_finished_one (task);
|
||||
}
|
||||
|
||||
static void
|
||||
resolve_all_finished_one (GTask *task)
|
||||
{
|
||||
ResolveAllData *data;
|
||||
|
||||
data = g_task_get_task_data (task);
|
||||
|
||||
data->n_finders_pending--;
|
||||
|
||||
if (data->n_finders_pending == 0)
|
||||
{
|
||||
gsize i;
|
||||
g_autoptr(GString) results_str = NULL;
|
||||
|
||||
g_ptr_array_sort (data->results, sort_results_cb);
|
||||
|
||||
results_str = g_string_new ("");
|
||||
for (i = 0; i < data->results->len; i++)
|
||||
{
|
||||
const OstreeRepoFinderResult *result = g_ptr_array_index (data->results, i);
|
||||
|
||||
if (i != 0)
|
||||
g_string_append (results_str, ", ");
|
||||
g_string_append (results_str, ostree_remote_get_name (result->remote));
|
||||
}
|
||||
if (i == 0)
|
||||
g_string_append (results_str, "(none)");
|
||||
|
||||
g_debug ("%s: Finished, results: %s", G_STRFUNC, results_str->str);
|
||||
|
||||
g_task_return_pointer (task, g_steal_pointer (&data->results), (GDestroyNotify) g_ptr_array_unref);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ostree_repo_finder_resolve_all_finish:
|
||||
* @result: #GAsyncResult from the callback
|
||||
* @error: return location for a #GError
|
||||
*
|
||||
* Get the results from a ostree_repo_finder_resolve_all_async() operation.
|
||||
*
|
||||
* Returns: (transfer full) (element-type OstreeRepoFinderResult): array of zero
|
||||
* or more results
|
||||
* Since: 2017.8
|
||||
*/
|
||||
GPtrArray *
|
||||
ostree_repo_finder_resolve_all_finish (GAsyncResult *result,
|
||||
GError **error)
|
||||
{
|
||||
g_return_val_if_fail (g_task_is_valid (result, NULL), NULL);
|
||||
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
|
||||
|
||||
return g_task_propagate_pointer (G_TASK (result), error);
|
||||
}
|
||||
|
||||
G_DEFINE_BOXED_TYPE (OstreeRepoFinderResult, ostree_repo_finder_result,
|
||||
ostree_repo_finder_result_dup, ostree_repo_finder_result_free)
|
||||
|
||||
/**
|
||||
* ostree_repo_finder_result_new:
|
||||
* @remote: (transfer none): an #OstreeRemote containing the transport details
|
||||
* for the result
|
||||
* @finder: (transfer none): the #OstreeRepoFinder instance which produced the
|
||||
* result
|
||||
* @priority: static priority of the result, where higher numbers indicate lower
|
||||
* priority
|
||||
* @ref_to_checksum: (element-type OstreeCollectionRef utf8): map of collection–ref pairs
|
||||
* to checksums provided by this result
|
||||
* @summary_last_modified: Unix timestamp (seconds since the epoch, UTC) when
|
||||
* the summary file for the result was last modified, or `0` if this is unknown
|
||||
*
|
||||
* Create a new #OstreeRepoFinderResult instance. The semantics for the arguments
|
||||
* are as described in the #OstreeRepoFinderResult documentation.
|
||||
*
|
||||
* Returns: (transfer full): a new #OstreeRepoFinderResult
|
||||
* Since: 2017.8
|
||||
*/
|
||||
OstreeRepoFinderResult *
|
||||
ostree_repo_finder_result_new (OstreeRemote *remote,
|
||||
OstreeRepoFinder *finder,
|
||||
gint priority,
|
||||
GHashTable *ref_to_checksum,
|
||||
guint64 summary_last_modified)
|
||||
{
|
||||
g_autoptr(OstreeRepoFinderResult) result = NULL;
|
||||
|
||||
g_return_val_if_fail (remote != NULL, NULL);
|
||||
g_return_val_if_fail (OSTREE_IS_REPO_FINDER (finder), NULL);
|
||||
g_return_val_if_fail (is_valid_collection_ref_map (ref_to_checksum), NULL);
|
||||
|
||||
result = g_new0 (OstreeRepoFinderResult, 1);
|
||||
result->remote = ostree_remote_ref (remote);
|
||||
result->finder = g_object_ref (finder);
|
||||
result->priority = priority;
|
||||
result->ref_to_checksum = g_hash_table_ref (ref_to_checksum);
|
||||
result->summary_last_modified = summary_last_modified;
|
||||
|
||||
return g_steal_pointer (&result);
|
||||
}
|
||||
|
||||
/**
|
||||
* ostree_repo_finder_result_dup:
|
||||
* @result: (transfer none): an #OstreeRepoFinderResult to copy
|
||||
*
|
||||
* Copy an #OstreeRepoFinderResult.
|
||||
*
|
||||
* Returns: (transfer full): a newly allocated copy of @result
|
||||
* Since: 2017.8
|
||||
*/
|
||||
OstreeRepoFinderResult *
|
||||
ostree_repo_finder_result_dup (OstreeRepoFinderResult *result)
|
||||
{
|
||||
g_return_val_if_fail (result != NULL, NULL);
|
||||
|
||||
return ostree_repo_finder_result_new (result->remote, result->finder,
|
||||
result->priority, result->ref_to_checksum,
|
||||
result->summary_last_modified);
|
||||
}
|
||||
|
||||
/**
|
||||
* ostree_repo_finder_result_compare:
|
||||
* @a: an #OstreeRepoFinderResult
|
||||
* @b: an #OstreeRepoFinderResult
|
||||
*
|
||||
* Compare two #OstreeRepoFinderResult instances to work out which one is better
|
||||
* to pull from, and hence needs to be ordered before the other.
|
||||
*
|
||||
* Returns: <0 if @a is ordered before @b, 0 if they are ordered equally,
|
||||
* >0 if @b is ordered before @a
|
||||
* Since: 2017.8
|
||||
*/
|
||||
gint
|
||||
ostree_repo_finder_result_compare (const OstreeRepoFinderResult *a,
|
||||
const OstreeRepoFinderResult *b)
|
||||
{
|
||||
guint a_n_refs, b_n_refs;
|
||||
|
||||
g_return_val_if_fail (a != NULL, 0);
|
||||
g_return_val_if_fail (b != NULL, 0);
|
||||
|
||||
/* FIXME: Check if this is really the ordering we want. For example, we
|
||||
* probably don’t want a result with 0 refs to be ordered before one with >0
|
||||
* refs, just because its priority is higher. */
|
||||
if (a->priority != b->priority)
|
||||
return a->priority - b->priority;
|
||||
|
||||
if (a->summary_last_modified != 0 && b->summary_last_modified != 0 &&
|
||||
a->summary_last_modified != b->summary_last_modified)
|
||||
return a->summary_last_modified - b->summary_last_modified;
|
||||
|
||||
gpointer value;
|
||||
GHashTableIter iter;
|
||||
a_n_refs = b_n_refs = 0;
|
||||
|
||||
g_hash_table_iter_init (&iter, a->ref_to_checksum);
|
||||
while (g_hash_table_iter_next (&iter, NULL, &value))
|
||||
if (value != NULL)
|
||||
a_n_refs++;
|
||||
|
||||
g_hash_table_iter_init (&iter, b->ref_to_checksum);
|
||||
while (g_hash_table_iter_next (&iter, NULL, &value))
|
||||
if (value != NULL)
|
||||
b_n_refs++;
|
||||
|
||||
if (a_n_refs != b_n_refs)
|
||||
return (gint) a_n_refs - (gint) b_n_refs;
|
||||
|
||||
return g_strcmp0 (a->remote->name, b->remote->name);
|
||||
}
|
||||
|
||||
/**
|
||||
* ostree_repo_finder_result_free:
|
||||
* @result: (transfer full): an #OstreeRepoFinderResult
|
||||
*
|
||||
* Free the given @result.
|
||||
*
|
||||
* Since: 2017.8
|
||||
*/
|
||||
void
|
||||
ostree_repo_finder_result_free (OstreeRepoFinderResult *result)
|
||||
{
|
||||
g_return_if_fail (result != NULL);
|
||||
|
||||
g_hash_table_unref (result->ref_to_checksum);
|
||||
g_object_unref (result->finder);
|
||||
ostree_remote_unref (result->remote);
|
||||
g_free (result);
|
||||
}
|
||||
|
||||
/**
|
||||
* ostree_repo_finder_result_freev:
|
||||
* @results: (array zero-terminated=1) (transfer full): an #OstreeRepoFinderResult
|
||||
*
|
||||
* Free the given @results array, freeing each element and the container.
|
||||
*
|
||||
* Since: 2017.8
|
||||
*/
|
||||
void
|
||||
ostree_repo_finder_result_freev (OstreeRepoFinderResult **results)
|
||||
{
|
||||
gsize i;
|
||||
|
||||
for (i = 0; results[i] != NULL; i++)
|
||||
ostree_repo_finder_result_free (results[i]);
|
||||
|
||||
g_free (results);
|
||||
}
|
||||
@@ -0,0 +1,172 @@
|
||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Authors:
|
||||
* - Philip Withnall <withnall@endlessm.com>
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <gio/gio.h>
|
||||
#include <glib.h>
|
||||
#include <glib-object.h>
|
||||
|
||||
#include "ostree-ref.h"
|
||||
#include "ostree-remote.h"
|
||||
#include "ostree-types.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define OSTREE_TYPE_REPO_FINDER (ostree_repo_finder_get_type ())
|
||||
|
||||
/* Manually expanded version of the following, omitting autoptr support (for GLib < 2.44):
|
||||
_OSTREE_PUBLIC
|
||||
G_DECLARE_INTERFACE (OstreeRepoFinder, ostree_repo_finder, OSTREE, REPO_FINDER, GObject) */
|
||||
|
||||
_OSTREE_PUBLIC
|
||||
GType ostree_repo_finder_get_type (void);
|
||||
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
|
||||
typedef struct _OstreeRepoFinder OstreeRepoFinder;
|
||||
typedef struct _OstreeRepoFinderInterface OstreeRepoFinderInterface;
|
||||
|
||||
static inline OstreeRepoFinder *OSTREE_REPO_FINDER (gpointer ptr) { return G_TYPE_CHECK_INSTANCE_CAST (ptr, ostree_repo_finder_get_type (), OstreeRepoFinder); }
|
||||
static inline gboolean OSTREE_IS_REPO_FINDER (gpointer ptr) { return G_TYPE_CHECK_INSTANCE_TYPE (ptr, ostree_repo_finder_get_type ()); }
|
||||
static inline OstreeRepoFinderInterface *OSTREE_REPO_FINDER_GET_IFACE (gpointer ptr) { return G_TYPE_INSTANCE_GET_INTERFACE (ptr, ostree_repo_finder_get_type (), OstreeRepoFinderInterface); }
|
||||
G_GNUC_END_IGNORE_DEPRECATIONS
|
||||
|
||||
struct _OstreeRepoFinderInterface
|
||||
{
|
||||
GTypeInterface g_iface;
|
||||
|
||||
void (*resolve_async) (OstreeRepoFinder *self,
|
||||
const OstreeCollectionRef * const *refs,
|
||||
OstreeRepo *parent_repo,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data);
|
||||
GPtrArray *(*resolve_finish) (OstreeRepoFinder *self,
|
||||
GAsyncResult *result,
|
||||
GError **error);
|
||||
};
|
||||
|
||||
_OSTREE_PUBLIC
|
||||
void ostree_repo_finder_resolve_async (OstreeRepoFinder *self,
|
||||
const OstreeCollectionRef * const *refs,
|
||||
OstreeRepo *parent_repo,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data);
|
||||
_OSTREE_PUBLIC
|
||||
GPtrArray *ostree_repo_finder_resolve_finish (OstreeRepoFinder *self,
|
||||
GAsyncResult *result,
|
||||
GError **error);
|
||||
|
||||
_OSTREE_PUBLIC
|
||||
void ostree_repo_finder_resolve_all_async (OstreeRepoFinder * const *finders,
|
||||
const OstreeCollectionRef * const *refs,
|
||||
OstreeRepo *parent_repo,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data);
|
||||
_OSTREE_PUBLIC
|
||||
GPtrArray *ostree_repo_finder_resolve_all_finish (GAsyncResult *result,
|
||||
GError **error);
|
||||
|
||||
/**
|
||||
* OstreeRepoFinderResult:
|
||||
* @remote: #OstreeRemote which contains the transport details for the result,
|
||||
* such as its URI and GPG key
|
||||
* @finder: the #OstreeRepoFinder instance which produced this result
|
||||
* @priority: static priority of the result, where higher numbers indicate lower
|
||||
* priority
|
||||
* @ref_to_checksum: (element-type OstreeCollectionRef utf8): map of collection–ref
|
||||
* pairs to checksums provided by this remote; values may be %NULL to
|
||||
* indicate this remote doesn’t provide that ref
|
||||
* @summary_last_modified: Unix timestamp (seconds since the epoch, UTC) when
|
||||
* the summary file on the remote was last modified, or `0` if unknown
|
||||
*
|
||||
* #OstreeRepoFinderResult gives a single result from an
|
||||
* ostree_repo_finder_resolve_async() or ostree_repo_finder_resolve_all_async()
|
||||
* operation. This represents a single remote which provides none, some or all
|
||||
* of the refs being resolved. The structure includes various bits of metadata
|
||||
* which allow ostree_repo_pull_from_remotes_async() (for example) to prioritise
|
||||
* how to pull the refs.
|
||||
*
|
||||
* The @priority is used as one input of many to ordering functions like
|
||||
* ostree_repo_finder_result_compare().
|
||||
*
|
||||
* @ref_to_checksum indicates which refs (out of the ones queried for as inputs
|
||||
* to ostree_repo_finder_resolve_async()) are provided by this remote. The refs
|
||||
* are present as keys (of type #OstreeCollectionRef), and the corresponding values
|
||||
* are the checksums of the commits the remote currently has for those refs. (These
|
||||
* might not be the latest commits available out of all results.) A
|
||||
* checksum may be %NULL if the remote does not advertise the corresponding ref.
|
||||
* After ostree_repo_finder_resolve_async() has been called, the commit metadata
|
||||
* should be available locally, so the details for each checksum can be looked
|
||||
* up using ostree_repo_load_commit().
|
||||
*
|
||||
* Since: 2017.8
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
OstreeRemote *remote;
|
||||
OstreeRepoFinder *finder;
|
||||
gint priority;
|
||||
GHashTable *ref_to_checksum;
|
||||
guint64 summary_last_modified;
|
||||
|
||||
/*< private >*/
|
||||
gpointer padding[4];
|
||||
} OstreeRepoFinderResult;
|
||||
|
||||
_OSTREE_PUBLIC
|
||||
GType ostree_repo_finder_result_get_type (void);
|
||||
|
||||
_OSTREE_PUBLIC
|
||||
OstreeRepoFinderResult *ostree_repo_finder_result_new (OstreeRemote *remote,
|
||||
OstreeRepoFinder *finder,
|
||||
gint priority,
|
||||
GHashTable *ref_to_checksum,
|
||||
guint64 summary_last_modified);
|
||||
_OSTREE_PUBLIC
|
||||
OstreeRepoFinderResult *ostree_repo_finder_result_dup (OstreeRepoFinderResult *result);
|
||||
_OSTREE_PUBLIC
|
||||
gint ostree_repo_finder_result_compare (const OstreeRepoFinderResult *a,
|
||||
const OstreeRepoFinderResult *b);
|
||||
_OSTREE_PUBLIC
|
||||
void ostree_repo_finder_result_free (OstreeRepoFinderResult *result);
|
||||
|
||||
/**
|
||||
* OstreeRepoFinderResultv:
|
||||
*
|
||||
* A %NULL-terminated array of #OstreeRepoFinderResult instances, designed to
|
||||
* be used with g_auto():
|
||||
*
|
||||
* |[<!-- language="C" -->
|
||||
* g_auto(OstreeRepoFinderResultv) results = NULL;
|
||||
* ]|
|
||||
*
|
||||
* Since: 2017.8
|
||||
*/
|
||||
typedef OstreeRepoFinderResult** OstreeRepoFinderResultv;
|
||||
|
||||
_OSTREE_PUBLIC
|
||||
void ostree_repo_finder_result_freev (OstreeRepoFinderResult **results);
|
||||
|
||||
G_END_DECLS
|
||||
@@ -126,33 +126,24 @@ squash_trailing_slashes (char *path)
|
||||
static GFileInfo *
|
||||
file_info_from_archive_entry (struct archive_entry *entry)
|
||||
{
|
||||
g_autoptr(GFileInfo) info = NULL;
|
||||
const struct stat *st = NULL;
|
||||
guint32 file_type;
|
||||
mode_t mode;
|
||||
|
||||
st = archive_entry_stat (entry);
|
||||
mode = st->st_mode;
|
||||
const struct stat *st = archive_entry_stat (entry);
|
||||
struct stat st_copy;
|
||||
|
||||
/* Some archives only store the permission mode bits in hardlink entries, so
|
||||
* let's just make it into a regular file. Yes, this hack will work even if
|
||||
* it's a hardlink to a symlink. */
|
||||
if (archive_entry_hardlink (entry))
|
||||
mode |= S_IFREG;
|
||||
|
||||
info = _ostree_header_gfile_info_new (mode, st->st_uid, st->st_gid);
|
||||
|
||||
file_type = ot_gfile_type_for_mode (mode);
|
||||
if (file_type == G_FILE_TYPE_REGULAR)
|
||||
{
|
||||
g_file_info_set_attribute_uint64 (info, "standard::size", st->st_size);
|
||||
}
|
||||
else if (file_type == G_FILE_TYPE_SYMBOLIC_LINK)
|
||||
{
|
||||
g_file_info_set_attribute_byte_string (info, "standard::symlink-target",
|
||||
archive_entry_symlink (entry));
|
||||
st_copy = *st;
|
||||
st_copy.st_mode |= S_IFREG;
|
||||
st = &st_copy;
|
||||
}
|
||||
|
||||
g_autoptr(GFileInfo) info = _ostree_stbuf_to_gfileinfo (st);
|
||||
if (S_ISLNK (st->st_mode))
|
||||
g_file_info_set_attribute_byte_string (info, "standard::symlink-target",
|
||||
archive_entry_symlink (entry));
|
||||
|
||||
return g_steal_pointer (&info);
|
||||
}
|
||||
|
||||
@@ -773,14 +764,11 @@ aic_import_deferred_hardlinks (OstreeRepoArchiveImportContext *ctx,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
GHashTableIter iter;
|
||||
gpointer key, value;
|
||||
|
||||
g_hash_table_iter_init (&iter, ctx->deferred_hardlinks);
|
||||
while (g_hash_table_iter_next (&iter, &key, &value))
|
||||
if (!aic_import_deferred_hardlinks_for (ctx, key, value, error))
|
||||
return FALSE;
|
||||
|
||||
GLNX_HASH_TABLE_FOREACH_KV (ctx->deferred_hardlinks, const char*, target, GSList*, links)
|
||||
{
|
||||
if (!aic_import_deferred_hardlinks_for (ctx, target, links, error))
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
@@ -20,9 +20,11 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <sys/statvfs.h>
|
||||
#include "ostree-ref.h"
|
||||
#include "ostree-repo.h"
|
||||
#include "ostree-remote-private.h"
|
||||
#include "libglnx.h"
|
||||
#include "otutil.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
@@ -47,6 +49,8 @@ G_BEGIN_DECLS
|
||||
/* 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"
|
||||
|
||||
/* Well-known keys for the additional metadata field in a commit in a ref entry
|
||||
* in a summary file. */
|
||||
@@ -95,9 +99,13 @@ struct OstreeRepo {
|
||||
GFile *sysroot_dir;
|
||||
char *remotes_config_dir;
|
||||
|
||||
GHashTable *txn_refs;
|
||||
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 cache_lock;
|
||||
guint dirmeta_cache_refcount;
|
||||
@@ -119,6 +127,7 @@ struct OstreeRepo {
|
||||
uid_t owner_uid;
|
||||
uid_t target_owner_uid;
|
||||
gid_t target_owner_gid;
|
||||
guint min_free_space_percent;
|
||||
|
||||
guint test_error_flags; /* OstreeRepoTestErrorFlags */
|
||||
|
||||
@@ -129,6 +138,7 @@ struct OstreeRepo {
|
||||
gboolean enable_uncompressed_cache;
|
||||
gboolean generate_sizes;
|
||||
guint64 tmp_expiry_seconds;
|
||||
gchar *collection_id;
|
||||
|
||||
OstreeRepo *parent_repo;
|
||||
};
|
||||
@@ -206,6 +216,14 @@ _ostree_repo_has_loose_object (OstreeRepo *self,
|
||||
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,
|
||||
@@ -219,7 +237,13 @@ _ostree_repo_update_refs (OstreeRepo *self,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
gboolean
|
||||
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,
|
||||
@@ -228,13 +252,13 @@ _ostree_repo_file_replace_contents (OstreeRepo *self,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
gboolean
|
||||
_ostree_repo_write_ref (OstreeRepo *self,
|
||||
const char *remote,
|
||||
const char *ref,
|
||||
const char *rev,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
gboolean
|
||||
_ostree_repo_write_ref (OstreeRepo *self,
|
||||
const char *remote,
|
||||
const OstreeCollectionRef *ref,
|
||||
const char *rev,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
OstreeRepoFile *
|
||||
_ostree_repo_file_new_for_commit (OstreeRepo *repo,
|
||||
@@ -285,26 +309,26 @@ _ostree_repo_verify_commit_internal (OstreeRepo *self,
|
||||
GError **error);
|
||||
|
||||
gboolean
|
||||
_ostree_repo_commit_loose_final (OstreeRepo *self,
|
||||
const char *checksum,
|
||||
OstreeObjectType objtype,
|
||||
int temp_dfd,
|
||||
int fd,
|
||||
const char *temp_filename,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
_ostree_repo_commit_tmpf_final (OstreeRepo *self,
|
||||
const char *checksum,
|
||||
OstreeObjectType objtype,
|
||||
GLnxTmpfile *tmpf,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
typedef struct {
|
||||
int fd;
|
||||
char *temp_filename;
|
||||
} OstreeRepoContentBareCommit;
|
||||
gboolean
|
||||
_ostree_repo_commit_path_final (OstreeRepo *self,
|
||||
const char *checksum,
|
||||
OstreeObjectType objtype,
|
||||
OtCleanupUnlinkat *tmp_path,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
gboolean
|
||||
_ostree_repo_open_content_bare (OstreeRepo *self,
|
||||
const char *checksum,
|
||||
guint64 content_len,
|
||||
OstreeRepoContentBareCommit *out_state,
|
||||
GOutputStream **out_stream,
|
||||
GLnxTmpfile *out_tmpf,
|
||||
gboolean *out_have_object,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
@@ -312,7 +336,7 @@ _ostree_repo_open_content_bare (OstreeRepo *self,
|
||||
gboolean
|
||||
_ostree_repo_commit_trusted_content_bare (OstreeRepo *self,
|
||||
const char *checksum,
|
||||
OstreeRepoContentBareCommit *state,
|
||||
GLnxTmpfile *tmpf,
|
||||
guint32 uid,
|
||||
guint32 gid,
|
||||
guint32 mode,
|
||||
@@ -321,11 +345,14 @@ _ostree_repo_commit_trusted_content_bare (OstreeRepo *self,
|
||||
GError **error);
|
||||
|
||||
gboolean
|
||||
_ostree_repo_read_bare_fd (OstreeRepo *self,
|
||||
const char *checksum,
|
||||
int *out_fd,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
_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,
|
||||
@@ -346,4 +373,29 @@ _ostree_repo_get_remote_inherited (OstreeRepo *self,
|
||||
const char *name,
|
||||
GError **error);
|
||||
|
||||
#ifndef OSTREE_ENABLE_EXPERIMENTAL_API
|
||||
|
||||
const gchar * ostree_repo_get_collection_id (OstreeRepo *self);
|
||||
gboolean ostree_repo_set_collection_id (OstreeRepo *self,
|
||||
const gchar *collection_id,
|
||||
GError **error);
|
||||
|
||||
gboolean ostree_repo_list_collection_refs (OstreeRepo *self,
|
||||
const char *match_collection_id,
|
||||
GHashTable **out_all_refs,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
void ostree_repo_transaction_set_collection_ref (OstreeRepo *self,
|
||||
const OstreeCollectionRef *ref,
|
||||
const char *checksum);
|
||||
|
||||
gboolean ostree_repo_set_collection_ref_immediate (OstreeRepo *self,
|
||||
const OstreeCollectionRef *ref,
|
||||
const char *checksum,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
#endif /* !OSTREE_ENABLE_EXPERIMENTAL_API */
|
||||
|
||||
G_END_DECLS
|
||||
@@ -232,8 +232,6 @@ repo_prune_internal (OstreeRepo *self,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
GHashTableIter hash_iter;
|
||||
gpointer key, value;
|
||||
OtPruneData data = { 0, };
|
||||
|
||||
data.repo = self;
|
||||
@@ -241,11 +239,8 @@ repo_prune_internal (OstreeRepo *self,
|
||||
g_autoptr(GHashTable) reachable_owned = g_hash_table_ref (options->reachable);
|
||||
data.reachable = reachable_owned;
|
||||
|
||||
g_hash_table_iter_init (&hash_iter, objects);
|
||||
while (g_hash_table_iter_next (&hash_iter, &key, &value))
|
||||
GLNX_HASH_TABLE_FOREACH_KV (objects, GVariant*, serialized_key, GVariant*, objdata)
|
||||
{
|
||||
GVariant *serialized_key = key;
|
||||
GVariant *objdata = value;
|
||||
const char *checksum;
|
||||
OstreeObjectType objtype;
|
||||
gboolean is_loose;
|
||||
@@ -309,14 +304,10 @@ ostree_repo_prune (OstreeRepo *self,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
GHashTableIter hash_iter;
|
||||
gpointer key, value;
|
||||
g_autoptr(GHashTable) objects = NULL;
|
||||
g_autoptr(GHashTable) all_refs = NULL;
|
||||
g_autoptr(GHashTable) reachable = NULL;
|
||||
gboolean refs_only = flags & OSTREE_REPO_PRUNE_FLAGS_REFS_ONLY;
|
||||
|
||||
reachable = ostree_repo_traverse_new_reachable ();
|
||||
g_autoptr(GHashTable) reachable = ostree_repo_traverse_new_reachable ();
|
||||
|
||||
/* This original prune API has fixed logic for traversing refs or all commits
|
||||
* combined with actually deleting content. The newer backend API just does
|
||||
@@ -325,16 +316,30 @@ ostree_repo_prune (OstreeRepo *self,
|
||||
|
||||
if (refs_only)
|
||||
{
|
||||
/* Ignoring collections. */
|
||||
g_autoptr(GHashTable) all_refs = NULL; /* (element-type utf8 utf8) */
|
||||
|
||||
if (!ostree_repo_list_refs (self, NULL, &all_refs,
|
||||
cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
g_hash_table_iter_init (&hash_iter, all_refs);
|
||||
|
||||
while (g_hash_table_iter_next (&hash_iter, &key, &value))
|
||||
GLNX_HASH_TABLE_FOREACH_V (all_refs, const char*, checksum)
|
||||
{
|
||||
const char *checksum = value;
|
||||
g_debug ("Finding objects to keep for commit %s", checksum);
|
||||
if (!ostree_repo_traverse_commit_union (self, checksum, depth, reachable,
|
||||
cancellable, error))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Using collections. */
|
||||
g_autoptr(GHashTable) all_collection_refs = NULL; /* (element-type OstreeChecksumRef utf8) */
|
||||
|
||||
if (!ostree_repo_list_collection_refs (self, NULL, &all_collection_refs,
|
||||
cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
GLNX_HASH_TABLE_FOREACH_V (all_collection_refs, const char*, checksum)
|
||||
{
|
||||
g_debug ("Finding objects to keep for commit %s", checksum);
|
||||
if (!ostree_repo_traverse_commit_union (self, checksum, depth, reachable,
|
||||
cancellable, error))
|
||||
@@ -348,10 +353,8 @@ ostree_repo_prune (OstreeRepo *self,
|
||||
|
||||
if (!refs_only)
|
||||
{
|
||||
g_hash_table_iter_init (&hash_iter, objects);
|
||||
while (g_hash_table_iter_next (&hash_iter, &key, &value))
|
||||
GLNX_HASH_TABLE_FOREACH (objects, GVariant*, serialized_key)
|
||||
{
|
||||
GVariant *serialized_key = key;
|
||||
const char *checksum;
|
||||
OstreeObjectType objtype;
|
||||
|
||||
|
||||
+1723
-172
File diff suppressed because it is too large.
Load diff
@@ -20,18 +20,25 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "ostree-core-private.h"
|
||||
#include "ostree-repo-private.h"
|
||||
#include "otutil.h"
|
||||
#include "ot-fs-utils.h"
|
||||
|
||||
/* This is polymorphic in @collection_id: if non-%NULL, @refs will be treated as of
|
||||
* type OstreeCollectionRef ↦ checksum. Otherwise, it will be treated as of type
|
||||
* refspec ↦ checksum. */
|
||||
static gboolean
|
||||
add_ref_to_set (const char *remote,
|
||||
const char *collection_id,
|
||||
int base_fd,
|
||||
const char *path,
|
||||
GHashTable *refs,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
g_return_val_if_fail (remote == NULL || collection_id == NULL, FALSE);
|
||||
|
||||
gsize len;
|
||||
char *contents = glnx_file_get_contents_utf8_at (base_fd, path, &len, cancellable, error);
|
||||
if (!contents)
|
||||
@@ -39,14 +46,21 @@ add_ref_to_set (const char *remote,
|
||||
|
||||
g_strchomp (contents);
|
||||
|
||||
g_autoptr(GString) refname = g_string_new ("");
|
||||
if (remote)
|
||||
if (collection_id == NULL)
|
||||
{
|
||||
g_string_append (refname, remote);
|
||||
g_string_append_c (refname, ':');
|
||||
g_autoptr(GString) refname = g_string_new ("");
|
||||
if (remote)
|
||||
{
|
||||
g_string_append (refname, remote);
|
||||
g_string_append_c (refname, ':');
|
||||
}
|
||||
g_string_append (refname, path);
|
||||
g_hash_table_insert (refs, g_string_free (g_steal_pointer (&refname), FALSE), contents);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_hash_table_insert (refs, ostree_collection_ref_new (collection_id, path), contents);
|
||||
}
|
||||
g_string_append (refname, path);
|
||||
g_hash_table_insert (refs, g_string_free (g_steal_pointer (&refname), FALSE), contents);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
@@ -99,6 +113,8 @@ write_checksum_file_at (OstreeRepo *self,
|
||||
|
||||
g_clear_error (&temp_error);
|
||||
|
||||
/* FIXME: Conflict detection needs to be extended to collection–refs
|
||||
* using ostree_repo_list_collection_refs(). */
|
||||
if (!ostree_repo_list_refs (self, name, &refs, cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
@@ -456,6 +472,7 @@ ostree_repo_resolve_rev_ext (OstreeRepo *self,
|
||||
static gboolean
|
||||
enumerate_refs_recurse (OstreeRepo *repo,
|
||||
const char *remote,
|
||||
const char *collection_id,
|
||||
int base_dfd,
|
||||
GString *base_path,
|
||||
int child_dfd,
|
||||
@@ -485,14 +502,14 @@ enumerate_refs_recurse (OstreeRepo *repo,
|
||||
{
|
||||
g_string_append_c (base_path, '/');
|
||||
|
||||
if (!enumerate_refs_recurse (repo, remote, base_dfd, base_path,
|
||||
if (!enumerate_refs_recurse (repo, remote, collection_id, base_dfd, base_path,
|
||||
dfd_iter.fd, dent->d_name,
|
||||
refs, cancellable, error))
|
||||
return FALSE;
|
||||
}
|
||||
else if (dent->d_type == DT_REG)
|
||||
{
|
||||
if (!add_ref_to_set (remote, base_dfd, base_path->str, refs,
|
||||
if (!add_ref_to_set (remote, collection_id, base_dfd, base_path->str, refs,
|
||||
cancellable, error))
|
||||
return FALSE;
|
||||
}
|
||||
@@ -554,7 +571,7 @@ _ostree_repo_list_refs_internal (OstreeRepo *self,
|
||||
if (!glnx_opendirat (self->repo_dir_fd, cut_prefix ? path : prefix_path, TRUE, &base_fd, error))
|
||||
return FALSE;
|
||||
|
||||
if (!enumerate_refs_recurse (self, remote, base_fd, base_path,
|
||||
if (!enumerate_refs_recurse (self, remote, NULL, base_fd, base_path,
|
||||
base_fd, cut_prefix ? "." : ref_prefix,
|
||||
ret_all_refs, cancellable, error))
|
||||
return FALSE;
|
||||
@@ -566,7 +583,7 @@ _ostree_repo_list_refs_internal (OstreeRepo *self,
|
||||
if (!glnx_opendirat (self->repo_dir_fd, prefix_path, TRUE, &prefix_dfd, error))
|
||||
return FALSE;
|
||||
|
||||
if (!add_ref_to_set (remote, prefix_dfd, ref_prefix, ret_all_refs,
|
||||
if (!add_ref_to_set (remote, NULL, prefix_dfd, ref_prefix, ret_all_refs,
|
||||
cancellable, error))
|
||||
return FALSE;
|
||||
}
|
||||
@@ -581,7 +598,7 @@ _ostree_repo_list_refs_internal (OstreeRepo *self,
|
||||
if (!glnx_opendirat (self->repo_dir_fd, "refs/heads", TRUE, &refs_heads_dfd, error))
|
||||
return FALSE;
|
||||
|
||||
if (!enumerate_refs_recurse (self, NULL, refs_heads_dfd, base_path,
|
||||
if (!enumerate_refs_recurse (self, NULL, NULL, refs_heads_dfd, base_path,
|
||||
refs_heads_dfd, ".",
|
||||
ret_all_refs, cancellable, error))
|
||||
return FALSE;
|
||||
@@ -607,7 +624,7 @@ _ostree_repo_list_refs_internal (OstreeRepo *self,
|
||||
if (!glnx_opendirat (dfd_iter.fd, dent->d_name, TRUE, &remote_dfd, error))
|
||||
return FALSE;
|
||||
|
||||
if (!enumerate_refs_recurse (self, dent->d_name, remote_dfd, base_path,
|
||||
if (!enumerate_refs_recurse (self, dent->d_name, NULL, remote_dfd, base_path,
|
||||
remote_dfd, ".",
|
||||
ret_all_refs,
|
||||
cancellable, error))
|
||||
@@ -741,16 +758,26 @@ ostree_repo_remote_list_refs (OstreeRepo *self,
|
||||
}
|
||||
|
||||
gboolean
|
||||
_ostree_repo_write_ref (OstreeRepo *self,
|
||||
const char *remote,
|
||||
const char *ref,
|
||||
const char *rev,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
_ostree_repo_write_ref (OstreeRepo *self,
|
||||
const char *remote,
|
||||
const OstreeCollectionRef *ref,
|
||||
const char *rev,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
glnx_fd_close int dfd = -1;
|
||||
|
||||
if (remote == NULL)
|
||||
g_return_val_if_fail (remote == NULL || ref->collection_id == NULL, FALSE);
|
||||
|
||||
if (remote != NULL && !ostree_validate_remote_name (remote, error))
|
||||
return FALSE;
|
||||
if (ref->collection_id != NULL && !ostree_validate_collection_id (ref->collection_id, error))
|
||||
return FALSE;
|
||||
if (!ostree_validate_rev (ref->ref_name, error))
|
||||
return FALSE;
|
||||
|
||||
if (remote == NULL &&
|
||||
(ref->collection_id == NULL || g_strcmp0 (ref->collection_id, ostree_repo_get_collection_id (self)) == 0))
|
||||
{
|
||||
if (!glnx_opendirat (self->repo_dir_fd, "refs/heads", TRUE,
|
||||
&dfd, error))
|
||||
@@ -759,6 +786,29 @@ _ostree_repo_write_ref (OstreeRepo *self,
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
else if (remote == NULL && ref->collection_id != NULL)
|
||||
{
|
||||
glnx_fd_close int refs_mirrors_dfd = -1;
|
||||
|
||||
/* refs/mirrors might not exist in older repositories, so create it. */
|
||||
if (!glnx_shutil_mkdir_p_at_open (self->repo_dir_fd, "refs/mirrors", 0777,
|
||||
&refs_mirrors_dfd, cancellable, error))
|
||||
{
|
||||
g_prefix_error (error, "Opening %s: ", "refs/mirrors");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (rev != NULL)
|
||||
{
|
||||
/* Ensure we have a dir for the collection */
|
||||
if (!glnx_shutil_mkdir_p_at (refs_mirrors_dfd, ref->collection_id, 0777, cancellable, error))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
dfd = glnx_opendirat_with_errno (refs_mirrors_dfd, ref->collection_id, TRUE);
|
||||
if (dfd < 0 && (errno != ENOENT || rev != NULL))
|
||||
return glnx_throw_errno_prefix (error, "Opening mirrors/ dir %s", ref->collection_id);
|
||||
}
|
||||
else
|
||||
{
|
||||
glnx_fd_close int refs_remotes_dfd = -1;
|
||||
@@ -786,7 +836,7 @@ _ostree_repo_write_ref (OstreeRepo *self,
|
||||
{
|
||||
if (dfd >= 0)
|
||||
{
|
||||
if (unlinkat (dfd, ref, 0) != 0)
|
||||
if (unlinkat (dfd, ref->ref_name, 0) != 0)
|
||||
{
|
||||
if (errno != ENOENT)
|
||||
return glnx_throw_errno (error);
|
||||
@@ -795,7 +845,7 @@ _ostree_repo_write_ref (OstreeRepo *self,
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!write_checksum_file_at (self, dfd, ref, rev, cancellable, error))
|
||||
if (!write_checksum_file_at (self, dfd, ref->ref_name, rev, cancellable, error))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
@@ -807,7 +857,7 @@ _ostree_repo_write_ref (OstreeRepo *self,
|
||||
|
||||
gboolean
|
||||
_ostree_repo_update_refs (OstreeRepo *self,
|
||||
GHashTable *refs,
|
||||
GHashTable *refs, /* (element-type utf8 utf8) */
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
@@ -820,15 +870,136 @@ _ostree_repo_update_refs (OstreeRepo *self,
|
||||
const char *refspec = key;
|
||||
const char *rev = value;
|
||||
g_autofree char *remote = NULL;
|
||||
g_autofree char *ref = NULL;
|
||||
g_autofree char *ref_name = NULL;
|
||||
|
||||
if (!ostree_parse_refspec (refspec, &remote, &ref, error))
|
||||
if (!ostree_parse_refspec (refspec, &remote, &ref_name, error))
|
||||
return FALSE;
|
||||
|
||||
if (!_ostree_repo_write_ref (self, remote, ref, rev,
|
||||
const OstreeCollectionRef ref = { NULL, ref_name };
|
||||
if (!_ostree_repo_write_ref (self, remote, &ref, rev,
|
||||
cancellable, error))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
_ostree_repo_update_collection_refs (OstreeRepo *self,
|
||||
GHashTable *refs, /* (element-type OstreeCollectionRef utf8) */
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
GHashTableIter hash_iter;
|
||||
gpointer key, value;
|
||||
|
||||
g_hash_table_iter_init (&hash_iter, refs);
|
||||
while (g_hash_table_iter_next (&hash_iter, &key, &value))
|
||||
{
|
||||
const OstreeCollectionRef *ref = key;
|
||||
const char *rev = value;
|
||||
|
||||
if (!_ostree_repo_write_ref (self, NULL, ref, rev,
|
||||
cancellable, error))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* ostree_repo_list_collection_refs:
|
||||
* @self: Repo
|
||||
* @match_collection_id: (nullable): If non-%NULL, only list refs from this collection
|
||||
* @out_all_refs: (out) (element-type OstreeCollectionRef utf8): Mapping from collection–ref to checksum
|
||||
* @cancellable: Cancellable
|
||||
* @error: Error
|
||||
*
|
||||
* List all local and mirrored refs, mapping them to the commit checksums they
|
||||
* currently point to in @out_all_refs. If @match_collection_id is specified,
|
||||
* the results will be limited to those with an equal collection ID.
|
||||
*
|
||||
* #OstreeCollectionRefs are guaranteed to be returned with their collection ID
|
||||
* set to a non-%NULL value; so no refs from `refs/heads` will be listed if no
|
||||
* collection ID is configured for the repository
|
||||
* (ostree_repo_get_collection_id()).
|
||||
*
|
||||
* Returns: %TRUE on success, %FALSE otherwise
|
||||
* Since: 2017.8
|
||||
*/
|
||||
gboolean
|
||||
ostree_repo_list_collection_refs (OstreeRepo *self,
|
||||
const char *match_collection_id,
|
||||
GHashTable **out_all_refs,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
g_return_val_if_fail (OSTREE_IS_REPO (self), FALSE);
|
||||
g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE);
|
||||
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
|
||||
|
||||
if (match_collection_id != NULL && !ostree_validate_collection_id (match_collection_id, error))
|
||||
return FALSE;
|
||||
|
||||
g_autoptr(GHashTable) ret_all_refs = NULL;
|
||||
|
||||
ret_all_refs = g_hash_table_new_full (ostree_collection_ref_hash,
|
||||
ostree_collection_ref_equal,
|
||||
(GDestroyNotify) ostree_collection_ref_free,
|
||||
g_free);
|
||||
|
||||
g_auto(GLnxDirFdIterator) dfd_iter = { 0, };
|
||||
g_autoptr(GString) base_path = g_string_new ("");
|
||||
|
||||
const gchar *main_collection_id = ostree_repo_get_collection_id (self);
|
||||
|
||||
if (main_collection_id != NULL &&
|
||||
(match_collection_id == NULL || g_strcmp0 (match_collection_id, main_collection_id) == 0))
|
||||
{
|
||||
glnx_fd_close int refs_heads_dfd = -1;
|
||||
|
||||
if (!glnx_opendirat (self->repo_dir_fd, "refs/heads", TRUE, &refs_heads_dfd, error))
|
||||
return FALSE;
|
||||
|
||||
if (!enumerate_refs_recurse (self, NULL, main_collection_id, refs_heads_dfd, base_path,
|
||||
refs_heads_dfd, ".",
|
||||
ret_all_refs, cancellable, error))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
g_string_truncate (base_path, 0);
|
||||
|
||||
gboolean refs_mirrors_exists = FALSE;
|
||||
if (!ot_dfd_iter_init_allow_noent (self->repo_dir_fd, "refs/mirrors",
|
||||
&dfd_iter, &refs_mirrors_exists, error))
|
||||
return FALSE;
|
||||
|
||||
while (refs_mirrors_exists)
|
||||
{
|
||||
struct dirent *dent;
|
||||
glnx_fd_close int collection_dfd = -1;
|
||||
|
||||
if (!glnx_dirfd_iterator_next_dent_ensure_dtype (&dfd_iter, &dent, cancellable, error))
|
||||
return FALSE;
|
||||
if (!dent)
|
||||
break;
|
||||
|
||||
if (dent->d_type != DT_DIR)
|
||||
continue;
|
||||
|
||||
if (match_collection_id != NULL && g_strcmp0 (match_collection_id, dent->d_name) != 0)
|
||||
continue;
|
||||
|
||||
if (!glnx_opendirat (dfd_iter.fd, dent->d_name, TRUE, &collection_dfd, error))
|
||||
return FALSE;
|
||||
|
||||
if (!enumerate_refs_recurse (self, NULL, dent->d_name, collection_dfd, base_path,
|
||||
collection_dfd, ".",
|
||||
ret_all_refs,
|
||||
cancellable, error))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
ot_transfer_out_value (out_all_refs, &ret_all_refs);
|
||||
return TRUE;
|
||||
}
|
||||
@@ -138,11 +138,11 @@ xattr_chunk_hash (const void *vp)
|
||||
g_variant_get_child (v, i, "(^&ay@ay)",
|
||||
&name, &value);
|
||||
value_data = g_variant_get_fixed_array (value, &value_len, 1);
|
||||
|
||||
|
||||
h += g_str_hash (name);
|
||||
h += bufhash (value_data, value_len);
|
||||
}
|
||||
|
||||
|
||||
return h;
|
||||
}
|
||||
|
||||
@@ -235,7 +235,7 @@ objtype_checksum_array_new (GPtrArray *objects)
|
||||
const char *checksum;
|
||||
guint8 csum[OSTREE_SHA256_DIGEST_LEN];
|
||||
guint8 objtype_v;
|
||||
|
||||
|
||||
ostree_object_name_deserialize (serialized_key, &checksum, &objtype);
|
||||
objtype_v = (guint8) objtype;
|
||||
|
||||
@@ -253,29 +253,25 @@ splice_stream_to_payload (OstreeStaticDeltaPartBuilder *current_part,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
gboolean ret = FALSE;
|
||||
const guint readlen = 4096;
|
||||
gsize bytes_read;
|
||||
|
||||
while (TRUE)
|
||||
{
|
||||
const guint readlen = 4096;
|
||||
allocate_part_buffer_space (current_part, readlen);
|
||||
|
||||
gsize bytes_read;
|
||||
if (!g_input_stream_read_all (istream,
|
||||
current_part->payload->str + current_part->payload->len,
|
||||
readlen,
|
||||
&bytes_read,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
return FALSE;
|
||||
if (bytes_read == 0)
|
||||
break;
|
||||
|
||||
|
||||
current_part->payload->len += bytes_read;
|
||||
}
|
||||
|
||||
ret = TRUE;
|
||||
out:
|
||||
return ret;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -293,7 +289,7 @@ write_content_mode_xattrs (OstreeRepo *repo,
|
||||
guint32 mode =
|
||||
g_file_info_get_attribute_uint32 (content_finfo, "unix::mode");
|
||||
g_autoptr(GVariant) modev
|
||||
= g_variant_ref_sink (g_variant_new ("(uuu)",
|
||||
= g_variant_ref_sink (g_variant_new ("(uuu)",
|
||||
GUINT32_TO_BE (uid),
|
||||
GUINT32_TO_BE (gid),
|
||||
GUINT32_TO_BE (mode)));
|
||||
@@ -317,41 +313,40 @@ process_one_object (OstreeRepo *repo,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
gboolean ret = FALSE;
|
||||
guint64 content_size;
|
||||
g_autoptr(GInputStream) content_stream = NULL;
|
||||
OstreeStaticDeltaPartBuilder *current_part = *current_part_val;
|
||||
g_autoptr(GFileInfo) content_finfo = NULL;
|
||||
g_autoptr(GVariant) content_xattrs = NULL;
|
||||
guint64 compressed_size;
|
||||
OstreeStaticDeltaPartBuilder *current_part = *current_part_val;
|
||||
guint64 content_size;
|
||||
g_autoptr(GInputStream) content_stream = NULL;
|
||||
|
||||
if (OSTREE_OBJECT_TYPE_IS_META (objtype))
|
||||
{
|
||||
if (!ostree_repo_load_object_stream (repo, objtype, checksum,
|
||||
&content_stream, &content_size,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
return FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!ostree_repo_load_file (repo, checksum, &content_stream,
|
||||
&content_finfo, &content_xattrs,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
return FALSE;
|
||||
content_size = g_file_info_get_size (content_finfo);
|
||||
}
|
||||
|
||||
|
||||
/* Check to see if this delta is maximum size */
|
||||
if (current_part->objects->len > 0 &&
|
||||
current_part->payload->len + content_size > builder->max_chunk_size_bytes)
|
||||
{
|
||||
*current_part_val = current_part = allocate_part (builder);
|
||||
}
|
||||
}
|
||||
|
||||
guint64 compressed_size;
|
||||
if (!ostree_repo_query_object_storage_size (repo, objtype, checksum,
|
||||
&compressed_size,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
return FALSE;
|
||||
builder->loose_compressed_size += compressed_size;
|
||||
|
||||
current_part->uncompressed_size += content_size;
|
||||
@@ -366,7 +361,7 @@ process_one_object (OstreeRepo *repo,
|
||||
|
||||
if (!splice_stream_to_payload (current_part, content_stream,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
return FALSE;
|
||||
|
||||
g_string_append_c (current_part->operations, (gchar)OSTREE_STATIC_DELTA_OP_OPEN_SPLICE_AND_CLOSE);
|
||||
_ostree_write_varuint64 (current_part->operations, content_size);
|
||||
@@ -375,21 +370,16 @@ process_one_object (OstreeRepo *repo,
|
||||
else
|
||||
{
|
||||
gsize mode_offset, xattr_offset, content_offset;
|
||||
guint32 mode;
|
||||
|
||||
mode = g_file_info_get_attribute_uint32 (content_finfo, "unix::mode");
|
||||
guint32 mode = g_file_info_get_attribute_uint32 (content_finfo, "unix::mode");
|
||||
|
||||
write_content_mode_xattrs (repo, current_part, content_finfo, content_xattrs,
|
||||
&mode_offset, &xattr_offset);
|
||||
|
||||
if (S_ISLNK (mode))
|
||||
{
|
||||
const char *target;
|
||||
|
||||
g_assert (content_stream == NULL);
|
||||
|
||||
target = g_file_info_get_symlink_target (content_finfo);
|
||||
content_stream =
|
||||
const char *target = g_file_info_get_symlink_target (content_finfo);
|
||||
content_stream =
|
||||
g_memory_input_stream_new_from_data (target, strlen (target), NULL);
|
||||
content_size = strlen (target);
|
||||
}
|
||||
@@ -401,7 +391,7 @@ process_one_object (OstreeRepo *repo,
|
||||
content_offset = current_part->payload->len;
|
||||
if (!splice_stream_to_payload (current_part, content_stream,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
return FALSE;
|
||||
|
||||
g_string_append_c (current_part->operations, (gchar)OSTREE_STATIC_DELTA_OP_OPEN_SPLICE_AND_CLOSE);
|
||||
_ostree_write_varuint64 (current_part->operations, mode_offset);
|
||||
@@ -410,9 +400,7 @@ process_one_object (OstreeRepo *repo,
|
||||
_ostree_write_varuint64 (current_part->operations, content_offset);
|
||||
}
|
||||
|
||||
ret = TRUE;
|
||||
out:
|
||||
return ret;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
@@ -449,36 +437,15 @@ get_unpacked_unlinked_content (OstreeRepo *repo,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
g_autofree char *tmpname = NULL;
|
||||
glnx_fd_close int fd = -1;
|
||||
g_autoptr(GBytes) ret_content = NULL;
|
||||
g_autoptr(GInputStream) istream = NULL;
|
||||
g_autoptr(GOutputStream) out = NULL;
|
||||
|
||||
if (!glnx_open_tmpfile_linkable_at (AT_FDCWD, "/tmp", O_RDWR | O_CLOEXEC,
|
||||
&fd, &tmpname, error))
|
||||
return FALSE;
|
||||
/* We don't need the file name */
|
||||
if (tmpname)
|
||||
(void) unlinkat (AT_FDCWD, tmpname, 0);
|
||||
|
||||
if (!ostree_repo_load_file (repo, checksum, &istream, NULL, NULL,
|
||||
cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
out = g_unix_output_stream_new (fd, FALSE);
|
||||
if (g_output_stream_splice (out, istream, G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET,
|
||||
cancellable, error) < 0)
|
||||
*out_content = ot_map_anonymous_tmpfile_from_content (istream, cancellable, error);
|
||||
if (!*out_content)
|
||||
return FALSE;
|
||||
|
||||
{ g_autoptr(GMappedFile) mfile = g_mapped_file_new_from_fd (fd, FALSE, error);
|
||||
if (!mfile)
|
||||
return FALSE;
|
||||
ret_content = g_mapped_file_get_bytes (mfile);
|
||||
}
|
||||
|
||||
if (out_content)
|
||||
*out_content = g_steal_pointer (&ret_content);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@@ -491,34 +458,28 @@ try_content_bsdiff (OstreeRepo *repo,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
gboolean ret = FALSE;
|
||||
|
||||
|
||||
g_autoptr(GFileInfo) from_finfo = NULL;
|
||||
g_autoptr(GFileInfo) to_finfo = NULL;
|
||||
ContentBsdiff *ret_bsdiff = NULL;
|
||||
|
||||
*out_bsdiff = NULL;
|
||||
|
||||
if (!ostree_repo_load_file (repo, from, NULL, &from_finfo, NULL,
|
||||
cancellable, error))
|
||||
return FALSE;
|
||||
g_autoptr(GFileInfo) to_finfo = NULL;
|
||||
if (!ostree_repo_load_file (repo, to, NULL, &to_finfo, NULL,
|
||||
cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
if (g_file_info_get_size (to_finfo) + g_file_info_get_size (from_finfo) > max_bsdiff_size_bytes)
|
||||
{
|
||||
ret = TRUE;
|
||||
goto out;
|
||||
}
|
||||
*out_bsdiff = NULL;
|
||||
|
||||
ret_bsdiff = g_new0 (ContentBsdiff, 1);
|
||||
/* Ignore this if it's too large */
|
||||
if (g_file_info_get_size (to_finfo) + g_file_info_get_size (from_finfo) > max_bsdiff_size_bytes)
|
||||
return TRUE;
|
||||
|
||||
ContentBsdiff *ret_bsdiff = g_new0 (ContentBsdiff, 1);
|
||||
ret_bsdiff->from_checksum = g_strdup (from);
|
||||
|
||||
ret = TRUE;
|
||||
if (out_bsdiff)
|
||||
*out_bsdiff = g_steal_pointer (&ret_bsdiff);
|
||||
out:
|
||||
return ret;
|
||||
ot_transfer_out_value (out_bsdiff, &ret_bsdiff);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
@@ -530,35 +491,27 @@ try_content_rollsum (OstreeRepo *repo,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
gboolean ret = FALSE;
|
||||
g_autoptr(GBytes) tmp_from = NULL;
|
||||
g_autoptr(GBytes) tmp_to = NULL;
|
||||
OstreeRollsumMatches *matches = NULL;
|
||||
ContentRollsum *ret_rollsum = NULL;
|
||||
|
||||
*out_rollsum = NULL;
|
||||
|
||||
/* Load the content objects, splice them to uncompressed temporary files that
|
||||
* we can just mmap() and seek around in conveniently.
|
||||
*/
|
||||
g_autoptr(GBytes) tmp_from = NULL;
|
||||
if (!get_unpacked_unlinked_content (repo, from, &tmp_from, cancellable, error))
|
||||
goto out;
|
||||
return FALSE;
|
||||
g_autoptr(GBytes) tmp_to = NULL;
|
||||
if (!get_unpacked_unlinked_content (repo, to, &tmp_to, cancellable, error))
|
||||
goto out;
|
||||
return FALSE;
|
||||
|
||||
matches = _ostree_compute_rollsum_matches (tmp_from, tmp_to);
|
||||
OstreeRollsumMatches *matches = _ostree_compute_rollsum_matches (tmp_from, tmp_to);
|
||||
|
||||
{ guint match_ratio = (matches->bufmatches*100)/matches->total;
|
||||
const guint match_ratio = (matches->bufmatches*100)/matches->total;
|
||||
|
||||
/* Only proceed if the file contains (arbitrary) more than 50% of
|
||||
* the previous chunks.
|
||||
*/
|
||||
if (match_ratio < 50)
|
||||
{
|
||||
ret = TRUE;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
/* Only proceed if the file contains (arbitrary) more than 50% of
|
||||
* the previous chunks.
|
||||
*/
|
||||
if (match_ratio < 50)
|
||||
return TRUE;
|
||||
|
||||
if (opts & DELTAOPT_FLAG_VERBOSE)
|
||||
{
|
||||
@@ -568,17 +521,11 @@ try_content_rollsum (OstreeRepo *repo,
|
||||
matches->total, (unsigned long long)matches->match_size);
|
||||
}
|
||||
|
||||
ret_rollsum = g_new0 (ContentRollsum, 1);
|
||||
ContentRollsum *ret_rollsum = g_new0 (ContentRollsum, 1);
|
||||
ret_rollsum->from_checksum = g_strdup (from);
|
||||
ret_rollsum->matches = g_steal_pointer (&matches);
|
||||
|
||||
ret = TRUE;
|
||||
if (out_rollsum)
|
||||
*out_rollsum = g_steal_pointer (&ret_rollsum);
|
||||
out:
|
||||
if (matches)
|
||||
_ostree_rollsum_matches_free (matches);
|
||||
return ret;
|
||||
ot_transfer_out_value (out_rollsum, &ret_rollsum);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
struct bzdiff_opaque_s
|
||||
@@ -625,14 +572,7 @@ process_one_rollsum (OstreeRepo *repo,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
gboolean ret = FALSE;
|
||||
guint64 content_size;
|
||||
g_autoptr(GBytes) tmp_to = NULL;
|
||||
g_autoptr(GFileInfo) content_finfo = NULL;
|
||||
g_autoptr(GVariant) content_xattrs = NULL;
|
||||
OstreeStaticDeltaPartBuilder *current_part = *current_part_val;
|
||||
const guint8 *tmp_to_buf;
|
||||
gsize tmp_to_len;
|
||||
|
||||
/* Check to see if this delta has gone over maximum size */
|
||||
if (current_part->objects->len > 0 &&
|
||||
@@ -641,17 +581,21 @@ process_one_rollsum (OstreeRepo *repo,
|
||||
*current_part_val = current_part = allocate_part (builder);
|
||||
}
|
||||
|
||||
g_autoptr(GBytes) tmp_to = NULL;
|
||||
if (!get_unpacked_unlinked_content (repo, to_checksum, &tmp_to,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
return FALSE;
|
||||
|
||||
tmp_to_buf = g_bytes_get_data (tmp_to, &tmp_to_len);
|
||||
gsize tmp_to_len;
|
||||
const guint8 *tmp_to_buf = g_bytes_get_data (tmp_to, &tmp_to_len);
|
||||
|
||||
g_autoptr(GFileInfo) content_finfo = NULL;
|
||||
g_autoptr(GVariant) content_xattrs = NULL;
|
||||
if (!ostree_repo_load_file (repo, to_checksum, NULL,
|
||||
&content_finfo, &content_xattrs,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
content_size = g_file_info_get_size (content_finfo);
|
||||
return FALSE;
|
||||
guint64 content_size = g_file_info_get_size (content_finfo);
|
||||
g_assert_cmpint (tmp_to_len, ==, content_size);
|
||||
|
||||
current_part->uncompressed_size += content_size;
|
||||
@@ -685,11 +629,10 @@ process_one_rollsum (OstreeRepo *repo,
|
||||
{
|
||||
GVariant *match = matchlist->pdata[i];
|
||||
guint32 crc;
|
||||
guint64 prefix;
|
||||
|
||||
g_variant_get (match, "(uttt)", &crc, &offset, &to_start, &from_start);
|
||||
|
||||
prefix = to_start - writing_offset;
|
||||
const guint64 prefix = to_start - writing_offset;
|
||||
|
||||
if (prefix > 0)
|
||||
{
|
||||
@@ -720,13 +663,11 @@ process_one_rollsum (OstreeRepo *repo,
|
||||
if (!reading_payload)
|
||||
g_string_append_c (current_part->operations, (gchar)OSTREE_STATIC_DELTA_OP_UNSET_READ_SOURCE);
|
||||
|
||||
{ guint64 remainder = tmp_to_len - writing_offset;
|
||||
if (remainder > 0)
|
||||
append_payload_chunk_and_write (current_part, tmp_to_buf + writing_offset, remainder);
|
||||
writing_offset += remainder;
|
||||
g_assert_cmpint (writing_offset, ==, tmp_to_len);
|
||||
}
|
||||
|
||||
const guint64 remainder = tmp_to_len - writing_offset;
|
||||
if (remainder > 0)
|
||||
append_payload_chunk_and_write (current_part, tmp_to_buf + writing_offset, remainder);
|
||||
writing_offset += remainder;
|
||||
g_assert_cmpint (writing_offset, ==, tmp_to_len);
|
||||
g_assert_cmpint (writing_offset, ==, content_size);
|
||||
}
|
||||
|
||||
@@ -734,9 +675,7 @@ process_one_rollsum (OstreeRepo *repo,
|
||||
g_string_append_c (current_part->operations, (gchar)OSTREE_STATIC_DELTA_OP_CLOSE);
|
||||
}
|
||||
|
||||
ret = TRUE;
|
||||
out:
|
||||
return ret;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
@@ -748,17 +687,7 @@ process_one_bsdiff (OstreeRepo *repo,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
gboolean ret = FALSE;
|
||||
guint64 content_size;
|
||||
g_autoptr(GFileInfo) content_finfo = NULL;
|
||||
g_autoptr(GVariant) content_xattrs = NULL;
|
||||
OstreeStaticDeltaPartBuilder *current_part = *current_part_val;
|
||||
g_autoptr(GBytes) tmp_from = NULL;
|
||||
g_autoptr(GBytes) tmp_to = NULL;
|
||||
const guint8 *tmp_to_buf;
|
||||
gsize tmp_to_len;
|
||||
const guint8 *tmp_from_buf;
|
||||
gsize tmp_from_len;
|
||||
|
||||
/* Check to see if this delta has gone over maximum size */
|
||||
if (current_part->objects->len > 0 &&
|
||||
@@ -767,21 +696,27 @@ process_one_bsdiff (OstreeRepo *repo,
|
||||
*current_part_val = current_part = allocate_part (builder);
|
||||
}
|
||||
|
||||
g_autoptr(GBytes) tmp_from = NULL;
|
||||
if (!get_unpacked_unlinked_content (repo, bsdiff_content->from_checksum, &tmp_from,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
return FALSE;
|
||||
g_autoptr(GBytes) tmp_to = NULL;
|
||||
if (!get_unpacked_unlinked_content (repo, to_checksum, &tmp_to,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
return FALSE;
|
||||
|
||||
tmp_to_buf = g_bytes_get_data (tmp_to, &tmp_to_len);
|
||||
tmp_from_buf = g_bytes_get_data (tmp_from, &tmp_from_len);
|
||||
gsize tmp_to_len;
|
||||
const guint8 *tmp_to_buf = g_bytes_get_data (tmp_to, &tmp_to_len);
|
||||
gsize tmp_from_len;
|
||||
const guint8 *tmp_from_buf = g_bytes_get_data (tmp_from, &tmp_from_len);
|
||||
|
||||
g_autoptr(GFileInfo) content_finfo = NULL;
|
||||
g_autoptr(GVariant) content_xattrs = NULL;
|
||||
if (!ostree_repo_load_file (repo, to_checksum, NULL,
|
||||
&content_finfo, &content_xattrs,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
content_size = g_file_info_get_size (content_finfo);
|
||||
return FALSE;
|
||||
const guint64 content_size = g_file_info_get_size (content_finfo);
|
||||
g_assert_cmpint (tmp_to_len, ==, content_size);
|
||||
|
||||
current_part->uncompressed_size += content_size;
|
||||
@@ -819,10 +754,8 @@ process_one_bsdiff (OstreeRepo *repo,
|
||||
op.cancellable = cancellable;
|
||||
op.error = error;
|
||||
stream.opaque = &op;
|
||||
if (bsdiff (tmp_from_buf, tmp_from_len, tmp_to_buf, tmp_to_len, &stream) < 0) {
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "bsdiff generation failed");
|
||||
goto out;
|
||||
}
|
||||
if (bsdiff (tmp_from_buf, tmp_from_len, tmp_to_buf, tmp_to_len, &stream) < 0)
|
||||
return glnx_throw (error, "bsdiff generation failed");
|
||||
|
||||
payload = g_memory_output_stream_get_data (G_MEMORY_OUTPUT_STREAM (out));
|
||||
payload_size = g_memory_output_stream_get_data_size (G_MEMORY_OUTPUT_STREAM (out));
|
||||
@@ -838,9 +771,7 @@ process_one_bsdiff (OstreeRepo *repo,
|
||||
|
||||
g_string_append_c (current_part->operations, (gchar)OSTREE_STATIC_DELTA_OP_UNSET_READ_SOURCE);
|
||||
|
||||
ret = TRUE;
|
||||
out:
|
||||
return ret;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
@@ -862,7 +793,7 @@ check_object_world_readable (OstreeRepo *repo,
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
static gboolean
|
||||
generate_delta_lowlatency (OstreeRepo *repo,
|
||||
const char *from,
|
||||
const char *to,
|
||||
@@ -871,7 +802,6 @@ generate_delta_lowlatency (OstreeRepo *repo,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
gboolean ret = FALSE;
|
||||
GHashTableIter hashiter;
|
||||
gpointer key, value;
|
||||
OstreeStaticDeltaPartBuilder *current_part = NULL;
|
||||
@@ -892,27 +822,27 @@ generate_delta_lowlatency (OstreeRepo *repo,
|
||||
{
|
||||
if (!ostree_repo_read_commit (repo, from, &root_from, NULL,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
return FALSE;
|
||||
|
||||
if (!ostree_repo_load_variant (repo, OSTREE_OBJECT_TYPE_COMMIT, from,
|
||||
&from_commit, error))
|
||||
goto out;
|
||||
return FALSE;
|
||||
|
||||
if (!ostree_repo_traverse_commit (repo, from, 0, &from_reachable_objects,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!ostree_repo_read_commit (repo, to, &root_to, NULL,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
return FALSE;
|
||||
if (!ostree_repo_load_variant (repo, OSTREE_OBJECT_TYPE_COMMIT, to,
|
||||
&to_commit, error))
|
||||
goto out;
|
||||
return FALSE;
|
||||
|
||||
if (!ostree_repo_traverse_commit (repo, to, 0, &to_reachable_objects,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
return FALSE;
|
||||
|
||||
new_reachable_metadata = ostree_repo_traverse_new_reachable ();
|
||||
new_reachable_regfile_content = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_free);
|
||||
@@ -939,7 +869,7 @@ generate_delta_lowlatency (OstreeRepo *repo,
|
||||
|
||||
if (!ostree_repo_load_file (repo, checksum, NULL, &finfo, NULL,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
return FALSE;
|
||||
|
||||
ftype = g_file_info_get_file_type (finfo);
|
||||
if (ftype == G_FILE_TYPE_REGULAR)
|
||||
@@ -958,7 +888,7 @@ generate_delta_lowlatency (OstreeRepo *repo,
|
||||
CONTENT_SIZE_SIMILARITY_THRESHOLD_PERCENT,
|
||||
&modified_regfile_content,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
return FALSE;
|
||||
}
|
||||
else
|
||||
modified_regfile_content = g_hash_table_new (g_str_hash, g_str_equal);
|
||||
@@ -1001,13 +931,13 @@ generate_delta_lowlatency (OstreeRepo *repo,
|
||||
* bare repository defined as its parent.
|
||||
*/
|
||||
if (!check_object_world_readable (repo, from_checksum, &from_world_readable, cancellable, error))
|
||||
goto out;
|
||||
return FALSE;
|
||||
if (!from_world_readable)
|
||||
continue;
|
||||
|
||||
if (!try_content_rollsum (repo, opts, from_checksum, to_checksum,
|
||||
&rollsum, cancellable, error))
|
||||
goto out;
|
||||
return FALSE;
|
||||
|
||||
if (rollsum)
|
||||
{
|
||||
@@ -1021,7 +951,7 @@ generate_delta_lowlatency (OstreeRepo *repo,
|
||||
if (!try_content_bsdiff (repo, from_checksum, to_checksum,
|
||||
&bsdiff, builder->max_bsdiff_size_bytes,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
return FALSE;
|
||||
|
||||
if (bsdiff)
|
||||
g_hash_table_insert (bsdiff_optimized_content_objects, g_strdup (to_checksum), bsdiff);
|
||||
@@ -1050,7 +980,7 @@ generate_delta_lowlatency (OstreeRepo *repo,
|
||||
if (!process_one_object (repo, builder, ¤t_part,
|
||||
checksum, objtype,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Now do rollsummed objects */
|
||||
@@ -1064,7 +994,7 @@ generate_delta_lowlatency (OstreeRepo *repo,
|
||||
if (!process_one_rollsum (repo, builder, ¤t_part,
|
||||
checksum, rollsum,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
return FALSE;
|
||||
|
||||
builder->n_rollsum++;
|
||||
}
|
||||
@@ -1080,7 +1010,7 @@ generate_delta_lowlatency (OstreeRepo *repo,
|
||||
if (!process_one_bsdiff (repo, builder, ¤t_part,
|
||||
checksum, bsdiff,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
return FALSE;
|
||||
|
||||
builder->n_bsdiff++;
|
||||
}
|
||||
@@ -1103,11 +1033,11 @@ generate_delta_lowlatency (OstreeRepo *repo,
|
||||
if (!ostree_repo_load_object_stream (repo, OSTREE_OBJECT_TYPE_FILE, checksum,
|
||||
NULL, &uncompressed_size,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
return FALSE;
|
||||
if (builder->min_fallback_size_bytes > 0 &&
|
||||
uncompressed_size > builder->min_fallback_size_bytes)
|
||||
fallback = TRUE;
|
||||
|
||||
|
||||
if (fallback)
|
||||
{
|
||||
g_autofree char *size = g_format_size (uncompressed_size);
|
||||
@@ -1115,7 +1045,7 @@ generate_delta_lowlatency (OstreeRepo *repo,
|
||||
if (opts & DELTAOPT_FLAG_VERBOSE)
|
||||
g_printerr ("fallback for %s (%s)\n", checksum, size);
|
||||
|
||||
g_ptr_array_add (builder->fallback_objects,
|
||||
g_ptr_array_add (builder->fallback_objects,
|
||||
ostree_object_name_serialize (checksum, OSTREE_OBJECT_TYPE_FILE));
|
||||
g_hash_table_iter_remove (&hashiter);
|
||||
builder->n_fallback++;
|
||||
@@ -1136,7 +1066,7 @@ generate_delta_lowlatency (OstreeRepo *repo,
|
||||
if (!process_one_object (repo, builder, ¤t_part,
|
||||
checksum, OSTREE_OBJECT_TYPE_FILE,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Now symlinks */
|
||||
@@ -1148,12 +1078,10 @@ generate_delta_lowlatency (OstreeRepo *repo,
|
||||
if (!process_one_object (repo, builder, ¤t_part,
|
||||
checksum, OSTREE_OBJECT_TYPE_FILE,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
ret = TRUE;
|
||||
out:
|
||||
return ret;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
@@ -1163,14 +1091,10 @@ get_fallback_headers (OstreeRepo *self,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
gboolean ret = FALSE;
|
||||
guint i;
|
||||
g_autoptr(GVariant) ret_headers = NULL;
|
||||
g_autoptr(GVariantBuilder) fallback_builder = NULL;
|
||||
g_autoptr(GVariantBuilder) fallback_builder =
|
||||
g_variant_builder_new (G_VARIANT_TYPE ("a" OSTREE_STATIC_DELTA_FALLBACK_FORMAT));
|
||||
|
||||
fallback_builder = g_variant_builder_new (G_VARIANT_TYPE ("a" OSTREE_STATIC_DELTA_FALLBACK_FORMAT));
|
||||
|
||||
for (i = 0; i < builder->fallback_objects->len; i++)
|
||||
for (guint i = 0; i < builder->fallback_objects->len; i++)
|
||||
{
|
||||
GVariant *serialized = builder->fallback_objects->pdata[i];
|
||||
const char *checksum;
|
||||
@@ -1185,23 +1109,22 @@ get_fallback_headers (OstreeRepo *self,
|
||||
if (!ostree_repo_load_object_stream (self, objtype, checksum,
|
||||
NULL, &uncompressed_size,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
return FALSE;
|
||||
compressed_size = uncompressed_size;
|
||||
}
|
||||
else
|
||||
{
|
||||
g_autoptr(GFileInfo) file_info = NULL;
|
||||
|
||||
if (!ostree_repo_query_object_storage_size (self, OSTREE_OBJECT_TYPE_FILE,
|
||||
checksum,
|
||||
&compressed_size,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
return FALSE;
|
||||
|
||||
g_autoptr(GFileInfo) file_info = NULL;
|
||||
if (!ostree_repo_load_file (self, checksum,
|
||||
NULL, &file_info, NULL,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
return FALSE;
|
||||
|
||||
uncompressed_size = g_file_info_get_size (file_info);
|
||||
}
|
||||
@@ -1214,13 +1137,9 @@ get_fallback_headers (OstreeRepo *self,
|
||||
maybe_swap_endian_u64 (builder->swap_endian, uncompressed_size)));
|
||||
}
|
||||
|
||||
ret_headers = g_variant_ref_sink (g_variant_builder_end (fallback_builder));
|
||||
|
||||
ret = TRUE;
|
||||
if (out_headers)
|
||||
*out_headers = g_steal_pointer (&ret_headers);
|
||||
out:
|
||||
return ret;
|
||||
g_autoptr(GVariant) ret_headers = g_variant_ref_sink (g_variant_builder_end (fallback_builder));
|
||||
ot_transfer_out_value (out_headers, &ret_headers);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1273,7 +1192,6 @@ ostree_repo_static_delta_generate (OstreeRepo *self,
|
||||
guint64 total_compressed_size = 0;
|
||||
guint64 total_uncompressed_size = 0;
|
||||
g_autoptr(GVariantBuilder) part_headers = NULL;
|
||||
g_autoptr(GArray) part_temp_fds = NULL;
|
||||
g_autoptr(GPtrArray) part_temp_paths = NULL;
|
||||
g_autoptr(GVariant) delta_descriptor = NULL;
|
||||
g_autoptr(GVariant) to_commit = NULL;
|
||||
@@ -1352,7 +1270,7 @@ ostree_repo_static_delta_generate (OstreeRepo *self,
|
||||
}
|
||||
|
||||
{ guint8 endianness_char;
|
||||
|
||||
|
||||
switch (endianness)
|
||||
{
|
||||
case G_LITTLE_ENDIAN:
|
||||
@@ -1385,8 +1303,7 @@ ostree_repo_static_delta_generate (OstreeRepo *self,
|
||||
}
|
||||
|
||||
part_headers = g_variant_builder_new (G_VARIANT_TYPE ("a" OSTREE_STATIC_DELTA_META_ENTRY_FORMAT));
|
||||
part_temp_paths = g_ptr_array_new_with_free_func (g_free);
|
||||
part_temp_fds = g_array_new (FALSE, TRUE, sizeof(int));
|
||||
part_temp_paths = g_ptr_array_new_with_free_func ((GDestroyNotify)glnx_tmpfile_clear);
|
||||
for (i = 0; i < builder.parts->len; i++)
|
||||
{
|
||||
OstreeStaticDeltaPartBuilder *part_builder = builder.parts->pdata[i];
|
||||
@@ -1413,14 +1330,14 @@ ostree_repo_static_delta_generate (OstreeRepo *self,
|
||||
{ guint j;
|
||||
for (j = 0; j < part_builder->modes->len; j++)
|
||||
g_variant_builder_add_value (&mode_builder, part_builder->modes->pdata[j]);
|
||||
|
||||
|
||||
for (j = 0; j < part_builder->xattrs->len; j++)
|
||||
g_variant_builder_add_value (&xattr_builder, part_builder->xattrs->pdata[j]);
|
||||
}
|
||||
|
||||
|
||||
payload_b = g_string_free_to_bytes (part_builder->payload);
|
||||
part_builder->payload = NULL;
|
||||
|
||||
|
||||
operations_b = g_string_free_to_bytes (part_builder->operations);
|
||||
part_builder->operations = NULL;
|
||||
/* FIXME - avoid duplicating memory here */
|
||||
@@ -1459,17 +1376,15 @@ ostree_repo_static_delta_generate (OstreeRepo *self,
|
||||
}
|
||||
else
|
||||
{
|
||||
char *part_tempfile;
|
||||
int part_temp_fd;
|
||||
GLnxTmpfile *part_tmpf = g_new0 (GLnxTmpfile, 1);
|
||||
|
||||
if (!glnx_open_tmpfile_linkable_at (tmp_dfd, ".", O_WRONLY | O_CLOEXEC,
|
||||
&part_temp_fd, &part_tempfile, error))
|
||||
part_tmpf, error))
|
||||
goto out;
|
||||
|
||||
/* Transfer tempfile ownership to arrays */
|
||||
g_array_append_val (part_temp_fds, part_temp_fd);
|
||||
g_ptr_array_add (part_temp_paths, g_steal_pointer (&part_tempfile));
|
||||
part_temp_outstream = g_unix_output_stream_new (part_temp_fd, FALSE);
|
||||
/* Transfer tempfile ownership */
|
||||
part_temp_outstream = g_unix_output_stream_new (part_tmpf->fd, FALSE);
|
||||
g_ptr_array_add (part_temp_paths, g_steal_pointer (&part_tmpf));
|
||||
}
|
||||
|
||||
part_in = ot_variant_read (delta_part);
|
||||
@@ -1528,17 +1443,16 @@ ostree_repo_static_delta_generate (OstreeRepo *self,
|
||||
{
|
||||
g_autofree char *partstr = g_strdup_printf ("%u", i);
|
||||
/* Take ownership of the path/fd here */
|
||||
g_autofree char *path = g_steal_pointer (&part_temp_paths->pdata[i]);
|
||||
glnx_fd_close int fd = g_array_index (part_temp_fds, int, i);
|
||||
g_array_index (part_temp_fds, int, i) = -1;
|
||||
g_auto(GLnxTmpfile) tmpf = *((GLnxTmpfile*)part_temp_paths->pdata[i]);
|
||||
g_clear_pointer (&(part_temp_paths->pdata[i]), g_free);
|
||||
|
||||
if (fchmod (fd, 0644) < 0)
|
||||
if (fchmod (tmpf.fd, 0644) < 0)
|
||||
{
|
||||
glnx_set_error_from_errno (error);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!glnx_link_tmpfile_at (tmp_dfd, GLNX_LINK_TMPFILE_REPLACE, fd, path,
|
||||
if (!glnx_link_tmpfile_at (&tmpf, GLNX_LINK_TMPFILE_REPLACE,
|
||||
descriptor_dfd, partstr, error))
|
||||
goto out;
|
||||
}
|
||||
@@ -1598,14 +1512,6 @@ ostree_repo_static_delta_generate (OstreeRepo *self,
|
||||
|
||||
ret = TRUE;
|
||||
out:
|
||||
if (part_temp_fds)
|
||||
for (i = 0; i < part_temp_fds->len; i++)
|
||||
{
|
||||
int fd = g_array_index (part_temp_fds, int, i);
|
||||
if (fd == -1)
|
||||
continue;
|
||||
(void) close (fd);
|
||||
}
|
||||
g_clear_pointer (&builder.parts, g_ptr_array_unref);
|
||||
g_clear_pointer (&builder.fallback_objects, g_ptr_array_unref);
|
||||
return ret;
|
||||
|
||||
@@ -65,7 +65,7 @@ _ostree_static_delta_parse_checksum_array (GVariant *array,
|
||||
*
|
||||
* This function synchronously enumerates all static deltas in the
|
||||
* repository, returning its result in @out_deltas.
|
||||
*/
|
||||
*/
|
||||
gboolean
|
||||
ostree_repo_list_static_delta_names (OstreeRepo *self,
|
||||
GPtrArray **out_deltas,
|
||||
@@ -165,31 +165,30 @@ _ostree_repo_static_delta_part_have_all_objects (OstreeRepo *repo,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
gboolean ret = FALSE;
|
||||
guint8 *checksums_data;
|
||||
guint i,n_checksums;
|
||||
guint n_checksums;
|
||||
gboolean have_object = TRUE;
|
||||
|
||||
if (!_ostree_static_delta_parse_checksum_array (checksum_array,
|
||||
&checksums_data,
|
||||
&n_checksums,
|
||||
error))
|
||||
goto out;
|
||||
return FALSE;
|
||||
|
||||
for (i = 0; i < n_checksums; i++)
|
||||
for (guint i = 0; i < n_checksums; i++)
|
||||
{
|
||||
guint8 objtype = *checksums_data;
|
||||
const guint8 *csum = checksums_data + 1;
|
||||
char tmp_checksum[OSTREE_SHA256_STRING_LEN+1];
|
||||
|
||||
if (G_UNLIKELY(!ostree_validate_structureof_objtype (objtype, error)))
|
||||
goto out;
|
||||
return FALSE;
|
||||
|
||||
ostree_checksum_inplace_from_bytes (csum, tmp_checksum);
|
||||
|
||||
if (!ostree_repo_has_object (repo, (OstreeObjectType) objtype, tmp_checksum,
|
||||
&have_object, cancellable, error))
|
||||
goto out;
|
||||
return FALSE;
|
||||
|
||||
if (!have_object)
|
||||
break;
|
||||
@@ -197,10 +196,8 @@ _ostree_repo_static_delta_part_have_all_objects (OstreeRepo *repo,
|
||||
checksums_data += OSTREE_STATIC_DELTA_OBJTYPE_CSUM_LEN;
|
||||
}
|
||||
|
||||
ret = TRUE;
|
||||
*out_have_all = have_object;
|
||||
out:
|
||||
return ret;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -223,57 +220,43 @@ ostree_repo_static_delta_execute_offline (OstreeRepo *self,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
gboolean ret = FALSE;
|
||||
guint i, n;
|
||||
const char *dir_or_file_path = NULL;
|
||||
glnx_fd_close int meta_fd = -1;
|
||||
glnx_fd_close int dfd = -1;
|
||||
g_autoptr(GVariant) meta = NULL;
|
||||
g_autoptr(GVariant) headers = NULL;
|
||||
g_autoptr(GVariant) metadata = NULL;
|
||||
g_autoptr(GVariant) fallback = NULL;
|
||||
g_autofree char *to_checksum = NULL;
|
||||
g_autofree char *from_checksum = NULL;
|
||||
g_autofree char *basename = NULL;
|
||||
|
||||
dir_or_file_path = gs_file_get_path_cached (dir_or_file);
|
||||
const char *dir_or_file_path = gs_file_get_path_cached (dir_or_file);
|
||||
|
||||
/* First, try opening it as a directory */
|
||||
dfd = glnx_opendirat_with_errno (AT_FDCWD, dir_or_file_path, TRUE);
|
||||
glnx_fd_close int dfd = glnx_opendirat_with_errno (AT_FDCWD, dir_or_file_path, TRUE);
|
||||
if (dfd < 0)
|
||||
{
|
||||
if (errno != ENOTDIR)
|
||||
{
|
||||
glnx_set_error_from_errno (error);
|
||||
goto out;
|
||||
}
|
||||
return glnx_throw_errno_prefix (error, "openat(O_DIRECTORY)");
|
||||
else
|
||||
{
|
||||
g_autofree char *dir = dirname (g_strdup (dir_or_file_path));
|
||||
basename = g_path_get_basename (dir_or_file_path);
|
||||
|
||||
if (!glnx_opendirat (AT_FDCWD, dir, TRUE, &dfd, error))
|
||||
goto out;
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
else
|
||||
basename = g_strdup ("superblock");
|
||||
|
||||
meta_fd = openat (dfd, basename, O_RDONLY | O_CLOEXEC);
|
||||
glnx_fd_close int meta_fd = openat (dfd, basename, O_RDONLY | O_CLOEXEC);
|
||||
if (meta_fd < 0)
|
||||
{
|
||||
glnx_set_error_from_errno (error);
|
||||
goto out;
|
||||
}
|
||||
|
||||
return glnx_throw_errno_prefix (error, "openat(%s)", basename);
|
||||
|
||||
g_autoptr(GVariant) meta = NULL;
|
||||
if (!ot_util_variant_map_fd (meta_fd, 0, G_VARIANT_TYPE (OSTREE_STATIC_DELTA_SUPERBLOCK_FORMAT),
|
||||
FALSE, &meta, error))
|
||||
goto out;
|
||||
return FALSE;
|
||||
|
||||
/* Parsing OSTREE_STATIC_DELTA_SUPERBLOCK_FORMAT */
|
||||
|
||||
metadata = g_variant_get_child_value (meta, 0);
|
||||
g_autoptr(GVariant) metadata = g_variant_get_child_value (meta, 0);
|
||||
|
||||
g_autofree char *to_checksum = NULL;
|
||||
g_autofree char *from_checksum = NULL;
|
||||
/* Write the to-commit object */
|
||||
{
|
||||
g_autoptr(GVariant) to_csum_v = NULL;
|
||||
@@ -284,32 +267,28 @@ ostree_repo_static_delta_execute_offline (OstreeRepo *self,
|
||||
|
||||
to_csum_v = g_variant_get_child_value (meta, 3);
|
||||
if (!ostree_validate_structureof_csum_v (to_csum_v, error))
|
||||
goto out;
|
||||
return FALSE;
|
||||
to_checksum = ostree_checksum_from_bytes_v (to_csum_v);
|
||||
|
||||
from_csum_v = g_variant_get_child_value (meta, 2);
|
||||
if (g_variant_n_children (from_csum_v) > 0)
|
||||
{
|
||||
if (!ostree_validate_structureof_csum_v (from_csum_v, error))
|
||||
goto out;
|
||||
return FALSE;
|
||||
from_checksum = ostree_checksum_from_bytes_v (from_csum_v);
|
||||
|
||||
if (!ostree_repo_has_object (self, OSTREE_OBJECT_TYPE_COMMIT, from_checksum,
|
||||
&have_from_commit, cancellable, error))
|
||||
goto out;
|
||||
return FALSE;
|
||||
|
||||
if (!have_from_commit)
|
||||
{
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||
"Commit %s, which is the delta source, is not in repository", from_checksum);
|
||||
goto out;
|
||||
}
|
||||
return glnx_throw (error, "Commit %s, which is the delta source, is not in repository", from_checksum);
|
||||
}
|
||||
|
||||
if (!ostree_repo_has_object (self, OSTREE_OBJECT_TYPE_COMMIT, to_checksum,
|
||||
&have_to_commit, cancellable, error))
|
||||
goto out;
|
||||
|
||||
return FALSE;
|
||||
|
||||
if (!have_to_commit)
|
||||
{
|
||||
g_autofree char *detached_path = _ostree_get_relative_static_delta_path (from_checksum, to_checksum, "commitmeta");
|
||||
@@ -321,27 +300,23 @@ ostree_repo_static_delta_execute_offline (OstreeRepo *self,
|
||||
detached_data,
|
||||
cancellable,
|
||||
error))
|
||||
goto out;
|
||||
return FALSE;
|
||||
|
||||
to_commit = g_variant_get_child_value (meta, 4);
|
||||
if (!ostree_repo_write_metadata (self, OSTREE_OBJECT_TYPE_COMMIT,
|
||||
to_checksum, to_commit, NULL,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
fallback = g_variant_get_child_value (meta, 7);
|
||||
g_autoptr(GVariant) fallback = g_variant_get_child_value (meta, 7);
|
||||
if (g_variant_n_children (fallback) > 0)
|
||||
{
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||
"Cannot execute delta offline: contains nonempty http fallback entries");
|
||||
goto out;
|
||||
}
|
||||
return glnx_throw (error, "Cannot execute delta offline: contains nonempty http fallback entries");
|
||||
|
||||
headers = g_variant_get_child_value (meta, 6);
|
||||
n = g_variant_n_children (headers);
|
||||
for (i = 0; i < n; i++)
|
||||
g_autoptr(GVariant) headers = g_variant_get_child_value (meta, 6);
|
||||
const guint n = g_variant_n_children (headers);
|
||||
for (guint i = 0; i < n; i++)
|
||||
{
|
||||
guint32 version;
|
||||
guint64 size;
|
||||
@@ -349,29 +324,21 @@ ostree_repo_static_delta_execute_offline (OstreeRepo *self,
|
||||
const guchar *csum;
|
||||
char checksum[OSTREE_SHA256_STRING_LEN+1];
|
||||
gboolean have_all;
|
||||
g_autoptr(GInputStream) part_in = NULL;
|
||||
g_autoptr(GVariant) inline_part_data = NULL;
|
||||
g_autoptr(GVariant) header = NULL;
|
||||
g_autoptr(GVariant) csum_v = NULL;
|
||||
g_autoptr(GVariant) objects = NULL;
|
||||
g_autoptr(GVariant) part = NULL;
|
||||
g_autofree char *deltapart_path = NULL;
|
||||
OstreeStaticDeltaOpenFlags delta_open_flags =
|
||||
OstreeStaticDeltaOpenFlags delta_open_flags =
|
||||
skip_validation ? OSTREE_STATIC_DELTA_OPEN_FLAGS_SKIP_CHECKSUM : 0;
|
||||
|
||||
header = g_variant_get_child_value (headers, i);
|
||||
g_autoptr(GVariant) header = g_variant_get_child_value (headers, i);
|
||||
g_variant_get (header, "(u@aytt@ay)", &version, &csum_v, &size, &usize, &objects);
|
||||
|
||||
if (version > OSTREE_DELTAPART_VERSION)
|
||||
{
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||
"Delta part has too new version %u", version);
|
||||
goto out;
|
||||
}
|
||||
return glnx_throw (error, "Delta part has too new version %u", version);
|
||||
|
||||
if (!_ostree_repo_static_delta_part_have_all_objects (self, objects, &have_all,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
return FALSE;
|
||||
|
||||
/* If we already have these objects, don't bother executing the
|
||||
* static delta.
|
||||
@@ -381,13 +348,14 @@ ostree_repo_static_delta_execute_offline (OstreeRepo *self,
|
||||
|
||||
csum = ostree_checksum_bytes_peek_validate (csum_v, error);
|
||||
if (!csum)
|
||||
goto out;
|
||||
return FALSE;
|
||||
ostree_checksum_inplace_from_bytes (csum, checksum);
|
||||
|
||||
deltapart_path =
|
||||
_ostree_get_relative_static_delta_part_path (from_checksum, to_checksum, i);
|
||||
|
||||
inline_part_data = g_variant_lookup_value (metadata, deltapart_path, G_VARIANT_TYPE("(yay)"));
|
||||
g_autoptr(GInputStream) part_in = NULL;
|
||||
g_autoptr(GVariant) inline_part_data = g_variant_lookup_value (metadata, deltapart_path, G_VARIANT_TYPE("(yay)"));
|
||||
if (inline_part_data)
|
||||
{
|
||||
g_autoptr(GBytes) inline_part_bytes = g_variant_get_data_as_bytes (inline_part_data);
|
||||
@@ -405,40 +373,31 @@ ostree_repo_static_delta_execute_offline (OstreeRepo *self,
|
||||
NULL,
|
||||
&part,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
return FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
g_autofree char *relpath = g_strdup_printf ("%u", i); /* TODO avoid malloc here */
|
||||
glnx_fd_close int part_fd = openat (dfd, relpath, O_RDONLY | O_CLOEXEC);
|
||||
if (part_fd < 0)
|
||||
{
|
||||
glnx_set_error_from_errno (error);
|
||||
g_prefix_error (error, "Opening deltapart '%s': ", deltapart_path);
|
||||
goto out;
|
||||
}
|
||||
return glnx_throw_errno_prefix (error, "Opening deltapart '%s'", relpath);
|
||||
|
||||
part_in = g_unix_input_stream_new (part_fd, FALSE);
|
||||
|
||||
if (!_ostree_static_delta_part_open (part_in, NULL,
|
||||
if (!_ostree_static_delta_part_open (part_in, NULL,
|
||||
delta_open_flags,
|
||||
checksum,
|
||||
&part,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!_ostree_static_delta_part_execute (self, objects, part, skip_validation,
|
||||
NULL, cancellable, error))
|
||||
{
|
||||
g_prefix_error (error, "Executing delta part %i: ", i);
|
||||
goto out;
|
||||
}
|
||||
return glnx_prefix_error (error, "Executing delta part %i", i);
|
||||
}
|
||||
|
||||
ret = TRUE;
|
||||
out:
|
||||
return ret;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
@@ -450,14 +409,12 @@ _ostree_static_delta_part_open (GInputStream *part_in,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
gboolean ret = FALSE;
|
||||
const gboolean trusted = (flags & OSTREE_STATIC_DELTA_OPEN_FLAGS_VARIANT_TRUSTED) > 0;
|
||||
const gboolean skip_checksum = (flags & OSTREE_STATIC_DELTA_OPEN_FLAGS_SKIP_CHECKSUM) > 0;
|
||||
gsize bytes_read;
|
||||
guint8 comptype;
|
||||
g_autoptr(GChecksum) checksum = NULL;
|
||||
g_autoptr(GInputStream) checksum_in = NULL;
|
||||
g_autoptr(GVariant) ret_part = NULL;
|
||||
GInputStream *source_in;
|
||||
|
||||
/* We either take a fd or a GBytes reference */
|
||||
@@ -479,13 +436,11 @@ _ostree_static_delta_part_open (GInputStream *part_in,
|
||||
/* First byte is compression type */
|
||||
if (!g_input_stream_read_all (source_in, buf, sizeof(buf), &bytes_read,
|
||||
cancellable, error))
|
||||
{
|
||||
g_prefix_error (error, "Reading initial compression flag byte: ");
|
||||
goto out;
|
||||
}
|
||||
return glnx_prefix_error (error, "Reading initial compression flag byte");
|
||||
comptype = buf[0];
|
||||
}
|
||||
|
||||
g_autoptr(GVariant) ret_part = NULL;
|
||||
switch (comptype)
|
||||
{
|
||||
case 0:
|
||||
@@ -496,7 +451,7 @@ _ostree_static_delta_part_open (GInputStream *part_in,
|
||||
/* No compression, no checksums - a fast path */
|
||||
if (!ot_util_variant_map_fd (part_fd, 1, G_VARIANT_TYPE (OSTREE_STATIC_DELTA_PART_PAYLOAD_FORMAT_V0),
|
||||
trusted, &ret_part, error))
|
||||
goto out;
|
||||
return FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -510,51 +465,22 @@ _ostree_static_delta_part_open (GInputStream *part_in,
|
||||
if (!skip_checksum)
|
||||
g_checksum_update (checksum, g_variant_get_data (ret_part),
|
||||
g_variant_get_size (ret_part));
|
||||
|
||||
|
||||
break;
|
||||
case 'x':
|
||||
{
|
||||
g_autofree char *tmppath = g_strdup ("/var/tmp/ostree-delta-XXXXXX");
|
||||
g_autoptr(GConverter) decomp = (GConverter*) _ostree_lzma_decompressor_new ();
|
||||
g_autoptr(GInputStream) convin = g_converter_input_stream_new (source_in, decomp);
|
||||
g_autoptr(GOutputStream) unpacked_out = NULL;
|
||||
glnx_fd_close int unpacked_fd = -1;
|
||||
gssize n_bytes_written;
|
||||
g_autoptr(GBytes) buf = ot_map_anonymous_tmpfile_from_content (convin, cancellable, error);
|
||||
if (!buf)
|
||||
return FALSE;
|
||||
|
||||
unpacked_fd = g_mkstemp_full (tmppath, O_RDWR | O_CLOEXEC, 0640);
|
||||
if (unpacked_fd < 0)
|
||||
{
|
||||
glnx_set_error_from_errno (error);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Now make it autocleanup on process exit - in the future, we
|
||||
* should consider caching unpacked deltas as well.
|
||||
*/
|
||||
if (unlink (tmppath) < 0)
|
||||
{
|
||||
glnx_set_error_from_errno (error);
|
||||
goto out;
|
||||
}
|
||||
|
||||
unpacked_out = g_unix_output_stream_new (unpacked_fd, FALSE);
|
||||
|
||||
n_bytes_written = g_output_stream_splice (unpacked_out, convin,
|
||||
G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE |
|
||||
G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET,
|
||||
cancellable, error);
|
||||
if (n_bytes_written < 0)
|
||||
goto out;
|
||||
|
||||
if (!ot_util_variant_map_fd (unpacked_fd, 0, G_VARIANT_TYPE (OSTREE_STATIC_DELTA_PART_PAYLOAD_FORMAT_V0),
|
||||
trusted, &ret_part, error))
|
||||
goto out;
|
||||
ret_part = g_variant_new_from_bytes (G_VARIANT_TYPE (OSTREE_STATIC_DELTA_PART_PAYLOAD_FORMAT_V0),
|
||||
buf, FALSE);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||
"Invalid compression type '%u'", comptype);
|
||||
goto out;
|
||||
return glnx_throw (error, "Invalid compression type '%u'", comptype);
|
||||
}
|
||||
|
||||
if (checksum)
|
||||
@@ -562,18 +488,12 @@ _ostree_static_delta_part_open (GInputStream *part_in,
|
||||
const char *actual_checksum = g_checksum_get_string (checksum);
|
||||
g_assert (expected_checksum != NULL);
|
||||
if (strcmp (actual_checksum, expected_checksum) != 0)
|
||||
{
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||
"Checksum mismatch in static delta part; expected=%s actual=%s",
|
||||
expected_checksum, actual_checksum);
|
||||
goto out;
|
||||
}
|
||||
return glnx_throw (error, "Checksum mismatch in static delta part; expected=%s actual=%s",
|
||||
expected_checksum, actual_checksum);
|
||||
}
|
||||
|
||||
ret = TRUE;
|
||||
|
||||
*out_part = g_steal_pointer (&ret_part);
|
||||
out:
|
||||
return ret;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -592,15 +512,12 @@ show_one_part (OstreeRepo *self,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
gboolean ret = FALSE;
|
||||
g_autoptr(GVariant) part = NULL;
|
||||
g_autofree char *part_path = _ostree_get_relative_static_delta_part_path (from, to, i);
|
||||
|
||||
guint32 version;
|
||||
guint64 size, usize;
|
||||
g_autoptr(GVariant) objects = NULL;
|
||||
g_autoptr(GInputStream) part_in = NULL;
|
||||
g_autoptr(GVariant) part = NULL;
|
||||
g_autofree char *part_path = _ostree_get_relative_static_delta_part_path (from, to, i);
|
||||
gint part_fd = -1;
|
||||
|
||||
g_variant_get_child (meta_entries, i, "(u@aytt@ay)", &version, NULL, &size, &usize, &objects);
|
||||
size = maybe_swap_endian_u64 (swap_endian, size);
|
||||
usize = maybe_swap_endian_u64 (swap_endian, usize);
|
||||
@@ -609,21 +526,17 @@ show_one_part (OstreeRepo *self,
|
||||
g_print ("PartMeta%u: nobjects=%u size=%" G_GUINT64_FORMAT " usize=%" G_GUINT64_FORMAT "\n",
|
||||
i, (guint)(g_variant_get_size (objects) / OSTREE_STATIC_DELTA_OBJTYPE_CSUM_LEN), size, usize);
|
||||
|
||||
part_fd = openat (self->repo_dir_fd, part_path, O_RDONLY | O_CLOEXEC);
|
||||
glnx_fd_close gint part_fd = openat (self->repo_dir_fd, part_path, O_RDONLY | O_CLOEXEC);
|
||||
if (part_fd < 0)
|
||||
{
|
||||
glnx_set_error_from_errno (error);
|
||||
goto out;
|
||||
}
|
||||
return glnx_throw_errno_prefix (error, "openat(%s)", part_path);
|
||||
g_autoptr(GInputStream) part_in = g_unix_input_stream_new (part_fd, FALSE);
|
||||
|
||||
part_in = g_unix_input_stream_new (part_fd, FALSE);
|
||||
|
||||
if (!_ostree_static_delta_part_open (part_in, NULL,
|
||||
if (!_ostree_static_delta_part_open (part_in, NULL,
|
||||
OSTREE_STATIC_DELTA_OPEN_FLAGS_SKIP_CHECKSUM,
|
||||
NULL,
|
||||
&part,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
return FALSE;
|
||||
|
||||
{ g_autoptr(GVariant) modes = NULL;
|
||||
g_autoptr(GVariant) xattrs = NULL;
|
||||
@@ -648,7 +561,7 @@ show_one_part (OstreeRepo *self,
|
||||
if (!_ostree_static_delta_part_execute (self, objects,
|
||||
part, TRUE,
|
||||
&stats, cancellable, error))
|
||||
goto out;
|
||||
return FALSE;
|
||||
|
||||
{ const guint *n_ops = stats.n_ops_executed;
|
||||
g_print ("PartPayloadOps%u: openspliceclose=%u open=%u write=%u setread=%u "
|
||||
@@ -656,10 +569,8 @@ show_one_part (OstreeRepo *self,
|
||||
i, n_ops[0], n_ops[1], n_ops[2], n_ops[3], n_ops[4], n_ops[5], n_ops[6]);
|
||||
}
|
||||
}
|
||||
|
||||
ret = TRUE;
|
||||
out:
|
||||
return ret;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
OstreeDeltaEndianness
|
||||
@@ -781,35 +692,30 @@ _ostree_repo_static_delta_delete (OstreeRepo *self,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
gboolean ret = FALSE;
|
||||
g_autofree char *from = NULL;
|
||||
g_autofree char *to = NULL;
|
||||
g_autofree char *deltadir = NULL;
|
||||
struct stat buf;
|
||||
|
||||
if (!_ostree_parse_delta_name (delta_id, &from, &to, error))
|
||||
goto out;
|
||||
|
||||
deltadir = _ostree_get_relative_static_delta_path (from, to, NULL);
|
||||
return FALSE;
|
||||
|
||||
g_autofree char *deltadir = _ostree_get_relative_static_delta_path (from, to, NULL);
|
||||
struct stat buf;
|
||||
if (fstatat (self->repo_dir_fd, deltadir, &buf, 0) != 0)
|
||||
{
|
||||
if (errno == ENOENT)
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
|
||||
"Can't find delta %s", delta_id);
|
||||
{
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
|
||||
"Can't find delta %s", delta_id);
|
||||
return FALSE;
|
||||
}
|
||||
else
|
||||
glnx_set_error_from_errno (error);
|
||||
|
||||
goto out;
|
||||
return glnx_throw_errno_prefix (error, "fstatat(%s)", deltadir);
|
||||
}
|
||||
|
||||
if (!glnx_shutil_rm_rf_at (self->repo_dir_fd, deltadir,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
return FALSE;
|
||||
|
||||
ret = TRUE;
|
||||
out:
|
||||
return ret;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
@@ -821,13 +727,12 @@ _ostree_repo_static_delta_query_exists (OstreeRepo *self,
|
||||
{
|
||||
g_autofree char *from = NULL;
|
||||
g_autofree char *to = NULL;
|
||||
g_autofree char *superblock_path = NULL;
|
||||
struct stat stbuf;
|
||||
|
||||
if (!_ostree_parse_delta_name (delta_id, &from, &to, error))
|
||||
return FALSE;
|
||||
|
||||
superblock_path = _ostree_get_relative_static_delta_superblock_path (from, to);
|
||||
g_autofree char *superblock_path = _ostree_get_relative_static_delta_superblock_path (from, to);
|
||||
|
||||
if (fstatat (self->repo_dir_fd, superblock_path, &stbuf, 0) < 0)
|
||||
{
|
||||
@@ -837,10 +742,7 @@ _ostree_repo_static_delta_query_exists (OstreeRepo *self,
|
||||
return TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
glnx_set_error_from_errno (error);
|
||||
return FALSE;
|
||||
}
|
||||
return glnx_throw_errno_prefix (error, "fstatat(%s)", superblock_path);
|
||||
}
|
||||
*out_exists = TRUE;
|
||||
return TRUE;
|
||||
@@ -852,28 +754,27 @@ _ostree_repo_static_delta_dump (OstreeRepo *self,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
gboolean ret = FALSE;
|
||||
g_autofree char *from = NULL;
|
||||
g_autofree char *from = NULL;
|
||||
g_autofree char *to = NULL;
|
||||
g_autofree char *superblock_path = NULL;
|
||||
g_autoptr(GVariant) delta_superblock = NULL;
|
||||
guint64 total_size = 0, total_usize = 0;
|
||||
guint64 total_fallback_size = 0, total_fallback_usize = 0;
|
||||
guint i;
|
||||
OstreeDeltaEndianness endianness;
|
||||
gboolean swap_endian = FALSE;
|
||||
|
||||
if (!_ostree_parse_delta_name (delta_id, &from, &to, error))
|
||||
goto out;
|
||||
return FALSE;
|
||||
|
||||
superblock_path = _ostree_get_relative_static_delta_superblock_path (from, to);
|
||||
|
||||
if (!ot_util_variant_map_at (self->repo_dir_fd, superblock_path,
|
||||
(GVariantType*)OSTREE_STATIC_DELTA_SUPERBLOCK_FORMAT,
|
||||
OT_VARIANT_MAP_TRUSTED, &delta_superblock, error))
|
||||
goto out;
|
||||
return FALSE;
|
||||
|
||||
g_print ("Delta: %s\n", delta_id);
|
||||
|
||||
{ const char *endianness_description;
|
||||
gboolean was_heuristic;
|
||||
|
||||
@@ -903,65 +804,63 @@ _ostree_repo_static_delta_dump (OstreeRepo *self,
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
|
||||
|
||||
g_print ("Endianness: %s\n", endianness_description);
|
||||
}
|
||||
{ guint64 ts;
|
||||
g_variant_get_child (delta_superblock, 1, "t", &ts);
|
||||
g_print ("Timestamp: %" G_GUINT64_FORMAT "\n", GUINT64_FROM_BE (ts));
|
||||
}
|
||||
{ g_autoptr(GVariant) recurse = NULL;
|
||||
g_variant_get_child (delta_superblock, 5, "@ay", &recurse);
|
||||
g_print ("Number of parents: %u\n", (guint)(g_variant_get_size (recurse) / (OSTREE_SHA256_DIGEST_LEN * 2)));
|
||||
}
|
||||
{ g_autoptr(GVariant) fallback = NULL;
|
||||
guint n_fallback;
|
||||
guint64 ts;
|
||||
g_variant_get_child (delta_superblock, 1, "t", &ts);
|
||||
g_print ("Timestamp: %" G_GUINT64_FORMAT "\n", GUINT64_FROM_BE (ts));
|
||||
|
||||
g_variant_get_child (delta_superblock, 7, "@a" OSTREE_STATIC_DELTA_FALLBACK_FORMAT, &fallback);
|
||||
n_fallback = g_variant_n_children (fallback);
|
||||
g_autoptr(GVariant) recurse = NULL;
|
||||
g_variant_get_child (delta_superblock, 5, "@ay", &recurse);
|
||||
g_print ("Number of parents: %u\n", (guint)(g_variant_get_size (recurse) / (OSTREE_SHA256_DIGEST_LEN * 2)));
|
||||
|
||||
g_print ("Number of fallback entries: %u\n", n_fallback);
|
||||
g_autoptr(GVariant) fallback = NULL;
|
||||
g_variant_get_child (delta_superblock, 7, "@a" OSTREE_STATIC_DELTA_FALLBACK_FORMAT, &fallback);
|
||||
const guint n_fallback = g_variant_n_children (fallback);
|
||||
|
||||
for (i = 0; i < n_fallback; i++)
|
||||
{
|
||||
guint64 size, usize;
|
||||
g_autoptr(GVariant) checksum_v = NULL;
|
||||
char checksum[OSTREE_SHA256_STRING_LEN+1];
|
||||
g_variant_get_child (fallback, i, "(y@aytt)", NULL, &checksum_v, &size, &usize);
|
||||
ostree_checksum_inplace_from_bytes (ostree_checksum_bytes_peek (checksum_v), checksum);
|
||||
size = maybe_swap_endian_u64 (swap_endian, size);
|
||||
usize = maybe_swap_endian_u64 (swap_endian, usize);
|
||||
g_print (" %s\n", checksum);
|
||||
total_fallback_size += size;
|
||||
total_fallback_usize += usize;
|
||||
}
|
||||
{ g_autofree char *sizestr = g_format_size (total_fallback_size);
|
||||
g_autofree char *usizestr = g_format_size (total_fallback_usize);
|
||||
g_print ("Total Fallback Size: %" G_GUINT64_FORMAT " (%s)\n", total_fallback_size, sizestr);
|
||||
g_print ("Total Fallback Uncompressed Size: %" G_GUINT64_FORMAT " (%s)\n", total_fallback_usize, usizestr);
|
||||
g_print ("Number of fallback entries: %u\n", n_fallback);
|
||||
|
||||
for (guint i = 0; i < n_fallback; i++)
|
||||
{
|
||||
guint64 size, usize;
|
||||
g_autoptr(GVariant) checksum_v = NULL;
|
||||
char checksum[OSTREE_SHA256_STRING_LEN+1];
|
||||
g_variant_get_child (fallback, i, "(y@aytt)", NULL, &checksum_v, &size, &usize);
|
||||
ostree_checksum_inplace_from_bytes (ostree_checksum_bytes_peek (checksum_v), checksum);
|
||||
size = maybe_swap_endian_u64 (swap_endian, size);
|
||||
usize = maybe_swap_endian_u64 (swap_endian, usize);
|
||||
g_print (" %s\n", checksum);
|
||||
total_fallback_size += size;
|
||||
total_fallback_usize += usize;
|
||||
}
|
||||
{ g_autofree char *sizestr = g_format_size (total_fallback_size);
|
||||
g_autofree char *usizestr = g_format_size (total_fallback_usize);
|
||||
g_print ("Total Fallback Size: %" G_GUINT64_FORMAT " (%s)\n", total_fallback_size, sizestr);
|
||||
g_print ("Total Fallback Uncompressed Size: %" G_GUINT64_FORMAT " (%s)\n", total_fallback_usize, usizestr);
|
||||
}
|
||||
{ g_autoptr(GVariant) meta_entries = NULL;
|
||||
guint n_parts;
|
||||
|
||||
g_variant_get_child (delta_superblock, 6, "@a" OSTREE_STATIC_DELTA_META_ENTRY_FORMAT, &meta_entries);
|
||||
n_parts = g_variant_n_children (meta_entries);
|
||||
g_print ("Number of parts: %u\n", n_parts);
|
||||
g_autoptr(GVariant) meta_entries = NULL;
|
||||
guint n_parts;
|
||||
|
||||
for (i = 0; i < n_parts; i++)
|
||||
{
|
||||
if (!show_one_part (self, swap_endian, from, to, meta_entries, i,
|
||||
&total_size, &total_usize,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
g_variant_get_child (delta_superblock, 6, "@a" OSTREE_STATIC_DELTA_META_ENTRY_FORMAT, &meta_entries);
|
||||
n_parts = g_variant_n_children (meta_entries);
|
||||
g_print ("Number of parts: %u\n", n_parts);
|
||||
|
||||
for (guint i = 0; i < n_parts; i++)
|
||||
{
|
||||
if (!show_one_part (self, swap_endian, from, to, meta_entries, i,
|
||||
&total_size, &total_usize,
|
||||
cancellable, error))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
{ g_autofree char *sizestr = g_format_size (total_size);
|
||||
g_autofree char *usizestr = g_format_size (total_usize);
|
||||
g_print ("Total Part Size: %" G_GUINT64_FORMAT " (%s)\n", total_size, sizestr);
|
||||
g_print ("Total Part Uncompressed Size: %" G_GUINT64_FORMAT " (%s)\n", total_usize, usizestr);
|
||||
}
|
||||
|
||||
{ guint64 overall_size = total_size + total_fallback_size;
|
||||
guint64 overall_usize = total_usize + total_fallback_usize;
|
||||
g_autofree char *sizestr = g_format_size (overall_size);
|
||||
@@ -970,7 +869,5 @@ _ostree_repo_static_delta_dump (OstreeRepo *self,
|
||||
g_print ("Total Uncompressed Size: %" G_GUINT64_FORMAT " (%s)\n", overall_usize, usizestr);
|
||||
}
|
||||
|
||||
ret = TRUE;
|
||||
out:
|
||||
return ret;
|
||||
return TRUE;
|
||||
}
|
||||
@@ -56,7 +56,7 @@ typedef struct {
|
||||
GError **async_error;
|
||||
|
||||
OstreeObjectType output_objtype;
|
||||
OstreeRepoContentBareCommit barecommitstate;
|
||||
GLnxTmpfile tmpf;
|
||||
guint64 content_size;
|
||||
GOutputStream *content_out;
|
||||
GChecksum *content_checksum;
|
||||
@@ -281,6 +281,8 @@ _ostree_static_delta_part_execute (OstreeRepo *repo,
|
||||
|
||||
ret = TRUE;
|
||||
out:
|
||||
glnx_tmpfile_clear (&state->tmpf);
|
||||
g_clear_object (&state->content_out);
|
||||
g_clear_pointer (&state->content_checksum, g_checksum_free);
|
||||
return ret;
|
||||
}
|
||||
@@ -419,8 +421,6 @@ do_content_open_generic (OstreeRepo *repo,
|
||||
if (!read_varuint64 (state, &xattr_offset, error))
|
||||
goto out;
|
||||
|
||||
state->barecommitstate.fd = -1;
|
||||
|
||||
modev = g_variant_get_child_value (state->mode_dict, mode_offset);
|
||||
g_variant_get (modev, "(uuu)", &uid, &gid, &mode);
|
||||
state->uid = GUINT32_FROM_BE (uid);
|
||||
@@ -515,18 +515,15 @@ dispatch_bspatch (OstreeRepo *repo,
|
||||
static gboolean
|
||||
handle_untrusted_content_checksum (OstreeRepo *repo,
|
||||
StaticDeltaExecutionState *state,
|
||||
GCancellable *cancellable,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
g_autoptr(GVariant) header = NULL;
|
||||
g_autoptr(GFileInfo) finfo = NULL;
|
||||
gsize bytes_written;
|
||||
|
||||
finfo = _ostree_header_gfile_info_new (state->mode, state->uid, state->gid);
|
||||
header = _ostree_file_header_new (finfo, state->xattrs);
|
||||
g_autoptr(GFileInfo) finfo = _ostree_mode_uidgid_to_gfileinfo (state->mode, state->uid, state->gid);
|
||||
g_autoptr(GVariant) header = _ostree_file_header_new (finfo, state->xattrs);
|
||||
|
||||
state->content_checksum = g_checksum_new (G_CHECKSUM_SHA256);
|
||||
|
||||
gsize bytes_written;
|
||||
if (!_ostree_write_variant_with_size (NULL, header, 0, &bytes_written, state->content_checksum,
|
||||
cancellable, error))
|
||||
return FALSE;
|
||||
@@ -608,14 +605,14 @@ dispatch_open_splice_and_close (OstreeRepo *repo,
|
||||
{
|
||||
if (!_ostree_repo_open_content_bare (repo, state->checksum,
|
||||
state->content_size,
|
||||
&state->barecommitstate,
|
||||
&state->content_out,
|
||||
&state->tmpf,
|
||||
&state->have_obj,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
|
||||
if (!state->have_obj)
|
||||
{
|
||||
state->content_out = g_unix_output_stream_new (state->tmpf.fd, FALSE);
|
||||
if (!handle_untrusted_content_checksum (repo, state, cancellable, error))
|
||||
goto out;
|
||||
|
||||
@@ -629,9 +626,8 @@ dispatch_open_splice_and_close (OstreeRepo *repo,
|
||||
else
|
||||
{
|
||||
/* Slower path, for symlinks and unpacking deltas into archive-z2 */
|
||||
g_autoptr(GFileInfo) finfo = NULL;
|
||||
|
||||
finfo = _ostree_header_gfile_info_new (state->mode, state->uid, state->gid);
|
||||
g_autoptr(GFileInfo) finfo =
|
||||
_ostree_mode_uidgid_to_gfileinfo (state->mode, state->uid, state->gid);
|
||||
|
||||
if (S_ISLNK (state->mode))
|
||||
{
|
||||
@@ -711,11 +707,12 @@ dispatch_open (OstreeRepo *repo,
|
||||
|
||||
if (!_ostree_repo_open_content_bare (repo, state->checksum,
|
||||
state->content_size,
|
||||
&state->barecommitstate,
|
||||
&state->content_out,
|
||||
&state->tmpf,
|
||||
&state->have_obj,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
if (!state->have_obj)
|
||||
state->content_out = g_unix_output_stream_new (state->tmpf.fd, FALSE);
|
||||
|
||||
if (!handle_untrusted_content_checksum (repo, state, cancellable, error))
|
||||
goto out;
|
||||
@@ -830,11 +827,13 @@ dispatch_set_read_source (OstreeRepo *repo,
|
||||
|
||||
g_free (state->read_source_object);
|
||||
state->read_source_object = ostree_checksum_from_bytes (state->payload_data + source_offset);
|
||||
|
||||
if (!_ostree_repo_read_bare_fd (repo, state->read_source_object, &state->read_source_fd,
|
||||
cancellable, error))
|
||||
|
||||
if (!_ostree_repo_load_file_bare (repo, state->read_source_object,
|
||||
&state->read_source_fd,
|
||||
NULL, NULL, NULL,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
|
||||
|
||||
ret = TRUE;
|
||||
out:
|
||||
if (!ret)
|
||||
@@ -897,11 +896,12 @@ dispatch_close (OstreeRepo *repo,
|
||||
}
|
||||
}
|
||||
|
||||
if (!_ostree_repo_commit_trusted_content_bare (repo, state->checksum, &state->barecommitstate,
|
||||
if (!_ostree_repo_commit_trusted_content_bare (repo, state->checksum, &state->tmpf,
|
||||
state->uid, state->gid, state->mode,
|
||||
state->xattrs,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
g_clear_object (&state->content_out);
|
||||
}
|
||||
|
||||
if (!dispatch_unset_read_source (repo, state, cancellable, error))
|
||||
@@ -909,7 +909,6 @@ dispatch_close (OstreeRepo *repo,
|
||||
|
||||
g_clear_pointer (&state->xattrs, g_variant_unref);
|
||||
g_clear_pointer (&state->content_checksum, g_checksum_free);
|
||||
g_clear_object (&state->content_out);
|
||||
|
||||
state->checksum_index++;
|
||||
state->output_target = NULL;
|
||||
|
||||
+488
-334
File diff suppressed because it is too large.
Load diff
@@ -25,6 +25,10 @@
|
||||
#include "ostree-core.h"
|
||||
#include "ostree-types.h"
|
||||
#include "ostree-async-progress.h"
|
||||
#ifdef OSTREE_ENABLE_EXPERIMENTAL_API
|
||||
#include "ostree-ref.h"
|
||||
#include "ostree-repo-finder.h"
|
||||
#endif
|
||||
#include "ostree-sepolicy.h"
|
||||
#include "ostree-gpg-verify-result.h"
|
||||
|
||||
@@ -86,6 +90,17 @@ gboolean ostree_repo_create (OstreeRepo *self,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
#ifdef OSTREE_ENABLE_EXPERIMENTAL_API
|
||||
|
||||
_OSTREE_PUBLIC
|
||||
const gchar * ostree_repo_get_collection_id (OstreeRepo *self);
|
||||
_OSTREE_PUBLIC
|
||||
gboolean ostree_repo_set_collection_id (OstreeRepo *self,
|
||||
const gchar *collection_id,
|
||||
GError **error);
|
||||
|
||||
#endif /* OSTREE_ENABLE_EXPERIMENTAL_API */
|
||||
|
||||
_OSTREE_PUBLIC
|
||||
GFile * ostree_repo_get_path (OstreeRepo *self);
|
||||
|
||||
@@ -295,6 +310,15 @@ void ostree_repo_transaction_set_ref (OstreeRepo *self,
|
||||
const char *ref,
|
||||
const char *checksum);
|
||||
|
||||
#ifdef OSTREE_ENABLE_EXPERIMENTAL_API
|
||||
|
||||
_OSTREE_PUBLIC
|
||||
void ostree_repo_transaction_set_collection_ref (OstreeRepo *self,
|
||||
const OstreeCollectionRef *ref,
|
||||
const char *checksum);
|
||||
|
||||
#endif /* OSTREE_ENABLE_EXPERIMENTAL_API */
|
||||
|
||||
_OSTREE_PUBLIC
|
||||
gboolean ostree_repo_set_ref_immediate (OstreeRepo *self,
|
||||
const char *remote,
|
||||
@@ -303,6 +327,17 @@ gboolean ostree_repo_set_ref_immediate (OstreeRepo *self,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
#ifdef OSTREE_ENABLE_EXPERIMENTAL_API
|
||||
|
||||
_OSTREE_PUBLIC
|
||||
gboolean ostree_repo_set_collection_ref_immediate (OstreeRepo *self,
|
||||
const OstreeCollectionRef *ref,
|
||||
const char *checksum,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
#endif /* OSTREE_ENABLE_EXPERIMENTAL_API */
|
||||
|
||||
_OSTREE_PUBLIC
|
||||
gboolean ostree_repo_has_object (OstreeRepo *self,
|
||||
OstreeObjectType objtype,
|
||||
@@ -1046,6 +1081,33 @@ ostree_repo_pull_one_dir (OstreeRepo *self,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
|
||||
|
||||
#if 0
|
||||
FIXME
|
||||
Called with: remote_name, refs, override-commit-ids
|
||||
or: URL, refs, override-commit-ids
|
||||
=> we only need refs; could use the remote_name or URL as additional results
|
||||
|
||||
Summary file is downloaded first, so this would result in multiple downloads of
|
||||
the summary, but we don’t care because of caching.
|
||||
|
||||
Big problem preventing this from being the overall API: presenting the download
|
||||
sizes in the gnome-software UI before the user chooses to download.
|
||||
|
||||
_OSTREE_PUBLIC
|
||||
gboolean ostree_repo_find_remotes_squashed (OstreeRepo *self,
|
||||
const gchar * const *refs, -> options
|
||||
GVariant *options,
|
||||
OstreeRepoFinder **finders, -> options
|
||||
GMainContext *context, -> nope
|
||||
OstreeAsyncProgress *progress,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
_OSTREE_PUBLIC
|
||||
gboolean ostree_repo_pull_with_options (OstreeRepo *self,
|
||||
const char *remote_name_or_baseurl,
|
||||
@@ -1054,6 +1116,50 @@ gboolean ostree_repo_pull_with_options (OstreeRepo *self,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
#ifdef OSTREE_ENABLE_EXPERIMENTAL_API
|
||||
|
||||
_OSTREE_PUBLIC
|
||||
void ostree_repo_find_remotes_async (OstreeRepo *self,
|
||||
const OstreeCollectionRef * const *refs,
|
||||
GVariant *options,
|
||||
OstreeRepoFinder **finders,
|
||||
OstreeAsyncProgress *progress,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data);
|
||||
_OSTREE_PUBLIC
|
||||
OstreeRepoFinderResult **ostree_repo_find_remotes_finish (OstreeRepo *self,
|
||||
GAsyncResult *result,
|
||||
GError **error);
|
||||
|
||||
_OSTREE_PUBLIC
|
||||
void ostree_repo_pull_from_remotes_async (OstreeRepo *self,
|
||||
const OstreeRepoFinderResult * const *results,
|
||||
GVariant *options,
|
||||
OstreeAsyncProgress *progress,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data);
|
||||
_OSTREE_PUBLIC
|
||||
gboolean ostree_repo_pull_from_remotes_finish (OstreeRepo *self,
|
||||
GAsyncResult *result,
|
||||
GError **error);
|
||||
|
||||
_OSTREE_PUBLIC
|
||||
gchar *ostree_repo_resolve_keyring_for_collection (OstreeRepo *self,
|
||||
const gchar *collection_id,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
_OSTREE_PUBLIC
|
||||
gboolean ostree_repo_list_collection_refs (OstreeRepo *self,
|
||||
const char *match_collection_id,
|
||||
GHashTable **out_all_refs,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
#endif /* OSTREE_ENABLE_EXPERIMENTAL_API */
|
||||
|
||||
_OSTREE_PUBLIC
|
||||
void ostree_repo_pull_default_console_progress_changed (OstreeAsyncProgress *progress,
|
||||
gpointer user_data);
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <gio/gio.h>
|
||||
#include "libglnx.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
@@ -39,5 +40,6 @@ _ostree_compute_rollsum_matches (GBytes *from,
|
||||
GBytes *to);
|
||||
|
||||
void _ostree_rollsum_matches_free (OstreeRollsumMatches *rollsum);
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(OstreeRollsumMatches, _ostree_rollsum_matches_free)
|
||||
|
||||
G_END_DECLS
|
||||
@@ -73,15 +73,9 @@ symlink_at_replace (const char *oldpath,
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Rename it into place */
|
||||
do
|
||||
res = renameat (parent_dfd, temppath, parent_dfd, newpath);
|
||||
while (G_UNLIKELY (res == -1 && errno == EINTR));
|
||||
if (res == -1)
|
||||
{
|
||||
glnx_set_error_from_errno (error);
|
||||
goto out;
|
||||
}
|
||||
/* Rename it into place */
|
||||
if (!glnx_renameat (parent_dfd, temppath, parent_dfd, newpath, error))
|
||||
goto out;
|
||||
|
||||
ret = TRUE;
|
||||
out:
|
||||
@@ -819,8 +813,8 @@ merge_configuration (OstreeSysroot *sysroot,
|
||||
else if (etc_exists)
|
||||
{
|
||||
/* Compatibility hack */
|
||||
if (renameat (deployment_dfd, "etc", deployment_dfd, "usr/etc") < 0)
|
||||
return glnx_throw_errno_prefix (error, "renameat");
|
||||
if (!glnx_renameat (deployment_dfd, "etc", deployment_dfd, "usr/etc", error))
|
||||
return FALSE;
|
||||
usretc_exists = TRUE;
|
||||
etc_exists = FALSE;
|
||||
}
|
||||
@@ -1424,7 +1418,6 @@ swap_bootloader (OstreeSysroot *sysroot,
|
||||
GError **error)
|
||||
{
|
||||
glnx_fd_close int boot_dfd = -1;
|
||||
int res;
|
||||
|
||||
g_assert ((current_bootversion == 0 && new_bootversion == 1) ||
|
||||
(current_bootversion == 1 && new_bootversion == 0));
|
||||
@@ -1436,11 +1429,8 @@ swap_bootloader (OstreeSysroot *sysroot,
|
||||
* its data is in place. Renaming now should give us atomic semantics;
|
||||
* see https://bugzilla.gnome.org/show_bug.cgi?id=755595
|
||||
*/
|
||||
do
|
||||
res = renameat (boot_dfd, "loader.tmp", boot_dfd, "loader");
|
||||
while (G_UNLIKELY (res == -1 && errno == EINTR));
|
||||
if (res == -1)
|
||||
return glnx_throw_errno (error);
|
||||
if (!glnx_renameat (boot_dfd, "loader.tmp", boot_dfd, "loader", error))
|
||||
return FALSE;
|
||||
|
||||
/* Now we explicitly fsync this directory, even though it
|
||||
* isn't required for atomicity, for two reasons:
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
*
|
||||
* Since: 2017.4
|
||||
*/
|
||||
#define OSTREE_RELEASE_VERSION (7)
|
||||
#define OSTREE_RELEASE_VERSION (8)
|
||||
|
||||
/**
|
||||
* OSTREE_VERSION
|
||||
@@ -53,7 +53,7 @@
|
||||
*
|
||||
* Since: 2017.4
|
||||
*/
|
||||
#define OSTREE_VERSION (2017.7)
|
||||
#define OSTREE_VERSION (2017.8)
|
||||
|
||||
/**
|
||||
* OSTREE_VERSION_S:
|
||||
@@ -63,7 +63,7 @@
|
||||
*
|
||||
* Since: 2017.4
|
||||
*/
|
||||
#define OSTREE_VERSION_S "2017.7"
|
||||
#define OSTREE_VERSION_S "2017.8"
|
||||
|
||||
#define OSTREE_ENCODE_VERSION(year,release) \
|
||||
((year) << 16 | (release))
|
||||
|
||||
@@ -35,5 +35,13 @@
|
||||
#include <ostree-diff.h>
|
||||
#include <ostree-gpg-verify-result.h>
|
||||
|
||||
#ifdef OSTREE_ENABLE_EXPERIMENTAL_API
|
||||
#include <ostree-ref.h>
|
||||
#include <ostree-repo-finder.h>
|
||||
#include <ostree-repo-finder-avahi.h>
|
||||
#include <ostree-repo-finder-config.h>
|
||||
#include <ostree-repo-finder-mount.h>
|
||||
#endif /* OSTREE_ENABLE_EXPERIMENTAL_API */
|
||||
|
||||
#include <ostree-autocleanups.h>
|
||||
#include <ostree-version.h>
|
||||
Reference in new issue
Block a user