/* SELinux uses PCRE pre-compiled regexps for binary caches, which can * fail if the version of PCRE on the host differs from the version * which generated the cache (in the target root). * * Note also this function is probably already broken in Fedora * 23+ from https://bugzilla.redhat.com/show_bug.cgi?id=1265406 */ static gboolean workaround_selinux_cross_labeling_recurse (int dfd, const char *path, GCancellable *cancellable, GError **error) { gboolean ret = FALSE; g_auto(GLnxDirFdIterator) dfd_iter = { 0, }; if (!glnx_dirfd_iterator_init_at (dfd, path, TRUE, &dfd_iter, error)) goto out; while (TRUE) { struct dirent *dent = NULL; const char *name; if (!glnx_dirfd_iterator_next_dent_ensure_dtype (&dfd_iter, &dent, cancellable, error)) goto out; if (!dent) break; name = dent->d_name; if (dent->d_type == DT_DIR) { if (!workaround_selinux_cross_labeling_recurse (dfd_iter.fd, name, cancellable, error)) goto out; } else if (g_str_has_suffix (name, ".bin")) { struct stat stbuf; const char *lastdot; gs_free char *nonbin_name = NULL; if (TEMP_FAILURE_RETRY (fstatat (dfd_iter.fd, name, &stbuf, AT_SYMLINK_NOFOLLOW)) != 0) { glnx_set_error_from_errno (error); goto out; } lastdot = strrchr (name, '.'); g_assert (lastdot); nonbin_name = g_strndup (name, lastdot - name); g_print ("Setting mtime of '%s' to newer than '%s'\n", nonbin_name, name); if (TEMP_FAILURE_RETRY (utimensat (dfd_iter.fd, nonbin_name, NULL, 0)) == -1) { glnx_set_error_from_errno (error); goto out; } } } ret = TRUE; out: return ret; }
static gpointer xattr_thread (gpointer data) { g_autoptr(GError) local_error = NULL; GError **error = &local_error; struct XattrWorker *worker = data; guint64 end_time = g_get_monotonic_time () + XATTR_THREAD_RUN_TIME_USECS; guint n_read = 0; while (g_get_monotonic_time () < end_time) { g_auto(GLnxDirFdIterator) dfd_iter = { 0, }; if (!glnx_dirfd_iterator_init_at (worker->dfd, ".", TRUE, &dfd_iter, error)) goto out; if (worker->is_writer) { if (!do_write_run (&dfd_iter, error)) goto out; } else { if (!do_read_run (&dfd_iter, &n_read, error)) goto out; } } out: g_assert_no_error (local_error); return GINT_TO_POINTER (n_read); }
/** * ostree_repo_checkout_gc: * @self: Repo * @cancellable: Cancellable * @error: Error * * Call this after finishing a succession of checkout operations; it * will delete any currently-unused uncompressed objects from the * cache. */ gboolean ostree_repo_checkout_gc (OstreeRepo *self, GCancellable *cancellable, GError **error) { gboolean ret = FALSE; g_autoptr(GHashTable) to_clean_dirs = NULL; GHashTableIter iter; gpointer key, value; g_mutex_lock (&self->cache_lock); to_clean_dirs = self->updated_uncompressed_dirs; self->updated_uncompressed_dirs = g_hash_table_new (NULL, NULL); g_mutex_unlock (&self->cache_lock); if (to_clean_dirs) g_hash_table_iter_init (&iter, to_clean_dirs); while (to_clean_dirs && g_hash_table_iter_next (&iter, &key, &value)) { g_autofree char *objdir_name = g_strdup_printf ("%02x", GPOINTER_TO_UINT (key)); g_auto(GLnxDirFdIterator) dfd_iter = { 0, }; if (!glnx_dirfd_iterator_init_at (self->uncompressed_objects_dir_fd, objdir_name, FALSE, &dfd_iter, error)) goto out; while (TRUE) { struct dirent *dent; struct stat stbuf; if (!glnx_dirfd_iterator_next_dent (&dfd_iter, &dent, cancellable, error)) goto out; if (dent == NULL) break; if (fstatat (dfd_iter.fd, dent->d_name, &stbuf, AT_SYMLINK_NOFOLLOW) != 0) { glnx_set_error_from_errno (error); goto out; } if (stbuf.st_nlink == 1) { if (unlinkat (dfd_iter.fd, dent->d_name, 0) != 0) { glnx_set_error_from_errno (error); goto out; } } } } ret = TRUE; out: return ret; }
static GString * get_directory_listing (int dfd, const char *path) { g_autoptr(GPtrArray) entries = g_ptr_array_new_with_free_func (g_free); g_auto(GLnxDirFdIterator) dfd_iter = { 0, }; g_autoptr(GError) local_error = NULL; GError **error = &local_error; guint i; char *escaped; GString *listing; listing = g_string_new ("<html>\r\n"); if (!glnx_dirfd_iterator_init_at (dfd, path, FALSE, &dfd_iter, error)) goto out; while (TRUE) { struct dirent *dent; if (!glnx_dirfd_iterator_next_dent (&dfd_iter, &dent, NULL, error)) goto out; if (dent == NULL) break; escaped = g_markup_escape_text (dent->d_name, -1); g_ptr_array_add (entries, escaped); } g_ptr_array_sort (entries, (GCompareFunc)compare_strings); escaped = g_markup_escape_text (strchr (path, '/'), -1); g_string_append_printf (listing, "<head><title>Index of %s</title></head>\r\n", escaped); g_string_append_printf (listing, "<body><h1>Index of %s</h1>\r\n<p>\r\n", escaped); g_free (escaped); for (i = 0; i < entries->len; i++) { g_string_append_printf (listing, "<a href=\"%s\">%s</a><br>\r\n", (char *)entries->pdata[i], (char *)entries->pdata[i]); g_free (g_steal_pointer (&entries->pdata[i])); } g_string_append (listing, "</body>\r\n</html>\r\n"); out: if (local_error) g_printerr ("%s\n", local_error->message); return listing; }
static gboolean enumerate_refs_recurse (OstreeRepo *repo, const char *remote, int base_dfd, GString *base_path, int child_dfd, const char *path, GHashTable *refs, GCancellable *cancellable, GError **error) { g_auto(GLnxDirFdIterator) dfd_iter = { 0, }; if (!glnx_dirfd_iterator_init_at (child_dfd, path, FALSE, &dfd_iter, error)) return FALSE; while (TRUE) { guint len = base_path->len; struct dirent *dent = NULL; if (!glnx_dirfd_iterator_next_dent_ensure_dtype (&dfd_iter, &dent, cancellable, error)) return FALSE; if (dent == NULL) break; g_string_append (base_path, dent->d_name); if (dent->d_type == DT_DIR) { g_string_append_c (base_path, '/'); if (!enumerate_refs_recurse (repo, remote, base_dfd, base_path, dfd_iter.fd, dent->d_name, refs, cancellable, error)) return FALSE; } else if (dent->d_type == DT_REG) { if (!add_ref_to_set (remote, base_dfd, base_path->str, refs, cancellable, error)) return FALSE; } g_string_truncate (base_path, len); } return TRUE; }
static gboolean find_ref_in_remotes (OstreeRepo *self, const char *rev, int *out_fd, GError **error) { g_auto(GLnxDirFdIterator) dfd_iter = { 0, }; glnx_fd_close int ret_fd = -1; if (!glnx_dirfd_iterator_init_at (self->repo_dir_fd, "refs/remotes", TRUE, &dfd_iter, error)) return FALSE; while (TRUE) { struct dirent *dent = NULL; glnx_fd_close int remote_dfd = -1; if (!glnx_dirfd_iterator_next_dent_ensure_dtype (&dfd_iter, &dent, NULL, error)) return FALSE; if (dent == NULL) break; if (dent->d_type != DT_DIR) continue; if (!glnx_opendirat (dfd_iter.fd, dent->d_name, TRUE, &remote_dfd, error)) return FALSE; if (!ot_openat_ignore_enoent (remote_dfd, rev, &ret_fd, error)) return FALSE; if (ret_fd != -1) break; } *out_fd = ret_fd; ret_fd = -1; return TRUE; }
static gboolean convert_var_to_tmpfiles_d_recurse (GOutputStream *tmpfiles_out, int dfd, GString *prefix, GCancellable *cancellable, GError **error) { gboolean ret = FALSE; g_auto(GLnxDirFdIterator) dfd_iter = { 0, }; gsize bytes_written; if (!glnx_dirfd_iterator_init_at (dfd, prefix->str + 1, TRUE, &dfd_iter, error)) goto out; while (TRUE) { struct dirent *dent = NULL; GString *tmpfiles_d_buf; gs_free char *tmpfiles_d_line = NULL; char filetype_c; gs_free char *relpath = NULL; if (!glnx_dirfd_iterator_next_dent_ensure_dtype (&dfd_iter, &dent, cancellable, error)) goto out; if (!dent) break; switch (dent->d_type) { case DT_DIR: filetype_c = 'd'; break; case DT_LNK: filetype_c = 'L'; break; default: g_print ("Ignoring non-directory/non-symlink '%s'\n", dent->d_name); continue; } tmpfiles_d_buf = g_string_new (""); g_string_append_c (tmpfiles_d_buf, filetype_c); g_string_append_c (tmpfiles_d_buf, ' '); g_string_append (tmpfiles_d_buf, prefix->str); g_string_append_c (tmpfiles_d_buf, '/'); g_string_append (tmpfiles_d_buf, dent->d_name); if (filetype_c == 'd') { struct stat stbuf; if (TEMP_FAILURE_RETRY (fstatat (dfd_iter.fd, dent->d_name, &stbuf, AT_SYMLINK_NOFOLLOW)) != 0) { glnx_set_error_from_errno (error); goto out; } g_string_append_printf (tmpfiles_d_buf, " 0%02o", stbuf.st_mode & ~S_IFMT); g_string_append_printf (tmpfiles_d_buf, " %d %d - -", stbuf.st_uid, stbuf.st_gid); /* Push prefix */ g_string_append_c (prefix, '/'); g_string_append (prefix, dent->d_name); if (!convert_var_to_tmpfiles_d_recurse (tmpfiles_out, dfd, prefix, cancellable, error)) goto out; /* Pop prefix */ { char *r = memrchr (prefix->str, '/', prefix->len); g_assert (r != NULL); g_string_truncate (prefix, r - prefix->str); } } else { g_autofree char *link = glnx_readlinkat_malloc (dfd_iter.fd, dent->d_name, cancellable, error); if (!link) goto out; g_string_append (tmpfiles_d_buf, " - - - - "); g_string_append (tmpfiles_d_buf, link); } g_string_append_c (tmpfiles_d_buf, '\n'); tmpfiles_d_line = g_string_free (tmpfiles_d_buf, FALSE); if (!g_output_stream_write_all (tmpfiles_out, tmpfiles_d_line, strlen (tmpfiles_d_line), &bytes_written, cancellable, error)) goto out; } ret = TRUE; out: return ret; }
/** * ostree_repo_list_static_delta_names: * @self: Repo * @out_deltas: (out) (element-type utf8) (transfer container): String name of deltas (checksum-checksum.delta) * @cancellable: Cancellable * @error: Error * * This function synchronously enumerates all static deltas in the * repository, returning its result in @out_deltas. */ gboolean ostree_repo_list_static_delta_names (OstreeRepo *self, GPtrArray **out_deltas, GCancellable *cancellable, GError **error) { g_autoptr(GPtrArray) ret_deltas = g_ptr_array_new_with_free_func (g_free); g_auto(GLnxDirFdIterator) dfd_iter = { 0, }; gboolean exists; if (!ot_dfd_iter_init_allow_noent (self->repo_dir_fd, "deltas", &dfd_iter, &exists, error)) return FALSE; if (!exists) { /* Note early return */ ot_transfer_out_value (out_deltas, &ret_deltas); return TRUE; } while (TRUE) { g_auto(GLnxDirFdIterator) sub_dfd_iter = { 0, }; struct dirent *dent; if (!glnx_dirfd_iterator_next_dent_ensure_dtype (&dfd_iter, &dent, cancellable, error)) return FALSE; if (dent == NULL) break; if (dent->d_type != DT_DIR) continue; if (!glnx_dirfd_iterator_init_at (dfd_iter.fd, dent->d_name, FALSE, &sub_dfd_iter, error)) return FALSE; while (TRUE) { struct dirent *sub_dent; const char *name1; const char *name2; g_autofree char *superblock_subpath = NULL; struct stat stbuf; if (!glnx_dirfd_iterator_next_dent_ensure_dtype (&sub_dfd_iter, &sub_dent, cancellable, error)) return FALSE; if (sub_dent == NULL) break; if (dent->d_type != DT_DIR) continue; name1 = dent->d_name; name2 = sub_dent->d_name; superblock_subpath = g_strconcat (name2, "/superblock", NULL); if (fstatat (sub_dfd_iter.fd, superblock_subpath, &stbuf, 0) < 0) { if (errno != ENOENT) { glnx_set_error_from_errno (error); return FALSE; } } else { g_autofree char *buf = g_strconcat (name1, name2, NULL); GString *out = g_string_new (""); char checksum[OSTREE_SHA256_STRING_LEN+1]; guchar csum[OSTREE_SHA256_DIGEST_LEN]; const char *dash = strchr (buf, '-'); ostree_checksum_b64_inplace_to_bytes (buf, csum); ostree_checksum_inplace_from_bytes (csum, checksum); g_string_append (out, checksum); if (dash) { g_string_append_c (out, '-'); ostree_checksum_b64_inplace_to_bytes (dash+1, csum); ostree_checksum_inplace_from_bytes (csum, checksum); g_string_append (out, checksum); } g_ptr_array_add (ret_deltas, g_string_free (out, FALSE)); } } } ot_transfer_out_value (out_deltas, &ret_deltas); return TRUE; }
static gboolean export_dir (int source_parent_fd, const char *source_name, const char *source_relpath, int destination_parent_fd, const char *destination_name, const char *required_prefix, GCancellable *cancellable, GError **error) { int res; g_auto(GLnxDirFdIterator) source_iter = {0}; glnx_fd_close int destination_dfd = -1; struct dirent *dent; if (!glnx_dirfd_iterator_init_at (source_parent_fd, source_name, FALSE, &source_iter, error)) return FALSE; do res = mkdirat (destination_parent_fd, destination_name, 0755); while (G_UNLIKELY (res == -1 && errno == EINTR)); if (res == -1) { if (errno != EEXIST) { glnx_set_error_from_errno (error); return FALSE; } } if (!glnx_opendirat (destination_parent_fd, destination_name, TRUE, &destination_dfd, error)) return FALSE; while (TRUE) { struct stat stbuf; g_autofree char *source_printable = NULL; if (!glnx_dirfd_iterator_next_dent (&source_iter, &dent, cancellable, error)) return FALSE; if (dent == NULL) break; if (fstatat (source_iter.fd, dent->d_name, &stbuf, AT_SYMLINK_NOFOLLOW) == -1) { if (errno == ENOENT) { continue; } else { glnx_set_error_from_errno (error); return FALSE; } } /* Don't export any hidden files or backups */ if (g_str_has_prefix (dent->d_name, ".") || g_str_has_suffix (dent->d_name, "~")) continue; if (S_ISDIR (stbuf.st_mode)) { g_autofree gchar *child_relpath = g_build_filename (source_relpath, dent->d_name, NULL); if (!export_dir (source_iter.fd, dent->d_name, child_relpath, destination_dfd, dent->d_name, required_prefix, cancellable, error)) return FALSE; } else if (S_ISREG (stbuf.st_mode)) { source_printable = g_build_filename (source_relpath, dent->d_name, NULL); if (!flatpak_has_name_prefix (dent->d_name, required_prefix)) { g_print ("Not exporting %s, wrong prefix\n", source_printable); continue; } g_print ("Exporting %s\n", source_printable); if (!glnx_file_copy_at (source_iter.fd, dent->d_name, &stbuf, destination_dfd, dent->d_name, GLNX_FILE_COPY_NOXATTRS, cancellable, error)) return FALSE; } else { source_printable = g_build_filename (source_relpath, dent->d_name, NULL); g_debug ("Not exporting non-regular file %s\n", source_printable); } } /* Try to remove the directory, as we don't want to export empty directories. * However, don't fail if the unlink fails due to the directory not being empty */ do res = unlinkat (destination_parent_fd, destination_name, AT_REMOVEDIR); while (G_UNLIKELY (res == -1 && errno == EINTR)); if (res == -1) { if (errno != ENOTEMPTY) { glnx_set_error_from_errno (error); return FALSE; } } return TRUE; }
static gboolean export_dir (int source_parent_fd, const char *source_name, const char *source_relpath, int destination_parent_fd, const char *destination_name, const char *required_prefix, GCancellable *cancellable, GError **error) { gboolean ret = FALSE; int res; g_auto(GLnxDirFdIterator) source_iter = {0}; glnx_fd_close int destination_dfd = -1; struct dirent *dent; if (!glnx_dirfd_iterator_init_at (source_parent_fd, source_name, FALSE, &source_iter, error)) goto out; do res = mkdirat (destination_parent_fd, destination_name, 0777); while (G_UNLIKELY (res == -1 && errno == EINTR)); if (res == -1) { if (errno != EEXIST) { glnx_set_error_from_errno (error); goto out; } } if (!gs_file_open_dir_fd_at (destination_parent_fd, destination_name, &destination_dfd, cancellable, error)) goto out; while (TRUE) { struct stat stbuf; g_autofree char *source_printable = NULL; if (!glnx_dirfd_iterator_next_dent (&source_iter, &dent, cancellable, error)) goto out; if (dent == NULL) break; if (fstatat (source_iter.fd, dent->d_name, &stbuf, AT_SYMLINK_NOFOLLOW) == -1) { if (errno == ENOENT) continue; else { glnx_set_error_from_errno (error); goto out; } } if (S_ISDIR (stbuf.st_mode)) { g_autofree gchar *child_relpath = g_build_filename(source_relpath, dent->d_name, NULL); if (!export_dir (source_iter.fd, dent->d_name, child_relpath, destination_dfd, dent->d_name, required_prefix, cancellable, error)) goto out; } else if (S_ISREG (stbuf.st_mode)) { source_printable = g_build_filename (source_relpath, dent->d_name, NULL); if (!xdg_app_has_name_prefix (dent->d_name, required_prefix)) { g_print ("Not exporting %s, wrong prefix\n", source_printable); continue; } g_print ("Exporting %s\n", source_printable); if (!glnx_file_copy_at (source_iter.fd, dent->d_name, &stbuf, destination_dfd, dent->d_name, GLNX_FILE_COPY_NOXATTRS, cancellable, error)) goto out; } else { source_printable = g_build_filename (source_relpath, dent->d_name, NULL); g_print ("Not exporting non-regular file %s\n", source_printable); } } ret = TRUE; out: return ret; }
/* TODO: Add a man page. */ gboolean ostree_builtin_create_usb (int argc, char **argv, OstreeCommandInvocation *invocation, GCancellable *cancellable, GError **error) { g_autoptr(GOptionContext) context = NULL; g_autoptr(OstreeAsyncProgress) progress = NULL; g_auto(GLnxConsoleRef) console = { 0, }; context = g_option_context_new ("MOUNT-PATH COLLECTION-ID REF [COLLECTION-ID REF...]"); /* Parse options. */ g_autoptr(OstreeRepo) src_repo = NULL; if (!ostree_option_context_parse (context, options, &argc, &argv, invocation, &src_repo, cancellable, error)) return FALSE; if (argc < 2) { ot_util_usage_error (context, "A MOUNT-PATH must be specified", error); return FALSE; } if (argc < 4) { ot_util_usage_error (context, "At least one COLLECTION-ID REF pair must be specified", error); return FALSE; } if (argc % 2 == 1) { ot_util_usage_error (context, "Only complete COLLECTION-ID REF pairs may be specified", error); return FALSE; } /* Open the USB stick, which must exist. Allow automounting and following symlinks. */ const char *mount_root_path = argv[1]; struct stat mount_root_stbuf; glnx_autofd int mount_root_dfd = -1; if (!glnx_opendirat (AT_FDCWD, mount_root_path, TRUE, &mount_root_dfd, error)) return FALSE; if (!glnx_fstat (mount_root_dfd, &mount_root_stbuf, error)) return FALSE; /* Read in the refs to add to the USB stick. */ g_autoptr(GPtrArray) refs = g_ptr_array_new_full (argc, (GDestroyNotify) ostree_collection_ref_free); for (gsize i = 2; i < argc; i += 2) { if (!ostree_validate_collection_id (argv[i], error) || !ostree_validate_rev (argv[i + 1], error)) return FALSE; g_ptr_array_add (refs, ostree_collection_ref_new (argv[i], argv[i + 1])); } /* Open the destination repository on the USB stick or create it if it doesn’t exist. * Check it’s below @mount_root_path, and that it’s not the same as the source * repository. * * If the destination file system supports xattrs (for example, ext4), we use * a BARE_USER repository; if it doesn’t (for example, FAT), we use ARCHIVE. * In either case, we want a lossless repository. */ const char *dest_repo_path = (opt_destination_repo != NULL) ? opt_destination_repo : ".ostree/repo"; if (!glnx_shutil_mkdir_p_at (mount_root_dfd, dest_repo_path, 0755, cancellable, error)) return FALSE; OstreeRepoMode mode = OSTREE_REPO_MODE_BARE_USER; if (TEMP_FAILURE_RETRY (fgetxattr (mount_root_dfd, "user.test", NULL, 0)) < 0 && (errno == ENOTSUP || errno == EOPNOTSUPP)) mode = OSTREE_REPO_MODE_ARCHIVE; g_debug ("%s: Creating repository in mode %u", G_STRFUNC, mode); g_autoptr(OstreeRepo) dest_repo = ostree_repo_create_at (mount_root_dfd, dest_repo_path, mode, NULL, cancellable, error); if (dest_repo == NULL) return FALSE; struct stat dest_repo_stbuf; if (!glnx_fstat (ostree_repo_get_dfd (dest_repo), &dest_repo_stbuf, error)) return FALSE; if (dest_repo_stbuf.st_dev != mount_root_stbuf.st_dev) { ot_util_usage_error (context, "--destination-repo must be a descendent of MOUNT-PATH", error); return FALSE; } if (ostree_repo_equal (src_repo, dest_repo)) { ot_util_usage_error (context, "--destination-repo must not be the source repository", error); return FALSE; } if (!ostree_ensure_repo_writable (dest_repo, error)) return FALSE; if (opt_disable_fsync) ostree_repo_set_disable_fsync (dest_repo, TRUE); /* Copy across all of the collection–refs to the destination repo. */ GVariantBuilder refs_builder; g_variant_builder_init (&refs_builder, G_VARIANT_TYPE ("a(sss)")); for (gsize i = 0; i < refs->len; i++) { const OstreeCollectionRef *ref = g_ptr_array_index (refs, i); g_variant_builder_add (&refs_builder, "(sss)", ref->collection_id, ref->ref_name, ""); } { GVariantBuilder builder; g_autoptr(GVariant) opts = NULL; OstreeRepoPullFlags flags = OSTREE_REPO_PULL_FLAGS_MIRROR; glnx_console_lock (&console); if (console.is_tty) progress = ostree_async_progress_new_and_connect (ostree_repo_pull_default_console_progress_changed, &console); g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}")); g_variant_builder_add (&builder, "{s@v}", "collection-refs", g_variant_new_variant (g_variant_builder_end (&refs_builder))); g_variant_builder_add (&builder, "{s@v}", "flags", g_variant_new_variant (g_variant_new_int32 (flags))); g_variant_builder_add (&builder, "{s@v}", "depth", g_variant_new_variant (g_variant_new_int32 (0))); opts = g_variant_ref_sink (g_variant_builder_end (&builder)); g_autofree char *src_repo_uri = g_file_get_uri (ostree_repo_get_path (src_repo)); if (!ostree_repo_pull_with_options (dest_repo, src_repo_uri, opts, progress, cancellable, error)) { ostree_repo_abort_transaction (dest_repo, cancellable, NULL); return FALSE; } if (progress != NULL) ostree_async_progress_finish (progress); } /* Ensure a summary file is present to make it easier to look up commit checksums. */ /* FIXME: It should be possible to work without this, but find_remotes_cb() in * ostree-repo-pull.c currently assumes a summary file (signed or unsigned) is * present. */ struct stat stbuf; if (!glnx_fstatat_allow_noent (ostree_repo_get_dfd (dest_repo), "summary", &stbuf, 0, error)) return FALSE; if (errno == ENOENT && !ostree_repo_regenerate_summary (dest_repo, NULL, cancellable, error)) return FALSE; /* Add the symlinks .ostree/repos.d/@symlink_name → @dest_repo_path, unless * the @dest_repo_path is a well-known one like ostree/repo, in which case no * symlink is necessary; #OstreeRepoFinderMount always looks there. */ if (!g_str_equal (dest_repo_path, "ostree/repo") && !g_str_equal (dest_repo_path, ".ostree/repo")) { if (!glnx_shutil_mkdir_p_at (mount_root_dfd, ".ostree/repos.d", 0755, cancellable, error)) return FALSE; /* Find a unique name for the symlink. If a symlink already targets * @dest_repo_path, use that and don’t create a new one. */ GLnxDirFdIterator repos_iter; gboolean need_symlink = TRUE; if (!glnx_dirfd_iterator_init_at (mount_root_dfd, ".ostree/repos.d", TRUE, &repos_iter, error)) return FALSE; while (TRUE) { struct dirent *repo_dent; if (!glnx_dirfd_iterator_next_dent (&repos_iter, &repo_dent, cancellable, error)) return FALSE; if (repo_dent == NULL) break; /* Does the symlink already point to this repository? (Or is the * repository itself present in repos.d?) We already guarantee that * they’re on the same device. */ if (repo_dent->d_ino == dest_repo_stbuf.st_ino) { need_symlink = FALSE; break; } } /* If we need a symlink, find a unique name for it and create it. */ if (need_symlink) { /* Relative to .ostree/repos.d. */ g_autofree char *relative_dest_repo_path = g_build_filename ("..", "..", dest_repo_path, NULL); guint i; const guint max_attempts = 100; for (i = 0; i < max_attempts; i++) { g_autofree char *symlink_path = g_strdup_printf (".ostree/repos.d/%02u-generated", i); int ret = TEMP_FAILURE_RETRY (symlinkat (relative_dest_repo_path, mount_root_dfd, symlink_path)); if (ret < 0 && errno != EEXIST) return glnx_throw_errno_prefix (error, "symlinkat(%s → %s)", symlink_path, relative_dest_repo_path); else if (ret >= 0) break; } if (i == max_attempts) return glnx_throw (error, "Could not find an unused symlink name for the repository"); } } /* Report success to the user. */ g_autofree char *src_repo_path = g_file_get_path (ostree_repo_get_path (src_repo)); g_print ("Copied %u/%u refs successfully from ‘%s’ to ‘%s’ repository in ‘%s’.\n", refs->len, refs->len, src_repo_path, dest_repo_path, mount_root_path); return TRUE; }
static gboolean _ostree_repo_list_refs_internal (OstreeRepo *self, gboolean cut_prefix, const char *refspec_prefix, GHashTable **out_all_refs, GCancellable *cancellable, GError **error) { g_autoptr(GHashTable) ret_all_refs = NULL; g_autofree char *remote = NULL; g_autofree char *ref_prefix = NULL; ret_all_refs = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); if (refspec_prefix) { struct stat stbuf; const char *prefix_path; const char *path; if (!ostree_parse_refspec (refspec_prefix, &remote, &ref_prefix, error)) return FALSE; if (remote) { prefix_path = glnx_strjoina ("refs/remotes/", remote, "/"); path = glnx_strjoina (prefix_path, ref_prefix); } else { prefix_path = "refs/heads/"; path = glnx_strjoina (prefix_path, ref_prefix); } if (fstatat (self->repo_dir_fd, path, &stbuf, 0) < 0) { if (errno != ENOENT) return glnx_throw_errno (error); } else { if (S_ISDIR (stbuf.st_mode)) { glnx_fd_close int base_fd = -1; g_autoptr(GString) base_path = g_string_new (""); if (!cut_prefix) g_string_printf (base_path, "%s/", ref_prefix); if (!glnx_opendirat (self->repo_dir_fd, cut_prefix ? path : prefix_path, TRUE, &base_fd, error)) return FALSE; if (!enumerate_refs_recurse (self, remote, base_fd, base_path, base_fd, cut_prefix ? "." : ref_prefix, ret_all_refs, cancellable, error)) return FALSE; } else { glnx_fd_close int prefix_dfd = -1; if (!glnx_opendirat (self->repo_dir_fd, prefix_path, TRUE, &prefix_dfd, error)) return FALSE; if (!add_ref_to_set (remote, prefix_dfd, ref_prefix, ret_all_refs, cancellable, error)) return FALSE; } } } else { g_auto(GLnxDirFdIterator) dfd_iter = { 0, }; g_autoptr(GString) base_path = g_string_new (""); glnx_fd_close int refs_heads_dfd = -1; if (!glnx_opendirat (self->repo_dir_fd, "refs/heads", TRUE, &refs_heads_dfd, error)) return FALSE; if (!enumerate_refs_recurse (self, NULL, refs_heads_dfd, base_path, refs_heads_dfd, ".", ret_all_refs, cancellable, error)) return FALSE; g_string_truncate (base_path, 0); if (!glnx_dirfd_iterator_init_at (self->repo_dir_fd, "refs/remotes", TRUE, &dfd_iter, error)) return FALSE; while (TRUE) { struct dirent *dent; glnx_fd_close int remote_dfd = -1; if (!glnx_dirfd_iterator_next_dent_ensure_dtype (&dfd_iter, &dent, cancellable, error)) return FALSE; if (!dent) break; if (dent->d_type != DT_DIR) continue; if (!glnx_opendirat (dfd_iter.fd, dent->d_name, TRUE, &remote_dfd, error)) return FALSE; if (!enumerate_refs_recurse (self, dent->d_name, remote_dfd, base_path, remote_dfd, ".", ret_all_refs, cancellable, error)) return FALSE; } } ot_transfer_out_value (out_all_refs, &ret_all_refs); return TRUE; }