New upstream version 2017.8

This commit is contained in:
Simon McVittie committed 2017-07-18 22:18:02 +01:00
commit b6c0af922c
158 files changed
+12692 -2420

No files matched your search

+5
View File
@@ -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.
*/
+8
View 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
+104
View File
@@ -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
+603
View File
@@ -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);
}
+5 -12
View File
@@ -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,
+26 -1
View File
@@ -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
View File
@@ -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 (&regex_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 (&regex_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 (&regex_initialized))
{
regex = g_regex_new ("^" OSTREE_REMOTE_NAME_REGEXP "$", 0, 0, NULL);
g_assert (regex);
g_once_init_leave (&regex_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))
+8
View File
@@ -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,
+10 -17
View File
@@ -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
+4 -10
View File
@@ -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;
}
+6 -8
View File
@@ -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
+1 -6
View File
@@ -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);
+193
View File
@@ -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);
}
+90
View File
@@ -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
+1 -1
View File
@@ -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 */
+19 -20
View File
@@ -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,
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
+62
View File
@@ -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
+235
View File
@@ -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);
}
+55
View File
@@ -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
+547
View File
@@ -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);
}
+55
View File
@@ -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
+576
View File
@@ -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);
}
+172
View File
@@ -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
+15 -27
View File
@@ -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;
}
+82 -30
View File
@@ -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
+21 -18
View File
@@ -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;
File diff suppressed because it is too large. Load diff
+196 -25
View File
@@ -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, &current_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, &current_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, &current_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, &current_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, &current_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;
+130 -233
View File
@@ -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;
File diff suppressed because it is too large. Load diff
+106
View File
@@ -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);
+2
View File
@@ -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
+7 -17
View File
@@ -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:
+3 -3
View File
@@ -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))
+8
View File
@@ -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>
+28 -57
View File
@@ -24,61 +24,7 @@
#include "libglnx.h"
#include <sys/xattr.h>
#include <gio/gunixinputstream.h>
/* Before https://github.com/GNOME/libglnx/commit/9929adc, the libglnx
* tmpfile API made it hard to clean up tmpfiles in failure cases.
* it's API breaking. Carry the fix here until we're ready to fully port.
*/
void
ot_tmpfile_clear (OtTmpfile *tmpf)
{
if (!tmpf->initialized)
return;
if (tmpf->fd == -1)
return;
(void) close (tmpf->fd);
/* If ->path is set, we're likely aborting due to an error. Clean it up */
if (tmpf->path)
{
(void) unlinkat (tmpf->src_dfd, tmpf->path, 0);
g_free (tmpf->path);
}
}
gboolean
ot_open_tmpfile_linkable_at (int dfd,
const char *subpath,
int flags,
OtTmpfile *out_tmpf,
GError **error)
{
if (!glnx_open_tmpfile_linkable_at (dfd, subpath, flags, &out_tmpf->fd, &out_tmpf->path, error))
return FALSE;
out_tmpf->initialized = TRUE;
out_tmpf->src_dfd = dfd;
return TRUE;
}
gboolean
ot_link_tmpfile_at (OtTmpfile *tmpf,
GLnxLinkTmpfileReplaceMode mode,
int target_dfd,
const char *target,
GError **error)
{
g_return_val_if_fail (tmpf->initialized, FALSE);
glnx_fd_close int fd = glnx_steal_fd (&tmpf->fd);
if (!glnx_link_tmpfile_at (tmpf->src_dfd, mode, fd, tmpf->path,
target_dfd, target, error))
{
if (tmpf->path)
(void) unlinkat (tmpf->src_dfd, tmpf->path, 0);
tmpf->initialized = FALSE;
return FALSE;
}
tmpf->initialized = FALSE;
return TRUE;
}
#include <gio/gunixoutputstream.h>
/* Convert a fd-relative path to a GFile* - use
* for legacy code.
@@ -213,9 +159,8 @@ ot_dfd_iter_init_allow_noent (int dfd,
*out_exists = FALSE;
return TRUE;
}
if (!glnx_dirfd_iterator_init_take_fd (fd, dfd_iter, error))
if (!glnx_dirfd_iterator_init_take_fd (&fd, dfd_iter, error))
return FALSE;
fd = -1;
*out_exists = TRUE;
return TRUE;
}
@@ -237,3 +182,29 @@ ot_file_mapat_bytes (int dfd,
return g_mapped_file_get_bytes (mfile);
}
/* Given an input stream, splice it to an anonymous file (O_TMPFILE).
* Useful for potentially large but transient files.
*/
GBytes *
ot_map_anonymous_tmpfile_from_content (GInputStream *instream,
GCancellable *cancellable,
GError **error)
{
g_auto(GLnxTmpfile) tmpf = { 0, };
if (!glnx_open_anonymous_tmpfile (O_RDWR | O_CLOEXEC, &tmpf, error))
return NULL;
g_autoptr(GOutputStream) out = g_unix_output_stream_new (tmpf.fd, FALSE);
gssize n_bytes_written = g_output_stream_splice (out, instream,
G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE |
G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET,
cancellable, error);
if (n_bytes_written < 0)
return NULL;
g_autoptr(GMappedFile) mfile = g_mapped_file_new_from_fd (tmpf.fd, FALSE, error);
if (!mfile)
return NULL;
return g_mapped_file_get_bytes (mfile);
}
+27 -20
View File
@@ -25,29 +25,31 @@
G_BEGIN_DECLS
/* This is a copy of https://github.com/GNOME/libglnx/pull/46 until we
* can do a full port; see https://github.com/ostreedev/ostree/pull/861 */
/* A little helper to call unlinkat() as a cleanup
* function. Mostly only necessary to handle
* deletion of temporary symlinks.
*/
typedef struct {
gboolean initialized;
int src_dfd;
int fd;
int dfd;
char *path;
} OtTmpfile;
void ot_tmpfile_clear (OtTmpfile *tmpf);
G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(OtTmpfile, ot_tmpfile_clear);
} OtCleanupUnlinkat;
gboolean
ot_open_tmpfile_linkable_at (int dfd,
const char *subpath,
int flags,
OtTmpfile *out_tmpf,
GError **error);
gboolean
ot_link_tmpfile_at (OtTmpfile *tmpf,
GLnxLinkTmpfileReplaceMode flags,
int target_dfd,
const char *target,
GError **error);
static inline void
ot_cleanup_unlinkat_clear (OtCleanupUnlinkat *cleanup)
{
g_clear_pointer (&cleanup->path, g_free);
}
static inline void
ot_cleanup_unlinkat (OtCleanupUnlinkat *cleanup)
{
if (cleanup->path)
{
(void) unlinkat (cleanup->dfd, cleanup->path, 0);
ot_cleanup_unlinkat_clear (cleanup);
}
}
G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(OtCleanupUnlinkat, ot_cleanup_unlinkat);
GFile * ot_fdrel_to_gfile (int dfd, const char *path);
@@ -84,6 +86,11 @@ gboolean ot_dfd_iter_init_allow_noent (int dfd,
gboolean *out_exists,
GError **error);
GBytes *
ot_map_anonymous_tmpfile_from_content (GInputStream *instream,
GCancellable *cancellable,
GError **error);
GBytes *ot_file_mapat_bytes (int dfd,
const char *path,
GError **error);
-16
View File
@@ -35,22 +35,6 @@
#define O_BINARY 0
#endif
GFileType
ot_gfile_type_for_mode (guint32 mode)
{
if (S_ISDIR (mode))
return G_FILE_TYPE_DIRECTORY;
else if (S_ISREG (mode))
return G_FILE_TYPE_REGULAR;
else if (S_ISLNK (mode))
return G_FILE_TYPE_SYMBOLIC_LINK;
else if (S_ISBLK (mode) || S_ISCHR(mode) || S_ISFIFO(mode))
return G_FILE_TYPE_SPECIAL;
else
return G_FILE_TYPE_UNKNOWN;
}
GFile *
ot_gfile_resolve_path_printf (GFile *path,
const char *format,
-2
View File
@@ -33,8 +33,6 @@ G_BEGIN_DECLS
#define OSTREE_GIO_FAST_QUERYINFO ("standard::name,standard::type,standard::size,standard::is-symlink,standard::symlink-target," \
"unix::device,unix::inode,unix::mode,unix::uid,unix::gid,unix::rdev")
GFileType ot_gfile_type_for_mode (guint32 mode);
GFile * ot_gfile_resolve_path_printf (GFile *path,
const char *format,
...) G_GNUC_PRINTF(2, 3);
+3
View File
@@ -41,6 +41,9 @@ static OstreeCommand commands[] = {
{ "config", ostree_builtin_config },
{ "diff", ostree_builtin_diff },
{ "export", ostree_builtin_export },
#ifdef OSTREE_ENABLE_EXPERIMENTAL_API
{ "find-remotes", ostree_builtin_find_remotes },
#endif
{ "fsck", ostree_builtin_fsck },
{ "gpg-sign", ostree_builtin_gpg_sign },
{ "init", ostree_builtin_init },
+62 -85
View File
@@ -63,12 +63,10 @@ parse_fsync_cb (const char *option_name,
GError **error)
{
gboolean val;
if (!ot_parse_boolean (value, &val, error))
return FALSE;
opt_disable_fsync = !val;
opt_disable_fsync = !val;
return TRUE;
}
@@ -109,16 +107,12 @@ parse_file_by_line (const char *path,
GCancellable *cancellable,
GError **error)
{
gboolean ret = FALSE;
g_autofree char *contents = NULL;
g_autoptr(GFile) file = NULL;
char **lines = NULL;
g_autofree char *contents =
glnx_file_get_contents_utf8_at (AT_FDCWD, path, NULL, cancellable, error);
if (!contents)
return FALSE;
file = g_file_new_for_path (path);
if (!g_file_load_contents (file, cancellable, &contents, NULL, NULL, error))
goto out;
lines = g_strsplit (contents, "\n", -1);
g_auto(GStrv) lines = g_strsplit (contents, "\n", -1);
for (char **iter = lines; iter && *iter; iter++)
{
/* skip empty lines at least */
@@ -126,33 +120,41 @@ parse_file_by_line (const char *path,
continue;
if (!cb (*iter, cbdata, error))
goto out;
return FALSE;
}
ret = TRUE;
out:
g_strfreev (lines);
return ret;
return TRUE;
}
struct CommitFilterData {
GHashTable *mode_adds;
GHashTable *mode_overrides;
GHashTable *skip_list;
};
static gboolean
handle_statoverride_line (const char *line,
void *data,
GError **error)
{
GHashTable *files = data;
const char *spc;
guint mode_add;
spc = strchr (line, ' ');
struct CommitFilterData *cf = data;
const char *spc = strchr (line, ' ');
if (spc == NULL)
{
return glnx_throw (error, "Malformed statoverride file (no space found)");
}
return glnx_throw (error, "Malformed statoverride file (no space found)");
const char *fn = spc + 1;
mode_add = (guint32)(gint32)g_ascii_strtod (line, NULL);
g_hash_table_insert (files, g_strdup (spc + 1),
GUINT_TO_POINTER((gint32)mode_add));
if (g_str_has_prefix (line, "="))
{
guint mode_override = (guint32)(gint32)g_ascii_strtod (line+1, NULL);
g_hash_table_insert (cf->mode_overrides, g_strdup (fn),
GUINT_TO_POINTER((gint32)mode_override));
}
else
{
guint mode_add = (guint32)(gint32)g_ascii_strtod (line, NULL);
g_hash_table_insert (cf->mode_adds, g_strdup (fn),
GUINT_TO_POINTER((gint32)mode_add));
}
return TRUE;
}
@@ -166,11 +168,6 @@ handle_skiplist_line (const char *line,
return TRUE;
}
struct CommitFilterData {
GHashTable *mode_adds;
GHashTable *skip_list;
};
static OstreeRepoCommitFilterResult
commit_filter (OstreeRepo *self,
const char *path,
@@ -179,6 +176,7 @@ commit_filter (OstreeRepo *self,
{
struct CommitFilterData *data = user_data;
GHashTable *mode_adds = data->mode_adds;
GHashTable *mode_overrides = data->mode_overrides;
GHashTable *skip_list = data->skip_list;
gpointer value;
@@ -195,6 +193,14 @@ commit_filter (OstreeRepo *self,
current_mode | mode_add);
g_hash_table_remove (mode_adds, path);
}
else if (mode_overrides && g_hash_table_lookup_extended (mode_overrides, path, NULL, &value))
{
guint current_fmt = g_file_info_get_attribute_uint32 (file_info, "unix::mode") & S_IFMT;
guint mode_override = GPOINTER_TO_UINT (value);
g_file_info_set_attribute_uint32 (file_info, "unix::mode",
current_fmt | mode_override);
g_hash_table_remove (mode_adds, path);
}
if (skip_list && g_hash_table_contains (skip_list, path))
{
@@ -213,14 +219,7 @@ commit_editor (OstreeRepo *repo,
GCancellable *cancellable,
GError **error)
{
g_autofree char *input = NULL;
g_autofree char *output = NULL;
gboolean ret = FALSE;
g_autoptr(GString) bodybuf = NULL;
char **lines = NULL;
int i;
input = g_strdup_printf ("\n"
g_autofree char *input = g_strdup_printf ("\n"
"# Please enter the commit message for your changes. The first line will\n"
"# become the subject, and the remainder the body. Lines starting\n"
"# with '#' will be ignored, and an empty message aborts the commit."
@@ -233,12 +232,13 @@ commit_editor (OstreeRepo *repo,
*subject = NULL;
*body = NULL;
output = ot_editor_prompt (repo, input, cancellable, error);
g_autofree char *output = ot_editor_prompt (repo, input, cancellable, error);
if (output == NULL)
goto out;
return FALSE;
lines = g_strsplit (output, "\n", -1);
for (i = 0; lines[i] != NULL; i++)
g_auto(GStrv) lines = g_strsplit (output, "\n", -1);
g_autoptr(GString) bodybuf = NULL;
for (guint i = 0; lines[i] != NULL; i++)
{
g_strchomp (lines[i]);
@@ -269,24 +269,15 @@ commit_editor (OstreeRepo *repo,
}
if (!*subject)
{
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"Aborting commit due to empty commit subject.");
goto out;
}
return glnx_throw (error, "Aborting commit due to empty commit subject.");
if (bodybuf)
{
*body = g_string_free (bodybuf, FALSE);
*body = g_string_free (g_steal_pointer (&bodybuf), FALSE);
g_strchomp (*body);
bodybuf = NULL;
}
ret = TRUE;
out:
g_strfreev (lines);
return ret;
return TRUE;
}
static gboolean
@@ -294,38 +285,22 @@ parse_keyvalue_strings (char **strings,
GVariant **out_metadata,
GError **error)
{
gboolean ret = FALSE;
char **iter;
g_autoptr(GVariantBuilder) builder = NULL;
g_autoptr(GVariantBuilder) builder =
g_variant_builder_new (G_VARIANT_TYPE ("a{sv}"));
builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}"));
for (iter = strings; *iter; iter++)
for (char ** iter = strings; *iter; iter++)
{
const char *s;
const char *eq;
g_autofree char *key = NULL;
s = *iter;
eq = strchr (s, '=');
const char *s = *iter;
const char *eq = strchr (s, '=');
if (!eq)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"Missing '=' in KEY=VALUE metadata '%s'", s);
goto out;
}
key = g_strndup (s, eq - s);
return glnx_throw (error, "Missing '=' in KEY=VALUE metadata '%s'", s);
g_autofree char *key = g_strndup (s, eq - s);
g_variant_builder_add (builder, "{sv}", key,
g_variant_new_string (eq + 1));
}
ret = TRUE;
*out_metadata = g_variant_builder_end (builder);
g_variant_ref_sink (*out_metadata);
out:
return ret;
*out_metadata = g_variant_ref_sink (g_variant_builder_end (builder));
return TRUE;
}
gboolean
@@ -344,6 +319,7 @@ ostree_builtin_commit (int argc, char **argv, GCancellable *cancellable, GError
glnx_unref_object OstreeMutableTree *mtree = NULL;
g_autofree char *tree_type = NULL;
g_autoptr(GHashTable) mode_adds = NULL;
g_autoptr(GHashTable) mode_overrides = NULL;
g_autoptr(GHashTable) skip_list = NULL;
OstreeRepoCommitModifierFlags flags = 0;
OstreeRepoCommitModifier *modifier = NULL;
@@ -361,9 +337,10 @@ ostree_builtin_commit (int argc, char **argv, GCancellable *cancellable, GError
if (opt_statoverride_file)
{
mode_adds = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
filter_data.mode_adds = mode_adds = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
filter_data.mode_overrides = mode_overrides = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
if (!parse_file_by_line (opt_statoverride_file, handle_statoverride_line,
mode_adds, cancellable, error))
&filter_data, cancellable, error))
goto out;
}
@@ -387,7 +364,7 @@ ostree_builtin_commit (int argc, char **argv, GCancellable *cancellable, GError
&detached_metadata, error))
goto out;
}
if (!(opt_branch || opt_orphan))
{
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+283
View File
@@ -0,0 +1,283 @@
/* -*- 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 "ot-main.h"
#include "ot-builtins.h"
#include "ostree.h"
#include "otutil.h"
#include "ostree-remote-private.h"
static gchar *opt_cache_dir = NULL;
static gboolean opt_disable_fsync = FALSE;
static gboolean opt_pull = FALSE;
static GOptionEntry options[] =
{
{ "cache-dir", 0, 0, G_OPTION_ARG_FILENAME, &opt_cache_dir, "Use custom cache dir", NULL },
{ "disable-fsync", 0, 0, G_OPTION_ARG_NONE, &opt_disable_fsync, "Do not invoke fsync()", NULL },
{ "pull", 0, 0, G_OPTION_ARG_NONE, &opt_pull, "Pull the updates after finding them", NULL },
{ NULL }
};
static gchar *
uint64_secs_to_iso8601 (guint64 secs)
{
g_autoptr(GDateTime) dt = g_date_time_new_from_unix_utc (secs);
if (dt != NULL)
return g_date_time_format (dt, "%FT%TZ");
else
return g_strdup ("invalid");
}
static gchar *
format_ref_to_checksum (GHashTable *ref_to_checksum /* (element-type OstreeCollectionRef utf8) */,
const gchar *line_prefix)
{
GHashTableIter iter;
const OstreeCollectionRef *ref;
const gchar *checksum;
g_autoptr(GString) out = NULL;
g_hash_table_iter_init (&iter, ref_to_checksum);
out = g_string_new ("");
while (g_hash_table_iter_next (&iter, (gpointer *) &ref, (gpointer *) &checksum))
g_string_append_printf (out, "%s - (%s, %s) = %s\n",
line_prefix, ref->collection_id, ref->ref_name,
(checksum != NULL) ? checksum : "(not found)");
return g_string_free (g_steal_pointer (&out), FALSE);
}
static gchar *
remote_get_uri (OstreeRemote *remote)
{
g_autoptr(GError) error = NULL;
g_autofree gchar *uri = NULL;
uri = g_key_file_get_string (remote->options, remote->group, "url", &error);
g_assert_no_error (error);
return g_steal_pointer (&uri);
}
/* Add each key from @keys_input to @set iff its value is non-%NULL. */
static void
add_keys_to_set_if_non_null (GHashTable *set,
GHashTable *keys_input)
{
GHashTableIter iter;
gpointer key, value;
g_hash_table_iter_init (&iter, keys_input);
while (g_hash_table_iter_next (&iter, &key, &value))
if (value != NULL)
g_hash_table_add (set, key);
}
static void
get_result_cb (GObject *obj,
GAsyncResult *result,
gpointer user_data)
{
GAsyncResult **result_out = user_data;
*result_out = g_object_ref (result);
}
static void
collection_ref_free0 (OstreeCollectionRef *ref)
{
if (ref == NULL)
return;
ostree_collection_ref_free (ref);
}
/* TODO: Add a man page. */
gboolean
ostree_builtin_find_remotes (int argc,
char **argv,
GCancellable *cancellable,
GError **error)
{
g_autoptr(GOptionContext) context = NULL;
glnx_unref_object OstreeRepo *repo = NULL;
g_autoptr(GPtrArray) refs = NULL; /* (element-type OstreeCollectionRef) */
glnx_unref_object OstreeAsyncProgress *progress = NULL;
gsize i;
g_autoptr(GAsyncResult) find_result = NULL, pull_result = NULL;
g_auto(OstreeRepoFinderResultv) results = NULL;
g_auto(GLnxConsoleRef) console = { 0, };
g_autoptr(GHashTable) refs_found = NULL; /* set (element-type OstreeCollectionRef) */
context = g_option_context_new ("COLLECTION-ID REF [COLLECTION-ID REF...] - Find remotes to serve the given refs");
/* Parse options. */
if (!ostree_option_context_parse (context, options, &argc, &argv, OSTREE_BUILTIN_FLAG_NONE, &repo, cancellable, error))
return FALSE;
if (!ostree_ensure_repo_writable (repo, error))
return FALSE;
if (argc < 3)
{
ot_util_usage_error (context, "At least one COLLECTION-ID REF pair must be specified", error);
return FALSE;
}
if (argc % 2 == 0)
{
ot_util_usage_error (context, "Only complete COLLECTION-ID REF pairs may be specified", error);
return FALSE;
}
if (opt_disable_fsync)
ostree_repo_set_disable_fsync (repo, TRUE);
if (opt_cache_dir &&
!ostree_repo_set_cache_dir (repo, AT_FDCWD, opt_cache_dir, cancellable, error))
return FALSE;
/* Read in the refs to search for remotes for. */
refs = g_ptr_array_new_full (argc, (GDestroyNotify) collection_ref_free0);
for (i = 1; i < argc; i += 2)
{
if (!ostree_validate_collection_id (argv[i], error) ||
!ostree_validate_rev (argv[i + 1], error))
return FALSE;
g_ptr_array_add (refs, ostree_collection_ref_new (argv[i], argv[i + 1]));
}
g_ptr_array_add (refs, NULL);
/* Run the operation. */
glnx_console_lock (&console);
if (console.is_tty)
progress = ostree_async_progress_new_and_connect (ostree_repo_pull_default_console_progress_changed, &console);
/* FIXME: Eventually some command line options for customising the finders
* list would be good. */
ostree_repo_find_remotes_async (repo,
(const OstreeCollectionRef * const *) refs->pdata,
NULL /* no options */,
NULL /* default finders */,
progress, cancellable,
get_result_cb, &find_result);
while (find_result == NULL)
g_main_context_iteration (NULL, TRUE);
results = ostree_repo_find_remotes_finish (repo, find_result, error);
if (results == NULL)
return FALSE;
if (progress)
ostree_async_progress_finish (progress);
/* Print results and work out which refs were not found. */
refs_found = g_hash_table_new_full (ostree_collection_ref_hash,
ostree_collection_ref_equal, NULL, NULL);
for (i = 0; results[i] != NULL; i++)
{
g_autofree gchar *uri = NULL;
g_autofree gchar *refs_string = NULL;
g_autofree gchar *last_modified_string = NULL;
uri = remote_get_uri (results[i]->remote);
refs_string = format_ref_to_checksum (results[i]->ref_to_checksum, " ");
add_keys_to_set_if_non_null (refs_found, results[i]->ref_to_checksum);
if (results[i]->summary_last_modified > 0)
last_modified_string = uint64_secs_to_iso8601 (results[i]->summary_last_modified);
else
last_modified_string = g_strdup ("unknown");
g_print ("Result %" G_GSIZE_FORMAT ": %s\n"
" - Finder: %s\n"
" - Keyring: %s\n"
" - Priority: %d\n"
" - Summary last modified: %s\n"
" - Refs:\n"
"%s\n",
i, uri, G_OBJECT_TYPE_NAME (results[i]->finder), results[i]->remote->keyring,
results[i]->priority, last_modified_string, refs_string);
}
if (results[0] == NULL)
{
g_print ("No results.\n");
return TRUE;
}
g_print ("%u/%u refs were found.\n", g_hash_table_size (refs_found), refs->len - 1);
/* Print out the refs which weren’t found. */
if (g_hash_table_size (refs_found) != refs->len - 1 /* NULL terminator */)
{
g_print ("Refs not found in any remote:\n");
for (i = 0; i < refs->len && refs->pdata[i] != NULL; i++)
{
const OstreeCollectionRef *ref = g_ptr_array_index (refs, i);
if (!g_hash_table_contains (refs_found, ref))
g_print (" - (%s, %s)\n", ref->collection_id, ref->ref_name);
}
}
/* Does the user want us to pull the updates? */
if (!opt_pull)
return TRUE;
/* Run the pull operation. */
if (console.is_tty)
progress = ostree_async_progress_new_and_connect (ostree_repo_pull_default_console_progress_changed, &console);
ostree_repo_pull_from_remotes_async (repo,
(const OstreeRepoFinderResult * const *) results,
NULL, /* no options */
progress, cancellable,
get_result_cb, &pull_result);
while (pull_result == NULL)
g_main_context_iteration (NULL, TRUE);
if (!ostree_repo_pull_from_remotes_finish (repo, pull_result, error))
return FALSE;
if (progress)
ostree_async_progress_finish (progress);
/* The pull operation fails if any of the refs can’t be pulled. */
g_print ("Pulled %u/%u refs successfully.\n", refs->len - 1, refs->len - 1);
return TRUE;
}
+66 -82
View File
@@ -47,13 +47,12 @@ load_and_fsck_one_object (OstreeRepo *repo,
GCancellable *cancellable,
GError **error)
{
gboolean ret = FALSE;
gboolean missing = FALSE;
g_autoptr(GVariant) metadata = NULL;
g_autoptr(GInputStream) input = NULL;
g_autoptr(GFileInfo) file_info = NULL;
g_autoptr(GVariant) xattrs = NULL;
GError *temp_error = NULL;
g_autoptr(GError) temp_error = NULL;
if (OSTREE_OBJECT_TYPE_IS_META (objtype))
{
@@ -69,8 +68,8 @@ load_and_fsck_one_object (OstreeRepo *repo,
}
else
{
g_prefix_error (error, "Loading metadata object %s: ", checksum);
goto out;
g_propagate_error (error, g_steal_pointer (&temp_error));
return glnx_prefix_error (error, "Loading metadata object %s", checksum);
}
}
else
@@ -78,28 +77,19 @@ load_and_fsck_one_object (OstreeRepo *repo,
if (objtype == OSTREE_OBJECT_TYPE_COMMIT)
{
if (!ostree_validate_structureof_commit (metadata, error))
{
g_prefix_error (error, "While validating commit metadata '%s': ", checksum);
goto out;
}
return glnx_prefix_error (error, "While validating commit metadata '%s'", checksum);
}
else if (objtype == OSTREE_OBJECT_TYPE_DIR_TREE)
{
if (!ostree_validate_structureof_dirtree (metadata, error))
{
g_prefix_error (error, "While validating directory tree '%s': ", checksum);
goto out;
}
return glnx_prefix_error (error, "While validating directory tree '%s'", checksum);
}
else if (objtype == OSTREE_OBJECT_TYPE_DIR_META)
{
if (!ostree_validate_structureof_dirmeta (metadata, error))
{
g_prefix_error (error, "While validating directory metadata '%s': ", checksum);
goto out;
}
return glnx_prefix_error (error, "While validating directory metadata '%s'", checksum);
}
input = g_memory_input_stream_new_from_data (g_variant_get_data (metadata),
g_variant_get_size (metadata),
NULL);
@@ -122,19 +112,15 @@ load_and_fsck_one_object (OstreeRepo *repo,
}
else
{
*error = temp_error;
g_prefix_error (error, "Loading file object %s: ", checksum);
goto out;
g_propagate_error (error, g_steal_pointer (&temp_error));
return glnx_prefix_error (error, "Loading file object %s", checksum);
}
}
else
{
mode = g_file_info_get_attribute_uint32 (file_info, "unix::mode");
if (!ostree_validate_structureof_file_mode (mode, error))
{
g_prefix_error (error, "While validating file '%s': ", checksum);
goto out;
}
return glnx_prefix_error (error, "While validating file '%s'", checksum);
}
}
@@ -150,8 +136,8 @@ load_and_fsck_one_object (OstreeRepo *repo,
if (!ostree_checksum_file_from_input (file_info, xattrs, input,
objtype, &computed_csum,
cancellable, error))
goto out;
return FALSE;
tmp_checksum = ostree_checksum_from_bytes (computed_csum);
if (strcmp (checksum, tmp_checksum) != 0)
{
@@ -165,16 +151,11 @@ load_and_fsck_one_object (OstreeRepo *repo,
*out_found_corruption = TRUE;
}
else
{
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, msg);
goto out;
}
return glnx_throw (error, "%s", msg);
}
}
ret = TRUE;
out:
return ret;
return TRUE;
}
static gboolean
@@ -184,16 +165,10 @@ fsck_reachable_objects_from_commits (OstreeRepo *repo,
GCancellable *cancellable,
GError **error)
{
gboolean ret = FALSE;
g_autoptr(GHashTable) reachable_objects = ostree_repo_traverse_new_reachable ();
GHashTableIter hash_iter;
gpointer key, value;
g_autoptr(GHashTable) reachable_objects = NULL;
guint i;
guint mod;
guint count;
reachable_objects = ostree_repo_traverse_new_reachable ();
g_hash_table_iter_init (&hash_iter, commits);
while (g_hash_table_iter_next (&hash_iter, &key, &value))
{
@@ -207,12 +182,12 @@ fsck_reachable_objects_from_commits (OstreeRepo *repo,
if (!ostree_repo_traverse_commit_union (repo, checksum, 0, reachable_objects,
cancellable, error))
goto out;
return FALSE;
}
count = g_hash_table_size (reachable_objects);
mod = count / 10;
i = 0;
const guint count = g_hash_table_size (reachable_objects);
const guint mod = count / 10;
guint i = 0;
g_hash_table_iter_init (&hash_iter, reachable_objects);
while (g_hash_table_iter_next (&hash_iter, &key, &value))
{
@@ -224,44 +199,37 @@ fsck_reachable_objects_from_commits (OstreeRepo *repo,
if (!load_and_fsck_one_object (repo, checksum, objtype, out_found_corruption,
cancellable, error))
goto out;
return FALSE;
if (mod == 0 || (i % mod == 0))
g_print ("%u/%u objects\n", i + 1, count);
i++;
}
ret = TRUE;
out:
return ret;
return TRUE;
}
gboolean
ostree_builtin_fsck (int argc, char **argv, GCancellable *cancellable, GError **error)
{
gboolean ret = FALSE;
g_autoptr(GOptionContext) context = NULL;
glnx_unref_object OstreeRepo *repo = NULL;
GHashTableIter hash_iter;
gpointer key, value;
gboolean found_corruption = FALSE;
guint n_partial = 0;
g_autoptr(GHashTable) all_refs = NULL;
g_autoptr(GHashTable) objects = NULL;
g_autoptr(GHashTable) commits = NULL;
g_autoptr(GPtrArray) tombstones = NULL;
context = g_option_context_new ("- Check the repository for consistency");
g_autoptr(GOptionContext) context = g_option_context_new ("- Check the repository for consistency");
if (!ostree_option_context_parse (context, options, &argc, &argv, OSTREE_BUILTIN_FLAG_NONE, &repo, cancellable, error))
goto out;
return FALSE;
if (!opt_quiet)
g_print ("Validating refs...\n");
/* Validate that the commit for each ref is available */
g_autoptr(GHashTable) all_refs = NULL;
if (!ostree_repo_list_refs (repo, NULL, &all_refs,
cancellable, error))
return FALSE;
GHashTableIter hash_iter;
gpointer key, value;
g_hash_table_iter_init (&hash_iter, all_refs);
while (g_hash_table_iter_next (&hash_iter, &key, &value))
{
@@ -270,27 +238,49 @@ ostree_builtin_fsck (int argc, char **argv, GCancellable *cancellable, GError **
g_autoptr(GVariant) commit = NULL;
if (!ostree_repo_load_variant (repo, OSTREE_OBJECT_TYPE_COMMIT,
checksum, &commit, error))
{
g_prefix_error (error, "Loading commit for ref %s: ", refname);
goto out;
}
return glnx_prefix_error (error, "Loading commit for ref %s", refname);
}
#ifdef OSTREE_ENABLE_EXPERIMENTAL_API
if (!opt_quiet)
g_print ("Validating refs in collections...\n");
g_autoptr(GHashTable) all_collection_refs = NULL; /* (element-type OstreeCollectionRef utf8) */
if (!ostree_repo_list_collection_refs (repo, NULL, &all_collection_refs,
cancellable, error))
return FALSE;
g_hash_table_iter_init (&hash_iter, all_collection_refs);
while (g_hash_table_iter_next (&hash_iter, &key, &value))
{
const OstreeCollectionRef *ref = key;
const char *checksum = value;
g_autoptr(GVariant) commit = NULL;
if (!ostree_repo_load_variant (repo, OSTREE_OBJECT_TYPE_COMMIT,
checksum, &commit, error))
return glnx_prefix_error (error, "Loading commit for ref (%s, %s)",
ref->collection_id, ref->ref_name);
}
#endif /* OSTREE_ENABLE_EXPERIMENTAL_API */
if (!opt_quiet)
g_print ("Enumerating objects...\n");
g_autoptr(GHashTable) objects = NULL;
if (!ostree_repo_list_objects (repo, OSTREE_REPO_LIST_OBJECTS_ALL,
&objects, cancellable, error))
goto out;
return FALSE;
commits = g_hash_table_new_full (ostree_hash_object_name, g_variant_equal,
(GDestroyNotify)g_variant_unref, NULL);
g_hash_table_iter_init (&hash_iter, objects);
g_autoptr(GHashTable) commits = g_hash_table_new_full (ostree_hash_object_name, g_variant_equal,
(GDestroyNotify)g_variant_unref, NULL);
g_autoptr(GPtrArray) tombstones = NULL;
if (opt_add_tombstones)
tombstones = g_ptr_array_new_with_free_func (g_free);
guint n_partial = 0;
g_hash_table_iter_init (&hash_iter, objects);
while (g_hash_table_iter_next (&hash_iter, &key, &value))
{
GVariant *serialized_key = key;
@@ -304,7 +294,7 @@ ostree_builtin_fsck (int argc, char **argv, GCancellable *cancellable, GError **
if (objtype == OSTREE_OBJECT_TYPE_COMMIT)
{
if (!ostree_repo_load_commit (repo, checksum, &commit, &commitstate, error))
goto out;
return FALSE;
if (opt_add_tombstones)
{
@@ -324,7 +314,7 @@ ostree_builtin_fsck (int argc, char **argv, GCancellable *cancellable, GError **
else
{
g_propagate_error (error, local_error);
goto out;
return FALSE;
}
}
}
@@ -345,7 +335,7 @@ ostree_builtin_fsck (int argc, char **argv, GCancellable *cancellable, GError **
if (!fsck_reachable_objects_from_commits (repo, commits, &found_corruption,
cancellable, error))
goto out;
return FALSE;
if (opt_add_tombstones)
{
@@ -353,14 +343,14 @@ ostree_builtin_fsck (int argc, char **argv, GCancellable *cancellable, GError **
if (tombstones->len)
{
if (!ot_enable_tombstone_commits (repo, error))
goto out;
return FALSE;
}
for (i = 0; i < tombstones->len; i++)
{
const char *checksum = tombstones->pdata[i];
g_print ("Adding tombstone for commit %s\n", checksum);
if (!ostree_repo_delete_object (repo, OSTREE_OBJECT_TYPE_COMMIT, checksum, cancellable, error))
goto out;
return FALSE;
}
}
else if (n_partial > 0)
@@ -369,13 +359,7 @@ ostree_builtin_fsck (int argc, char **argv, GCancellable *cancellable, GError **
}
if (found_corruption)
{
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"Repository corruption encountered");
goto out;
}
return glnx_throw (error, "Repository corruption encountered");
ret = TRUE;
out:
return ret;
return TRUE;
}
+11
View File
@@ -27,9 +27,16 @@
#include "ostree.h"
static char *opt_mode = "bare";
#ifdef OSTREE_ENABLE_EXPERIMENTAL_API
static char *opt_collection_id = NULL;
#endif /* OSTREE_ENABLE_EXPERIMENTAL_API */
static GOptionEntry options[] = {
{ "mode", 0, 0, G_OPTION_ARG_STRING, &opt_mode, "Initialize repository in given mode (bare, archive-z2)", NULL },
#ifdef OSTREE_ENABLE_EXPERIMENTAL_API
{ "collection-id", 0, 0, G_OPTION_ARG_STRING, &opt_collection_id,
"Globally unique ID for this repository as an collection of refs for redistribution to other repositories", "COLLECTION-ID" },
#endif /* OSTREE_ENABLE_EXPERIMENTAL_API */
{ NULL }
};
@@ -48,6 +55,10 @@ ostree_builtin_init (int argc, char **argv, GCancellable *cancellable, GError **
if (!ostree_repo_mode_from_string (opt_mode, &mode, error))
goto out;
#ifdef OSTREE_ENABLE_EXPERIMENTAL_API
if (!ostree_repo_set_collection_id (repo, opt_collection_id, error))
goto out;
#endif /* OSTREE_ENABLE_EXPERIMENTAL_API */
if (!ostree_repo_create (repo, mode, NULL, error))
goto out;
+26 -1
View File
@@ -50,11 +50,15 @@ static GOptionEntry options[] = {
static gboolean
delete_commit (OstreeRepo *repo, const char *commit_to_delete, GCancellable *cancellable, GError **error)
{
g_autoptr(GHashTable) refs = NULL;
g_autoptr(GHashTable) refs = NULL; /* (element-type utf8 utf8) */
#ifdef OSTREE_ENABLE_EXPERIMENTAL_API
g_autoptr(GHashTable) collection_refs = NULL; /* (element-type OstreeCollectionRef utf8) */
#endif /* OSTREE_ENABLE_EXPERIMENTAL_API */
GHashTableIter hashiter;
gpointer hashkey, hashvalue;
gboolean ret = FALSE;
/* Check refs which are not in a collection. */
if (!ostree_repo_list_refs (repo, NULL, &refs, cancellable, error))
goto out;
@@ -71,6 +75,26 @@ delete_commit (OstreeRepo *repo, const char *commit_to_delete, GCancellable *can
}
}
#ifdef OSTREE_ENABLE_EXPERIMENTAL_API
/* And check refs which *are* in a collection. */
if (!ostree_repo_list_collection_refs (repo, NULL, &collection_refs, cancellable, error))
goto out;
g_hash_table_iter_init (&hashiter, collection_refs);
while (g_hash_table_iter_next (&hashiter, &hashkey, &hashvalue))
{
const OstreeCollectionRef *ref = hashkey;
const char *commit = hashvalue;
if (g_strcmp0 (commit_to_delete, commit) == 0)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"Commit '%s' is referenced by (%s, %s)",
commit_to_delete, ref->collection_id, ref->ref_name);
goto out;
}
}
#endif /* OSTREE_ENABLE_EXPERIMENTAL_API */
if (!ot_enable_tombstone_commits (repo, error))
goto out;
@@ -249,6 +273,7 @@ ostree_builtin_prune (int argc, char **argv, GCancellable *cancellable, GError *
}
/* We start from the refs */
/* FIXME: Do we also want to look at ostree_repo_list_collection_refs()? */
if (!ostree_repo_list_refs (repo, NULL, &all_refs,
cancellable, error))
return FALSE;
+2
View File
@@ -111,6 +111,8 @@ ostree_builtin_pull_local (int argc, char **argv, GCancellable *cancellable, GEr
if (!ostree_repo_open (src_repo, cancellable, error))
goto out;
/* FIXME: This should grow support for pulling refs from refs/mirrors on
* a local repository, using ostree_repo_list_collection_refs(). */
if (!ostree_repo_list_refs (src_repo, NULL, &refs_to_clone,
cancellable, error))
goto out;
+23 -2
View File
@@ -41,6 +41,7 @@ static char* opt_cache_dir;
static int opt_depth = 0;
static int opt_frequency = 0;
static char* opt_url;
static char** opt_localcache_repos;
static GOptionEntry options[] = {
{ "commit-metadata-only", 0, 0, G_OPTION_ARG_NONE, &opt_commit_only, "Fetch only the commit metadata", NULL },
@@ -57,6 +58,7 @@ static GOptionEntry options[] = {
{ "url", 0, 0, G_OPTION_ARG_STRING, &opt_url, "Pull objects from this URL instead of the one from the remote config", NULL },
{ "http-header", 0, 0, G_OPTION_ARG_STRING_ARRAY, &opt_http_headers, "Add NAME=VALUE as HTTP header to all requests", "NAME=VALUE" },
{ "update-frequency", 0, 0, G_OPTION_ARG_INT, &opt_frequency, "Sets the update frequency, in milliseconds (0=1000ms) (default: 0)", "FREQUENCY" },
{ "localcache-repo", 'L', 0, G_OPTION_ARG_FILENAME_ARRAY, &opt_localcache_repos, "Add REPO as local cache source for objects during this pull", "REPO" },
{ NULL }
};
@@ -125,6 +127,13 @@ dry_run_console_progress_changed (OstreeAsyncProgress *progress,
g_print ("%s\n", buf->str);
}
static void
noninteractive_console_progress_changed (OstreeAsyncProgress *progress,
gpointer user_data)
{
/* We do nothing here - we just want the final status */
}
gboolean
ostree_builtin_pull (int argc, char **argv, GCancellable *cancellable, GError **error)
{
@@ -274,6 +283,9 @@ ostree_builtin_pull (int argc, char **argv, GCancellable *cancellable, GError **
if (override_commit_ids)
g_variant_builder_add (&builder, "{s@v}", "override-commit-ids",
g_variant_new_variant (g_variant_new_strv ((const char*const*)override_commit_ids->pdata, override_commit_ids->len)));
if (opt_localcache_repos)
g_variant_builder_add (&builder, "{s@v}", "localcache-repos",
g_variant_new_variant (g_variant_new_strv ((const char*const*)opt_localcache_repos, -1)));
if (opt_http_headers)
{
@@ -302,6 +314,8 @@ ostree_builtin_pull (int argc, char **argv, GCancellable *cancellable, GError **
{
if (console.is_tty)
progress = ostree_async_progress_new_and_connect (ostree_repo_pull_default_console_progress_changed, &console);
else
progress = ostree_async_progress_new_and_connect (noninteractive_console_progress_changed, &console);
}
else
{
@@ -321,8 +335,15 @@ ostree_builtin_pull (int argc, char **argv, GCancellable *cancellable, GError **
progress, cancellable, error))
goto out;
if (progress)
ostree_async_progress_finish (progress);
if (!console.is_tty && !opt_dry_run)
{
g_assert (progress);
const char *status = ostree_async_progress_get_status (progress);
if (status)
g_print ("%s\n", status);
}
ostree_async_progress_finish (progress);
if (opt_dry_run)
g_assert (printed_console_progress);
+97
View File
@@ -29,14 +29,106 @@
static gboolean opt_delete;
static gboolean opt_list;
static char *opt_create;
#ifdef OSTREE_ENABLE_EXPERIMENTAL_API
static gboolean opt_collections;
#endif /* OSTREE_ENABLE_EXPERIMENTAL_API */
static GOptionEntry options[] = {
{ "delete", 0, 0, G_OPTION_ARG_NONE, &opt_delete, "Delete refs which match PREFIX, rather than listing them", NULL },
{ "list", 0, 0, G_OPTION_ARG_NONE, &opt_list, "Do not remove the prefix from the refs", NULL },
{ "create", 0, 0, G_OPTION_ARG_STRING, &opt_create, "Create a new ref for an existing commit", "NEWREF" },
#ifdef OSTREE_ENABLE_EXPERIMENTAL_API
{ "collections", 'c', 0, G_OPTION_ARG_NONE, &opt_collections, "Enable listing collection IDs for refs", NULL },
#endif /* OSTREE_ENABLE_EXPERIMENTAL_API */
{ NULL }
};
#ifdef OSTREE_ENABLE_EXPERIMENTAL_API
static gboolean
do_ref_with_collections (OstreeRepo *repo,
const char *refspec_prefix,
GCancellable *cancellable,
GError **error)
{
g_autoptr(GHashTable) refs = NULL; /* (element-type OstreeCollectionRef utf8) */
GHashTableIter hashiter;
gpointer hashkey, hashvalue;
gboolean ret = FALSE;
if (!ostree_repo_list_collection_refs (repo,
(!opt_create) ? refspec_prefix : NULL,
&refs, cancellable, error))
goto out;
if (!opt_delete && !opt_create)
{
g_hash_table_iter_init (&hashiter, refs);
while (g_hash_table_iter_next (&hashiter, &hashkey, &hashvalue))
{
const OstreeCollectionRef *ref = hashkey;
g_print ("(%s, %s)\n", ref->collection_id, ref->ref_name);
}
}
else if (opt_create)
{
g_autofree char *checksum = NULL;
g_autofree char *checksum_existing = NULL;
if (!ostree_repo_resolve_rev_ext (repo, opt_create, TRUE, OSTREE_REPO_RESOLVE_REV_EXT_NONE, &checksum_existing, error))
{
if (g_error_matches (*error, G_IO_ERROR, G_IO_ERROR_IS_DIRECTORY))
{
/* A folder exists with the specified ref name,
* which is handled by _ostree_repo_write_ref */
g_clear_error (error);
}
else goto out;
}
if (checksum_existing != NULL)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"--create specified but ref %s already exists", opt_create);
goto out;
}
if (!ostree_repo_resolve_rev (repo, refspec_prefix, FALSE, &checksum, error))
goto out;
/* This is technically an abuse of the refspec syntax: collection IDs
* should not be treated like remote names. */
g_auto(GStrv) parts = g_strsplit (opt_create, ":", 2);
const char *collection_id = parts[0];
const char *ref_name = parts[1];
if (!ostree_validate_collection_id (collection_id, error))
goto out;
if (!ostree_validate_rev (ref_name, error))
goto out;
const OstreeCollectionRef ref = { (gchar *) collection_id, (gchar *) ref_name };
if (!ostree_repo_set_collection_ref_immediate (repo, &ref, checksum,
cancellable, error))
goto out;
}
else
/* delete */
{
g_hash_table_iter_init (&hashiter, refs);
while (g_hash_table_iter_next (&hashiter, &hashkey, &hashvalue))
{
const OstreeCollectionRef *ref = hashkey;
if (!ostree_repo_set_collection_ref_immediate (repo, ref, NULL,
cancellable, error))
goto out;
}
}
ret = TRUE;
out:
return ret;
}
#endif /* OSTREE_ENABLE_EXPERIMENTAL_API */
static gboolean do_ref (OstreeRepo *repo, const char *refspec_prefix, GCancellable *cancellable, GError **error)
{
g_autoptr(GHashTable) refs = NULL;
@@ -44,6 +136,11 @@ static gboolean do_ref (OstreeRepo *repo, const char *refspec_prefix, GCancellab
gpointer hashkey, hashvalue;
gboolean ret = FALSE;
#ifdef OSTREE_ENABLE_EXPERIMENTAL_API
if (opt_collections)
return do_ref_with_collections (repo, refspec_prefix, cancellable, error);
#endif /* OSTREE_ENABLE_EXPERIMENTAL_API */
if (opt_delete || opt_list)
{
if (!ostree_repo_list_refs_ext (repo, refspec_prefix, &refs, OSTREE_REPO_LIST_REFS_EXT_NONE,
+1
View File
@@ -45,6 +45,7 @@ ostree_builtin_reset (int argc,
const char *target = NULL;
g_autofree char *checksum = NULL;
/* FIXME: Add support for collection–refs. */
context = g_option_context_new ("REF COMMIT - Reset a REF to a previous COMMIT");
if (!ostree_option_context_parse (context, options, &argc, &argv, OSTREE_BUILTIN_FLAG_NONE, &repo, cancellable, error))
+46 -1
View File
@@ -30,6 +30,7 @@
static gboolean opt_update, opt_view, opt_raw;
static char **opt_key_ids;
static char *opt_gpg_homedir;
static char **opt_metadata;
static GOptionEntry options[] = {
{ "update", 'u', 0, G_OPTION_ARG_NONE, &opt_update, "Update the summary", NULL },
@@ -37,9 +38,44 @@ static GOptionEntry options[] = {
{ "raw", 0, 0, G_OPTION_ARG_NONE, &opt_raw, "View the raw bytes of the summary file", NULL },
{ "gpg-sign", 0, 0, G_OPTION_ARG_STRING_ARRAY, &opt_key_ids, "GPG Key ID to sign the summary with", "KEY-ID"},
{ "gpg-homedir", 0, 0, G_OPTION_ARG_FILENAME, &opt_gpg_homedir, "GPG Homedir to use when looking for keyrings", "HOMEDIR"},
{ "add-metadata", 'm', 0, G_OPTION_ARG_STRING_ARRAY, &opt_metadata, "Additional metadata field to add to the summary", "KEY=VALUE" },
{ NULL }
};
/* Take arguments of the form KEY=VALUE and put them into an a{sv} variant. The
* value arguments must be parsable using g_variant_parse(). */
static GVariant *
build_additional_metadata (const char * const *args,
GError **error)
{
g_autoptr(GVariantBuilder) builder = NULL;
builder = g_variant_builder_new (G_VARIANT_TYPE_VARDICT);
for (gsize i = 0; args[i] != NULL; i++)
{
const gchar *equals = strchr (args[i], '=');
g_autofree gchar *key = NULL;
const gchar *value_str;
g_autoptr(GVariant) value = NULL;
if (equals == NULL)
return glnx_null_throw (error,
"Missing '=' in KEY=VALUE metadata '%s'", args[i]);
key = g_strndup (args[i], equals - args[i]);
value_str = equals + 1;
value = g_variant_parse (NULL, value_str, NULL, NULL, error);
if (value == NULL)
return glnx_prefix_error_null (error, "Error parsing variant ‘%s’: ", value_str);
g_variant_builder_add (builder, "{sv}", key, value);
}
return g_variant_ref_sink (g_variant_builder_end (builder));
}
gboolean
ostree_builtin_summary (int argc, char **argv, GCancellable *cancellable, GError **error)
{
@@ -55,10 +91,19 @@ ostree_builtin_summary (int argc, char **argv, GCancellable *cancellable, GError
if (opt_update)
{
g_autoptr(GVariant) additional_metadata = NULL;
if (!ostree_ensure_repo_writable (repo, error))
goto out;
if (!ostree_repo_regenerate_summary (repo, NULL, cancellable, error))
if (opt_metadata != NULL)
{
additional_metadata = build_additional_metadata ((const char * const *) opt_metadata, error);
if (additional_metadata == NULL)
goto out;
}
if (!ostree_repo_regenerate_summary (repo, additional_metadata, cancellable, error))
goto out;
if (opt_key_ids)
+5
View File
@@ -22,6 +22,8 @@
#pragma once
#include "config.h"
#include "ostree.h"
G_BEGIN_DECLS
@@ -36,6 +38,9 @@ BUILTINPROTO(checksum);
BUILTINPROTO(commit);
BUILTINPROTO(diff);
BUILTINPROTO(export);
#ifdef OSTREE_ENABLE_EXPERIMENTAL_API
BUILTINPROTO(find_remotes);
#endif
BUILTINPROTO(gpg_sign);
BUILTINPROTO(init);
BUILTINPROTO(log);
+64 -22
View File
@@ -176,7 +176,8 @@ ot_dump_object (OstreeObjectType objtype,
}
static void
dump_summary_ref (const char *ref_name,
dump_summary_ref (const char *collection_id,
const char *ref_name,
guint64 commit_size,
GVariant *csum_v,
GVariantIter *metadata)
@@ -187,7 +188,10 @@ dump_summary_ref (const char *ref_name,
GVariant *value;
char *key;
g_print ("* %s\n", ref_name);
if (collection_id == NULL)
g_print ("* %s\n", ref_name);
else
g_print ("* (%s, %s)\n", collection_id, ref_name);
size = g_format_size (commit_size);
g_print (" Latest Commit (%s):\n", size);
@@ -229,6 +233,39 @@ dump_summary_ref (const char *ref_name,
}
}
static void
dump_summary_refs (const gchar *collection_id,
GVariant *refs)
{
GVariantIter iter;
GVariant *value;
g_variant_iter_init (&iter, refs);
while ((value = g_variant_iter_next_value (&iter)) != NULL)
{
const char *ref_name = NULL;
g_variant_get_child (value, 0, "&s", &ref_name);
if (ref_name != NULL)
{
g_autoptr(GVariant) csum_v = NULL;
g_autoptr(GVariantIter) metadata = NULL;
guint64 commit_size;
g_variant_get_child (value, 1, "(t@aya{sv})",
&commit_size, &csum_v, &metadata);
dump_summary_ref (collection_id, ref_name, commit_size, csum_v, metadata);
g_print ("\n");
}
g_variant_unref (value);
}
}
void
ot_dump_summary_bytes (GBytes *summary_bytes,
OstreeDumpFlags flags)
@@ -254,31 +291,26 @@ ot_dump_summary_bytes (GBytes *summary_bytes,
refs = g_variant_get_child_value (summary, 0);
exts = g_variant_get_child_value (summary, 1);
g_variant_iter_init (&iter, refs);
/* Print the refs, including those with a collection ID specified. */
const gchar *main_collection_id;
g_autoptr(GVariant) collection_map = NULL;
const gchar *collection_id;
while ((value = g_variant_iter_next_value (&iter)) != NULL)
if (!g_variant_lookup (exts, OSTREE_SUMMARY_COLLECTION_ID, "&s", &main_collection_id))
main_collection_id = NULL;
dump_summary_refs (main_collection_id, refs);
collection_map = g_variant_lookup_value (exts, OSTREE_SUMMARY_COLLECTION_MAP, G_VARIANT_TYPE ("a{sa(s(taya{sv}))}"));
if (collection_map != NULL)
{
const char *ref_name = NULL;
g_variant_iter_init (&iter, collection_map);
g_variant_get_child (value, 0, "&s", &ref_name);
if (ref_name != NULL)
{
g_autoptr(GVariant) csum_v = NULL;
g_autoptr(GVariantIter) metadata = NULL;
guint64 commit_size;
g_variant_get_child (value, 1, "(t@aya{sv})",
&commit_size, &csum_v, &metadata);
dump_summary_ref (ref_name, commit_size, csum_v, metadata);
g_print ("\n");
}
g_variant_unref (value);
while (g_variant_iter_loop (&iter, "{&s@a(s(taya{sv}))}", &collection_id, &refs))
dump_summary_refs (collection_id, refs);
}
/* Print out the additional metadata. */
g_variant_iter_init (&iter, exts);
while (g_variant_iter_loop (&iter, "{sv}", &key, &value))
@@ -301,6 +333,16 @@ ot_dump_summary_bytes (GBytes *summary_bytes,
pretty_key = "Expires";
value_str = uint64_secs_to_iso8601 (GUINT64_FROM_BE (g_variant_get_uint64 (value)));
}
else if (g_strcmp0 (key, OSTREE_SUMMARY_COLLECTION_ID) == 0)
{
pretty_key = "Collection ID";
value_str = g_variant_dup_string (value, NULL);
}
else if (g_strcmp0 (key, OSTREE_SUMMARY_COLLECTION_MAP) == 0)
{
pretty_key = "Collection Map";
value_str = g_strdup ("(printed above)");
}
else
{
value_str = g_variant_print (value, FALSE);
+3
View File
@@ -242,6 +242,9 @@ ostree_option_context_parse (GOptionContext *context,
g_print (" Version: %s\n", PACKAGE_VERSION);
if (strlen (OSTREE_GITREV) > 0)
g_print (" Git: %s\n", OSTREE_GITREV);
#ifdef BUILDOPT_IS_DEVEL_BUILD
g_print (" DevelBuild: yes\n");
#endif
g_print (" Features:\n");
for (char **iter = features; iter && *iter; iter++)
g_print (" - %s\n", *iter);
+13
View File
@@ -31,6 +31,9 @@ static gboolean opt_no_gpg_verify;
static gboolean opt_if_not_exists;
static char *opt_gpg_import;
static char *opt_contenturl;
#ifdef OSTREE_ENABLE_EXPERIMENTAL_API
static char *opt_collection_id;
#endif /* OSTREE_ENABLE_EXPERIMENTAL_API */
static GOptionEntry option_entries[] = {
{ "set", 0, 0, G_OPTION_ARG_STRING_ARRAY, &opt_set, "Set config option KEY=VALUE for remote", "KEY=VALUE" },
@@ -38,6 +41,10 @@ static GOptionEntry option_entries[] = {
{ "if-not-exists", 0, 0, G_OPTION_ARG_NONE, &opt_if_not_exists, "Do nothing if the provided remote exists", NULL },
{ "gpg-import", 0, 0, G_OPTION_ARG_FILENAME, &opt_gpg_import, "Import GPG key from FILE", "FILE" },
{ "contenturl", 0, 0, G_OPTION_ARG_STRING, &opt_contenturl, "Use URL when fetching content", "URL" },
#ifdef OSTREE_ENABLE_EXPERIMENTAL_API
{ "collection-id", 0, 0, G_OPTION_ARG_STRING, &opt_collection_id,
"Globally unique ID for this repository as an collection of refs for redistribution to other repositories", "COLLECTION-ID" },
#endif /* OSTREE_ENABLE_EXPERIMENTAL_API */
{ NULL }
};
@@ -110,6 +117,12 @@ ot_remote_builtin_add (int argc, char **argv, GCancellable *cancellable, GError
"gpg-verify",
g_variant_new_variant (g_variant_new_boolean (FALSE)));
#ifdef OSTREE_ENABLE_EXPERIMENTAL_API
if (opt_collection_id != NULL)
g_variant_builder_add (optbuilder, "{s@v}", "collection-id",
g_variant_new_variant (g_variant_new_take_string (g_steal_pointer (&opt_collection_id))));
#endif /* OSTREE_ENABLE_EXPERIMENTAL_API */
options = g_variant_ref_sink (g_variant_builder_end (optbuilder));
if (!ostree_repo_remote_change (repo, NULL,
+6 -6
View File
@@ -202,7 +202,7 @@ ot_delete_cookie_at (int dfd, const char *jar_path,
{
gboolean found = FALSE;
#ifdef HAVE_LIBCURL
g_auto(OtTmpfile) tmpf = { 0, };
g_auto(GLnxTmpfile) tmpf = { 0, };
g_autofree char *dnbuf = NULL;
const char *dn = NULL;
g_autoptr(OtCookieParser) parser = NULL;
@@ -212,8 +212,8 @@ ot_delete_cookie_at (int dfd, const char *jar_path,
dnbuf = g_strdup (jar_path);
dn = dirname (dnbuf);
if (!ot_open_tmpfile_linkable_at (AT_FDCWD, dn, O_WRONLY | O_CLOEXEC,
&tmpf, error))
if (!glnx_open_tmpfile_linkable_at (AT_FDCWD, dn, O_WRONLY | O_CLOEXEC,
&tmpf, error))
return FALSE;
while (ot_parse_cookies_next (parser))
@@ -232,9 +232,9 @@ ot_delete_cookie_at (int dfd, const char *jar_path,
return glnx_throw_errno_prefix (error, "write");
}
if (!ot_link_tmpfile_at (&tmpf, GLNX_LINK_TMPFILE_REPLACE,
AT_FDCWD, jar_path,
error))
if (!glnx_link_tmpfile_at (&tmpf, GLNX_LINK_TMPFILE_REPLACE,
AT_FDCWD, jar_path,
error))
return FALSE;
#else
GSList *cookies;