New upstream version 2017.14

This commit is contained in:
Simon McVittie committed 2017-12-10 19:02:31 +00:00
commit 84beec3c14
77 files changed
+1754 -1343

No files matched your search

+3 -2
View File
@@ -18,8 +18,9 @@
***/
/* Add new symbols here. Release commits should copy this section into -released.sym. */
LIBOSTREE_2017.14 {
} LIBOSTREE_2017.13;
LIBOSTREE_2017.15 {
} LIBOSTREE_2017.14;
/* Stub section for the stable release *after* this development one; don't
* edit this other than to update the last number. This is just a copy/paste
+6
View File
@@ -89,3 +89,9 @@ global:
ostree_repo_finder_override_get_type;
ostree_repo_finder_override_new;
} LIBOSTREE_2017.12_EXPERIMENTAL;
LIBOSTREE_2017.14_EXPERIMENTAL {
global:
ostree_remote_get_type;
ostree_remote_get_url;
} LIBOSTREE_2017.13_EXPERIMENTAL;
+3
View File
@@ -442,6 +442,9 @@ global:
ostree_repo_checkout_at_options_set_devino;
} LIBOSTREE_2017.12;
LIBOSTREE_2017.14 {
} LIBOSTREE_2017.13;
/* NOTE: Only add more content here in release commits! See the
* comments at the top of this file.
*/
+2 -2
View File
@@ -278,7 +278,7 @@ ostree_validate_remote_name (const char *remote_name,
/**
* ostree_validate_collection_id:
* @rev: (nullable): A collection ID
* @collection_id: (nullable): A collection ID
* @error: Error
*
* Check whether the given @collection_id is valid. Return an error if it is
@@ -2204,7 +2204,7 @@ _ostree_compare_timestamps (const char *current_rev,
g_autofree char *current_ts_str = g_date_time_format (current_dt, "%c");
g_autofree char *new_ts_str = g_date_time_format (new_dt, "%c");
return glnx_throw (error, "Upgrade target revision '%s' with timestamp '%s' is chronologically older than current revision '%s' with timestamp '%s'",
return glnx_throw (error, "Upgrade target revision '%s' with timestamp '%s' is chronologically older than current revision '%s' with timestamp '%s'; use --allow-downgrade to permit",
new_rev, new_ts_str, current_rev, current_ts_str);
}
+1 -3
View File
@@ -18,9 +18,7 @@
* Boston, MA 02111-1307, USA.
*/
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include "config.h"
#include <string.h>
#include "ostree-enumtypes.h"
+8 -3
View File
@@ -167,6 +167,7 @@ _ostree_fetcher_finalize (GObject *object)
{
OstreeFetcher *self = OSTREE_FETCHER (object);
curl_multi_cleanup (self->multi);
g_free (self->remote_name);
g_free (self->cookie_jar_path);
g_free (self->proxy);
@@ -177,7 +178,6 @@ _ostree_fetcher_finalize (GObject *object)
g_clear_pointer (&self->timer_event, (GDestroyNotify)destroy_and_unref_source);
if (self->mainctx)
g_main_context_unref (self->mainctx);
curl_multi_cleanup (self->multi);
G_OBJECT_CLASS (_ostree_fetcher_parent_class)->finalize (object);
}
@@ -788,8 +788,13 @@ initiate_next_curl_request (FetcherRequest *req,
curl_easy_setopt (req->easy, CURLOPT_PROGRESSFUNCTION, prog_cb);
curl_easy_setopt (req->easy, CURLOPT_FOLLOWLOCATION, 1L);
curl_easy_setopt (req->easy, CURLOPT_CONNECTTIMEOUT, 30L);
curl_easy_setopt (req->easy, CURLOPT_LOW_SPEED_LIMIT, 1L);
curl_easy_setopt (req->easy, CURLOPT_LOW_SPEED_TIME, 30L);
/* We used to set CURLOPT_LOW_SPEED_LIMIT and CURLOPT_LOW_SPEED_TIME
* here, but see https://github.com/ostreedev/ostree/issues/878#issuecomment-347228854
* basically those options don't play well with HTTP2 at the moment
* where we can have lots of outstanding requests. Further,
* we could implement that functionality at a higher level
* more consistently too.
*/
/* closure bindings -> task */
curl_easy_setopt (req->easy, CURLOPT_PRIVATE, task);
-2
View File
@@ -48,7 +48,6 @@ typedef struct
gchar *ref_name; /* (not nullable) */
} OstreeCollectionRef;
#ifndef __GI_SCANNER__
_OSTREE_PUBLIC
GType ostree_collection_ref_get_type (void);
@@ -59,7 +58,6 @@ _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);
+18
View File
@@ -180,3 +180,21 @@ ostree_remote_get_name (OstreeRemote *remote)
return remote->name;
}
/**
* ostree_remote_get_url:
* @remote: an #OstreeRemote
*
* Get the URL from the remote.
*
* Returns: (transfer full): the remote's URL
* Since: 2017.14
*/
gchar *
ostree_remote_get_url (OstreeRemote *remote)
{
g_return_val_if_fail (remote != NULL, NULL);
g_return_val_if_fail (remote->ref_count > 0, NULL);
return g_key_file_get_string (remote->options, remote->group, "url", NULL);
}
+3 -2
View File
@@ -47,16 +47,17 @@ G_BEGIN_DECLS
typedef struct OstreeRemote OstreeRemote;
#endif
#ifndef __GI_SCANNER__
_OSTREE_PUBLIC
GType ostree_remote_get_type (void) G_GNUC_CONST;
_OSTREE_PUBLIC
OstreeRemote *ostree_remote_ref (OstreeRemote *remote);
_OSTREE_PUBLIC
void ostree_remote_unref (OstreeRemote *remote);
#endif /* GI_SCANNER */
_OSTREE_PUBLIC
const gchar *ostree_remote_get_name (OstreeRemote *remote);
_OSTREE_PUBLIC
gchar *ostree_remote_get_url (OstreeRemote *remote);
G_END_DECLS
+237 -116
View File
@@ -38,18 +38,33 @@
#include "ostree-checksum-input-stream.h"
#include "ostree-varint.h"
/* In most cases, we write into a staging dir for commit, but we also allow
* direct writes into objects/ for e.g. hardlink imports.
/* If fsync is enabled and we're in a txn, we write into a staging dir for
* commit, but we also allow direct writes into objects/ for e.g. hardlink
* imports.
*/
static int
commit_dest_dfd (OstreeRepo *self)
{
if (self->in_transaction)
if (self->in_transaction && !self->disable_fsync)
return self->commit_stagedir.fd;
else
return self->objects_dir_fd;
}
/* If we don't have O_TMPFILE, or for symlinks we'll create temporary
* files. If we have a txn, use the staging dir to ensure that
* things are consistently locked against concurrent cleanup, and
* in general we have all of our data in one place.
*/
static int
commit_tmp_dfd (OstreeRepo *self)
{
if (self->in_transaction)
return self->commit_stagedir.fd;
else
return self->tmp_dir_fd;
}
/* The objects/ directory has a two-character directory prefix for checksums
* to avoid putting lots of files in a single directory. This technique
* is quite old, but Git also uses it for example.
@@ -404,46 +419,116 @@ add_size_index_to_metadata (OstreeRepo *self,
return g_variant_ref_sink (g_variant_builder_end (builder));
}
typedef struct {
gboolean initialized;
GLnxTmpfile tmpf;
char *expected_checksum;
OtChecksum checksum;
guint64 content_len;
guint64 bytes_written;
guint uid;
guint gid;
guint mode;
GVariant *xattrs;
} OstreeRealRepoBareContent;
G_STATIC_ASSERT (sizeof (OstreeRepoBareContent) >= sizeof (OstreeRealRepoBareContent));
/* Create a tmpfile for writing a bare file. Currently just used
* by the static delta code, but will likely later be extended
* to be used also by the dfd_iter commit path.
*/
gboolean
_ostree_repo_open_content_bare (OstreeRepo *self,
const char *checksum,
guint64 content_len,
GLnxTmpfile *out_tmpf,
GCancellable *cancellable,
GError **error)
_ostree_repo_bare_content_open (OstreeRepo *self,
const char *expected_checksum,
guint64 content_len,
guint uid,
guint gid,
guint mode,
GVariant *xattrs,
OstreeRepoBareContent *out_regwrite,
GCancellable *cancellable,
GError **error)
{
return glnx_open_tmpfile_linkable_at (self->tmp_dir_fd, ".", O_WRONLY|O_CLOEXEC,
out_tmpf, error);
OstreeRealRepoBareContent *real = (OstreeRealRepoBareContent*) out_regwrite;
g_assert (!real->initialized);
real->initialized = TRUE;
g_assert (S_ISREG (mode));
if (!glnx_open_tmpfile_linkable_at (commit_tmp_dfd (self), ".", O_WRONLY|O_CLOEXEC,
&real->tmpf, error))
return FALSE;
ot_checksum_init (&real->checksum);
real->expected_checksum = g_strdup (expected_checksum);
real->content_len = content_len;
real->bytes_written = 0;
real->uid = uid;
real->gid = gid;
real->mode = mode;
real->xattrs = xattrs ? g_variant_ref (xattrs) : NULL;
/* Initialize the checksum with the header info */
g_autoptr(GFileInfo) finfo = _ostree_mode_uidgid_to_gfileinfo (mode, uid, gid);
g_autoptr(GBytes) header = _ostree_file_header_new (finfo, xattrs);
ot_checksum_update_bytes (&real->checksum, header);
return TRUE;
}
/* Used by static deltas, which have a separate "push" flow for
* regfile objects distinct from the "pull" model used by
* write_content_object().
*/
gboolean
_ostree_repo_commit_trusted_content_bare (OstreeRepo *self,
const char *checksum,
GLnxTmpfile *tmpf,
guint32 uid,
guint32 gid,
guint32 mode,
GVariant *xattrs,
GCancellable *cancellable,
GError **error)
_ostree_repo_bare_content_write (OstreeRepo *repo,
OstreeRepoBareContent *barewrite,
const guint8 *buf,
size_t len,
GCancellable *cancellable,
GError **error)
{
/* I don't think this is necessary, but a similar check was here previously,
* keeping it for extra redundancy.
*/
if (!tmpf->initialized || tmpf->fd == -1)
return TRUE;
OstreeRealRepoBareContent *real = (OstreeRealRepoBareContent*) barewrite;
g_assert (real->initialized);
ot_checksum_update (&real->checksum, buf, len);
if (glnx_loop_write (real->tmpf.fd, buf, len) < 0)
return glnx_throw_errno_prefix (error, "write");
return TRUE;
}
return commit_loose_regfile_object (self, checksum,
tmpf, uid, gid, mode, xattrs,
cancellable, error);
gboolean
_ostree_repo_bare_content_commit (OstreeRepo *self,
OstreeRepoBareContent *barewrite,
char *checksum_buf,
size_t buflen,
GCancellable *cancellable,
GError **error)
{
OstreeRealRepoBareContent *real = (OstreeRealRepoBareContent*) barewrite;
g_assert (real->initialized);
ot_checksum_get_hexdigest (&real->checksum, checksum_buf, buflen);
if (real->expected_checksum &&
!_ostree_compare_object_checksum (OSTREE_OBJECT_TYPE_FILE,
real->expected_checksum, checksum_buf,
error))
return FALSE;
if (!commit_loose_regfile_object (self, checksum_buf,
&real->tmpf, real->uid, real->gid,
real->mode, real->xattrs,
cancellable, error))
return FALSE;
/* Let's have a guarantee that after commit the object is cleaned up */
_ostree_repo_bare_content_cleanup (barewrite);
return TRUE;
}
void
_ostree_repo_bare_content_cleanup (OstreeRepoBareContent *regwrite)
{
OstreeRealRepoBareContent *real = (OstreeRealRepoBareContent*) regwrite;
if (!real->initialized)
return;
glnx_tmpfile_clear (&real->tmpf);
ot_checksum_clear (&real->checksum);
g_clear_pointer (&real->expected_checksum, (GDestroyNotify)g_free);
g_clear_pointer (&real->xattrs, (GDestroyNotify)g_variant_unref);
real->initialized = FALSE;
}
/* Allocate an O_TMPFILE, write everything from @input to it, but
@@ -459,7 +544,7 @@ create_regular_tmpfile_linkable_with_content (OstreeRepo *self,
GError **error)
{
g_auto(GLnxTmpfile) tmpf = { 0, };
if (!glnx_open_tmpfile_linkable_at (self->tmp_dir_fd, ".", O_WRONLY|O_CLOEXEC,
if (!glnx_open_tmpfile_linkable_at (commit_tmp_dfd (self), ".", O_WRONLY|O_CLOEXEC,
&tmpf, error))
return FALSE;
@@ -597,7 +682,7 @@ write_content_object (OstreeRepo *self,
*
* We use GLnxTmpfile for regular files, and OtCleanupUnlinkat for symlinks.
*/
g_auto(OtCleanupUnlinkat) tmp_unlinker = { self->tmp_dir_fd, NULL };
g_auto(OtCleanupUnlinkat) tmp_unlinker = { commit_tmp_dfd (self), NULL };
g_auto(GLnxTmpfile) tmpf = { 0, };
goffset unpacked_size = 0;
gboolean indexable = FALSE;
@@ -606,7 +691,7 @@ write_content_object (OstreeRepo *self,
{
/* This will not be hit for bare-user or archive */
g_assert (self->mode == OSTREE_REPO_MODE_BARE || self->mode == OSTREE_REPO_MODE_BARE_USER_ONLY);
if (!_ostree_make_temporary_symlink_at (self->tmp_dir_fd,
if (!_ostree_make_temporary_symlink_at (commit_tmp_dfd (self),
g_file_info_get_symlink_target (file_info),
&tmp_unlinker.path,
cancellable, error))
@@ -629,7 +714,7 @@ write_content_object (OstreeRepo *self,
if (self->generate_sizes)
indexable = TRUE;
if (!glnx_open_tmpfile_linkable_at (self->tmp_dir_fd, ".", O_WRONLY|O_CLOEXEC,
if (!glnx_open_tmpfile_linkable_at (commit_tmp_dfd (self), ".", O_WRONLY|O_CLOEXEC,
&tmpf, error))
return FALSE;
temp_out = g_unix_output_stream_new (tmpf.fd, FALSE);
@@ -719,14 +804,14 @@ write_content_object (OstreeRepo *self,
* Note, this does not apply for bare-user repos, as they store symlinks
* as regular files.
*/
if (G_UNLIKELY (fchownat (self->tmp_dir_fd, tmp_unlinker.path,
if (G_UNLIKELY (fchownat (tmp_unlinker.dfd, tmp_unlinker.path,
uid, gid, AT_SYMLINK_NOFOLLOW) == -1))
return glnx_throw_errno_prefix (error, "fchownat");
if (xattrs != NULL)
{
ot_security_smack_reset_dfd_name (self->tmp_dir_fd, tmp_unlinker.path);
if (!glnx_dfd_name_set_all_xattrs (self->tmp_dir_fd, tmp_unlinker.path,
ot_security_smack_reset_dfd_name (tmp_unlinker.dfd, tmp_unlinker.path);
if (!glnx_dfd_name_set_all_xattrs (tmp_unlinker.dfd, tmp_unlinker.path,
xattrs, cancellable, error))
return FALSE;
}
@@ -968,7 +1053,7 @@ write_metadata_object (OstreeRepo *self,
/* Write the metadata to a temporary file */
g_auto(GLnxTmpfile) tmpf = { 0, };
if (!glnx_open_tmpfile_linkable_at (self->tmp_dir_fd, ".", O_WRONLY|O_CLOEXEC,
if (!glnx_open_tmpfile_linkable_at (commit_tmp_dfd (self), ".", O_WRONLY|O_CLOEXEC,
&tmpf, error))
return FALSE;
if (!glnx_try_fallocate (tmpf.fd, 0, len, error))
@@ -1350,8 +1435,40 @@ rename_pending_loose_objects (OstreeRepo *self,
return glnx_throw_errno_prefix (error, "fsync");
}
if (!glnx_tmpdir_delete (&self->commit_stagedir, cancellable, error))
return TRUE;
}
/* Try to lock a transaction stage directory created by
* ostree_repo_prepare_transaction().
*/
static gboolean
cleanup_txn_dir (OstreeRepo *self,
int dfd,
const char *path,
GCancellable *cancellable,
GError **error)
{
g_auto(GLnxLockFile) lockfile = { 0, };
gboolean did_lock;
/* Try to lock, but if we don't get it, move on */
if (!_ostree_repo_try_lock_tmpdir (dfd, path, &lockfile, &did_lock, error))
return FALSE;
if (!did_lock)
return TRUE; /* Note early return */
/* If however this is the staging directory for the *current*
* boot, then don't delete it now - we may end up reusing it, as
* is the point.
*/
if (g_str_has_prefix (path, self->stagedir_prefix))
return TRUE; /* Note early return */
/* But, crucially we can now clean up staging directories
* from *other* boots.
*/
if (!glnx_shutil_rm_rf_at (dfd, path, cancellable, error))
return glnx_prefix_error (error, "Removing %s", path);
return TRUE;
}
@@ -1376,15 +1493,9 @@ cleanup_tmpdir (OstreeRepo *self,
while (TRUE)
{
guint64 delta;
struct dirent *dent;
struct stat stbuf;
g_auto(GLnxLockFile) lockfile = { 0, };
gboolean did_lock;
if (!glnx_dirfd_iterator_next_dent (&dfd_iter, &dent, cancellable, error))
return FALSE;
if (dent == NULL)
break;
@@ -1394,56 +1505,40 @@ cleanup_tmpdir (OstreeRepo *self,
if (strcmp (dent->d_name, "cache") == 0)
continue;
struct stat stbuf;
if (!glnx_fstatat_allow_noent (dfd_iter.fd, dent->d_name, &stbuf, AT_SYMLINK_NOFOLLOW, error))
return FALSE;
if (errno == ENOENT) /* Did another cleanup win? */
continue;
/* First, if it's a directory which needs locking, but it's
* busy, skip it.
*/
/* Handle transaction tmpdirs */
if (_ostree_repo_is_locked_tmpdir (dent->d_name))
{
if (!_ostree_repo_try_lock_tmpdir (dfd_iter.fd, dent->d_name,
&lockfile, &did_lock, error))
if (!cleanup_txn_dir (self, dfd_iter.fd, dent->d_name, cancellable, error))
return FALSE;
if (!did_lock)
continue;
continue; /* We've handled this, move on */
}
/* If however this is the staging directory for the *current*
* boot, then don't delete it now - we may end up reusing it, as
* is the point.
/* At this point we're looking at an unknown-origin file or directory in
* the tmpdir. This could be something like a temporary checkout dir (used
* by rpm-ostree), or (from older versions of libostree) a tempfile if we
* don't have O_TMPFILE for commits.
*/
if (g_str_has_prefix (dent->d_name, self->stagedir_prefix))
/* Ignore files from the future */
if (stbuf.st_mtime > curtime_secs)
continue;
else if (g_str_has_prefix (dent->d_name, OSTREE_REPO_TMPDIR_STAGING))
/* We're pruning content based on the expiry, which
* defaults to a day. That's what we were doing before we
* had locking...but in future we can be smarter here.
*/
guint64 delta = curtime_secs - stbuf.st_mtime;
if (delta > self->tmp_expiry_seconds)
{
/* But, crucially we can now clean up staging directories
* from *other* boots
*/
if (!glnx_shutil_rm_rf_at (dfd_iter.fd, dent->d_name, cancellable, error))
return glnx_prefix_error (error, "Removing %s", dent->d_name);
}
else
{
/* Now we do time-based cleanup. Ignore it if it's somehow
* in the future...
*/
if (stbuf.st_mtime > curtime_secs)
continue;
/* Now, we're pruning content based on the expiry, which
* defaults to a day. That's what we were doing before we
* had locking...but in future we can be smarter here.
*/
delta = curtime_secs - stbuf.st_mtime;
if (delta > self->tmp_expiry_seconds)
{
if (!glnx_shutil_rm_rf_at (dfd_iter.fd, dent->d_name, cancellable, error))
return glnx_prefix_error (error, "Removing %s", dent->d_name);
}
}
}
return TRUE;
@@ -1675,6 +1770,12 @@ ostree_repo_commit_transaction (OstreeRepo *self,
if (!rename_pending_loose_objects (self, cancellable, error))
return FALSE;
g_debug ("txn commit %s", glnx_basename (self->commit_stagedir.path));
if (!glnx_tmpdir_delete (&self->commit_stagedir, cancellable, error))
return FALSE;
glnx_release_lock_file (&self->commit_stagedir_lock);
/* This performs a global cleanup */
if (!cleanup_tmpdir (self, cancellable, error))
return FALSE;
@@ -1691,9 +1792,6 @@ ostree_repo_commit_transaction (OstreeRepo *self,
return FALSE;
g_clear_pointer (&self->txn_collection_refs, g_hash_table_destroy);
glnx_tmpdir_unset (&self->commit_stagedir);
glnx_release_lock_file (&self->commit_stagedir_lock);
self->in_transaction = FALSE;
if (!ot_ensure_unlinked_at (self->repo_dir_fd, "transaction", 0))
@@ -2695,47 +2793,70 @@ write_directory_content_to_mtree_internal (OstreeRepo *self,
g_assert (dir_enum != NULL || dfd_iter != NULL);
GFileType file_type = g_file_info_get_file_type (child_info);
const char *name = g_file_info_get_name (child_info);
g_ptr_array_add (path, (char*)name);
g_autofree char *child_relpath = ptrarray_path_join (path);
/* See if we have a devino hit; this is used below. Further, for bare-user
* repos we'll reload our file info from the object (specifically the
* ostreemeta xattr). The on-disk state is not normally what we want to
* commit. Basically we're making sure that we pick up "real" uid/gid and any
* xattrs there.
/* Load flags into boolean constants for ease of readability (we also need to
* NULL-check modifier)
*/
const char *loose_checksum = NULL;
g_autoptr(GVariant) source_xattrs = NULL;
if (dfd_iter != NULL && (file_type != G_FILE_TYPE_DIRECTORY))
{
guint32 dev = g_file_info_get_attribute_uint32 (child_info, "unix::device");
guint64 inode = g_file_info_get_attribute_uint64 (child_info, "unix::inode");
loose_checksum = devino_cache_lookup (self, modifier, dev, inode);
if (loose_checksum && self->mode == OSTREE_REPO_MODE_BARE_USER)
{
child_info = NULL;
if (!ostree_repo_load_file (self, loose_checksum, NULL, &child_info, &source_xattrs,
cancellable, error))
return FALSE;
}
}
g_autoptr(GFileInfo) modified_info = NULL;
OstreeRepoCommitFilterResult filter_result =
_ostree_repo_commit_modifier_apply (self, modifier, child_relpath, child_info, &modified_info);
const gboolean child_info_was_modified = !_ostree_gfileinfo_equal (child_info, modified_info);
const gboolean canonical_permissions = modifier &&
(modifier->flags & OSTREE_REPO_COMMIT_MODIFIER_FLAGS_CANONICAL_PERMISSIONS);
const gboolean devino_canonical = modifier &&
(modifier->flags & OSTREE_REPO_COMMIT_MODIFIER_FLAGS_DEVINO_CANONICAL);
/* We currently only honor the CONSUME flag in the dfd_iter case to avoid even
* more complexity in this function, and it'd mostly only be useful when
* operating on local filesystems anyways.
*/
const gboolean delete_after_commit = dfd_iter && modifier &&
(modifier->flags & OSTREE_REPO_COMMIT_MODIFIER_FLAGS_CONSUME);
const gboolean canonical_permissions = modifier &&
(modifier->flags & OSTREE_REPO_COMMIT_MODIFIER_FLAGS_CANONICAL_PERMISSIONS);
/* See if we have a devino hit; this is used below in a few places. */
const char *loose_checksum = NULL;
if (dfd_iter != NULL && (file_type != G_FILE_TYPE_DIRECTORY))
{
guint32 dev = g_file_info_get_attribute_uint32 (child_info, "unix::device");
guint64 inode = g_file_info_get_attribute_uint64 (child_info, "unix::inode");
loose_checksum = devino_cache_lookup (self, modifier, dev, inode);
if (loose_checksum && devino_canonical)
{
/* Go directly to checksum, do not pass Go, do not collect $200.
* In this mode the app is required to break hardlinks for any
* files it wants to modify.
*/
if (!ostree_mutable_tree_replace_file (mtree, name, loose_checksum, error))
return FALSE;
if (delete_after_commit)
{
if (!glnx_shutil_rm_rf_at (dfd_iter->fd, name, cancellable, error))
return FALSE;
}
return TRUE; /* Early return */
}
}
/* Build the full path which we need for callbacks */
g_ptr_array_add (path, (char*)name);
g_autofree char *child_relpath = ptrarray_path_join (path);
/* For bare-user repos we'll reload our file info from the object
* (specifically the ostreemeta xattr), if it was checked out that way (via
* hardlink). The on-disk state is not normally what we want to commit.
* Basically we're making sure that we pick up "real" uid/gid and any xattrs
* there.
*/
g_autoptr(GVariant) source_xattrs = NULL;
if (loose_checksum && self->mode == OSTREE_REPO_MODE_BARE_USER)
{
child_info = NULL;
if (!ostree_repo_load_file (self, loose_checksum, NULL, &child_info, &source_xattrs,
cancellable, error))
return FALSE;
}
/* Call the filter */
g_autoptr(GFileInfo) modified_info = NULL;
OstreeRepoCommitFilterResult filter_result =
_ostree_repo_commit_modifier_apply (self, modifier, child_relpath, child_info, &modified_info);
const gboolean child_info_was_modified = !_ostree_gfileinfo_equal (child_info, modified_info);
if (filter_result != OSTREE_REPO_COMMIT_FILTER_ALLOW)
{
+2 -2
View File
@@ -432,8 +432,8 @@ G_DEFINE_BOXED_TYPE (OstreeRepoFinderResult, ostree_repo_finder_result,
* 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
* @ref_to_checksum: (element-type OstreeCollectionRef utf8) (transfer none):
* 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
*
+30 -13
View File
@@ -365,24 +365,41 @@ _ostree_repo_commit_tmpf_final (OstreeRepo *self,
GCancellable *cancellable,
GError **error);
typedef struct {
gboolean initialized;
gpointer opaque0[10];
guint opaque1[10];
} OstreeRepoBareContent;
void _ostree_repo_bare_content_cleanup (OstreeRepoBareContent *regwrite);
G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(OstreeRepoBareContent, _ostree_repo_bare_content_cleanup)
gboolean
_ostree_repo_open_content_bare (OstreeRepo *self,
const char *checksum,
guint64 content_len,
GLnxTmpfile *out_tmpf,
_ostree_repo_bare_content_open (OstreeRepo *self,
const char *checksum,
guint64 content_len,
guint uid,
guint gid,
guint mode,
GVariant *xattrs,
OstreeRepoBareContent *out_regwrite,
GCancellable *cancellable,
GError **error);
gboolean
_ostree_repo_commit_trusted_content_bare (OstreeRepo *self,
const char *checksum,
GLnxTmpfile *tmpf,
guint32 uid,
guint32 gid,
guint32 mode,
GVariant *xattrs,
GCancellable *cancellable,
GError **error);
_ostree_repo_bare_content_write (OstreeRepo *repo,
OstreeRepoBareContent *barewrite,
const guint8 *buf,
size_t len,
GCancellable *cancellable,
GError **error);
gboolean
_ostree_repo_bare_content_commit (OstreeRepo *self,
OstreeRepoBareContent *barewrite,
char *checksum_buf,
size_t buflen,
GCancellable *cancellable,
GError **error);
gboolean
_ostree_repo_load_file_bare (OstreeRepo *self,
+96 -23
View File
@@ -2325,19 +2325,39 @@ process_one_static_delta (OtPullData *pull_data,
return ret;
}
/* Loop over the static delta data we got from the summary,
* and find the newest commit for @out_from_revision that
* goes to @to_revision.
/*
* DELTA_SEARCH_RESULT_UNCHANGED:
* We already have the commit.
*
* Additionally, @out_have_scratch_delta will be set to %TRUE
* if there is a %NULL → @to_revision delta, also known as
* DELTA_SEARCH_RESULT_NO_MATCH:
* No deltas were found.
*
* DELTA_SEARCH_RESULT_FROM:
* A regular delta was found, and the "from" revision will be
* set in `from_revision`.
*
* DELTA_SEARCH_RESULT_SCRATCH:
* There is a %NULL → @to_revision delta, also known as
* a "from scratch" delta.
*/
typedef struct {
enum {
DELTA_SEARCH_RESULT_UNCHANGED,
DELTA_SEARCH_RESULT_NO_MATCH,
DELTA_SEARCH_RESULT_FROM,
DELTA_SEARCH_RESULT_SCRATCH,
} result;
char from_revision[OSTREE_SHA256_STRING_LEN+1];
} DeltaSearchResult;
/* Loop over the static delta data we got from the summary,
* and find the a delta path (if available) that goes to @to_revision.
* See the enum in `DeltaSearchResult` for available result types.
*/
static gboolean
get_best_static_delta_start_for (OtPullData *pull_data,
const char *to_revision,
gboolean *out_have_scratch_delta,
char **out_from_revision,
DeltaSearchResult *out_result,
GCancellable *cancellable,
GError **error)
{
@@ -2348,7 +2368,28 @@ get_best_static_delta_start_for (OtPullData *pull_data,
g_assert (pull_data->summary_deltas_checksums != NULL);
*out_have_scratch_delta = FALSE;
out_result->result = DELTA_SEARCH_RESULT_NO_MATCH;
out_result->from_revision[0] = '\0';
/* First, do we already have this commit completely downloaded? */
gboolean have_to_rev;
if (!ostree_repo_has_object (pull_data->repo, OSTREE_OBJECT_TYPE_COMMIT,
to_revision, &have_to_rev,
cancellable, error))
return FALSE;
if (have_to_rev)
{
OstreeRepoCommitState to_rev_state;
if (!ostree_repo_load_commit (pull_data->repo, to_revision,
NULL, &to_rev_state, error))
return FALSE;
if (!(to_rev_state & OSTREE_REPO_COMMIT_STATE_PARTIAL))
{
/* We already have this commit, we're done! */
out_result->result = DELTA_SEARCH_RESULT_UNCHANGED;
return TRUE; /* Early return */
}
}
/* Loop over all deltas known from the summary file,
* finding ones which go to to_revision */
@@ -2366,9 +2407,17 @@ get_best_static_delta_start_for (OtPullData *pull_data,
continue;
if (cur_from_rev)
g_ptr_array_add (candidates, g_steal_pointer (&cur_from_rev));
{
g_ptr_array_add (candidates, g_steal_pointer (&cur_from_rev));
}
else
*out_have_scratch_delta = TRUE;
{
/* We note that we have a _SCRATCH delta here, but we'll prefer using
* "from" deltas (obviously, they'll be smaller) where possible if we
* find one below.
*/
out_result->result = DELTA_SEARCH_RESULT_SCRATCH;
}
}
/* Loop over our candidates, find the newest one */
@@ -2407,7 +2456,11 @@ get_best_static_delta_start_for (OtPullData *pull_data,
}
}
*out_from_revision = g_strdup (newest_candidate);
if (newest_candidate)
{
out_result->result = DELTA_SEARCH_RESULT_FROM;
memcpy (out_result->from_revision, newest_candidate, OSTREE_SHA256_STRING_LEN+1);
}
return TRUE;
}
@@ -3082,25 +3135,45 @@ initiate_request (OtPullData *pull_data,
/* If we have a summary, we can use the newer logic */
if (pull_data->summary)
{
gboolean have_scratch_delta = FALSE;
DeltaSearchResult deltares;
/* Look for a delta to @to_revision in the summary data */
if (!get_best_static_delta_start_for (pull_data, to_revision,
&have_scratch_delta, &delta_from_revision,
if (!get_best_static_delta_start_for (pull_data, to_revision, &deltares,
pull_data->cancellable, error))
return FALSE;
if (delta_from_revision) /* Did we find a delta FROM commit? */
initiate_delta_request (pull_data, delta_from_revision, to_revision, ref);
else if (have_scratch_delta) /* No delta FROM, do we have a scratch? */
initiate_delta_request (pull_data, NULL, to_revision, ref);
else if (pull_data->require_static_deltas) /* No deltas found; are they required? */
switch (deltares.result)
{
set_required_deltas_error (error, (ref != NULL) ? ref->ref_name : "", to_revision);
return FALSE;
case DELTA_SEARCH_RESULT_NO_MATCH:
{
if (pull_data->require_static_deltas) /* No deltas found; are they required? */
{
set_required_deltas_error (error, (ref != NULL) ? ref->ref_name : "", to_revision);
return FALSE;
}
else /* No deltas, fall back to object fetches. */
queue_scan_one_metadata_object (pull_data, to_revision, OSTREE_OBJECT_TYPE_COMMIT, NULL, 0, ref);
}
break;
case DELTA_SEARCH_RESULT_FROM:
initiate_delta_request (pull_data, deltares.from_revision, to_revision, ref);
break;
case DELTA_SEARCH_RESULT_SCRATCH:
initiate_delta_request (pull_data, NULL, to_revision, ref);
break;
case DELTA_SEARCH_RESULT_UNCHANGED:
{
/* If we already have the commit, here things get a little special; we've historically
* fetched detached metadata, so let's keep doing that. But in the --require-static-deltas
* path, we don't, under the assumption the user wants as little network traffic as
* possible.
*/
if (pull_data->require_static_deltas)
break;
else
queue_scan_one_metadata_object (pull_data, to_revision, OSTREE_OBJECT_TYPE_COMMIT, NULL, 0, ref);
}
}
else /* No deltas, fall back to object fetches. */
queue_scan_one_metadata_object (pull_data, to_revision, OSTREE_OBJECT_TYPE_COMMIT, NULL, 0, ref);
}
else if (ref != NULL)
{
+10 -5
View File
@@ -725,7 +725,8 @@ _ostree_repo_list_refs_internal (OstreeRepo *self,
* ostree_repo_list_refs:
* @self: Repo
* @refspec_prefix: (allow-none): Only list refs which match this prefix
* @out_all_refs: (out) (element-type utf8 utf8): Mapping from ref to checksum
* @out_all_refs: (out) (element-type utf8 utf8) (transfer container):
* Mapping from ref to checksum
* @cancellable: Cancellable
* @error: Error
*
@@ -750,7 +751,8 @@ ostree_repo_list_refs (OstreeRepo *self,
* ostree_repo_list_refs_ext:
* @self: Repo
* @refspec_prefix: (allow-none): Only list refs which match this prefix
* @out_all_refs: (out) (element-type utf8 utf8): Mapping from ref to checksum
* @out_all_refs: (out) (element-type utf8 utf8) (transfer container):
* Mapping from ref to checksum
* @flags: Options controlling listing behavior
* @cancellable: Cancellable
* @error: Error
@@ -778,7 +780,8 @@ ostree_repo_list_refs_ext (OstreeRepo *self,
* ostree_repo_remote_list_refs:
* @self: Repo
* @remote_name: Name of the remote.
* @out_all_refs: (out) (element-type utf8 utf8): Mapping from ref to checksum
* @out_all_refs: (out) (element-type utf8 utf8) (transfer container):
* Mapping from ref to checksum
* @cancellable: Cancellable
* @error: Error
*
@@ -893,7 +896,8 @@ remote_list_collection_refs_process_refs (OstreeRepo *self,
* ostree_repo_remote_list_collection_refs:
* @self: Repo
* @remote_name: Name of the remote.
* @out_all_refs: (out) (element-type OstreeCollectionRef utf8): Mapping from collection–ref to checksum
* @out_all_refs: (out) (element-type OstreeCollectionRef utf8) (transfer container):
* Mapping from collection–ref to checksum
* @cancellable: Cancellable
* @error: Error
*
@@ -1165,7 +1169,8 @@ _ostree_repo_update_collection_refs (OstreeRepo *self,
* 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
* @out_all_refs: (out) (element-type OstreeCollectionRef utf8) (transfer container):
* Mapping from collection–ref to checksum
* @flags: Options controlling listing behavior
* @cancellable: Cancellable
* @error: Error
@@ -217,6 +217,37 @@ string_array_nonempty_intersection (GPtrArray *a,
return FALSE;
}
static gboolean
sizename_is_delta_candidate (OstreeDeltaContentSizeNames *sizename)
{
/* Don't build candidates for the empty object */
if (sizename->size == 0)
return FALSE;
/* Look for known non-delta-able files (currently just compression like xz) */
for (guint i = 0; i < sizename->basenames->len; i++)
{
const char *name = sizename->basenames->pdata[i];
/* We could replace this down the line with g_content_type_guess() or so,
* but it's not clear to me that's a major win; we'd still need to
* maintain a list of compression formats, and this doesn't require
* allocation.
* NB: We explicitly don't have .gz here in case someone might be
* using --rsyncable for that.
*/
const char *dot = strrchr (name, '.');
if (!dot)
continue;
const char *extension = dot+1;
/* Don't add .gz here, see above */
if (g_str_equal (extension, "xz") || g_str_equal (extension, "bz2"))
return FALSE;
}
/* Let's try it */
return TRUE;
}
/*
* Build up a map of files with matching basenames and similar size,
* and use it to find apparently similar objects.
@@ -258,7 +289,7 @@ _ostree_delta_compute_similar_objects (OstreeRepo *repo,
&to_sizes,
cancellable, error))
goto out;
/* Iterate over all newly added objects, find objects which have
* similar basename and sizes.
*
@@ -277,8 +308,7 @@ _ostree_delta_compute_similar_objects (OstreeRepo *repo,
const guint64 max_threshold = to_sizenames->size *
(1.0+similarity_percent_threshold/100.0);
/* Don't build candidates for the empty object */
if (to_sizenames->size == 0)
if (!sizename_is_delta_candidate (to_sizenames))
continue;
for (fuzzy = 0; fuzzy < 2 && !found; fuzzy++)
@@ -286,12 +316,8 @@ _ostree_delta_compute_similar_objects (OstreeRepo *repo,
for (j = lower; j < upper; j++)
{
OstreeDeltaContentSizeNames *from_sizenames = from_sizes->pdata[j];
/* Don't build candidates for the empty object */
if (from_sizenames->size == 0)
{
continue;
}
if (!sizename_is_delta_candidate (from_sizenames))
continue;
if (from_sizenames->size < min_threshold)
{
@@ -55,11 +55,9 @@ typedef struct {
GError **async_error;
OstreeObjectType output_objtype;
GLnxTmpfile tmpf;
guint64 content_size;
GOutputStream *content_out;
OtChecksum content_checksum;
char checksum[OSTREE_SHA256_STRING_LEN+1];
OstreeRepoBareContent content_out;
char *read_source_object;
int read_source_fd;
gboolean have_obj;
@@ -278,9 +276,7 @@ _ostree_static_delta_part_execute (OstreeRepo *repo,
ret = TRUE;
out:
glnx_tmpfile_clear (&state->tmpf);
g_clear_object (&state->content_out);
ot_checksum_clear (&state->content_checksum);
_ostree_repo_bare_content_cleanup (&state->content_out);
return ret;
}
@@ -378,29 +374,6 @@ validate_ofs (StaticDeltaExecutionState *state,
return TRUE;
}
static gboolean
content_out_write (OstreeRepo *repo,
StaticDeltaExecutionState *state,
const guint8* buf,
gsize len,
GCancellable *cancellable,
GError **error)
{
gsize bytes_written;
if (state->content_checksum.initialized)
ot_checksum_update (&state->content_checksum, buf, len);
/* Ignore bytes_written since we discard partial content */
if (!g_output_stream_write_all (state->content_out,
buf, len,
&bytes_written,
cancellable, error))
return FALSE;
return TRUE;
}
static gboolean
do_content_open_generic (OstreeRepo *repo,
StaticDeltaExecutionState *state,
@@ -485,33 +458,15 @@ dispatch_bspatch (OstreeRepo *repo,
&stream) < 0)
return FALSE;
if (!content_out_write (repo, state, buf, state->content_size,
cancellable, error))
if (!_ostree_repo_bare_content_write (repo, &state->content_out,
buf, state->content_size,
cancellable, error))
return FALSE;
}
return TRUE;
}
/* Before, we had a distinction between "trusted" and "untrusted" deltas
* which we've decided wasn't a good idea. Now, we always checksum the content.
* Compare with what ostree_checksum_file_from_input() is doing too.
*/
static gboolean
handle_untrusted_content_checksum (OstreeRepo *repo,
StaticDeltaExecutionState *state,
GCancellable *cancellable,
GError **error)
{
g_autoptr(GFileInfo) finfo = _ostree_mode_uidgid_to_gfileinfo (state->mode, state->uid, state->gid);
g_autoptr(GBytes) header = _ostree_file_header_new (finfo, state->xattrs);
ot_checksum_init (&state->content_checksum);
ot_checksum_update_bytes (&state->content_checksum, header);
return TRUE;
}
static gboolean
dispatch_open_splice_and_close (OstreeRepo *repo,
StaticDeltaExecutionState *state,
@@ -589,20 +544,18 @@ dispatch_open_splice_and_close (OstreeRepo *repo,
if (!state->have_obj)
{
if (!_ostree_repo_open_content_bare (repo, state->checksum,
if (!_ostree_repo_bare_content_open (repo, state->checksum,
state->content_size,
&state->tmpf,
state->uid, state->gid, state->mode,
state->xattrs,
&state->content_out,
cancellable, error))
goto out;
state->content_out = g_unix_output_stream_new (state->tmpf.fd, FALSE);
if (!handle_untrusted_content_checksum (repo, state, cancellable, error))
goto out;
if (!content_out_write (repo, state,
state->payload_data + content_offset,
state->content_size,
cancellable, error))
if (!_ostree_repo_bare_content_write (repo, &state->content_out,
state->payload_data + content_offset,
state->content_size,
cancellable, error))
goto out;
}
}
@@ -691,17 +644,15 @@ dispatch_open (OstreeRepo *repo,
if (!state->have_obj)
{
if (!_ostree_repo_open_content_bare (repo, state->checksum,
if (!_ostree_repo_bare_content_open (repo, state->checksum,
state->content_size,
&state->tmpf,
state->uid, state->gid, state->mode,
state->xattrs,
&state->content_out,
cancellable, error))
return FALSE;
state->content_out = g_unix_output_stream_new (state->tmpf.fd, FALSE);
}
if (!handle_untrusted_content_checksum (repo, state, cancellable, error))
return FALSE;
return TRUE;
}
@@ -740,8 +691,9 @@ dispatch_write (OstreeRepo *repo,
if (G_UNLIKELY (bytes_read == 0))
return glnx_throw (error, "Unexpected EOF reading object %s", state->read_source_object);
if (!content_out_write (repo, state, (guint8*)buf, bytes_read,
cancellable, error))
if (!_ostree_repo_bare_content_write (repo, &state->content_out,
(guint8*)buf, bytes_read,
cancellable, error))
return FALSE;
content_size -= bytes_read;
@@ -753,8 +705,9 @@ dispatch_write (OstreeRepo *repo,
if (!validate_ofs (state, content_offset, content_size, error))
return FALSE;
if (!content_out_write (repo, state, state->payload_data + content_offset, content_size,
cancellable, error))
if (!_ostree_repo_bare_content_write (repo, &state->content_out,
state->payload_data + content_offset, content_size,
cancellable, error))
return FALSE;
}
}
@@ -818,34 +771,22 @@ dispatch_close (OstreeRepo *repo,
{
GLNX_AUTO_PREFIX_ERROR("opcode close", error);
if (state->content_out)
if (state->content_out.initialized)
{
if (!g_output_stream_flush (state->content_out, cancellable, error))
char actual_checksum[OSTREE_SHA256_STRING_LEN+1];
if (!_ostree_repo_bare_content_commit (repo, &state->content_out, actual_checksum,
sizeof (actual_checksum),
cancellable, error))
return FALSE;
if (state->content_checksum.initialized)
{
char actual_checksum[OSTREE_SHA256_STRING_LEN+1];
ot_checksum_get_hexdigest (&state->content_checksum, actual_checksum, sizeof (actual_checksum));
if (strcmp (actual_checksum, state->checksum) != 0)
return glnx_throw (error, "Corrupted object %s (actual checksum is %s)",
state->checksum, actual_checksum);
}
if (!_ostree_repo_commit_trusted_content_bare (repo, state->checksum, &state->tmpf,
state->uid, state->gid, state->mode,
state->xattrs,
cancellable, error))
return FALSE;
g_clear_object (&state->content_out);
g_assert_cmpstr (state->checksum, ==, actual_checksum);
}
if (!dispatch_unset_read_source (repo, state, cancellable, error))
return FALSE;
g_clear_pointer (&state->xattrs, g_variant_unref);
ot_checksum_clear (&state->content_checksum);
_ostree_repo_bare_content_cleanup (&state->content_out);
state->checksum_index++;
state->output_target = NULL;
+36 -58
View File
@@ -56,10 +56,6 @@ ostree_repo_commit_traverse_iter_init_commit (OstreeRepoCommitTraverseIter *it
{
struct _OstreeRepoRealCommitTraverseIter *real =
(struct _OstreeRepoRealCommitTraverseIter*)iter;
gboolean ret = FALSE;
const guchar *csum;
g_autoptr(GVariant) meta_csum_bytes = NULL;
g_autoptr(GVariant) content_csum_bytes = NULL;
memset (real, 0, sizeof (*real));
real->initialized = TRUE;
@@ -68,21 +64,21 @@ ostree_repo_commit_traverse_iter_init_commit (OstreeRepoCommitTraverseIter *it
real->current_dir = NULL;
real->idx = 0;
g_autoptr(GVariant) content_csum_bytes = NULL;
g_variant_get_child (commit, 6, "@ay", &content_csum_bytes);
csum = ostree_checksum_bytes_peek_validate (content_csum_bytes, error);
const guchar *csum = ostree_checksum_bytes_peek_validate (content_csum_bytes, error);
if (!csum)
goto out;
return FALSE;
ostree_checksum_inplace_from_bytes (csum, real->checksum_content);
g_autoptr(GVariant) meta_csum_bytes = NULL;
g_variant_get_child (commit, 7, "@ay", &meta_csum_bytes);
csum = ostree_checksum_bytes_peek_validate (meta_csum_bytes, error);
if (!csum)
goto out;
return FALSE;
ostree_checksum_inplace_from_bytes (csum, real->checksum_meta);
ret = TRUE;
out:
return ret;
return TRUE;
}
/**
@@ -312,8 +308,6 @@ traverse_iter (OstreeRepo *repo,
GCancellable *cancellable,
GError **error)
{
gboolean ret = FALSE;
while (TRUE)
{
g_autoptr(GVariant) key = NULL;
@@ -330,12 +324,11 @@ traverse_iter (OstreeRepo *repo,
g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
{
g_debug ("Ignoring not-found dirmeta");
ret = TRUE;
return TRUE; /* Note early return */
}
else
g_propagate_error (error, g_steal_pointer (&local_error));
goto out;
g_propagate_error (error, g_steal_pointer (&local_error));
return FALSE;
}
else if (iterres == OSTREE_REPO_COMMIT_ITER_RESULT_END)
break;
@@ -371,16 +364,14 @@ traverse_iter (OstreeRepo *repo,
if (!traverse_dirtree (repo, content_checksum, inout_reachable,
ignore_missing_dirs, cancellable, error))
goto out;
return FALSE;
}
}
else
g_assert_not_reached ();
}
ret = TRUE;
out:
return ret;
return TRUE;
}
static gboolean
@@ -391,12 +382,9 @@ traverse_dirtree (OstreeRepo *repo,
GCancellable *cancellable,
GError **error)
{
gboolean ret = FALSE;
g_autoptr(GVariant) dirtree = NULL;
ostree_cleanup_repo_commit_traverse_iter
OstreeRepoCommitTraverseIter iter = { 0, };
g_autoptr(GError) local_error = NULL;
g_autoptr(GVariant) dirtree = NULL;
if (!ostree_repo_load_variant (repo, OSTREE_OBJECT_TYPE_DIR_TREE, checksum,
&dirtree, &local_error))
{
@@ -404,26 +392,25 @@ traverse_dirtree (OstreeRepo *repo,
g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
{
g_debug ("Ignoring not-found dirmeta %s", checksum);
ret = TRUE;
return TRUE; /* Early return */
}
else
g_propagate_error (error, g_steal_pointer (&local_error));
goto out;
g_propagate_error (error, g_steal_pointer (&local_error));
return FALSE;
}
g_debug ("Traversing dirtree %s", checksum);
ostree_cleanup_repo_commit_traverse_iter
OstreeRepoCommitTraverseIter iter = { 0, };
if (!ostree_repo_commit_traverse_iter_init_dirtree (&iter, repo, dirtree,
OSTREE_REPO_COMMIT_TRAVERSE_FLAG_NONE,
error))
goto out;
return FALSE;
if (!traverse_iter (repo, &iter, inout_reachable, ignore_missing_dirs, cancellable, error))
goto out;
return FALSE;
ret = TRUE;
out:
return ret;
return TRUE;
}
/**
@@ -446,28 +433,21 @@ ostree_repo_traverse_commit_union (OstreeRepo *repo,
GCancellable *cancellable,
GError **error)
{
gboolean ret = FALSE;
g_autofree char *tmp_checksum = NULL;
while (TRUE)
{
gboolean recurse = FALSE;
g_autoptr(GVariant) key = NULL;
g_autoptr(GVariant) commit = NULL;
ostree_cleanup_repo_commit_traverse_iter
OstreeRepoCommitTraverseIter iter = { 0, };
OstreeRepoCommitState commitstate;
gboolean ignore_missing_dirs = FALSE;
key = g_variant_ref_sink (ostree_object_name_serialize (commit_checksum, OSTREE_OBJECT_TYPE_COMMIT));
g_autoptr(GVariant) key =
g_variant_ref_sink (ostree_object_name_serialize (commit_checksum, OSTREE_OBJECT_TYPE_COMMIT));
if (g_hash_table_contains (inout_reachable, key))
break;
g_autoptr(GVariant) commit = NULL;
if (!ostree_repo_load_variant_if_exists (repo, OSTREE_OBJECT_TYPE_COMMIT,
commit_checksum, &commit,
error))
goto out;
return FALSE;
/* Just return if the parent isn't found; we do expect most
* people to have partial repositories.
@@ -476,10 +456,12 @@ ostree_repo_traverse_commit_union (OstreeRepo *repo,
break;
/* See if the commit is partial, if so it's not an error to lack objects */
OstreeRepoCommitState commitstate;
if (!ostree_repo_load_commit (repo, commit_checksum, NULL, &commitstate,
error))
goto out;
return FALSE;
gboolean ignore_missing_dirs = FALSE;
if ((commitstate & OSTREE_REPO_COMMIT_STATE_PARTIAL) != 0)
ignore_missing_dirs = TRUE;
@@ -487,14 +469,17 @@ ostree_repo_traverse_commit_union (OstreeRepo *repo,
key = NULL;
g_debug ("Traversing commit %s", commit_checksum);
ostree_cleanup_repo_commit_traverse_iter
OstreeRepoCommitTraverseIter iter = { 0, };
if (!ostree_repo_commit_traverse_iter_init_commit (&iter, repo, commit,
OSTREE_REPO_COMMIT_TRAVERSE_FLAG_NONE,
error))
goto out;
return FALSE;
if (!traverse_iter (repo, &iter, inout_reachable, ignore_missing_dirs, cancellable, error))
goto out;
return FALSE;
gboolean recurse = FALSE;
if (maxdepth == -1 || maxdepth > 0)
{
g_free (tmp_checksum);
@@ -511,9 +496,7 @@ ostree_repo_traverse_commit_union (OstreeRepo *repo,
break;
}
ret = TRUE;
out:
return ret;
return TRUE;
}
/**
@@ -536,17 +519,12 @@ ostree_repo_traverse_commit (OstreeRepo *repo,
GCancellable *cancellable,
GError **error)
{
gboolean ret = FALSE;
g_autoptr(GHashTable) ret_reachable =
ostree_repo_traverse_new_reachable ();
g_autoptr(GHashTable) ret_reachable = ostree_repo_traverse_new_reachable ();
if (!ostree_repo_traverse_commit_union (repo, commit_checksum, maxdepth,
ret_reachable, cancellable, error))
goto out;
return FALSE;
ret = TRUE;
if (out_reachable)
*out_reachable = g_steal_pointer (&ret_reachable);
out:
return ret;
return TRUE;
}
+27 -4
View File
@@ -1918,8 +1918,9 @@ ostree_repo_create (OstreeRepo *self,
g_variant_new_variant (g_variant_new_string (self->collection_id)));
glnx_autofd int repo_dir_fd = -1;
g_autoptr(GVariant) options = g_variant_ref_sink (g_variant_builder_end (builder));
if (!repo_create_at_internal (AT_FDCWD, repopath, mode,
g_variant_builder_end (builder),
options,
&repo_dir_fd,
cancellable, error))
return FALSE;
@@ -4903,7 +4904,16 @@ _ostree_repo_try_lock_tmpdir (int tmpdir_dfd,
}
else
{
did_lock = TRUE;
/* It's possible that we got a lock after seeing the directory, but
* another process deleted the tmpdir, so verify it still exists.
*/
struct stat stbuf;
if (!glnx_fstatat_allow_noent (tmpdir_dfd, tmpdir_name, &stbuf, AT_SYMLINK_NOFOLLOW, error))
return FALSE;
if (errno == 0 && S_ISDIR (stbuf.st_mode))
did_lock = TRUE;
else
glnx_release_lock_file (file_lock_out);
}
*out_did_lock = did_lock;
@@ -4954,7 +4964,8 @@ _ostree_repo_allocate_tmpdir (int tmpdir_dfd,
if (!glnx_opendirat (dfd_iter.fd, dent->d_name, FALSE,
&target_dfd, &local_error))
{
if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_NOT_DIRECTORY))
if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_NOT_DIRECTORY) ||
g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
continue;
else
{
@@ -4978,6 +4989,8 @@ _ostree_repo_allocate_tmpdir (int tmpdir_dfd,
(void)futimens (target_dfd, NULL);
/* We found an existing tmpdir which we managed to lock */
g_debug ("Reusing tmpdir %s", dent->d_name);
reusing_dir = TRUE;
ret_tmpdir.src_dfd = tmpdir_dfd;
ret_tmpdir.fd = glnx_steal_fd (&target_dfd);
ret_tmpdir.path = g_strdup (dent->d_name);
@@ -5000,8 +5013,18 @@ _ostree_repo_allocate_tmpdir (int tmpdir_dfd,
error))
return FALSE;
if (!did_lock)
continue;
{
/* We raced and someone else already locked the newly created
* directory. Free the resources here and then mark it as
* uninitialized so glnx_tmpdir_cleanup doesn't delete the directory
* when new_tmpdir goes out of scope.
*/
glnx_tmpdir_unset (&new_tmpdir);
new_tmpdir.initialized = FALSE;
continue;
}
g_debug ("Using new tmpdir %s", new_tmpdir.path);
ret_tmpdir = new_tmpdir; /* Transfer ownership */
new_tmpdir.initialized = FALSE;
}
+15
View File
@@ -539,7 +539,20 @@ gboolean ostree_repo_load_variant_if_exists (OstreeRepo *self,
GVariant **out_variant,
GError **error);
/**
* OstreeRepoCommitState:
* @OSTREE_REPO_COMMIT_STATE_NORMAL: Commit is complete. This is the default.
* (Since: 2017.14.)
* @OSTREE_REPO_COMMIT_STATE_PARTIAL: One or more objects are missing from the
* local copy of the commit, but metadata is present. (Since: 2015.7.)
*
* Flags representing the state of a commit in the local repository, as returned
* by ostree_repo_load_commit().
*
* Since: 2015.7
*/
typedef enum {
OSTREE_REPO_COMMIT_STATE_NORMAL = 0,
OSTREE_REPO_COMMIT_STATE_PARTIAL = (1 << 0),
} OstreeRepoCommitState;
@@ -631,6 +644,7 @@ typedef OstreeRepoCommitFilterResult (*OstreeRepoCommitFilter) (OstreeRepo *r
* @OSTREE_REPO_COMMIT_MODIFIER_FLAGS_CANONICAL_PERMISSIONS: Canonicalize permissions for bare-user-only mode.
* @OSTREE_REPO_COMMIT_MODIFIER_FLAGS_ERROR_ON_UNLABELED: Emit an error if configured SELinux policy does not provide a label
* @OSTREE_REPO_COMMIT_MODIFIER_FLAGS_CONSUME: Delete added files/directories after commit; Since: 2017.13
* @OSTREE_REPO_COMMIT_MODIFIER_FLAGS_DEVINO_CANONICAL: If a devino cache hit is found, skip modifier filters (non-directories only); Since: 2017.14
*/
typedef enum {
OSTREE_REPO_COMMIT_MODIFIER_FLAGS_NONE = 0,
@@ -639,6 +653,7 @@ typedef enum {
OSTREE_REPO_COMMIT_MODIFIER_FLAGS_CANONICAL_PERMISSIONS = (1 << 2),
OSTREE_REPO_COMMIT_MODIFIER_FLAGS_ERROR_ON_UNLABELED = (1 << 3),
OSTREE_REPO_COMMIT_MODIFIER_FLAGS_CONSUME = (1 << 4),
OSTREE_REPO_COMMIT_MODIFIER_FLAGS_DEVINO_CANONICAL = (1 << 5),
} OstreeRepoCommitModifierFlags;
/**
+1 -1
View File
@@ -82,7 +82,7 @@ encode_pair (GString *str, const char *name, const char *value)
/**
* soup_form_encode_hash:
* @form_data_set: (element-type utf8 utf8): a hash table containing
* @form_data_set: (element-type utf8 utf8) (transfer none): a hash table containing
* name/value pairs (as strings)
*
* Encodes @form_data_set into a value of type
+1 -1
View File
@@ -1281,7 +1281,7 @@ soup_uri_set_query (SoupURI *uri, const char *query)
/**
* soup_uri_set_query_from_form:
* @uri: a #SoupURI
* @form: (element-type utf8 utf8): a #GHashTable containing HTML form
* @form: (element-type utf8 utf8) (transfer none): a #GHashTable containing HTML form
* information
*
* Sets @uri's query to the result of encoding @form according to the
+3 -3
View File
@@ -43,7 +43,7 @@
*
* Since: 2017.4
*/
#define OSTREE_RELEASE_VERSION (13)
#define OSTREE_RELEASE_VERSION (14)
/**
* OSTREE_VERSION
@@ -52,7 +52,7 @@
*
* Since: 2017.4
*/
#define OSTREE_VERSION (2017.13)
#define OSTREE_VERSION (2017.14)
/**
* OSTREE_VERSION_S:
@@ -62,7 +62,7 @@
*
* Since: 2017.4
*/
#define OSTREE_VERSION_S "2017.13"
#define OSTREE_VERSION_S "2017.14"
#define OSTREE_ENCODE_VERSION(year,release) \
((year) << 16 | (release))