/* * Copyright (C) 2011 Colin Walters * * SPDX-License-Identifier: LGPL-2.0+ * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: Colin Walters */ #include "config.h" #include "otutil.h" #include "ostree-repo-file-enumerator.h" #include "ostree-repo-private.h" static void ostree_repo_file_file_iface_init (GFileIface *iface); struct OstreeRepoFile { GObject parent_instance; OstreeRepo *repo; OstreeRepoFile *parent; int index; char *name; char *cached_file_checksum; char *tree_contents_checksum; GVariant *tree_contents; char *tree_metadata_checksum; GVariant *tree_metadata; }; G_DEFINE_TYPE_WITH_CODE (OstreeRepoFile, ostree_repo_file, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (G_TYPE_FILE, ostree_repo_file_file_iface_init)) static void ostree_repo_file_finalize (GObject *object) { OstreeRepoFile *self; self = OSTREE_REPO_FILE (object); g_clear_pointer (&self->tree_contents, (GDestroyNotify) g_variant_unref); g_clear_pointer (&self->tree_metadata, (GDestroyNotify) g_variant_unref); g_free (self->cached_file_checksum); g_free (self->tree_contents_checksum); g_free (self->tree_metadata_checksum); g_free (self->name); G_OBJECT_CLASS (ostree_repo_file_parent_class)->finalize (object); } static void ostree_repo_file_dispose (GObject *object) { OstreeRepoFile *self; self = OSTREE_REPO_FILE (object); g_clear_object (&self->repo); g_clear_object (&self->parent); if (G_OBJECT_CLASS (ostree_repo_file_parent_class)->dispose) G_OBJECT_CLASS (ostree_repo_file_parent_class)->dispose (object); } static void ostree_repo_file_class_init (OstreeRepoFileClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); gobject_class->finalize = ostree_repo_file_finalize; gobject_class->dispose = ostree_repo_file_dispose; } static void ostree_repo_file_init (OstreeRepoFile *self) { self->index = -1; } static gboolean set_error_noent (GFile *self, GError **error) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, "No such file or directory: %s", gs_file_get_path_cached (self)); return FALSE; } OstreeRepoFile * _ostree_repo_file_new_root (OstreeRepo *repo, const char *contents_checksum, const char *metadata_checksum) { OstreeRepoFile *self; g_return_val_if_fail (repo != NULL, NULL); g_return_val_if_fail (contents_checksum != NULL, NULL); g_return_val_if_fail (strlen (contents_checksum) == OSTREE_SHA256_STRING_LEN, NULL); g_return_val_if_fail (metadata_checksum != NULL, NULL); g_return_val_if_fail (strlen (metadata_checksum) == OSTREE_SHA256_STRING_LEN, NULL); self = g_object_new (OSTREE_TYPE_REPO_FILE, NULL); self->repo = g_object_ref (repo); self->tree_contents_checksum = g_strdup (contents_checksum); self->tree_metadata_checksum = g_strdup (metadata_checksum); return self; } static OstreeRepoFile * ostree_repo_file_new_child (OstreeRepoFile *parent, const char *name) { OstreeRepoFile *self; size_t len; self = g_object_new (OSTREE_TYPE_REPO_FILE, NULL); self->repo = g_object_ref (parent->repo); self->parent = g_object_ref (parent); self->name = g_strdup (name); len = strlen(self->name); if (self->name[len-1] == '/') self->name[len-1] = '\0'; return self; } OstreeRepoFile * _ostree_repo_file_new_for_commit (OstreeRepo *repo, const char *commit, GError **error) { g_autoptr(GVariant) commit_v = NULL; g_autoptr(GVariant) tree_contents_csum_v = NULL; g_autoptr(GVariant) tree_metadata_csum_v = NULL; char tree_contents_csum[OSTREE_SHA256_STRING_LEN + 1]; char tree_metadata_csum[OSTREE_SHA256_STRING_LEN + 1]; g_return_val_if_fail (repo != NULL, NULL); g_return_val_if_fail (commit != NULL, NULL); g_return_val_if_fail (strlen (commit) == OSTREE_SHA256_STRING_LEN, NULL); if (!ostree_repo_load_variant (repo, OSTREE_OBJECT_TYPE_COMMIT, commit, &commit_v, error)) return NULL; /* PARSE OSTREE_OBJECT_TYPE_COMMIT */ g_variant_get_child (commit_v, 6, "@ay", &tree_contents_csum_v); ostree_checksum_inplace_from_bytes (g_variant_get_data (tree_contents_csum_v), tree_contents_csum); g_variant_get_child (commit_v, 7, "@ay", &tree_metadata_csum_v); ostree_checksum_inplace_from_bytes (g_variant_get_data (tree_metadata_csum_v), tree_metadata_csum); return _ostree_repo_file_new_root (repo, tree_contents_csum, tree_metadata_csum); } static gboolean do_resolve (OstreeRepoFile *self, GError **error) { g_autoptr(GVariant) root_contents = NULL; g_autoptr(GVariant) root_metadata = NULL; g_assert (self->parent == NULL); if (!ostree_repo_load_variant (self->repo, OSTREE_OBJECT_TYPE_DIR_TREE, self->tree_contents_checksum, &root_contents, error)) return FALSE; if (!ostree_repo_load_variant (self->repo, OSTREE_OBJECT_TYPE_DIR_META, self->tree_metadata_checksum, &root_metadata, error)) return FALSE; self->tree_metadata = root_metadata; root_metadata = NULL; self->tree_contents = root_contents; root_contents = NULL; return TRUE; } static gboolean do_resolve_nonroot (OstreeRepoFile *self, GError **error) { gboolean is_dir; int i; g_autoptr(GVariant) container = NULL; g_autoptr(GVariant) tree_contents = NULL; g_autoptr(GVariant) tree_metadata = NULL; g_autoptr(GVariant) contents_csum_v = NULL; g_autoptr(GVariant) metadata_csum_v = NULL; g_autofree char *tmp_checksum = NULL; if (!ostree_repo_file_ensure_resolved (self->parent, error)) return FALSE; if (!self->parent->tree_contents) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_DIRECTORY, "Not a directory"); return FALSE; } i = ostree_repo_file_tree_find_child (self->parent, self->name, &is_dir, &container); if (i < 0) { set_error_noent ((GFile*)self, error); return FALSE; } if (is_dir) { const char *name; GVariant *files_variant; files_variant = g_variant_get_child_value (self->parent->tree_contents, 0); self->index = g_variant_n_children (files_variant) + i; g_clear_pointer (&files_variant, (GDestroyNotify) g_variant_unref); g_variant_get_child (container, i, "(&s@ay@ay)", &name, &contents_csum_v, &metadata_csum_v); g_free (tmp_checksum); tmp_checksum = ostree_checksum_from_bytes_v (contents_csum_v); if (!ostree_repo_load_variant (self->repo, OSTREE_OBJECT_TYPE_DIR_TREE, tmp_checksum, &tree_contents, error)) return FALSE; g_free (tmp_checksum); tmp_checksum = ostree_checksum_from_bytes_v (metadata_csum_v); if (!ostree_repo_load_variant (self->repo, OSTREE_OBJECT_TYPE_DIR_META, tmp_checksum, &tree_metadata, error)) return FALSE; self->tree_contents = tree_contents; tree_contents = NULL; self->tree_metadata = tree_metadata; tree_metadata = NULL; self->tree_contents_checksum = ostree_checksum_from_bytes_v (contents_csum_v); self->tree_metadata_checksum = ostree_checksum_from_bytes_v (metadata_csum_v); } else self->index = i; return TRUE; } gboolean ostree_repo_file_ensure_resolved (OstreeRepoFile *self, GError **error) { if (self->parent == NULL) { if (self->tree_contents == NULL) if (!do_resolve (self, error)) return FALSE; } else { if (self->index == -1) { if (!do_resolve_nonroot (self, error)) return FALSE; } } return TRUE; } /** * ostree_repo_file_get_xattrs: * @self: #OstreeRepoFile * @out_xattrs: (out) (optional): the extended attributes * @cancellable: Cancellable * @error: Error */ gboolean ostree_repo_file_get_xattrs (OstreeRepoFile *self, GVariant **out_xattrs, GCancellable *cancellable, GError **error) { if (!ostree_repo_file_ensure_resolved (self, error)) return FALSE; g_autoptr(GVariant) ret_xattrs = NULL; if (self->tree_metadata) ret_xattrs = g_variant_get_child_value (self->tree_metadata, 3); else { if (!ostree_repo_load_file (self->repo, ostree_repo_file_get_checksum (self), NULL, NULL, &ret_xattrs, cancellable, error)) return FALSE; } ot_transfer_out_value(out_xattrs, &ret_xattrs); return TRUE; } GVariant * ostree_repo_file_tree_get_contents (OstreeRepoFile *self) { return self->tree_contents; } GVariant * ostree_repo_file_tree_get_metadata (OstreeRepoFile *self) { return self->tree_metadata; } void ostree_repo_file_tree_set_metadata (OstreeRepoFile *self, const char *checksum, GVariant *metadata) { g_clear_pointer (&self->tree_metadata, (GDestroyNotify) g_variant_unref); self->tree_metadata = g_variant_ref (metadata); g_free (self->tree_metadata_checksum); self->tree_metadata_checksum = g_strdup (checksum); } const char * ostree_repo_file_tree_get_contents_checksum (OstreeRepoFile *self) { return self->tree_contents_checksum; } const char * ostree_repo_file_tree_get_metadata_checksum (OstreeRepoFile *self) { return self->tree_metadata_checksum; } /** * ostree_repo_file_get_repo: * @self: * * Returns: (transfer none): Repository */ OstreeRepo * ostree_repo_file_get_repo (OstreeRepoFile *self) { return self->repo; } /** * ostree_repo_file_get_root: * @self: * * Returns: (transfer none): The root directory for the commit referenced by this file */ OstreeRepoFile * ostree_repo_file_get_root (OstreeRepoFile *self) { OstreeRepoFile *parent = self; while (parent->parent) parent = parent->parent; return parent; } const char * ostree_repo_file_get_checksum (OstreeRepoFile *self) { int n; gboolean is_dir; GVariant *files_variant; GVariant *dirs_variant; GVariant *csum_bytes; if (!self->parent) return self->tree_metadata_checksum; if (self->cached_file_checksum) return self->cached_file_checksum; n = ostree_repo_file_tree_find_child (self->parent, self->name, &is_dir, NULL); g_assert (n >= 0); files_variant = g_variant_get_child_value (self->parent->tree_contents, 0); dirs_variant = g_variant_get_child_value (self->parent->tree_contents, 1); if (is_dir) { g_variant_get_child (dirs_variant, n, "(@s@ay@ay)", NULL, NULL, &csum_bytes); } else { g_variant_get_child (files_variant, n, "(@s@ay)", NULL, &csum_bytes); } g_clear_pointer (&files_variant, (GDestroyNotify) g_variant_unref); g_clear_pointer (&dirs_variant, (GDestroyNotify) g_variant_unref); self->cached_file_checksum = ostree_checksum_from_bytes_v (csum_bytes); g_variant_unref (csum_bytes); return self->cached_file_checksum; } static gboolean ostree_repo_file_is_native (GFile *file) { return FALSE; } static gboolean ostree_repo_file_has_uri_scheme (GFile *file, const char *uri_scheme) { return g_ascii_strcasecmp (uri_scheme, "ostree") == 0; } static char * ostree_repo_file_get_uri_scheme (GFile *file) { return g_strdup ("ostree"); } static char * ostree_repo_file_get_basename (GFile *file) { OstreeRepoFile *self = OSTREE_REPO_FILE (file); return g_strdup (self->name); } static char * ostree_repo_file_get_path (GFile *file) { OstreeRepoFile *self = OSTREE_REPO_FILE (file); OstreeRepoFile *parent; GString *buf; GSList *parents; GSList *iter; buf = g_string_new (""); parents = NULL; for (parent = self->parent; parent; parent = parent->parent) parents = g_slist_prepend (parents, parent); if (parents && parents->next) { for (iter = parents->next; iter; iter = iter->next) { parent = iter->data; g_string_append_c (buf, '/'); g_string_append (buf, parent->name); } } g_string_append_c (buf, '/'); if (self->name) g_string_append (buf, self->name); g_slist_free (parents); return g_string_free (buf, FALSE); } static char * ostree_repo_file_get_uri (GFile *file) { OstreeRepoFile *self = OSTREE_REPO_FILE (file); const char *path; char *uri_path; char *ret; OstreeRepoFile *root = ostree_repo_file_get_root (self); path = gs_file_get_path_cached (file); uri_path = g_filename_to_uri (path, NULL, NULL); g_assert (g_str_has_prefix (uri_path, "file://")); ret = g_strconcat ("ostree://", root->tree_contents_checksum, "/", root->tree_metadata_checksum, uri_path+strlen("file://"), NULL); g_free (uri_path); return ret; } static char * ostree_repo_file_get_parse_name (GFile *file) { return ostree_repo_file_get_uri (file); } static GFile * ostree_repo_file_get_parent (GFile *file) { OstreeRepoFile *self = OSTREE_REPO_FILE (file); return (GFile*)g_object_ref (self->parent); } static GFile * ostree_repo_file_dup (GFile *file) { OstreeRepoFile *self = OSTREE_REPO_FILE (file); if (self->parent) return G_FILE (ostree_repo_file_new_child (self->parent, self->name)); else return G_FILE (_ostree_repo_file_new_root (self->repo, self->tree_contents_checksum, self->tree_metadata_checksum)); } static guint ostree_repo_file_hash (GFile *file) { OstreeRepoFile *self = OSTREE_REPO_FILE (file); if (self->parent) return g_file_hash (self->parent) + g_str_hash (self->name); else return g_str_hash (self->tree_contents_checksum) + g_str_hash (self->tree_metadata_checksum); } static gboolean ostree_repo_file_equal (GFile *file1, GFile *file2) { OstreeRepoFile *self1 = OSTREE_REPO_FILE (file1); OstreeRepoFile *self2 = OSTREE_REPO_FILE (file2); if (self1->parent && self2->parent) { return (g_str_equal (self1->name, self2->name) && g_file_equal ((GFile*)self1->parent, (GFile*)self2->parent)); } else if (!self1->parent && !self2->parent) { return (g_str_equal (self1->tree_contents_checksum, self2->tree_contents_checksum) && g_str_equal (self1->tree_metadata_checksum, self2->tree_metadata_checksum)); } else return FALSE; } static const char * match_prefix (const char *path, const char *prefix) { int prefix_len; prefix_len = strlen (prefix); if (strncmp (path, prefix, prefix_len) != 0) return NULL; /* Handle the case where prefix is the root, so that * the IS_DIR_SEPRARATOR check below works */ if (prefix_len > 0 && G_IS_DIR_SEPARATOR (prefix[prefix_len-1])) prefix_len--; return path + prefix_len; } static gboolean ostree_repo_file_prefix_matches (GFile *parent, GFile *descendant) { const char *remainder; const char *parent_path; const char *descendant_path; parent_path = gs_file_get_path_cached (parent); descendant_path = gs_file_get_path_cached (descendant); remainder = match_prefix (descendant_path, parent_path); if (remainder != NULL && G_IS_DIR_SEPARATOR (*remainder)) return TRUE; return FALSE; } static char * ostree_repo_file_get_relative_path (GFile *parent, GFile *descendant) { const char *remainder; const char *parent_path; const char *descendant_path; parent_path = gs_file_get_path_cached (parent); descendant_path = gs_file_get_path_cached (descendant); remainder = match_prefix (descendant_path, parent_path); if (remainder != NULL && G_IS_DIR_SEPARATOR (*remainder)) return g_strdup (remainder + 1); return NULL; } static GFile * ostree_repo_file_resolve_relative_path (GFile *file, const char *relative_path) { OstreeRepoFile *self = OSTREE_REPO_FILE (file); OstreeRepoFile *parent; char *filename; const char *rest; GFile *ret; if (g_path_is_absolute (relative_path)) { g_assert (*relative_path == '/'); if (strcmp (relative_path, "/") == 0) return (GFile*)g_object_ref (ostree_repo_file_get_root (self)); if (self->parent) return ostree_repo_file_resolve_relative_path ((GFile*)ostree_repo_file_get_root (self), relative_path+1); else relative_path = relative_path+1; } rest = strchr (relative_path, '/'); if (rest) { rest += 1; filename = g_strndup (relative_path, rest - relative_path); } else filename = g_strdup (relative_path); parent = ostree_repo_file_new_child (self, filename); g_free (filename); if (!rest) ret = (GFile*)parent; else { ret = ostree_repo_file_resolve_relative_path ((GFile*)parent, rest); g_clear_object (&parent); } return ret; } static GFileEnumerator * ostree_repo_file_enumerate_children (GFile *file, const char *attributes, GFileQueryInfoFlags flags, GCancellable *cancellable, GError **error) { OstreeRepoFile *self = OSTREE_REPO_FILE (file); return _ostree_repo_file_enumerator_new (self, attributes, flags, cancellable, error); } static GFile * ostree_repo_file_get_child_for_display_name (GFile *file, const char *display_name, GError **error) { return g_file_get_child (file, display_name); } static void set_info_from_dirmeta (GFileInfo *info, GVariant *metadata) { guint32 uid, gid, mode; g_file_info_set_attribute_uint32 (info, "standard::type", G_FILE_TYPE_DIRECTORY); /* PARSE OSTREE_OBJECT_TYPE_DIR_META */ g_variant_get (metadata, "(uuu@a(ayay))", &uid, &gid, &mode, NULL); uid = GUINT32_FROM_BE (uid); gid = GUINT32_FROM_BE (gid); mode = GUINT32_FROM_BE (mode); g_file_info_set_attribute_uint32 (info, "unix::uid", uid); g_file_info_set_attribute_uint32 (info, "unix::gid", gid); g_file_info_set_attribute_uint32 (info, "unix::mode", mode); } static gboolean query_child_info_dir (OstreeRepo *repo, const char *metadata_checksum, GFileAttributeMatcher *matcher, GFileQueryInfoFlags flags, GFileInfo **out_info, GCancellable *cancellable, GError **error) { g_autoptr(GFileInfo) ret_info = g_file_info_new (); g_file_info_set_attribute_uint32 (ret_info, "standard::type", G_FILE_TYPE_DIRECTORY); if (g_file_attribute_matcher_matches (matcher, "unix::mode")) { g_autoptr(GVariant) metadata = NULL; if (!ostree_repo_load_variant (repo, OSTREE_OBJECT_TYPE_DIR_META, metadata_checksum, &metadata, error)) return FALSE; set_info_from_dirmeta (ret_info, metadata); } ot_transfer_out_value(out_info, &ret_info); return TRUE; } /** * ostree_repo_file_tree_find_child: * @self: #OstreeRepoFile * @name: name of the child * @is_dir: (out caller-allocates): * @out_container: (out): */ int ostree_repo_file_tree_find_child (OstreeRepoFile *self, const char *name, gboolean *is_dir, GVariant **out_container) { int i; GVariant *files_variant = NULL; GVariant *dirs_variant = NULL; GVariant *ret_container = NULL; files_variant = g_variant_get_child_value (self->tree_contents, 0); dirs_variant = g_variant_get_child_value (self->tree_contents, 1); i = -1; if (ot_variant_bsearch_str (files_variant, name, &i)) { *is_dir = FALSE; ret_container = files_variant; files_variant = NULL; } else { if (ot_variant_bsearch_str (dirs_variant, name, &i)) { *is_dir = TRUE; ret_container = dirs_variant; dirs_variant = NULL; } else { i = -1; } } if (ret_container && out_container) { *out_container = ret_container; ret_container = NULL; } g_clear_pointer (&ret_container, (GDestroyNotify) g_variant_unref); g_clear_pointer (&files_variant, (GDestroyNotify) g_variant_unref); g_clear_pointer (&dirs_variant, (GDestroyNotify) g_variant_unref); return i; } /** * ostree_repo_file_tree_query_child: * @self: #OstreeRepoFile * @n: * @attributes: * @flags: * @out_info: (out): * @cancellable: Cancellable * @error: Error */ gboolean ostree_repo_file_tree_query_child (OstreeRepoFile *self, int n, const char *attributes, GFileQueryInfoFlags flags, GFileInfo **out_info, GCancellable *cancellable, GError **error) { gboolean ret = FALSE; const char *name = NULL; int c; g_autoptr(GFileInfo) ret_info = NULL; g_autoptr(GVariant) files_variant = NULL; g_autoptr(GVariant) dirs_variant = NULL; g_autoptr(GVariant) content_csum_v = NULL; g_autoptr(GVariant) meta_csum_v = NULL; char tmp_checksum[OSTREE_SHA256_STRING_LEN+1]; GFileAttributeMatcher *matcher = NULL; if (!ostree_repo_file_ensure_resolved (self, error)) goto out; matcher = g_file_attribute_matcher_new (attributes); g_assert (self->tree_contents); files_variant = g_variant_get_child_value (self->tree_contents, 0); dirs_variant = g_variant_get_child_value (self->tree_contents, 1); c = g_variant_n_children (files_variant); if (n < c) { const guchar *csum_bytes; g_variant_get_child (files_variant, n, "(&s@ay)", &name, &content_csum_v); csum_bytes = ostree_checksum_bytes_peek_validate (content_csum_v, error); if (csum_bytes == NULL) goto out; ostree_checksum_inplace_from_bytes (csum_bytes, tmp_checksum); if (!ostree_repo_load_file (self->repo, tmp_checksum, NULL, &ret_info, NULL, cancellable, error)) goto out; } else { n -= c; c = g_variant_n_children (dirs_variant); if (n < c) { const guchar *csum_bytes; g_variant_get_child (dirs_variant, n, "(&s@ay@ay)", &name, NULL, &meta_csum_v); csum_bytes = ostree_checksum_bytes_peek_validate (meta_csum_v, error); if (csum_bytes == NULL) goto out; ostree_checksum_inplace_from_bytes (csum_bytes, tmp_checksum); if (!query_child_info_dir (self->repo, tmp_checksum, matcher, flags, &ret_info, cancellable, error)) goto out; } } if (name) { g_file_info_set_attribute_byte_string (ret_info, "standard::name", name); g_file_info_set_attribute_string (ret_info, "standard::display-name", name); if (*name == '.') g_file_info_set_is_hidden (ret_info, TRUE); } else { g_clear_object (&ret_info); } ret = TRUE; ot_transfer_out_value(out_info, &ret_info); out: if (matcher) g_file_attribute_matcher_unref (matcher); return ret; } static GFileInfo * ostree_repo_file_query_info (GFile *file, const char *attributes, GFileQueryInfoFlags flags, GCancellable *cancellable, GError **error) { OstreeRepoFile *self = OSTREE_REPO_FILE (file); g_autoptr(GFileInfo) info = NULL; if (!ostree_repo_file_ensure_resolved (self, error)) return NULL; if (!self->parent) { info = g_file_info_new (); set_info_from_dirmeta (info, self->tree_metadata); } else { if (!ostree_repo_file_tree_query_child (self->parent, self->index, attributes, flags, &info, cancellable, error)) return NULL; g_assert (info != NULL); } return g_steal_pointer (&info); } static GFileAttributeInfoList * ostree_repo_file_query_settable_attributes (GFile *file, GCancellable *cancellable, GError **error) { return g_file_attribute_info_list_new (); } static GFileAttributeInfoList * ostree_repo_file_query_writable_namespaces (GFile *file, GCancellable *cancellable, GError **error) { return g_file_attribute_info_list_new (); } static GFileInputStream * ostree_repo_file_read (GFile *file, GCancellable *cancellable, GError **error) { OstreeRepoFile *self = OSTREE_REPO_FILE (file); const char *checksum; g_autoptr(GInputStream) ret_stream = NULL; if (!ostree_repo_file_ensure_resolved (self, error)) return FALSE; if (self->tree_contents) { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_IS_DIRECTORY, "Can't open directory"); return NULL; } checksum = ostree_repo_file_get_checksum (self); g_autoptr(GFileInfo) finfo = NULL; if (!ostree_repo_load_file (self->repo, checksum, NULL, &finfo, NULL, cancellable, error)) return NULL; if (g_file_info_get_file_type (finfo) == G_FILE_TYPE_REGULAR) { if (!ostree_repo_load_file (self->repo, checksum, &ret_stream, NULL, NULL, cancellable, error)) return NULL; } else { g_autoptr(GFile) parent = g_file_get_parent (file); const char *target = g_file_info_get_symlink_target (finfo); g_autoptr(GFile) dest = g_file_resolve_relative_path (parent, target); return g_file_read (dest, cancellable, error); } return g_steal_pointer (&ret_stream); } static void ostree_repo_file_file_iface_init (GFileIface *iface) { iface->dup = ostree_repo_file_dup; iface->hash = ostree_repo_file_hash; iface->equal = ostree_repo_file_equal; iface->is_native = ostree_repo_file_is_native; iface->has_uri_scheme = ostree_repo_file_has_uri_scheme; iface->get_uri_scheme = ostree_repo_file_get_uri_scheme; iface->get_basename = ostree_repo_file_get_basename; iface->get_path = ostree_repo_file_get_path; iface->get_uri = ostree_repo_file_get_uri; iface->get_parse_name = ostree_repo_file_get_parse_name; iface->get_parent = ostree_repo_file_get_parent; iface->prefix_matches = ostree_repo_file_prefix_matches; iface->get_relative_path = ostree_repo_file_get_relative_path; iface->resolve_relative_path = ostree_repo_file_resolve_relative_path; iface->get_child_for_display_name = ostree_repo_file_get_child_for_display_name; iface->set_display_name = NULL; iface->enumerate_children = ostree_repo_file_enumerate_children; iface->query_info = ostree_repo_file_query_info; iface->query_filesystem_info = NULL; iface->find_enclosing_mount = NULL; iface->query_settable_attributes = ostree_repo_file_query_settable_attributes; iface->query_writable_namespaces = ostree_repo_file_query_writable_namespaces; iface->set_attribute = NULL; iface->set_attributes_from_info = NULL; iface->read_fn = ostree_repo_file_read; iface->append_to = NULL; iface->create = NULL; iface->replace = NULL; iface->open_readwrite = NULL; iface->create_readwrite = NULL; iface->replace_readwrite = NULL; iface->delete_file = NULL; iface->trash = NULL; iface->make_directory = NULL; iface->make_symbolic_link = NULL; iface->copy = NULL; iface->move = NULL; iface->monitor_dir = NULL; iface->monitor_file = NULL; iface->supports_thread_contexts = TRUE; }