New upstream version 2018.4
This commit is contained in:
commit
de3c1878b1
85 files changed
+1520
-1199
No files matched your search
@@ -18,6 +18,8 @@
|
||||
***/
|
||||
|
||||
/* Add new symbols here. Release commits should copy this section into -released.sym. */
|
||||
LIBOSTREE_2018.5 {
|
||||
} LIBOSTREE_2018.3;
|
||||
|
||||
/* 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
|
||||
|
||||
@@ -458,6 +458,12 @@ LIBOSTREE_2018.2 {
|
||||
ostree_commit_get_content_checksum;
|
||||
} LIBOSTREE_2018.1;
|
||||
|
||||
LIBOSTREE_2018.3 {
|
||||
ostree_deployment_origin_remove_transient_state;
|
||||
ostree_sysroot_deployment_set_pinned;
|
||||
ostree_deployment_is_pinned;
|
||||
} LIBOSTREE_2018.2;
|
||||
|
||||
/* NOTE: Only add more content here in release commits! See the
|
||||
* comments at the top of this file.
|
||||
*/
|
||||
@@ -1239,6 +1239,8 @@ ostree_object_type_to_string (OstreeObjectType objtype)
|
||||
return "tombstone-commit";
|
||||
case OSTREE_OBJECT_TYPE_COMMIT_META:
|
||||
return "commitmeta";
|
||||
case OSTREE_OBJECT_TYPE_PAYLOAD_LINK:
|
||||
return "payload-link";
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
return NULL;
|
||||
@@ -1266,6 +1268,8 @@ ostree_object_type_from_string (const char *str)
|
||||
return OSTREE_OBJECT_TYPE_TOMBSTONE_COMMIT;
|
||||
else if (!strcmp (str, "commitmeta"))
|
||||
return OSTREE_OBJECT_TYPE_COMMIT_META;
|
||||
else if (!strcmp (str, "payload-link"))
|
||||
return OSTREE_OBJECT_TYPE_PAYLOAD_LINK;
|
||||
g_assert_not_reached ();
|
||||
return 0;
|
||||
}
|
||||
@@ -2122,6 +2126,7 @@ _ostree_validate_structureof_metadata (OstreeObjectType objtype,
|
||||
break;
|
||||
case OSTREE_OBJECT_TYPE_TOMBSTONE_COMMIT:
|
||||
case OSTREE_OBJECT_TYPE_COMMIT_META:
|
||||
case OSTREE_OBJECT_TYPE_PAYLOAD_LINK:
|
||||
/* TODO */
|
||||
break;
|
||||
case OSTREE_OBJECT_TYPE_FILE:
|
||||
|
||||
@@ -68,6 +68,7 @@ G_BEGIN_DECLS
|
||||
* @OSTREE_OBJECT_TYPE_COMMIT: Toplevel object, refers to tree and dirmeta for root
|
||||
* @OSTREE_OBJECT_TYPE_TOMBSTONE_COMMIT: Toplevel object, refers to a deleted commit
|
||||
* @OSTREE_OBJECT_TYPE_COMMIT_META: Detached metadata for a commit
|
||||
* @OSTREE_OBJECT_TYPE_PAYLOAD_LINK: Symlink to a .file given its checksum on the payload only.
|
||||
*
|
||||
* Enumeration for core object types; %OSTREE_OBJECT_TYPE_FILE is for
|
||||
* content, the other types are metadata.
|
||||
@@ -79,6 +80,7 @@ typedef enum {
|
||||
OSTREE_OBJECT_TYPE_COMMIT = 4, /* .commit */
|
||||
OSTREE_OBJECT_TYPE_TOMBSTONE_COMMIT = 5, /* .commit-tombstone */
|
||||
OSTREE_OBJECT_TYPE_COMMIT_META = 6, /* .commitmeta */
|
||||
OSTREE_OBJECT_TYPE_PAYLOAD_LINK = 7, /* .payload-link */
|
||||
} OstreeObjectType;
|
||||
|
||||
/**
|
||||
@@ -94,7 +96,7 @@ typedef enum {
|
||||
*
|
||||
* Last valid object type; use this to validate ranges.
|
||||
*/
|
||||
#define OSTREE_OBJECT_TYPE_LAST OSTREE_OBJECT_TYPE_COMMIT_META
|
||||
#define OSTREE_OBJECT_TYPE_LAST OSTREE_OBJECT_TYPE_PAYLOAD_LINK
|
||||
|
||||
/**
|
||||
* OSTREE_DIRMETA_GVARIANT_FORMAT:
|
||||
|
||||
@@ -121,6 +121,35 @@ ostree_deployment_set_origin (OstreeDeployment *self, GKeyFile *origin)
|
||||
self->origin = g_key_file_ref (origin);
|
||||
}
|
||||
|
||||
/**
|
||||
* ostree_deployment_origin_remove_transient_state:
|
||||
* @origin: An origin
|
||||
*
|
||||
* The intention of an origin file is primarily describe the "inputs" that
|
||||
* resulted in a deployment, and it's commonly used to derive the new state. For
|
||||
* example, a key value (in pure libostree mode) is the "refspec". However,
|
||||
* libostree (or other applications) may want to store "transient" state that
|
||||
* should not be carried across upgrades.
|
||||
*
|
||||
* This function just removes all members of the `libostree-transient` group.
|
||||
* The name of that group is available to all libostree users; best practice
|
||||
* would be to prefix values underneath there with a short identifier for your
|
||||
* software.
|
||||
*
|
||||
* Additionally, this function will remove the `origin/unlocked` and
|
||||
* `origin/override-commit` members; these should be considered transient state
|
||||
* that should have been under an explicit group.
|
||||
*
|
||||
* Since: 2018.3
|
||||
*/
|
||||
void
|
||||
ostree_deployment_origin_remove_transient_state (GKeyFile *origin)
|
||||
{
|
||||
g_key_file_remove_group (origin, OSTREE_ORIGIN_TRANSIENT_GROUP, NULL);
|
||||
g_key_file_remove_key (origin, "origin", "override-commit", NULL);
|
||||
g_key_file_remove_key (origin, "origin", "unlocked", NULL);
|
||||
}
|
||||
|
||||
void
|
||||
_ostree_deployment_set_bootcsum (OstreeDeployment *self,
|
||||
const char *bootcsum)
|
||||
@@ -293,3 +322,20 @@ ostree_deployment_get_unlocked (OstreeDeployment *self)
|
||||
{
|
||||
return self->unlocked;
|
||||
}
|
||||
|
||||
/**
|
||||
* ostree_deployment_is_pinned:
|
||||
* @self: Deployment
|
||||
*
|
||||
* See ostree_sysroot_deployment_set_pinned().
|
||||
*
|
||||
* Returns: `TRUE` if deployment will not be subject to GC
|
||||
* Since: 2018.3
|
||||
*/
|
||||
gboolean
|
||||
ostree_deployment_is_pinned (OstreeDeployment *self)
|
||||
{
|
||||
if (!self->origin)
|
||||
return FALSE;
|
||||
return g_key_file_get_boolean (self->origin, OSTREE_ORIGIN_TRANSIENT_GROUP, "pinned", NULL);
|
||||
}
|
||||
@@ -27,6 +27,17 @@ G_BEGIN_DECLS
|
||||
#define OSTREE_DEPLOYMENT(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), OSTREE_TYPE_DEPLOYMENT, OstreeDeployment))
|
||||
#define OSTREE_IS_DEPLOYMENT(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst), OSTREE_TYPE_DEPLOYMENT))
|
||||
|
||||
/**
|
||||
* OSTREE_ORIGIN_TRANSIENT_GROUP:
|
||||
*
|
||||
* The name of a `GKeyFile` group for data that should not
|
||||
* be carried across upgrades. For more information,
|
||||
* see ostree_deployment_origin_remove_transient_state().
|
||||
*
|
||||
* Since: 2018.3
|
||||
*/
|
||||
#define OSTREE_ORIGIN_TRANSIENT_GROUP "libostree-transient"
|
||||
|
||||
typedef struct _OstreeDeployment OstreeDeployment;
|
||||
|
||||
_OSTREE_PUBLIC
|
||||
@@ -62,6 +73,10 @@ OstreeBootconfigParser *ostree_deployment_get_bootconfig (OstreeDeployment *self
|
||||
_OSTREE_PUBLIC
|
||||
GKeyFile *ostree_deployment_get_origin (OstreeDeployment *self);
|
||||
|
||||
|
||||
_OSTREE_PUBLIC
|
||||
gboolean ostree_deployment_is_pinned (OstreeDeployment *self);
|
||||
|
||||
_OSTREE_PUBLIC
|
||||
void ostree_deployment_set_index (OstreeDeployment *self, int index);
|
||||
_OSTREE_PUBLIC
|
||||
@@ -71,6 +86,9 @@ void ostree_deployment_set_bootconfig (OstreeDeployment *self, OstreeBootconfigP
|
||||
_OSTREE_PUBLIC
|
||||
void ostree_deployment_set_origin (OstreeDeployment *self, GKeyFile *origin);
|
||||
|
||||
_OSTREE_PUBLIC
|
||||
void ostree_deployment_origin_remove_transient_state (GKeyFile *origin);
|
||||
|
||||
_OSTREE_PUBLIC
|
||||
OstreeDeployment *ostree_deployment_clone (OstreeDeployment *self);
|
||||
|
||||
|
||||
@@ -77,6 +77,7 @@ struct OstreeFetcher
|
||||
char *proxy;
|
||||
struct curl_slist *extra_headers;
|
||||
int tmpdir_dfd;
|
||||
char *custom_user_agent;
|
||||
|
||||
GMainContext *mainctx;
|
||||
CURLM *multi;
|
||||
@@ -180,6 +181,7 @@ _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);
|
||||
g_clear_pointer (&self->custom_user_agent, (GDestroyNotify)g_free);
|
||||
|
||||
G_OBJECT_CLASS (_ostree_fetcher_parent_class)->finalize (object);
|
||||
}
|
||||
@@ -676,6 +678,18 @@ _ostree_fetcher_set_extra_headers (OstreeFetcher *self,
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
_ostree_fetcher_set_extra_user_agent (OstreeFetcher *self,
|
||||
const char *extra_user_agent)
|
||||
{
|
||||
g_clear_pointer (&self->custom_user_agent, (GDestroyNotify)g_free);
|
||||
if (extra_user_agent)
|
||||
{
|
||||
self->custom_user_agent =
|
||||
g_strdup_printf ("%s %s", OSTREE_FETCHER_USERAGENT_STRING, extra_user_agent);
|
||||
}
|
||||
}
|
||||
|
||||
/* Re-bind all of the outstanding curl items to our new main context */
|
||||
static void
|
||||
adopt_steal_mainctx (OstreeFetcher *self,
|
||||
@@ -716,7 +730,8 @@ initiate_next_curl_request (FetcherRequest *req,
|
||||
curl_easy_setopt (req->easy, CURLOPT_URL, uri);
|
||||
}
|
||||
|
||||
curl_easy_setopt (req->easy, CURLOPT_USERAGENT, OSTREE_FETCHER_USERAGENT_STRING);
|
||||
curl_easy_setopt (req->easy, CURLOPT_USERAGENT,
|
||||
self->custom_user_agent ?: OSTREE_FETCHER_USERAGENT_STRING);
|
||||
if (self->extra_headers)
|
||||
curl_easy_setopt (req->easy, CURLOPT_HTTPHEADER, self->extra_headers);
|
||||
|
||||
|
||||
@@ -374,6 +374,24 @@ session_thread_set_tls_database_cb (ThreadClosure *thread_closure,
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
session_thread_set_extra_user_agent_cb (ThreadClosure *thread_closure,
|
||||
gpointer data)
|
||||
{
|
||||
const char *extra_user_agent = data;
|
||||
if (extra_user_agent != NULL)
|
||||
{
|
||||
g_autofree char *ua =
|
||||
g_strdup_printf ("%s %s", OSTREE_FETCHER_USERAGENT_STRING, extra_user_agent);
|
||||
g_object_set (thread_closure->session, SOUP_SESSION_USER_AGENT, ua, NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_object_set (thread_closure->session, SOUP_SESSION_USER_AGENT,
|
||||
OSTREE_FETCHER_USERAGENT_STRING, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
on_request_sent (GObject *object, GAsyncResult *result, gpointer user_data);
|
||||
|
||||
@@ -774,6 +792,16 @@ _ostree_fetcher_set_extra_headers (OstreeFetcher *self,
|
||||
(GDestroyNotify) g_variant_unref);
|
||||
}
|
||||
|
||||
void
|
||||
_ostree_fetcher_set_extra_user_agent (OstreeFetcher *self,
|
||||
const char *extra_user_agent)
|
||||
{
|
||||
session_thread_idle_add (self->thread_closure,
|
||||
session_thread_set_extra_user_agent_cb,
|
||||
g_strdup (extra_user_agent),
|
||||
(GDestroyNotify) g_free);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
finish_stream (OstreeFetcherPendingURI *pending,
|
||||
GCancellable *cancellable,
|
||||
|
||||
@@ -114,6 +114,9 @@ void _ostree_fetcher_set_tls_database (OstreeFetcher *self,
|
||||
void _ostree_fetcher_set_extra_headers (OstreeFetcher *self,
|
||||
GVariant *extra_headers);
|
||||
|
||||
void _ostree_fetcher_set_extra_user_agent (OstreeFetcher *self,
|
||||
const char *extra_user_agent);
|
||||
|
||||
guint64 _ostree_fetcher_bytes_transferred (OstreeFetcher *self);
|
||||
|
||||
void _ostree_fetcher_request_to_tmpfile (OstreeFetcher *self,
|
||||
|
||||
@@ -149,6 +149,7 @@ ostree_remote_unref (OstreeRemote *remote)
|
||||
if (g_atomic_int_dec_and_test (&remote->ref_count))
|
||||
{
|
||||
g_clear_pointer (&remote->name, g_free);
|
||||
g_clear_pointer (&remote->refspec_name, g_free);
|
||||
g_clear_pointer (&remote->group, g_free);
|
||||
g_clear_pointer (&remote->keyring, g_free);
|
||||
g_clear_object (&remote->file);
|
||||
|
||||
@@ -30,6 +30,8 @@
|
||||
#include <gio/gunixoutputstream.h>
|
||||
#include <sys/xattr.h>
|
||||
#include <glib/gprintf.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <linux/fs.h>
|
||||
|
||||
#include "otutil.h"
|
||||
#include "ostree.h"
|
||||
@@ -40,6 +42,12 @@
|
||||
#include "ostree-checksum-input-stream.h"
|
||||
#include "ostree-varint.h"
|
||||
|
||||
/* The standardized version of BTRFS_IOC_CLONE */
|
||||
#ifndef FICLONE
|
||||
#define FICLONE _IOW(0x94, 9, int)
|
||||
#endif
|
||||
|
||||
|
||||
/* 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.
|
||||
@@ -589,6 +597,192 @@ create_regular_tmpfile_linkable_with_content (OstreeRepo *self,
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
_check_support_reflink (OstreeRepo *self, gboolean *supported, GError **error)
|
||||
{
|
||||
/* We have not checked yet if the file system supports reflinks, do it here */
|
||||
if (g_atomic_int_get (&self->fs_support_reflink) == 0)
|
||||
{
|
||||
g_auto(GLnxTmpfile) src_tmpf = { 0, };
|
||||
g_auto(GLnxTmpfile) dest_tmpf = { 0, };
|
||||
|
||||
if (!glnx_open_tmpfile_linkable_at (commit_tmp_dfd (self), ".", O_RDWR|O_CLOEXEC,
|
||||
&src_tmpf, error))
|
||||
return FALSE;
|
||||
if (!glnx_open_tmpfile_linkable_at (commit_tmp_dfd (self), ".", O_WRONLY|O_CLOEXEC,
|
||||
&dest_tmpf, error))
|
||||
return FALSE;
|
||||
|
||||
if (ioctl (dest_tmpf.fd, FICLONE, src_tmpf.fd) == 0)
|
||||
g_atomic_int_set (&self->fs_support_reflink, 1);
|
||||
else if (errno == EOPNOTSUPP) /* Ignore other kind of errors as they might be temporary failures */
|
||||
g_atomic_int_set (&self->fs_support_reflink, -1);
|
||||
}
|
||||
*supported = g_atomic_int_get (&self->fs_support_reflink) >= 0;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
_create_payload_link (OstreeRepo *self,
|
||||
const char *checksum,
|
||||
const char *payload_checksum,
|
||||
GFileInfo *file_info,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
gboolean reflinks_supported = FALSE;
|
||||
if (!_check_support_reflink (self, &reflinks_supported, error))
|
||||
return FALSE;
|
||||
|
||||
if (!reflinks_supported)
|
||||
return TRUE;
|
||||
|
||||
if (g_file_info_get_file_type (file_info) != G_FILE_TYPE_REGULAR
|
||||
|| !G_IN_SET(self->mode, OSTREE_REPO_MODE_BARE, OSTREE_REPO_MODE_BARE_USER, OSTREE_REPO_MODE_BARE_USER_ONLY))
|
||||
return TRUE;
|
||||
|
||||
if (payload_checksum == NULL || g_file_info_get_size (file_info) < self->payload_link_threshold)
|
||||
return TRUE;
|
||||
|
||||
char target_buf[_OSTREE_LOOSE_PATH_MAX + _OSTREE_PAYLOAD_LINK_PREFIX_LEN];
|
||||
strcpy (target_buf, _OSTREE_PAYLOAD_LINK_PREFIX);
|
||||
_ostree_loose_path (target_buf + _OSTREE_PAYLOAD_LINK_PREFIX_LEN, checksum, OSTREE_OBJECT_TYPE_FILE, self->mode);
|
||||
|
||||
if (symlinkat (target_buf, commit_tmp_dfd (self), payload_checksum) < 0)
|
||||
{
|
||||
if (errno != EEXIST)
|
||||
return glnx_throw_errno_prefix (error, "symlinkat");
|
||||
}
|
||||
else
|
||||
{
|
||||
g_auto(OtCleanupUnlinkat) tmp_unlinker = { commit_tmp_dfd (self), g_strdup (payload_checksum) };
|
||||
if (!commit_path_final (self, payload_checksum, OSTREE_OBJECT_TYPE_PAYLOAD_LINK, &tmp_unlinker, cancellable, error))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
_import_payload_link (OstreeRepo *self,
|
||||
OstreeRepo *source,
|
||||
const char *checksum,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
gboolean reflinks_supported = FALSE;
|
||||
g_autofree char *payload_checksum = NULL;
|
||||
g_autoptr(GInputStream) is = NULL;
|
||||
glnx_unref_object OtChecksumInstream *checksum_payload = NULL;
|
||||
g_autoptr(GFileInfo) file_info = NULL;
|
||||
|
||||
if (!_check_support_reflink (self, &reflinks_supported, error))
|
||||
return FALSE;
|
||||
|
||||
if (!reflinks_supported)
|
||||
return TRUE;
|
||||
|
||||
if (!G_IN_SET(self->mode, OSTREE_REPO_MODE_BARE, OSTREE_REPO_MODE_BARE_USER, OSTREE_REPO_MODE_BARE_USER_ONLY))
|
||||
return TRUE;
|
||||
|
||||
if (!ostree_repo_load_file (source, checksum, &is, &file_info, NULL, cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
if (g_file_info_get_file_type (file_info) != G_FILE_TYPE_REGULAR
|
||||
|| g_file_info_get_size (file_info) < self->payload_link_threshold)
|
||||
return TRUE;
|
||||
|
||||
checksum_payload = ot_checksum_instream_new (is, G_CHECKSUM_SHA256);
|
||||
|
||||
guint64 remaining = g_file_info_get_size (file_info);
|
||||
while (remaining)
|
||||
{
|
||||
char buf[8192];
|
||||
gssize ret = g_input_stream_read ((GInputStream *) checksum_payload, buf,
|
||||
MIN (sizeof (buf), remaining), cancellable, error);
|
||||
if (ret < 0)
|
||||
return FALSE;
|
||||
remaining -= ret;
|
||||
}
|
||||
payload_checksum = ot_checksum_instream_get_string (checksum_payload);
|
||||
|
||||
return _create_payload_link (self, checksum, payload_checksum, file_info, cancellable, error);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
_try_clone_from_payload_link (OstreeRepo *self,
|
||||
const char *payload_checksum,
|
||||
GFileInfo *file_info,
|
||||
GLnxTmpfile *tmpf,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
gboolean reflinks_supported = FALSE;
|
||||
int dfd_searches[] = { -1, self->objects_dir_fd };
|
||||
if (self->commit_stagedir.initialized)
|
||||
dfd_searches[0] = self->commit_stagedir.fd;
|
||||
|
||||
if (!_check_support_reflink (self, &reflinks_supported, error))
|
||||
return FALSE;
|
||||
|
||||
if (!reflinks_supported)
|
||||
return TRUE;
|
||||
|
||||
for (guint i = 0; i < G_N_ELEMENTS (dfd_searches); i++)
|
||||
{
|
||||
glnx_autofd int fdf = -1;
|
||||
char loose_path_buf[_OSTREE_LOOSE_PATH_MAX];
|
||||
char loose_path_target_buf[_OSTREE_LOOSE_PATH_MAX];
|
||||
char target_buf[_OSTREE_LOOSE_PATH_MAX + _OSTREE_PAYLOAD_LINK_PREFIX_LEN];
|
||||
char target_checksum[OSTREE_SHA256_STRING_LEN+1];
|
||||
int dfd = dfd_searches[i];
|
||||
ssize_t size;
|
||||
if (dfd == -1)
|
||||
continue;
|
||||
|
||||
_ostree_loose_path (loose_path_buf, payload_checksum, OSTREE_OBJECT_TYPE_PAYLOAD_LINK, self->mode);
|
||||
|
||||
size = TEMP_FAILURE_RETRY (readlinkat (dfd, loose_path_buf, target_buf, sizeof (target_buf)));
|
||||
if (size < 0)
|
||||
{
|
||||
if (errno == ENOENT)
|
||||
continue;
|
||||
return glnx_throw_errno_prefix (error, "readlinkat");
|
||||
}
|
||||
|
||||
if (size < OSTREE_SHA256_STRING_LEN + _OSTREE_PAYLOAD_LINK_PREFIX_LEN)
|
||||
return glnx_throw (error, "invalid data size for %s", loose_path_buf);
|
||||
|
||||
sprintf (target_checksum, "%.2s%.62s", target_buf + _OSTREE_PAYLOAD_LINK_PREFIX_LEN, target_buf + _OSTREE_PAYLOAD_LINK_PREFIX_LEN + 3);
|
||||
|
||||
_ostree_loose_path (loose_path_target_buf, target_checksum, OSTREE_OBJECT_TYPE_FILE, self->mode);
|
||||
if (!ot_openat_ignore_enoent (dfd, loose_path_target_buf, &fdf, error))
|
||||
return FALSE;
|
||||
|
||||
if (fdf < 0)
|
||||
{
|
||||
/* If the link is referring to an object that doesn't exist anymore in the repository, just unlink it. */
|
||||
if (!glnx_unlinkat (dfd, loose_path_buf, 0, error))
|
||||
return FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* This undoes all of the previous writes; we want to generate reflinked data. */
|
||||
if (ftruncate (tmpf->fd, 0) < 0)
|
||||
return glnx_throw_errno_prefix (error, "ftruncate");
|
||||
|
||||
if (glnx_regfile_copy_bytes (fdf, tmpf->fd, -1) < 0)
|
||||
return glnx_throw_errno_prefix (error, "regfile copy");
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
if (self->parent_repo)
|
||||
return _try_clone_from_payload_link (self->parent_repo, payload_checksum, file_info, tmpf, cancellable, error);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* The main driver for writing a content (regfile or symlink) object.
|
||||
* There are a variety of tricky cases here; for example, bare-user
|
||||
* repos store symlinks as regular files. Computing checksums
|
||||
@@ -616,6 +810,8 @@ write_content_object (OstreeRepo *self,
|
||||
GInputStream *file_input; /* Unowned alias */
|
||||
g_autoptr(GInputStream) file_input_owned = NULL; /* We need a temporary for bare-user symlinks */
|
||||
glnx_unref_object OtChecksumInstream *checksum_input = NULL;
|
||||
glnx_unref_object OtChecksumInstream *checksum_payload_input = NULL;
|
||||
const GFileType object_file_type = g_file_info_get_file_type (file_info);
|
||||
if (out_csum)
|
||||
{
|
||||
/* Previously we checksummed the input verbatim; now
|
||||
@@ -624,6 +820,7 @@ write_content_object (OstreeRepo *self,
|
||||
* it's not that's not a serious problem because we're still computing a
|
||||
* checksum over the data we actually use.
|
||||
*/
|
||||
gboolean reflinks_supported = FALSE;
|
||||
g_autoptr(GBytes) header = _ostree_file_header_new (file_info, xattrs);
|
||||
size_t len;
|
||||
const guint8 *buf = g_bytes_get_data (header, &len);
|
||||
@@ -633,13 +830,26 @@ write_content_object (OstreeRepo *self,
|
||||
null_input = input = g_memory_input_stream_new_from_data ("", 0, NULL);
|
||||
checksum_input = ot_checksum_instream_new_with_start (input, G_CHECKSUM_SHA256,
|
||||
buf, len);
|
||||
file_input = (GInputStream*)checksum_input;
|
||||
|
||||
if (!_check_support_reflink (self, &reflinks_supported, error))
|
||||
return FALSE;
|
||||
|
||||
if (xattrs == NULL || !G_IN_SET(self->mode, OSTREE_REPO_MODE_BARE, OSTREE_REPO_MODE_BARE_USER, OSTREE_REPO_MODE_BARE_USER_ONLY) || object_file_type != G_FILE_TYPE_REGULAR ||
|
||||
!reflinks_supported)
|
||||
file_input = (GInputStream*)checksum_input;
|
||||
else
|
||||
{
|
||||
/* The payload checksum-input reads from the full object checksum-input; this
|
||||
* means it skips the header.
|
||||
*/
|
||||
checksum_payload_input = ot_checksum_instream_new ((GInputStream*)checksum_input, G_CHECKSUM_SHA256);
|
||||
file_input = (GInputStream*)checksum_payload_input;
|
||||
}
|
||||
}
|
||||
else
|
||||
file_input = input;
|
||||
|
||||
gboolean phys_object_is_symlink = FALSE;
|
||||
const GFileType object_file_type = g_file_info_get_file_type (file_info);
|
||||
switch (object_file_type)
|
||||
{
|
||||
case G_FILE_TYPE_REGULAR:
|
||||
@@ -765,6 +975,7 @@ write_content_object (OstreeRepo *self,
|
||||
}
|
||||
|
||||
const char *actual_checksum = NULL;
|
||||
g_autofree char *actual_payload_checksum = NULL;
|
||||
g_autofree char *actual_checksum_owned = NULL;
|
||||
if (!checksum_input)
|
||||
actual_checksum = expected_checksum;
|
||||
@@ -777,6 +988,9 @@ write_content_object (OstreeRepo *self,
|
||||
error))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (checksum_payload_input)
|
||||
actual_payload_checksum = ot_checksum_instream_get_string (checksum_payload_input);
|
||||
}
|
||||
|
||||
g_assert (actual_checksum != NULL); /* Pacify static analysis */
|
||||
@@ -794,6 +1008,10 @@ write_content_object (OstreeRepo *self,
|
||||
g_mutex_lock (&self->txn_lock);
|
||||
self->txn.stats.content_objects_total++;
|
||||
g_mutex_unlock (&self->txn_lock);
|
||||
|
||||
if (!_create_payload_link (self, actual_checksum, actual_payload_checksum, file_info, cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
if (out_csum)
|
||||
*out_csum = ostree_checksum_to_bytes (actual_checksum);
|
||||
/* Note early return */
|
||||
@@ -853,12 +1071,20 @@ write_content_object (OstreeRepo *self,
|
||||
repo_store_size_entry (self, actual_checksum, unpacked_size, stbuf.st_size);
|
||||
}
|
||||
|
||||
/* Check if a file with the same payload is present in the repository,
|
||||
and in case try to reflink it */
|
||||
if (actual_payload_checksum && !_try_clone_from_payload_link (self, actual_payload_checksum, file_info, &tmpf, cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
/* This path is for regular files */
|
||||
if (!commit_loose_regfile_object (self, actual_checksum, &tmpf,
|
||||
uid, gid, mode,
|
||||
xattrs,
|
||||
cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
if (!_create_payload_link (self, actual_checksum, actual_payload_checksum, file_info, cancellable, error))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Update statistics */
|
||||
@@ -1927,6 +2153,12 @@ ostree_repo_abort_transaction (OstreeRepo *self,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
/* Always ignore the cancellable to avoid the chance that, if it gets
|
||||
* canceled, the transaction may not be fully cleaned up.
|
||||
* See https://github.com/ostreedev/ostree/issues/1491 .
|
||||
*/
|
||||
cancellable = NULL;
|
||||
|
||||
/* Note early return */
|
||||
if (!self->in_transaction)
|
||||
return TRUE;
|
||||
@@ -3999,7 +4231,11 @@ import_one_object_direct (OstreeRepo *dest_repo,
|
||||
if (!copy_detached_metadata (dest_repo, src_repo, checksum, cancellable, error))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
else if (objtype == OSTREE_OBJECT_TYPE_FILE)
|
||||
{
|
||||
if (!_import_payload_link (dest_repo, src_repo, checksum, cancellable, error))
|
||||
return FALSE;
|
||||
}
|
||||
*out_was_supported = TRUE;
|
||||
return TRUE;
|
||||
}
|
||||
@@ -4092,7 +4328,14 @@ _ostree_repo_import_object (OstreeRepo *self,
|
||||
return FALSE;
|
||||
/* If we have it, we're done */
|
||||
if (has_object)
|
||||
return TRUE;
|
||||
{
|
||||
if (objtype == OSTREE_OBJECT_TYPE_FILE)
|
||||
{
|
||||
if (!_import_payload_link (self, source, checksum, cancellable, error))
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if (OSTREE_OBJECT_TYPE_IS_META (objtype))
|
||||
{
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include <glib-object.h>
|
||||
#include <libglnx.h>
|
||||
|
||||
#include "ostree-autocleanups.h"
|
||||
#include "ostree-remote-private.h"
|
||||
#include "ostree-repo.h"
|
||||
#include "ostree-repo-private.h"
|
||||
@@ -176,7 +177,7 @@ ostree_repo_finder_config_resolve_async (OstreeRepoFinder *find
|
||||
while (g_hash_table_iter_next (&iter, (gpointer *) &remote_name, (gpointer *) &supported_ref_to_checksum))
|
||||
{
|
||||
g_autoptr(GError) local_error = NULL;
|
||||
OstreeRemote *remote;
|
||||
g_autoptr(OstreeRemote) remote = NULL;
|
||||
|
||||
/* We don’t know what last-modified timestamp the remote has without
|
||||
* making expensive HTTP queries, so leave that information blank. We
|
||||
|
||||
@@ -52,7 +52,7 @@
|
||||
* enumerated, and all OSTree repositories below it will be searched, in lexical
|
||||
* order, for the requested #OstreeCollectionRefs. The names of the directories
|
||||
* below `.ostree/repos.d` are irrelevant, apart from their lexical ordering.
|
||||
* The directories `.ostree/repo`, `ostree/repo` and `var/lib/flatpak`
|
||||
* The directories `.ostree/repo`, `ostree/repo` and `var/lib/flatpak/repo`
|
||||
* will be searched after the others, if they exist.
|
||||
* Non-removable volumes are ignored.
|
||||
*
|
||||
@@ -295,8 +295,8 @@ scan_and_add_repo (int dfd,
|
||||
};
|
||||
g_array_append_val (inout_repos_refs, val);
|
||||
|
||||
g_debug ("%s: Adding repo ‘%s’ (%ssortable)",
|
||||
G_STRFUNC, path, sortable ? "" : "not ");
|
||||
g_debug ("%s: Adding repo ‘%s’ on mount ‘%s’ (%ssortable)",
|
||||
G_STRFUNC, path, mount_name, sortable ? "" : "not ");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -439,7 +439,7 @@ G_GNUC_END_IGNORE_DEPRECATIONS
|
||||
{
|
||||
".ostree/repo",
|
||||
"ostree/repo",
|
||||
"var/lib/flatpak",
|
||||
"var/lib/flatpak/repo",
|
||||
};
|
||||
|
||||
for (i = 0; i < G_N_ELEMENTS (well_known_repos); i++)
|
||||
|
||||
@@ -53,6 +53,9 @@ G_BEGIN_DECLS
|
||||
#define OSTREE_SUMMARY_COLLECTION_ID "ostree.summary.collection-id"
|
||||
#define OSTREE_SUMMARY_COLLECTION_MAP "ostree.summary.collection-map"
|
||||
|
||||
#define _OSTREE_PAYLOAD_LINK_PREFIX "../"
|
||||
#define _OSTREE_PAYLOAD_LINK_PREFIX_LEN (sizeof (_OSTREE_PAYLOAD_LINK_PREFIX) - 1)
|
||||
|
||||
/* Well-known keys for the additional metadata field in a commit in a ref entry
|
||||
* in a summary file. */
|
||||
#define OSTREE_COMMIT_TIMESTAMP "ostree.commit.timestamp"
|
||||
@@ -161,6 +164,8 @@ struct OstreeRepo {
|
||||
gchar *collection_id;
|
||||
gboolean add_remotes_config_dir; /* Add new remotes in remotes.d dir */
|
||||
gint lock_timeout_seconds;
|
||||
guint64 payload_link_threshold;
|
||||
gint fs_support_reflink; /* The underlying filesystem has support for ioctl (FICLONE..) */
|
||||
|
||||
OstreeRepo *parent_repo;
|
||||
};
|
||||
@@ -237,14 +242,6 @@ _ostree_repo_ensure_loose_objdir_at (int dfd,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
gboolean
|
||||
_ostree_repo_find_object (OstreeRepo *self,
|
||||
OstreeObjectType objtype,
|
||||
const char *checksum,
|
||||
GFile **out_stored_path,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
GFile *
|
||||
_ostree_repo_get_commit_metadata_loose_path (OstreeRepo *self,
|
||||
const char *checksum);
|
||||
|
||||
@@ -46,40 +46,79 @@ maybe_prune_loose_object (OtPruneData *data,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
gboolean reachable = FALSE;
|
||||
g_autoptr(GVariant) key = NULL;
|
||||
|
||||
key = ostree_object_name_serialize (checksum, objtype);
|
||||
|
||||
if (!g_hash_table_lookup_extended (data->reachable, key, NULL, NULL))
|
||||
if (g_hash_table_lookup_extended (data->reachable, key, NULL, NULL))
|
||||
reachable = TRUE;
|
||||
else
|
||||
{
|
||||
guint64 storage_size = 0;
|
||||
|
||||
g_debug ("Pruning unneeded object %s.%s", checksum,
|
||||
ostree_object_type_to_string (objtype));
|
||||
|
||||
if (!ostree_repo_query_object_storage_size (data->repo, objtype, checksum,
|
||||
&storage_size, cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
data->freed_bytes += storage_size;
|
||||
|
||||
if (!(flags & OSTREE_REPO_PRUNE_FLAGS_NO_PRUNE))
|
||||
{
|
||||
guint64 storage_size = 0;
|
||||
if (objtype == OSTREE_OBJECT_TYPE_PAYLOAD_LINK)
|
||||
{
|
||||
ssize_t size;
|
||||
char loose_path_buf[_OSTREE_LOOSE_PATH_MAX];
|
||||
char target_checksum[OSTREE_SHA256_STRING_LEN+1];
|
||||
char target_buf[_OSTREE_LOOSE_PATH_MAX + _OSTREE_PAYLOAD_LINK_PREFIX_LEN];
|
||||
|
||||
if (objtype == OSTREE_OBJECT_TYPE_COMMIT)
|
||||
_ostree_loose_path (loose_path_buf, checksum, OSTREE_OBJECT_TYPE_PAYLOAD_LINK, data->repo->mode);
|
||||
size = readlinkat (data->repo->objects_dir_fd, loose_path_buf, target_buf, sizeof (target_buf));
|
||||
if (size < 0)
|
||||
return glnx_throw_errno_prefix (error, "readlinkat");
|
||||
|
||||
if (size < OSTREE_SHA256_STRING_LEN + _OSTREE_PAYLOAD_LINK_PREFIX_LEN)
|
||||
return glnx_throw (error, "invalid data size for %s", loose_path_buf);
|
||||
|
||||
sprintf (target_checksum, "%.2s%.62s", target_buf + _OSTREE_PAYLOAD_LINK_PREFIX_LEN, target_buf + _OSTREE_PAYLOAD_LINK_PREFIX_LEN + 3);
|
||||
|
||||
g_autoptr(GVariant) target_key = ostree_object_name_serialize (target_checksum, OSTREE_OBJECT_TYPE_FILE);
|
||||
|
||||
if (g_hash_table_lookup_extended (data->reachable, target_key, NULL, NULL))
|
||||
{
|
||||
guint64 target_storage_size = 0;
|
||||
if (!ostree_repo_query_object_storage_size (data->repo, OSTREE_OBJECT_TYPE_FILE, target_checksum,
|
||||
&target_storage_size, cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
reachable = target_storage_size >= data->repo->payload_link_threshold;
|
||||
if (reachable)
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
else if (objtype == OSTREE_OBJECT_TYPE_COMMIT)
|
||||
{
|
||||
if (!ostree_repo_mark_commit_partial (data->repo, checksum, FALSE, error))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!ostree_repo_query_object_storage_size (data->repo, objtype, checksum,
|
||||
&storage_size, cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
if (!ostree_repo_delete_object (data->repo, objtype, checksum,
|
||||
cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
data->freed_bytes += storage_size;
|
||||
}
|
||||
|
||||
if (OSTREE_OBJECT_TYPE_IS_META (objtype))
|
||||
data->n_unreachable_meta++;
|
||||
else
|
||||
data->n_unreachable_content++;
|
||||
}
|
||||
else
|
||||
|
||||
exit:
|
||||
if (reachable)
|
||||
{
|
||||
g_debug ("Keeping needed object %s.%s", checksum,
|
||||
ostree_object_type_to_string (objtype));
|
||||
@@ -283,7 +322,7 @@ repo_prune_internal (OstreeRepo *self,
|
||||
* of traversing all commits, only refs will be used. Particularly
|
||||
* when combined with @depth, this is a convenient way to delete
|
||||
* history from the repository.
|
||||
*
|
||||
*
|
||||
* Use the %OSTREE_REPO_PRUNE_FLAGS_NO_PRUNE to just determine
|
||||
* statistics on objects that would be deleted, without actually
|
||||
* deleting them.
|
||||
|
||||
@@ -87,6 +87,7 @@ typedef struct {
|
||||
OstreeAsyncProgress *progress;
|
||||
|
||||
GVariant *extra_headers;
|
||||
char *append_user_agent;
|
||||
|
||||
gboolean dry_run;
|
||||
gboolean dry_run_emitted_progress;
|
||||
@@ -2922,11 +2923,13 @@ repo_remote_fetch_summary (OstreeRepo *self,
|
||||
const char *url_override = NULL;
|
||||
g_autoptr(GVariant) extra_headers = NULL;
|
||||
g_autoptr(GPtrArray) mirrorlist = NULL;
|
||||
const char *append_user_agent = NULL;
|
||||
|
||||
if (options)
|
||||
{
|
||||
(void) g_variant_lookup (options, "override-url", "&s", &url_override);
|
||||
(void) g_variant_lookup (options, "http-headers", "@a(ss)", &extra_headers);
|
||||
(void) g_variant_lookup (options, "append-user-agent", "&s", &append_user_agent);
|
||||
}
|
||||
|
||||
mainctx = g_main_context_new ();
|
||||
@@ -2939,6 +2942,9 @@ repo_remote_fetch_summary (OstreeRepo *self,
|
||||
if (extra_headers)
|
||||
_ostree_fetcher_set_extra_headers (fetcher, extra_headers);
|
||||
|
||||
if (append_user_agent)
|
||||
_ostree_fetcher_set_extra_user_agent (fetcher, append_user_agent);
|
||||
|
||||
{
|
||||
g_autofree char *url_string = NULL;
|
||||
if (metalink_url_string)
|
||||
@@ -3055,6 +3061,9 @@ reinitialize_fetcher (OtPullData *pull_data, const char *remote_name,
|
||||
if (pull_data->extra_headers)
|
||||
_ostree_fetcher_set_extra_headers (pull_data->fetcher, pull_data->extra_headers);
|
||||
|
||||
if (pull_data->append_user_agent)
|
||||
_ostree_fetcher_set_extra_user_agent (pull_data->fetcher, pull_data->append_user_agent);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@@ -3240,6 +3249,7 @@ initiate_request (OtPullData *pull_data,
|
||||
* * http-headers (a(ss)): Additional headers to add to all HTTP requests
|
||||
* * update-frequency (u): Frequency to call the async progress callback in milliseconds, if any; only values higher than 0 are valid
|
||||
* * localcache-repos (as): File paths for local repos to use as caches when doing remote fetches
|
||||
* * append-user-agent (s): Additional string to append to the user agent
|
||||
*/
|
||||
gboolean
|
||||
ostree_repo_pull_with_options (OstreeRepo *self,
|
||||
@@ -3311,6 +3321,7 @@ ostree_repo_pull_with_options (OstreeRepo *self,
|
||||
(void) g_variant_lookup (options, "update-frequency", "u", &update_frequency);
|
||||
(void) g_variant_lookup (options, "localcache-repos", "^a&s", &opt_localcache_repos);
|
||||
(void) g_variant_lookup (options, "timestamp-check", "b", &pull_data->timestamp_check);
|
||||
(void) g_variant_lookup (options, "append-user-agent", "s", &pull_data->append_user_agent);
|
||||
|
||||
if (pull_data->remote_refspec_name != NULL)
|
||||
pull_data->remote_name = g_strdup (pull_data->remote_refspec_name);
|
||||
@@ -3365,7 +3376,7 @@ ostree_repo_pull_with_options (OstreeRepo *self,
|
||||
(GDestroyNotify)g_free);
|
||||
pull_data->ref_original_commits = g_hash_table_new_full (ostree_collection_ref_hash, ostree_collection_ref_equal,
|
||||
(GDestroyNotify)NULL,
|
||||
(GDestroyNotify)g_variant_unref);
|
||||
(GDestroyNotify)g_free);
|
||||
pull_data->gpg_verified_commits = g_hash_table_new_full (g_str_hash, g_str_equal,
|
||||
(GDestroyNotify)g_free, NULL);
|
||||
pull_data->scanned_metadata = g_hash_table_new_full (ostree_hash_object_name, g_variant_equal,
|
||||
@@ -4323,6 +4334,7 @@ ostree_repo_pull_with_options (OstreeRepo *self,
|
||||
g_clear_pointer (&pull_data->localcache_repos, (GDestroyNotify)g_ptr_array_unref);
|
||||
g_clear_object (&pull_data->remote_repo_local);
|
||||
g_free (pull_data->remote_name);
|
||||
g_free (pull_data->append_user_agent);
|
||||
g_clear_pointer (&pull_data->meta_mirrorlist, (GDestroyNotify) g_ptr_array_unref);
|
||||
g_clear_pointer (&pull_data->content_mirrorlist, (GDestroyNotify) g_ptr_array_unref);
|
||||
g_clear_pointer (&pull_data->summary_data, (GDestroyNotify) g_bytes_unref);
|
||||
@@ -5502,6 +5514,7 @@ ostree_repo_pull_from_remotes_async (OstreeRepo *self,
|
||||
copy_option (&options_dict, &local_options_dict, "http-headers", G_VARIANT_TYPE ("a(ss)"));
|
||||
copy_option (&options_dict, &local_options_dict, "subdirs", G_VARIANT_TYPE ("as"));
|
||||
copy_option (&options_dict, &local_options_dict, "update-frequency", G_VARIANT_TYPE ("u"));
|
||||
copy_option (&options_dict, &local_options_dict, "append-user-agent", G_VARIANT_TYPE ("s"));
|
||||
|
||||
local_options = g_variant_dict_end (&local_options_dict);
|
||||
|
||||
@@ -5645,7 +5658,7 @@ ostree_repo_resolve_keyring_for_collection (OstreeRepo *self,
|
||||
{
|
||||
gsize i;
|
||||
g_auto(GStrv) remotes = NULL;
|
||||
OstreeRemote *keyring_remote = NULL;
|
||||
g_autoptr(OstreeRemote) keyring_remote = NULL;
|
||||
|
||||
g_return_val_if_fail (OSTREE_IS_REPO (self), NULL);
|
||||
g_return_val_if_fail (ostree_validate_collection_id (collection_id, NULL), NULL);
|
||||
@@ -5680,6 +5693,7 @@ ostree_repo_resolve_keyring_for_collection (OstreeRepo *self,
|
||||
{
|
||||
g_debug ("%s: Ignoring remote ‘%s’ as it has no keyring configured.",
|
||||
G_STRFUNC, remotes[i]);
|
||||
g_clear_object (&keyring_remote);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -5695,7 +5709,7 @@ ostree_repo_resolve_keyring_for_collection (OstreeRepo *self,
|
||||
}
|
||||
|
||||
if (keyring_remote != NULL)
|
||||
return ostree_remote_ref (keyring_remote);
|
||||
return g_steal_pointer (&keyring_remote);
|
||||
else
|
||||
{
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
|
||||
@@ -5724,6 +5738,7 @@ ostree_repo_resolve_keyring_for_collection (OstreeRepo *self,
|
||||
*
|
||||
* - override-url (s): Fetch summary from this URL if remote specifies no metalink in options
|
||||
* - http-headers (a(ss)): Additional headers to add to all HTTP requests
|
||||
* - append-user-agent (s): Additional string to append to the user agent
|
||||
*
|
||||
* Returns: %TRUE on success, %FALSE on failure
|
||||
*/
|
||||
|
||||
@@ -617,6 +617,8 @@ _ostree_repo_list_refs_internal (OstreeRepo *self,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
GLNX_AUTO_PREFIX_ERROR ("Listing refs", error);
|
||||
|
||||
g_autofree char *remote = NULL;
|
||||
g_autofree char *ref_prefix = NULL;
|
||||
|
||||
@@ -627,8 +629,24 @@ _ostree_repo_list_refs_internal (OstreeRepo *self,
|
||||
const char *prefix_path;
|
||||
const char *path;
|
||||
|
||||
if (!ostree_parse_refspec (refspec_prefix, &remote, &ref_prefix, error))
|
||||
return FALSE;
|
||||
/* special-case "<remote>:" and "<remote>:.", which ostree_parse_refspec won't like */
|
||||
if (g_str_has_suffix (refspec_prefix, ":") ||
|
||||
g_str_has_suffix (refspec_prefix, ":."))
|
||||
{
|
||||
const char *colon = strrchr (refspec_prefix, ':');
|
||||
g_autofree char *r = g_strndup (refspec_prefix, colon - refspec_prefix);
|
||||
if (ostree_validate_remote_name (r, NULL))
|
||||
{
|
||||
remote = g_steal_pointer (&r);
|
||||
ref_prefix = g_strdup (".");
|
||||
}
|
||||
}
|
||||
|
||||
if (!ref_prefix)
|
||||
{
|
||||
if (!ostree_parse_refspec (refspec_prefix, &remote, &ref_prefix, error))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!(flags & OSTREE_REPO_LIST_REFS_EXT_EXCLUDE_REMOTES) && remote)
|
||||
{
|
||||
@@ -1029,10 +1047,7 @@ _ostree_repo_write_ref (OstreeRepo *self,
|
||||
{
|
||||
if (!glnx_opendirat (self->repo_dir_fd, "refs/heads", TRUE,
|
||||
&dfd, error))
|
||||
{
|
||||
g_prefix_error (error, "Opening %s: ", "refs/heads");
|
||||
return FALSE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
else if (remote == NULL && ref->collection_id != NULL)
|
||||
{
|
||||
@@ -1041,10 +1056,7 @@ _ostree_repo_write_ref (OstreeRepo *self,
|
||||
/* refs/mirrors might not exist in older repositories, so create it. */
|
||||
if (!glnx_shutil_mkdir_p_at_open (self->repo_dir_fd, "refs/mirrors", 0777,
|
||||
&refs_mirrors_dfd, cancellable, error))
|
||||
{
|
||||
g_prefix_error (error, "Opening %s: ", "refs/mirrors");
|
||||
return FALSE;
|
||||
}
|
||||
return FALSE;
|
||||
|
||||
if (rev != NULL)
|
||||
{
|
||||
@@ -1063,10 +1075,7 @@ _ostree_repo_write_ref (OstreeRepo *self,
|
||||
|
||||
if (!glnx_opendirat (self->repo_dir_fd, "refs/remotes", TRUE,
|
||||
&refs_remotes_dfd, error))
|
||||
{
|
||||
g_prefix_error (error, "Opening %s: ", "refs/remotes");
|
||||
return FALSE;
|
||||
}
|
||||
return FALSE;
|
||||
|
||||
if (rev != NULL)
|
||||
{
|
||||
@@ -1207,6 +1216,8 @@ ostree_repo_list_collection_refs (OstreeRepo *self,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
GLNX_AUTO_PREFIX_ERROR ("Listing refs", error);
|
||||
|
||||
g_return_val_if_fail (OSTREE_IS_REPO (self), FALSE);
|
||||
g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE);
|
||||
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
|
||||
|
||||
@@ -241,14 +241,8 @@ finish_part (OstreeStaticDeltaBuilder *builder, GError **error)
|
||||
g_variant_builder_add_value (&xattr_builder, part_builder->xattrs->pdata[j]);
|
||||
|
||||
{
|
||||
g_autoptr(GBytes) payload_b;
|
||||
g_autoptr(GBytes) operations_b;
|
||||
|
||||
payload_b = g_string_free_to_bytes (part_builder->payload);
|
||||
part_builder->payload = NULL;
|
||||
|
||||
operations_b = g_string_free_to_bytes (part_builder->operations);
|
||||
part_builder->operations = NULL;
|
||||
g_autoptr(GBytes) payload_b = g_string_free_to_bytes (g_steal_pointer (&part_builder->payload));
|
||||
g_autoptr(GBytes) operations_b = g_string_free_to_bytes (g_steal_pointer (&part_builder->operations));
|
||||
|
||||
delta_part_content = g_variant_new ("(a(uuu)aa(ayay)@ay@ay)",
|
||||
&mode_builder, &xattr_builder,
|
||||
|
||||
@@ -38,10 +38,8 @@ _ostree_static_delta_parse_checksum_array (GVariant *array,
|
||||
guint *out_n_checksums,
|
||||
GError **error)
|
||||
{
|
||||
gsize n = g_variant_n_children (array);
|
||||
guint n_checksums;
|
||||
|
||||
n_checksums = n / OSTREE_STATIC_DELTA_OBJTYPE_CSUM_LEN;
|
||||
const gsize n = g_variant_n_children (array);
|
||||
const guint n_checksums = n / OSTREE_STATIC_DELTA_OBJTYPE_CSUM_LEN;
|
||||
|
||||
if (G_UNLIKELY(n > (G_MAXUINT32/OSTREE_STATIC_DELTA_OBJTYPE_CSUM_LEN) ||
|
||||
(n_checksums * OSTREE_STATIC_DELTA_OBJTYPE_CSUM_LEN) != n))
|
||||
@@ -166,8 +164,8 @@ _ostree_repo_static_delta_part_have_all_objects (OstreeRepo *repo,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
guint8 *checksums_data;
|
||||
guint n_checksums;
|
||||
guint8 *checksums_data = NULL;
|
||||
guint n_checksums = 0;
|
||||
gboolean have_object = TRUE;
|
||||
|
||||
if (!_ostree_static_delta_parse_checksum_array (checksum_array,
|
||||
|
||||
@@ -2827,6 +2827,15 @@ reload_core_config (OstreeRepo *self,
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
{ g_autofree char *payload_threshold = NULL;
|
||||
|
||||
if (!ot_keyfile_get_value_with_default (self->config, "core", "payload-link-threshold", "-1",
|
||||
&payload_threshold, error))
|
||||
return FALSE;
|
||||
|
||||
self->payload_link_threshold = g_ascii_strtoull (payload_threshold, NULL, 10);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@@ -3278,6 +3287,8 @@ list_loose_objects_at (OstreeRepo *self,
|
||||
objtype = OSTREE_OBJECT_TYPE_DIR_META;
|
||||
else if (strcmp (dot, ".commit") == 0)
|
||||
objtype = OSTREE_OBJECT_TYPE_COMMIT;
|
||||
else if (strcmp (dot, ".payload-link") == 0)
|
||||
objtype = OSTREE_OBJECT_TYPE_PAYLOAD_LINK;
|
||||
else
|
||||
continue;
|
||||
|
||||
@@ -4800,17 +4811,16 @@ ostree_repo_add_gpg_signature_summary (OstreeRepo *self,
|
||||
/* Note that fd is reused below */
|
||||
glnx_close_fd (&fd);
|
||||
|
||||
g_autoptr(GVariant) existing_signatures = NULL;
|
||||
g_autoptr(GVariant) metadata = NULL;
|
||||
if (!ot_openat_ignore_enoent (self->repo_dir_fd, "summary.sig", &fd, error))
|
||||
return FALSE;
|
||||
if (fd != -1)
|
||||
{
|
||||
if (!ot_variant_read_fd (fd, 0, G_VARIANT_TYPE (OSTREE_SUMMARY_SIG_GVARIANT_STRING),
|
||||
FALSE, &existing_signatures, error))
|
||||
FALSE, &metadata, error))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
g_autoptr(GVariant) new_metadata = NULL;
|
||||
for (guint i = 0; key_id[i]; i++)
|
||||
{
|
||||
g_autoptr(GBytes) signature_data = NULL;
|
||||
@@ -4819,10 +4829,11 @@ ostree_repo_add_gpg_signature_summary (OstreeRepo *self,
|
||||
cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
new_metadata = _ostree_detached_metadata_append_gpg_sig (existing_signatures, signature_data);
|
||||
g_autoptr(GVariant) old_metadata = g_steal_pointer (&metadata);
|
||||
metadata = _ostree_detached_metadata_append_gpg_sig (old_metadata, signature_data);
|
||||
}
|
||||
|
||||
g_autoptr(GVariant) normalized = g_variant_get_normal_form (new_metadata);
|
||||
g_autoptr(GVariant) normalized = g_variant_get_normal_form (metadata);
|
||||
|
||||
if (!_ostree_repo_file_replace_contents (self,
|
||||
self->repo_dir_fd,
|
||||
@@ -4914,7 +4925,7 @@ _ostree_repo_gpg_verify_data_internal (OstreeRepo *self,
|
||||
g_autofree char *gpgkeypath = NULL;
|
||||
/* Add the remote's keyring file if it exists. */
|
||||
|
||||
OstreeRemote *remote;
|
||||
g_autoptr(OstreeRemote) remote = NULL;
|
||||
|
||||
remote = _ostree_repo_get_remote_inherited (self, remote_name, error);
|
||||
if (remote == NULL)
|
||||
@@ -4936,8 +4947,6 @@ _ostree_repo_gpg_verify_data_internal (OstreeRepo *self,
|
||||
|
||||
if (gpgkeypath)
|
||||
_ostree_gpg_verifier_add_key_ascii_file (verifier, gpgkeypath);
|
||||
|
||||
ostree_remote_unref (remote);
|
||||
}
|
||||
|
||||
if (add_global_keyring_dir)
|
||||
|
||||
@@ -523,38 +523,33 @@ checkout_deployment_tree (OstreeSysroot *sysroot,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
gboolean ret = FALSE;
|
||||
OstreeRepoCheckoutAtOptions checkout_opts = { 0, };
|
||||
const char *csum = ostree_deployment_get_csum (deployment);
|
||||
g_autofree char *checkout_target_name = NULL;
|
||||
g_autofree char *osdeploy_path = NULL;
|
||||
glnx_autofd int osdeploy_dfd = -1;
|
||||
int ret_fd;
|
||||
|
||||
osdeploy_path = g_strconcat ("ostree/deploy/", ostree_deployment_get_osname (deployment), "/deploy", NULL);
|
||||
checkout_target_name = g_strdup_printf ("%s.%d", csum, ostree_deployment_get_deployserial (deployment));
|
||||
|
||||
GLNX_AUTO_PREFIX_ERROR ("Checking out deployment tree", error);
|
||||
/* Find the directory with deployments for this stateroot */
|
||||
g_autofree char *osdeploy_path =
|
||||
g_strconcat ("ostree/deploy/", ostree_deployment_get_osname (deployment), "/deploy", NULL);
|
||||
if (!glnx_shutil_mkdir_p_at (sysroot->sysroot_fd, osdeploy_path, 0775, cancellable, error))
|
||||
goto out;
|
||||
return FALSE;
|
||||
|
||||
glnx_autofd int osdeploy_dfd = -1;
|
||||
if (!glnx_opendirat (sysroot->sysroot_fd, osdeploy_path, TRUE, &osdeploy_dfd, error))
|
||||
goto out;
|
||||
return FALSE;
|
||||
|
||||
/* Clean up anything that was there before, from e.g. an interrupted checkout */
|
||||
const char *csum = ostree_deployment_get_csum (deployment);
|
||||
g_autofree char *checkout_target_name =
|
||||
g_strdup_printf ("%s.%d", csum, ostree_deployment_get_deployserial (deployment));
|
||||
if (!glnx_shutil_rm_rf_at (osdeploy_dfd, checkout_target_name, cancellable, error))
|
||||
goto out;
|
||||
return FALSE;
|
||||
|
||||
/* Generate hardlink farm, then opendir it */
|
||||
OstreeRepoCheckoutAtOptions checkout_opts = { 0, };
|
||||
if (!ostree_repo_checkout_at (repo, &checkout_opts, osdeploy_dfd,
|
||||
checkout_target_name, csum,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
return FALSE;
|
||||
|
||||
if (!glnx_opendirat (osdeploy_dfd, checkout_target_name, TRUE, &ret_fd, error))
|
||||
goto out;
|
||||
|
||||
ret = TRUE;
|
||||
*out_deployment_dfd = ret_fd;
|
||||
out:
|
||||
return ret;
|
||||
return glnx_opendirat (osdeploy_dfd, checkout_target_name, TRUE, out_deployment_dfd,
|
||||
error);
|
||||
}
|
||||
|
||||
static char *
|
||||
@@ -672,27 +667,21 @@ selinux_relabel_dir (OstreeSysroot *sysroot,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
gboolean ret = FALSE;
|
||||
g_autoptr(GPtrArray) path_parts = g_ptr_array_new ();
|
||||
g_autoptr(GFileInfo) root_info = NULL;
|
||||
|
||||
root_info = g_file_query_info (dir, OSTREE_GIO_FAST_QUERYINFO,
|
||||
G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
|
||||
cancellable, error);
|
||||
g_autoptr(GFileInfo) root_info =
|
||||
g_file_query_info (dir, OSTREE_GIO_FAST_QUERYINFO,
|
||||
G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
|
||||
cancellable, error);
|
||||
if (!root_info)
|
||||
goto out;
|
||||
return FALSE;
|
||||
|
||||
g_autoptr(GPtrArray) path_parts = g_ptr_array_new ();
|
||||
g_ptr_array_add (path_parts, (char*)prefix);
|
||||
if (!relabel_recursively (sysroot, sepolicy, dir, root_info, path_parts,
|
||||
cancellable, error))
|
||||
{
|
||||
g_prefix_error (error, "Relabeling /%s: ", prefix);
|
||||
goto out;
|
||||
}
|
||||
return glnx_prefix_error (error, "Relabeling /%s", prefix);
|
||||
|
||||
ret = TRUE;
|
||||
out:
|
||||
return ret;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* Handles SELinux labeling for /var; this is slated to be deleted. See
|
||||
@@ -767,16 +756,12 @@ merge_configuration (OstreeSysroot *sysroot,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
GLNX_AUTO_PREFIX_ERROR ("During /etc merge", error);
|
||||
g_autoptr(OstreeSePolicy) sepolicy = NULL;
|
||||
|
||||
if (previous_deployment)
|
||||
{
|
||||
g_autoptr(GFile) previous_path = NULL;
|
||||
OstreeBootconfigParser *previous_bootconfig;
|
||||
|
||||
previous_path = ostree_sysroot_get_deployment_directory (sysroot, previous_deployment);
|
||||
|
||||
previous_bootconfig = ostree_deployment_get_bootconfig (previous_deployment);
|
||||
OstreeBootconfigParser *previous_bootconfig = ostree_deployment_get_bootconfig (previous_deployment);
|
||||
if (previous_bootconfig)
|
||||
{
|
||||
const char *previous_options = ostree_bootconfig_parser_get (previous_bootconfig, "options");
|
||||
@@ -847,30 +832,39 @@ merge_configuration (OstreeSysroot *sysroot,
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* Write the origin file for a deployment. */
|
||||
/* Write the origin file for a deployment; this does not bump the mtime, under
|
||||
* the assumption the caller may be writing multiple.
|
||||
*/
|
||||
static gboolean
|
||||
write_origin_file_internal (OstreeSysroot *sysroot,
|
||||
OstreeSePolicy *sepolicy,
|
||||
OstreeDeployment *deployment,
|
||||
GKeyFile *new_origin,
|
||||
GLnxFileReplaceFlags flags,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
GLNX_AUTO_PREFIX_ERROR ("Writing out origin file", error);
|
||||
GKeyFile *origin =
|
||||
new_origin ? new_origin : ostree_deployment_get_origin (deployment);
|
||||
|
||||
if (origin)
|
||||
{
|
||||
g_autofree char *origin_path = NULL;
|
||||
g_autofree char *contents = NULL;
|
||||
g_auto(OstreeSepolicyFsCreatecon) con = { 0, };
|
||||
if (!_ostree_sepolicy_preparefscreatecon (&con, sepolicy,
|
||||
"/etc/ostree/remotes.d/dummy.conf",
|
||||
0644, error))
|
||||
return FALSE;
|
||||
|
||||
g_autofree char *origin_path =
|
||||
g_strdup_printf ("ostree/deploy/%s/deploy/%s.%d.origin",
|
||||
ostree_deployment_get_osname (deployment),
|
||||
ostree_deployment_get_csum (deployment),
|
||||
ostree_deployment_get_deployserial (deployment));
|
||||
|
||||
|
||||
gsize len;
|
||||
|
||||
origin_path = g_strdup_printf ("ostree/deploy/%s/deploy/%s.%d.origin",
|
||||
ostree_deployment_get_osname (deployment),
|
||||
ostree_deployment_get_csum (deployment),
|
||||
ostree_deployment_get_deployserial (deployment));
|
||||
|
||||
contents = g_key_file_to_data (origin, &len, error);
|
||||
g_autofree char *contents = g_key_file_to_data (origin, &len, error);
|
||||
if (!contents)
|
||||
return FALSE;
|
||||
|
||||
@@ -903,9 +897,20 @@ ostree_sysroot_write_origin_file (OstreeSysroot *sysroot,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
return write_origin_file_internal (sysroot, deployment, new_origin,
|
||||
GLNX_FILE_REPLACE_DATASYNC_NEW,
|
||||
cancellable, error);
|
||||
g_autoptr(GFile) rootfs = g_file_new_for_path ("/");
|
||||
g_autoptr(OstreeSePolicy) sepolicy = ostree_sepolicy_new (rootfs, cancellable, error);
|
||||
if (!sepolicy)
|
||||
return FALSE;
|
||||
|
||||
if (!write_origin_file_internal (sysroot, sepolicy, deployment, new_origin,
|
||||
GLNX_FILE_REPLACE_DATASYNC_NEW,
|
||||
cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
if (!_ostree_sysroot_bump_mtime (sysroot, error))
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
@@ -1497,6 +1502,7 @@ create_new_bootlinks (OstreeSysroot *self,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
GLNX_AUTO_PREFIX_ERROR ("Creating new current bootlinks", error);
|
||||
glnx_autofd int ostree_dfd = -1;
|
||||
if (!glnx_opendirat (self->sysroot_fd, "ostree", TRUE, &ostree_dfd, error))
|
||||
return FALSE;
|
||||
@@ -1559,6 +1565,7 @@ swap_bootlinks (OstreeSysroot *self,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
GLNX_AUTO_PREFIX_ERROR ("Swapping new version bootlinks", error);
|
||||
glnx_autofd int ostree_dfd = -1;
|
||||
if (!glnx_opendirat (self->sysroot_fd, "ostree", TRUE, &ostree_dfd, error))
|
||||
return FALSE;
|
||||
@@ -1628,6 +1635,7 @@ install_deployment_kernel (OstreeSysroot *sysroot,
|
||||
GError **error)
|
||||
|
||||
{
|
||||
GLNX_AUTO_PREFIX_ERROR ("Installing kernel", error);
|
||||
OstreeBootconfigParser *bootconfig = ostree_deployment_get_bootconfig (deployment);
|
||||
g_autofree char *deployment_dirpath = ostree_sysroot_get_deployment_dirpath (sysroot, deployment);
|
||||
glnx_autofd int deployment_dfd = -1;
|
||||
@@ -1936,23 +1944,18 @@ deployment_bootconfigs_equal (OstreeDeployment *a,
|
||||
return FALSE;
|
||||
|
||||
{
|
||||
OstreeBootconfigParser *a_bootconfig = ostree_deployment_get_bootconfig (a);
|
||||
OstreeBootconfigParser *b_bootconfig = ostree_deployment_get_bootconfig (b);
|
||||
const char *a_boot_options = ostree_bootconfig_parser_get (a_bootconfig, "options");
|
||||
const char *b_boot_options = ostree_bootconfig_parser_get (b_bootconfig, "options");
|
||||
g_autoptr(OstreeKernelArgs) a_kargs = NULL;
|
||||
g_autoptr(OstreeKernelArgs) b_kargs = NULL;
|
||||
g_autofree char *a_boot_options_without_ostree = NULL;
|
||||
g_autofree char *b_boot_options_without_ostree = NULL;
|
||||
|
||||
/* We checksum the kernel arguments *except* ostree= */
|
||||
a_kargs = _ostree_kernel_args_from_string (a_boot_options);
|
||||
OstreeBootconfigParser *a_bootconfig = ostree_deployment_get_bootconfig (a);
|
||||
const char *a_boot_options = ostree_bootconfig_parser_get (a_bootconfig, "options");
|
||||
g_autoptr(OstreeKernelArgs) a_kargs = _ostree_kernel_args_from_string (a_boot_options);
|
||||
_ostree_kernel_args_replace (a_kargs, "ostree");
|
||||
a_boot_options_without_ostree = _ostree_kernel_args_to_string (a_kargs);
|
||||
g_autofree char *a_boot_options_without_ostree = _ostree_kernel_args_to_string (a_kargs);
|
||||
|
||||
b_kargs = _ostree_kernel_args_from_string (b_boot_options);
|
||||
OstreeBootconfigParser *b_bootconfig = ostree_deployment_get_bootconfig (b);
|
||||
const char *b_boot_options = ostree_bootconfig_parser_get (b_bootconfig, "options");
|
||||
g_autoptr(OstreeKernelArgs) b_kargs = _ostree_kernel_args_from_string (b_boot_options);
|
||||
_ostree_kernel_args_replace (b_kargs, "ostree");
|
||||
b_boot_options_without_ostree = _ostree_kernel_args_to_string (b_kargs);
|
||||
g_autofree char *b_boot_options_without_ostree = _ostree_kernel_args_to_string (b_kargs);
|
||||
|
||||
if (strcmp (a_boot_options_without_ostree, b_boot_options_without_ostree) != 0)
|
||||
return FALSE;
|
||||
@@ -2083,12 +2086,7 @@ ostree_sysroot_write_deployments_with_options (OstreeSysroot *self,
|
||||
GError **error)
|
||||
{
|
||||
gboolean ret = FALSE;
|
||||
guint i;
|
||||
gboolean requires_new_bootversion = FALSE;
|
||||
gboolean found_booted_deployment = FALSE;
|
||||
gboolean bootloader_is_atomic = FALSE;
|
||||
gboolean boot_was_ro_mount = FALSE;
|
||||
SyncStats syncstats = { 0, };
|
||||
g_autoptr(OstreeBootloader) bootloader = NULL;
|
||||
|
||||
g_assert (self->loaded);
|
||||
@@ -2102,11 +2100,12 @@ ostree_sysroot_write_deployments_with_options (OstreeSysroot *self,
|
||||
* matching bootloader configuration, then we can just swap the
|
||||
* subbootversion bootlinks.
|
||||
*/
|
||||
gboolean requires_new_bootversion = FALSE;
|
||||
if (new_deployments->len != self->deployments->len)
|
||||
requires_new_bootversion = TRUE;
|
||||
else
|
||||
{
|
||||
for (i = 0; i < new_deployments->len; i++)
|
||||
for (guint i = 0; i < new_deployments->len; i++)
|
||||
{
|
||||
if (!deployment_bootconfigs_equal (new_deployments->pdata[i],
|
||||
self->deployments->pdata[i]))
|
||||
@@ -2117,7 +2116,8 @@ ostree_sysroot_write_deployments_with_options (OstreeSysroot *self,
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < new_deployments->len; i++)
|
||||
gboolean found_booted_deployment = FALSE;
|
||||
for (guint i = 0; i < new_deployments->len; i++)
|
||||
{
|
||||
OstreeDeployment *deployment = new_deployments->pdata[i];
|
||||
g_autoptr(GFile) deployment_root = NULL;
|
||||
@@ -2143,15 +2143,14 @@ ostree_sysroot_write_deployments_with_options (OstreeSysroot *self,
|
||||
goto out;
|
||||
}
|
||||
|
||||
gboolean bootloader_is_atomic = FALSE;
|
||||
SyncStats syncstats = { 0, };
|
||||
if (!requires_new_bootversion)
|
||||
{
|
||||
if (!create_new_bootlinks (self, self->bootversion,
|
||||
new_deployments,
|
||||
cancellable, error))
|
||||
{
|
||||
g_prefix_error (error, "Creating new current bootlinks: ");
|
||||
goto out;
|
||||
}
|
||||
goto out;
|
||||
|
||||
if (!full_system_sync (self, &syncstats, cancellable, error))
|
||||
{
|
||||
@@ -2162,10 +2161,7 @@ ostree_sysroot_write_deployments_with_options (OstreeSysroot *self,
|
||||
if (!swap_bootlinks (self, self->bootversion,
|
||||
new_deployments,
|
||||
cancellable, error))
|
||||
{
|
||||
g_prefix_error (error, "Swapping current bootlinks: ");
|
||||
goto out;
|
||||
}
|
||||
goto out;
|
||||
|
||||
bootloader_is_atomic = TRUE;
|
||||
}
|
||||
@@ -2208,13 +2204,10 @@ ostree_sysroot_write_deployments_with_options (OstreeSysroot *self,
|
||||
|
||||
/* Only show the osname in bootloader titles if there are multiple
|
||||
* osname's among the new deployments. Check for that here. */
|
||||
for (i = 1; i < new_deployments->len; i++)
|
||||
for (guint i = 1; i < new_deployments->len; i++)
|
||||
{
|
||||
const gchar *osname_0, *osname_i;
|
||||
|
||||
osname_0 = ostree_deployment_get_osname (new_deployments->pdata[0]);
|
||||
osname_i = ostree_deployment_get_osname (new_deployments->pdata[i]);
|
||||
|
||||
const char *osname_0 = ostree_deployment_get_osname (new_deployments->pdata[0]);
|
||||
const char *osname_i = ostree_deployment_get_osname (new_deployments->pdata[i]);
|
||||
if (!g_str_equal (osname_0, osname_i))
|
||||
{
|
||||
show_osname = TRUE;
|
||||
@@ -2222,32 +2215,23 @@ ostree_sysroot_write_deployments_with_options (OstreeSysroot *self,
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < new_deployments->len; i++)
|
||||
for (guint i = 0; i < new_deployments->len; i++)
|
||||
{
|
||||
OstreeDeployment *deployment = new_deployments->pdata[i];
|
||||
if (!install_deployment_kernel (self, repo, new_bootversion,
|
||||
deployment, new_deployments->len,
|
||||
show_osname, cancellable, error))
|
||||
{
|
||||
g_prefix_error (error, "Installing kernel: ");
|
||||
goto out;
|
||||
}
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Create and swap bootlinks for *new* version */
|
||||
if (!create_new_bootlinks (self, new_bootversion,
|
||||
new_deployments,
|
||||
cancellable, error))
|
||||
{
|
||||
g_prefix_error (error, "Creating new version bootlinks: ");
|
||||
goto out;
|
||||
}
|
||||
goto out;
|
||||
if (!swap_bootlinks (self, new_bootversion, new_deployments,
|
||||
cancellable, error))
|
||||
{
|
||||
g_prefix_error (error, "Swapping new version bootlinks: ");
|
||||
goto out;
|
||||
}
|
||||
goto out;
|
||||
|
||||
g_debug ("Using bootloader: %s", bootloader ?
|
||||
g_type_name (G_TYPE_FROM_INSTANCE (bootloader)) : "(none)");
|
||||
@@ -2438,10 +2422,7 @@ ostree_sysroot_deploy_tree (OstreeSysroot *self,
|
||||
glnx_autofd int deployment_dfd = -1;
|
||||
if (!checkout_deployment_tree (self, repo, new_deployment, &deployment_dfd,
|
||||
cancellable, error))
|
||||
{
|
||||
g_prefix_error (error, "Checking out tree: ");
|
||||
return FALSE;
|
||||
}
|
||||
return FALSE;
|
||||
|
||||
g_autoptr(OstreeKernelLayout) kernel_layout = NULL;
|
||||
if (!get_kernel_from_tree (deployment_dfd, &kernel_layout,
|
||||
@@ -2461,10 +2442,7 @@ ostree_sysroot_deploy_tree (OstreeSysroot *self,
|
||||
deployment_dfd,
|
||||
&sepolicy,
|
||||
cancellable, error))
|
||||
{
|
||||
g_prefix_error (error, "During /etc merge: ");
|
||||
return FALSE;
|
||||
}
|
||||
return FALSE;
|
||||
|
||||
if (!selinux_relabel_var_if_needed (self, sepolicy, os_deploy_dfd,
|
||||
cancellable, error))
|
||||
@@ -2477,24 +2455,13 @@ ostree_sysroot_deploy_tree (OstreeSysroot *self,
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
{ g_auto(OstreeSepolicyFsCreatecon) con = { 0, };
|
||||
|
||||
if (!_ostree_sepolicy_preparefscreatecon (&con, sepolicy,
|
||||
"/etc/ostree/remotes.d/dummy.conf",
|
||||
0644, error))
|
||||
return FALSE;
|
||||
|
||||
/* Don't fsync here, as we assume that's all done in
|
||||
* ostree_sysroot_write_deployments().
|
||||
*/
|
||||
if (!write_origin_file_internal (self, new_deployment, NULL,
|
||||
GLNX_FILE_REPLACE_NODATASYNC,
|
||||
cancellable, error))
|
||||
{
|
||||
g_prefix_error (error, "Writing out origin file: ");
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
/* Don't fsync here, as we assume that's all done in
|
||||
* ostree_sysroot_write_deployments().
|
||||
*/
|
||||
if (!write_origin_file_internal (self, sepolicy, new_deployment, NULL,
|
||||
GLNX_FILE_REPLACE_NODATASYNC,
|
||||
cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
/* After this, install_deployment_kernel() will set the other boot
|
||||
* options and write it out to disk.
|
||||
|
||||
@@ -50,6 +50,11 @@ struct OstreeSysroot {
|
||||
GLnxLockFile lock;
|
||||
|
||||
gboolean loaded;
|
||||
gboolean ostree_booted;
|
||||
gboolean root_is_sysroot; /* TRUE if sysroot_fd is pointed to rootfs "/" */
|
||||
/* The device/inode for /, used to detect booted deployment */
|
||||
dev_t root_device;
|
||||
ino_t root_inode;
|
||||
|
||||
gboolean is_physical; /* TRUE if we're pointed at physical storage root and not a deployment */
|
||||
GPtrArray *deployments;
|
||||
|
||||
+114
-114
@@ -37,13 +37,6 @@
|
||||
#include "ostree-bootloader-syslinux.h"
|
||||
#include "ostree-bootloader-grub2.h"
|
||||
|
||||
static gboolean
|
||||
find_booted_deployment (OstreeSysroot *self,
|
||||
GPtrArray *deployments,
|
||||
OstreeDeployment **out_deployment,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
/**
|
||||
* SECTION:ostree-sysroot
|
||||
* @title: Root partition mount point
|
||||
@@ -642,6 +635,24 @@ parse_deployment (OstreeSysroot *self,
|
||||
&deployment_dfd, error))
|
||||
return FALSE;
|
||||
|
||||
/* See if this is the booted deployment */
|
||||
const gboolean looking_for_booted_deployment =
|
||||
(self->ostree_booted && self->root_is_sysroot &&
|
||||
!self->booted_deployment);
|
||||
gboolean is_booted_deployment = FALSE;
|
||||
if (looking_for_booted_deployment)
|
||||
{
|
||||
struct stat stbuf;
|
||||
if (!glnx_fstat (deployment_dfd, &stbuf, error))
|
||||
return FALSE;
|
||||
/* A bit ugly, we're assigning to a sysroot-owned variable from deep in
|
||||
* this parsing code. But eh, if something fails the sysroot state can't
|
||||
* be relied on anyways.
|
||||
*/
|
||||
is_booted_deployment = (stbuf.st_dev == self->root_device &&
|
||||
stbuf.st_ino == self->root_inode);
|
||||
}
|
||||
|
||||
g_autoptr(GKeyFile) origin = NULL;
|
||||
if (!parse_origin (self, deployment_dfd, deploy_basename, &origin,
|
||||
cancellable, error))
|
||||
@@ -672,35 +683,32 @@ parse_deployment (OstreeSysroot *self,
|
||||
|
||||
g_debug ("Deployment %s.%d unlocked=%d", treecsum, deployserial, ret_deployment->unlocked);
|
||||
|
||||
if (is_booted_deployment)
|
||||
self->booted_deployment = g_object_ref (ret_deployment);
|
||||
if (out_deployment)
|
||||
*out_deployment = g_steal_pointer (&ret_deployment);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* Given a bootloader config, return the value part of the ostree= kernel
|
||||
* argument.
|
||||
*/
|
||||
static char *
|
||||
get_ostree_kernel_arg_from_config (OstreeBootconfigParser *config)
|
||||
{
|
||||
const char *options;
|
||||
char *ret = NULL;
|
||||
char **opts, **iter;
|
||||
|
||||
options = ostree_bootconfig_parser_get (config, "options");
|
||||
const char *options = ostree_bootconfig_parser_get (config, "options");
|
||||
if (!options)
|
||||
return NULL;
|
||||
|
||||
opts = g_strsplit (options, " ", -1);
|
||||
for (iter = opts; *iter; iter++)
|
||||
g_auto(GStrv) opts = g_strsplit (options, " ", -1);
|
||||
for (char **iter = opts; *iter; iter++)
|
||||
{
|
||||
const char *opt = *iter;
|
||||
if (g_str_has_prefix (opt, "ostree="))
|
||||
{
|
||||
ret = g_strdup (opt + strlen ("ostree="));
|
||||
break;
|
||||
}
|
||||
return g_strdup (opt + strlen ("ostree="));
|
||||
}
|
||||
g_strfreev (opts);
|
||||
|
||||
return ret;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
@@ -797,6 +805,32 @@ ostree_sysroot_load_if_changed (OstreeSysroot *self,
|
||||
if (!ensure_repo (self, error))
|
||||
return FALSE;
|
||||
|
||||
/* Gather some global state; first if we have the global ostree-booted flag;
|
||||
* we'll use it to sanity check that we found a booted deployment for example.
|
||||
* Second, we also find out whether sysroot == /.
|
||||
*/
|
||||
if (!self->loaded)
|
||||
{
|
||||
if (!glnx_fstatat_allow_noent (AT_FDCWD, "/run/ostree-booted", NULL, 0, error))
|
||||
return FALSE;
|
||||
self->ostree_booted = (errno == 0);
|
||||
|
||||
{ struct stat root_stbuf;
|
||||
if (!glnx_fstatat (AT_FDCWD, "/", &root_stbuf, 0, error))
|
||||
return FALSE;
|
||||
self->root_device = root_stbuf.st_dev;
|
||||
self->root_inode = root_stbuf.st_ino;
|
||||
}
|
||||
|
||||
struct stat self_stbuf;
|
||||
if (!glnx_fstat (self->sysroot_fd, &self_stbuf, error))
|
||||
return FALSE;
|
||||
|
||||
self->root_is_sysroot =
|
||||
(self->root_device == self_stbuf.st_dev &&
|
||||
self->root_inode == self_stbuf.st_ino);
|
||||
}
|
||||
|
||||
int bootversion = 0;
|
||||
if (!read_current_bootversion (self, &bootversion, cancellable, error))
|
||||
return FALSE;
|
||||
@@ -837,11 +871,19 @@ ostree_sysroot_load_if_changed (OstreeSysroot *self,
|
||||
{
|
||||
OstreeBootconfigParser *config = boot_loader_configs->pdata[i];
|
||||
|
||||
/* Note this also sets self->booted_deployment */
|
||||
if (!list_deployments_process_one_boot_entry (self, config, deployments,
|
||||
cancellable, error))
|
||||
return FALSE;
|
||||
{
|
||||
g_clear_object (&self->booted_deployment);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
if (self->ostree_booted && self->root_is_sysroot
|
||||
&& !self->booted_deployment)
|
||||
return glnx_throw (error, "Unexpected state: /run/ostree-booted found and in / sysroot but not in a booted deployment");
|
||||
|
||||
g_ptr_array_sort (deployments, compare_deployments_by_boot_loader_version_reversed);
|
||||
for (guint i = 0; i < deployments->len; i++)
|
||||
{
|
||||
@@ -849,10 +891,6 @@ ostree_sysroot_load_if_changed (OstreeSysroot *self,
|
||||
ostree_deployment_set_index (deployment, i);
|
||||
}
|
||||
|
||||
if (!find_booted_deployment (self, deployments, &self->booted_deployment,
|
||||
cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
/* Determine whether we're "physical" or not, the first time we initialize */
|
||||
if (!self->loaded)
|
||||
{
|
||||
@@ -920,13 +958,10 @@ ostree_sysroot_get_booted_deployment (OstreeSysroot *self)
|
||||
GPtrArray *
|
||||
ostree_sysroot_get_deployments (OstreeSysroot *self)
|
||||
{
|
||||
GPtrArray *copy;
|
||||
guint i;
|
||||
|
||||
g_return_val_if_fail (self->loaded, NULL);
|
||||
|
||||
copy = g_ptr_array_new_with_free_func ((GDestroyNotify)g_object_unref);
|
||||
for (i = 0; i < self->deployments->len; i++)
|
||||
GPtrArray *copy = g_ptr_array_new_with_free_func ((GDestroyNotify)g_object_unref);
|
||||
for (guint i = 0; i < self->deployments->len; i++)
|
||||
g_ptr_array_add (copy, g_object_ref (self->deployments->pdata[i]));
|
||||
return copy;
|
||||
}
|
||||
@@ -1071,10 +1106,9 @@ char *
|
||||
_ostree_sysroot_join_lines (GPtrArray *lines)
|
||||
{
|
||||
GString *buf = g_string_new ("");
|
||||
guint i;
|
||||
gboolean prev_was_empty = FALSE;
|
||||
|
||||
for (i = 0; i < lines->len; i++)
|
||||
for (guint i = 0; i < lines->len; i++)
|
||||
{
|
||||
const char *line = lines->pdata[i];
|
||||
/* Special bit to remove extraneous empty lines */
|
||||
@@ -1091,84 +1125,6 @@ _ostree_sysroot_join_lines (GPtrArray *lines)
|
||||
return g_string_free (buf, FALSE);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
parse_kernel_commandline (OstreeKernelArgs **out_args,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
g_autoptr(GFile) proc_cmdline = g_file_new_for_path ("/proc/cmdline");
|
||||
g_autofree char *contents = NULL;
|
||||
gsize len;
|
||||
|
||||
if (!g_file_load_contents (proc_cmdline, cancellable, &contents, &len, NULL,
|
||||
error))
|
||||
return FALSE;
|
||||
|
||||
g_strchomp (contents);
|
||||
*out_args = _ostree_kernel_args_from_string (contents);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
find_booted_deployment (OstreeSysroot *self,
|
||||
GPtrArray *deployments,
|
||||
OstreeDeployment **out_deployment,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
struct stat root_stbuf;
|
||||
struct stat self_stbuf;
|
||||
g_autoptr(OstreeDeployment) ret_deployment = NULL;
|
||||
|
||||
if (stat ("/", &root_stbuf) != 0)
|
||||
return glnx_throw_errno_prefix (error, "stat /");
|
||||
|
||||
if (!ensure_sysroot_fd (self, error))
|
||||
return FALSE;
|
||||
|
||||
if (fstat (self->sysroot_fd, &self_stbuf) != 0)
|
||||
return glnx_throw_errno_prefix (error, "fstat");
|
||||
|
||||
if (root_stbuf.st_dev == self_stbuf.st_dev &&
|
||||
root_stbuf.st_ino == self_stbuf.st_ino)
|
||||
{
|
||||
g_autoptr(OstreeKernelArgs) kernel_args = NULL;
|
||||
if (!parse_kernel_commandline (&kernel_args, cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
const char *bootlink_arg = _ostree_kernel_args_get_last_value (kernel_args, "ostree");
|
||||
if (bootlink_arg)
|
||||
{
|
||||
for (guint i = 0; i < deployments->len; i++)
|
||||
{
|
||||
OstreeDeployment *deployment = deployments->pdata[i];
|
||||
g_autofree char *deployment_path = ostree_sysroot_get_deployment_dirpath (self, deployment);
|
||||
struct stat stbuf;
|
||||
|
||||
if (fstatat (self->sysroot_fd, deployment_path, &stbuf, 0) != 0)
|
||||
return glnx_throw_errno_prefix (error, "fstatat");
|
||||
|
||||
if (stbuf.st_dev == root_stbuf.st_dev &&
|
||||
stbuf.st_ino == root_stbuf.st_ino)
|
||||
{
|
||||
ret_deployment = g_object_ref (deployment);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (ret_deployment == NULL)
|
||||
return glnx_throw (error, "Unexpected state: ostree= kernel argument found, but / is not a deployment root");
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Not an ostree system */
|
||||
}
|
||||
}
|
||||
|
||||
ot_transfer_out_value (out_deployment, &ret_deployment);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* ostree_sysroot_query_deployments_for:
|
||||
* @self: Sysroot
|
||||
@@ -1202,6 +1158,10 @@ ostree_sysroot_query_deployments_for (OstreeSysroot *self,
|
||||
{
|
||||
OstreeDeployment *deployment = self->deployments->pdata[i];
|
||||
|
||||
/* Ignore deployments not for this osname */
|
||||
if (strcmp (ostree_deployment_get_osname (deployment), osname) != 0)
|
||||
continue;
|
||||
|
||||
/* Is this deployment booted? If so, note we're past the booted */
|
||||
if (self->booted_deployment != NULL &&
|
||||
ostree_deployment_equal (deployment, self->booted_deployment))
|
||||
@@ -1210,10 +1170,6 @@ ostree_sysroot_query_deployments_for (OstreeSysroot *self,
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Ignore deployments not for this osname */
|
||||
if (strcmp (ostree_deployment_get_osname (deployment), osname) != 0)
|
||||
continue;
|
||||
|
||||
if (!found_booted && !ret_pending)
|
||||
ret_pending = g_object_ref (deployment);
|
||||
else if (found_booted && !ret_rollback)
|
||||
@@ -1572,12 +1528,14 @@ ostree_sysroot_simple_write_deployment (OstreeSysroot *sysroot,
|
||||
|
||||
/* Retain deployment if:
|
||||
* - we're explicitly asked to, or
|
||||
* - it's pinned
|
||||
* - the deployment is for another osname, or
|
||||
* - we're keeping pending deployments and this is a pending deployment, or
|
||||
* - this is the merge or boot deployment, or
|
||||
* - we're keeping rollback deployments and this is a rollback deployment
|
||||
*/
|
||||
if (retain
|
||||
|| ostree_deployment_is_pinned (deployment)
|
||||
|| !osname_matches
|
||||
|| (retain_pending && !passed_crossover)
|
||||
|| (is_booted || is_merge)
|
||||
@@ -1832,3 +1790,45 @@ ostree_sysroot_deployment_unlock (OstreeSysroot *self,
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* ostree_sysroot_deployment_set_pinned:
|
||||
* @self: Sysroot
|
||||
* @deployment: A deployment
|
||||
* @is_pinned: Whether or not deployment will be automatically GC'd
|
||||
* @error: Error
|
||||
*
|
||||
* By default, deployments may be subject to garbage collection. Typical uses of
|
||||
* libostree only retain at most 2 deployments. If @is_pinned is `TRUE`, a
|
||||
* metadata bit will be set causing libostree to avoid automatic GC of the
|
||||
* deployment. However, this is really an "advisory" note; it's still possible
|
||||
* for e.g. older versions of libostree unaware of pinning to GC the deployment.
|
||||
*
|
||||
* This function does nothing and returns successfully if the deployment
|
||||
* is already in the desired pinning state.
|
||||
*
|
||||
* Since: 2018.3
|
||||
*/
|
||||
gboolean
|
||||
ostree_sysroot_deployment_set_pinned (OstreeSysroot *self,
|
||||
OstreeDeployment *deployment,
|
||||
gboolean is_pinned,
|
||||
GError **error)
|
||||
{
|
||||
const gboolean current_pin = ostree_deployment_is_pinned (deployment);
|
||||
if (is_pinned == current_pin)
|
||||
return TRUE;
|
||||
|
||||
g_autoptr(OstreeDeployment) deployment_clone = ostree_deployment_clone (deployment);
|
||||
GKeyFile *origin_clone = ostree_deployment_get_origin (deployment_clone);
|
||||
|
||||
if (is_pinned)
|
||||
g_key_file_set_boolean (origin_clone, OSTREE_ORIGIN_TRANSIENT_GROUP, "pinned", TRUE);
|
||||
else
|
||||
g_key_file_remove_key (origin_clone, OSTREE_ORIGIN_TRANSIENT_GROUP, "pinned", NULL);
|
||||
|
||||
if (!ostree_sysroot_write_origin_file (self, deployment, origin_clone, NULL, error))
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
@@ -181,6 +181,12 @@ gboolean ostree_sysroot_deployment_set_mutable (OstreeSysroot *self,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
_OSTREE_PUBLIC
|
||||
gboolean ostree_sysroot_deployment_set_pinned (OstreeSysroot *self,
|
||||
OstreeDeployment *deployment,
|
||||
gboolean is_pinned,
|
||||
GError **error);
|
||||
|
||||
_OSTREE_PUBLIC
|
||||
gboolean ostree_sysroot_deployment_unlock (OstreeSysroot *self,
|
||||
OstreeDeployment *deployment,
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
*
|
||||
* Since: 2017.4
|
||||
*/
|
||||
#define OSTREE_RELEASE_VERSION (2)
|
||||
#define OSTREE_RELEASE_VERSION (4)
|
||||
|
||||
/**
|
||||
* OSTREE_VERSION
|
||||
@@ -52,7 +52,7 @@
|
||||
*
|
||||
* Since: 2017.4
|
||||
*/
|
||||
#define OSTREE_VERSION (2018.2)
|
||||
#define OSTREE_VERSION (2018.4)
|
||||
|
||||
/**
|
||||
* OSTREE_VERSION_S:
|
||||
@@ -62,7 +62,7 @@
|
||||
*
|
||||
* Since: 2017.4
|
||||
*/
|
||||
#define OSTREE_VERSION_S "2018.2"
|
||||
#define OSTREE_VERSION_S "2018.4"
|
||||
|
||||
#define OSTREE_ENCODE_VERSION(year,release) \
|
||||
((year) << 16 | (release))
|
||||
|
||||
Reference in new issue
Block a user