New upstream version 2017.14
This commit is contained in:
commit
84beec3c14
77 files changed
+1754
-1343
No files matched your search
@@ -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
|
||||
|
||||
@@ -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;
|
||||
@@ -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.
|
||||
*/
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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
|
||||
*
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
/**
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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))
|
||||
|
||||
Reference in new issue
Block a user