lib/repo-finder-mount: Change the schema for finding repos on volumes
See issue #1174 for the rationale behind this. In summary: • It required two lists of collection–refs to be maintained: one in the repository, and one pointing to the repository. • It didn’t automatically work for live USBs of OSs based on OSTree (where there’s always a repository at /ostree/repo). • It was unnecessarily complex. The new scheme allows a list of repositories to be searched, but without needing a layer of indirection through their collection–refs. It adds /ostree/repo and /.ostree/repo as well-known repository locations which are always checked on a mounted volume (if they exist). Update the unit tests accordingly. Signed-off-by: Philip Withnall <withnall@endlessm.com> https://github.com/ostreedev/ostree/issues/1174 Closes: #1179 Approved by: cgwalters
This commit is contained in:
parent
981eb6c226
commit
15247641d9
|
|
@ -46,19 +46,20 @@
|
||||||
* #OstreeRepoFinderMount is an implementation of #OstreeRepoFinder which looks
|
* #OstreeRepoFinderMount is an implementation of #OstreeRepoFinder which looks
|
||||||
* refs up in well-known locations on any mounted removable volumes.
|
* refs up in well-known locations on any mounted removable volumes.
|
||||||
*
|
*
|
||||||
* For an #OstreeCollectionRef, (`C`, `R`), it checks whether `.ostree/repos/C/R`
|
* For each mounted removable volume, the directory `.ostree/repos.d` will be
|
||||||
* exists and is an OSTree repository on each mounted removable volume. Collection
|
* enumerated, and all OSTree repositories below it will be searched, in lexical
|
||||||
* IDs and ref names are not escaped when building the path, so if either
|
* order, for the requested #OstreeCollectionRefs. The names of the directories
|
||||||
* contains `/` in its name, the repository will be checked for in a
|
* below `.ostree/repos.d` are irrelevant, apart from their lexical ordering.
|
||||||
* subdirectory of `.ostree/repos`. Non-removable volumes are ignored.
|
* 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
|
* 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
|
* intersection of the refs being searched for, and the refs in `refs/heads` and
|
||||||
* `refs/mirrors` in the repository on the removable volume.
|
* `refs/mirrors` in the repository on the removable volume.
|
||||||
*
|
*
|
||||||
* Symlinks are followed when resolving the refs, so a volume might contain a
|
* Symlinks are followed when listing the repositories, so a volume might
|
||||||
* single OSTree at some arbitrary path, with a number of refs linking to it
|
* contain a single OSTree at some arbitrary path, with a symlink from
|
||||||
* from `.ostree/repos`. Any symlink which points outside the volume’s file
|
* `.ostree/repos.d`. Any symlink which points outside the volume’s file
|
||||||
* system will be ignored. Repositories are deduplicated in the results.
|
* system will be ignored. Repositories are deduplicated in the results.
|
||||||
*
|
*
|
||||||
* The volume monitor used to find mounted volumes can be overridden by setting
|
* The volume monitor used to find mounted volumes can be overridden by setting
|
||||||
|
|
@ -166,6 +167,137 @@ results_compare_cb (gconstpointer a,
|
||||||
return ostree_repo_finder_result_compare (result_a, result_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
|
static void
|
||||||
ostree_repo_finder_mount_resolve_async (OstreeRepoFinder *finder,
|
ostree_repo_finder_mount_resolve_async (OstreeRepoFinder *finder,
|
||||||
const OstreeCollectionRef * const *refs,
|
const OstreeCollectionRef * const *refs,
|
||||||
|
|
@ -214,7 +346,6 @@ ostree_repo_finder_mount_resolve_async (OstreeRepoFinder *finde
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check if it contains a .ostree/repos directory. */
|
|
||||||
mount_root = g_mount_get_root (mount);
|
mount_root = g_mount_get_root (mount);
|
||||||
mount_root_path = g_file_get_path (mount_root);
|
mount_root_path = g_file_get_path (mount_root);
|
||||||
|
|
||||||
|
|
@ -225,18 +356,6 @@ ostree_repo_finder_mount_resolve_async (OstreeRepoFinder *finde
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!glnx_opendirat (mount_root_dfd, ".ostree/repos", TRUE, &repos_dfd, &local_error))
|
|
||||||
{
|
|
||||||
if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
|
|
||||||
g_debug ("Ignoring mount ‘%s’ as ‘%s/.ostree/repos’ directory doesn’t exist.",
|
|
||||||
mount_name, mount_root_path);
|
|
||||||
else
|
|
||||||
g_debug ("Ignoring mount ‘%s’ as ‘%s/.ostree/repos’ directory can’t be opened: %s",
|
|
||||||
mount_name, mount_root_path, local_error->message);
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* stat() the mount root so we can later check whether the resolved
|
/* stat() the mount root so we can later check whether the resolved
|
||||||
* repositories for individual refs are on the same device (to avoid the
|
* repositories for individual refs are on the same device (to avoid the
|
||||||
* symlinks for them pointing outside the mount root). */
|
* symlinks for them pointing outside the mount root). */
|
||||||
|
|
@ -247,6 +366,64 @@ ostree_repo_finder_mount_resolve_async (OstreeRepoFinder *finde
|
||||||
continue;
|
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
|
/* 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
|
* for. If so, and it’s a symbolic link, dereference it so multiple links
|
||||||
* to the same repository (containing multiple refs) are coalesced.
|
* to the same repository (containing multiple refs) are coalesced.
|
||||||
|
|
@ -256,108 +433,48 @@ ostree_repo_finder_mount_resolve_async (OstreeRepoFinder *finde
|
||||||
|
|
||||||
for (i = 0; refs[i] != NULL; i++)
|
for (i = 0; refs[i] != NULL; i++)
|
||||||
{
|
{
|
||||||
struct stat stbuf;
|
const OstreeCollectionRef *ref = refs[i];
|
||||||
g_autofree gchar *collection_and_ref = NULL;
|
|
||||||
g_autofree gchar *resolved_repo_uri = NULL;
|
g_autofree gchar *resolved_repo_uri = NULL;
|
||||||
g_autofree gchar *keyring = NULL;
|
g_autofree gchar *keyring = NULL;
|
||||||
g_autoptr(UriAndKeyring) resolved_repo = NULL;
|
g_autoptr(UriAndKeyring) resolved_repo = NULL;
|
||||||
|
|
||||||
collection_and_ref = g_build_filename (refs[i]->collection_id, refs[i]->ref_name, NULL);
|
for (gsize j = 0; j < repos_refs->len; j++)
|
||||||
|
|
||||||
if (!glnx_fstatat (repos_dfd, collection_and_ref, &stbuf, AT_NO_AUTOMOUNT, &local_error))
|
|
||||||
{
|
{
|
||||||
g_debug ("Ignoring ref (%s, %s) on mount ‘%s’ as querying info of ‘%s’ failed: %s",
|
const RepoAndRefs *repo_and_refs = &g_array_index (repos_refs, RepoAndRefs, j);
|
||||||
refs[i]->collection_id, refs[i]->ref_name, mount_name, collection_and_ref, local_error->message);
|
OstreeRepo *repo = repo_and_refs->repo;
|
||||||
g_clear_error (&local_error);
|
GHashTable *repo_refs = repo_and_refs->refs;
|
||||||
continue;
|
g_autofree char *repo_path = g_file_get_path (ostree_repo_get_path (repo));
|
||||||
}
|
|
||||||
|
|
||||||
if ((stbuf.st_mode & S_IFMT) != S_IFDIR)
|
const gchar *checksum = g_hash_table_lookup (repo_refs, ref);
|
||||||
{
|
|
||||||
g_debug ("Ignoring ref (%s, %s) on mount ‘%s’ as ‘%s’ is of type %u, not a directory.",
|
|
||||||
refs[i]->collection_id, refs[i]->ref_name, mount_name, collection_and_ref, (stbuf.st_mode & S_IFMT));
|
|
||||||
g_clear_error (&local_error);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check the resolved repository path is below the mount point. Do not
|
|
||||||
* allow ref symlinks to point somewhere outside of the mounted
|
|
||||||
* volume. */
|
|
||||||
if (stbuf.st_dev != mount_root_stbuf.st_dev)
|
|
||||||
{
|
|
||||||
g_debug ("Ignoring ref (%s, %s) on mount ‘%s’ as it’s on a different file system from the mount.",
|
|
||||||
refs[i]->collection_id, refs[i]->ref_name, mount_name);
|
|
||||||
g_clear_error (&local_error);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Exclude repositories which resolve to @parent_repo. */
|
|
||||||
if (stbuf.st_dev == parent_repo->device &&
|
|
||||||
stbuf.st_ino == parent_repo->inode)
|
|
||||||
{
|
|
||||||
g_debug ("Ignoring ref (%s, %s) on mount ‘%s’ as it is the same as the one we are resolving",
|
|
||||||
refs[i]->collection_id, refs[i]->ref_name, mount_name);
|
|
||||||
g_clear_error (&local_error);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Grab the given ref and a checksum for it from the repo, if it appears to be a valid repo */
|
|
||||||
g_autoptr(OstreeRepo) repo = ostree_repo_open_at (repos_dfd, collection_and_ref,
|
|
||||||
cancellable, &local_error);
|
|
||||||
if (!repo)
|
|
||||||
{
|
|
||||||
g_debug ("Ignoring ref (%s, %s) on mount ‘%s’ as its repository could not be opened: %s",
|
|
||||||
refs[i]->collection_id, refs[i]->ref_name, mount_name, local_error->message);
|
|
||||||
g_clear_error (&local_error);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
g_autoptr(GHashTable) repo_refs = NULL; /* (element-type OstreeCollectionRef utf8) */
|
|
||||||
|
|
||||||
if (!ostree_repo_list_collection_refs (repo, refs[i]->collection_id, &repo_refs,
|
|
||||||
OSTREE_REPO_LIST_REFS_EXT_EXCLUDE_REMOTES,
|
|
||||||
cancellable, &local_error))
|
|
||||||
{
|
|
||||||
g_debug ("Ignoring ref (%s, %s) on mount ‘%s’ as its refs could not be listed: %s",
|
|
||||||
refs[i]->collection_id, refs[i]->ref_name, mount_name, local_error->message);
|
|
||||||
g_clear_error (&local_error);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const gchar *checksum = g_hash_table_lookup (repo_refs, refs[i]);
|
|
||||||
|
|
||||||
if (checksum == NULL)
|
if (checksum == NULL)
|
||||||
{
|
{
|
||||||
g_debug ("Ignoring ref (%s, %s) on mount ‘%s’ as its repository doesn’t contain the ref.",
|
g_debug ("Ignoring repository ‘%s’ when looking for ref (%s, %s) on mount ‘%s’ as it doesn’t contain the ref.",
|
||||||
refs[i]->collection_id, refs[i]->ref_name, mount_name);
|
repo_path, ref->collection_id, ref->ref_name, mount_name);
|
||||||
g_clear_error (&local_error);
|
g_clear_error (&local_error);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Finally, look up the GPG keyring for this ref. */
|
/* Finally, look up the GPG keyring for this ref. */
|
||||||
keyring = ostree_repo_resolve_keyring_for_collection (parent_repo, refs[i]->collection_id,
|
keyring = ostree_repo_resolve_keyring_for_collection (parent_repo, ref->collection_id,
|
||||||
cancellable, &local_error);
|
cancellable, &local_error);
|
||||||
|
|
||||||
if (keyring == NULL)
|
if (keyring == NULL)
|
||||||
{
|
{
|
||||||
g_debug ("Ignoring ref (%s, %s) on mount ‘%s’ due to missing keyring: %s",
|
g_debug ("Ignoring repository ‘%s’ when looking for ref (%s, %s) on mount ‘%s’ due to missing keyring: %s",
|
||||||
refs[i]->collection_id, refs[i]->ref_name, mount_name, local_error->message);
|
repo_path, ref->collection_id, ref->ref_name, mount_name, local_error->message);
|
||||||
g_clear_error (&local_error);
|
g_clear_error (&local_error);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* There is a valid repo at (or pointed to by)
|
/* There is a valid repo at (or pointed to by)
|
||||||
* $mount_root/.ostree/repos/$refs[i]->collection_id/$refs[i]->ref_name.
|
* $mount_root/.ostree/repos.d/$something.
|
||||||
* Add it to the results, keyed by the canonicalised repository URI
|
* Add it to the results, keyed by the canonicalised repository URI
|
||||||
* to deduplicate the results. */
|
* to deduplicate the results. */
|
||||||
|
g_autofree char *canonical_repo_path = realpath (repo_path, NULL);
|
||||||
g_autofree char *repo_abspath = g_build_filename (mount_root_path, ".ostree/repos",
|
resolved_repo_uri = g_strconcat ("file://", canonical_repo_path, NULL);
|
||||||
collection_and_ref, NULL);
|
|
||||||
/* FIXME - why are we using realpath here? */
|
|
||||||
g_autofree char *canonical_repo_dir_path = realpath (repo_abspath, NULL);
|
|
||||||
resolved_repo_uri = g_strconcat ("file://", canonical_repo_dir_path, NULL);
|
|
||||||
g_debug ("Resolved ref (%s, %s) on mount ‘%s’ to repo URI ‘%s’ with keyring ‘%s’.",
|
g_debug ("Resolved ref (%s, %s) on mount ‘%s’ to repo URI ‘%s’ with keyring ‘%s’.",
|
||||||
refs[i]->collection_id, refs[i]->ref_name, mount_name, resolved_repo_uri, keyring);
|
ref->collection_id, ref->ref_name, mount_name, resolved_repo_uri, keyring);
|
||||||
|
|
||||||
resolved_repo = uri_and_keyring_new (resolved_repo_uri, keyring);
|
resolved_repo = uri_and_keyring_new (resolved_repo_uri, keyring);
|
||||||
|
|
||||||
|
|
@ -371,7 +488,12 @@ ostree_repo_finder_mount_resolve_async (OstreeRepoFinder *finde
|
||||||
g_hash_table_insert (repo_to_refs, g_steal_pointer (&resolved_repo), supported_ref_to_checksum /* transfer */);
|
g_hash_table_insert (repo_to_refs, g_steal_pointer (&resolved_repo), supported_ref_to_checksum /* transfer */);
|
||||||
}
|
}
|
||||||
|
|
||||||
g_hash_table_insert (supported_ref_to_checksum, (gpointer) refs[i], g_strdup (checksum));
|
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. */
|
/* Aggregate the results. */
|
||||||
|
|
|
||||||
|
|
@ -70,8 +70,6 @@ static void
|
||||||
teardown (Fixture *fixture,
|
teardown (Fixture *fixture,
|
||||||
gconstpointer test_data)
|
gconstpointer test_data)
|
||||||
{
|
{
|
||||||
g_autoptr(GError) error = NULL;
|
|
||||||
|
|
||||||
/* Recursively remove the temporary directory. */
|
/* Recursively remove the temporary directory. */
|
||||||
(void)glnx_tmpdir_delete (&fixture->tmpdir, NULL, NULL);
|
(void)glnx_tmpdir_delete (&fixture->tmpdir, NULL, NULL);
|
||||||
|
|
||||||
|
|
@ -150,7 +148,7 @@ test_repo_finder_mount_no_mounts (Fixture *fixture,
|
||||||
g_main_context_pop_thread_default (context);
|
g_main_context_pop_thread_default (context);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Create a .ostree/repos directory under the given @mount_root, or abort. */
|
/* Create a .ostree/repos.d directory under the given @mount_root, or abort. */
|
||||||
static gboolean
|
static gboolean
|
||||||
assert_create_repos_dir (Fixture *fixture,
|
assert_create_repos_dir (Fixture *fixture,
|
||||||
const gchar *mount_root_name,
|
const gchar *mount_root_name,
|
||||||
|
|
@ -160,7 +158,7 @@ assert_create_repos_dir (Fixture *fixture,
|
||||||
glnx_fd_close int repos_dfd = -1;
|
glnx_fd_close int repos_dfd = -1;
|
||||||
g_autoptr(GError) error = NULL;
|
g_autoptr(GError) error = NULL;
|
||||||
|
|
||||||
g_autofree gchar *path = g_build_filename (mount_root_name, ".ostree", "repos", NULL);
|
g_autofree gchar *path = g_build_filename (mount_root_name, ".ostree", "repos.d", NULL);
|
||||||
glnx_shutil_mkdir_p_at_open (fixture->tmpdir.fd, path, 0700, &repos_dfd, NULL, &error);
|
glnx_shutil_mkdir_p_at_open (fixture->tmpdir.fd, path, 0700, &repos_dfd, NULL, &error);
|
||||||
if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_EXISTS))
|
if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_EXISTS))
|
||||||
g_clear_error (&error);
|
g_clear_error (&error);
|
||||||
|
|
@ -230,18 +228,18 @@ static OstreeRepo *
|
||||||
assert_create_repo_dir (Fixture *fixture,
|
assert_create_repo_dir (Fixture *fixture,
|
||||||
int repos_dfd,
|
int repos_dfd,
|
||||||
GMount *repos_mount,
|
GMount *repos_mount,
|
||||||
const OstreeCollectionRef *ref,
|
const char *repo_name,
|
||||||
gchar **out_uri,
|
gchar **out_uri,
|
||||||
...) G_GNUC_NULL_TERMINATED;
|
...) G_GNUC_NULL_TERMINATED;
|
||||||
|
|
||||||
/* Create a @ref directory under the given @repos_dfd, or abort. Create a new
|
/* Create a @repo_name directory under the given @repos_dfd, or abort. Create a
|
||||||
* repository in it with the refs given in @..., as per assert_create_remote_va().
|
* new repository in it with the refs given in @..., as per
|
||||||
* Return the URI of the repository. */
|
* assert_create_remote_va(). Return the URI of the repository. */
|
||||||
static OstreeRepo *
|
static OstreeRepo *
|
||||||
assert_create_repo_dir (Fixture *fixture,
|
assert_create_repo_dir (Fixture *fixture,
|
||||||
int repos_dfd,
|
int repos_dfd,
|
||||||
GMount *repos_mount,
|
GMount *repos_mount,
|
||||||
const OstreeCollectionRef *ref,
|
const char *repo_name,
|
||||||
gchar **out_uri,
|
gchar **out_uri,
|
||||||
...)
|
...)
|
||||||
{
|
{
|
||||||
|
|
@ -250,15 +248,14 @@ assert_create_repo_dir (Fixture *fixture,
|
||||||
g_autoptr(GError) error = NULL;
|
g_autoptr(GError) error = NULL;
|
||||||
va_list args;
|
va_list args;
|
||||||
|
|
||||||
g_autofree gchar *path = g_build_filename (ref->collection_id, ref->ref_name, NULL);
|
glnx_shutil_mkdir_p_at_open (repos_dfd, repo_name, 0700, &ref_dfd, NULL, &error);
|
||||||
glnx_shutil_mkdir_p_at_open (repos_dfd, path, 0700, &ref_dfd, NULL, &error);
|
|
||||||
if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_EXISTS))
|
if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_EXISTS))
|
||||||
g_clear_error (&error);
|
g_clear_error (&error);
|
||||||
g_assert_no_error (error);
|
g_assert_no_error (error);
|
||||||
|
|
||||||
g_autoptr(GFile) mount_root = g_mount_get_root (repos_mount);
|
g_autoptr(GFile) mount_root = g_mount_get_root (repos_mount);
|
||||||
g_autoptr(GFile) repos_dir = g_file_get_child (mount_root, ".ostree/repos");
|
g_autoptr(GFile) repos_dir = g_file_get_child (mount_root, ".ostree/repos.d");
|
||||||
g_autoptr(GFile) repo_dir = g_file_get_child (repos_dir, path);
|
g_autoptr(GFile) repo_dir = g_file_get_child (repos_dir, repo_name);
|
||||||
|
|
||||||
va_start (args, out_uri);
|
va_start (args, out_uri);
|
||||||
repo = assert_create_remote_va (fixture, repo_dir, args);
|
repo = assert_create_remote_va (fixture, repo_dir, args);
|
||||||
|
|
@ -269,38 +266,19 @@ assert_create_repo_dir (Fixture *fixture,
|
||||||
return g_steal_pointer (&repo);
|
return g_steal_pointer (&repo);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Create a @ref symlink under the given @repos_dfd, pointing to
|
/* Create a @repo_name symlink under the given @repos_dfd, pointing to
|
||||||
* @symlink_target, or abort. */
|
* @symlink_target_path, or abort. */
|
||||||
static int
|
static void
|
||||||
assert_create_repo_symlink (int repos_dfd,
|
assert_create_repo_symlink (int repos_dfd,
|
||||||
const OstreeCollectionRef *ref,
|
const char *repo_name,
|
||||||
const gchar *symlink_target_path)
|
const char *symlink_target_path)
|
||||||
{
|
{
|
||||||
glnx_fd_close int symlink_target_dfd = -1;
|
if (TEMP_FAILURE_RETRY (symlinkat (symlink_target_path, repos_dfd, repo_name)) != 0)
|
||||||
g_autoptr(GError) error = NULL;
|
|
||||||
|
|
||||||
/* The @ref_parent_dir is not necessarily @collection_dir, since @ref may
|
|
||||||
* contain slashes. */
|
|
||||||
g_autofree gchar *path = g_build_filename (ref->collection_id, ref->ref_name, NULL);
|
|
||||||
g_autofree gchar *path_parent = g_path_get_dirname (path);
|
|
||||||
|
|
||||||
glnx_shutil_mkdir_p_at (repos_dfd, path_parent, 0700, NULL, &error);
|
|
||||||
if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_EXISTS))
|
|
||||||
g_clear_error (&error);
|
|
||||||
g_assert_no_error (error);
|
|
||||||
|
|
||||||
if (TEMP_FAILURE_RETRY (symlinkat (symlink_target_path, repos_dfd, path)) != 0)
|
|
||||||
{
|
{
|
||||||
g_autoptr(GError) error = NULL;
|
g_autoptr(GError) error = NULL;
|
||||||
glnx_throw_errno_prefix (&error, "symlinkat");
|
glnx_throw_errno_prefix (&error, "symlinkat");
|
||||||
g_assert_no_error (error);
|
g_assert_no_error (error);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Return a dir FD for the symlink target. */
|
|
||||||
glnx_opendirat (repos_dfd, path, TRUE, &symlink_target_dfd, &error);
|
|
||||||
g_assert_no_error (error);
|
|
||||||
|
|
||||||
return glnx_steal_fd (&symlink_target_dfd);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Add configuration for a remote named @remote_name, at @remote_uri, with a
|
/* Add configuration for a remote named @remote_name, at @remote_uri, with a
|
||||||
|
|
@ -350,7 +328,7 @@ test_repo_finder_mount_mixed_mounts (Fixture *fixture,
|
||||||
g_autofree gchar *repo2_repo_a_uri = NULL;
|
g_autofree gchar *repo2_repo_a_uri = NULL;
|
||||||
g_autofree gchar *repo1_ref0_checksum = NULL, *repo1_ref1_checksum = NULL, *repo1_ref2_checksum = NULL;
|
g_autofree gchar *repo1_ref0_checksum = NULL, *repo1_ref1_checksum = NULL, *repo1_ref2_checksum = NULL;
|
||||||
g_autofree gchar *repo2_ref0_checksum = NULL, *repo2_ref1_checksum = NULL, *repo2_ref2_checksum = NULL;
|
g_autofree gchar *repo2_ref0_checksum = NULL, *repo2_ref1_checksum = NULL, *repo2_ref2_checksum = NULL;
|
||||||
g_autofree gchar *repo1_ref5_checksum = NULL;
|
g_autofree gchar *repo1_ref5_checksum = NULL, *repo2_ref3_checksum = NULL;
|
||||||
gsize i;
|
gsize i;
|
||||||
const OstreeCollectionRef ref0 = { "org.example.Collection1", "exampleos/x86_64/ref0" };
|
const OstreeCollectionRef ref0 = { "org.example.Collection1", "exampleos/x86_64/ref0" };
|
||||||
const OstreeCollectionRef ref1 = { "org.example.Collection1", "exampleos/x86_64/ref1" };
|
const OstreeCollectionRef ref1 = { "org.example.Collection1", "exampleos/x86_64/ref1" };
|
||||||
|
|
@ -373,27 +351,26 @@ test_repo_finder_mount_mixed_mounts (Fixture *fixture,
|
||||||
assert_create_repos_dir (fixture, "no-repos-mount", &no_repos_repos, &no_repos_mount);
|
assert_create_repos_dir (fixture, "no-repos-mount", &no_repos_repos, &no_repos_mount);
|
||||||
|
|
||||||
assert_create_repos_dir (fixture, "repo1-mount", &repo1_repos, &repo1_mount);
|
assert_create_repos_dir (fixture, "repo1-mount", &repo1_repos, &repo1_mount);
|
||||||
repo1_repo_a = assert_create_repo_dir (fixture, repo1_repos, repo1_mount, refs[0], &repo1_repo_a_uri,
|
repo1_repo_a = assert_create_repo_dir (fixture, repo1_repos, repo1_mount, "repo1-repo-a", &repo1_repo_a_uri,
|
||||||
refs[0], &repo1_ref0_checksum,
|
refs[0], &repo1_ref0_checksum,
|
||||||
refs[2], &repo1_ref2_checksum,
|
refs[2], &repo1_ref2_checksum,
|
||||||
refs[5], &repo1_ref5_checksum,
|
refs[5], &repo1_ref5_checksum,
|
||||||
NULL);
|
NULL);
|
||||||
repo1_repo_b = assert_create_repo_dir (fixture, repo1_repos, repo1_mount, refs[1], &repo1_repo_b_uri,
|
repo1_repo_b = assert_create_repo_dir (fixture, repo1_repos, repo1_mount, "repo1-repo-b", &repo1_repo_b_uri,
|
||||||
refs[1], &repo1_ref1_checksum,
|
refs[1], &repo1_ref1_checksum,
|
||||||
NULL);
|
NULL);
|
||||||
assert_create_repo_symlink (repo1_repos, refs[2], "ref0"); /* repo1_repo_a */
|
assert_create_repo_symlink (repo1_repos, "repo1-repo-a-alias", "repo1-repo-a");
|
||||||
assert_create_repo_symlink (repo1_repos, refs[5], "../../../org.example.Collection1/exampleos/x86_64/ref0"); /* repo1_repo_a */
|
|
||||||
|
|
||||||
assert_create_repos_dir (fixture, "repo2-mount", &repo2_repos, &repo2_mount);
|
assert_create_repos_dir (fixture, "repo2-mount", &repo2_repos, &repo2_mount);
|
||||||
repo2_repo_a = assert_create_repo_dir (fixture, repo2_repos, repo2_mount, refs[0], &repo2_repo_a_uri,
|
repo2_repo_a = assert_create_repo_dir (fixture, repo2_repos, repo2_mount, "repo2-repo-a", &repo2_repo_a_uri,
|
||||||
refs[0], &repo2_ref0_checksum,
|
refs[0], &repo2_ref0_checksum,
|
||||||
refs[1], &repo2_ref1_checksum,
|
refs[1], &repo2_ref1_checksum,
|
||||||
refs[2], &repo2_ref2_checksum,
|
refs[2], &repo2_ref2_checksum,
|
||||||
refs[3], NULL,
|
refs[3], &repo2_ref3_checksum,
|
||||||
NULL);
|
NULL);
|
||||||
assert_create_repo_symlink (repo2_repos, refs[1], "ref0"); /* repo2_repo_a */
|
assert_create_repo_symlink (repo2_repos, "repo2-repo-a-alias", "repo2-repo-a");
|
||||||
assert_create_repo_symlink (repo2_repos, refs[2], "ref1"); /* repo2_repo_b */
|
assert_create_repo_symlink (repo2_repos, "dangling-symlink", "repo2-repo-b");
|
||||||
assert_create_repo_symlink (repo2_repos, refs[3], "/");
|
assert_create_repo_symlink (repo2_repos, "root", "/");
|
||||||
|
|
||||||
mounts = g_list_prepend (mounts, non_removable_mount);
|
mounts = g_list_prepend (mounts, non_removable_mount);
|
||||||
mounts = g_list_prepend (mounts, no_repos_mount);
|
mounts = g_list_prepend (mounts, no_repos_mount);
|
||||||
|
|
@ -456,10 +433,108 @@ test_repo_finder_mount_mixed_mounts (Fixture *fixture,
|
||||||
else if (g_strcmp0 (uri, repo2_repo_a_uri) == 0 &&
|
else if (g_strcmp0 (uri, repo2_repo_a_uri) == 0 &&
|
||||||
g_strcmp0 (keyring, "remote1.trustedkeys.gpg") == 0)
|
g_strcmp0 (keyring, "remote1.trustedkeys.gpg") == 0)
|
||||||
{
|
{
|
||||||
g_assert_cmpuint (g_hash_table_size (result->ref_to_checksum), ==, 3);
|
g_assert_cmpuint (g_hash_table_size (result->ref_to_checksum), ==, 4);
|
||||||
g_assert_cmpstr (g_hash_table_lookup (result->ref_to_checksum, refs[0]), ==, repo2_ref0_checksum);
|
g_assert_cmpstr (g_hash_table_lookup (result->ref_to_checksum, refs[0]), ==, repo2_ref0_checksum);
|
||||||
g_assert_cmpstr (g_hash_table_lookup (result->ref_to_checksum, refs[1]), ==, repo2_ref1_checksum);
|
g_assert_cmpstr (g_hash_table_lookup (result->ref_to_checksum, refs[1]), ==, repo2_ref1_checksum);
|
||||||
g_assert_cmpstr (g_hash_table_lookup (result->ref_to_checksum, refs[2]), ==, repo2_ref2_checksum);
|
g_assert_cmpstr (g_hash_table_lookup (result->ref_to_checksum, refs[2]), ==, repo2_ref2_checksum);
|
||||||
|
g_assert_cmpstr (g_hash_table_lookup (result->ref_to_checksum, refs[3]), ==, repo2_ref3_checksum);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
g_test_message ("Unknown result ‘%s’ with keyring ‘%s’.",
|
||||||
|
result->remote->name, result->remote->keyring);
|
||||||
|
g_assert_not_reached ();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
g_main_context_pop_thread_default (context);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Test resolving the refs against a mock volume which contains two repositories
|
||||||
|
* in the default repository paths ostree/repo and .ostree/repo, to check that
|
||||||
|
* those paths are read */
|
||||||
|
static void
|
||||||
|
test_repo_finder_mount_well_known (Fixture *fixture,
|
||||||
|
gconstpointer test_data)
|
||||||
|
{
|
||||||
|
g_autoptr(OstreeRepoFinderMount) finder = NULL;
|
||||||
|
g_autoptr(GVolumeMonitor) monitor = NULL;
|
||||||
|
g_autoptr(GMainContext) context = NULL;
|
||||||
|
g_autoptr(GAsyncResult) result = NULL;
|
||||||
|
g_autoptr(GPtrArray) results = NULL; /* (element-type OstreeRepoFinderResult) */
|
||||||
|
g_autoptr(GError) error = NULL;
|
||||||
|
g_autoptr(GList) mounts = NULL; /* (element-type OstreeMockMount) */
|
||||||
|
g_autoptr(GMount) mount = NULL;
|
||||||
|
glnx_fd_close int repos = -1;
|
||||||
|
g_autoptr(OstreeRepo) repo_a = NULL, repo_b = NULL;
|
||||||
|
g_autofree gchar *repo_a_uri = NULL, *repo_b_uri = NULL;
|
||||||
|
g_autofree gchar *ref_a_checksum = NULL, *ref_b_checksum = NULL;
|
||||||
|
gsize i;
|
||||||
|
const OstreeCollectionRef ref_a = { "org.example.Collection1", "refA" };
|
||||||
|
const OstreeCollectionRef ref_b = { "org.example.Collection2", "refB" };
|
||||||
|
const OstreeCollectionRef * const refs[] = { &ref_a, &ref_b, NULL };
|
||||||
|
|
||||||
|
context = g_main_context_new ();
|
||||||
|
g_main_context_push_thread_default (context);
|
||||||
|
|
||||||
|
/* Build the various mock drives/volumes/mounts, and some repositories with
|
||||||
|
* refs within them. We use "/" under the assumption that it’s on a separate
|
||||||
|
* file system from /tmp, so it’s an example of a symlink pointing outside
|
||||||
|
* its mount point. */
|
||||||
|
assert_create_repos_dir (fixture, "mount", &repos, &mount);
|
||||||
|
repo_a = assert_create_repo_dir (fixture, repos, mount, "../../ostree/repo", &repo_a_uri,
|
||||||
|
&ref_a, &ref_a_checksum,
|
||||||
|
NULL);
|
||||||
|
repo_b = assert_create_repo_dir (fixture, repos, mount, "../../.ostree/repo", &repo_b_uri,
|
||||||
|
&ref_b, &ref_b_checksum,
|
||||||
|
NULL);
|
||||||
|
assert_create_repo_symlink (repos, "repo-a-alias", "../../ostree/repo");
|
||||||
|
|
||||||
|
mounts = g_list_prepend (mounts, mount);
|
||||||
|
|
||||||
|
monitor = ostree_mock_volume_monitor_new (mounts, NULL);
|
||||||
|
finder = ostree_repo_finder_mount_new (monitor);
|
||||||
|
|
||||||
|
assert_create_remote_config (fixture->parent_repo, "remote1", "https://nope1", "org.example.Collection1");
|
||||||
|
assert_create_remote_config (fixture->parent_repo, "remote2", "https://nope2", "org.example.Collection2");
|
||||||
|
|
||||||
|
/* Resolve the refs. */
|
||||||
|
ostree_repo_finder_resolve_async (OSTREE_REPO_FINDER (finder), refs,
|
||||||
|
fixture->parent_repo,
|
||||||
|
NULL, result_cb, &result);
|
||||||
|
|
||||||
|
while (result == NULL)
|
||||||
|
g_main_context_iteration (context, TRUE);
|
||||||
|
|
||||||
|
results = ostree_repo_finder_resolve_finish (OSTREE_REPO_FINDER (finder),
|
||||||
|
result, &error);
|
||||||
|
g_assert_no_error (error);
|
||||||
|
g_assert_nonnull (results);
|
||||||
|
g_assert_cmpuint (results->len, ==, 2);
|
||||||
|
|
||||||
|
/* Check that the results are correct: the valid results canonicalised and
|
||||||
|
* deduplicated. */
|
||||||
|
for (i = 0; i < results->len; i++)
|
||||||
|
{
|
||||||
|
g_autofree gchar *uri = NULL;
|
||||||
|
const gchar *keyring;
|
||||||
|
const OstreeRepoFinderResult *result = g_ptr_array_index (results, i);
|
||||||
|
|
||||||
|
uri = g_key_file_get_string (result->remote->options, result->remote->group, "url", &error);
|
||||||
|
g_assert_no_error (error);
|
||||||
|
keyring = result->remote->keyring;
|
||||||
|
|
||||||
|
if (g_strcmp0 (uri, repo_a_uri) == 0 &&
|
||||||
|
g_strcmp0 (keyring, "remote1.trustedkeys.gpg") == 0)
|
||||||
|
{
|
||||||
|
g_assert_cmpuint (g_hash_table_size (result->ref_to_checksum), ==, 1);
|
||||||
|
g_assert_cmpstr (g_hash_table_lookup (result->ref_to_checksum, &ref_a), ==, ref_a_checksum);
|
||||||
|
}
|
||||||
|
else if (g_strcmp0 (uri, repo_b_uri) == 0 &&
|
||||||
|
g_strcmp0 (keyring, "remote2.trustedkeys.gpg") == 0)
|
||||||
|
{
|
||||||
|
g_assert_cmpuint (g_hash_table_size (result->ref_to_checksum), ==, 1);
|
||||||
|
g_assert_cmpstr (g_hash_table_lookup (result->ref_to_checksum, &ref_b), ==, ref_b_checksum);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -482,6 +557,8 @@ int main (int argc, char **argv)
|
||||||
test_repo_finder_mount_no_mounts, teardown);
|
test_repo_finder_mount_no_mounts, teardown);
|
||||||
g_test_add ("/repo-finder-mount/mixed-mounts", Fixture, NULL, setup,
|
g_test_add ("/repo-finder-mount/mixed-mounts", Fixture, NULL, setup,
|
||||||
test_repo_finder_mount_mixed_mounts, teardown);
|
test_repo_finder_mount_mixed_mounts, teardown);
|
||||||
|
g_test_add ("/repo-finder-mount/well-known", Fixture, NULL, setup,
|
||||||
|
test_repo_finder_mount_well_known, teardown);
|
||||||
|
|
||||||
return g_test_run();
|
return g_test_run();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue