667 lines
25 KiB
C
667 lines
25 KiB
C
/*
|
||
* Copyright © 2017 Endless Mobile, Inc.
|
||
*
|
||
* This library is free software; you can redistribute it and/or
|
||
* modify it under the terms of the GNU Lesser General Public
|
||
* License as published by the Free Software Foundation; either
|
||
* version 2 of the License, or (at your option) any later version.
|
||
*
|
||
* This library is distributed in the hope that it will be useful,
|
||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||
* Lesser General Public License for more details.
|
||
*
|
||
* You should have received a copy of the GNU Lesser General Public
|
||
* License along with this library; if not, write to the
|
||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||
* Boston, MA 02111-1307, USA.
|
||
*
|
||
* Authors:
|
||
* - Philip Withnall <withnall@endlessm.com>
|
||
*/
|
||
|
||
#include "config.h"
|
||
|
||
#include <gio/gio.h>
|
||
#include <glib.h>
|
||
#include <glib-object.h>
|
||
#include <libglnx.h>
|
||
#include <stdlib.h>
|
||
|
||
#include "ostree-autocleanups.h"
|
||
#include "ostree-remote-private.h"
|
||
#include "ostree-repo-private.h"
|
||
#include "ostree-repo-finder.h"
|
||
#include "ostree-repo-finder-mount.h"
|
||
|
||
/**
|
||
* SECTION:ostree-repo-finder-mount
|
||
* @title: OstreeRepoFinderMount
|
||
* @short_description: Finds remote repositories from ref names by looking at
|
||
* mounted removable volumes
|
||
* @stability: Unstable
|
||
* @include: libostree/ostree-repo-finder-mount.h
|
||
*
|
||
* #OstreeRepoFinderMount is an implementation of #OstreeRepoFinder which looks
|
||
* refs up in well-known locations on any mounted removable volumes.
|
||
*
|
||
* For each mounted removable volume, the directory `.ostree/repos.d` will be
|
||
* 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 directory `ostree/repo` will be searched after the others, if it exists.
|
||
* Non-removable volumes are ignored.
|
||
*
|
||
* For each repository which is found, a result will be returned for the
|
||
* intersection of the refs being searched for, and the refs in `refs/heads` and
|
||
* `refs/mirrors` in the repository on the removable volume.
|
||
*
|
||
* Symlinks are followed when listing the repositories, so a volume might
|
||
* contain a single OSTree at some arbitrary path, with a symlink from
|
||
* `.ostree/repos.d`. Any symlink which points outside the volume’s file
|
||
* system will be ignored. Repositories are deduplicated in the results.
|
||
*
|
||
* The volume monitor used to find mounted volumes can be overridden by setting
|
||
* #OstreeRepoFinderMount:monitor. By default, g_volume_monitor_get() is used.
|
||
*
|
||
* Since: 2017.8
|
||
*/
|
||
|
||
typedef GList/*<owned GObject>*/ ObjectList;
|
||
|
||
static void
|
||
object_list_free (ObjectList *list)
|
||
{
|
||
g_list_free_full (list, g_object_unref);
|
||
}
|
||
|
||
G_DEFINE_AUTOPTR_CLEANUP_FUNC (ObjectList, object_list_free)
|
||
|
||
static void ostree_repo_finder_mount_iface_init (OstreeRepoFinderInterface *iface);
|
||
|
||
struct _OstreeRepoFinderMount
|
||
{
|
||
GObject parent_instance;
|
||
|
||
GVolumeMonitor *monitor; /* owned */
|
||
};
|
||
|
||
G_DEFINE_TYPE_WITH_CODE (OstreeRepoFinderMount, ostree_repo_finder_mount, G_TYPE_OBJECT,
|
||
G_IMPLEMENT_INTERFACE (OSTREE_TYPE_REPO_FINDER, ostree_repo_finder_mount_iface_init))
|
||
|
||
typedef struct
|
||
{
|
||
gchar *uri;
|
||
gchar *keyring;
|
||
} UriAndKeyring;
|
||
|
||
static void
|
||
uri_and_keyring_free (UriAndKeyring *data)
|
||
{
|
||
g_free (data->uri);
|
||
g_free (data->keyring);
|
||
g_free (data);
|
||
}
|
||
|
||
G_DEFINE_AUTOPTR_CLEANUP_FUNC (UriAndKeyring, uri_and_keyring_free)
|
||
|
||
static UriAndKeyring *
|
||
uri_and_keyring_new (const gchar *uri,
|
||
const gchar *keyring)
|
||
{
|
||
g_autoptr(UriAndKeyring) data = NULL;
|
||
|
||
data = g_new0 (UriAndKeyring, 1);
|
||
data->uri = g_strdup (uri);
|
||
data->keyring = g_strdup (keyring);
|
||
|
||
return g_steal_pointer (&data);
|
||
}
|
||
|
||
static guint
|
||
uri_and_keyring_hash (gconstpointer key)
|
||
{
|
||
const UriAndKeyring *_key = key;
|
||
|
||
return g_str_hash (_key->uri) ^ g_str_hash (_key->keyring);
|
||
}
|
||
|
||
static gboolean
|
||
uri_and_keyring_equal (gconstpointer a,
|
||
gconstpointer b)
|
||
{
|
||
const UriAndKeyring *_a = a, *_b = b;
|
||
|
||
return g_str_equal (_a->uri, _b->uri) && g_str_equal (_a->keyring, _b->keyring);
|
||
}
|
||
|
||
/* This must return a valid remote name (suitable for use in a refspec). */
|
||
static gchar *
|
||
uri_and_keyring_to_name (UriAndKeyring *data)
|
||
{
|
||
g_autofree gchar *escaped_uri = g_uri_escape_string (data->uri, NULL, FALSE);
|
||
g_autofree gchar *escaped_keyring = g_uri_escape_string (data->keyring, NULL, FALSE);
|
||
|
||
/* FIXME: Need a better separator than `_`, since it’s not escaped in the input. */
|
||
g_autofree gchar *out = g_strdup_printf ("%s_%s", escaped_uri, escaped_keyring);
|
||
|
||
for (gsize i = 0; out[i] != '\0'; i++)
|
||
{
|
||
if (out[i] == '%')
|
||
out[i] = '_';
|
||
}
|
||
|
||
g_return_val_if_fail (ostree_validate_remote_name (out, NULL), NULL);
|
||
|
||
return g_steal_pointer (&out);
|
||
}
|
||
|
||
static gint
|
||
results_compare_cb (gconstpointer a,
|
||
gconstpointer b)
|
||
{
|
||
const OstreeRepoFinderResult *result_a = *((const OstreeRepoFinderResult **) a);
|
||
const OstreeRepoFinderResult *result_b = *((const OstreeRepoFinderResult **) b);
|
||
|
||
return ostree_repo_finder_result_compare (result_a, result_b);
|
||
}
|
||
|
||
typedef struct
|
||
{
|
||
char *ordering_name; /* (owned) */
|
||
OstreeRepo *repo; /* (owned) */
|
||
GHashTable *refs; /* (owned) (element-type OstreeCollectionRef utf8) */
|
||
} RepoAndRefs;
|
||
|
||
static void
|
||
repo_and_refs_clear (RepoAndRefs *data)
|
||
{
|
||
g_hash_table_unref (data->refs);
|
||
g_object_unref (data->repo);
|
||
g_free (data->ordering_name);
|
||
}
|
||
|
||
static gint
|
||
repo_and_refs_compare (gconstpointer a,
|
||
gconstpointer b)
|
||
{
|
||
const RepoAndRefs *_a = a;
|
||
const RepoAndRefs *_b = b;
|
||
|
||
return strcmp (_a->ordering_name, _b->ordering_name);
|
||
}
|
||
|
||
/* Check whether the repo at @dfd/@path is within the given mount, is not equal
|
||
* to the @parent_repo, and can be opened. If so, return it as @out_repo and
|
||
* all its collection–refs as @out_refs, to be added into the results. */
|
||
static gboolean
|
||
scan_repo (int dfd,
|
||
const char *path,
|
||
const char *mount_name,
|
||
const struct stat *mount_root_stbuf,
|
||
OstreeRepo *parent_repo,
|
||
OstreeRepo **out_repo,
|
||
GHashTable **out_refs,
|
||
GCancellable *cancellable,
|
||
GError **error)
|
||
{
|
||
g_autoptr(GError) local_error = NULL;
|
||
|
||
g_autoptr(OstreeRepo) repo = ostree_repo_open_at (dfd, path, cancellable, &local_error);
|
||
if (repo == NULL)
|
||
{
|
||
g_debug ("Ignoring repository ‘%s’ on mount ‘%s’ as it could not be opened: %s",
|
||
path, mount_name, local_error->message);
|
||
g_propagate_error (error, g_steal_pointer (&local_error));
|
||
return FALSE;
|
||
}
|
||
|
||
int repo_dfd = ostree_repo_get_dfd (repo);
|
||
struct stat stbuf;
|
||
|
||
if (!glnx_fstat (repo_dfd, &stbuf, &local_error))
|
||
{
|
||
g_debug ("Ignoring repository ‘%s’ on mount ‘%s’ as querying its info failed: %s",
|
||
path, mount_name, local_error->message);
|
||
g_propagate_error (error, g_steal_pointer (&local_error));
|
||
return FALSE;
|
||
}
|
||
|
||
/* Check the resolved repository path is below the mount point. Do not
|
||
* allow ref symlinks to point somewhere outside of the mounted volume. */
|
||
if (stbuf.st_dev != mount_root_stbuf->st_dev)
|
||
{
|
||
g_debug ("Ignoring repository ‘%s’ on mount ‘%s’ as it’s on a different file system from the mount",
|
||
path, mount_name);
|
||
g_propagate_error (error, g_steal_pointer (&local_error));
|
||
return FALSE;
|
||
}
|
||
|
||
/* Exclude repositories which resolve to @parent_repo. */
|
||
if (stbuf.st_dev == parent_repo->device &&
|
||
stbuf.st_ino == parent_repo->inode)
|
||
{
|
||
g_debug ("Ignoring repository ‘%s’ on mount ‘%s’ as it is the same as the one we are resolving",
|
||
path, mount_name);
|
||
g_propagate_error (error, g_steal_pointer (&local_error));
|
||
return FALSE;
|
||
}
|
||
|
||
/* List the repo’s refs and return them. */
|
||
g_autoptr(GHashTable) repo_refs = NULL; /* (element-type OstreeCollectionRef utf8) */
|
||
|
||
if (!ostree_repo_list_collection_refs (repo, NULL, &repo_refs,
|
||
OSTREE_REPO_LIST_REFS_EXT_EXCLUDE_REMOTES,
|
||
cancellable, &local_error))
|
||
{
|
||
g_debug ("Ignoring repository ‘%s’ on mount ‘%s’ as its refs could not be listed: %s",
|
||
path, mount_name, local_error->message);
|
||
g_propagate_error (error, g_steal_pointer (&local_error));
|
||
return FALSE;
|
||
}
|
||
|
||
if (out_repo != NULL)
|
||
*out_repo = g_steal_pointer (&repo);
|
||
if (out_refs != NULL)
|
||
*out_refs = g_steal_pointer (&repo_refs);
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
static void
|
||
scan_and_add_repo (int dfd,
|
||
const char *path,
|
||
gboolean sortable,
|
||
const char *mount_name,
|
||
const struct stat *mount_root_stbuf,
|
||
OstreeRepo *parent_repo,
|
||
GArray *inout_repos_refs,
|
||
GCancellable *cancellable)
|
||
{
|
||
g_autoptr(GHashTable) repo_refs = NULL;
|
||
g_autoptr(OstreeRepo) repo = NULL;
|
||
|
||
if (scan_repo (dfd, path,
|
||
mount_name, mount_root_stbuf,
|
||
parent_repo, &repo, &repo_refs, cancellable, NULL))
|
||
{
|
||
RepoAndRefs val = {
|
||
sortable ? g_strdup (path) : NULL,
|
||
g_steal_pointer (&repo),
|
||
g_steal_pointer (&repo_refs)
|
||
};
|
||
g_array_append_val (inout_repos_refs, val);
|
||
|
||
g_debug ("%s: Adding repo ‘%s’ (%ssortable)",
|
||
G_STRFUNC, path, sortable ? "" : "not ");
|
||
}
|
||
}
|
||
|
||
static void
|
||
ostree_repo_finder_mount_resolve_async (OstreeRepoFinder *finder,
|
||
const OstreeCollectionRef * const *refs,
|
||
OstreeRepo *parent_repo,
|
||
GCancellable *cancellable,
|
||
GAsyncReadyCallback callback,
|
||
gpointer user_data)
|
||
{
|
||
OstreeRepoFinderMount *self = OSTREE_REPO_FINDER_MOUNT (finder);
|
||
g_autoptr(GTask) task = NULL;
|
||
g_autoptr(ObjectList) mounts = NULL;
|
||
g_autoptr(GPtrArray) results = NULL; /* (element-type OstreeRepoFinderResult) */
|
||
GList *l;
|
||
const gint priority = 50; /* arbitrarily chosen */
|
||
|
||
task = g_task_new (finder, cancellable, callback, user_data);
|
||
g_task_set_source_tag (task, ostree_repo_finder_mount_resolve_async);
|
||
|
||
mounts = g_volume_monitor_get_mounts (self->monitor);
|
||
results = g_ptr_array_new_with_free_func ((GDestroyNotify) ostree_repo_finder_result_free);
|
||
|
||
g_debug ("%s: Found %u mounts", G_STRFUNC, g_list_length (mounts));
|
||
|
||
for (l = mounts; l != NULL; l = l->next)
|
||
{
|
||
GMount *mount = G_MOUNT (l->data);
|
||
g_autofree gchar *mount_name = NULL;
|
||
g_autoptr(GFile) mount_root = NULL;
|
||
g_autofree gchar *mount_root_path = NULL;
|
||
glnx_fd_close int mount_root_dfd = -1;
|
||
struct stat mount_root_stbuf;
|
||
glnx_fd_close int repos_dfd = -1;
|
||
gsize i;
|
||
g_autoptr(GHashTable) repo_to_refs = NULL; /* (element-type UriAndKeyring GHashTable) */
|
||
GHashTable *supported_ref_to_checksum; /* (element-type OstreeCollectionRef utf8) */
|
||
GHashTableIter iter;
|
||
UriAndKeyring *repo;
|
||
g_autoptr(GError) local_error = NULL;
|
||
|
||
mount_name = g_mount_get_name (mount);
|
||
|
||
/* Check the mount’s general properties. */
|
||
if (g_mount_is_shadowed (mount))
|
||
{
|
||
g_debug ("Ignoring mount ‘%s’ as it’s shadowed.", mount_name);
|
||
continue;
|
||
}
|
||
|
||
mount_root = g_mount_get_root (mount);
|
||
mount_root_path = g_file_get_path (mount_root);
|
||
|
||
if (!glnx_opendirat (AT_FDCWD, mount_root_path, TRUE, &mount_root_dfd, &local_error))
|
||
{
|
||
g_debug ("Ignoring mount ‘%s’ as ‘%s’ directory can’t be opened: %s",
|
||
mount_name, mount_root_path, local_error->message);
|
||
continue;
|
||
}
|
||
|
||
/* stat() the mount root so we can later check whether the resolved
|
||
* repositories for individual refs are on the same device (to avoid the
|
||
* symlinks for them pointing outside the mount root). */
|
||
if (!glnx_fstat (mount_root_dfd, &mount_root_stbuf, &local_error))
|
||
{
|
||
g_debug ("Ignoring mount ‘%s’ as querying info of ‘%s’ failed: %s",
|
||
mount_name, mount_root_path, local_error->message);
|
||
continue;
|
||
}
|
||
|
||
/* Check if it contains a .ostree/repos.d directory. If not, move on and
|
||
* try the other well-known subdirectories. */
|
||
if (!glnx_opendirat (mount_root_dfd, ".ostree/repos.d", TRUE, &repos_dfd, NULL))
|
||
repos_dfd = -1;
|
||
|
||
/* List all the repositories in the repos.d directory. */
|
||
/* (element-type GHashTable (element-type OstreeCollectionRef utf8)) */
|
||
g_autoptr(GArray) repos_refs = g_array_new (FALSE, TRUE, sizeof (RepoAndRefs));
|
||
g_array_set_clear_func (repos_refs, (GDestroyNotify) repo_and_refs_clear);
|
||
|
||
GLnxDirFdIterator repos_iter;
|
||
|
||
if (repos_dfd >= 0 &&
|
||
!glnx_dirfd_iterator_init_at (repos_dfd, ".", TRUE, &repos_iter, &local_error))
|
||
{
|
||
g_debug ("Error iterating over ‘%s/.ostree/repos.d’ directory in mount ‘%s’: %s",
|
||
mount_root_path, mount_name, local_error->message);
|
||
g_clear_error (&local_error);
|
||
/* don’t skip this mount as there’s still the ostree/repo directory to try */
|
||
}
|
||
else if (repos_dfd >= 0)
|
||
{
|
||
while (TRUE)
|
||
{
|
||
struct dirent *repo_dent;
|
||
|
||
if (!glnx_dirfd_iterator_next_dent (&repos_iter, &repo_dent, cancellable, &local_error))
|
||
{
|
||
g_debug ("Error iterating over ‘%s/.ostree/repos.d’ directory in mount ‘%s’: %s",
|
||
mount_root_path, mount_name, local_error->message);
|
||
g_clear_error (&local_error);
|
||
/* don’t skip this mount as there’s still the ostree/repo directory to try */
|
||
break;
|
||
}
|
||
|
||
if (repo_dent == NULL)
|
||
break;
|
||
|
||
/* Grab the set of collection–refs from the repo if we can open it. */
|
||
scan_and_add_repo (repos_dfd, repo_dent->d_name, TRUE,
|
||
mount_name, &mount_root_stbuf,
|
||
parent_repo, repos_refs, cancellable);
|
||
}
|
||
}
|
||
|
||
/* Sort the repos lexically. */
|
||
g_array_sort (repos_refs, repo_and_refs_compare);
|
||
|
||
/* Also check the .ostree/repo and ostree/repo directories in the mount,
|
||
* as well-known special cases. Add them after sorting, so they’re always
|
||
* last. */
|
||
scan_and_add_repo (mount_root_dfd, ".ostree/repo", FALSE,
|
||
mount_name, &mount_root_stbuf,
|
||
parent_repo, repos_refs, cancellable);
|
||
scan_and_add_repo (mount_root_dfd, "ostree/repo", FALSE,
|
||
mount_name, &mount_root_stbuf,
|
||
parent_repo, repos_refs, cancellable);
|
||
|
||
/* Check whether a subdirectory exists for any of the @refs we’re looking
|
||
* for. If so, and it’s a symbolic link, dereference it so multiple links
|
||
* to the same repository (containing multiple refs) are coalesced.
|
||
* Otherwise, include it as a result by itself. */
|
||
repo_to_refs = g_hash_table_new_full (uri_and_keyring_hash, uri_and_keyring_equal,
|
||
(GDestroyNotify) uri_and_keyring_free, (GDestroyNotify) g_hash_table_unref);
|
||
|
||
for (i = 0; refs[i] != NULL; i++)
|
||
{
|
||
const OstreeCollectionRef *ref = refs[i];
|
||
g_autofree gchar *resolved_repo_uri = NULL;
|
||
g_autofree gchar *keyring = NULL;
|
||
g_autoptr(UriAndKeyring) resolved_repo = NULL;
|
||
|
||
for (gsize j = 0; j < repos_refs->len; j++)
|
||
{
|
||
const RepoAndRefs *repo_and_refs = &g_array_index (repos_refs, RepoAndRefs, j);
|
||
OstreeRepo *repo = repo_and_refs->repo;
|
||
GHashTable *repo_refs = repo_and_refs->refs;
|
||
g_autofree char *repo_path = g_file_get_path (ostree_repo_get_path (repo));
|
||
|
||
const gchar *checksum = g_hash_table_lookup (repo_refs, ref);
|
||
|
||
if (checksum == NULL)
|
||
{
|
||
g_debug ("Ignoring repository ‘%s’ when looking for ref (%s, %s) on mount ‘%s’ as it doesn’t contain the ref.",
|
||
repo_path, ref->collection_id, ref->ref_name, mount_name);
|
||
g_clear_error (&local_error);
|
||
continue;
|
||
}
|
||
|
||
/* Finally, look up the GPG keyring for this ref. */
|
||
keyring = ostree_repo_resolve_keyring_for_collection (parent_repo, ref->collection_id,
|
||
cancellable, &local_error);
|
||
|
||
if (keyring == NULL)
|
||
{
|
||
g_debug ("Ignoring repository ‘%s’ when looking for ref (%s, %s) on mount ‘%s’ due to missing keyring: %s",
|
||
repo_path, ref->collection_id, ref->ref_name, mount_name, local_error->message);
|
||
g_clear_error (&local_error);
|
||
continue;
|
||
}
|
||
|
||
/* There is a valid repo at (or pointed to by)
|
||
* $mount_root/.ostree/repos.d/$something.
|
||
* Add it to the results, keyed by the canonicalised repository URI
|
||
* to deduplicate the results. */
|
||
g_autofree char *canonical_repo_path = realpath (repo_path, NULL);
|
||
resolved_repo_uri = g_strconcat ("file://", canonical_repo_path, NULL);
|
||
g_debug ("Resolved ref (%s, %s) on mount ‘%s’ to repo URI ‘%s’ with keyring ‘%s’.",
|
||
ref->collection_id, ref->ref_name, mount_name, resolved_repo_uri, keyring);
|
||
|
||
resolved_repo = uri_and_keyring_new (resolved_repo_uri, keyring);
|
||
|
||
supported_ref_to_checksum = g_hash_table_lookup (repo_to_refs, resolved_repo);
|
||
|
||
if (supported_ref_to_checksum == NULL)
|
||
{
|
||
supported_ref_to_checksum = g_hash_table_new_full (ostree_collection_ref_hash,
|
||
ostree_collection_ref_equal,
|
||
NULL, g_free);
|
||
g_hash_table_insert (repo_to_refs, g_steal_pointer (&resolved_repo), supported_ref_to_checksum /* transfer */);
|
||
}
|
||
|
||
g_hash_table_insert (supported_ref_to_checksum, (gpointer) ref, g_strdup (checksum));
|
||
|
||
/* We’ve found a result for this collection–ref. No point in checking
|
||
* the other repos on the mount, since pulling in parallel from them won’t help. */
|
||
break;
|
||
}
|
||
}
|
||
|
||
/* Aggregate the results. */
|
||
g_hash_table_iter_init (&iter, repo_to_refs);
|
||
|
||
while (g_hash_table_iter_next (&iter, (gpointer *) &repo, (gpointer *) &supported_ref_to_checksum))
|
||
{
|
||
g_autoptr(OstreeRemote) remote = NULL;
|
||
|
||
/* Build an #OstreeRemote. Use the escaped URI, since remote->name
|
||
* is used in file paths, so needs to not contain special characters. */
|
||
g_autofree gchar *name = uri_and_keyring_to_name (repo);
|
||
remote = ostree_remote_new (name);
|
||
|
||
g_clear_pointer (&remote->keyring, g_free);
|
||
remote->keyring = g_strdup (repo->keyring);
|
||
|
||
/* gpg-verify-summary is false since we use the unsigned summary file support. */
|
||
g_key_file_set_string (remote->options, remote->group, "url", repo->uri);
|
||
g_key_file_set_boolean (remote->options, remote->group, "gpg-verify", TRUE);
|
||
g_key_file_set_boolean (remote->options, remote->group, "gpg-verify-summary", FALSE);
|
||
|
||
/* Set the timestamp in the #OstreeRepoFinderResult to 0 because
|
||
* the code in ostree_repo_pull_from_remotes_async() will be able to
|
||
* check it just as quickly as we can here; so don’t duplicate the
|
||
* code. */
|
||
g_ptr_array_add (results, ostree_repo_finder_result_new (remote, finder, priority, supported_ref_to_checksum, 0));
|
||
}
|
||
}
|
||
|
||
g_ptr_array_sort (results, results_compare_cb);
|
||
|
||
g_task_return_pointer (task, g_steal_pointer (&results), (GDestroyNotify) g_ptr_array_unref);
|
||
}
|
||
|
||
static GPtrArray *
|
||
ostree_repo_finder_mount_resolve_finish (OstreeRepoFinder *self,
|
||
GAsyncResult *result,
|
||
GError **error)
|
||
{
|
||
g_return_val_if_fail (g_task_is_valid (result, self), NULL);
|
||
return g_task_propagate_pointer (G_TASK (result), error);
|
||
}
|
||
|
||
static void
|
||
ostree_repo_finder_mount_init (OstreeRepoFinderMount *self)
|
||
{
|
||
/* Nothing to see here. */
|
||
}
|
||
|
||
static void
|
||
ostree_repo_finder_mount_constructed (GObject *object)
|
||
{
|
||
OstreeRepoFinderMount *self = OSTREE_REPO_FINDER_MOUNT (object);
|
||
|
||
G_OBJECT_CLASS (ostree_repo_finder_mount_parent_class)->constructed (object);
|
||
|
||
if (self->monitor == NULL)
|
||
self->monitor = g_volume_monitor_get ();
|
||
}
|
||
|
||
typedef enum
|
||
{
|
||
PROP_MONITOR = 1,
|
||
} OstreeRepoFinderMountProperty;
|
||
|
||
static void
|
||
ostree_repo_finder_mount_get_property (GObject *object,
|
||
guint property_id,
|
||
GValue *value,
|
||
GParamSpec *pspec)
|
||
{
|
||
OstreeRepoFinderMount *self = OSTREE_REPO_FINDER_MOUNT (object);
|
||
|
||
switch ((OstreeRepoFinderMountProperty) property_id)
|
||
{
|
||
case PROP_MONITOR:
|
||
g_value_set_object (value, self->monitor);
|
||
break;
|
||
default:
|
||
g_assert_not_reached ();
|
||
}
|
||
}
|
||
|
||
static void
|
||
ostree_repo_finder_mount_set_property (GObject *object,
|
||
guint property_id,
|
||
const GValue *value,
|
||
GParamSpec *pspec)
|
||
{
|
||
OstreeRepoFinderMount *self = OSTREE_REPO_FINDER_MOUNT (object);
|
||
|
||
switch ((OstreeRepoFinderMountProperty) property_id)
|
||
{
|
||
case PROP_MONITOR:
|
||
/* Construct-only. */
|
||
g_assert (self->monitor == NULL);
|
||
self->monitor = g_value_dup_object (value);
|
||
break;
|
||
default:
|
||
g_assert_not_reached ();
|
||
}
|
||
}
|
||
|
||
static void
|
||
ostree_repo_finder_mount_dispose (GObject *object)
|
||
{
|
||
OstreeRepoFinderMount *self = OSTREE_REPO_FINDER_MOUNT (object);
|
||
|
||
g_clear_object (&self->monitor);
|
||
|
||
G_OBJECT_CLASS (ostree_repo_finder_mount_parent_class)->dispose (object);
|
||
}
|
||
|
||
static void
|
||
ostree_repo_finder_mount_class_init (OstreeRepoFinderMountClass *klass)
|
||
{
|
||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||
|
||
object_class->get_property = ostree_repo_finder_mount_get_property;
|
||
object_class->set_property = ostree_repo_finder_mount_set_property;
|
||
object_class->constructed = ostree_repo_finder_mount_constructed;
|
||
object_class->dispose = ostree_repo_finder_mount_dispose;
|
||
|
||
/**
|
||
* OstreeRepoFinderMount:monitor:
|
||
*
|
||
* Volume monitor to use to look up mounted volumes when queried.
|
||
*
|
||
* Since: 2017.8
|
||
*/
|
||
g_object_class_install_property (object_class, PROP_MONITOR,
|
||
g_param_spec_object ("monitor",
|
||
"Volume Monitor",
|
||
"Volume monitor to use "
|
||
"to look up mounted "
|
||
"volumes when queried.",
|
||
G_TYPE_VOLUME_MONITOR,
|
||
G_PARAM_CONSTRUCT_ONLY |
|
||
G_PARAM_READWRITE |
|
||
G_PARAM_STATIC_STRINGS));
|
||
}
|
||
|
||
static void
|
||
ostree_repo_finder_mount_iface_init (OstreeRepoFinderInterface *iface)
|
||
{
|
||
iface->resolve_async = ostree_repo_finder_mount_resolve_async;
|
||
iface->resolve_finish = ostree_repo_finder_mount_resolve_finish;
|
||
}
|
||
|
||
/**
|
||
* ostree_repo_finder_mount_new:
|
||
* @monitor: (nullable) (transfer none): volume monitor to use, or %NULL to use
|
||
* the system default
|
||
*
|
||
* Create a new #OstreeRepoFinderMount, using the given @monitor to look up
|
||
* volumes. If @monitor is %NULL, the monitor from g_volume_monitor_get() will
|
||
* be used.
|
||
*
|
||
* Returns: (transfer full): a new #OstreeRepoFinderMount
|
||
* Since: 2017.8
|
||
*/
|
||
OstreeRepoFinderMount *
|
||
ostree_repo_finder_mount_new (GVolumeMonitor *monitor)
|
||
{
|
||
g_return_val_if_fail (monitor == NULL || G_IS_VOLUME_MONITOR (monitor), NULL);
|
||
|
||
return g_object_new (OSTREE_TYPE_REPO_FINDER_MOUNT,
|
||
"monitor", monitor,
|
||
NULL);
|
||
}
|