* #OstreeRepoFinderMount is an implementation of #OstreeRepoFinder which looks
* refs up in well-known locations on any mounted removable volumes.
*
- * For an #OstreeCollectionRef, (`C`, `R`), it checks whether `.ostree/repos/C/R`
- * exists and is an OSTree repository on each mounted removable volume. Collection
- * IDs and ref names are not escaped when building the path, so if either
- * contains `/` in its name, the repository will be checked for in a
- * subdirectory of `.ostree/repos`. Non-removable volumes are ignored.
+ * 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 resolving the refs, so a volume might contain a
- * single OSTree at some arbitrary path, with a number of refs linking to it
- * from `.ostree/repos`. Any symlink which points outside the volume’s file
+ * 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
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,
continue;
}
- /* Check if it contains a .ostree/repos directory. */
mount_root = g_mount_get_root (mount);
mount_root_path = g_file_get_path (mount_root);
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
* repositories for individual refs are on the same device (to avoid the
* symlinks for them pointing outside the mount root). */
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.
for (i = 0; refs[i] != NULL; i++)
{
- struct stat stbuf;
- g_autofree gchar *collection_and_ref = NULL;
+ const OstreeCollectionRef *ref = refs[i];
g_autofree gchar *resolved_repo_uri = NULL;
g_autofree gchar *keyring = NULL;
g_autoptr(UriAndKeyring) resolved_repo = NULL;
- collection_and_ref = g_build_filename (refs[i]->collection_id, refs[i]->ref_name, NULL);
-
- 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",
- refs[i]->collection_id, refs[i]->ref_name, mount_name, collection_and_ref, local_error->message);
- g_clear_error (&local_error);
- continue;
- }
-
- if ((stbuf.st_mode & S_IFMT) != S_IFDIR)
- {
- 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)
+ for (gsize j = 0; j < repos_refs->len; j++)
{
- 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;
+ 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;
}
-
- /* 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)
- {
- g_debug ("Ignoring ref (%s, %s) on mount ‘%s’ as its repository doesn’t contain the ref.",
- refs[i]->collection_id, refs[i]->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, refs[i]->collection_id,
- cancellable, &local_error);
-
- if (keyring == NULL)
- {
- g_debug ("Ignoring ref (%s, %s) on mount ‘%s’ due to missing keyring: %s",
- refs[i]->collection_id, refs[i]->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/$refs[i]->collection_id/$refs[i]->ref_name.
- * Add it to the results, keyed by the canonicalised repository URI
- * to deduplicate the results. */
-
- g_autofree char *repo_abspath = g_build_filename (mount_root_path, ".ostree/repos",
- 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’.",
- refs[i]->collection_id, refs[i]->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) refs[i], g_strdup (checksum));
}
/* Aggregate the results. */
teardown (Fixture *fixture,
gconstpointer test_data)
{
- g_autoptr(GError) error = NULL;
-
/* Recursively remove the temporary directory. */
(void)glnx_tmpdir_delete (&fixture->tmpdir, NULL, NULL);
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
assert_create_repos_dir (Fixture *fixture,
const gchar *mount_root_name,
glnx_fd_close int repos_dfd = -1;
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);
if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_EXISTS))
g_clear_error (&error);
}
static OstreeRepo *
-assert_create_repo_dir (Fixture *fixture,
- int repos_dfd,
- GMount *repos_mount,
- const OstreeCollectionRef *ref,
- gchar **out_uri,
+assert_create_repo_dir (Fixture *fixture,
+ int repos_dfd,
+ GMount *repos_mount,
+ const char *repo_name,
+ gchar **out_uri,
...) G_GNUC_NULL_TERMINATED;
-/* Create a @ref directory under the given @repos_dfd, or abort. Create a new
- * repository in it with the refs given in @..., as per assert_create_remote_va().
- * Return the URI of the repository. */
+/* Create a @repo_name directory under the given @repos_dfd, or abort. Create a
+ * new repository in it with the refs given in @..., as per
+ * assert_create_remote_va(). Return the URI of the repository. */
static OstreeRepo *
-assert_create_repo_dir (Fixture *fixture,
- int repos_dfd,
- GMount *repos_mount,
- const OstreeCollectionRef *ref,
- gchar **out_uri,
+assert_create_repo_dir (Fixture *fixture,
+ int repos_dfd,
+ GMount *repos_mount,
+ const char *repo_name,
+ gchar **out_uri,
...)
{
glnx_fd_close int ref_dfd = -1;
g_autoptr(GError) error = NULL;
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, path, 0700, &ref_dfd, NULL, &error);
+ glnx_shutil_mkdir_p_at_open (repos_dfd, repo_name, 0700, &ref_dfd, NULL, &error);
if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_EXISTS))
g_clear_error (&error);
g_assert_no_error (error);
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) repo_dir = g_file_get_child (repos_dir, path);
+ 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, repo_name);
va_start (args, out_uri);
repo = assert_create_remote_va (fixture, repo_dir, args);
return g_steal_pointer (&repo);
}
-/* Create a @ref symlink under the given @repos_dfd, pointing to
- * @symlink_target, or abort. */
-static int
-assert_create_repo_symlink (int repos_dfd,
- const OstreeCollectionRef *ref,
- const gchar *symlink_target_path)
+/* Create a @repo_name symlink under the given @repos_dfd, pointing to
+ * @symlink_target_path, or abort. */
+static void
+assert_create_repo_symlink (int repos_dfd,
+ const char *repo_name,
+ const char *symlink_target_path)
{
- glnx_fd_close int symlink_target_dfd = -1;
- 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)
+ if (TEMP_FAILURE_RETRY (symlinkat (symlink_target_path, repos_dfd, repo_name)) != 0)
{
g_autoptr(GError) error = NULL;
glnx_throw_errno_prefix (&error, "symlinkat");
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
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 *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;
const OstreeCollectionRef ref0 = { "org.example.Collection1", "exampleos/x86_64/ref0" };
const OstreeCollectionRef ref1 = { "org.example.Collection1", "exampleos/x86_64/ref1" };
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);
- 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[2], &repo1_ref2_checksum,
refs[5], &repo1_ref5_checksum,
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,
NULL);
- assert_create_repo_symlink (repo1_repos, refs[2], "ref0"); /* repo1_repo_a */
- assert_create_repo_symlink (repo1_repos, refs[5], "../../../org.example.Collection1/exampleos/x86_64/ref0"); /* repo1_repo_a */
+ assert_create_repo_symlink (repo1_repos, "repo1-repo-a-alias", "repo1-repo-a");
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[1], &repo2_ref1_checksum,
refs[2], &repo2_ref2_checksum,
- refs[3], NULL,
+ refs[3], &repo2_ref3_checksum,
NULL);
- assert_create_repo_symlink (repo2_repos, refs[1], "ref0"); /* repo2_repo_a */
- assert_create_repo_symlink (repo2_repos, refs[2], "ref1"); /* repo2_repo_b */
- assert_create_repo_symlink (repo2_repos, refs[3], "/");
+ assert_create_repo_symlink (repo2_repos, "repo2-repo-a-alias", "repo2-repo-a");
+ assert_create_repo_symlink (repo2_repos, "dangling-symlink", "repo2-repo-b");
+ assert_create_repo_symlink (repo2_repos, "root", "/");
mounts = g_list_prepend (mounts, non_removable_mount);
mounts = g_list_prepend (mounts, no_repos_mount);
else if (g_strcmp0 (uri, repo2_repo_a_uri) == 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[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[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
{
test_repo_finder_mount_no_mounts, teardown);
g_test_add ("/repo-finder-mount/mixed-mounts", Fixture, NULL, setup,
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();
}