diff options
42 files changed, 19 insertions, 8806 deletions
@@ -36,7 +36,6 @@ flatpak-session-helper xdg-document-portal xdg-permission-store flatpak-builder -testdb *~ profile/flatpak.sh flatpak-dbus.[ch] @@ -68,9 +67,6 @@ Flatpak-1.0.* /doc/flatpak-docs.xml /doc/*.1 /doc/*.5 -/test-doc-portal -/test-doc-portal.log -/test-doc-portal.trs /test-libglnx-errors /test-libglnx-errors.log /test-libglnx-errors.trs @@ -89,8 +85,6 @@ Flatpak-1.0.* /testlibrary /testlibrary.log /testlibrary.trs -/testdb.log -/testdb.trs /tests/test-keyring/.gpg-v21-migrated /tests/test-keyring/private-keys-v1.d/ /tests/test-keyring/trustdb.gpg diff --git a/Makefile.am b/Makefile.am index 96b83ab1..ce2508ff 100644 --- a/Makefile.am +++ b/Makefile.am @@ -94,8 +94,6 @@ include lib/Makefile.am.inc include session-helper/Makefile.am.inc include system-helper/Makefile.am.inc include dbus-proxy/Makefile.am.inc -include permission-store/Makefile.am.inc -include document-portal/Makefile.am.inc include tests/Makefile.am.inc if !WITH_SYSTEM_BWRAP diff --git a/app/flatpak-builtins-document-export.c b/app/flatpak-builtins-document-export.c index 1a16c7b0..46b4c944 100644 --- a/app/flatpak-builtins-document-export.c +++ b/app/flatpak-builtins-document-export.c @@ -29,7 +29,7 @@ #include <glib/gi18n.h> #include "libglnx/libglnx.h" -#include "document-portal/xdp-dbus.h" +#include "flatpak-document-dbus.h" #include <gio/gunixfdlist.h> diff --git a/app/flatpak-builtins-document-info.c b/app/flatpak-builtins-document-info.c index bd7edc68..c9605456 100644 --- a/app/flatpak-builtins-document-info.c +++ b/app/flatpak-builtins-document-info.c @@ -29,7 +29,7 @@ #include <glib/gi18n.h> #include "libglnx/libglnx.h" -#include "document-portal/xdp-dbus.h" +#include "flatpak-document-dbus.h" #include <gio/gunixfdlist.h> diff --git a/app/flatpak-builtins-document-list.c b/app/flatpak-builtins-document-list.c index 4644086c..48cd1afb 100644 --- a/app/flatpak-builtins-document-list.c +++ b/app/flatpak-builtins-document-list.c @@ -29,7 +29,7 @@ #include <glib/gi18n.h> #include "libglnx/libglnx.h" -#include "document-portal/xdp-dbus.h" +#include "flatpak-document-dbus.h" #include "flatpak-builtins.h" #include "flatpak-utils.h" diff --git a/app/flatpak-builtins-document-unexport.c b/app/flatpak-builtins-document-unexport.c index 8efeeb76..82e58f38 100644 --- a/app/flatpak-builtins-document-unexport.c +++ b/app/flatpak-builtins-document-unexport.c @@ -29,7 +29,7 @@ #include <glib/gi18n.h> #include "libglnx/libglnx.h" -#include "document-portal/xdp-dbus.h" +#include "flatpak-document-dbus.h" #include <gio/gunixfdlist.h> diff --git a/common/Makefile.am.inc b/common/Makefile.am.inc index ea5afdb7..74db3b47 100644 --- a/common/Makefile.am.inc +++ b/common/Makefile.am.inc @@ -1,6 +1,6 @@ noinst_LTLIBRARIES += libflatpak-common.la -dbus_built_sources = common/flatpak-dbus.c common/flatpak-dbus.h +dbus_built_sources = common/flatpak-dbus.c common/flatpak-dbus.h common/flatpak-document-dbus.c common/flatpak-document-dbus.h systemd_dbus_built_sources = common/flatpak-systemd-dbus.c common/flatpak-systemd-dbus.h common/flatpak-dbus.c: data/org.freedesktop.Flatpak.xml Makefile @@ -9,7 +9,16 @@ common/flatpak-dbus.c: data/org.freedesktop.Flatpak.xml Makefile --interface-prefix org.freedesktop.Flatpak. \ --c-namespace Flatpak \ --generate-c-code $(builddir)/common/flatpak-dbus \ - $(srcdir)/data/org.freedesktop.Flatpak.xml \ + $(srcdir)/data/org.freedesktop.Flatpak.xml \ + $(NULL) + +common/flatpak-document-dbus.c: data/org.freedesktop.portal.Documents.xml Makefile + mkdir -p $(builddir)/common + $(AM_V_GEN) $(GDBUS_CODEGEN) \ + --interface-prefix org.freedesktop.portal. \ + --c-namespace XdpDbus \ + --generate-c-code $(builddir)/common/flatpak-document-dbus \ + $(srcdir)/data/org.freedesktop.portal.Documents.xml \ $(NULL) common/flatpak-systemd-dbus.c: data/org.freedesktop.systemd1.xml Makefile @@ -53,13 +62,6 @@ libflatpak_common_la_SOURCES = \ common/flatpak-table-printer.h \ common/flatpak-chain-input-stream.c \ common/flatpak-chain-input-stream.h \ - common/gvdb/gvdb-reader.h \ - common/gvdb/gvdb-format.h \ - common/gvdb/gvdb-reader.c \ - common/gvdb/gvdb-builder.h \ - common/gvdb/gvdb-builder.c \ - common/flatpak-db.c \ - common/flatpak-db.h \ common/flatpak-json.c \ common/flatpak-json.h \ common/flatpak-json-oci.c \ diff --git a/common/flatpak-context.c b/common/flatpak-context.c index aae2726f..209e84fc 100644 --- a/common/flatpak-context.c +++ b/common/flatpak-context.c @@ -42,7 +42,6 @@ #include "flatpak-utils.h" #include "flatpak-dir.h" #include "flatpak-systemd-dbus.h" -#include "document-portal/xdp-dbus.h" #include "lib/flatpak-error.h" /* Same order as enum */ diff --git a/common/flatpak-db.c b/common/flatpak-db.c deleted file mode 100644 index f20443ed..00000000 --- a/common/flatpak-db.c +++ /dev/null @@ -1,1224 +0,0 @@ -/* flatpak-db.c - * - * Copyright (C) 2015 Red Hat, Inc - * - * This file 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 file 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 program. If not, see <http://www.gnu.org/licenses/>. - * - * Authors: - * Alexander Larsson <alexl@redhat.com> - */ - -#include "config.h" - -#include <string.h> -#include <fcntl.h> -#include <stdio.h> -#include <stdlib.h> -#include <sys/statfs.h> - -#include "flatpak-db.h" -#include "gvdb/gvdb-reader.h" -#include "gvdb/gvdb-builder.h" - -struct FlatpakDb -{ - GObject parent; - - char *path; - gboolean fail_if_not_found; - GvdbTable *gvdb; - GBytes *gvdb_contents; - - gboolean dirty; - - /* Map id => GVariant (data, sorted-dict[appid->perms]) */ - GvdbTable *main_table; - GHashTable *main_updates; - - /* (reverse) Map app id => [ id ]*/ - GvdbTable *app_table; - GHashTable *app_additions; - GHashTable *app_removals; -}; - -typedef struct -{ - GObjectClass parent_class; -} FlatpakDbClass; - -static void initable_iface_init (GInitableIface *initable_iface); - -G_DEFINE_TYPE_WITH_CODE (FlatpakDb, flatpak_db, G_TYPE_OBJECT, - G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, initable_iface_init)); - -enum { - PROP_0, - PROP_PATH, - PROP_FAIL_IF_NOT_FOUND, - LAST_PROP -}; - -static int -cmpstringp (const void *p1, const void *p2) -{ - return strcmp (*(char * const *) p1, *(char * const *) p2); -} - -static void -sort_strv (const char **strv) -{ - qsort (strv, g_strv_length ((char **) strv), sizeof (const char *), cmpstringp); -} - -static int -str_ptr_array_find (GPtrArray *array, - const char *str) -{ - int i; - - for (i = 0; i < array->len; i++) - if (strcmp (g_ptr_array_index (array, i), str) == 0) - return i; - - return -1; -} - -static gboolean -str_ptr_array_contains (GPtrArray *array, - const char *str) -{ - return str_ptr_array_find (array, str) >= 0; -} - -const char * -flatpak_db_get_path (FlatpakDb *self) -{ - g_return_val_if_fail (FLATPAK_IS_DB (self), NULL); - - return self->path; -} - -void -flatpak_db_set_path (FlatpakDb *self, - const char *path) -{ - g_return_if_fail (FLATPAK_IS_DB (self)); - - g_clear_pointer (&self->path, g_free); - self->path = g_strdup (path); -} - -FlatpakDb * -flatpak_db_new (const char *path, - gboolean fail_if_not_found, - GError **error) -{ - return g_initable_new (FLATPAK_TYPE_DB, - NULL, - error, - "path", path, - "fail-if-not-found", fail_if_not_found, - NULL); -} - -static void -flatpak_db_finalize (GObject *object) -{ - FlatpakDb *self = (FlatpakDb *) object; - - g_clear_pointer (&self->path, g_free); - g_clear_pointer (&self->gvdb_contents, g_bytes_unref); - g_clear_pointer (&self->gvdb, gvdb_table_free); - g_clear_pointer (&self->main_table, gvdb_table_free); - g_clear_pointer (&self->app_table, gvdb_table_free); - g_clear_pointer (&self->main_updates, g_hash_table_unref); - g_clear_pointer (&self->app_additions, g_hash_table_unref); - g_clear_pointer (&self->app_removals, g_hash_table_unref); - - G_OBJECT_CLASS (flatpak_db_parent_class)->finalize (object); -} - -static void -flatpak_db_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - FlatpakDb *self = FLATPAK_DB (object); - - switch (prop_id) - { - case PROP_PATH: - g_value_set_string (value, self->path); - break; - - case PROP_FAIL_IF_NOT_FOUND: - g_value_set_boolean (value, self->fail_if_not_found); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - -static void -flatpak_db_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - FlatpakDb *self = FLATPAK_DB (object); - - switch (prop_id) - { - case PROP_PATH: - g_clear_pointer (&self->path, g_free); - self->path = g_value_dup_string (value); - break; - - case PROP_FAIL_IF_NOT_FOUND: - self->fail_if_not_found = g_value_get_boolean (value); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - -static void -flatpak_db_class_init (FlatpakDbClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - object_class->finalize = flatpak_db_finalize; - object_class->get_property = flatpak_db_get_property; - object_class->set_property = flatpak_db_set_property; - - g_object_class_install_property (object_class, - PROP_PATH, - g_param_spec_string ("path", - "", - "", - NULL, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); - g_object_class_install_property (object_class, - PROP_FAIL_IF_NOT_FOUND, - g_param_spec_boolean ("fail-if-not-found", - "", - "", - TRUE, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); -} - -static void -flatpak_db_init (FlatpakDb *self) -{ - self->fail_if_not_found = TRUE; - - self->main_updates = - g_hash_table_new_full (g_str_hash, g_str_equal, - g_free, (GDestroyNotify) g_variant_unref); - self->app_additions = - g_hash_table_new_full (g_str_hash, g_str_equal, - g_free, (GDestroyNotify) g_ptr_array_unref); - self->app_removals = - g_hash_table_new_full (g_str_hash, g_str_equal, - g_free, (GDestroyNotify) g_ptr_array_unref); -} - -static gboolean -is_on_nfs (const char *path) -{ - struct statfs statfs_buffer; - int statfs_result; - g_autofree char *dirname = NULL; - - dirname = g_path_get_dirname (path); - - statfs_result = statfs (dirname, &statfs_buffer); - if (statfs_result != 0) - return FALSE; - - return statfs_buffer.f_type == 0x6969; -} - -static gboolean -initable_init (GInitable *initable, - GCancellable *cancellable, - GError **error) -{ - FlatpakDb *self = (FlatpakDb *) initable; - GError *my_error = NULL; - - if (self->path == NULL) - return TRUE; - - if (is_on_nfs (self->path)) - { - g_autoptr(GFile) file = g_file_new_for_path (self->path); - char *contents; - gsize length; - - /* We avoid using mmap on NFS, because its prone to give us SIGBUS at semi-random - times (nfs down, file removed, etc). Instead we just load the file */ - if (g_file_load_contents (file, cancellable, &contents, &length, NULL, &my_error)) - self->gvdb_contents = g_bytes_new_take (contents, length); - } - else - { - GMappedFile *mapped = g_mapped_file_new (self->path, FALSE, &my_error); - if (mapped) - { - self->gvdb_contents = g_mapped_file_get_bytes (mapped); - g_mapped_file_unref (mapped); - } - } - - if (self->gvdb_contents == NULL) - { - if (!self->fail_if_not_found && - g_error_matches (my_error, G_FILE_ERROR, G_FILE_ERROR_NOENT)) - { - g_error_free (my_error); - } - else - { - g_propagate_error (error, my_error); - return FALSE; - } - } - else - { - self->gvdb = gvdb_table_new_from_bytes (self->gvdb_contents, TRUE, error); - if (self->gvdb == NULL) - return FALSE; - - self->main_table = gvdb_table_get_table (self->gvdb, "main"); - if (self->main_table == NULL) - { - g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_INVAL, - "No main table in db"); - return FALSE; - } - - self->app_table = gvdb_table_get_table (self->gvdb, "apps"); - if (self->app_table == NULL) - { - g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_INVAL, - "No app table in db"); - return FALSE; - } - } - - return TRUE; -} - -static void -initable_iface_init (GInitableIface *initable_iface) -{ - initable_iface->init = initable_init; -} - -/* Transfer: full */ -char ** -flatpak_db_list_ids (FlatpakDb *self) -{ - GPtrArray *res; - GHashTableIter iter; - gpointer key, value; - int i; - - g_return_val_if_fail (FLATPAK_IS_DB (self), NULL); - - res = g_ptr_array_new (); - - g_hash_table_iter_init (&iter, self->main_updates); - while (g_hash_table_iter_next (&iter, &key, &value)) - { - if (value != NULL) - g_ptr_array_add (res, g_strdup (key)); - } - - if (self->main_table) - { - // TODO: can we use gvdb_table_list here??? - g_autofree char **main_ids = gvdb_table_get_names (self->main_table, NULL); - - for (i = 0; main_ids[i] != NULL; i++) - { - char *id = main_ids[i]; - - if (g_hash_table_lookup_extended (self->main_updates, id, NULL, NULL)) - g_free (id); - else - g_ptr_array_add (res, id); - } - } - - g_ptr_array_add (res, NULL); - return (char **) g_ptr_array_free (res, FALSE); -} - -static gboolean -app_update_empty (GHashTable *ht, const char *app) -{ - GPtrArray *array; - - array = g_hash_table_lookup (ht, app); - if (array == NULL) - return TRUE; - - return array->len == 0; -} - -/* Transfer: full */ -char ** -flatpak_db_list_apps (FlatpakDb *self) -{ - gpointer key, _value; - GHashTableIter iter; - GPtrArray *res; - int i; - - g_return_val_if_fail (FLATPAK_IS_DB (self), NULL); - - res = g_ptr_array_new (); - - g_hash_table_iter_init (&iter, self->app_additions); - while (g_hash_table_iter_next (&iter, &key, &_value)) - { - GPtrArray *value = _value; - if (value->len > 0) - g_ptr_array_add (res, g_strdup (key)); - } - - if (self->app_table) - { - // TODO: can we use gvdb_table_list here??? - g_autofree char **apps = gvdb_table_get_names (self->app_table, NULL); - - for (i = 0; apps[i] != NULL; i++) - { - char *app = apps[i]; - gboolean empty = TRUE; - GPtrArray *removals; - int j; - - /* Don't use if we already added above */ - if (app_update_empty (self->app_additions, app)) - { - g_autoptr(GVariant) ids_v = NULL; - - removals = g_hash_table_lookup (self->app_removals, app); - - /* Add unless all items are removed */ - ids_v = gvdb_table_get_value (self->app_table, app); - - if (ids_v) - { - g_autofree const char **ids = g_variant_get_strv (ids_v, NULL); - - for (j = 0; ids[j] != NULL; j++) - { - if (removals == NULL || - !str_ptr_array_contains (removals, ids[j])) - { - empty = FALSE; - break; - } - } - } - } - - if (empty) - g_free (app); - else - g_ptr_array_add (res, app); - } - } - - g_ptr_array_add (res, NULL); - return (char **) g_ptr_array_free (res, FALSE); -} - -/* Transfer: full */ -char ** -flatpak_db_list_ids_by_app (FlatpakDb *self, - const char *app) -{ - GPtrArray *res; - GPtrArray *additions; - GPtrArray *removals; - int i; - - g_return_val_if_fail (FLATPAK_IS_DB (self), NULL); - - res = g_ptr_array_new (); - - additions = g_hash_table_lookup (self->app_additions, app); - removals = g_hash_table_lookup (self->app_removals, app); - - if (additions) - { - for (i = 0; i < additions->len; i++) - g_ptr_array_add (res, - g_strdup (g_ptr_array_index (additions, i))); - } - - if (self->app_table) - { - g_autoptr(GVariant) ids_v = gvdb_table_get_value (self->app_table, app); - if (ids_v) - { - g_autofree const char **ids = g_variant_get_strv (ids_v, NULL); - - for (i = 0; ids[i] != NULL; i++) - { - if (removals == NULL || - !str_ptr_array_contains (removals, ids[i])) - g_ptr_array_add (res, g_strdup (ids[i])); - } - } - } - - g_ptr_array_add (res, NULL); - return (char **) g_ptr_array_free (res, FALSE); -} - -/* Transfer: full */ -FlatpakDbEntry * -flatpak_db_lookup (FlatpakDb *self, - const char *id) -{ - GVariant *res = NULL; - gpointer value; - - g_return_val_if_fail (FLATPAK_IS_DB (self), NULL); - g_return_val_if_fail (id != NULL, NULL); - - if (g_hash_table_lookup_extended (self->main_updates, id, NULL, &value)) - { - if (value != NULL) - res = g_variant_ref ((GVariant *) value); - } - else if (self->main_table) - { - res = gvdb_table_get_value (self->main_table, id); - } - - return (FlatpakDbEntry *) res; -} - -/* Transfer: full */ -char ** -flatpak_db_list_ids_by_value (FlatpakDb *self, - GVariant *data) -{ - g_autofree char **ids = flatpak_db_list_ids (self); - int i; - GPtrArray *res; - - g_return_val_if_fail (FLATPAK_IS_DB (self), NULL); - g_return_val_if_fail (data != NULL, NULL); - - res = g_ptr_array_new (); - - for (i = 0; ids[i] != NULL; i++) - { - char *id = ids[i]; - - g_autoptr(FlatpakDbEntry) entry = NULL; - g_autoptr(GVariant) entry_data = NULL; - - entry = flatpak_db_lookup (self, id); - if (entry) - { - entry_data = flatpak_db_entry_get_data (entry); - if (g_variant_equal (data, entry_data)) - { - g_ptr_array_add (res, id); - id = NULL; /* Don't free, as we return this */ - } - } - g_free (id); - } - - g_ptr_array_add (res, NULL); - return (char **) g_ptr_array_free (res, FALSE); -} - -static void -add_app_id (FlatpakDb *self, - const char *app, - const char *id) -{ - GPtrArray *additions; - GPtrArray *removals; - int i; - - additions = g_hash_table_lookup (self->app_additions, app); - removals = g_hash_table_lookup (self->app_removals, app); - - if (removals) - { - i = str_ptr_array_find (removals, id); - if (i >= 0) - g_ptr_array_remove_index_fast (removals, i); - } - - if (additions) - { - if (!str_ptr_array_contains (additions, id)) - g_ptr_array_add (additions, g_strdup (id)); - } - else - { - additions = g_ptr_array_new_with_free_func (g_free); - g_ptr_array_add (additions, g_strdup (id)); - g_hash_table_insert (self->app_additions, - g_strdup (app), additions); - } -} - -static void -remove_app_id (FlatpakDb *self, - const char *app, - const char *id) -{ - GPtrArray *additions; - GPtrArray *removals; - int i; - - additions = g_hash_table_lookup (self->app_additions, app); - removals = g_hash_table_lookup (self->app_removals, app); - - if (additions) - { - i = str_ptr_array_find (additions, id); - if (i >= 0) - g_ptr_array_remove_index_fast (additions, i); - } - - if (removals) - { - if (!str_ptr_array_contains (removals, id)) - g_ptr_array_add (removals, g_strdup (id)); - } - else - { - removals = g_ptr_array_new_with_free_func (g_free); - g_ptr_array_add (removals, g_strdup (id)); - g_hash_table_insert (self->app_removals, - g_strdup (app), removals); - } -} - -gboolean -flatpak_db_is_dirty (FlatpakDb *self) -{ - g_return_val_if_fail (FLATPAK_IS_DB (self), FALSE); - - return self->dirty; -} - -/* add, replace, or NULL entry to remove */ -void -flatpak_db_set_entry (FlatpakDb *self, - const char *id, - FlatpakDbEntry *entry) -{ - g_autoptr(FlatpakDbEntry) old_entry = NULL; - g_autofree const char **old = NULL; - g_autofree const char **new = NULL; - static const char *empty[] = { NULL }; - const char **a, **b; - int ia, ib; - - g_return_if_fail (FLATPAK_IS_DB (self)); - g_return_if_fail (id != NULL); - - self->dirty = TRUE; - - old_entry = flatpak_db_lookup (self, id); - - g_hash_table_insert (self->main_updates, - g_strdup (id), - flatpak_db_entry_ref (entry)); - - a = empty; - b = empty; - - if (old_entry) - { - old = flatpak_db_entry_list_apps (old_entry); - sort_strv (old); - a = old; - } - - if (entry) - { - new = flatpak_db_entry_list_apps (entry); - sort_strv (new); - b = new; - } - - ia = 0; - ib = 0; - while (a[ia] != NULL || b[ib] != NULL) - { - if (a[ia] == NULL) - { - /* Not in old, but in new => added */ - add_app_id (self, b[ib], id); - ib++; - } - else if (b[ib] == NULL) - { - /* Not in new, but in old => removed */ - remove_app_id (self, a[ia], id); - ia++; - } - else - { - int cmp = strcmp (a[ia], b[ib]); - - if (cmp == 0) - { - /* In both, no change */ - ia++; - ib++; - } - else if (cmp < 0) - { - /* Not in new, but in old => removed */ - remove_app_id (self, a[ia], id); - ia++; - } - else /* cmp > 0 */ - { - /* Not in old, but in new => added */ - add_app_id (self, b[ib], id); - ib++; - } - } - } -} - -void -flatpak_db_update (FlatpakDb *self) -{ - GHashTable *root, *main_h, *apps_h; - GBytes *new_contents; - GvdbTable *new_gvdb; - int i; - - g_auto(GStrv) ids = NULL; - g_auto(GStrv) apps = NULL; - - g_return_if_fail (FLATPAK_IS_DB (self)); - - root = gvdb_hash_table_new (NULL, NULL); - main_h = gvdb_hash_table_new (root, "main"); - apps_h = gvdb_hash_table_new (root, "apps"); - g_hash_table_unref (main_h); - g_hash_table_unref (apps_h); - - ids = flatpak_db_list_ids (self); - for (i = 0; ids[i] != 0; i++) - { - g_autoptr(FlatpakDbEntry) entry = flatpak_db_lookup (self, ids[i]); - if (entry != NULL) - { - GvdbItem *item; - - item = gvdb_hash_table_insert (main_h, ids[i]); - gvdb_item_set_value (item, (GVariant *) entry); - } - } - - apps = flatpak_db_list_apps (self); - for (i = 0; apps[i] != 0; i++) - { - g_auto(GStrv) app_ids = flatpak_db_list_ids_by_app (self, apps[i]); - GVariantBuilder builder; - GvdbItem *item; - int j; - - /* May as well ensure that on-disk arrays are sorted, even if we don't use it yet */ - sort_strv ((const char **) app_ids); - - /* We should never list an app that has empty id lists */ - g_assert (app_ids[0] != NULL); - - g_variant_builder_init (&builder, G_VARIANT_TYPE_ARRAY); - for (j = 0; app_ids[j] != NULL; j++) - g_variant_builder_add (&builder, "s", app_ids[j]); - - item = gvdb_hash_table_insert (apps_h, apps[i]); - gvdb_item_set_value (item, g_variant_builder_end (&builder)); - } - - new_contents = gvdb_table_get_content (root, FALSE); - new_gvdb = gvdb_table_new_from_bytes (new_contents, TRUE, NULL); - - /* This was just created, any failure to parse it is purely an internal error */ - g_assert (new_gvdb != NULL); - - g_clear_pointer (&self->gvdb_contents, g_bytes_unref); - g_clear_pointer (&self->gvdb, gvdb_table_free); - self->gvdb_contents = new_contents; - self->gvdb = new_gvdb; - self->dirty = FALSE; -} - -GBytes * -flatpak_db_get_content (FlatpakDb *self) -{ - g_return_val_if_fail (FLATPAK_IS_DB (self), NULL); - - return self->gvdb_contents; -} - -/* Note: You must first call update to serialize, this only saves serialied data */ -gboolean -flatpak_db_save_content (FlatpakDb *self, - GError **error) -{ - GBytes *content = NULL; - - if (self->gvdb_contents == NULL) - { - g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_INVAL, - "No content to save"); - return FALSE; - } - - if (self->path == NULL) - { - g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_INVAL, - "No path set"); - return FALSE; - } - - content = self->gvdb_contents; - return g_file_set_contents (self->path, g_bytes_get_data (content, NULL), g_bytes_get_size (content), error); -} - -static void -save_content_callback (GObject *source_object, - GAsyncResult *res, - gpointer user_data) -{ - g_autoptr(GTask) task = user_data; - GFile *file = G_FILE (source_object); - gboolean ok; - g_autoptr(GError) error = NULL; - - ok = g_file_replace_contents_finish (file, - res, - NULL, &error); - if (ok) - g_task_return_boolean (task, TRUE); - else - g_task_return_error (task, error); -} - -void -flatpak_db_save_content_async (FlatpakDb *self, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - GBytes *content = NULL; - - g_autoptr(GTask) task = NULL; - g_autoptr(GFile) file = NULL; - - task = g_task_new (self, cancellable, callback, user_data); - - if (self->gvdb_contents == NULL) - { - g_task_return_new_error (task, G_FILE_ERROR, G_FILE_ERROR_INVAL, - "No content to save"); - return; - } - - if (self->path == NULL) - { - g_task_return_new_error (task, G_FILE_ERROR, G_FILE_ERROR_INVAL, - "No path set"); - return; - } - - content = g_bytes_ref (self->gvdb_contents); - g_task_set_task_data (task, content, (GDestroyNotify) g_bytes_unref); - - file = g_file_new_for_path (self->path); - g_file_replace_contents_bytes_async (file, content, - NULL, FALSE, 0, - cancellable, - save_content_callback, - g_object_ref (task)); -} - -gboolean -flatpak_db_save_content_finish (FlatpakDb *self, - GAsyncResult *res, - GError **error) -{ - return g_task_propagate_boolean (G_TASK (res), error); -} - - -GString * -flatpak_db_print_string (FlatpakDb *self, - GString *string) -{ - g_auto(GStrv) ids = NULL; - g_auto(GStrv) apps = NULL; - int i; - - g_return_val_if_fail (FLATPAK_IS_DB (self), NULL); - - if G_UNLIKELY (string == NULL) - string = g_string_new (NULL); - - g_string_append_printf (string, "main {\n"); - - ids = flatpak_db_list_ids (self); - sort_strv ((const char **) ids); - for (i = 0; ids[i] != 0; i++) - { - g_autoptr(FlatpakDbEntry) entry = flatpak_db_lookup (self, ids[i]); - g_string_append_printf (string, " %s: ", ids[i]); - if (entry != NULL) - flatpak_db_entry_print_string (entry, string); - g_string_append_printf (string, "\n"); - } - - g_string_append_printf (string, "}\napps {\n"); - - apps = flatpak_db_list_apps (self); - sort_strv ((const char **) apps); - for (i = 0; apps[i] != 0; i++) - { - int j; - g_auto(GStrv) app_ids = NULL; - - app_ids = flatpak_db_list_ids_by_app (self, apps[i]); - sort_strv ((const char **) app_ids); - - g_string_append_printf (string, " %s: ", apps[i]); - for (j = 0; app_ids[j] != NULL; j++) - g_string_append_printf (string, "%s%s", j == 0 ? "" : ", ", app_ids[j]); - g_string_append_printf (string, "\n"); - } - - g_string_append_printf (string, "}\n"); - - return string; -} - -char * -flatpak_db_print (FlatpakDb *self) -{ - return g_string_free (flatpak_db_print_string (self, NULL), FALSE); -} - -FlatpakDbEntry * -flatpak_db_entry_ref (FlatpakDbEntry *entry) -{ - if (entry != NULL) - g_variant_ref ((GVariant *) entry); - return entry; -} - -void -flatpak_db_entry_unref (FlatpakDbEntry *entry) -{ - g_variant_unref ((GVariant *) entry); -} - -/* Transfer: full */ -GVariant * -flatpak_db_entry_get_data (FlatpakDbEntry *entry) -{ - g_autoptr(GVariant) variant = g_variant_get_child_value ((GVariant *) entry, 0); - - return g_variant_get_child_value (variant, 0); -} - -/* Transfer: container */ -const char ** -flatpak_db_entry_list_apps (FlatpakDbEntry *entry) -{ - GVariant *v = (GVariant *) entry; - - g_autoptr(GVariant) app_array = NULL; - GVariantIter iter; - GVariant *child; - GPtrArray *res; - - res = g_ptr_array_new (); - - app_array = g_variant_get_child_value (v, 1); - - g_variant_iter_init (&iter, app_array); - while ((child = g_variant_iter_next_value (&iter))) - { - const char *child_app_id; - g_autoptr(GVariant) permissions = g_variant_get_child_value (child, 1); - - if (g_variant_n_children (permissions) > 0) - { - g_variant_get_child (child, 0, "&s", &child_app_id); - g_ptr_array_add (res, (char *) child_app_id); - } - - g_variant_unref (child); - } - - g_ptr_array_add (res, NULL); - return (const char **) g_ptr_array_free (res, FALSE); -} - -static GVariant * -flatpak_db_entry_get_permissions_variant (FlatpakDbEntry *entry, - const char *app_id) -{ - GVariant *v = (GVariant *) entry; - - g_autoptr(GVariant) app_array = NULL; - GVariant *child; - GVariant *res = NULL; - gsize n_children, start, end, m; - const char *child_app_id; - int cmp; - - app_array = g_variant_get_child_value (v, 1); - - n_children = g_variant_n_children (app_array); - - start = 0; - end = n_children; - while (start < end) - { - m = (start + end) / 2; - - child = g_variant_get_child_value (app_array, m); - g_variant_get_child (child, 0, "&s", &child_app_id); - - cmp = strcmp (app_id, child_app_id); - if (cmp == 0) - { - res = g_variant_get_child_value (child, 1); - break; - } - else if (cmp < 0) - { - end = m; - } - else /* cmp > 0 */ - { - start = m + 1; - } - } - - return res; -} - - -/* Transfer: container */ -const char ** -flatpak_db_entry_list_permissions (FlatpakDbEntry *entry, - const char *app) -{ - g_autoptr(GVariant) permissions = NULL; - - permissions = flatpak_db_entry_get_permissions_variant (entry, app); - if (permissions) - return g_variant_get_strv (permissions, NULL); - else - return g_new0 (const char *, 1); -} - -gboolean -flatpak_db_entry_has_permission (FlatpakDbEntry *entry, - const char *app, - const char *permission) -{ - g_autofree const char **app_permissions = NULL; - - app_permissions = flatpak_db_entry_list_permissions (entry, app); - - return g_strv_contains (app_permissions, permission); -} - -gboolean -flatpak_db_entry_has_permissions (FlatpakDbEntry *entry, - const char *app, - const char **permissions) -{ - g_autofree const char **app_permissions = NULL; - int i; - - app_permissions = flatpak_db_entry_list_permissions (entry, app); - - for (i = 0; permissions[i] != NULL; i++) - { - if (!g_strv_contains (app_permissions, permissions[i])) - return FALSE; - } - - return TRUE; -} - -static GVariant * -make_entry (GVariant *data, - GVariant *app_permissions) -{ - return g_variant_new ("(v@a{sas})", data, app_permissions); -} - -static GVariant * -make_empty_app_permissions (void) -{ - return g_variant_new_array (G_VARIANT_TYPE ("{sas}"), NULL, 0); -} - -static GVariant * -make_permissions (const char *app, const char **permissions) -{ - static const char **empty = { NULL }; - - if (permissions == NULL) - permissions = empty; - - return g_variant_new ("{s@as}", - app, - g_variant_new_strv (permissions, -1)); -} - -static GVariant * -add_permissions (GVariant *app_permissions, - GVariant *permissions) -{ - GVariantBuilder builder; - GVariantIter iter; - GVariant *child; - gboolean added = FALSE; - int cmp; - const char *new_app_id; - const char *child_app_id; - - g_autoptr(GVariant) new_perms_array = NULL; - - g_variant_get (permissions, "{&s@as}", &new_app_id, &new_perms_array); - - g_variant_builder_init (&builder, G_VARIANT_TYPE_ARRAY); - - /* Insert or replace permissions in sorted order */ - - g_variant_iter_init (&iter, app_permissions); - while ((child = g_variant_iter_next_value (&iter))) - { - g_autoptr(GVariant) old_perms_array = NULL; - - g_variant_get (child, "{&s@as}", &child_app_id, &old_perms_array); - - cmp = strcmp (new_app_id, child_app_id); - if (cmp == 0) - { - added = TRUE; - /* Replace old permissions */ - g_variant_builder_add_value (&builder, permissions); - } - else if (cmp < 0) - { - if (!added) - { - added = TRUE; - g_variant_builder_add_value (&builder, permissions); - } - g_variant_builder_add_value (&builder, child); - } - else /* cmp > 0 */ - { - g_variant_builder_add_value (&builder, child); - } - - g_variant_unref (child); - } - - if (!added) - g_variant_builder_add_value (&builder, permissions); - - return g_variant_builder_end (&builder); -} - -FlatpakDbEntry * -flatpak_db_entry_new (GVariant *data) -{ - GVariant *res; - - if (data == NULL) - data = g_variant_new_byte (0); - - res = make_entry (data, - make_empty_app_permissions ()); - - return (FlatpakDbEntry *) g_variant_ref_sink (res); -} - -FlatpakDbEntry * -flatpak_db_entry_modify_data (FlatpakDbEntry *entry, - GVariant *data) -{ - GVariant *v = (GVariant *) entry; - GVariant *res; - - if (data == NULL) - data = g_variant_new_byte (0); - - res = make_entry (data, - g_variant_get_child_value (v, 1)); - return (FlatpakDbEntry *) g_variant_ref_sink (res); -} - -/* NULL (or empty) permissions to remove permissions */ -FlatpakDbEntry * -flatpak_db_entry_set_app_permissions (FlatpakDbEntry *entry, - const char *app, - const char **permissions) -{ - GVariant *v = (GVariant *) entry; - GVariant *res; - - g_autoptr(GVariant) old_data_v = g_variant_get_child_value (v, 0); - g_autoptr(GVariant) old_data = g_variant_get_child_value (old_data_v, 0); - g_autoptr(GVariant) old_permissions = g_variant_get_child_value (v, 1); - - res = make_entry (old_data, - add_permissions (old_permissions, - make_permissions (app, - permissions))); - return (FlatpakDbEntry *) g_variant_ref_sink (res); -} - -GString * -flatpak_db_entry_print_string (FlatpakDbEntry *entry, - GString *string) -{ - return g_variant_print_string ((GVariant *) entry, string, FALSE); -} diff --git a/common/flatpak-db.h b/common/flatpak-db.h deleted file mode 100644 index f7757854..00000000 --- a/common/flatpak-db.h +++ /dev/null @@ -1,103 +0,0 @@ -/* flatpak-db.h - * - * Copyright © 2015 Red Hat, Inc - * - * This file 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 file 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 program. If not, see <http://www.gnu.org/licenses/>. - * - * Authors: - * Alexander Larsson <alexl@redhat.com> - */ - -#ifndef FLATPAK_DB_H -#define FLATPAK_DB_H - -#include <string.h> - -#include "libglnx/libglnx.h" -#include <glib-object.h> - -G_BEGIN_DECLS - -typedef struct FlatpakDb FlatpakDb; -typedef struct _FlatpakDbEntry FlatpakDbEntry; - -#define FLATPAK_TYPE_DB (flatpak_db_get_type ()) -#define FLATPAK_DB(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), FLATPAK_TYPE_DB, FlatpakDb)) -#define FLATPAK_IS_DB(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), FLATPAK_TYPE_DB)) - -GType flatpak_db_get_type (void); - -FlatpakDb * flatpak_db_new (const char *path, - gboolean fail_if_not_found, - GError **error); -char ** flatpak_db_list_ids (FlatpakDb *self); -char ** flatpak_db_list_apps (FlatpakDb *self); -char ** flatpak_db_list_ids_by_app (FlatpakDb *self, - const char *app); -char ** flatpak_db_list_ids_by_value (FlatpakDb *self, - GVariant *data); -FlatpakDbEntry *flatpak_db_lookup (FlatpakDb *self, - const char *id); -GString * flatpak_db_print_string (FlatpakDb *self, - GString *string); -char * flatpak_db_print (FlatpakDb *self); - -gboolean flatpak_db_is_dirty (FlatpakDb *self); -void flatpak_db_set_entry (FlatpakDb *self, - const char *id, - FlatpakDbEntry *entry); -void flatpak_db_update (FlatpakDb *self); -GBytes * flatpak_db_get_content (FlatpakDb *self); -const char * flatpak_db_get_path (FlatpakDb *self); -gboolean flatpak_db_save_content (FlatpakDb *self, - GError **error); -void flatpak_db_save_content_async (FlatpakDb *self, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -gboolean flatpak_db_save_content_finish (FlatpakDb *self, - GAsyncResult *res, - GError **error); -void flatpak_db_set_path (FlatpakDb *self, - const char *path); - - -FlatpakDbEntry *flatpak_db_entry_ref (FlatpakDbEntry *entry); -void flatpak_db_entry_unref (FlatpakDbEntry *entry); -GVariant * flatpak_db_entry_get_data (FlatpakDbEntry *entry); -const char ** flatpak_db_entry_list_apps (FlatpakDbEntry *entry); -const char ** flatpak_db_entry_list_permissions (FlatpakDbEntry *entry, - const char *app); -gboolean flatpak_db_entry_has_permission (FlatpakDbEntry *entry, - const char *app, - const char *permission); -gboolean flatpak_db_entry_has_permissions (FlatpakDbEntry *entry, - const char *app, - const char **permissions); -GString * flatpak_db_entry_print_string (FlatpakDbEntry *entry, - GString *string); - -FlatpakDbEntry *flatpak_db_entry_new (GVariant *data); -FlatpakDbEntry *flatpak_db_entry_modify_data (FlatpakDbEntry *entry, - GVariant *data); -FlatpakDbEntry *flatpak_db_entry_set_app_permissions (FlatpakDbEntry *entry, - const char *app, - const char **permissions); - -G_DEFINE_AUTOPTR_CLEANUP_FUNC (FlatpakDb, g_object_unref) -G_DEFINE_AUTOPTR_CLEANUP_FUNC (FlatpakDbEntry, flatpak_db_entry_unref) - -G_END_DECLS - -#endif /* FLATPAK_DB_H */ diff --git a/common/flatpak-exports.c b/common/flatpak-exports.c index 586a3527..64eb8e1b 100644 --- a/common/flatpak-exports.c +++ b/common/flatpak-exports.c @@ -43,7 +43,6 @@ #include "flatpak-utils.h" #include "flatpak-dir.h" #include "flatpak-systemd-dbus.h" -#include "document-portal/xdp-dbus.h" #include "lib/flatpak-error.h" /* We don't want to export paths pointing into these, because they are readonly diff --git a/common/flatpak-run.c b/common/flatpak-run.c index 8638f713..2637b0c5 100644 --- a/common/flatpak-run.c +++ b/common/flatpak-run.c @@ -50,7 +50,7 @@ #include "flatpak-utils.h" #include "flatpak-dir.h" #include "flatpak-systemd-dbus.h" -#include "document-portal/xdp-dbus.h" +#include "flatpak-document-dbus.h" #include "lib/flatpak-error.h" #define DEFAULT_SHELL "/bin/sh" diff --git a/common/flatpak-utils.h b/common/flatpak-utils.h index 52a72a3c..9e5bf7e4 100644 --- a/common/flatpak-utils.h +++ b/common/flatpak-utils.h @@ -29,9 +29,9 @@ #include <gio/gunixfdlist.h> #include <libsoup/soup.h> #include "flatpak-dbus.h" +#include "flatpak-document-dbus.h" #include <ostree.h> #include <json-glib/json-glib.h> -#include "document-portal/xdp-dbus.h" typedef enum { FLATPAK_HOST_COMMAND_FLAGS_CLEAR_ENV = 1 << 0, diff --git a/common/gvdb/.gitignore b/common/gvdb/.gitignore deleted file mode 100644 index 8b5dee6a..00000000 --- a/common/gvdb/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -libgvdb.a -libgvdb-shared.a diff --git a/common/gvdb/README b/common/gvdb/README deleted file mode 100644 index 94e6c5d1..00000000 --- a/common/gvdb/README +++ /dev/null @@ -1,7 +0,0 @@ -DO NOT MODIFY ANY FILE IN THIS DIRECTORY - -(except maybe the Makefile.am) - -This directory is the result of a git subtree merge with the 'gvdb' -module on git.gnome.org. Please apply fixes to the 'gvdb' module and -perform a git merge. diff --git a/common/gvdb/gvdb-builder.c b/common/gvdb/gvdb-builder.c deleted file mode 100644 index ded77086..00000000 --- a/common/gvdb/gvdb-builder.c +++ /dev/null @@ -1,537 +0,0 @@ -/* - * Copyright © 2010 Codethink Limited - * - * 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 licence, 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 <http://www.gnu.org/licenses/>. - * - * Author: Ryan Lortie <desrt@desrt.ca> - */ - -#include "gvdb-builder.h" -#include "gvdb-format.h" - -#include <glib.h> -#include <fcntl.h> -#if !defined(G_OS_WIN32) || !defined(_MSC_VER) -#include <unistd.h> -#endif -#include <string.h> - - -struct _GvdbItem -{ - gchar *key; - guint32 hash_value; - guint32_le assigned_index; - GvdbItem *parent; - GvdbItem *sibling; - GvdbItem *next; - - /* one of: - * this: - */ - GVariant *value; - - /* this: */ - GHashTable *table; - - /* or this: */ - GvdbItem *child; -}; - -static void -gvdb_item_free (gpointer data) -{ - GvdbItem *item = data; - - g_free (item->key); - - if (item->value) - g_variant_unref (item->value); - - if (item->table) - g_hash_table_unref (item->table); - - g_slice_free (GvdbItem, item); -} - -GHashTable * -gvdb_hash_table_new (GHashTable *parent, - const gchar *name_in_parent) -{ - GHashTable *table; - - table = g_hash_table_new_full (g_str_hash, g_str_equal, - g_free, gvdb_item_free); - - if (parent) - { - GvdbItem *item; - - item = gvdb_hash_table_insert (parent, name_in_parent); - gvdb_item_set_hash_table (item, table); - } - - return table; -} - -static guint32 -djb_hash (const gchar *key) -{ - guint32 hash_value = 5381; - - while (*key) - hash_value = hash_value * 33 + *(signed char *)key++; - - return hash_value; -} - -GvdbItem * -gvdb_hash_table_insert (GHashTable *table, - const gchar *key) -{ - GvdbItem *item; - - item = g_slice_new0 (GvdbItem); - item->key = g_strdup (key); - item->hash_value = djb_hash (key); - - g_hash_table_insert (table, g_strdup (key), item); - - return item; -} - -void -gvdb_hash_table_insert_string (GHashTable *table, - const gchar *key, - const gchar *value) -{ - GvdbItem *item; - - item = gvdb_hash_table_insert (table, key); - gvdb_item_set_value (item, g_variant_new_string (value)); -} - -void -gvdb_item_set_value (GvdbItem *item, - GVariant *value) -{ - g_return_if_fail (!item->value && !item->table && !item->child); - - item->value = g_variant_ref_sink (value); -} - -void -gvdb_item_set_hash_table (GvdbItem *item, - GHashTable *table) -{ - g_return_if_fail (!item->value && !item->table && !item->child); - - item->table = g_hash_table_ref (table); -} - -void -gvdb_item_set_parent (GvdbItem *item, - GvdbItem *parent) -{ - GvdbItem **node; - - g_return_if_fail (g_str_has_prefix (item->key, parent->key)); - g_return_if_fail (!parent->value && !parent->table); - g_return_if_fail (!item->parent && !item->sibling); - - for (node = &parent->child; *node; node = &(*node)->sibling) - if (strcmp ((*node)->key, item->key) > 0) - break; - - item->parent = parent; - item->sibling = *node; - *node = item; -} - -typedef struct -{ - GvdbItem **buckets; - gint n_buckets; -} HashTable; - -static HashTable * -hash_table_new (gint n_buckets) -{ - HashTable *table; - - table = g_slice_new (HashTable); - table->buckets = g_new0 (GvdbItem *, n_buckets); - table->n_buckets = n_buckets; - - return table; -} - -static void -hash_table_free (HashTable *table) -{ - g_free (table->buckets); - - g_slice_free (HashTable, table); -} - -static void -hash_table_insert (gpointer key, - gpointer value, - gpointer data) -{ - guint32 hash_value, bucket; - HashTable *table = data; - GvdbItem *item = value; - - hash_value = djb_hash (key); - bucket = hash_value % table->n_buckets; - item->next = table->buckets[bucket]; - table->buckets[bucket] = item; -} - -static guint32_le -item_to_index (GvdbItem *item) -{ - if (item != NULL) - return item->assigned_index; - - return guint32_to_le (-1u); -} - -typedef struct -{ - GQueue *chunks; - guint64 offset; - gboolean byteswap; -} FileBuilder; - -typedef struct -{ - gsize offset; - gsize size; - gpointer data; -} FileChunk; - -static gpointer -file_builder_allocate (FileBuilder *fb, - guint alignment, - gsize size, - struct gvdb_pointer *pointer) -{ - FileChunk *chunk; - - if (size == 0) - return NULL; - - fb->offset += (-fb->offset) & (alignment - 1); - chunk = g_slice_new (FileChunk); - chunk->offset = fb->offset; - chunk->size = size; - chunk->data = g_malloc (size); - - pointer->start = guint32_to_le (fb->offset); - fb->offset += size; - pointer->end = guint32_to_le (fb->offset); - - g_queue_push_tail (fb->chunks, chunk); - - return chunk->data; -} - -static void -file_builder_add_value (FileBuilder *fb, - GVariant *value, - struct gvdb_pointer *pointer) -{ - GVariant *variant, *normal; - gpointer data; - gsize size; - - if (fb->byteswap) - { - value = g_variant_byteswap (value); - variant = g_variant_new_variant (value); - g_variant_unref (value); - } - else - variant = g_variant_new_variant (value); - - normal = g_variant_get_normal_form (variant); - g_variant_unref (variant); - - size = g_variant_get_size (normal); - data = file_builder_allocate (fb, 8, size, pointer); - g_variant_store (normal, data); - g_variant_unref (normal); -} - -static void -file_builder_add_string (FileBuilder *fb, - const gchar *string, - guint32_le *start, - guint16_le *size) -{ - FileChunk *chunk; - gsize length; - - length = strlen (string); - - chunk = g_slice_new (FileChunk); - chunk->offset = fb->offset; - chunk->size = length; - chunk->data = g_malloc (length); - memcpy (chunk->data, string, length); - - *start = guint32_to_le (fb->offset); - *size = guint16_to_le (length); - fb->offset += length; - - g_queue_push_tail (fb->chunks, chunk); -} - -static void -file_builder_allocate_for_hash (FileBuilder *fb, - gsize n_buckets, - gsize n_items, - guint bloom_shift, - gsize n_bloom_words, - guint32_le **bloom_filter, - guint32_le **hash_buckets, - struct gvdb_hash_item **hash_items, - struct gvdb_pointer *pointer) -{ - guint32_le bloom_hdr, table_hdr; - guchar *data; - gsize size; - - g_assert (n_bloom_words < (1u << 27)); - - bloom_hdr = guint32_to_le (bloom_shift << 27 | n_bloom_words); - table_hdr = guint32_to_le (n_buckets); - - size = sizeof bloom_hdr + sizeof table_hdr + - n_bloom_words * sizeof (guint32_le) + - n_buckets * sizeof (guint32_le) + - n_items * sizeof (struct gvdb_hash_item); - - data = file_builder_allocate (fb, 4, size, pointer); - -#define chunk(s) (size -= (s), data += (s), data - (s)) - memcpy (chunk (sizeof bloom_hdr), &bloom_hdr, sizeof bloom_hdr); - memcpy (chunk (sizeof table_hdr), &table_hdr, sizeof table_hdr); - *bloom_filter = (guint32_le *) chunk (n_bloom_words * sizeof (guint32_le)); - *hash_buckets = (guint32_le *) chunk (n_buckets * sizeof (guint32_le)); - *hash_items = (struct gvdb_hash_item *) chunk (n_items * - sizeof (struct gvdb_hash_item)); - g_assert (size == 0); -#undef chunk - - memset (*bloom_filter, 0, n_bloom_words * sizeof (guint32_le)); - - /* NOTE - the code to actually fill in the bloom filter here is missing. - * Patches welcome! - * - * http://en.wikipedia.org/wiki/Bloom_filter - * http://0pointer.de/blog/projects/bloom.html - */ -} - -static void -file_builder_add_hash (FileBuilder *fb, - GHashTable *table, - struct gvdb_pointer *pointer) -{ - guint32_le *buckets, *bloom_filter; - struct gvdb_hash_item *items; - HashTable *mytable; - GvdbItem *item; - guint32 index; - gint bucket; - - mytable = hash_table_new (g_hash_table_size (table)); - g_hash_table_foreach (table, hash_table_insert, mytable); - index = 0; - - for (bucket = 0; bucket < mytable->n_buckets; bucket++) - for (item = mytable->buckets[bucket]; item; item = item->next) - item->assigned_index = guint32_to_le (index++); - - file_builder_allocate_for_hash (fb, mytable->n_buckets, index, 5, 0, - &bloom_filter, &buckets, &items, pointer); - - index = 0; - for (bucket = 0; bucket < mytable->n_buckets; bucket++) - { - buckets[bucket] = guint32_to_le (index); - - for (item = mytable->buckets[bucket]; item; item = item->next) - { - struct gvdb_hash_item *entry = items++; - const gchar *basename; - - g_assert (index == guint32_from_le (item->assigned_index)); - entry->hash_value = guint32_to_le (item->hash_value); - entry->parent = item_to_index (item->parent); - entry->unused = 0; - - if (item->parent != NULL) - basename = item->key + strlen (item->parent->key); - else - basename = item->key; - - file_builder_add_string (fb, basename, - &entry->key_start, - &entry->key_size); - - if (item->value != NULL) - { - g_assert (item->child == NULL && item->table == NULL); - - file_builder_add_value (fb, item->value, &entry->value.pointer); - entry->type = 'v'; - } - - if (item->child != NULL) - { - guint32 children = 0, i = 0; - guint32_le *offsets; - GvdbItem *child; - - g_assert (item->table == NULL); - - for (child = item->child; child; child = child->sibling) - children++; - - offsets = file_builder_allocate (fb, 4, 4 * children, - &entry->value.pointer); - entry->type = 'L'; - - for (child = item->child; child; child = child->sibling) - offsets[i++] = child->assigned_index; - - g_assert (children == i); - } - - if (item->table != NULL) - { - entry->type = 'H'; - file_builder_add_hash (fb, item->table, &entry->value.pointer); - } - - index++; - } - } - - hash_table_free (mytable); -} - -static FileBuilder * -file_builder_new (gboolean byteswap) -{ - FileBuilder *builder; - - builder = g_slice_new (FileBuilder); - builder->chunks = g_queue_new (); - builder->offset = sizeof (struct gvdb_header); - builder->byteswap = byteswap; - - return builder; -} - -static GString * -file_builder_serialise (FileBuilder *fb, - struct gvdb_pointer root) -{ - struct gvdb_header header = { { 0, }, }; - GString *result; - - if (fb->byteswap) - { - header.signature[0] = GVDB_SWAPPED_SIGNATURE0; - header.signature[1] = GVDB_SWAPPED_SIGNATURE1; - } - else - { - header.signature[0] = GVDB_SIGNATURE0; - header.signature[1] = GVDB_SIGNATURE1; - } - - result = g_string_new (NULL); - - header.root = root; - g_string_append_len (result, (gpointer) &header, sizeof header); - - while (!g_queue_is_empty (fb->chunks)) - { - FileChunk *chunk = g_queue_pop_head (fb->chunks); - - if (result->len != chunk->offset) - { - gchar zero[8] = { 0, }; - - g_assert (chunk->offset > result->len); - g_assert (chunk->offset - result->len < 8); - - g_string_append_len (result, zero, chunk->offset - result->len); - g_assert (result->len == chunk->offset); - } - - g_string_append_len (result, chunk->data, chunk->size); - g_free (chunk->data); - - g_slice_free (FileChunk, chunk); - } - - g_queue_free (fb->chunks); - g_slice_free (FileBuilder, fb); - - return result; -} - -GBytes * -gvdb_table_get_content (GHashTable *table, - gboolean byteswap) -{ - struct gvdb_pointer root; - FileBuilder *fb; - GString *str; - GBytes *res; - - fb = file_builder_new (byteswap); - file_builder_add_hash (fb, table, &root); - str = file_builder_serialise (fb, root); - - res = g_bytes_new_take (str->str, str->len); - g_string_free (str, FALSE); - - return res; -} - -gboolean -gvdb_table_write_contents (GHashTable *table, - const gchar *filename, - gboolean byteswap, - GError **error) -{ - GBytes *content; - gboolean status; - - content = gvdb_table_get_content (table, byteswap); - - status = g_file_set_contents (filename, g_bytes_get_data (content, NULL), g_bytes_get_size (content), error); - - g_bytes_unref (content); - - return status; -} diff --git a/common/gvdb/gvdb-builder.h b/common/gvdb/gvdb-builder.h deleted file mode 100644 index ad361dc6..00000000 --- a/common/gvdb/gvdb-builder.h +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright © 2010 Codethink Limited - * - * 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 licence, 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 <http://www.gnu.org/licenses/>. - * - * Author: Ryan Lortie <desrt@desrt.ca> - */ - -#ifndef __gvdb_builder_h__ -#define __gvdb_builder_h__ - -#include <gio/gio.h> - -typedef struct _GvdbItem GvdbItem; - -G_GNUC_INTERNAL -GHashTable * gvdb_hash_table_new (GHashTable *parent, - const gchar *key); - -G_GNUC_INTERNAL -GvdbItem * gvdb_hash_table_insert (GHashTable *table, - const gchar *key); -G_GNUC_INTERNAL -void gvdb_hash_table_insert_string (GHashTable *table, - const gchar *key, - const gchar *value); - -G_GNUC_INTERNAL -void gvdb_item_set_value (GvdbItem *item, - GVariant *value); -G_GNUC_INTERNAL -void gvdb_item_set_hash_table (GvdbItem *item, - GHashTable *table); -G_GNUC_INTERNAL -void gvdb_item_set_parent (GvdbItem *item, - GvdbItem *parent); - -G_GNUC_INTERNAL -gboolean gvdb_table_write_contents (GHashTable *table, - const gchar *filename, - gboolean byteswap, - GError **error); - -G_GNUC_INTERNAL -GBytes * gvdb_table_get_content (GHashTable *table, - gboolean byteswap); - - -#endif /* __gvdb_builder_h__ */ diff --git a/common/gvdb/gvdb-format.h b/common/gvdb/gvdb-format.h deleted file mode 100644 index 486e8547..00000000 --- a/common/gvdb/gvdb-format.h +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright © 2010 Codethink Limited - * - * 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 licence, 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 <http://www.gnu.org/licenses/>. - * - * Author: Ryan Lortie <desrt@desrt.ca> - */ - -#ifndef __gvdb_format_h__ -#define __gvdb_format_h__ - -#include <glib.h> - -typedef struct { guint16 value; } guint16_le; -typedef struct { guint32 value; } guint32_le; - -struct gvdb_pointer { - guint32_le start; - guint32_le end; -}; - -struct gvdb_hash_header { - guint32_le n_bloom_words; - guint32_le n_buckets; -}; - -struct gvdb_hash_item { - guint32_le hash_value; - guint32_le parent; - - guint32_le key_start; - guint16_le key_size; - gchar type; - gchar unused; - - union - { - struct gvdb_pointer pointer; - gchar direct[8]; - } value; -}; - -struct gvdb_header { - guint32 signature[2]; - guint32_le version; - guint32_le options; - - struct gvdb_pointer root; -}; - -static inline guint32_le guint32_to_le (guint32 value) { - guint32_le result = { GUINT32_TO_LE (value) }; - return result; -} - -static inline guint32 guint32_from_le (guint32_le value) { - return GUINT32_FROM_LE (value.value); -} - -static inline guint16_le guint16_to_le (guint16 value) { - guint16_le result = { GUINT16_TO_LE (value) }; - return result; -} - -static inline guint16 guint16_from_le (guint16_le value) { - return GUINT16_FROM_LE (value.value); -} - -#define GVDB_SIGNATURE0 1918981703 -#define GVDB_SIGNATURE1 1953390953 -#define GVDB_SWAPPED_SIGNATURE0 GUINT32_SWAP_LE_BE (GVDB_SIGNATURE0) -#define GVDB_SWAPPED_SIGNATURE1 GUINT32_SWAP_LE_BE (GVDB_SIGNATURE1) - -#endif /* __gvdb_format_h__ */ diff --git a/common/gvdb/gvdb-reader.c b/common/gvdb/gvdb-reader.c deleted file mode 100644 index 08b5bc8c..00000000 --- a/common/gvdb/gvdb-reader.c +++ /dev/null @@ -1,718 +0,0 @@ -/* - * Copyright © 2010 Codethink Limited - * - * 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 licence, 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 <http://www.gnu.org/licenses/>. - * - * Author: Ryan Lortie <desrt@desrt.ca> - */ - -#include "gvdb-reader.h" -#include "gvdb-format.h" - -#include <string.h> - -struct _GvdbTable { - GBytes *bytes; - - const gchar *data; - gsize size; - - gboolean byteswapped; - gboolean trusted; - - const guint32_le *bloom_words; - guint32 n_bloom_words; - guint bloom_shift; - - const guint32_le *hash_buckets; - guint32 n_buckets; - - struct gvdb_hash_item *hash_items; - guint32 n_hash_items; -}; - -static const gchar * -gvdb_table_item_get_key (GvdbTable *file, - const struct gvdb_hash_item *item, - gsize *size) -{ - guint32 start, end; - - start = guint32_from_le (item->key_start); - *size = guint16_from_le (item->key_size); - end = start + *size; - - if G_UNLIKELY (start > end || end > file->size) - return NULL; - - return file->data + start; -} - -static gconstpointer -gvdb_table_dereference (GvdbTable *file, - const struct gvdb_pointer *pointer, - gint alignment, - gsize *size) -{ - guint32 start, end; - - start = guint32_from_le (pointer->start); - end = guint32_from_le (pointer->end); - - if G_UNLIKELY (start > end || end > file->size || start & (alignment - 1)) - return NULL; - - *size = end - start; - - return file->data + start; -} - -static void -gvdb_table_setup_root (GvdbTable *file, - const struct gvdb_pointer *pointer) -{ - const struct gvdb_hash_header *header; - guint32 n_bloom_words; - guint32 n_buckets; - gsize size; - - header = gvdb_table_dereference (file, pointer, 4, &size); - - if G_UNLIKELY (header == NULL || size < sizeof *header) - return; - - size -= sizeof *header; - - n_bloom_words = guint32_from_le (header->n_bloom_words); - n_buckets = guint32_from_le (header->n_buckets); - n_bloom_words &= (1u << 27) - 1; - - if G_UNLIKELY (n_bloom_words * sizeof (guint32_le) > size) - return; - - file->bloom_words = (gpointer) (header + 1); - size -= n_bloom_words * sizeof (guint32_le); - file->n_bloom_words = n_bloom_words; - - if G_UNLIKELY (n_buckets > G_MAXUINT / sizeof (guint32_le) || - n_buckets * sizeof (guint32_le) > size) - return; - - file->hash_buckets = file->bloom_words + file->n_bloom_words; - size -= n_buckets * sizeof (guint32_le); - file->n_buckets = n_buckets; - - if G_UNLIKELY (size % sizeof (struct gvdb_hash_item)) - return; - - file->hash_items = (gpointer) (file->hash_buckets + n_buckets); - file->n_hash_items = size / sizeof (struct gvdb_hash_item); -} - -/** - * gvdb_table_new_from_bytes: - * @bytes: the #GBytes with the data - * @trusted: if the contents of @bytes are trusted - * @error: %NULL, or a pointer to a %NULL #GError - * @returns: a new #GvdbTable - * - * Creates a new #GvdbTable from the contents of @bytes. - * - * This call can fail if the header contained in @bytes is invalid. - * - * You should call gvdb_table_free() on the return result when you no - * longer require it. - **/ -GvdbTable * -gvdb_table_new_from_bytes (GBytes *bytes, - gboolean trusted, - GError **error) -{ - const struct gvdb_header *header; - GvdbTable *file; - - file = g_slice_new0 (GvdbTable); - file->bytes = g_bytes_ref (bytes); - file->data = g_bytes_get_data (bytes, &file->size); - file->trusted = trusted; - - if (file->size < sizeof (struct gvdb_header)) - goto invalid; - - header = (gpointer) file->data; - - if (header->signature[0] == GVDB_SIGNATURE0 && - header->signature[1] == GVDB_SIGNATURE1 && - guint32_from_le (header->version) == 0) - file->byteswapped = FALSE; - - else if (header->signature[0] == GVDB_SWAPPED_SIGNATURE0 && - header->signature[1] == GVDB_SWAPPED_SIGNATURE1 && - guint32_from_le (header->version) == 0) - file->byteswapped = TRUE; - - else - goto invalid; - - gvdb_table_setup_root (file, &header->root); - - return file; - -invalid: - g_set_error_literal (error, G_FILE_ERROR, G_FILE_ERROR_INVAL, "invalid gvdb header"); - - g_bytes_unref (file->bytes); - - g_slice_free (GvdbTable, file); - - return NULL; -} - -/** - * gvdb_table_new: - * @filename: a filename - * @trusted: if the contents of @bytes are trusted - * @error: %NULL, or a pointer to a %NULL #GError - * @returns: a new #GvdbTable - * - * Creates a new #GvdbTable using the #GMappedFile for @filename as the - * #GBytes. - **/ -GvdbTable * -gvdb_table_new (const gchar *filename, - gboolean trusted, - GError **error) -{ - GMappedFile *mapped; - GvdbTable *table; - GBytes *bytes; - - mapped = g_mapped_file_new (filename, FALSE, error); - if (!mapped) - return NULL; - - bytes = g_mapped_file_get_bytes (mapped); - table = gvdb_table_new_from_bytes (bytes, trusted, error); - g_mapped_file_unref (mapped); - g_bytes_unref (bytes); - - g_prefix_error (error, "%s: ", filename); - - return table; -} - -static gboolean -gvdb_table_bloom_filter (GvdbTable *file, - guint32 hash_value) -{ - guint32 word, mask; - - if (file->n_bloom_words == 0) - return TRUE; - - word = (hash_value / 32) % file->n_bloom_words; - mask = 1 << (hash_value & 31); - mask |= 1 << ((hash_value >> file->bloom_shift) & 31); - - return (guint32_from_le (file->bloom_words[word]) & mask) == mask; -} - -static gboolean -gvdb_table_check_name (GvdbTable *file, - struct gvdb_hash_item *item, - const gchar *key, - guint key_length) -{ - const gchar *this_key; - gsize this_size; - guint32 parent; - - this_key = gvdb_table_item_get_key (file, item, &this_size); - - if G_UNLIKELY (this_key == NULL || this_size > key_length) - return FALSE; - - key_length -= this_size; - - if G_UNLIKELY (memcmp (this_key, key + key_length, this_size) != 0) - return FALSE; - - parent = guint32_from_le (item->parent); - if (key_length == 0 && parent == 0xffffffffu) - return TRUE; - - if G_LIKELY (parent < file->n_hash_items && this_size > 0) - return gvdb_table_check_name (file, - &file->hash_items[parent], - key, key_length); - - return FALSE; -} - -static const struct gvdb_hash_item * -gvdb_table_lookup (GvdbTable *file, - const gchar *key, - gchar type) -{ - guint32 hash_value = 5381; - guint key_length; - guint32 bucket; - guint32 lastno; - guint32 itemno; - - if G_UNLIKELY (file->n_buckets == 0 || file->n_hash_items == 0) - return NULL; - - for (key_length = 0; key[key_length]; key_length++) - hash_value = (hash_value * 33) + ((signed char *) key)[key_length]; - - if (!gvdb_table_bloom_filter (file, hash_value)) - return NULL; - - bucket = hash_value % file->n_buckets; - itemno = guint32_from_le (file->hash_buckets[bucket]); - - if (bucket == file->n_buckets - 1 || - (lastno = guint32_from_le(file->hash_buckets[bucket + 1])) > file->n_hash_items) - lastno = file->n_hash_items; - - while G_LIKELY (itemno < lastno) - { - struct gvdb_hash_item *item = &file->hash_items[itemno]; - - if (hash_value == guint32_from_le (item->hash_value)) - if G_LIKELY (gvdb_table_check_name (file, item, key, key_length)) - if G_LIKELY (item->type == type) - return item; - - itemno++; - } - - return NULL; -} - -static gboolean -gvdb_table_list_from_item (GvdbTable *table, - const struct gvdb_hash_item *item, - const guint32_le **list, - guint *length) -{ - gsize size; - - *list = gvdb_table_dereference (table, &item->value.pointer, 4, &size); - - if G_LIKELY (*list == NULL || size % 4) - return FALSE; - - *length = size / 4; - - return TRUE; -} - -/** - * gvdb_table_get_names: - * @table: a #GvdbTable - * @length: the number of items returned, or %NULL - * - * Gets a list of all names contained in @table. - * - * No call to gvdb_table_get_table(), gvdb_table_list() or - * gvdb_table_get_value() will succeed unless it is for one of the - * names returned by this function. - * - * Note that some names that are returned may still fail for all of the - * above calls in the case of the corrupted file. Note also that the - * returned strings may not be utf8. - * - * Returns: a %NULL-terminated list of strings, of length @length - **/ -gchar ** -gvdb_table_get_names (GvdbTable *table, - gint *length) -{ - gchar **names; - gint n_names; - gint filled; - gint total; - gint i; - - /* We generally proceed by iterating over the list of items in the - * hash table (in order of appearance) recording them into an array. - * - * Each item has a parent item (except root items). The parent item - * forms part of the name of the item. We could go fetching the - * parent item chain at the point that we encounter each item but then - * we would need to implement some sort of recursion along with checks - * for self-referential items. - * - * Instead, we do a number of passes. Each pass will build up one - * level of names (starting from the root). We continue to do passes - * until no more items are left. The first pass will only add root - * items and each further pass will only add items whose direct parent - * is an item added in the immediately previous pass. It's also - * possible that items get filled if they follow their parent within a - * particular pass. - * - * At most we will have a number of passes equal to the depth of the - * tree. Self-referential items will never be filled in (since their - * parent will have never been filled in). We continue until we have - * a pass that fills in no additional items. - * - * This takes an O(n) algorithm and turns it into O(n*m) where m is - * the depth of the tree, but in all sane cases the tree won't be very - * deep and the constant factor of this algorithm is lower (and the - * complexity of coding it, as well). - */ - - n_names = table->n_hash_items; - names = g_new0 (gchar *, n_names + 1); - - /* 'names' starts out all-NULL. On each pass we record the number - * of items changed from NULL to non-NULL in 'filled' so we know if we - * should repeat the loop. 'total' counts the total number of items - * filled. If 'total' ends up equal to 'n_names' then we know that - * 'names' has been completely filled. - */ - - total = 0; - do - { - /* Loop until we have filled no more entries */ - filled = 0; - - for (i = 0; i < n_names; i++) - { - const struct gvdb_hash_item *item = &table->hash_items[i]; - const gchar *name; - gsize name_length; - guint32 parent; - - /* already got it on a previous pass */ - if (names[i] != NULL) - continue; - - parent = guint32_from_le (item->parent); - - if (parent == 0xffffffffu) - { - /* it's a root item */ - name = gvdb_table_item_get_key (table, item, &name_length); - - if (name != NULL) - { - names[i] = g_strndup (name, name_length); - filled++; - } - } - - else if (parent < n_names && names[parent] != NULL) - { - /* It's a non-root item whose parent was filled in already. - * - * Calculate the name of this item by combining it with - * its parent name. - */ - name = gvdb_table_item_get_key (table, item, &name_length); - - if (name != NULL) - { - const gchar *parent_name = names[parent]; - gsize parent_length; - gchar *fullname; - - parent_length = strlen (parent_name); - fullname = g_malloc (parent_length + name_length + 1); - memcpy (fullname, parent_name, parent_length); - memcpy (fullname + parent_length, name, name_length); - fullname[parent_length + name_length] = '\0'; - names[i] = fullname; - filled++; - } - } - } - - total += filled; - } - while (filled && total < n_names); - - /* If the table was corrupted then 'names' may have holes in it. - * Collapse those. - */ - if G_UNLIKELY (total != n_names) - { - GPtrArray *fixed_names; - - fixed_names = g_ptr_array_new (); - for (i = 0; i < n_names; i++) - if (names[i] != NULL) - g_ptr_array_add (fixed_names, names[i]); - - g_free (names); - n_names = fixed_names->len; - g_ptr_array_add (fixed_names, NULL); - names = (gchar **) g_ptr_array_free (fixed_names, FALSE); - } - - if (length) - *length = n_names; - - return names; -} - -/** - * gvdb_table_list: - * @file: a #GvdbTable - * @key: a string - * @returns: a %NULL-terminated string array - * - * List all of the keys that appear below @key. The nesting of keys - * within the hash file is defined by the program that created the hash - * file. One thing is constant: each item in the returned array can be - * concatenated to @key to obtain the full name of that key. - * - * It is not possible to tell from this function if a given key is - * itself a path, a value, or another hash table; you are expected to - * know this for yourself. - * - * You should call g_strfreev() on the return result when you no longer - * require it. - **/ -gchar ** -gvdb_table_list (GvdbTable *file, - const gchar *key) -{ - const struct gvdb_hash_item *item; - const guint32_le *list; - gchar **strv; - guint length; - guint i; - - if ((item = gvdb_table_lookup (file, key, 'L')) == NULL) - return NULL; - - if (!gvdb_table_list_from_item (file, item, &list, &length)) - return NULL; - - strv = g_new (gchar *, length + 1); - for (i = 0; i < length; i++) - { - guint32 itemno = guint32_from_le (list[i]); - - if (itemno < file->n_hash_items) - { - const struct gvdb_hash_item *item; - const gchar *string; - gsize strsize; - - item = file->hash_items + itemno; - - string = gvdb_table_item_get_key (file, item, &strsize); - - if (string != NULL) - strv[i] = g_strndup (string, strsize); - else - strv[i] = g_malloc0 (1); - } - else - strv[i] = g_malloc0 (1); - } - - strv[i] = NULL; - - return strv; -} - -/** - * gvdb_table_has_value: - * @file: a #GvdbTable - * @key: a string - * @returns: %TRUE if @key is in the table - * - * Checks for a value named @key in @file. - * - * Note: this function does not consider non-value nodes (other hash - * tables, for example). - **/ -gboolean -gvdb_table_has_value (GvdbTable *file, - const gchar *key) -{ - static const struct gvdb_hash_item *item; - gsize size; - - item = gvdb_table_lookup (file, key, 'v'); - - if (item == NULL) - return FALSE; - - return gvdb_table_dereference (file, &item->value.pointer, 8, &size) != NULL; -} - -static GVariant * -gvdb_table_value_from_item (GvdbTable *table, - const struct gvdb_hash_item *item) -{ - GVariant *variant, *value; - gconstpointer data; - GBytes *bytes; - gsize size; - - data = gvdb_table_dereference (table, &item->value.pointer, 8, &size); - - if G_UNLIKELY (data == NULL) - return NULL; - - bytes = g_bytes_new_from_bytes (table->bytes, ((gchar *) data) - table->data, size); - variant = g_variant_new_from_bytes (G_VARIANT_TYPE_VARIANT, bytes, table->trusted); - value = g_variant_get_variant (variant); - g_variant_unref (variant); - g_bytes_unref (bytes); - - return value; -} - -/** - * gvdb_table_get_value: - * @file: a #GvdbTable - * @key: a string - * @returns: a #GVariant, or %NULL - * - * Looks up a value named @key in @file. - * - * If the value is not found then %NULL is returned. Otherwise, a new - * #GVariant instance is returned. The #GVariant does not depend on the - * continued existence of @file. - * - * You should call g_variant_unref() on the return result when you no - * longer require it. - **/ -GVariant * -gvdb_table_get_value (GvdbTable *file, - const gchar *key) -{ - const struct gvdb_hash_item *item; - GVariant *value; - - if ((item = gvdb_table_lookup (file, key, 'v')) == NULL) - return NULL; - - value = gvdb_table_value_from_item (file, item); - - if (value && file->byteswapped) - { - GVariant *tmp; - - tmp = g_variant_byteswap (value); - g_variant_unref (value); - value = tmp; - } - - return value; -} - -/** - * gvdb_table_get_raw_value: - * @table: a #GvdbTable - * @key: a string - * @returns: a #GVariant, or %NULL - * - * Looks up a value named @key in @file. - * - * This call is equivalent to gvdb_table_get_value() except that it - * never byteswaps the value. - **/ -GVariant * -gvdb_table_get_raw_value (GvdbTable *table, - const gchar *key) -{ - const struct gvdb_hash_item *item; - - if ((item = gvdb_table_lookup (table, key, 'v')) == NULL) - return NULL; - - return gvdb_table_value_from_item (table, item); -} - -/** - * gvdb_table_get_table: - * @file: a #GvdbTable - * @key: a string - * @returns: a new #GvdbTable, or %NULL - * - * Looks up the hash table named @key in @file. - * - * The toplevel hash table in a #GvdbTable can contain reference to - * child hash tables (and those can contain further references...). - * - * If @key is not found in @file then %NULL is returned. Otherwise, a - * new #GvdbTable is returned, referring to the child hashtable as - * contained in the file. This newly-created #GvdbTable does not depend - * on the continued existence of @file. - * - * You should call gvdb_table_free() on the return result when you no - * longer require it. - **/ -GvdbTable * -gvdb_table_get_table (GvdbTable *file, - const gchar *key) -{ - const struct gvdb_hash_item *item; - GvdbTable *new; - - item = gvdb_table_lookup (file, key, 'H'); - - if (item == NULL) - return NULL; - - new = g_slice_new0 (GvdbTable); - new->bytes = g_bytes_ref (file->bytes); - new->byteswapped = file->byteswapped; - new->trusted = file->trusted; - new->data = file->data; - new->size = file->size; - - gvdb_table_setup_root (new, &item->value.pointer); - - return new; -} - -/** - * gvdb_table_free: - * @file: a #GvdbTable - * - * Frees @file. - **/ -void -gvdb_table_free (GvdbTable *file) -{ - g_bytes_unref (file->bytes); - g_slice_free (GvdbTable, file); -} - -/** - * gvdb_table_is_valid: - * @table: a #GvdbTable - * @returns: %TRUE if @table is still valid - * - * Checks if the table is still valid. - * - * An on-disk GVDB can be marked as invalid. This happens when the file - * has been replaced. The appropriate action is typically to reopen the - * file. - **/ -gboolean -gvdb_table_is_valid (GvdbTable *table) -{ - return !!*table->data; -} diff --git a/common/gvdb/gvdb-reader.h b/common/gvdb/gvdb-reader.h deleted file mode 100644 index 241b41ae..00000000 --- a/common/gvdb/gvdb-reader.h +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright © 2010 Codethink Limited - * - * 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 licence, 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 <http://www.gnu.org/licenses/>. - * - * Author: Ryan Lortie <desrt@desrt.ca> - */ - -#ifndef __gvdb_reader_h__ -#define __gvdb_reader_h__ - -#include <glib.h> - -typedef struct _GvdbTable GvdbTable; - -G_BEGIN_DECLS - -G_GNUC_INTERNAL -GvdbTable * gvdb_table_new_from_bytes (GBytes *bytes, - gboolean trusted, - GError **error); -G_GNUC_INTERNAL -GvdbTable * gvdb_table_new (const gchar *filename, - gboolean trusted, - GError **error); -G_GNUC_INTERNAL -void gvdb_table_free (GvdbTable *table); -G_GNUC_INTERNAL -gchar ** gvdb_table_get_names (GvdbTable *table, - gint *length); -G_GNUC_INTERNAL -gchar ** gvdb_table_list (GvdbTable *table, - const gchar *key); -G_GNUC_INTERNAL -GvdbTable * gvdb_table_get_table (GvdbTable *table, - const gchar *key); -G_GNUC_INTERNAL -GVariant * gvdb_table_get_raw_value (GvdbTable *table, - const gchar *key); -G_GNUC_INTERNAL -GVariant * gvdb_table_get_value (GvdbTable *table, - const gchar *key); - -G_GNUC_INTERNAL -gboolean gvdb_table_has_value (GvdbTable *table, - const gchar *key); -G_GNUC_INTERNAL -gboolean gvdb_table_is_valid (GvdbTable *table); - -G_END_DECLS - -#endif /* __gvdb_reader_h__ */ diff --git a/common/gvdb/gvdb.doap b/common/gvdb/gvdb.doap deleted file mode 100644 index b4ae60c8..00000000 --- a/common/gvdb/gvdb.doap +++ /dev/null @@ -1,32 +0,0 @@ -<?xml version='1.0' encoding='utf-8'?> - -<Project xmlns='http://usefulinc.com/ns/doap#' - xmlns:foaf='http://xmlns.com/foaf/0.1/' - xmlns:rdf='http://www.w3.org/1999/02/22-rdf-syntax-ns#' - xmlns:gnome='http://api.gnome.org/doap-extensions#'> - - <name xml:lang='en'>gvdb</name> - <shortdesc xml:lang='en'>GVariant Database file</shortdesc> - <description xml:lang='en'> - A simple database file format that stores a mapping from strings to - GVariant values in a way that is extremely efficient for lookups. - - The database is written once and can not be modified. - - Included here is reader code and a first-pass implementation of a - writer (that does not currently produce particularly optimised - output). - - It is intended that this code be used by copy-pasting into your - project or by making use of git-merge(1). - </description> - - <maintainer> - <foaf:Person> - <foaf:name>Ryan Lortie</foaf:name> - <foaf:mbox rdf:resource='mailto:desrt@desrt.ca'/> - <gnome:userid>ryanl</gnome:userid> - </foaf:Person> - </maintainer> - -</Project> diff --git a/data/Makefile.am.inc b/data/Makefile.am.inc index 71e514e6..9bfe6782 100644 --- a/data/Makefile.am.inc +++ b/data/Makefile.am.inc @@ -1,13 +1,10 @@ introspectiondir = $(datadir)/dbus-1/interfaces introspection_DATA = \ - data/org.freedesktop.impl.portal.PermissionStore.xml \ - data/org.freedesktop.portal.Documents.xml \ data/org.freedesktop.Flatpak.xml \ $(NULL) EXTRA_DIST += \ data/org.freedesktop.portal.Documents.xml \ - data/org.freedesktop.impl.portal.PermissionStore.xml \ data/org.freedesktop.systemd1.xml \ data/org.freedesktop.Flatpak.xml \ $(NULL) diff --git a/data/org.freedesktop.impl.portal.PermissionStore.xml b/data/org.freedesktop.impl.portal.PermissionStore.xml deleted file mode 100644 index 0e59e386..00000000 --- a/data/org.freedesktop.impl.portal.PermissionStore.xml +++ /dev/null @@ -1,163 +0,0 @@ -<!DOCTYPE node PUBLIC -"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" -"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"> - -<!-- - Copyright (C) 2015 Red Hat, Inc. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General - Public License along with this library; if not, write to the - Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. - - Author: Alexander Larsson <alexl@redhat.com> ---> - -<node name="/" xmlns:doc="http://www.freedesktop.org/dbus/1.0/doc.dtd"> - <!-- - org.freedesktop.impl.portal.PermissionStore: - @short_description: Database to store permissions - - The permission store can be used by portals to store permissions - that sandboxed applications have to various resources, such as - files outside the sandbox. - - Since the resources managed by portals can be varied, the permission - store is fairly free-form: there can be multiple tables; resources are - identified by an ID, as are applications, and permissions are stored as - string arrays. None of these strings are interpreted by the permission - store in any way. - - In addition, the permission store allows to associate extra data - (in the form of a GVariant) with each resource. - --> - <interface name='org.freedesktop.impl.portal.PermissionStore'> - <property name="version" type="u" access="read"/> - - <!-- - Lookup: - @table: the name of the table to use - @id: the resource ID to look up - @permissions: map from application ID to permissions - @data: data that is associated with the resource - - Looks up the entry for a resource in one of the tables and returns - all associated application permissions and data. - --> - <method name="Lookup"> - <arg name='table' type='s' direction='in'/> - <arg name='id' type='s' direction='in'/> - <arg name='permissions' type='a{sas}' direction='out'/> - <arg name='data' type='v' direction='out'/> - </method> - - <!-- - Set: - @table: the name of the table to use - @create: whether to create the table if it does not exist - @id: the resource ID to modify - @app_permissions: map from application ID to permissions - @data: data to associate with the resource - - Writes the entry for a resource in the given table. - --> - <method name="Set"> - <arg name='table' type='s' direction='in'/> - <arg name='create' type='b' direction='in'/> - <arg name='id' type='s' direction='in'/> - <arg name='app_permissions' type='a{sas}' direction='in'/> - <arg name='data' type='v' direction='in'/> - </method> - - <!-- - Delete: - @table: the name of the table to use - @id: the resource ID to delete - - Removes the entry for a resource in the given table. - --> - <method name="Delete"> - <arg name='table' type='s' direction='in'/> - <arg name='id' type='s' direction='in'/> - </method> - - <!-- - SetValue: - @table: the name of the table to use - @create: whether to create the table if it does not exist - @id: the resource ID to modify - @data: data to associate with the resource - - Sets just the data for a resource in the given table. - --> - <method name="SetValue"> - <arg name='table' type='s' direction='in'/> - <arg name='create' type='b' direction='in'/> - <arg name='id' type='s' direction='in'/> - <arg name='data' type='v' direction='in'/> - </method> - - <!-- - SetPermission: - @table: the name of the table to use - @create: whether to create the table if it does not exist - @id: the resource ID to modify - @app: the application ID to modify - @permissions: permissions to set - - Sets the permissions for an application and a resource - in the given table. - --> - <method name="SetPermission"> - <arg name='table' type='s' direction='in'/> - <arg name='create' type='b' direction='in'/> - <arg name='id' type='s' direction='in'/> - <arg name='app' type='s' direction='in'/> - <arg name='permissions' type='as' direction='in'/> - </method> - - <!-- - List: - @table: the name of the table to use - @ids: IDs of all resources that are present in the table - - Returns all the resources that are present in the table. - --> - <method name="List"> - <arg name='table' type='s' direction='in'/> - <arg name='ids' type='as' direction='out'/> - </method> - - <!-- - Changed: - @table: the name of the table - @ids: IDs of the changed resource - @deleted: whether the resource was deleted - @data: the data that is associated the resource - @permissions: the permissions that are associated with the resource - - The Changed signal is emitted when the entry for a resource - is modified or deleted. If the entry was deleted, then @data - and @permissions contain the last values that were found in the - database. If the entry was modified, they contain the new values. - --> - <signal name="Changed"> - <arg name='table' type='s' direction='out'/> - <arg name='id' type='s' direction='out'/> - <arg name='deleted' type='b' direction='out'/> - <arg name='data' type='v' direction='out'/> - <arg name='permissions' type='a{sas}' direction='out'/> - </signal> - </interface> - -</node> diff --git a/document-portal/Makefile.am.inc b/document-portal/Makefile.am.inc deleted file mode 100644 index f011ccdf..00000000 --- a/document-portal/Makefile.am.inc +++ /dev/null @@ -1,43 +0,0 @@ -libexec_PROGRAMS += \ - xdg-document-portal \ - $(NULL) - -xdp_dbus_built_sources = document-portal/xdp-dbus.c document-portal/xdp-dbus.h -BUILT_SOURCES += $(xdp_dbus_built_sources) - -document-portal/xdp-dbus.c: data/org.freedesktop.portal.Documents.xml Makefile - mkdir -p $(builddir)/document-portal - $(AM_V_GEN) $(GDBUS_CODEGEN) \ - --interface-prefix org.freedesktop.portal. \ - --c-namespace XdpDbus \ - --generate-c-code $(builddir)/document-portal/xdp-dbus \ - --annotate "org.freedesktop.portal.Documents.Add()" org.gtk.GDBus.C.UnixFD "yes" \ - --annotate "org.freedesktop.portal.Documents.AddFull()" org.gtk.GDBus.C.UnixFD "yes" \ - $(srcdir)/data/org.freedesktop.portal.Documents.xml \ - $(NULL) - -document-portal/%-dbus.h: document-portal/%-dbus.c - @true # Built as a side-effect of the rules for the .c - -service_in_files += document-portal/xdg-document-portal.service.in -systemduserunit_DATA += document-portal/xdg-document-portal.service - -service_in_files += document-portal/org.freedesktop.portal.Documents.service.in -dbus_service_DATA += document-portal/org.freedesktop.portal.Documents.service - -nodist_xdg_document_portal_SOURCES = \ - $(xdp_dbus_built_sources) \ - $(ps_dbus_built_sources) \ - $(NULL) - -xdg_document_portal_SOURCES = \ - document-portal/xdp-main.c \ - document-portal/xdp-enums.h \ - document-portal/xdp-util.h \ - document-portal/xdp-util.c \ - document-portal/xdp-fuse.h \ - document-portal/xdp-fuse.c \ - $(NULL) - -xdg_document_portal_LDADD = $(AM_LDADD) $(BASE_LIBS) $(FUSE_LIBS) libflatpak-common.la -xdg_document_portal_CFLAGS = $(AM_CFLAGS) $(BASE_CFLAGS) $(OSTREE_CFLAGS) $(SOUP_CFLAGS) $(JSON_CFLAGS) $(FUSE_CFLAGS) -I$(srcdir)/document-portal -I$(builddir)/document-portal -I$(srcdir)/permission-store -I$(builddir)/permission-store -DFLATPAK_COMPILATION diff --git a/document-portal/org.freedesktop.portal.Documents.service.in b/document-portal/org.freedesktop.portal.Documents.service.in deleted file mode 100644 index cf0c1efc..00000000 --- a/document-portal/org.freedesktop.portal.Documents.service.in +++ /dev/null @@ -1,4 +0,0 @@ -[D-BUS Service] -Name=org.freedesktop.portal.Documents -Exec=@libexecdir@/xdg-document-portal -SystemdService=xdg-document-portal.service diff --git a/document-portal/xdg-document-portal.service.in b/document-portal/xdg-document-portal.service.in deleted file mode 100644 index 66f9cbd7..00000000 --- a/document-portal/xdg-document-portal.service.in +++ /dev/null @@ -1,7 +0,0 @@ -[Unit] -Description=flatpak document portal service - -[Service] -BusName=org.freedesktop.portal.Documents -ExecStart=@libexecdir@/xdg-document-portal -Type=dbus diff --git a/document-portal/xdp-enums.h b/document-portal/xdp-enums.h deleted file mode 100644 index f9cd223d..00000000 --- a/document-portal/xdp-enums.h +++ /dev/null @@ -1,25 +0,0 @@ -#ifndef XDP_ENUMS_H -#define XDP_ENUMS_H - -G_BEGIN_DECLS - -typedef enum { - XDP_PERMISSION_FLAGS_READ = (1 << 0), - XDP_PERMISSION_FLAGS_WRITE = (1 << 1), - XDP_PERMISSION_FLAGS_GRANT_PERMISSIONS = (1 << 2), - XDP_PERMISSION_FLAGS_DELETE = (1 << 3), - - XDP_PERMISSION_FLAGS_ALL = ((1 << 4) - 1) -} XdpPermissionFlags; - -typedef enum { - XDP_ADD_FLAGS_REUSE_EXISTING = (1 << 0), - XDP_ADD_FLAGS_PERSISTENT = (1 << 1), - XDP_ADD_FLAGS_AS_NEEDED_BY_APP = (1 << 2), - - XDP_ADD_FLAGS_FLAGS_ALL = ((1 << 3) - 1) -} XdpAddFullFlags; - -G_END_DECLS - -#endif /* XDP_ENUMS_H */ diff --git a/document-portal/xdp-fuse.c b/document-portal/xdp-fuse.c deleted file mode 100644 index 0d2f413f..00000000 --- a/document-portal/xdp-fuse.c +++ /dev/null @@ -1,2369 +0,0 @@ -#include "config.h" - -#define FUSE_USE_VERSION 26 - -#include <glib-unix.h> - -#include <fuse_lowlevel.h> -#include <stdio.h> -#include <string.h> -#include <errno.h> -#include <fcntl.h> -#include <stdlib.h> -#include <assert.h> -#include <glib/gprintf.h> -#include <gio/gio.h> -#include <pthread.h> -#include <sys/statfs.h> - -#include "flatpak-portal-error.h" -#include "xdp-fuse.h" -#include "xdp-util.h" -#include "flatpak-utils.h" - -#define NON_DOC_DIR_PERMS 0500 -#define DOC_DIR_PERMS 0700 - -/* TODO: What do we put here */ -#define ATTR_CACHE_TIME 60.0 -#define ENTRY_CACHE_TIME 60.0 - -/* We pretend that the file is hardlinked. This causes most apps to do - a truncating overwrite, which suits us better, as we do the atomic - rename ourselves anyway. */ -#define DOC_FILE_NLINK 2 - -typedef enum { - XDP_INODE_ROOT, - XDP_INODE_BY_APP, - XDP_INODE_APP_DIR, /* app id */ - XDP_INODE_APP_DOC_DIR, /* app_id + doc id */ - XDP_INODE_DOC_DIR, /* doc id */ - XDP_INODE_DOC_FILE, /* doc id (NULL if tmp), name (== basename) */ -} XdpInodeType; - -typedef struct _XdpInode XdpInode; - -struct _XdpInode -{ - gint ref_count; /* atomic */ - - /* These are all immutable */ - fuse_ino_t ino; - XdpInodeType type; - XdpInode *parent; - char *app_id; - char *doc_id; - - /* For doc dirs */ - char *basename; - char *dirname; - dev_t dir_dev; - ino_t dir_ino; - - /* mutable data */ - - GList *children; /* lazily filled, protected by inodes lock */ - char *filename; /* variable (for non-dirs), null if deleted, - protected by inodes lock *and* mutex */ - gboolean is_doc; /* True if this is the document file for this dir */ - - /* Used when the file is open, protected by mutex */ - GMutex mutex; /* Always lock inodes lock (if needed) before mutex */ - GList *open_files; - int dir_fd; - int fd; /* RW fd for tempfiles, RO fd for doc files */ - char *backing_filename; - char *trunc_filename; - int trunc_fd; - gboolean truncated; -}; - -typedef struct _XdpFile XdpFile; - -struct _XdpFile -{ - XdpInode *inode; - int open_mode; -}; - -#define ROOT_INODE 1 -#define BY_APP_INODE 2 -#define BY_APP_NAME "by-app" - -static GHashTable *dir_to_inode_nr; - -static GHashTable *inodes; /* The in memory XdpInode:s, protected by inodes lock */ -static XdpInode *root_inode; -static XdpInode *by_app_inode; -static fuse_ino_t next_inode_nr = 3; - -G_LOCK_DEFINE (inodes); - -static GThread *fuse_thread = NULL; -static struct fuse_session *session = NULL; -static struct fuse_chan *main_ch = NULL; -static char *mount_path = NULL; -static pthread_t fuse_pthread = 0; - -static int -reopen_fd (int fd, int flags) -{ - g_autofree char *path = g_strdup_printf ("/proc/self/fd/%d", fd); - - return open (path, flags | O_CLOEXEC); -} - -/* Call with inodes lock held */ -static fuse_ino_t -allocate_inode_unlocked (void) -{ - fuse_ino_t next = next_inode_nr++; - - /* Bail out on overflow, to avoid reuse */ - if (next <= 0) - g_assert_not_reached (); - - return next; -} - -static fuse_ino_t -get_dir_inode_nr_unlocked (const char *app_id, const char *doc_id) -{ - gpointer res; - fuse_ino_t allocated; - g_autofree char *dir = NULL; - - if (app_id == NULL) - { - dir = g_strdup (doc_id); - } - else - { - if (doc_id == NULL) - dir = g_strconcat (app_id, "/", NULL); - else - dir = g_build_filename (app_id, doc_id, NULL); - } - - res = g_hash_table_lookup (dir_to_inode_nr, dir); - if (res != NULL) - return (fuse_ino_t) (gsize) res; - - allocated = allocate_inode_unlocked (); - g_hash_table_insert (dir_to_inode_nr, g_strdup (dir), (gpointer) allocated); - return allocated; -} - -static fuse_ino_t -get_dir_inode_nr (const char *app_id, const char *doc_id) -{ - AUTOLOCK (inodes); - return get_dir_inode_nr_unlocked (app_id, doc_id); -} - -static void -allocate_app_dir_inode_nr (char **app_ids) -{ - int i; - - AUTOLOCK (inodes); - for (i = 0; app_ids[i] != NULL; i++) - get_dir_inode_nr_unlocked (app_ids[i], NULL); -} - -static char ** -get_allocated_app_dirs (void) -{ - GHashTableIter iter; - gpointer key, value; - GPtrArray *array = g_ptr_array_new (); - - AUTOLOCK (inodes); - g_hash_table_iter_init (&iter, dir_to_inode_nr); - while (g_hash_table_iter_next (&iter, &key, &value)) - { - const char *name = key; - - if (g_str_has_suffix (name, "/")) - { - char *app = strndup (name, strlen (name) - 1); - g_ptr_array_add (array, app); - } - } - g_ptr_array_add (array, NULL); - return (char **) g_ptr_array_free (array, FALSE); -} - -static void xdp_inode_unref_internal (XdpInode *inode, - gboolean locked); -static void xdp_inode_unref (XdpInode *inode); - -G_DEFINE_AUTOPTR_CLEANUP_FUNC (XdpInode, xdp_inode_unref) - -static void -xdp_inode_destroy (XdpInode *inode, gboolean locked) -{ - g_assert (inode->dir_fd == -1); - g_assert (inode->fd == -1); - g_assert (inode->trunc_fd == -1); - g_assert (inode->trunc_filename == NULL); - g_assert (inode->children == NULL); - xdp_inode_unref_internal (inode->parent, locked); - g_free (inode->backing_filename); - g_free (inode->filename); - g_free (inode->dirname); - g_free (inode->app_id); - g_free (inode->doc_id); - g_free (inode); -} - -static XdpInode * -xdp_inode_ref (XdpInode *inode) -{ - if (inode) - g_atomic_int_inc (&inode->ref_count); - return inode; -} - -static void -xdp_inode_unref_internal (XdpInode *inode, gboolean locked) -{ - gint old_ref; - - if (inode == NULL) - return; - - /* here we want to atomically do: if (ref_count>1) { ref_count--; return; } */ -retry_atomic_decrement1: - old_ref = g_atomic_int_get (&inode->ref_count); - if (old_ref > 1) - { - if (!g_atomic_int_compare_and_exchange ((int *) &inode->ref_count, old_ref, old_ref - 1)) - goto retry_atomic_decrement1; - } - else - { - if (old_ref <= 0) - { - g_warning ("Can't unref dead inode"); - return; - } - /* Protect against revival from xdp_inode_lookup() */ - if (!locked) - G_LOCK (inodes); - if (!g_atomic_int_compare_and_exchange ((int *) &inode->ref_count, old_ref, old_ref - 1)) - { - if (!locked) - G_UNLOCK (inodes); - goto retry_atomic_decrement1; - } - - g_hash_table_remove (inodes, (gpointer) inode->ino); - if (inode->parent) - inode->parent->children = g_list_remove (inode->parent->children, inode); - - if (!locked) - G_UNLOCK (inodes); - - xdp_inode_destroy (inode, locked); - } -} - -static void -xdp_inode_unref (XdpInode *inode) -{ - return xdp_inode_unref_internal (inode, FALSE); -} - -static XdpInode * -xdp_inode_new_unlocked (fuse_ino_t ino, - XdpInodeType type, - XdpInode *parent, - const char *filename, - const char *app_id, - const char *doc_id) -{ - XdpInode *inode; - - inode = g_new0 (XdpInode, 1); - inode->ino = ino; - inode->type = type; - inode->parent = xdp_inode_ref (parent); - inode->filename = g_strdup (filename); - inode->app_id = g_strdup (app_id); - inode->doc_id = g_strdup (doc_id); - inode->ref_count = 1; - inode->dir_fd = -1; - inode->fd = -1; - inode->trunc_fd = -1; - - if (parent) - parent->children = g_list_prepend (parent->children, inode); - g_hash_table_insert (inodes, (gpointer) ino, inode); - - return inode; -} - -static XdpInode * -xdp_inode_new (fuse_ino_t ino, - XdpInodeType type, - XdpInode *parent, - const char *filename, - const char *app_id, - const char *doc_id) -{ - AUTOLOCK (inodes); - return xdp_inode_new_unlocked (ino, type, parent, filename, app_id, doc_id); -} - -static XdpInode * -xdp_inode_lookup_unlocked (fuse_ino_t inode_nr) -{ - XdpInode *inode; - - inode = g_hash_table_lookup (inodes, (gpointer) inode_nr); - if (inode != NULL) - return xdp_inode_ref (inode); - return NULL; -} - -static GList * -xdp_inode_list_children (XdpInode *inode) -{ - GList *list = NULL, *l; - - AUTOLOCK (inodes); - for (l = inode->children; l != NULL; l = l->next) - { - XdpInode *child = l->data; - - list = g_list_prepend (list, xdp_inode_ref (child)); - } - - return g_list_reverse (list); -} - -static XdpInode * -xdp_inode_lookup_child_unlocked (XdpInode *inode, const char *filename) -{ - GList *l; - - for (l = inode->children; l != NULL; l = l->next) - { - XdpInode *child = l->data; - if (child->filename != NULL && strcmp (child->filename, filename) == 0) - return xdp_inode_ref (child); - } - - return NULL; -} - -static XdpInode * -xdp_inode_lookup_child (XdpInode *inode, const char *filename) -{ - AUTOLOCK (inodes); - return xdp_inode_lookup_child_unlocked (inode, filename); -} - -static int -xdp_inode_open_dir_fd (XdpInode *dir) -{ - struct stat st_buf; - glnx_autofd int fd = -1; - - g_assert (dir->dirname != NULL); - - fd = open (dir->dirname, O_CLOEXEC | O_PATH | O_DIRECTORY); - if (fd == -1) - return -1; - - if (fstat (fd, &st_buf) < 0) - { - errno = ENOENT; - return -1; - } - - if (st_buf.st_ino != dir->dir_ino || - st_buf.st_dev != dir->dir_dev) - { - errno = ENOENT; - return -1; - } - - return glnx_steal_fd (&fd); -} - -static void -xdp_inode_unlink_backing_files (XdpInode *child_inode, int dir_fd) -{ - if (dir_fd == -1) - { - g_debug ("Can't unlink child inode due to no dir_fd"); - return; - } - - if (child_inode->is_doc) - { - g_debug ("unlinking doc file %s", child_inode->filename); - unlinkat (dir_fd, child_inode->filename, 0); - if (child_inode->trunc_filename != NULL) - { - g_debug ("unlinking doc trunc_file %s", child_inode->trunc_filename); - unlinkat (dir_fd, child_inode->trunc_filename, 0); - } - } - else - { - g_debug ("unlinking tmp_file %s", child_inode->backing_filename); - unlinkat (dir_fd, child_inode->backing_filename, 0); - } -} - -static void -xdp_inode_do_unlink (XdpInode *child_inode, int dir_fd, gboolean unlink_backing) -{ - if (unlink_backing) - xdp_inode_unlink_backing_files (child_inode, dir_fd); - - /* Zero out filename to mark it deleted */ - g_free (child_inode->filename); - child_inode->filename = NULL; - - /* Drop keep-alive-until-unlink ref */ - if (!child_inode->is_doc) - xdp_inode_unref (child_inode); -} - -static XdpInode * -xdp_inode_unlink_child (XdpInode *dir, const char *filename) -{ - XdpInode *child_inode; - glnx_autofd int dir_fd = -1; - - AUTOLOCK (inodes); - child_inode = xdp_inode_lookup_child_unlocked (dir, filename); - if (child_inode == NULL) - return NULL; - - g_assert (child_inode->type == XDP_INODE_DOC_FILE); - g_assert (child_inode->filename != NULL); - - /* Here we take *both* the inodes lock and the mutex. - The inodes lock is to make this safe against concurrent lookups, - but the mutex is to make it safe to access inode->filename inside - a mutex-only lock */ - g_mutex_lock (&child_inode->mutex); - - dir_fd = xdp_inode_open_dir_fd (dir); - - xdp_inode_do_unlink (child_inode, dir_fd, TRUE); - - g_mutex_unlock (&child_inode->mutex); - - return child_inode; -} - -/* Sets errno */ -static int -xdp_inode_rename_child (XdpInode *dir, - const char *src_filename, - const char *dst_filename) -{ - g_autoptr(XdpInode) src_inode = NULL; - g_autoptr(XdpInode) dst_inode = NULL; - glnx_autofd int dir_fd = -1; - int res; - - AUTOLOCK (inodes); - src_inode = xdp_inode_lookup_child_unlocked (dir, src_filename); - if (src_inode == NULL) - { - errno = ENOENT; - return -1; - } - - g_assert (src_inode->type == XDP_INODE_DOC_FILE); - g_assert (src_inode->filename != NULL); - - dst_inode = xdp_inode_lookup_child_unlocked (dir, dst_filename); - if (dst_inode) - { - g_assert (dst_inode->type == XDP_INODE_DOC_FILE); - g_assert (dst_inode->filename != NULL); - } - - /* Here we take *both* the inodes lock and the mutex. - The inodes lock is to make this safe against concurrent lookups, - but the mutex is to make it safe to access inode->filename inside - a mutex-only lock */ - g_mutex_lock (&src_inode->mutex); - if (dst_inode) - g_mutex_lock (&dst_inode->mutex); - - dir_fd = xdp_inode_open_dir_fd (dir); - res = 0; - - if (src_inode->is_doc) - { - /* doc -> tmp */ - - /* We don't want to allow renaming an exiting doc file, because - doing so would make a tmpfile of the real doc-file which some - host-side app may have open. You have to make a copy and - remove instead. */ - errno = EACCES; - res = -1; - } - else if (strcmp (dst_filename, dir->basename) != 0) - { - /* tmp -> tmp */ - - if (dst_inode) - xdp_inode_do_unlink (dst_inode, dir_fd, TRUE); - - g_free (src_inode->filename); - src_inode->filename = g_strdup (dst_filename); - } - else - { - /* tmp -> doc */ - - g_debug ("atomic renaming %s to %s", src_inode->backing_filename, dst_filename); - res = renameat (dir_fd, src_inode->backing_filename, - dir_fd, dst_filename); - if (res == 0) - { - if (dst_inode != NULL) - { - /* Unlink, but don't remove backing files, which are now the new one */ - xdp_inode_do_unlink (dst_inode, dir_fd, FALSE); - - /* However, unlink trunc_file if its there */ - if (dst_inode->trunc_filename) - unlinkat (dir_fd, dst_inode->trunc_filename, 0); - } - - src_inode->is_doc = TRUE; - g_free (src_inode->filename); - src_inode->filename = g_strdup (dst_filename); - g_free (src_inode->backing_filename); - src_inode->backing_filename = g_strdup (dst_filename); - - /* Convert ->fd to read-only */ - if (src_inode->fd != -1) - { - int new_fd = reopen_fd (src_inode->fd, O_RDONLY); - close (src_inode->fd); - src_inode->fd = new_fd; - } - - /* This neuters any outstanding write files, since we have no trunc_fd at this point. - However, that is not really a problem, we would not support them well anyway as - a newly opened trunc file would have to have a truncate operation initially for - it to work anyway */ - } - } - - g_mutex_unlock (&src_inode->mutex); - if (dst_inode) - g_mutex_unlock (&dst_inode->mutex); - - return res; -} - -/* NULL if removed */ -static char * -xdp_inode_get_filename (XdpInode *inode) -{ - AUTOLOCK (inodes); - return g_strdup (inode->filename); -} - -static XdpInode * -xdp_inode_ensure_document_file (XdpInode *dir) -{ - XdpInode *inode; - - g_assert (dir->type == XDP_INODE_APP_DOC_DIR || dir->type == XDP_INODE_DOC_DIR); - - AUTOLOCK (inodes); - - inode = xdp_inode_lookup_child_unlocked (dir, dir->basename); - if (inode == NULL) - { - inode = xdp_inode_new_unlocked (allocate_inode_unlocked (), - XDP_INODE_DOC_FILE, - dir, - dir->basename, - dir->app_id, - dir->doc_id); - inode->backing_filename = g_strdup (dir->basename); - inode->is_doc = TRUE; - } - - return inode; -} - -static char * -create_tmp_for_doc (XdpInode *dir, int dir_fd, int flags, mode_t mode, int *fd_out) -{ - g_autofree char *template = g_strconcat (".xdp_", dir->basename, ".XXXXXX", NULL); - int fd; - - fd = flatpak_mkstempat (dir_fd, template, flags | O_CLOEXEC, mode); - if (fd == -1) - return NULL; - - g_debug ("Created temp file %s", template); - *fd_out = fd; - return g_steal_pointer (&template); -} - -/* sets errno */ -static XdpInode * -xdp_inode_create_file (XdpInode *dir, - const char *filename, - mode_t mode, - gboolean truncate, - gboolean exclusive) -{ - XdpInode *inode; - g_autofree char *backing_filename = NULL; - g_autofree char *trunc_filename = NULL; - gboolean is_doc; - glnx_autofd int dir_fd = -1; - glnx_autofd int fd = -1; - glnx_autofd int trunc_fd = -1; - - g_assert (dir->type == XDP_INODE_APP_DOC_DIR || dir->type == XDP_INODE_DOC_DIR); - - AUTOLOCK (inodes); - - inode = xdp_inode_lookup_child_unlocked (dir, filename); - if (inode != NULL) - { - if (exclusive) - { - xdp_inode_unref (inode); - errno = EEXIST; - return NULL; - } - - if (truncate) - { - /* TODO: Handle extra truncate for existing file */ - errno = ENOSYS; - return NULL; - } - - return inode; - } - - dir_fd = xdp_inode_open_dir_fd (dir); - if (dir_fd == -1) - return NULL; - - is_doc = strcmp (dir->basename, filename) == 0; - - if (is_doc) - { - backing_filename = g_strdup (filename); - int flags = O_CREAT | O_RDONLY | O_NOFOLLOW | O_CLOEXEC; - - if (exclusive) - flags |= O_EXCL; - - g_debug ("Creating doc file %s", dir->basename); - fd = openat (dir_fd, dir->basename, flags, mode & 0777); - if (fd < 0) - return NULL; - - trunc_filename = create_tmp_for_doc (dir, dir_fd, O_RDWR, mode & 0777, &trunc_fd); - if (trunc_filename == NULL) - return NULL; - } - else - { - backing_filename = create_tmp_for_doc (dir, dir_fd, O_RDWR, mode & 0777, &fd); - if (backing_filename == NULL) - return NULL; - } - - inode = xdp_inode_new_unlocked (allocate_inode_unlocked (), - XDP_INODE_DOC_FILE, - dir, - filename, - dir->app_id, - dir->doc_id); - inode->backing_filename = g_steal_pointer (&backing_filename); - inode->trunc_filename = g_steal_pointer (&trunc_filename); - inode->is_doc = is_doc; - inode->dir_fd = glnx_steal_fd (&dir_fd); - inode->fd = glnx_steal_fd (&fd); - inode->trunc_fd = glnx_steal_fd (&trunc_fd); - if (inode->trunc_fd != -1 && (truncate || exclusive)) - { - inode->truncated = TRUE; - g_free (inode->backing_filename); - inode->backing_filename = g_strdup (inode->trunc_filename); - } - - /* We add an extra ref for tmp files to keep them alive until unlink */ - if (!is_doc) - xdp_inode_ref (inode); - - return inode; -} - -static XdpInode * -xdp_inode_lookup (fuse_ino_t inode_nr) -{ - AUTOLOCK (inodes); - return xdp_inode_lookup_unlocked (inode_nr); -} - -static XdpInode * -xdp_inode_get_dir_unlocked (const char *app_id, const char *doc_id, FlatpakDbEntry *entry) -{ - fuse_ino_t ino; - XdpInode *inode; - XdpInode *parent = NULL; - XdpInodeType type; - const char *filename; - - ino = get_dir_inode_nr_unlocked (app_id, doc_id); - - inode = xdp_inode_lookup_unlocked (ino); - if (inode) - return inode; - - if (app_id == NULL) - { - g_assert (doc_id != NULL); - parent = xdp_inode_ref (root_inode); - type = XDP_INODE_DOC_DIR; - filename = doc_id; - } - else - { - if (doc_id == NULL) - { - parent = xdp_inode_ref (by_app_inode); - filename = app_id; - type = XDP_INODE_APP_DIR; - } - else - { - parent = xdp_inode_get_dir_unlocked (app_id, NULL, NULL); - filename = doc_id; - type = XDP_INODE_APP_DOC_DIR; - } - } - - inode = xdp_inode_new_unlocked (ino, type, parent, filename, app_id, doc_id); - xdp_inode_unref_internal (parent, TRUE); - - if (entry) - { - inode->basename = xdp_entry_dup_basename (entry); - inode->dirname = xdp_entry_dup_dirname (entry); - inode->dir_ino = xdp_entry_get_inode (entry); - inode->dir_dev = xdp_entry_get_device (entry); - } - - return inode; -} - -static XdpInode * -xdp_inode_get_dir (const char *app_id, const char *doc_id, FlatpakDbEntry *entry) -{ - AUTOLOCK (inodes); - return xdp_inode_get_dir_unlocked (app_id, doc_id, entry); -} - -/********************************************************************** \ -* FUSE Implementation -\***********************************************************************/ - -static int -get_user_perms (const struct stat *stbuf) -{ - /* Strip out exec and setuid bits */ - return stbuf->st_mode & 0666; -} - -static gboolean -app_can_write_doc (FlatpakDbEntry *entry, const char *app_id) -{ - if (app_id == NULL) - return TRUE; - - if (xdp_entry_has_permissions (entry, app_id, XDP_PERMISSION_FLAGS_WRITE)) - return TRUE; - - return FALSE; -} - -static gboolean -app_can_see_doc (FlatpakDbEntry *entry, const char *app_id) -{ - if (app_id == NULL) - return TRUE; - - if (xdp_entry_has_permissions (entry, app_id, XDP_PERMISSION_FLAGS_READ)) - return TRUE; - - return FALSE; -} - -/* Call with mutex held! */ -static int -xdp_inode_locked_get_fd (XdpInode *inode) -{ - if (inode->truncated) - return inode->trunc_fd; - - return inode->fd; -} - -/* Call with mutex held! */ -static int -xdp_inode_locked_get_write_fd (XdpInode *inode) -{ - if (inode->is_doc) - { - if (!inode->truncated) - { - errno = ENOSYS; - return -1; - } - return inode->trunc_fd; - } - - return inode->fd; -} - -static int -xdp_inode_stat (XdpInode *inode, - struct stat *stbuf) -{ - stbuf->st_ino = inode->ino; - stbuf->st_uid = getuid (); - stbuf->st_gid = getgid (); - - switch (inode->type) - { - case XDP_INODE_ROOT: - case XDP_INODE_BY_APP: - case XDP_INODE_APP_DIR: - stbuf->st_mode = S_IFDIR | NON_DOC_DIR_PERMS; - stbuf->st_nlink = 2; - break; - - case XDP_INODE_DOC_DIR: - case XDP_INODE_APP_DOC_DIR: - stbuf->st_mode = S_IFDIR | DOC_DIR_PERMS; - stbuf->st_nlink = 2; - break; - - case XDP_INODE_DOC_FILE: - { - g_autoptr(FlatpakDbEntry) entry = NULL; - struct stat tmp_stbuf; - gboolean can_see, can_write; - int fd, res, errsv; - - entry = xdp_lookup_doc (inode->doc_id); - if (entry == NULL) - { - errno = ENOENT; - return -1; - } - - can_see = app_can_see_doc (entry, inode->app_id); - can_write = app_can_write_doc (entry, inode->app_id); - - if (!can_see) - { - errno = ENOENT; - return -1; - } - - g_mutex_lock (&inode->mutex); - - fd = xdp_inode_locked_get_fd (inode); - if (fd != -1) - { - res = fstat (fd, &tmp_stbuf); - } - else - { - glnx_autofd int dir_fd = xdp_inode_open_dir_fd (inode->parent); - - if (dir_fd == -1) - res = -1; - else - res = fstatat (dir_fd, inode->backing_filename, - &tmp_stbuf, AT_SYMLINK_NOFOLLOW); - } - errsv = errno; - - g_mutex_unlock (&inode->mutex); - - if (res != 0) - { - errno = errsv; - return -1; - } - - stbuf->st_mode = S_IFREG | get_user_perms (&tmp_stbuf); - if (!can_write) - stbuf->st_mode &= ~(0222); - stbuf->st_size = tmp_stbuf.st_size; - stbuf->st_uid = tmp_stbuf.st_uid; - stbuf->st_gid = tmp_stbuf.st_gid; - stbuf->st_blksize = tmp_stbuf.st_blksize; - stbuf->st_blocks = tmp_stbuf.st_blocks; - stbuf->st_atim = tmp_stbuf.st_atim; - stbuf->st_mtim = tmp_stbuf.st_mtim; - stbuf->st_ctim = tmp_stbuf.st_ctim; - } - break; - - default: - g_assert_not_reached (); - } - - return 0; -} - -static void -xdp_fuse_lookup (fuse_req_t req, - fuse_ino_t parent, - const char *name) -{ - g_autoptr(XdpInode) parent_inode = NULL; - struct fuse_entry_param e = {0}; - g_autoptr(XdpInode) child_inode = NULL; - g_autoptr(FlatpakDbEntry) entry = NULL; - - g_debug ("xdp_fuse_lookup %lx/%s -> ", parent, name); - - parent_inode = xdp_inode_lookup (parent); - if (parent_inode == NULL) - { - g_debug ("xdp_fuse_lookup <- error parent ENOENT"); - fuse_reply_err (req, ENOENT); - return; - } - - /* Default */ - e.attr_timeout = ATTR_CACHE_TIME; - e.entry_timeout = ENTRY_CACHE_TIME; - - switch (parent_inode->type) - { - case XDP_INODE_ROOT: - if (strcmp (name, BY_APP_NAME) == 0) - { - child_inode = xdp_inode_ref (by_app_inode); - } - else - { - entry = xdp_lookup_doc (name); - if (entry != NULL) - child_inode = xdp_inode_get_dir (NULL, name, entry); - } - break; - - case XDP_INODE_BY_APP: - /* This lazily creates the app dir */ - if (flatpak_is_valid_name (name, NULL)) - child_inode = xdp_inode_get_dir (name, NULL, NULL); - break; - - case XDP_INODE_APP_DIR: - entry = xdp_lookup_doc (name); - if (entry != NULL && - app_can_see_doc (entry, parent_inode->app_id)) - child_inode = xdp_inode_get_dir (parent_inode->app_id, name, entry); - break; - - case XDP_INODE_APP_DOC_DIR: - case XDP_INODE_DOC_DIR: - { - g_autoptr(XdpInode) doc_inode = NULL; - entry = xdp_lookup_doc (parent_inode->doc_id); - if (entry == NULL) - { - g_debug ("xdp_fuse_lookup <- error no parent entry ENOENT"); - fuse_reply_err (req, ENOENT); - return; - } - - /* Ensure it is alive at least during lookup_child () */ - doc_inode = xdp_inode_ensure_document_file (parent_inode); - - child_inode = xdp_inode_lookup_child (parent_inode, name); - - /* We verify in the stat below if the backing file exists */ - - /* Files can be changed from outside the fuse fs, so don't cache any data */ - e.attr_timeout = 0; - e.entry_timeout = 0; - } - break; - - case XDP_INODE_DOC_FILE: - fuse_reply_err (req, ENOTDIR); - return; - - default: - break; - } - - if (child_inode == NULL) - { - g_debug ("xdp_fuse_lookup <- error child ENOENT"); - fuse_reply_err (req, ENOENT); - return; - } - - if (xdp_inode_stat (child_inode, &e.attr) != 0) - { - fuse_reply_err (req, errno); - return; - } - - e.ino = child_inode->ino; - - g_debug ("xdp_fuse_lookup <- inode %lx", (long) e.ino); - xdp_inode_ref (child_inode); /* Ref given to the kernel, returned in xdp_fuse_forget() */ - fuse_reply_entry (req, &e); -} - -static void -xdp_fuse_forget (fuse_req_t req, fuse_ino_t ino, unsigned long nlookup) -{ - g_autoptr(XdpInode) inode = NULL; - g_debug ("xdp_fuse_forget %lx %ld -> ", ino, nlookup); - - inode = xdp_inode_lookup (ino); - if (inode == NULL) - { - g_warning ("xdp_fuse_forget, unknown inode"); - } - else - { - while (nlookup > 0) - { - xdp_inode_unref (inode); - nlookup--; - } - } - - fuse_reply_none (req); -} - -struct dirbuf -{ - char *p; - size_t size; -}; - -static void -dirbuf_add (fuse_req_t req, - struct dirbuf *b, - const char *name, - fuse_ino_t ino, - mode_t mode) -{ - struct stat stbuf; - - size_t oldsize = b->size; - - b->size += fuse_add_direntry (req, NULL, 0, name, NULL, 0); - b->p = (char *) g_realloc (b->p, b->size); - memset (&stbuf, 0, sizeof (stbuf)); - stbuf.st_ino = ino; - stbuf.st_mode = mode; - fuse_add_direntry (req, b->p + oldsize, - b->size - oldsize, - name, &stbuf, - b->size); -} - -static void -dirbuf_add_docs (fuse_req_t req, - struct dirbuf *b, - const char *app_id) -{ - g_auto(GStrv) docs = NULL; - fuse_ino_t ino; - int i; - - docs = xdp_list_docs (); - for (i = 0; docs[i] != NULL; i++) - { - if (app_id) - { - g_autoptr(FlatpakDbEntry) entry = xdp_lookup_doc (docs[i]); - if (entry == NULL || - !app_can_see_doc (entry, app_id)) - continue; - } - ino = get_dir_inode_nr (app_id, docs[i]); - dirbuf_add (req, b, docs[i], ino, S_IFDIR); - } -} - -static int -reply_buf_limited (fuse_req_t req, - const char *buf, - size_t bufsize, - off_t off, - size_t maxsize) -{ - if (off < bufsize) - return fuse_reply_buf (req, buf + off, - MIN (bufsize - off, maxsize)); - else - return fuse_reply_buf (req, NULL, 0); -} - -static void -xdp_fuse_readdir (fuse_req_t req, fuse_ino_t ino, size_t size, - off_t off, struct fuse_file_info *fi) -{ - struct dirbuf *b = (struct dirbuf *) (gsize) (fi->fh); - - reply_buf_limited (req, b->p, b->size, off, size); -} - -static void -xdp_fuse_opendir (fuse_req_t req, - fuse_ino_t ino, - struct fuse_file_info *fi) -{ - g_autoptr(XdpInode) inode = NULL; - struct dirbuf b = {0}; - - g_debug ("xdp_fuse_opendir %lx", ino); - - inode = xdp_inode_lookup (ino); - if (inode == NULL) - { - g_debug ("xdp_fuse_opendir <- error ENOENT"); - fuse_reply_err (req, ENOENT); - return; - } - - switch (inode->type) - { - case XDP_INODE_ROOT: - dirbuf_add (req, &b, ".", ROOT_INODE, S_IFDIR); - dirbuf_add (req, &b, "..", ROOT_INODE, S_IFDIR); - dirbuf_add (req, &b, BY_APP_NAME, BY_APP_INODE, S_IFDIR); - dirbuf_add_docs (req, &b, NULL); - break; - - case XDP_INODE_BY_APP: - { - g_auto(GStrv) db_app_ids = NULL; - g_auto(GStrv) app_ids = NULL; - int i; - - dirbuf_add (req, &b, ".", BY_APP_INODE, S_IFDIR); - dirbuf_add (req, &b, "..", ROOT_INODE, S_IFDIR); - - /* Ensure that all apps from db are allocated */ - db_app_ids = xdp_list_apps (); - allocate_app_dir_inode_nr (db_app_ids); - - /* But return all allocated dirs. We might have app dirs - that have no permissions, and are thus not in the db */ - app_ids = get_allocated_app_dirs (); - for (i = 0; app_ids[i] != NULL; i++) - dirbuf_add (req, &b, app_ids[i], - get_dir_inode_nr (app_ids[i], NULL), S_IFDIR); - } - break; - - case XDP_INODE_APP_DIR: - dirbuf_add (req, &b, ".", inode->ino, S_IFDIR); - dirbuf_add (req, &b, "..", BY_APP_INODE, S_IFDIR); - dirbuf_add_docs (req, &b, inode->app_id); - break; - - case XDP_INODE_DOC_FILE: - fuse_reply_err (req, ENOTDIR); - break; - - case XDP_INODE_APP_DOC_DIR: - case XDP_INODE_DOC_DIR: - { - GList *children, *l; - g_autoptr(XdpInode) doc_inode = NULL; - g_autoptr(FlatpakDbEntry) entry = NULL; - - entry = xdp_lookup_doc (inode->doc_id); - if (entry == NULL) - { - fuse_reply_err (req, ENOENT); - break; - } - - dirbuf_add (req, &b, ".", inode->ino, S_IFDIR); - dirbuf_add (req, &b, "..", inode->parent->ino, S_IFDIR); - - /* Ensure it is alive at least during list_children () */ - doc_inode = xdp_inode_ensure_document_file (inode); - - children = xdp_inode_list_children (inode); - - for (l = children; l != NULL; l = l->next) - { - struct stat stbuf; - XdpInode *child = l->data; - g_autofree char *filename = xdp_inode_get_filename (child); - if (filename != NULL && xdp_inode_stat (child, &stbuf) == 0) - dirbuf_add (req, &b, filename, child->ino, stbuf.st_mode); - xdp_inode_unref (child); - } - g_list_free (children); - } - break; - - default: - g_assert_not_reached (); - } - - if (b.p != NULL) - { - fi->fh = (gsize) g_memdup (&b, sizeof (b)); - if (fuse_reply_open (req, fi) == -ENOENT) - { - g_free (b.p); - g_free ((gpointer) (gsize) (fi->fh)); - } - } -} - -static void -xdp_fuse_releasedir (fuse_req_t req, - fuse_ino_t ino, - struct fuse_file_info *fi) -{ - struct dirbuf *b = (struct dirbuf *) (gsize) (fi->fh); - - g_free (b->p); - g_free (b); - fuse_reply_err (req, 0); -} - - - -static void -xdp_fuse_getattr (fuse_req_t req, - fuse_ino_t ino, - struct fuse_file_info *fi) -{ - g_autoptr(XdpInode) inode = NULL; - struct stat stbuf = { 0 }; - - g_debug ("xdp_fuse_getattr %lx (fi=%p)", ino, fi); - - inode = xdp_inode_lookup (ino); - if (inode == NULL) - { - g_debug ("xdp_fuse_getattr <- error ENOENT"); - fuse_reply_err (req, ENOENT); - return; - } - - if (xdp_inode_stat (inode, &stbuf) != 0) - { - fuse_reply_err (req, errno); - return; - } - - fuse_reply_attr (req, &stbuf, ATTR_CACHE_TIME); -} - -static void -xdp_fuse_fsyncdir (fuse_req_t req, - fuse_ino_t ino, - int datasync, - struct fuse_file_info *fi) -{ - g_autoptr(XdpInode) inode = NULL; - - g_debug ("xdp_fuse_fsyncdir %lx %p", ino, fi); - - inode = xdp_inode_lookup (ino); - if (inode == NULL) - { - g_debug ("xdp_fuse_fsyncdir <- error ENOENT"); - fuse_reply_err (req, ENOENT); - return; - } - - if (inode->type == XDP_INODE_APP_DOC_DIR || - inode->type == XDP_INODE_DOC_DIR) - { - g_autoptr(FlatpakDbEntry) entry = xdp_lookup_doc (inode->doc_id); - if (entry != NULL) - { - g_autofree char *dirname = xdp_entry_dup_dirname (entry); - int fd = open (dirname, O_DIRECTORY | O_RDONLY); - if (fd >= 0) - { - if (datasync) - fdatasync (fd); - else - fsync (fd); - close (fd); - } - } - } - - fuse_reply_err (req, 0); -} - -static XdpFile * -xdp_file_new (XdpInode *inode, - int open_mode) -{ - XdpFile *file = g_new (XdpFile, 1); - - file->inode = xdp_inode_ref (inode); - file->open_mode = open_mode; - - return file; -} - -/* Call with mutex held */ -static void -xdp_inode_locked_close_unneeded_fds (XdpInode *inode) -{ - gboolean has_open_for_write = FALSE; - GList *l; - - for (l = inode->open_files; l != NULL; l = l->next) - { - XdpFile *file = l->data; - - if (file->open_mode != O_RDONLY) - { - has_open_for_write = TRUE; - break; - } - } - - if (!has_open_for_write) - { - if (inode->truncated) - { - if (inode->open_files != NULL && inode->fd != -1) - { - /* We're not going to close the ->fd, so we repoint it to the trunc_fd, but reopened O_RDONLY */ - close (inode->fd); - inode->fd = reopen_fd (inode->trunc_fd, O_RDONLY); - } - - if (inode->filename != NULL) - { - /* not removed, replace original */ - fsync (inode->trunc_fd); - g_free (inode->backing_filename); - inode->backing_filename = g_strdup (inode->filename); - g_debug ("moving %s to %s", inode->trunc_filename, inode->backing_filename); - if (renameat (inode->dir_fd, inode->trunc_filename, - inode->dir_fd, inode->backing_filename) != 0) - g_warning ("Unable to replace truncated document: %s", g_strerror (errno)); - } - - inode->truncated = FALSE; - } - else if (inode->trunc_filename != NULL) - { - unlinkat (inode->dir_fd, inode->trunc_filename, 0); - g_debug ("unlinked truc_filename %s", inode->trunc_filename); - } - - if (inode->trunc_fd != -1) - { - close (inode->trunc_fd); - inode->trunc_fd = -1; - g_free (inode->trunc_filename); - inode->trunc_filename = NULL; - } - } - - if (inode->open_files == NULL) - { - if (inode->fd != -1) - { - close (inode->fd); - inode->fd = -1; - } - - if (inode->dir_fd != -1) - { - close (inode->dir_fd); - inode->dir_fd = -1; - } - } -} - -static void -xdp_file_free (XdpFile *file) -{ - XdpInode *inode = file->inode; - - g_mutex_lock (&inode->mutex); - inode->open_files = g_list_remove (inode->open_files, file); - - xdp_inode_locked_close_unneeded_fds (inode); - - g_mutex_unlock (&inode->mutex); - xdp_inode_unref (inode); - g_free (file); -} - -/* sets errno */ -static int -xdp_inode_locked_ensure_fd_open (XdpInode *inode, - FlatpakDbEntry *entry, - gboolean for_write) -{ - /* Ensure all fds are open */ - if (inode->dir_fd == -1) - { - inode->dir_fd = xdp_inode_open_dir_fd (inode->parent); - if (inode->dir_fd == -1) - return -1; - } - - if (for_write) - { - if (faccessat (inode->dir_fd, inode->backing_filename, W_OK, 0) != 0) - return -1; - } - - if (inode->fd == -1) - { - int mode = O_NOFOLLOW | O_CLOEXEC; - - if (inode->is_doc) - mode |= O_RDONLY; - else - mode |= O_RDWR; - - inode->fd = openat (inode->dir_fd, inode->backing_filename, mode); - if (inode->fd < 0) - return -1; - } - - if (inode->is_doc && for_write && inode->trunc_fd == -1) - { - struct stat st_buf; - mode_t mode = 0600; - - if (fstat (inode->fd, &st_buf) == 0) - mode = get_user_perms (&st_buf); - - g_assert (inode->trunc_filename == NULL); - inode->trunc_filename = create_tmp_for_doc (inode->parent, inode->dir_fd, O_RDWR, mode, - &inode->trunc_fd); - if (inode->trunc_filename == NULL) - return -1; - } - - return 0; -} - -static void -xdp_fuse_open (fuse_req_t req, - fuse_ino_t ino, - struct fuse_file_info *fi) -{ - g_autoptr(XdpInode) inode = NULL; - g_autoptr(FlatpakDbEntry) entry = NULL; - gboolean can_write; - int open_mode; - XdpFile *file = NULL; - int errsv; - - g_debug ("xdp_fuse_open %lx flags %o", ino, fi->flags); - - inode = xdp_inode_lookup (ino); - if (inode == NULL) - { - g_debug ("xdp_fuse_open <- no inode error ENOENT"); - fuse_reply_err (req, ENOENT); - return; - } - - if (inode->type != XDP_INODE_DOC_FILE) - { - g_debug ("xdp_fuse_open <- error EISDIR"); - fuse_reply_err (req, EISDIR); - return; - } - - entry = xdp_lookup_doc (inode->doc_id); - if (entry == NULL || - !app_can_see_doc (entry, inode->app_id)) - { - g_debug ("xdp_fuse_open <- no entry error ENOENT"); - fuse_reply_err (req, ENOENT); - return; - } - - can_write = app_can_write_doc (entry, inode->app_id); - - open_mode = fi->flags & 3; - - if (open_mode != O_RDONLY && !can_write) - { - g_debug ("xdp_fuse_open <- no write EACCES"); - fuse_reply_err (req, EACCES); - return; - } - - g_mutex_lock (&inode->mutex); - - if (xdp_inode_locked_ensure_fd_open (inode, entry, - open_mode != O_RDONLY) == 0) - { - file = xdp_file_new (inode, open_mode); - inode->open_files = g_list_prepend (inode->open_files, file); - errsv = 0; - } - else - { - errsv = errno; - xdp_inode_locked_close_unneeded_fds (inode); - } - - g_mutex_unlock (&inode->mutex); - - if (file != NULL) - { - fi->fh = (gsize) file; - if (fuse_reply_open (req, fi)) - xdp_file_free (file); - } - else - { - fuse_reply_err (req, errsv); - } -} - - -static void -xdp_fuse_create (fuse_req_t req, - fuse_ino_t parent, - const char *filename, - mode_t mode, - struct fuse_file_info *fi) -{ - g_autoptr(XdpInode) parent_inode = NULL; - g_autoptr(FlatpakDbEntry) entry = NULL; - struct fuse_entry_param e = {0}; - gboolean can_see, can_write; - int open_mode; - XdpFile *file = NULL; - XdpInode *inode; - int errsv; - - g_debug ("xdp_fuse_create %lx/%s, flags %o", parent, filename, fi->flags); - - parent_inode = xdp_inode_lookup (parent); - if (parent_inode == NULL) - { - g_debug ("xdp_fuse_create <- error parent ENOENT"); - fuse_reply_err (req, ENOENT); - return; - } - - if (parent_inode->type == XDP_INODE_DOC_FILE) - { - g_debug ("xdp_fuse_create <- error parent ENOTDIR"); - fuse_reply_err (req, ENOTDIR); - return; - } - - if (parent_inode->type != XDP_INODE_APP_DOC_DIR && - parent_inode->type != XDP_INODE_DOC_DIR) - { - fuse_reply_err (req, EACCES); - return; - } - - entry = xdp_lookup_doc (parent_inode->doc_id); - if (entry == NULL) - { - fuse_reply_err (req, ENOENT); - return; - } - - can_see = app_can_see_doc (entry, parent_inode->app_id); - if (!can_see) - { - fuse_reply_err (req, ENOENT); - return; - } - - can_write = app_can_write_doc (entry, parent_inode->app_id); - if (!can_write) - { - fuse_reply_err (req, EACCES); - return; - } - - inode = xdp_inode_create_file (parent_inode, filename, - mode, - (fi->flags & O_TRUNC) != 0, - (fi->flags & O_EXCL) != 0); - if (inode == NULL) - { - fuse_reply_err (req, errno); - return; - } - - g_mutex_lock (&inode->mutex); - - open_mode = fi->flags & 3; - - if (xdp_inode_locked_ensure_fd_open (inode, entry, - open_mode != O_RDONLY) == 0) - { - file = xdp_file_new (inode, open_mode); - inode->open_files = g_list_prepend (inode->open_files, file); - errsv = 0; - } - else - { - errsv = errno; - xdp_inode_locked_close_unneeded_fds (inode); - } - - g_mutex_unlock (&inode->mutex); - - if (file != NULL) - { - if (xdp_inode_stat (inode, &e.attr) != 0) - { - xdp_file_free (file); - fuse_reply_err (req, errno); - return; - } - - e.ino = inode->ino; - if (inode->is_doc) - { - e.attr_timeout = 0; - e.entry_timeout = 0; - } - else - { - e.attr_timeout = ATTR_CACHE_TIME; - e.entry_timeout = ENTRY_CACHE_TIME; - } - - xdp_inode_ref (inode); /* Ref given to the kernel, returned in xdp_fuse_forget() */ - - fi->fh = (gsize) file; - if (fuse_reply_create (req, &e, fi)) - { - xdp_file_free (file); - xdp_inode_unref (inode); - } - } - else - { - fuse_reply_err (req, errsv); - } -} - -static void -xdp_fuse_read (fuse_req_t req, - fuse_ino_t ino, - size_t size, - off_t off, - struct fuse_file_info *fi) -{ - XdpFile *file = (gpointer) (gsize) fi->fh; - XdpInode *inode = file->inode; - struct fuse_bufvec bufv = FUSE_BUFVEC_INIT (size); - int fd; - - g_debug ("xdp_fuse_real %lx %ld %ld", ino, (long) size, (long) off); - - g_mutex_lock (&inode->mutex); - - fd = xdp_inode_locked_get_fd (inode); - if (fd == -1) - { - static char c = 'x'; - bufv.buf[0].flags = 0; - bufv.buf[0].mem = &c; - bufv.buf[0].size = 0; - - fuse_reply_data (req, &bufv, FUSE_BUF_NO_SPLICE); - } - else - { - bufv.buf[0].flags = FUSE_BUF_IS_FD | FUSE_BUF_FD_SEEK; - bufv.buf[0].fd = fd; - bufv.buf[0].pos = off; - - fuse_reply_data (req, &bufv, FUSE_BUF_SPLICE_MOVE); - } - - g_mutex_unlock (&inode->mutex); -} - -static void -xdp_fuse_release (fuse_req_t req, - fuse_ino_t ino, - struct fuse_file_info *fi) -{ - XdpFile *file = (gpointer) (gsize) fi->fh; - - g_debug ("xdp_fuse_release %lx (fi=%p)", ino, fi); - - xdp_file_free (file); - fuse_reply_err (req, 0); -} - -static int -truncateat (int dir_fd, const char *filename, int size) -{ - int fd; - int errsv, res; - - fd = openat (dir_fd, filename, O_RDWR); - if (fd == -1) - return -1; - - res = ftruncate (fd, size); - errsv = errno; - - close (fd); - - errno = errsv; - return res; -} - -static void -xdp_fuse_setattr (fuse_req_t req, - fuse_ino_t ino, - struct stat *attr, - int to_set, - struct fuse_file_info *fi) -{ - g_autoptr(XdpInode) inode = NULL; - g_autoptr(FlatpakDbEntry) entry = NULL; - double attr_cache_time = ATTR_CACHE_TIME; - struct stat newattr = {0}; - gboolean can_write; - int res = 0; - - g_debug ("xdp_fuse_setattr %lx %x %p", ino, to_set, fi); - - inode = xdp_inode_lookup (ino); - if (inode == NULL) - { - g_debug ("xdp_fuse_setattr <- error ENOENT"); - fuse_reply_err (req, ENOENT); - return; - } - - if (inode->type != XDP_INODE_DOC_FILE) - { - g_debug ("xdp_fuse_setattr <- not file ENOSYS"); - fuse_reply_err (req, ENOSYS); - return; - } - - entry = xdp_lookup_doc (inode->doc_id); - if (entry == NULL || - !app_can_see_doc (entry, inode->app_id)) - { - g_debug ("xdp_fuse_setattr <- no entry error ENOENT"); - fuse_reply_err (req, ENOENT); - return; - } - - can_write = app_can_write_doc (entry, inode->app_id); - - if (to_set == FUSE_SET_ATTR_SIZE) - { - g_mutex_lock (&inode->mutex); - - if (!can_write) - { - res = EACCES; - } - else if (inode->is_doc) - { - /* Only allow ftruncate with the file open for write. We could - * allow a truncate, but it would have to be implemented as - * an atomic-replace-with-empty-file to not affect other apps - * having the file open. - * Also, only support truncate-to-zero on first truncation, to - * avoid having to copy lots of data from the old file to the - * trunc_fd. - */ - if (inode->trunc_fd == -1) - { - res = EACCES; - } - else if (!inode->truncated && attr->st_size != 0) - { - res = ENOSYS; - } - else - { - if (ftruncate (inode->trunc_fd, attr->st_size) != 0) - { - res = errno; - } - else if (!inode->truncated) - { - inode->truncated = TRUE; - g_free (inode->backing_filename); - inode->backing_filename = g_strdup (inode->trunc_filename); - } - } - } - else - { - if (inode->fd) - { - if (ftruncate (inode->fd, attr->st_size) != 0) - res = errno; - } - else - { - glnx_autofd int dir_fd = xdp_inode_open_dir_fd (inode->parent); - if (dir_fd == -1 || - truncateat (dir_fd, inode->backing_filename, attr->st_size) != 0) - res = errno; - } - } - g_mutex_unlock (&inode->mutex); - } - else if (to_set == FUSE_SET_ATTR_MODE) - { - if (!can_write) - { - res = EACCES; - } - else - { - int fd = xdp_inode_locked_get_write_fd (inode); - if (fd == -1 || - fchmod (fd, get_user_perms (attr)) != 0) - res = errno; - } - } - else - { - res = ENOSYS; - } - - if (res != 0) - { - fuse_reply_err (req, res); - } - else - { - if (xdp_inode_stat (inode, &newattr) != 0) - fuse_reply_err (req, errno); - else - fuse_reply_attr (req, &newattr, attr_cache_time); - } -} - -static void -xdp_fuse_write (fuse_req_t req, - fuse_ino_t ino, - const char *buf, - size_t size, - off_t off, - struct fuse_file_info *fi) -{ - XdpFile *file = (gpointer) (gsize) fi->fh; - XdpInode *inode = file->inode; - int fd; - int res; - - g_debug ("xdp_fuse_write %lx %ld %ld", ino, (long) size, (long) off); - - g_mutex_lock (&inode->mutex); - - fd = xdp_inode_locked_get_write_fd (inode); - if (fd < 0) - { - fuse_reply_err (req, errno); - } - else - { - res = pwrite (fd, buf, size, off); - if (res < 0) - fuse_reply_err (req, errno); - else - fuse_reply_write (req, res); - } - - g_mutex_unlock (&inode->mutex); -} - -static void -xdp_fuse_write_buf (fuse_req_t req, - fuse_ino_t ino, - struct fuse_bufvec *bufv, - off_t off, - struct fuse_file_info *fi) -{ - XdpFile *file = (gpointer) (gsize) fi->fh; - struct fuse_bufvec dst = FUSE_BUFVEC_INIT (fuse_buf_size (bufv)); - XdpInode *inode = file->inode; - int fd; - int res; - - g_debug ("xdp_fuse_write_buf %lx %ld", ino, (long) off); - - g_mutex_lock (&inode->mutex); - - fd = xdp_inode_locked_get_write_fd (inode); - if (fd == -1) - { - g_debug ("xdp_fuse_write_buf <- error %s", strerror (errno)); - fuse_reply_err (req, errno); - } - else - { - dst.buf[0].flags = FUSE_BUF_IS_FD | FUSE_BUF_FD_SEEK; - dst.buf[0].fd = fd; - dst.buf[0].pos = off; - - res = fuse_buf_copy (&dst, bufv, FUSE_BUF_SPLICE_NONBLOCK); - if (res < 0) - fuse_reply_err (req, -res); - else - fuse_reply_write (req, res); - } - - g_mutex_unlock (&inode->mutex); -} - -static void -xdp_fuse_fsync (fuse_req_t req, - fuse_ino_t ino, - int datasync, - struct fuse_file_info *fi) -{ - g_autoptr(XdpInode) inode = NULL; - int fd; - int res = 0; - - g_debug ("xdp_fuse_fsync %lx", ino); - - inode = xdp_inode_lookup (ino); - if (inode == NULL) - { - g_debug ("xdp_fuse_setattr <- error ENOENT"); - fuse_reply_err (req, ENOENT); - return; - } - - if (inode->type == XDP_INODE_DOC_FILE) - { - g_mutex_lock (&inode->mutex); - - fd = xdp_inode_locked_get_write_fd (inode); - if (fd != -1 && fsync (fd) != 0) - res = errno; - - g_mutex_unlock (&inode->mutex); - } - - fuse_reply_err (req, res); -} - -static void -xdp_fuse_unlink (fuse_req_t req, - fuse_ino_t parent, - const char *filename) -{ - g_autoptr(XdpInode) parent_inode = NULL; - g_autoptr(XdpInode) child_inode = NULL; - - g_debug ("xdp_fuse_unlink %lx/%s", parent, filename); - - parent_inode = xdp_inode_lookup (parent); - if (parent_inode == NULL) - { - g_debug ("xdp_fuse_lookup <- error parent ENOENT"); - fuse_reply_err (req, ENOENT); - return; - } - - if (parent_inode->type == XDP_INODE_DOC_FILE) - { - fuse_reply_err (req, ENOTDIR); - return; - } - - if (parent_inode->type != XDP_INODE_APP_DOC_DIR && - parent_inode->type != XDP_INODE_DOC_DIR) - { - fuse_reply_err (req, EACCES); - return; - } - - child_inode = xdp_inode_unlink_child (parent_inode, filename); - if (child_inode == NULL) - { - fuse_reply_err (req, ENOENT); - return; - } - - fuse_reply_err (req, 0); -} - -static void -xdp_fuse_rename (fuse_req_t req, - fuse_ino_t parent, - const char *name, - fuse_ino_t newparent, - const char *newname) -{ - g_autoptr(XdpInode) parent_inode = NULL; - g_autoptr(FlatpakDbEntry) entry = NULL; - gboolean can_see, can_write; - - g_debug ("xdp_fuse_rename %lx/%s -> %lx/%s", parent, name, newparent, newname); - - parent_inode = xdp_inode_lookup (parent); - if (parent_inode == NULL) - { - g_debug ("xdp_fuse_rename <- error parent ENOENT"); - fuse_reply_err (req, ENOENT); - return; - } - - if (parent_inode->type == XDP_INODE_DOC_FILE) - { - fuse_reply_err (req, ENOTDIR); - return; - } - - if (parent_inode->type != XDP_INODE_APP_DOC_DIR && - parent_inode->type != XDP_INODE_DOC_DIR) - { - fuse_reply_err (req, EACCES); - return; - } - - if (newparent != parent) - { - g_debug ("xdp_fuse_rename <- error different parents EACCES"); - fuse_reply_err (req, EACCES); - return; - } - - if (strcmp (name, newname) == 0) - { - fuse_reply_err (req, 0); - return; - } - - entry = xdp_lookup_doc (parent_inode->doc_id); - if (entry == NULL) - { - fuse_reply_err (req, ENOENT); - return; - } - - can_see = app_can_see_doc (entry, parent_inode->app_id); - can_write = app_can_write_doc (entry, parent_inode->app_id); - - if (!can_see) - { - fuse_reply_err (req, ENOENT); - return; - } - - if (!can_write) - { - fuse_reply_err (req, EACCES); - return; - } - - if (xdp_inode_rename_child (parent_inode, name, newname) != 0) - fuse_reply_err (req, errno); - else - fuse_reply_err (req, 0); -} - -static void -xdp_fuse_access (fuse_req_t req, fuse_ino_t ino, int mask) -{ - g_autoptr(XdpInode) inode = NULL; - g_autoptr(FlatpakDbEntry) entry = NULL; - - g_debug ("xdp_fuse_access %lx %d", ino, mask); - - if (mask != F_OK && (mask & ~(R_OK|W_OK|X_OK)) != 0) - { - g_debug ("xdp_fuse_access <- error EINVAL"); - fuse_reply_err (req, EINVAL); - return; - } - - inode = xdp_inode_lookup (ino); - if (inode == NULL) - { - g_debug ("xdp_fuse_access <- error ENOENT"); - fuse_reply_err (req, ENOENT); - return; - } - - if (inode->type != XDP_INODE_DOC_FILE) - { - int dir_mask = 0; - - switch (inode->type) - { - case XDP_INODE_ROOT: - case XDP_INODE_BY_APP: - case XDP_INODE_APP_DIR: - dir_mask = R_OK | X_OK; - break; - case XDP_INODE_APP_DOC_DIR: - case XDP_INODE_DOC_DIR: - dir_mask = R_OK | X_OK | W_OK; - break; - - default: - g_assert_not_reached (); - } - - if (mask != F_OK && ((mask & dir_mask) != mask)) - { - fuse_reply_err (req, EACCES); - return; - } - } - else /* A file */ - { - entry = xdp_lookup_doc (inode->doc_id); - if (entry == NULL || - !app_can_see_doc (entry, inode->app_id)) - { - g_debug ("xdp_fuse_access <- no entry error ENOENT"); - fuse_reply_err (req, ENOENT); - return; - } - - if (mask == F_OK) - { - if (!app_can_see_doc (entry, inode->app_id)) - { - fuse_reply_err (req, EACCES); - return; - } - } - else - { - if (((mask & R_OK) && !app_can_see_doc (entry, inode->app_id)) || - ((mask & W_OK) && !app_can_write_doc (entry, inode->app_id)) || - (mask & X_OK)) - { - fuse_reply_err (req, EACCES); - return; - } - } - } - - fuse_reply_err (req, 0); -} - -static struct fuse_lowlevel_ops xdp_fuse_oper = { - .lookup = xdp_fuse_lookup, - .forget = xdp_fuse_forget, - .getattr = xdp_fuse_getattr, - .opendir = xdp_fuse_opendir, - .readdir = xdp_fuse_readdir, - .releasedir = xdp_fuse_releasedir, - .fsyncdir = xdp_fuse_fsyncdir, - .open = xdp_fuse_open, - .read = xdp_fuse_read, - .release = xdp_fuse_release, - .setattr = xdp_fuse_setattr, - .write = xdp_fuse_write, - .write_buf = xdp_fuse_write_buf, - .fsync = xdp_fuse_fsync, - .create = xdp_fuse_create, - .unlink = xdp_fuse_unlink, - .rename = xdp_fuse_rename, - .access = xdp_fuse_access, -}; - -/* Called when a apps permissions to see a document is changed, - and with null opt_app_id when the doc is created/removed */ -void -xdp_fuse_invalidate_doc_app (const char *doc_id, - const char *opt_app_id) -{ - g_autoptr(XdpInode) inode = NULL; - fuse_ino_t ino; - GList *l; - - /* This can happen if fuse is not initialized yet for the very - first dbus message that activated the service */ - if (main_ch == NULL) - return; - - g_debug ("invalidate %s/%s", doc_id, opt_app_id ? opt_app_id : "*"); - - AUTOLOCK (inodes); - ino = get_dir_inode_nr_unlocked (opt_app_id, doc_id); - inode = xdp_inode_lookup_unlocked (ino); - if (inode != NULL) - { - fuse_lowlevel_notify_inval_inode (main_ch, inode->ino, 0, 0); - fuse_lowlevel_notify_inval_entry (main_ch, inode->parent->ino, - inode->filename, strlen (inode->filename)); - - for (l = inode->children; l != NULL; l = l->next) - { - XdpInode *child = l->data; - - fuse_lowlevel_notify_inval_inode (main_ch, child->ino, 0, 0); - if (child->filename != NULL) - fuse_lowlevel_notify_inval_entry (main_ch, inode->ino, - child->filename, strlen (child->filename)); - } - } -} - -char * -xdp_fuse_lookup_id_for_inode (ino_t ino) -{ - g_autoptr(XdpInode) inode = NULL; - - inode = xdp_inode_lookup (ino); - if (inode == NULL) - return NULL; - - if (inode->type != XDP_INODE_DOC_FILE || - !inode->is_doc) - return NULL; - - return g_strdup (inode->doc_id); -} - -const char * -xdp_fuse_get_mountpoint (void) -{ - if (mount_path == NULL) - mount_path = g_build_filename (g_get_user_runtime_dir (), "doc", NULL); - return mount_path; -} - -void -xdp_fuse_exit (void) -{ - if (session) - fuse_session_exit (session); - - if (fuse_pthread) - pthread_kill (fuse_pthread, SIGHUP); - - if (fuse_thread) - g_thread_join (fuse_thread); -} - -static gpointer -xdp_fuse_mainloop (gpointer data) -{ - fuse_pthread = pthread_self (); - - fuse_session_loop_mt (session); - - fuse_session_remove_chan (main_ch); - fuse_session_destroy (session); - fuse_unmount (mount_path, main_ch); - return NULL; -} - -gboolean -xdp_fuse_init (GError **error) -{ - char *argv[] = { "xdp-fuse", "-osplice_write,splice_move" }; - struct fuse_args args = FUSE_ARGS_INIT (G_N_ELEMENTS (argv), argv); - struct stat st; - struct statfs stfs; - const char *path; - int statfs_res; - - inodes = - g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, NULL); - root_inode = xdp_inode_new (ROOT_INODE, XDP_INODE_ROOT, NULL, "/", NULL, NULL); - by_app_inode = xdp_inode_new (BY_APP_INODE, XDP_INODE_BY_APP, root_inode, BY_APP_NAME, NULL, NULL); - dir_to_inode_nr = - g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); - - path = xdp_fuse_get_mountpoint (); - if ((stat (path, &st) == -1 && errno == ENOTCONN) || - (((statfs_res = statfs (path, &stfs)) == -1 && errno == ENOTCONN) || - (statfs_res == 0 && stfs.f_type == 0x65735546 /* fuse */))) - { - int count; - char *umount_argv[] = { "fusermount", "-u", "-z", (char *) path, NULL }; - - g_spawn_sync (NULL, umount_argv, NULL, G_SPAWN_SEARCH_PATH, - NULL, NULL, NULL, NULL, NULL, NULL); - - g_usleep (10000); /* 10ms */ - count = 0; - while (stat (path, &st) == -1 && count < 10) - g_usleep (10000); /* 10ms */ - } - - if (g_mkdir_with_parents (path, 0700)) - { - g_set_error (error, FLATPAK_PORTAL_ERROR, FLATPAK_PORTAL_ERROR_FAILED, - "Unable to create dir %s", path); - return FALSE; - } - - main_ch = fuse_mount (path, &args); - if (main_ch == NULL) - { - g_set_error (error, FLATPAK_PORTAL_ERROR, FLATPAK_PORTAL_ERROR_FAILED, "Can't mount fuse fs"); - return FALSE; - } - - session = fuse_lowlevel_new (&args, &xdp_fuse_oper, - sizeof (xdp_fuse_oper), NULL); - if (session == NULL) - { - g_set_error (error, FLATPAK_PORTAL_ERROR, FLATPAK_PORTAL_ERROR_FAILED, - "Can't create fuse session"); - return FALSE; - } - fuse_session_add_chan (session, main_ch); - - fuse_thread = g_thread_new ("fuse mainloop", xdp_fuse_mainloop, session); - - return TRUE; -} diff --git a/document-portal/xdp-fuse.h b/document-portal/xdp-fuse.h deleted file mode 100644 index e208b53d..00000000 --- a/document-portal/xdp-fuse.h +++ /dev/null @@ -1,23 +0,0 @@ -#ifndef XDP_FUSE_H -#define XDP_FUSE_H - -#include <glib.h> -#include "flatpak-db.h" - -G_BEGIN_DECLS - -char ** xdp_list_apps (void); -char ** xdp_list_docs (void); -FlatpakDbEntry *xdp_lookup_doc (const char *doc_id); - -gboolean xdp_fuse_init (GError **error); -void xdp_fuse_exit (void); -const char *xdp_fuse_get_mountpoint (void); -void xdp_fuse_invalidate_doc_app (const char *doc_id, - const char *opt_app_id); -char *xdp_fuse_lookup_id_for_inode (ino_t inode); - - -G_END_DECLS - -#endif /* XDP_FUSE_H */ diff --git a/document-portal/xdp-main.c b/document-portal/xdp-main.c deleted file mode 100644 index a8532b45..00000000 --- a/document-portal/xdp-main.c +++ /dev/null @@ -1,1558 +0,0 @@ -#include "config.h" - -#include <locale.h> -#include <stdlib.h> -#include <stdio.h> -#include <string.h> -#include <sys/stat.h> -#include <sys/types.h> -#include <unistd.h> -#include <fcntl.h> -#include <errno.h> - -#include <gio/gio.h> -#include <gio/gunixfdlist.h> -#include "xdp-dbus.h" -#include "xdp-util.h" -#include "flatpak-db.h" -#include "flatpak-dbus.h" -#include "flatpak-utils.h" -#include "flatpak-dir.h" -#include "flatpak-run.h" -#include "flatpak-portal-error.h" -#include "permission-store/permission-store-dbus.h" -#include "xdp-fuse.h" - -#include <sys/eventfd.h> - -#define TABLE_NAME "documents" - -typedef struct -{ - char *doc_id; - int fd; - char *owner; - guint flags; - - GDBusMethodInvocation *finish_invocation; -} XdpDocUpdate; - - -static GMainLoop *loop = NULL; -static FlatpakDb *db = NULL; -static XdgPermissionStore *permission_store; -static int daemon_event_fd = -1; -static int final_exit_status = 0; -static GError *exit_error = NULL; -static dev_t fuse_dev = 0; -static GQueue get_mount_point_invocations = G_QUEUE_INIT; -static XdpDbusDocuments *dbus_api; - -G_LOCK_DEFINE (db); - -char ** -xdp_list_apps (void) -{ - AUTOLOCK (db); - return flatpak_db_list_apps (db); -} - -char ** -xdp_list_docs (void) -{ - AUTOLOCK (db); - return flatpak_db_list_ids (db); -} - -FlatpakDbEntry * -xdp_lookup_doc (const char *doc_id) -{ - AUTOLOCK (db); - return flatpak_db_lookup (db, doc_id); -} - -static gboolean -persist_entry (FlatpakDbEntry *entry) -{ - guint32 flags = xdp_entry_get_flags (entry); - - return (flags & XDP_ENTRY_FLAG_TRANSIENT) == 0; -} - -static void -do_set_permissions (FlatpakDbEntry *entry, - const char *doc_id, - const char *app_id, - XdpPermissionFlags perms) -{ - g_autofree const char **perms_s = xdg_unparse_permissions (perms); - - g_autoptr(FlatpakDbEntry) new_entry = NULL; - - g_debug ("set_permissions %s %s %x", doc_id, app_id, perms); - - new_entry = flatpak_db_entry_set_app_permissions (entry, app_id, perms_s); - flatpak_db_set_entry (db, doc_id, new_entry); - - if (persist_entry (new_entry)) - { - xdg_permission_store_call_set_permission (permission_store, - TABLE_NAME, - FALSE, - doc_id, - app_id, - perms_s, - NULL, - NULL, NULL); - } -} - -static void -portal_grant_permissions (GDBusMethodInvocation *invocation, - GVariant *parameters, - const char *app_id) -{ - g_autoptr(GError) my_error = NULL; - const char *target_app_id; - const char *id; - g_autofree const char **permissions = NULL; - XdpPermissionFlags perms; - - g_autoptr(FlatpakDbEntry) entry = NULL; - - g_variant_get (parameters, "(&s&s^a&s)", &id, &target_app_id, &permissions); - - { - AUTOLOCK (db); - - entry = flatpak_db_lookup (db, id); - if (entry == NULL) - { - g_dbus_method_invocation_return_error (invocation, - FLATPAK_PORTAL_ERROR, FLATPAK_PORTAL_ERROR_NOT_FOUND, - "No such document: %s", id); - return; - } - - if (!flatpak_is_valid_name (target_app_id, &my_error)) - { - g_dbus_method_invocation_return_error (invocation, - FLATPAK_PORTAL_ERROR, FLATPAK_PORTAL_ERROR_INVALID_ARGUMENT, - "'%s' is not a valid app name: %s", target_app_id, my_error->message); - return; - } - - perms = xdp_parse_permissions (permissions); - - /* Must have grant-permissions and all the newly granted permissions */ - if (!xdp_entry_has_permissions (entry, app_id, - XDP_PERMISSION_FLAGS_GRANT_PERMISSIONS | perms)) - { - g_dbus_method_invocation_return_error (invocation, - FLATPAK_PORTAL_ERROR, FLATPAK_PORTAL_ERROR_NOT_ALLOWED, - "Not enough permissions"); - return; - } - - do_set_permissions (entry, id, target_app_id, - perms | xdp_entry_get_permissions (entry, target_app_id)); - } - - /* Invalidate with lock dropped to avoid deadlock */ - xdp_fuse_invalidate_doc_app (id, target_app_id); - - g_dbus_method_invocation_return_value (invocation, g_variant_new ("()")); -} - -static void -portal_revoke_permissions (GDBusMethodInvocation *invocation, - GVariant *parameters, - const char *app_id) -{ - const char *target_app_id; - const char *id; - g_autofree const char **permissions = NULL; - - g_autoptr(FlatpakDbEntry) entry = NULL; - g_autoptr(GError) my_error = NULL; - XdpPermissionFlags perms; - - g_variant_get (parameters, "(&s&s^a&s)", &id, &target_app_id, &permissions); - - { - AUTOLOCK (db); - - entry = flatpak_db_lookup (db, id); - if (entry == NULL) - { - g_dbus_method_invocation_return_error (invocation, - FLATPAK_PORTAL_ERROR, FLATPAK_PORTAL_ERROR_NOT_FOUND, - "No such document: %s", id); - return; - } - - if (!flatpak_is_valid_name (target_app_id, &my_error)) - { - g_dbus_method_invocation_return_error (invocation, - FLATPAK_PORTAL_ERROR, FLATPAK_PORTAL_ERROR_INVALID_ARGUMENT, - "'%s' is not a valid app name: %s", target_app_id, my_error->message); - return; - } - - perms = xdp_parse_permissions (permissions); - - /* Must have grant-permissions, or be itself */ - if (!xdp_entry_has_permissions (entry, app_id, - XDP_PERMISSION_FLAGS_GRANT_PERMISSIONS) || - strcmp (app_id, target_app_id) == 0) - { - g_dbus_method_invocation_return_error (invocation, - FLATPAK_PORTAL_ERROR, FLATPAK_PORTAL_ERROR_NOT_ALLOWED, - "Not enough permissions"); - return; - } - - do_set_permissions (entry, id, target_app_id, - ~perms & xdp_entry_get_permissions (entry, target_app_id)); - } - - /* Invalidate with lock dropped to avoid deadlock */ - xdp_fuse_invalidate_doc_app (id, target_app_id); - - g_dbus_method_invocation_return_value (invocation, g_variant_new ("()")); -} - -static void -portal_delete (GDBusMethodInvocation *invocation, - GVariant *parameters, - const char *app_id) -{ - const char *id; - - g_autoptr(FlatpakDbEntry) entry = NULL; - g_autofree const char **old_apps = NULL; - int i; - - g_variant_get (parameters, "(s)", &id); - - { - AUTOLOCK (db); - - entry = flatpak_db_lookup (db, id); - if (entry == NULL) - { - g_dbus_method_invocation_return_error (invocation, - FLATPAK_PORTAL_ERROR, FLATPAK_PORTAL_ERROR_NOT_FOUND, - "No such document: %s", id); - return; - } - - if (!xdp_entry_has_permissions (entry, app_id, XDP_PERMISSION_FLAGS_DELETE)) - { - g_dbus_method_invocation_return_error (invocation, - FLATPAK_PORTAL_ERROR, FLATPAK_PORTAL_ERROR_NOT_ALLOWED, - "Not enough permissions"); - return; - } - - g_debug ("delete %s", id); - - flatpak_db_set_entry (db, id, NULL); - - if (persist_entry (entry)) - xdg_permission_store_call_delete (permission_store, TABLE_NAME, - id, NULL, NULL, NULL); - } - - /* All i/o is done now, so drop the lock so we can invalidate the fuse caches */ - old_apps = flatpak_db_entry_list_apps (entry); - for (i = 0; old_apps[i] != NULL; i++) - xdp_fuse_invalidate_doc_app (id, old_apps[i]); - xdp_fuse_invalidate_doc_app (id, NULL); - - /* Now fuse view is up-to-date, so we can return the call */ - g_dbus_method_invocation_return_value (invocation, g_variant_new ("()")); -} - -static char * -do_create_doc (struct stat *parent_st_buf, const char *path, gboolean reuse_existing, gboolean persistent) -{ - g_autoptr(GVariant) data = NULL; - g_autoptr(FlatpakDbEntry) entry = NULL; - g_auto(GStrv) ids = NULL; - char *id = NULL; - guint32 flags = 0; - - if (!reuse_existing) - flags |= XDP_ENTRY_FLAG_UNIQUE; - if (!persistent) - flags |= XDP_ENTRY_FLAG_TRANSIENT; - data = - g_variant_ref_sink (g_variant_new ("(^ayttu)", - path, - (guint64) parent_st_buf->st_dev, - (guint64) parent_st_buf->st_ino, - flags)); - - if (reuse_existing) - { - ids = flatpak_db_list_ids_by_value (db, data); - - if (ids[0] != NULL) - return g_strdup (ids[0]); /* Reuse pre-existing entry with same path */ - } - - while (TRUE) - { - g_autoptr(FlatpakDbEntry) existing = NULL; - - g_clear_pointer (&id, g_free); - id = xdp_name_from_id ((guint32) g_random_int ()); - existing = flatpak_db_lookup (db, id); - if (existing == NULL) - break; - } - - g_debug ("create_doc %s", id); - - entry = flatpak_db_entry_new (data); - flatpak_db_set_entry (db, id, entry); - - if (persistent) - { - xdg_permission_store_call_set (permission_store, - TABLE_NAME, - TRUE, - id, - g_variant_new_array (G_VARIANT_TYPE ("{sas}"), NULL, 0), - g_variant_new_variant (data), - NULL, NULL, NULL); - } - - return id; -} - -static gboolean -validate_fd_common (int fd, - struct stat *st_buf, - mode_t st_mode, - char *path_buffer, - GError **error) -{ - g_autofree char *proc_path = NULL; - ssize_t symlink_size; - int fd_flags; - - proc_path = g_strdup_printf ("/proc/self/fd/%d", fd); - - if (fd == -1 || - /* Must be able to get fd flags */ - (fd_flags = fcntl (fd, F_GETFL)) == -1 || - /* Must be O_PATH */ - ((fd_flags & O_PATH) != O_PATH) || - /* Must not be O_NOFOLLOW (because we want the target file) */ - ((fd_flags & O_NOFOLLOW) == O_NOFOLLOW) || - /* Must be able to fstat */ - fstat (fd, st_buf) < 0 || - /* Must be a regular file or directory (depending on use) */ - (st_buf->st_mode & S_IFMT) != st_mode || - /* Must be able to read path from /proc/self/fd */ - /* This is an absolute and (at least at open time) symlink-expanded path */ - (symlink_size = readlink (proc_path, path_buffer, PATH_MAX)) < 0) - { - g_set_error (error, - FLATPAK_PORTAL_ERROR, FLATPAK_PORTAL_ERROR_INVALID_ARGUMENT, - "Invalid fd passed"); - return FALSE; - } - - path_buffer[symlink_size] = 0; - return TRUE; -} - -static gboolean -validate_parent_dir (const char *path, - struct stat *st_buf, - struct stat *real_parent_st_buf, - GError **error) -{ - g_autofree char *dirname = NULL; - g_autofree char *name = NULL; - glnx_autofd int dir_fd = -1; - struct stat real_st_buf; - - /* We open the parent directory and do the stat in that, so that we have - * trustworthy parent dev/ino for later verification. Otherwise the caller - * could later replace a parent with a symlink and make us read some other file - */ - dirname = g_path_get_dirname (path); - name = g_path_get_basename (path); - dir_fd = open (dirname, O_CLOEXEC | O_PATH); - - if (dir_fd < 0 || - fstat (dir_fd, real_parent_st_buf) < 0 || - fstatat (dir_fd, name, &real_st_buf, AT_SYMLINK_NOFOLLOW) < 0 || - st_buf->st_dev != real_st_buf.st_dev || - st_buf->st_ino != real_st_buf.st_ino) - { - /* Don't leak any info about real file path existence, etc */ - g_set_error (error, - FLATPAK_PORTAL_ERROR, FLATPAK_PORTAL_ERROR_INVALID_ARGUMENT, - "Invalid fd passed"); - return FALSE; - } - - return TRUE; -} - -static gboolean -validate_fd (int fd, - GKeyFile *app_info, - struct stat *st_buf, - struct stat *real_parent_st_buf, - char *path_buffer, - GError **error) -{ - g_autofree char *app_path = NULL; - g_autofree char *runtime_path = NULL; - - if (!validate_fd_common (fd, st_buf, S_IFREG, path_buffer, error)) - return FALSE; - - /* For apps we translate /app and /usr to the installed locations. - Also, we need to rewrite to drop the /newroot prefix added by - bubblewrap for other files to work. See - https://github.com/projectatomic/bubblewrap/pull/172 - for a bit more information on the /newroot issue. - */ - app_path = g_key_file_get_string (app_info, FLATPAK_METADATA_GROUP_INSTANCE, - FLATPAK_METADATA_KEY_APP_PATH, NULL); - runtime_path = g_key_file_get_string (app_info, - FLATPAK_METADATA_GROUP_INSTANCE, - FLATPAK_METADATA_KEY_RUNTIME_PATH, - NULL); - if (app_path != NULL || runtime_path != NULL) - { - gboolean had_newroot_prefix = g_str_has_prefix (path_buffer, "/newroot/"); - const char *tmp_path_buf; - if (had_newroot_prefix) - tmp_path_buf = path_buffer + strlen ("/newroot"); - else - tmp_path_buf = path_buffer; - if (app_path != NULL && - g_str_has_prefix (tmp_path_buf, "/app/")) - { - const char *rel_path = tmp_path_buf + strlen ("/app/"); - g_autofree char *real_path = g_build_filename (app_path, rel_path, NULL); - strncpy (path_buffer, real_path, PATH_MAX); - } - else if (runtime_path != NULL && - g_str_has_prefix (tmp_path_buf, "/usr/")) - { - const char *rel_path = tmp_path_buf + strlen ("/usr/"); - g_autofree char *real_path = g_build_filename (runtime_path, rel_path, NULL); - strncpy (path_buffer, real_path, PATH_MAX); - } - else if (g_str_has_prefix (tmp_path_buf, "/run/host/usr/")) - { - const char *rel_path = tmp_path_buf + strlen ("/run/host/usr/"); - g_autofree char *real_path = g_build_filename ("/usr", rel_path, NULL); - strncpy (path_buffer, real_path, PATH_MAX); - } - else if (g_str_has_prefix (tmp_path_buf, "/run/host/etc/")) - { - const char *rel_path = tmp_path_buf + strlen ("/run/host/etc/"); - g_autofree char *real_path = g_build_filename ("/etc", rel_path, NULL); - strncpy (path_buffer, real_path, PATH_MAX); - } - else if (had_newroot_prefix) - { - /* Create a separate copy to avoid memcpy-type issues where - * source and destination overlap. - */ - const char *rel_path = strdupa (tmp_path_buf); - g_strlcpy (path_buffer, rel_path, PATH_MAX); - } - } - - if (!validate_parent_dir (path_buffer, st_buf, real_parent_st_buf, error)) - return FALSE; - - return TRUE; -} - -static char * -verify_existing_document (struct stat *st_buf, gboolean reuse_existing) -{ - g_autoptr(FlatpakDbEntry) old_entry = NULL; - g_autofree char *id = NULL; - - g_assert (st_buf->st_dev == fuse_dev); - - /* The passed in fd is on the fuse filesystem itself */ - id = xdp_fuse_lookup_id_for_inode (st_buf->st_ino); - g_debug ("path on fuse, id %s", id); - if (id == NULL) - return NULL; - - /* Don't lock the db before doing the fuse call above, because it takes takes a lock - that can block something calling back, causing a deadlock on the db lock */ - AUTOLOCK (db); - - /* If the entry doesn't exist anymore, fail. Also fail if not - * reuse_existing, because otherwise the user could use this to - * get a copy with permissions and thus escape later permission - * revocations - */ - old_entry = flatpak_db_lookup (db, id); - if (old_entry == NULL || !reuse_existing) - return NULL; - - return g_steal_pointer (&id); -} - -static void -portal_add (GDBusMethodInvocation *invocation, - GVariant *parameters, - const char *app_id) -{ - GDBusMessage *message; - GUnixFDList *fd_list; - g_autofree char *id = NULL; - int fd_id, fd, fds_len; - char path_buffer[PATH_MAX + 1]; - const int *fds; - struct stat st_buf, real_parent_st_buf; - gboolean reuse_existing, persistent; - GError *error = NULL; - GKeyFile *app_info = g_object_get_data (G_OBJECT (invocation), "app-info"); - - g_variant_get (parameters, "(hbb)", &fd_id, &reuse_existing, &persistent); - - message = g_dbus_method_invocation_get_message (invocation); - fd_list = g_dbus_message_get_unix_fd_list (message); - - fd = -1; - if (fd_list != NULL) - { - fds = g_unix_fd_list_peek_fds (fd_list, &fds_len); - if (fd_id < fds_len) - fd = fds[fd_id]; - } - - if (!validate_fd (fd, app_info, &st_buf, &real_parent_st_buf, path_buffer, &error)) - { - g_dbus_method_invocation_take_error (invocation, error); - return; - } - - if (st_buf.st_dev == fuse_dev) - { - /* The passed in fd is on the fuse filesystem itself */ - id = verify_existing_document (&st_buf, reuse_existing); - if (id == NULL) - { - g_dbus_method_invocation_return_error (invocation, - FLATPAK_PORTAL_ERROR, FLATPAK_PORTAL_ERROR_INVALID_ARGUMENT, - "Invalid fd passed"); - return; - } - } - else - { - { - AUTOLOCK (db); - - id = do_create_doc (&real_parent_st_buf, path_buffer, reuse_existing, persistent); - - if (app_id[0] != '\0') - { - g_autoptr(FlatpakDbEntry) entry = flatpak_db_lookup (db, id); - XdpPermissionFlags perms = - XDP_PERMISSION_FLAGS_GRANT_PERMISSIONS | - XDP_PERMISSION_FLAGS_READ | - XDP_PERMISSION_FLAGS_WRITE; - - /* If its a unique one its safe for the creator to - delete it at will */ - if (!reuse_existing) - perms |= XDP_PERMISSION_FLAGS_DELETE; - - do_set_permissions (entry, id, app_id, perms); - } - } - - /* Invalidate with lock dropped to avoid deadlock */ - xdp_fuse_invalidate_doc_app (id, NULL); - if (app_id[0] != '\0') - xdp_fuse_invalidate_doc_app (id, app_id); - } - - g_dbus_method_invocation_return_value (invocation, - g_variant_new ("(s)", id)); -} - -static gboolean -app_has_file_access (const char *target_app_id, - XdpPermissionFlags target_perms, - const char *path) -{ - g_autoptr(FlatpakContext) app_context = NULL; - g_autoptr(FlatpakExports) app_exports = NULL; - FlatpakFilesystemMode mode = 0; - - if (target_app_id == NULL || target_app_id[0] == '\0') - return FALSE; - - app_context = flatpak_context_load_for_app (target_app_id, NULL); - if (app_context == NULL) - return FALSE; - - app_exports = flatpak_context_get_exports (app_context, target_app_id); - if (app_exports == NULL) - return FALSE; - - mode = flatpak_exports_path_get_mode (app_exports, path); - - if (mode == FLATPAK_FILESYSTEM_MODE_READ_WRITE) - return TRUE; - - if ((mode == FLATPAK_FILESYSTEM_MODE_READ_ONLY) && - ((target_perms & XDP_PERMISSION_FLAGS_WRITE) == 0)) - return TRUE; - - return FALSE; -} - -static void -portal_add_full (GDBusMethodInvocation *invocation, - GVariant *parameters, - const char *app_id) -{ - GDBusMessage *message; - GUnixFDList *fd_list; - char *id; - int fd_id, fd, fds_len; - char path_buffer[PATH_MAX + 1]; - const int *fds = NULL; - struct stat st_buf; - gboolean reuse_existing, persistent, as_needed_by_app; - GError *error = NULL; - guint32 flags = 0; - GKeyFile *app_info = g_object_get_data (G_OBJECT (invocation), "app-info"); - g_autoptr(GVariant) array = NULL; - const char *target_app_id; - g_autofree const char **permissions = NULL; - g_autoptr(GPtrArray) ids = g_ptr_array_new_with_free_func (g_free); - g_autoptr(GPtrArray) paths = g_ptr_array_new_with_free_func (g_free); - g_autofree struct stat *real_parent_st_bufs = NULL; - int i; - gsize n_args; - XdpPermissionFlags target_perms; - GVariantBuilder builder; - - g_variant_get (parameters, "(@ahus^a&s)", - &array, &flags, &target_app_id, &permissions); - - if ((flags & ~XDP_ADD_FLAGS_FLAGS_ALL) != 0) - { - g_dbus_method_invocation_return_error (invocation, - FLATPAK_PORTAL_ERROR, FLATPAK_PORTAL_ERROR_INVALID_ARGUMENT, - "Invalid flags"); - return; - } - - reuse_existing = (flags & XDP_ADD_FLAGS_REUSE_EXISTING) != 0; - persistent = (flags & XDP_ADD_FLAGS_PERSISTENT) != 0; - as_needed_by_app = (flags & XDP_ADD_FLAGS_AS_NEEDED_BY_APP) != 0; - - target_perms = xdp_parse_permissions (permissions); - - n_args = g_variant_n_children (array); - g_ptr_array_set_size (ids, n_args + 1); - g_ptr_array_set_size (paths, n_args + 1); - real_parent_st_bufs = g_new0 (struct stat, n_args); - - message = g_dbus_method_invocation_get_message (invocation); - fd_list = g_dbus_message_get_unix_fd_list (message); - if (fd_list != NULL) - fds = g_unix_fd_list_peek_fds (fd_list, &fds_len); - - for (i = 0; i < n_args; i++) - { - g_variant_get_child (array, i, "h", &fd_id); - - fd = -1; - if (fds != NULL && fd_id < fds_len) - fd = fds[fd_id]; - - if (!validate_fd (fd, app_info, &st_buf, &real_parent_st_bufs[i], path_buffer, &error)) - { - g_dbus_method_invocation_take_error (invocation, error); - return; - } - - g_ptr_array_index(paths,i) = g_strdup (path_buffer); - - if (st_buf.st_dev == fuse_dev) - { - /* The passed in fd is on the fuse filesystem itself */ - id = verify_existing_document (&st_buf, reuse_existing); - if (id == NULL) - { - g_dbus_method_invocation_return_error (invocation, - FLATPAK_PORTAL_ERROR, FLATPAK_PORTAL_ERROR_INVALID_ARGUMENT, - "Invalid fd passed"); - return; - } - g_ptr_array_index(ids,i) = id; - } - } - - { - XdpPermissionFlags caller_perms = - XDP_PERMISSION_FLAGS_GRANT_PERMISSIONS | - XDP_PERMISSION_FLAGS_READ | - XDP_PERMISSION_FLAGS_WRITE; - - /* If its a unique one its safe for the creator to - delete it at will */ - if (!reuse_existing) - caller_perms |= XDP_PERMISSION_FLAGS_DELETE; - - AUTOLOCK (db); /* Lock once for all ops */ - - for (i = 0; i < n_args; i++) - { - const char *path = g_ptr_array_index(paths,i); - g_assert (path != NULL); - - if (as_needed_by_app && - app_has_file_access (target_app_id, target_perms, path)) - { - g_free (g_ptr_array_index(ids,i)); - g_ptr_array_index(ids,i) = g_strdup (""); - continue; - } - - if (g_ptr_array_index(ids,i) == NULL) - { - id = do_create_doc (&real_parent_st_bufs[i], path, reuse_existing, persistent); - g_ptr_array_index(ids,i) = id; - - if (app_id[0] != '\0' && strcmp (app_id, target_app_id) != 0) - { - g_autoptr(FlatpakDbEntry) entry = flatpak_db_lookup (db, id);; - do_set_permissions (entry, id, app_id, caller_perms); - } - - if (target_app_id[0] != '\0' && target_perms != 0) - { - g_autoptr(FlatpakDbEntry) entry = flatpak_db_lookup (db, id); - do_set_permissions (entry, id, target_app_id, target_perms); - } - } - } - } - - /* Invalidate with lock dropped to avoid deadlock */ - for (i = 0; i < n_args; i++) - { - id = g_ptr_array_index (ids,i); - g_assert (id != NULL); - - if (*id == 0) - continue; - - xdp_fuse_invalidate_doc_app (id, NULL); - if (app_id[0] != '\0') - xdp_fuse_invalidate_doc_app (id, app_id); - if (target_app_id[0] != '\0' && target_perms != 0) - xdp_fuse_invalidate_doc_app (id, target_app_id); - } - - g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}")); - g_variant_builder_add (&builder, "{sv}", "mountpoint", - g_variant_new_bytestring (xdp_fuse_get_mountpoint ())); - - g_dbus_method_invocation_return_value (invocation, - g_variant_new ("(^as@a{sv})", - (char **)ids->pdata, - g_variant_builder_end (&builder))); -} - -static void -portal_add_named_full (GDBusMethodInvocation *invocation, - GVariant *parameters, - const char *app_id) -{ - GDBusMessage *message; - GUnixFDList *fd_list; - int parent_fd_id, parent_fd, fds_len; - char parent_path_buffer[PATH_MAX + 1]; - const int *fds = NULL; - struct stat parent_st_buf; - gboolean reuse_existing, persistent, as_needed_by_app; - GError *error = NULL; - guint32 flags = 0; - const char *filename; - const char *target_app_id; - g_autofree const char **permissions = NULL; - g_autofree char *id = NULL; - g_autofree char *path = NULL; - XdpPermissionFlags target_perms; - GVariantBuilder builder; - g_autoptr(GVariant) filename_v = NULL; - - g_variant_get (parameters, "(h@ayus^a&s)", &parent_fd_id, &filename_v, &flags, &target_app_id, &permissions); - filename = g_variant_get_bytestring (filename_v); - - /* This is only allowed from the host, or else we could leak existence of files */ - if (*app_id != 0) - { - g_dbus_method_invocation_return_error (invocation, - FLATPAK_PORTAL_ERROR, FLATPAK_PORTAL_ERROR_NOT_ALLOWED, - "Not enough permissions"); - return; - } - - if ((flags & ~XDP_ADD_FLAGS_FLAGS_ALL) != 0) - { - g_dbus_method_invocation_return_error (invocation, - FLATPAK_PORTAL_ERROR, FLATPAK_PORTAL_ERROR_INVALID_ARGUMENT, - "Invalid flags"); - return; - } - - reuse_existing = (flags & XDP_ADD_FLAGS_REUSE_EXISTING) != 0; - persistent = (flags & XDP_ADD_FLAGS_PERSISTENT) != 0; - as_needed_by_app = (flags & XDP_ADD_FLAGS_AS_NEEDED_BY_APP) != 0; - - target_perms = xdp_parse_permissions (permissions); - - message = g_dbus_method_invocation_get_message (invocation); - fd_list = g_dbus_message_get_unix_fd_list (message); - - parent_fd = -1; - if (fd_list != NULL) - { - fds = g_unix_fd_list_peek_fds (fd_list, &fds_len); - if (parent_fd_id < fds_len) - parent_fd = fds[parent_fd_id]; - } - - if (strchr (filename, '/') != NULL) - { - g_dbus_method_invocation_return_error (invocation, - FLATPAK_PORTAL_ERROR, FLATPAK_PORTAL_ERROR_INVALID_ARGUMENT, - "Invalid filename passed"); - return; - } - - if (!validate_fd_common (parent_fd, &parent_st_buf, S_IFDIR, parent_path_buffer, &error)) - { - g_dbus_method_invocation_return_gerror (invocation, error); - return; - } - - if (parent_st_buf.st_dev == fuse_dev) - { - g_dbus_method_invocation_return_error (invocation, - FLATPAK_PORTAL_ERROR, FLATPAK_PORTAL_ERROR_INVALID_ARGUMENT, - "Invalid fd passed"); - return; - } - - path = g_build_filename (parent_path_buffer, filename, NULL); - - g_debug ("portal_add_named_full %s", path); - - { - XdpPermissionFlags caller_perms = - XDP_PERMISSION_FLAGS_GRANT_PERMISSIONS | - XDP_PERMISSION_FLAGS_READ | - XDP_PERMISSION_FLAGS_WRITE; - - /* If its a unique one its safe for the creator to - delete it at will */ - if (!reuse_existing) - caller_perms |= XDP_PERMISSION_FLAGS_DELETE; - - AUTOLOCK (db); - - if (as_needed_by_app && - app_has_file_access (target_app_id, target_perms, path)) - { - id = g_strdup (""); - } - else - { - id = do_create_doc (&parent_st_buf, path, reuse_existing, persistent); - - if (app_id[0] != '\0' && strcmp (app_id, target_app_id) != 0) - { - g_autoptr(FlatpakDbEntry) entry = flatpak_db_lookup (db, id);; - do_set_permissions (entry, id, app_id, caller_perms); - } - - if (target_app_id[0] != '\0' && target_perms != 0) - { - g_autoptr(FlatpakDbEntry) entry = flatpak_db_lookup (db, id); - do_set_permissions (entry, id, target_app_id, target_perms); - } - } - } - - /* Invalidate with lock dropped to avoid deadlock */ - g_assert (id != NULL); - - if (*id != 0) - { - xdp_fuse_invalidate_doc_app (id, NULL); - if (app_id[0] != '\0') - xdp_fuse_invalidate_doc_app (id, app_id); - if (target_app_id[0] != '\0' && target_perms != 0) - xdp_fuse_invalidate_doc_app (id, target_app_id); - } - - g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}")); - g_variant_builder_add (&builder, "{sv}", "mountpoint", - g_variant_new_bytestring (xdp_fuse_get_mountpoint ())); - - g_dbus_method_invocation_return_value (invocation, - g_variant_new ("(s@a{sv})", - id, - g_variant_builder_end (&builder))); -} - -static void -portal_add_named (GDBusMethodInvocation *invocation, - GVariant *parameters, - const char *app_id) -{ - GDBusMessage *message; - GUnixFDList *fd_list; - g_autofree char *id = NULL; - int parent_fd_id, parent_fd, fds_len; - const int *fds; - char parent_path_buffer[PATH_MAX + 1]; - g_autofree char *path = NULL; - struct stat parent_st_buf; - const char *filename; - gboolean reuse_existing, persistent; - g_autoptr(GError) error = NULL; - - g_autoptr(GVariant) filename_v = NULL; - - g_variant_get (parameters, "(h@aybb)", &parent_fd_id, &filename_v, &reuse_existing, &persistent); - filename = g_variant_get_bytestring (filename_v); - - /* This is only allowed from the host, or else we could leak existence of files */ - if (*app_id != 0) - { - g_dbus_method_invocation_return_error (invocation, - FLATPAK_PORTAL_ERROR, FLATPAK_PORTAL_ERROR_NOT_ALLOWED, - "Not enough permissions"); - return; - } - - message = g_dbus_method_invocation_get_message (invocation); - fd_list = g_dbus_message_get_unix_fd_list (message); - - parent_fd = -1; - if (fd_list != NULL) - { - fds = g_unix_fd_list_peek_fds (fd_list, &fds_len); - if (parent_fd_id < fds_len) - parent_fd = fds[parent_fd_id]; - } - - if (strchr (filename, '/') != NULL) - { - g_dbus_method_invocation_return_error (invocation, - FLATPAK_PORTAL_ERROR, FLATPAK_PORTAL_ERROR_INVALID_ARGUMENT, - "Invalid filename passed"); - return; - } - - if (!validate_fd_common (parent_fd, &parent_st_buf, S_IFDIR, parent_path_buffer, &error)) - { - g_dbus_method_invocation_return_gerror (invocation, error); - return; - } - - if (parent_st_buf.st_dev == fuse_dev) - { - g_dbus_method_invocation_return_error (invocation, - FLATPAK_PORTAL_ERROR, FLATPAK_PORTAL_ERROR_INVALID_ARGUMENT, - "Invalid fd passed"); - return; - } - - path = g_build_filename (parent_path_buffer, filename, NULL); - - g_debug ("portal_add_named %s", path); - - AUTOLOCK (db); - - id = do_create_doc (&parent_st_buf, path, reuse_existing, persistent); - - g_dbus_method_invocation_return_value (invocation, - g_variant_new ("(s)", id)); -} - - -typedef void (*PortalMethod) (GDBusMethodInvocation *invocation, - GVariant *parameters, - const char *app_id); - -static void -got_app_id_cb (GObject *source_object, - GAsyncResult *res, - gpointer user_data) -{ - GDBusMethodInvocation *invocation = G_DBUS_METHOD_INVOCATION (source_object); - - g_autoptr(GError) error = NULL; - g_autoptr(GKeyFile) app_info = NULL; - g_autofree char *app_id = NULL; - PortalMethod portal_method = user_data; - - app_info = flatpak_invocation_lookup_app_info_finish (invocation, res, &error); - if (app_info != NULL) - app_id = g_key_file_get_string (app_info, - FLATPAK_METADATA_GROUP_APPLICATION, - FLATPAK_METADATA_KEY_NAME, &error); - - if (app_id == NULL) - g_dbus_method_invocation_return_gerror (invocation, error); - else - { - g_object_set_data_full (G_OBJECT (invocation), "app-info", g_steal_pointer (&app_info), (GDestroyNotify)g_key_file_unref); - portal_method (invocation, g_dbus_method_invocation_get_parameters (invocation), app_id); - } -} - -static gboolean -handle_method (GCallback method_callback, - GDBusMethodInvocation *invocation) -{ - flatpak_invocation_lookup_app_info (invocation, NULL, got_app_id_cb, method_callback); - - return TRUE; -} - -static gboolean -handle_get_mount_point (XdpDbusDocuments *object, GDBusMethodInvocation *invocation) -{ - if (fuse_dev == 0) - { - /* We mustn't reply to this until the FUSE mount point is open for - * business. */ - g_queue_push_tail (&get_mount_point_invocations, g_object_ref (invocation)); - return TRUE; - } - - xdp_dbus_documents_complete_get_mount_point (object, invocation, xdp_fuse_get_mountpoint ()); - return TRUE; -} - -static gboolean -portal_lookup (GDBusMethodInvocation *invocation, - GVariant *parameters, - const char *app_id) -{ - const char *filename; - char path_buffer[PATH_MAX + 1]; - glnx_autofd int fd = -1; - struct stat st_buf, real_parent_st_buf; - g_auto(GStrv) ids = NULL; - g_autofree char *id = NULL; - GError *error = NULL; - GKeyFile *app_info = g_object_get_data (G_OBJECT (invocation), "app-info"); - - if (strcmp (app_id, "") != 0) - { - g_dbus_method_invocation_return_error (invocation, - FLATPAK_PORTAL_ERROR, FLATPAK_PORTAL_ERROR_NOT_ALLOWED, - "Not allowed in sandbox"); - return TRUE; - } - - g_variant_get (parameters, "(^&ay)", &filename); - - fd = open (filename, O_PATH | O_CLOEXEC); - if (fd == -1) - { - glnx_set_error_from_errno (&error); - g_dbus_method_invocation_take_error (invocation, error); - return TRUE; - } - - if (!validate_fd (fd, app_info, &st_buf, &real_parent_st_buf, path_buffer, &error)) - { - g_dbus_method_invocation_take_error (invocation, error); - return TRUE; - } - - if (st_buf.st_dev == fuse_dev) - { - /* The passed in fd is on the fuse filesystem itself */ - id = xdp_fuse_lookup_id_for_inode (st_buf.st_ino); - g_debug ("path on fuse, id %s", id); - } - else - { - g_autoptr(GVariant) data = NULL; - - data = g_variant_ref_sink (g_variant_new ("(^ayttu)", - path_buffer, - (guint64)real_parent_st_buf.st_dev, - (guint64)real_parent_st_buf.st_ino, - 0)); - ids = flatpak_db_list_ids_by_value (db, data); - if (ids[0] != NULL) - id = g_strdup (ids[0]); - } - - g_dbus_method_invocation_return_value (invocation, - g_variant_new ("(s)", id ? id : "")); - - return TRUE; -} - -static GVariant * -get_app_permissions (FlatpakDbEntry *entry) -{ - g_autofree const char **apps = NULL; - GVariantBuilder builder; - int i; - - apps = flatpak_db_entry_list_apps (entry); - g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sas}")); - - for (i = 0; apps[i] != NULL; i++) - { - g_autofree const char **permissions = flatpak_db_entry_list_permissions (entry, apps[i]); - g_variant_builder_add_value (&builder, - g_variant_new ("{s^as}", apps[i], permissions)); - } - - return g_variant_builder_end (&builder); -} - -static GVariant * -get_path (FlatpakDbEntry *entry) -{ - g_autoptr (GVariant) data = flatpak_db_entry_get_data (entry); - const char *path; - - g_variant_get (data, "(^ayttu)", &path, NULL, NULL, NULL); - return g_variant_new_bytestring (path); -} - -static gboolean -portal_info (GDBusMethodInvocation *invocation, - GVariant *parameters, - const char *app_id) -{ - const char *id = NULL; - g_autoptr(FlatpakDbEntry) entry = NULL; - - if (strcmp (app_id, "") != 0) - { - g_dbus_method_invocation_return_error (invocation, - FLATPAK_PORTAL_ERROR, FLATPAK_PORTAL_ERROR_NOT_ALLOWED, - "Not allowed in sandbox"); - return TRUE; - } - - g_variant_get (parameters, "(&s)", &id); - - AUTOLOCK (db); - - entry = flatpak_db_lookup (db, id); - - if (!entry) - { - g_dbus_method_invocation_return_error (invocation, - FLATPAK_PORTAL_ERROR, FLATPAK_PORTAL_ERROR_INVALID_ARGUMENT, - "Invalid ID passed"); - return TRUE; - } - - g_dbus_method_invocation_return_value (invocation, - g_variant_new ("(@ay@a{sas})", - get_path (entry), - get_app_permissions (entry))); - - return TRUE; -} - -static gboolean -portal_list (GDBusMethodInvocation *invocation, - GVariant *parameters, - const char *app_id) -{ - g_auto(GStrv) ids = NULL; - GVariantBuilder builder; - int i; - - if (strcmp (app_id, "") != 0) - { - g_dbus_method_invocation_return_error (invocation, - FLATPAK_PORTAL_ERROR, FLATPAK_PORTAL_ERROR_NOT_ALLOWED, - "Not allowed in sandbox"); - return TRUE; - } - - g_variant_get (parameters, "(&s)", &app_id); - - AUTOLOCK (db); - - if (strcmp (app_id, "") == 0) - ids = flatpak_db_list_ids (db); - else - ids = flatpak_db_list_ids_by_app (db, app_id); - - g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{say}")); - for (i = 0; ids[i]; i++) - { - g_autoptr(FlatpakDbEntry) entry = NULL; - - entry = flatpak_db_lookup (db, ids[i]); - - g_variant_builder_add (&builder, "{s@ay}", ids[i], get_path (entry)); - } - - g_dbus_method_invocation_return_value (invocation, - g_variant_new ("(@a{say})", - g_variant_builder_end (&builder))); - - return TRUE; -} - -static void -on_bus_acquired (GDBusConnection *connection, - const gchar *name, - gpointer user_data) -{ - GError *error = NULL; - - dbus_api = xdp_dbus_documents_skeleton_new (); - - xdp_dbus_documents_set_version (XDP_DBUS_DOCUMENTS (dbus_api), 3); - - g_signal_connect_swapped (dbus_api, "handle-get-mount-point", G_CALLBACK (handle_get_mount_point), NULL); - g_signal_connect_swapped (dbus_api, "handle-add", G_CALLBACK (handle_method), portal_add); - g_signal_connect_swapped (dbus_api, "handle-add-named", G_CALLBACK (handle_method), portal_add_named); - g_signal_connect_swapped (dbus_api, "handle-add-full", G_CALLBACK (handle_method), portal_add_full); - g_signal_connect_swapped (dbus_api, "handle-add-named-full", G_CALLBACK (handle_method), portal_add_named_full); - g_signal_connect_swapped (dbus_api, "handle-grant-permissions", G_CALLBACK (handle_method), portal_grant_permissions); - g_signal_connect_swapped (dbus_api, "handle-revoke-permissions", G_CALLBACK (handle_method), portal_revoke_permissions); - g_signal_connect_swapped (dbus_api, "handle-delete", G_CALLBACK (handle_method), portal_delete); - g_signal_connect_swapped (dbus_api, "handle-lookup", G_CALLBACK (handle_method), portal_lookup); - g_signal_connect_swapped (dbus_api, "handle-info", G_CALLBACK (handle_method), portal_info); - g_signal_connect_swapped (dbus_api, "handle-list", G_CALLBACK (handle_method), portal_list); - - flatpak_connection_track_name_owners (connection); - - if (!g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (dbus_api), - connection, - "/org/freedesktop/portal/documents", - &error)) - { - g_warning ("error: %s", error->message); - g_error_free (error); - } -} - -static void -daemon_report_done (int status) -{ - if (daemon_event_fd != -1) - { - guint64 counter; - - counter = status + 1; - if (write (daemon_event_fd, &counter, sizeof (counter)) < 0) - g_critical ("Unable to report exit status: %s", g_strerror (errno)); - - daemon_event_fd = -1; - } -} - -static void -do_exit (int status) -{ - daemon_report_done (status); - exit (status); -} - -static void -on_name_acquired (GDBusConnection *connection, - const gchar *name, - gpointer user_data) -{ - struct stat stbuf; - gpointer invocation; - - g_debug ("%s acquired", name); - - if (!xdp_fuse_init (&exit_error)) - { - final_exit_status = 6; - g_printerr ("fuse init failed: %s", exit_error->message); - g_main_loop_quit (loop); - return; - } - - if (stat (xdp_fuse_get_mountpoint (), &stbuf) != 0) - { - g_set_error (&exit_error, G_DBUS_ERROR, G_DBUS_ERROR_FAILED, "fuse stat failed: %s", g_strerror (errno)); - final_exit_status = 7; - g_printerr ("fuse stat failed: %s", g_strerror (errno)); - g_main_loop_quit (loop); - return; - } - - fuse_dev = stbuf.st_dev; - - while ((invocation = g_queue_pop_head (&get_mount_point_invocations)) != NULL) - { - xdp_dbus_documents_complete_get_mount_point (dbus_api, invocation, xdp_fuse_get_mountpoint ()); - g_object_unref (invocation); - } - - daemon_report_done (0); -} - -static void -on_name_lost (GDBusConnection *connection, - const gchar *name, - gpointer user_data) -{ - g_debug ("%s lost", name); - - if (final_exit_status == 0) - final_exit_status = 20; - - if (exit_error == NULL) - g_set_error (&exit_error, G_DBUS_ERROR, G_DBUS_ERROR_FAILED, "D-Bus name \"%s\" lost", name); - - g_main_loop_quit (loop); -} - -static void -exit_handler (int sig) -{ - /* We cannot set exit_error here, because malloc() in a signal handler - * is undefined behaviour. Rely on main() coping gracefully with - * that. */ - g_main_loop_quit (loop); -} - -static void -session_bus_closed (GDBusConnection *connection, - gboolean remote_peer_vanished, - GError *bus_error) -{ - if (exit_error == NULL) - g_set_error (&exit_error, G_IO_ERROR, G_IO_ERROR_BROKEN_PIPE, "Disconnected from session bus"); - - g_main_loop_quit (loop); -} - -static int -set_one_signal_handler (int sig, - void (*handler)(int), - int remove) -{ - struct sigaction sa; - struct sigaction old_sa; - - memset (&sa, 0, sizeof (struct sigaction)); - sa.sa_handler = remove ? SIG_DFL : handler; - sigemptyset (&(sa.sa_mask)); - sa.sa_flags = 0; - - if (sigaction (sig, NULL, &old_sa) == -1) - { - g_warning ("cannot get old signal handler"); - return -1; - } - - if (old_sa.sa_handler == (remove ? handler : SIG_DFL) && - sigaction (sig, &sa, NULL) == -1) - { - g_warning ("cannot set signal handler"); - return -1; - } - - return 0; -} - -static gboolean opt_verbose; -static gboolean opt_daemon; -static gboolean opt_replace; -static gboolean opt_version; - -static GOptionEntry entries[] = { - { "verbose", 'v', 0, G_OPTION_ARG_NONE, &opt_verbose, "Print debug information", NULL }, - { "daemon", 'd', 0, G_OPTION_ARG_NONE, &opt_daemon, "Run in background", NULL }, - { "replace", 'r', 0, G_OPTION_ARG_NONE, &opt_replace, "Replace", NULL }, - { "version", 0, 0, G_OPTION_ARG_NONE, &opt_version, "Print version and exit", NULL }, - { NULL } -}; - -static void -message_handler (const gchar *log_domain, - GLogLevelFlags log_level, - const gchar *message, - gpointer user_data) -{ - /* Make this look like normal console output */ - if (log_level & G_LOG_LEVEL_DEBUG) - printf ("XDP: %s\n", message); - else - printf ("%s: %s\n", g_get_prgname (), message); -} - -static void -printerr_handler (const gchar *string) -{ - const char *prefix = ""; - const char *suffix = ""; - if (flatpak_fancy_output ()) - { - prefix = FLATPAK_ANSI_RED FLATPAK_ANSI_BOLD_ON; - suffix = FLATPAK_ANSI_BOLD_OFF FLATPAK_ANSI_COLOR_RESET; - } - fprintf (stderr, "%serror: %s%s\n", prefix, suffix, string); -} - -int -main (int argc, - char **argv) -{ - guint owner_id; - - g_autoptr(GError) error = NULL; - g_autofree char *path = NULL; - GDBusConnection *session_bus; - GOptionContext *context; - GDBusMethodInvocation *invocation; - - setlocale (LC_ALL, ""); - - /* Avoid even loading gvfs to avoid accidental confusion */ - g_setenv ("GIO_USE_VFS", "local", TRUE); - - flatpak_migrate_from_xdg_app (); - - g_set_printerr_handler (printerr_handler); - - context = g_option_context_new ("- document portal"); - g_option_context_add_main_entries (context, entries, NULL); - if (!g_option_context_parse (context, &argc, &argv, &error)) - { - g_printerr ("Option parsing failed: %s", error->message); - return 1; - } - - if (opt_version) - { - g_print ("%s\n", PACKAGE_STRING); - exit (EXIT_SUCCESS); - } - - if (opt_daemon) - { - pid_t pid; - ssize_t read_res; - - daemon_event_fd = eventfd (0, EFD_CLOEXEC); - pid = fork (); - if (pid != 0) - { - guint64 counter; - - read_res = read (daemon_event_fd, &counter, sizeof (counter)); - if (read_res != 8) - exit (1); - exit (counter - 1); - } - } - - if (opt_verbose) - g_log_set_handler (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, message_handler, NULL); - - g_set_prgname (argv[0]); - - loop = g_main_loop_new (NULL, FALSE); - - path = g_build_filename (g_get_user_data_dir (), "flatpak/db", TABLE_NAME, NULL); - db = flatpak_db_new (path, FALSE, &error); - if (db == NULL) - { - g_printerr ("Failed to load db: %s", error->message); - do_exit (2); - } - - session_bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error); - if (session_bus == NULL) - { - g_printerr ("No session bus: %s", error->message); - do_exit (3); - } - - permission_store = xdg_permission_store_proxy_new_sync (session_bus, G_DBUS_PROXY_FLAGS_NONE, - "org.freedesktop.impl.portal.PermissionStore", - "/org/freedesktop/impl/portal/PermissionStore", - NULL, &error); - if (permission_store == NULL) - { - g_print ("No permission store: %s", error->message); - do_exit (4); - } - - /* We want do do our custom post-mainloop exit */ - g_dbus_connection_set_exit_on_close (session_bus, FALSE); - - g_signal_connect (session_bus, "closed", G_CALLBACK (session_bus_closed), NULL); - - if (set_one_signal_handler (SIGHUP, exit_handler, 0) == -1 || - set_one_signal_handler (SIGINT, exit_handler, 0) == -1 || - set_one_signal_handler (SIGTERM, exit_handler, 0) == -1 || - set_one_signal_handler (SIGPIPE, SIG_IGN, 0) == -1) - do_exit (5); - - owner_id = g_bus_own_name (G_BUS_TYPE_SESSION, - "org.freedesktop.portal.Documents", - G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT | (opt_replace ? G_BUS_NAME_OWNER_FLAGS_REPLACE : 0), - on_bus_acquired, - on_name_acquired, - on_name_lost, - NULL, - NULL); - - g_main_loop_run (loop); - - while ((invocation = g_queue_pop_head (&get_mount_point_invocations)) != NULL) - { - if (exit_error != NULL) - g_dbus_method_invocation_return_gerror (invocation, exit_error); - else - g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_FAILED, "Terminated"); - - g_object_unref (invocation); - } - - xdp_fuse_exit (); - - g_bus_unown_name (owner_id); - - do_exit (final_exit_status); - - return 0; -} diff --git a/document-portal/xdp-util.c b/document-portal/xdp-util.c deleted file mode 100644 index 0f5f0854..00000000 --- a/document-portal/xdp-util.c +++ /dev/null @@ -1,129 +0,0 @@ -#include "config.h" -#include <string.h> -#include <errno.h> -#include <gio/gio.h> -#include "flatpak-portal-error.h" -#include "xdp-util.h" - -const char ** -xdg_unparse_permissions (XdpPermissionFlags permissions) -{ - GPtrArray *array; - - array = g_ptr_array_new (); - - if (permissions & XDP_PERMISSION_FLAGS_READ) - g_ptr_array_add (array, "read"); - if (permissions & XDP_PERMISSION_FLAGS_WRITE) - g_ptr_array_add (array, "write"); - if (permissions & XDP_PERMISSION_FLAGS_GRANT_PERMISSIONS) - g_ptr_array_add (array, "grant-permissions"); - if (permissions & XDP_PERMISSION_FLAGS_DELETE) - g_ptr_array_add (array, "delete"); - - g_ptr_array_add (array, NULL); - return (const char **) g_ptr_array_free (array, FALSE); -} - -XdpPermissionFlags -xdp_parse_permissions (const char **permissions) -{ - XdpPermissionFlags perms; - int i; - - perms = 0; - for (i = 0; permissions[i]; i++) - { - if (strcmp (permissions[i], "read") == 0) - perms |= XDP_PERMISSION_FLAGS_READ; - else if (strcmp (permissions[i], "write") == 0) - perms |= XDP_PERMISSION_FLAGS_WRITE; - else if (strcmp (permissions[i], "grant-permissions") == 0) - perms |= XDP_PERMISSION_FLAGS_GRANT_PERMISSIONS; - else if (strcmp (permissions[i], "delete") == 0) - perms |= XDP_PERMISSION_FLAGS_DELETE; - else - g_warning ("No such permission: %s", permissions[i]); - } - - return perms; -} - -XdpPermissionFlags -xdp_entry_get_permissions (FlatpakDbEntry *entry, - const char *app_id) -{ - g_autofree const char **permissions = NULL; - - if (strcmp (app_id, "") == 0) - return XDP_PERMISSION_FLAGS_ALL; - - permissions = flatpak_db_entry_list_permissions (entry, app_id); - return xdp_parse_permissions (permissions); -} - -gboolean -xdp_entry_has_permissions (FlatpakDbEntry *entry, - const char *app_id, - XdpPermissionFlags perms) -{ - XdpPermissionFlags current_perms; - - current_perms = xdp_entry_get_permissions (entry, app_id); - - return (current_perms & perms) == perms; -} - -char * -xdp_name_from_id (guint32 doc_id) -{ - return g_strdup_printf ("%x", doc_id); -} - -const char * -xdp_entry_get_path (FlatpakDbEntry *entry) -{ - g_autoptr(GVariant) v = flatpak_db_entry_get_data (entry); - g_autoptr(GVariant) c = g_variant_get_child_value (v, 0); - return g_variant_get_bytestring (c); -} - -char * -xdp_entry_dup_basename (FlatpakDbEntry *entry) -{ - const char *path = xdp_entry_get_path (entry); - - return g_path_get_basename (path); -} - -char * -xdp_entry_dup_dirname (FlatpakDbEntry *entry) -{ - const char *path = xdp_entry_get_path (entry); - - return g_path_get_dirname (path); -} - -guint64 -xdp_entry_get_device (FlatpakDbEntry *entry) -{ - g_autoptr(GVariant) v = flatpak_db_entry_get_data (entry); - g_autoptr(GVariant) c = g_variant_get_child_value (v, 1); - return g_variant_get_uint64 (c); -} - -guint64 -xdp_entry_get_inode (FlatpakDbEntry *entry) -{ - g_autoptr(GVariant) v = flatpak_db_entry_get_data (entry); - g_autoptr(GVariant) c = g_variant_get_child_value (v, 2); - return g_variant_get_uint64 (c); -} - -guint32 -xdp_entry_get_flags (FlatpakDbEntry *entry) -{ - g_autoptr(GVariant) v = flatpak_db_entry_get_data (entry); - g_autoptr(GVariant) c = g_variant_get_child_value (v, 3); - return g_variant_get_uint32 (c); -} diff --git a/document-portal/xdp-util.h b/document-portal/xdp-util.h deleted file mode 100644 index 007a2438..00000000 --- a/document-portal/xdp-util.h +++ /dev/null @@ -1,33 +0,0 @@ -#ifndef XDP_UTIL_H -#define XDP_UTIL_H - -#include <gio/gio.h> -#include "flatpak-db.h" -#include "xdp-enums.h" - -G_BEGIN_DECLS - -#define XDP_ENTRY_FLAG_UNIQUE (1 << 0) -#define XDP_ENTRY_FLAG_TRANSIENT (1 << 1) - -const char ** xdg_unparse_permissions (XdpPermissionFlags permissions); -XdpPermissionFlags xdp_parse_permissions (const char **permissions); - -XdpPermissionFlags xdp_entry_get_permissions (FlatpakDbEntry *entry, - const char *app_id); -gboolean xdp_entry_has_permissions (FlatpakDbEntry *entry, - const char *app_id, - XdpPermissionFlags perms); -const char * xdp_entry_get_path (FlatpakDbEntry *entry); -char * xdp_entry_dup_basename (FlatpakDbEntry *entry); -char * xdp_entry_dup_dirname (FlatpakDbEntry *entry); -guint64 xdp_entry_get_device (FlatpakDbEntry *entry); -guint64 xdp_entry_get_inode (FlatpakDbEntry *entry); -guint32 xdp_entry_get_flags (FlatpakDbEntry *entry); - -char * xdp_name_from_id (guint32 doc_id); - - -G_END_DECLS - -#endif /* XDP_UTIL_H */ diff --git a/permission-store/Makefile.am.inc b/permission-store/Makefile.am.inc deleted file mode 100644 index 023aad31..00000000 --- a/permission-store/Makefile.am.inc +++ /dev/null @@ -1,37 +0,0 @@ -libexec_PROGRAMS += \ - xdg-permission-store \ - $(NULL) - -service_in_files += permission-store/xdg-permission-store.service.in -systemduserunit_DATA += permission-store/xdg-permission-store.service - -service_in_files += permission-store/org.freedesktop.impl.portal.PermissionStore.service.in -dbus_service_DATA += permission-store/org.freedesktop.impl.portal.PermissionStore.service - -nodist_xdg_permission_store_SOURCES = permission-store/permission-store-dbus.c permission-store/permission-store-dbus.h -BUILT_SOURCES += $(nodist_xdg_permission_store_SOURCES) -CLEANFILES += $(nodist_xdg_permission_store_SOURCES) - -permission-store/permission-store-dbus.c: data/org.freedesktop.impl.portal.PermissionStore.xml Makefile - mkdir -p $(builddir)/permission-store - $(AM_V_GEN) $(GDBUS_CODEGEN) \ - --interface-prefix org.freedesktop.impl.portal. \ - --c-namespace Xdg \ - --generate-c-code $(builddir)/permission-store/permission-store-dbus \ - $(srcdir)/data/org.freedesktop.impl.portal.PermissionStore.xml \ - $(NULL) - -permission-store/%-dbus.h: permission-store/%-dbus.c - @true # Built as a side-effect of the rules for the .c - -# also used by the document portal -ps_dbus_built_sources = $(nodist_xdg_permission_store_SOURCES) - -xdg_permission_store_SOURCES = \ - permission-store/permission-store.c \ - permission-store/xdg-permission-store.c \ - permission-store/xdg-permission-store.h \ - $(NULL) - -xdg_permission_store_LDADD = $(AM_LDADD) $(BASE_LIBS) libflatpak-common.la -xdg_permission_store_CFLAGS = $(AM_CFLAGS) $(BASE_CFLAGS) $(SOUP_CFLAGS) $(JSON_CFLAGS) $(OSTREE_CFLAGS) $(GSYSTEM_CFLAGS) -I$(srcdir)/permission-store -I$(builddir)/permission-store diff --git a/permission-store/org.freedesktop.impl.portal.PermissionStore.service.in b/permission-store/org.freedesktop.impl.portal.PermissionStore.service.in deleted file mode 100644 index cb444f1d..00000000 --- a/permission-store/org.freedesktop.impl.portal.PermissionStore.service.in +++ /dev/null @@ -1,4 +0,0 @@ -[D-BUS Service] -Name=org.freedesktop.impl.portal.PermissionStore -Exec=@libexecdir@/xdg-permission-store -SystemdService=xdg-permission-store.service diff --git a/permission-store/permission-store.c b/permission-store/permission-store.c deleted file mode 100644 index 30971af4..00000000 --- a/permission-store/permission-store.c +++ /dev/null @@ -1,146 +0,0 @@ -/* - * Copyright © 2014 Red Hat, Inc - * - * This program 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.1 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 <http://www.gnu.org/licenses/>. - * - * Authors: - * Alexander Larsson <alexl@redhat.com> - */ - -#include "config.h" - -#include <locale.h> -#include <stdlib.h> -#include <stdio.h> -#include <string.h> -#include <gio/gio.h> -#include "permission-store-dbus.h" -#include "xdg-permission-store.h" -#include "flatpak-utils.h" - -static void -on_bus_acquired (GDBusConnection *connection, - const gchar *name, - gpointer user_data) -{ - xdg_permission_store_start (connection); -} - -static void -on_name_acquired (GDBusConnection *connection, - const gchar *name, - gpointer user_data) -{ -} - -static void -on_name_lost (GDBusConnection *connection, - const gchar *name, - gpointer user_data) -{ - exit (1); -} - -static gboolean opt_verbose; -static gboolean opt_replace; -static gboolean opt_version; - -static GOptionEntry entries[] = { - { "verbose", 'v', 0, G_OPTION_ARG_NONE, &opt_verbose, "Print debug information", NULL }, - { "replace", 'r', 0, G_OPTION_ARG_NONE, &opt_replace, "Replace", NULL }, - { "version", 0, 0, G_OPTION_ARG_NONE, &opt_version, "Print version and exit", NULL }, - { NULL } -}; - -static void -message_handler (const gchar *log_domain, - GLogLevelFlags log_level, - const gchar *message, - gpointer user_data) -{ - /* Make this look like normal console output */ - if (log_level & G_LOG_LEVEL_DEBUG) - printf ("XDP: %s\n", message); - else - printf ("%s: %s\n", g_get_prgname (), message); -} - -static void -printerr_handler (const gchar *string) -{ - const char *prefix = ""; - const char *suffix = ""; - if (flatpak_fancy_output ()) - { - prefix = FLATPAK_ANSI_RED FLATPAK_ANSI_BOLD_ON; - suffix = FLATPAK_ANSI_BOLD_OFF FLATPAK_ANSI_COLOR_RESET; - } - fprintf (stderr, "%serror: %s%s\n", prefix, suffix, string); -} - -int -main (int argc, - char **argv) -{ - guint owner_id; - GMainLoop *loop; - GOptionContext *context; - g_autoptr(GError) error = NULL; - - setlocale (LC_ALL, ""); - - g_setenv ("GIO_USE_VFS", "local", TRUE); - - g_set_prgname (argv[0]); - - flatpak_migrate_from_xdg_app (); - - g_set_printerr_handler (printerr_handler); - - context = g_option_context_new ("- permission store"); - g_option_context_add_main_entries (context, entries, NULL); - if (!g_option_context_parse (context, &argc, &argv, &error)) - { - g_printerr ("Option parsing failed: %s", error->message); - return 1; - } - - if (opt_version) - { - g_print ("%s\n", PACKAGE_STRING); - exit (EXIT_SUCCESS); - } - - if (opt_verbose) - g_log_set_handler (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, message_handler, NULL); - - g_set_prgname (argv[0]); - - owner_id = g_bus_own_name (G_BUS_TYPE_SESSION, - "org.freedesktop.impl.portal.PermissionStore", - G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT | (opt_replace ? G_BUS_NAME_OWNER_FLAGS_REPLACE : 0), - - on_bus_acquired, - on_name_acquired, - on_name_lost, - NULL, - NULL); - - loop = g_main_loop_new (NULL, FALSE); - g_main_loop_run (loop); - - g_bus_unown_name (owner_id); - - return 0; -} diff --git a/permission-store/xdg-permission-store.c b/permission-store/xdg-permission-store.c deleted file mode 100644 index 499a35f1..00000000 --- a/permission-store/xdg-permission-store.c +++ /dev/null @@ -1,468 +0,0 @@ -/* - * Copyright © 2015 Red Hat, Inc - * - * This program 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.1 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 <http://www.gnu.org/licenses/>. - * - * Authors: - * Alexander Larsson <alexl@redhat.com> - */ - -#include "config.h" - -#include <locale.h> -#include <stdlib.h> -#include <string.h> -#include <gio/gio.h> -#include "permission-store/permission-store-dbus.h" -#include "xdg-permission-store.h" -#include "flatpak-db.h" -#include "flatpak-portal-error.h" - -GHashTable *tables = NULL; - -typedef struct -{ - char *name; - FlatpakDb *db; - GList *outstanding_writes; - GList *current_writes; - gboolean writing; -} Table; - -static void start_writeout (Table *table); - -static void -table_free (Table *table) -{ - g_free (table->name); - g_object_unref (table->db); - g_free (table); -} - -static Table * -lookup_table (const char *name, - GDBusMethodInvocation *invocation) -{ - Table *table; - FlatpakDb *db; - g_autofree char *dir = NULL; - g_autofree char *path = NULL; - - g_autoptr(GError) error = NULL; - - table = g_hash_table_lookup (tables, name); - if (table != NULL) - return table; - - dir = g_build_filename (g_get_user_data_dir (), "flatpak/db", NULL); - g_mkdir_with_parents (dir, 0755); - - path = g_build_filename (dir, name, NULL); - db = flatpak_db_new (path, FALSE, &error); - if (db == NULL) - { - g_dbus_method_invocation_return_error (invocation, - FLATPAK_PORTAL_ERROR, FLATPAK_PORTAL_ERROR_FAILED, - "Unable to load db file: %s", error->message); - return NULL; - } - - table = g_new0 (Table, 1); - table->name = g_strdup (name); - table->db = db; - - g_hash_table_insert (tables, table->name, table); - - return table; -} - -static void -writeout_done (GObject *source_object, - GAsyncResult *res, - gpointer user_data) -{ - Table *table = user_data; - GList *l; - - g_autoptr(GError) error = NULL; - gboolean ok; - - ok = flatpak_db_save_content_finish (table->db, res, &error); - - for (l = table->current_writes; l != NULL; l = l->next) - { - GDBusMethodInvocation *invocation = l->data; - - if (ok) - g_dbus_method_invocation_return_value (invocation, - g_variant_new ("()")); - else - g_dbus_method_invocation_return_error (invocation, - FLATPAK_PORTAL_ERROR, FLATPAK_PORTAL_ERROR_FAILED, - "Unable to write db: %s", error->message); - } - - g_list_free (table->current_writes); - table->current_writes = NULL; - table->writing = FALSE; - - if (table->outstanding_writes != NULL) - start_writeout (table); -} - -static void -start_writeout (Table *table) -{ - g_assert (table->current_writes == NULL); - table->current_writes = table->outstanding_writes; - table->outstanding_writes = NULL; - table->writing = TRUE; - - flatpak_db_update (table->db); - - flatpak_db_save_content_async (table->db, NULL, writeout_done, table); -} - -static void -ensure_writeout (Table *table, - GDBusMethodInvocation *invocation) -{ - table->outstanding_writes = g_list_prepend (table->outstanding_writes, invocation); - - if (!table->writing) - start_writeout (table); -} - -static gboolean -handle_list (XdgPermissionStore *object, - GDBusMethodInvocation *invocation, - const gchar *table_name) -{ - Table *table; - - g_auto(GStrv) ids = NULL; - - table = lookup_table (table_name, invocation); - if (table == NULL) - return TRUE; - - ids = flatpak_db_list_ids (table->db); - - xdg_permission_store_complete_list (object, invocation, (const char * const *) ids); - - return TRUE; -} - -static GVariant * -get_app_permissions (FlatpakDbEntry *entry) -{ - g_autofree const char **apps = NULL; - GVariantBuilder builder; - int i; - - apps = flatpak_db_entry_list_apps (entry); - g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sas}")); - - for (i = 0; apps[i] != NULL; i++) - { - g_autofree const char **permissions = flatpak_db_entry_list_permissions (entry, apps[i]); - g_variant_builder_add_value (&builder, - g_variant_new ("{s@as}", - apps[i], - g_variant_new_strv (permissions, -1))); - } - - return g_variant_ref_sink (g_variant_builder_end (&builder)); -} - -static gboolean -handle_lookup (XdgPermissionStore *object, - GDBusMethodInvocation *invocation, - const gchar *table_name, - const gchar *id) -{ - Table *table; - - g_autoptr(GVariant) data = NULL; - g_autoptr(GVariant) permissions = NULL; - g_autoptr(FlatpakDbEntry) entry = NULL; - - table = lookup_table (table_name, invocation); - if (table == NULL) - return TRUE; - - entry = flatpak_db_lookup (table->db, id); - if (entry == NULL) - { - g_dbus_method_invocation_return_error (invocation, - FLATPAK_PORTAL_ERROR, FLATPAK_PORTAL_ERROR_NOT_FOUND, - "No entry for %s", id); - return TRUE; - } - - data = flatpak_db_entry_get_data (entry); - permissions = get_app_permissions (entry); - - xdg_permission_store_complete_lookup (object, invocation, - permissions, - g_variant_new_variant (data)); - - return TRUE; -} - -static void -emit_deleted (XdgPermissionStore *object, - const gchar *table_name, - const gchar *id, - FlatpakDbEntry *entry) -{ - g_autoptr(GVariant) data = NULL; - g_autoptr(GVariant) permissions = NULL; - - data = flatpak_db_entry_get_data (entry); - permissions = g_variant_ref_sink (g_variant_new_array (G_VARIANT_TYPE ("{sas}"), NULL, 0)); - - xdg_permission_store_emit_changed (object, - table_name, id, - TRUE, - g_variant_new_variant (data), - permissions); -} - - -static void -emit_changed (XdgPermissionStore *object, - const gchar *table_name, - const gchar *id, - FlatpakDbEntry *entry) -{ - g_autoptr(GVariant) data = NULL; - g_autoptr(GVariant) permissions = NULL; - - data = flatpak_db_entry_get_data (entry); - permissions = get_app_permissions (entry); - - xdg_permission_store_emit_changed (object, - table_name, id, - FALSE, - g_variant_new_variant (data), - permissions); -} - -static gboolean -handle_delete (XdgPermissionStore *object, - GDBusMethodInvocation *invocation, - const gchar *table_name, - const gchar *id) -{ - Table *table; - - g_autoptr(FlatpakDbEntry) entry = NULL; - - table = lookup_table (table_name, invocation); - if (table == NULL) - return TRUE; - - entry = flatpak_db_lookup (table->db, id); - if (entry == NULL) - { - g_dbus_method_invocation_return_error (invocation, - FLATPAK_PORTAL_ERROR, FLATPAK_PORTAL_ERROR_NOT_FOUND, - "No entry for %s", id); - return TRUE; - } - - flatpak_db_set_entry (table->db, id, NULL); - emit_deleted (object, table_name, id, entry); - - ensure_writeout (table, invocation); - - return TRUE; -} - -static gboolean -handle_set (XdgPermissionStore *object, - GDBusMethodInvocation *invocation, - const gchar *table_name, - gboolean create, - const gchar *id, - GVariant *app_permissions, - GVariant *data) -{ - Table *table; - GVariantIter iter; - GVariant *child; - - g_autoptr(GVariant) data_child = NULL; - g_autoptr(FlatpakDbEntry) old_entry = NULL; - g_autoptr(FlatpakDbEntry) new_entry = NULL; - - table = lookup_table (table_name, invocation); - if (table == NULL) - return TRUE; - - old_entry = flatpak_db_lookup (table->db, id); - if (old_entry == NULL && !create) - { - g_dbus_method_invocation_return_error (invocation, - FLATPAK_PORTAL_ERROR, FLATPAK_PORTAL_ERROR_NOT_FOUND, - "Id %s not found", id); - return TRUE; - } - - data_child = g_variant_get_child_value (data, 0); - new_entry = flatpak_db_entry_new (data_child); - - /* Add all the given app permissions */ - - g_variant_iter_init (&iter, app_permissions); - while ((child = g_variant_iter_next_value (&iter))) - { - g_autoptr(FlatpakDbEntry) old_entry = NULL; - const char *child_app_id; - g_autofree const char **permissions; - - g_variant_get (child, "{&s^a&s}", &child_app_id, &permissions); - - old_entry = new_entry; - new_entry = flatpak_db_entry_set_app_permissions (new_entry, child_app_id, (const char **) permissions); - - g_variant_unref (child); - } - - flatpak_db_set_entry (table->db, id, new_entry); - emit_changed (object, table_name, id, new_entry); - - ensure_writeout (table, invocation); - - return TRUE; -} - -static gboolean -handle_set_permission (XdgPermissionStore *object, - GDBusMethodInvocation *invocation, - const gchar *table_name, - gboolean create, - const gchar *id, - const gchar *app, - const gchar *const *permissions) -{ - Table *table; - - g_autoptr(FlatpakDbEntry) entry = NULL; - g_autoptr(FlatpakDbEntry) new_entry = NULL; - - table = lookup_table (table_name, invocation); - if (table == NULL) - return TRUE; - - entry = flatpak_db_lookup (table->db, id); - if (entry == NULL) - { - if (create) - { - entry = flatpak_db_entry_new (NULL); - } - else - { - g_dbus_method_invocation_return_error (invocation, - FLATPAK_PORTAL_ERROR, FLATPAK_PORTAL_ERROR_NOT_FOUND, - "Id %s not found", id); - return TRUE; - } - } - - new_entry = flatpak_db_entry_set_app_permissions (entry, app, (const char **) permissions); - flatpak_db_set_entry (table->db, id, new_entry); - emit_changed (object, table_name, id, new_entry); - - ensure_writeout (table, invocation); - - return TRUE; -} - -static gboolean -handle_set_value (XdgPermissionStore *object, - GDBusMethodInvocation *invocation, - const gchar *table_name, - gboolean create, - const gchar *id, - GVariant *data) -{ - Table *table; - - g_autoptr(FlatpakDbEntry) entry = NULL; - g_autoptr(FlatpakDbEntry) new_entry = NULL; - - table = lookup_table (table_name, invocation); - if (table == NULL) - return TRUE; - - entry = flatpak_db_lookup (table->db, id); - if (entry == NULL) - { - if (create) - { - new_entry = flatpak_db_entry_new (data); - } - else - { - g_dbus_method_invocation_return_error (invocation, - FLATPAK_PORTAL_ERROR, FLATPAK_PORTAL_ERROR_NOT_FOUND, - "Id %s not found", id); - return TRUE; - } - } - else - { - new_entry = flatpak_db_entry_modify_data (entry, data); - } - - flatpak_db_set_entry (table->db, id, new_entry); - emit_changed (object, table_name, id, new_entry); - - ensure_writeout (table, invocation); - - return TRUE; -} - -void -xdg_permission_store_start (GDBusConnection *connection) -{ - XdgPermissionStore *store; - GError *error = NULL; - - tables = g_hash_table_new_full (g_str_hash, g_str_equal, - g_free, (GDestroyNotify) table_free); - - store = xdg_permission_store_skeleton_new (); - - xdg_permission_store_set_version (XDG_PERMISSION_STORE (store), 1); - - g_signal_connect (store, "handle-list", G_CALLBACK (handle_list), NULL); - g_signal_connect (store, "handle-lookup", G_CALLBACK (handle_lookup), NULL); - g_signal_connect (store, "handle-set", G_CALLBACK (handle_set), NULL); - g_signal_connect (store, "handle-set-permission", G_CALLBACK (handle_set_permission), NULL); - g_signal_connect (store, "handle-set-value", G_CALLBACK (handle_set_value), NULL); - g_signal_connect (store, "handle-delete", G_CALLBACK (handle_delete), NULL); - - if (!g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (store), - connection, - "/org/freedesktop/impl/portal/PermissionStore", - &error)) - { - g_warning ("error: %s", error->message); - g_error_free (error); - } -} diff --git a/permission-store/xdg-permission-store.h b/permission-store/xdg-permission-store.h deleted file mode 100644 index 55dcc5c2..00000000 --- a/permission-store/xdg-permission-store.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright © 2015 Red Hat, Inc - * - * This program 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.1 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 <http://www.gnu.org/licenses/>. - * - * Authors: - * Alexander Larsson <alexl@redhat.com> - */ - -#ifndef __FLATPAK_PERMISSION_STORE_H__ -#define __FLATPAK_PERMISSION_STORE_H__ - -#include "flatpak-dbus.h" - -void xdg_permission_store_start (GDBusConnection *connection); - -#endif /* __FLATPAK_PERMISSION_STORE_H__ */ diff --git a/permission-store/xdg-permission-store.service.in b/permission-store/xdg-permission-store.service.in deleted file mode 100644 index 66854306..00000000 --- a/permission-store/xdg-permission-store.service.in +++ /dev/null @@ -1,7 +0,0 @@ -[Unit] -Description=sandboxed app permission store - -[Service] -BusName=org.freedesktop.impl.portal.PermissionStore -ExecStart=@libexecdir@/xdg-permission-store -Type=dbus diff --git a/tests/Makefile.am.inc b/tests/Makefile.am.inc index 2aef5d5a..e49e9ec8 100644 --- a/tests/Makefile.am.inc +++ b/tests/Makefile.am.inc @@ -12,27 +12,6 @@ else AM_TESTS_ENVIRONMENT += FLATPAK_BWRAP=$$(cd $(top_builddir) && pwd)/flatpak-bwrap endif -testdb_CFLAGS = $(AM_CFLAGS) $(BASE_CFLAGS) -testdb_LDADD = \ - $(AM_LDADD) \ - $(BASE_LIBS) \ - $(OSTREE_LIBS) \ - libglnx.la \ - libflatpak-common.la \ - $(NULL) -testdb_SOURCES = tests/testdb.c - -test_doc_portal_CFLAGS = $(AM_CFLAGS) $(BASE_CFLAGS) -test_doc_portal_LDADD = \ - $(AM_LDADD) \ - $(BASE_LIBS) \ - $(OSTREE_LIBS) \ - libglnx.la \ - libflatpak-common.la \ - $(NULL) -test_doc_portal_SOURCES = tests/test-doc-portal.c -nodist_test_doc_portal_SOURCES = $(xdp_dbus_built_sources) - testlibrary_CFLAGS = $(AM_CFLAGS) $(BASE_CFLAGS) testlibrary_LDADD = \ $(AM_LDADD) \ @@ -42,16 +21,6 @@ testlibrary_LDADD = \ $(NULL) testlibrary_SOURCES = tests/testlibrary.c -EXTRA_test_doc_portal_DEPENDENCIES = tests/services/org.freedesktop.impl.portal.PermissionStore.service tests/services/org.freedesktop.portal.Documents.service tests/services/org.freedesktop.Flatpak.service tests/services/org.freedesktop.Flatpak.SystemHelper.service - -tests/services/org.freedesktop.portal.Documents.service: document-portal/org.freedesktop.portal.Documents.service.in - mkdir -p tests/services - $(AM_V_GEN) $(SED) -e "s|\@libexecdir\@|$(abs_top_builddir)|" $< > $@ - -tests/services/org.freedesktop.impl.portal.PermissionStore.service: permission-store/org.freedesktop.impl.portal.PermissionStore.service.in - mkdir -p tests/services - $(AM_V_GEN) $(SED) -e "s|\@libexecdir\@|$(abs_top_builddir)|" $< > $@ - tests/services/org.freedesktop.Flatpak.service: session-helper/org.freedesktop.Flatpak.service.in mkdir -p tests/services $(AM_V_GEN) $(SED) -e "s|\@libexecdir\@|$(abs_top_builddir)|" $< > $@ @@ -60,14 +29,12 @@ tests/services/org.freedesktop.Flatpak.SystemHelper.service: system-helper/org.f mkdir -p tests/services $(AM_V_GEN) $(SED) -e "s|\@libexecdir\@|$(abs_top_builddir)|" -e "s|\@extraargs\@| --session --no-idle-exit|" $< > $@ -tests/libtest.sh: tests/services/org.freedesktop.impl.portal.PermissionStore.service tests/services/org.freedesktop.portal.Documents.service tests/services/org.freedesktop.Flatpak.service +tests/libtest.sh: tests/services/org.freedesktop.Flatpak.service tests/services/org.freedesktop.Flatpak.SystemHelper.service install-test-data-hook: if ENABLE_INSTALLED_TESTS mkdir -p $(DESTDIR)$(installed_testdir)/services ln -sf $(dbus_servicedir)/org.freedesktop.Flatpak.service $(DESTDIR)$(installed_testdir)/services/ - ln -sf $(dbus_servicedir)/org.freedesktop.portal.Documents.service $(DESTDIR)$(installed_testdir)/services/ - ln -sf $(dbus_servicedir)/org.freedesktop.impl.portal.PermissionStore.service $(DESTDIR)$(installed_testdir)/services/ $(AM_V_GEN) $(SED) -e "s|\@libexecdir\@|$(libexecdir)|" -e "s|\@extraargs\@| --session --no-idle-exit|" $(top_srcdir)/system-helper/org.freedesktop.Flatpak.SystemHelper.service.in > $(DESTDIR)$(installed_testdir)/services/org.freedesktop.Flatpak.SystemHelper.service endif @@ -93,7 +60,6 @@ dist_installed_test_data = \ installed_test_keyringdir = $(installed_testdir)/test-keyring installed_test_keyring2dir = $(installed_testdir)/test-keyring2 -installed_test_dbsdir = $(installed_testdir)/dbs if ENABLE_INSTALLED_TESTS dist_installed_test_keyring_DATA = \ @@ -106,7 +72,6 @@ dist_installed_test_keyring2_DATA = \ tests/test-keyring2/pubring.gpg \ tests/test-keyring2/secring.gpg \ $(NULL) -dist_installed_test_dbs_DATA = tests/dbs/no_tables endif dist_test_scripts = \ @@ -127,15 +92,13 @@ dist_test_scripts = \ tests/test-update-remote-configuration.sh \ $(NULL) -test_programs = testdb test-doc-portal testlibrary +test_programs = testlibrary @VALGRIND_CHECK_RULES@ VALGRIND_SUPPRESSIONS_FILES=tests/flatpak.supp tests/glib.supp EXTRA_DIST += tests/flatpak.supp tests/glib.supp DISTCLEANFILES += \ tests/services/org.freedesktop.Flatpak.service \ - tests/services/org.freedesktop.portal.Documents.service \ - tests/services/org.freedesktop.impl.portal.PermissionStore.service \ tests/services/org.freedesktop.Flatpak.SystemHelper.service \ tests/package_version.txt \ $(NULL) diff --git a/tests/dbs/no_tables b/tests/dbs/no_tables Binary files differdeleted file mode 100644 index c700bdb1..00000000 --- a/tests/dbs/no_tables +++ /dev/null diff --git a/tests/test-doc-portal.c b/tests/test-doc-portal.c deleted file mode 100644 index ff08316f..00000000 --- a/tests/test-doc-portal.c +++ /dev/null @@ -1,505 +0,0 @@ -#include "config.h" - -#include <locale.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> -#include <errno.h> - -#include "libglnx/libglnx.h" - -#include <gio/gio.h> -#include <gio/gunixfdlist.h> - -#include "document-portal/xdp-dbus.h" - -#include "flatpak-dbus.h" - -char outdir[] = "/tmp/xdp-test-XXXXXX"; - -GTestDBus *dbus; -GDBusConnection *session_bus; -XdpDbusDocuments *documents; -char *mountpoint; -static gboolean have_fuse; - -static char * -make_doc_dir (const char *id, const char *app) -{ - if (app) - return g_build_filename (mountpoint, "by-app", app, id, NULL); - else - return g_build_filename (mountpoint, id, NULL); -} - -static char * -make_doc_path (const char *id, const char *basename, const char *app) -{ - g_autofree char *dir = make_doc_dir (id, app); - - return g_build_filename (dir, basename, NULL); -} - -static void -assert_host_has_contents (const char *basename, const char *expected_contents) -{ - g_autofree char *path = g_build_filename (outdir, basename, NULL); - g_autofree char *real_contents = NULL; - gsize real_contents_length; - GError *error = NULL; - - g_file_get_contents (path, &real_contents, &real_contents_length, &error); - g_assert_no_error (error); - g_assert_cmpstr (real_contents, ==, expected_contents); - g_assert_cmpuint (real_contents_length, ==, strlen (expected_contents)); -} - -static void -assert_doc_has_contents (const char *id, const char *basename, const char *app, const char *expected_contents) -{ - g_autofree char *path = make_doc_path (id, basename, app); - g_autofree char *real_contents = NULL; - gsize real_contents_length; - GError *error = NULL; - - g_file_get_contents (path, &real_contents, &real_contents_length, &error); - g_assert_no_error (error); - g_assert_cmpstr (real_contents, ==, expected_contents); - g_assert_cmpuint (real_contents_length, ==, strlen (expected_contents)); -} - -static void -assert_doc_not_exist (const char *id, const char *basename, const char *app) -{ - g_autofree char *path = make_doc_path (id, basename, app); - struct stat buf; - int res, fd; - - res = stat (path, &buf); - g_assert_cmpint (res, ==, -1); - g_assert_cmpint (errno, ==, ENOENT); - - fd = open (path, O_RDONLY); - g_assert_cmpint (fd, ==, -1); - g_assert_cmpint (errno, ==, ENOENT); -} - -static char * -export_file (const char *path, gboolean unique) -{ - int fd, fd_id; - GUnixFDList *fd_list = NULL; - - g_autoptr(GVariant) reply = NULL; - GError *error = NULL; - char *doc_id; - - fd = open (path, O_PATH | O_CLOEXEC); - g_assert (fd >= 0); - - fd_list = g_unix_fd_list_new (); - fd_id = g_unix_fd_list_append (fd_list, fd, &error); - g_assert_no_error (error); - close (fd); - - reply = g_dbus_connection_call_with_unix_fd_list_sync (session_bus, - "org.freedesktop.portal.Documents", - "/org/freedesktop/portal/documents", - "org.freedesktop.portal.Documents", - "Add", - g_variant_new ("(hbb)", fd_id, !unique, FALSE), - G_VARIANT_TYPE ("(s)"), - G_DBUS_CALL_FLAGS_NONE, - 30000, - fd_list, NULL, - NULL, - &error); - g_object_unref (fd_list); - g_assert_no_error (error); - g_assert (reply != NULL); - - g_variant_get (reply, "(s)", &doc_id); - g_assert (doc_id != NULL); - return doc_id; -} - -static char * -export_new_file (const char *basename, const char *contents, gboolean unique) -{ - g_autofree char *path = NULL; - GError *error = NULL; - - path = g_build_filename (outdir, basename, NULL); - - g_file_set_contents (path, contents, -1, &error); - g_assert_no_error (error); - - return export_file (path, unique); -} - -static gboolean -update_doc (const char *id, const char *basename, const char *app, const char *contents, GError **error) -{ - g_autofree char *path = make_doc_path (id, basename, app); - - return g_file_set_contents (path, contents, -1, error); -} - -static gboolean -update_from_host (const char *basename, const char *contents, GError **error) -{ - g_autofree char *path = g_build_filename (outdir, basename, NULL); - - return g_file_set_contents (path, contents, -1, error); -} - - -static void -grant_permissions (const char *id, const char *app, gboolean write) -{ - g_autoptr(GPtrArray) permissions = g_ptr_array_new (); - GError *error = NULL; - - g_ptr_array_add (permissions, "read"); - if (write) - g_ptr_array_add (permissions, "write"); - g_ptr_array_add (permissions, NULL); - - xdp_dbus_documents_call_grant_permissions_sync (documents, - id, - app, - (const char **) permissions->pdata, - NULL, - &error); - g_assert_no_error (error); -} - -static void -test_create_doc (void) -{ - g_autofree char *doc_path = NULL; - g_autofree char *doc_app_path = NULL; - g_autofree char *host_path = NULL; - g_autofree char *id = NULL; - g_autofree char *id2 = NULL; - g_autofree char *id3 = NULL; - g_autofree char *id4 = NULL; - g_autofree char *id5 = NULL; - const char *basename = "a-file"; - GError *error = NULL; - - if (!have_fuse) - { - g_test_skip ("this test requires FUSE"); - return; - } - - /* Export a document */ - id = export_new_file (basename, "content", FALSE); - - /* Ensure its there and not viewable by apps */ - assert_doc_has_contents (id, basename, NULL, "content"); - assert_host_has_contents (basename, "content"); - assert_doc_not_exist (id, basename, "com.test.App1"); - assert_doc_not_exist (id, basename, "com.test.App2"); - assert_doc_not_exist (id, "another-file", NULL); - assert_doc_not_exist ("anotherid", basename, NULL); - - /* Create a tmp file in same dir, ensure it works and can't be seen by other apps */ - assert_doc_not_exist (id, "tmp1", NULL); - update_doc (id, "tmp1", NULL, "tmpdata1", &error); - g_assert_no_error (error); - assert_doc_has_contents (id, "tmp1", NULL, "tmpdata1"); - assert_doc_not_exist (id, "tmp1", "com.test.App1"); - - /* Let App 1 see the document (but not write) */ - grant_permissions (id, "com.test.App1", FALSE); - - /* Ensure App 1 and only it can see the document and tmpfile */ - assert_doc_has_contents (id, basename, "com.test.App1", "content"); - assert_doc_not_exist (id, basename, "com.test.App2"); - assert_doc_not_exist (id, "tmp1", "com.test.App1"); - - /* Make sure App 1 can't create a tmpfile */ - assert_doc_not_exist (id, "tmp2", "com.test.App1"); - update_doc (id, "tmp2", "com.test.App1", "tmpdata2", &error); - g_assert_error (error, G_FILE_ERROR, G_FILE_ERROR_ACCES); - g_clear_error (&error); - assert_doc_not_exist (id, "tmp2", "com.test.App1"); - - /* Update the document contents, ensure this is propagater */ - update_doc (id, basename, NULL, "content2", &error); - g_assert_no_error (error); - assert_host_has_contents (basename, "content2"); - assert_doc_has_contents (id, basename, NULL, "content2"); - assert_doc_has_contents (id, basename, "com.test.App1", "content2"); - assert_doc_not_exist (id, basename, "com.test.App2"); - assert_doc_not_exist (id, "tmp1", "com.test.App2"); - - /* Update the document contents outside fuse fd, ensure this is propagater */ - update_from_host (basename, "content3", &error); - g_assert_no_error (error); - assert_host_has_contents (basename, "content3"); - assert_doc_has_contents (id, basename, NULL, "content3"); - assert_doc_has_contents (id, basename, "com.test.App1", "content3"); - assert_doc_not_exist (id, basename, "com.test.App2"); - assert_doc_not_exist (id, "tmp1", "com.test.App2"); - - /* Try to update the doc from an app that can't write to it */ - update_doc (id, basename, "com.test.App1", "content4", &error); - g_assert_error (error, G_FILE_ERROR, G_FILE_ERROR_ACCES); - g_clear_error (&error); - - /* Try to create a tmp file for an app that is not allowed */ - assert_doc_not_exist (id, "tmp2", "com.test.App1"); - update_doc (id, "tmp2", "com.test.App1", "tmpdata2", &error); - g_assert_error (error, G_FILE_ERROR, G_FILE_ERROR_ACCES); - g_clear_error (&error); - assert_doc_not_exist (id, "tmp2", "com.test.App1"); - assert_doc_not_exist (id, "tmp2", NULL); - - /* Grant write permissions to App1 */ - grant_permissions (id, "com.test.App1", TRUE); - - /* update the doc from an app with write access */ - update_doc (id, basename, "com.test.App1", "content5", &error); - g_assert_no_error (error); - assert_host_has_contents (basename, "content5"); - assert_doc_has_contents (id, basename, NULL, "content5"); - assert_doc_has_contents (id, basename, "com.test.App1", "content5"); - assert_doc_not_exist (id, basename, "com.test.App2"); - - /* Try to create a tmp file for an app */ - assert_doc_not_exist (id, "tmp3", "com.test.App1"); - update_doc (id, "tmp3", "com.test.App1", "tmpdata3", &error); - g_assert_no_error (error); - assert_doc_has_contents (id, "tmp3", "com.test.App1", "tmpdata3"); - assert_doc_not_exist (id, "tmp3", NULL); - - /* Re-Create a file from a fuse document file, in various ways */ - doc_path = make_doc_path (id, basename, NULL); - doc_app_path = make_doc_path (id, basename, "com.test.App1"); - host_path = g_build_filename (outdir, basename, NULL); - id2 = export_file (doc_path, FALSE); - g_assert_cmpstr (id, ==, id2); - id3 = export_file (doc_app_path, FALSE); - g_assert_cmpstr (id, ==, id3); - id4 = export_file (host_path, FALSE); - g_assert_cmpstr (id, ==, id4); - - /* Ensure we can make a unique document */ - id5 = export_file (host_path, TRUE); - g_assert_cmpstr (id, !=, id5); -} - -static void -test_recursive_doc (void) -{ - g_autofree char *id = NULL; - g_autofree char *id2 = NULL; - g_autofree char *id3 = NULL; - const char *basename = "recursive-file"; - g_autofree char *path = NULL; - g_autofree char *app_path = NULL; - - if (!have_fuse) - { - g_test_skip ("this test requires FUSE"); - return; - } - - id = export_new_file (basename, "recursive-content", FALSE); - - assert_doc_has_contents (id, basename, NULL, "recursive-content"); - - path = make_doc_path (id, basename, NULL); - g_print ("path: %s\n", path); - - id2 = export_file (path, FALSE); - - g_assert_cmpstr (id, ==, id2); - - grant_permissions (id, "com.test.App1", FALSE); - - app_path = make_doc_path (id, basename, "com.test.App1"); - - id3 = export_file (app_path, FALSE); - - g_assert_cmpstr (id, ==, id3); -} - -static void -test_create_docs (void) -{ - GError *error = NULL; - g_autofree char *path1 = NULL; - g_autofree char *path2 = NULL; - int fd1, fd2; - guint32 fd_ids[2]; - GUnixFDList *fd_list = NULL; - gboolean res; - char **out_doc_ids; - g_autoptr(GVariant) out_extra = NULL; - const char *permissions[] = { "read", NULL }; - const char *basenames[] = { "doc1", "doc2" }; - int i; - - if (!have_fuse) - { - g_test_skip ("this test requires FUSE"); - return; - } - - path1 = g_build_filename (outdir, basenames[0], NULL); - g_file_set_contents (path1, basenames[0], -1, &error); - g_assert_no_error (error); - - fd1 = open (path1, O_PATH | O_CLOEXEC); - g_assert (fd1 >= 0); - - path2 = g_build_filename (outdir, basenames[1], NULL); - g_file_set_contents (path2, basenames[1], -1, &error); - g_assert_no_error (error); - - fd2 = open (path2, O_PATH | O_CLOEXEC); - g_assert (fd2 >= 0); - - fd_list = g_unix_fd_list_new (); - fd_ids[0] = g_unix_fd_list_append (fd_list, fd1, &error); - g_assert_no_error (error); - close (fd1); - fd_ids[1] = g_unix_fd_list_append (fd_list, fd2, &error); - g_assert_no_error (error); - close (fd2); - - res = xdp_dbus_documents_call_add_full_sync (documents, - g_variant_new_fixed_array (G_VARIANT_TYPE_HANDLE, - fd_ids, 2, sizeof (guint32)), - 0, - "org.other.App", - permissions, - fd_list, - &out_doc_ids, - &out_extra, - NULL, - NULL, &error); - g_assert_no_error (error); - g_assert (res); - - g_assert (g_strv_length (out_doc_ids) == 2); - for (i = 0; i < 2; i++) - { - const char *id = out_doc_ids[i]; - - /* Ensure its there and not viewable by apps */ - assert_doc_has_contents (id, basenames[i], NULL, basenames[i]); - assert_host_has_contents (basenames[i], basenames[i]); - assert_doc_not_exist (id, basenames[i], "com.test.App1"); - assert_doc_not_exist (id, basenames[i], "com.test.App2"); - assert_doc_not_exist (id, "another-file", NULL); - assert_doc_not_exist ("anotherid", basenames[i], NULL); - - assert_doc_has_contents (id, basenames[i], "org.other.App", basenames[i]); - update_doc (id, basenames[i], "org.other.App", "tmpdata2", &error); - g_assert_error (error, G_FILE_ERROR, G_FILE_ERROR_ACCES); - g_clear_error (&error); - } - g_assert (g_variant_lookup_value (out_extra, "mountpoint", G_VARIANT_TYPE_VARIANT) == 0); -} - - -static void -global_setup (void) -{ - gboolean inited; - g_autofree gchar *fusermount = NULL; - GError *error = NULL; - g_autofree gchar *services = NULL; - - fusermount = g_find_program_in_path ("fusermount"); - /* cache result so subsequent tests can be marked as skipped */ - have_fuse = (access ("/dev/fuse", W_OK) == 0 && - fusermount != NULL && - g_file_test (fusermount, G_FILE_TEST_IS_EXECUTABLE)); - - if (!have_fuse) - return; - - g_mkdtemp (outdir); - g_print ("outdir: %s\n", outdir); - - g_setenv ("XDG_RUNTIME_DIR", outdir, TRUE); - g_setenv ("XDG_DATA_HOME", outdir, TRUE); - - dbus = g_test_dbus_new (G_TEST_DBUS_NONE); - services = g_test_build_filename (G_TEST_BUILT, "services", NULL); - g_test_dbus_add_service_dir (dbus, services); - g_test_dbus_up (dbus); - - /* g_test_dbus_up unsets this, so re-set */ - g_setenv ("XDG_RUNTIME_DIR", outdir, TRUE); - - session_bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error); - g_assert_no_error (error); - - documents = xdp_dbus_documents_proxy_new_sync (session_bus, 0, - "org.freedesktop.portal.Documents", - "/org/freedesktop/portal/documents", - NULL, &error); - g_assert_no_error (error); - g_assert (documents != NULL); - - inited = xdp_dbus_documents_call_get_mount_point_sync (documents, &mountpoint, - NULL, &error); - g_assert_no_error (error); - g_assert (inited); - g_assert (mountpoint != NULL); -} - -static void -global_teardown (void) -{ - GError *error = NULL; - - if (!have_fuse) - return; - - g_free (mountpoint); - - g_object_unref (documents); - - g_dbus_connection_close_sync (session_bus, NULL, &error); - g_assert_no_error (error); - - g_object_unref (session_bus); - - g_test_dbus_down (dbus); - - g_object_unref (dbus); - - /* We race on the unmount of the fuse fs, which causes the rm -rf to stop at the doc dir. - This makes the chance of completely removing the directory higher */ - sleep (1); - - glnx_shutil_rm_rf_at (-1, outdir, NULL, NULL); -} - -int -main (int argc, char **argv) -{ - int res; - - g_test_init (&argc, &argv, NULL); - - g_test_add_func ("/db/create_doc", test_create_doc); - g_test_add_func ("/db/recursive_doc", test_recursive_doc); - g_test_add_func ("/db/create_docs", test_create_docs); - - global_setup (); - - res = g_test_run (); - - global_teardown (); - - return res; -} diff --git a/tests/testdb.c b/tests/testdb.c deleted file mode 100644 index 92d2bd88..00000000 --- a/tests/testdb.c +++ /dev/null @@ -1,359 +0,0 @@ -#include "config.h" - -#include <glib.h> -#include <flatpak-db.h> - -/* -static void -dump_db (FlatpakDb *db) -{ - g_autofree char *s = flatpak_db_print (db); - g_printerr ("\n%s\n", s); -} -*/ - -static FlatpakDb * -create_test_db (gboolean serialized) -{ - FlatpakDb *db; - - g_autoptr(FlatpakDbEntry) entry1 = NULL; - g_autoptr(FlatpakDbEntry) entry2 = NULL; - g_autoptr(FlatpakDbEntry) entry3 = NULL; - g_autoptr(FlatpakDbEntry) entry4 = NULL; - g_autoptr(FlatpakDbEntry) entry5 = NULL; - g_autoptr(FlatpakDbEntry) entry6 = NULL; - g_autoptr(FlatpakDbEntry) entry7 = NULL; - GError *error = NULL; - const char *permissions1[] = { "read", "write", NULL }; - const char *permissions2[] = { "read", NULL }; - const char *permissions3[] = { "write", NULL }; - - db = flatpak_db_new (NULL, FALSE, &error); - g_assert_no_error (error); - g_assert (db != NULL); - - { - g_auto(GStrv) ids = flatpak_db_list_ids (db); - g_assert (ids != NULL); - g_assert (ids[0] == NULL); - } - - { - g_auto(GStrv) apps = flatpak_db_list_apps (db); - g_assert (apps != NULL); - g_assert (apps[0] == NULL); - } - - entry1 = flatpak_db_entry_new (g_variant_new_string ("foo-data")); - entry2 = flatpak_db_entry_set_app_permissions (entry1, "org.test.bapp", permissions2); - entry3 = flatpak_db_entry_set_app_permissions (entry2, "org.test.app", permissions1); - entry4 = flatpak_db_entry_set_app_permissions (entry3, "org.test.capp", permissions1); - - flatpak_db_set_entry (db, "foo", entry4); - - entry5 = flatpak_db_entry_new (g_variant_new_string ("bar-data")); - entry6 = flatpak_db_entry_set_app_permissions (entry5, "org.test.app", permissions2); - entry7 = flatpak_db_entry_set_app_permissions (entry6, "org.test.dapp", permissions3); - - flatpak_db_set_entry (db, "bar", entry7); - - if (serialized) - flatpak_db_update (db); - - return db; -} - -static void -verify_test_db (FlatpakDb *db) -{ - g_auto(GStrv) ids; - g_autofree const char **apps1 = NULL; - g_autofree const char **apps2 = NULL; - g_auto(GStrv) all_apps = NULL; - - ids = flatpak_db_list_ids (db); - g_assert (g_strv_length (ids) == 2); - g_assert (g_strv_contains ((const char **) ids, "foo")); - g_assert (g_strv_contains ((const char **) ids, "bar")); - - { - g_autoptr(FlatpakDbEntry) entry = NULL; - g_autofree const char **permissions1 = NULL; - g_autofree const char **permissions2 = NULL; - g_autofree const char **permissions3 = NULL; - g_autofree const char **permissions4 = NULL; - g_autoptr(GVariant) data1 = NULL; - - entry = flatpak_db_lookup (db, "foo"); - g_assert (entry != NULL); - data1 = flatpak_db_entry_get_data (entry); - g_assert (data1 != NULL); - g_assert_cmpstr (g_variant_get_type_string (data1), ==, "s"); - g_assert_cmpstr (g_variant_get_string (data1, NULL), ==, "foo-data"); - apps1 = flatpak_db_entry_list_apps (entry); - g_assert (g_strv_length ((char **) apps1) == 3); - g_assert (g_strv_contains (apps1, "org.test.app")); - g_assert (g_strv_contains (apps1, "org.test.bapp")); - g_assert (g_strv_contains (apps1, "org.test.capp")); - permissions1 = flatpak_db_entry_list_permissions (entry, "org.test.app"); - g_assert (g_strv_length ((char **) permissions1) == 2); - g_assert (g_strv_contains (permissions1, "read")); - g_assert (g_strv_contains (permissions1, "write")); - permissions2 = flatpak_db_entry_list_permissions (entry, "org.test.bapp"); - g_assert (g_strv_length ((char **) permissions2) == 1); - g_assert (g_strv_contains (permissions2, "read")); - permissions3 = flatpak_db_entry_list_permissions (entry, "org.test.capp"); - g_assert (g_strv_length ((char **) permissions3) == 2); - g_assert (g_strv_contains (permissions3, "read")); - g_assert (g_strv_contains (permissions3, "write")); - permissions4 = flatpak_db_entry_list_permissions (entry, "org.test.noapp"); - g_assert (permissions4 != NULL); - g_assert (g_strv_length ((char **) permissions4) == 0); - } - - { - g_autoptr(FlatpakDbEntry) entry = NULL; - g_autofree const char **permissions5 = NULL; - g_autofree const char **permissions6 = NULL; - g_autoptr(GVariant) data2 = NULL; - - entry = flatpak_db_lookup (db, "bar"); - g_assert (entry != NULL); - data2 = flatpak_db_entry_get_data (entry); - g_assert (data2 != NULL); - g_assert_cmpstr (g_variant_get_type_string (data2), ==, "s"); - g_assert_cmpstr (g_variant_get_string (data2, NULL), ==, "bar-data"); - apps2 = flatpak_db_entry_list_apps (entry); - g_assert (g_strv_length ((char **) apps2) == 2); - g_assert (g_strv_contains (apps2, "org.test.app")); - g_assert (g_strv_contains (apps2, "org.test.dapp")); - permissions5 = flatpak_db_entry_list_permissions (entry, "org.test.app"); - g_assert (g_strv_length ((char **) permissions5) == 1); - g_assert (g_strv_contains (permissions5, "read")); - permissions6 = flatpak_db_entry_list_permissions (entry, "org.test.dapp"); - g_assert (g_strv_length ((char **) permissions6) == 1); - g_assert (g_strv_contains (permissions6, "write")); - } - - { - g_autoptr(FlatpakDbEntry) entry = NULL; - entry = flatpak_db_lookup (db, "gazonk"); - g_assert (entry == NULL); - } - - all_apps = flatpak_db_list_apps (db); - g_assert (g_strv_length (all_apps) == 4); - g_assert (g_strv_contains ((const char **) all_apps, "org.test.app")); - g_assert (g_strv_contains ((const char **) all_apps, "org.test.bapp")); - g_assert (g_strv_contains ((const char **) all_apps, "org.test.capp")); - g_assert (g_strv_contains ((const char **) all_apps, "org.test.dapp")); -} - -static void -test_db_open (void) -{ - GError *error = NULL; - FlatpakDb *db; - - db = flatpak_db_new (g_test_get_filename (G_TEST_DIST, "dbs", "does_not_exist", NULL), TRUE, &error); - g_assert_error (error, G_FILE_ERROR, G_FILE_ERROR_NOENT); - g_assert (db == NULL); - g_clear_error (&error); - - db = flatpak_db_new (g_test_get_filename (G_TEST_DIST, "dbs", "does_not_exist", NULL), FALSE, &error); - g_assert_no_error (error); - g_assert (db != NULL); - g_clear_error (&error); - g_object_unref (db); - - db = flatpak_db_new (g_test_get_filename (G_TEST_DIST, "dbs", "no_tables", NULL), TRUE, &error); - g_assert_error (error, G_FILE_ERROR, G_FILE_ERROR_INVAL); - g_assert (db == NULL); - g_clear_error (&error); -} - -static void -test_serialize (void) -{ - g_autoptr(FlatpakDb) db = NULL; - g_autoptr(FlatpakDb) db2 = NULL; - g_autofree char *dump1 = NULL; - g_autofree char *dump2 = NULL; - g_autofree char *dump3 = NULL; - GError *error = NULL; - char tmpfile[] = "/tmp/testdbXXXXXX"; - int fd; - - db = create_test_db (FALSE); - - verify_test_db (db); - - dump1 = flatpak_db_print (db); - - g_assert (flatpak_db_is_dirty (db)); - - flatpak_db_update (db); - - verify_test_db (db); - - g_assert (!flatpak_db_is_dirty (db)); - - dump2 = flatpak_db_print (db); - - g_assert_cmpstr (dump1, ==, dump2); - - fd = g_mkstemp (tmpfile); - close (fd); - - flatpak_db_set_path (db, tmpfile); - - flatpak_db_save_content (db, &error); - g_assert_no_error (error); - - db2 = flatpak_db_new (tmpfile, TRUE, &error); - g_assert_no_error (error); - g_assert (db2 != NULL); - - dump3 = flatpak_db_print (db2); - - g_assert_cmpstr (dump1, ==, dump3); - - unlink (tmpfile); -} - -static void -test_modify (void) -{ - g_autoptr(FlatpakDb) db = NULL; - const char *permissions[] = { "read", "write", "execute", NULL }; - const char *no_permissions[] = { NULL }; - - db = create_test_db (FALSE); - - /* Add permission */ - { - g_autoptr(FlatpakDbEntry) entry1 = NULL; - g_autoptr(FlatpakDbEntry) entry2 = NULL; - - entry1 = flatpak_db_lookup (db, "foo"); - entry2 = flatpak_db_entry_set_app_permissions (entry1, "org.test.app", permissions); - flatpak_db_set_entry (db, "foo", entry2); - } - - /* Add entry */ - { - g_autoptr(FlatpakDbEntry) entry1 = NULL; - g_autoptr(FlatpakDbEntry) entry2 = NULL; - - entry1 = flatpak_db_entry_new (g_variant_new_string ("gazonk-data")); - entry2 = flatpak_db_entry_set_app_permissions (entry1, "org.test.eapp", permissions); - flatpak_db_set_entry (db, "gazonk", entry2); - } - - /* Remove permission */ - { - g_autoptr(FlatpakDbEntry) entry1 = NULL; - g_autoptr(FlatpakDbEntry) entry2 = NULL; - - entry1 = flatpak_db_lookup (db, "bar"); - entry2 = flatpak_db_entry_set_app_permissions (entry1, "org.test.dapp", no_permissions); - flatpak_db_set_entry (db, "bar", entry2); - } - - /* Verify */ - { - g_autoptr(FlatpakDbEntry) entry5 = NULL; - g_autoptr(FlatpakDbEntry) entry6 = NULL; - g_autoptr(FlatpakDbEntry) entry7 = NULL; - g_autofree const char **apps2 = NULL; - g_auto(GStrv) apps3 = NULL; - g_autofree const char **permissions1 = NULL; - g_autofree const char **permissions2 = NULL; - g_autofree const char **permissions3 = NULL; - - entry5 = flatpak_db_lookup (db, "foo"); - permissions1 = flatpak_db_entry_list_permissions (entry5, "org.test.app"); - g_assert (g_strv_length ((char **) permissions1) == 3); - g_assert (g_strv_contains (permissions1, "read")); - g_assert (g_strv_contains (permissions1, "write")); - g_assert (g_strv_contains (permissions1, "execute")); - - entry6 = flatpak_db_lookup (db, "bar"); - permissions2 = flatpak_db_entry_list_permissions (entry6, "org.test.dapp"); - g_assert (g_strv_length ((char **) permissions2) == 0); - - entry7 = flatpak_db_lookup (db, "gazonk"); - permissions3 = flatpak_db_entry_list_permissions (entry7, "org.test.eapp"); - g_assert (g_strv_length ((char **) permissions3) == 3); - g_assert (g_strv_contains (permissions3, "read")); - g_assert (g_strv_contains (permissions3, "write")); - g_assert (g_strv_contains (permissions3, "execute")); - - apps2 = flatpak_db_entry_list_apps (entry6); - g_assert_cmpint (g_strv_length ((char **) apps2), ==, 1); - g_assert (g_strv_contains (apps2, "org.test.app")); - - apps3 = flatpak_db_list_apps (db); - g_assert_cmpint (g_strv_length (apps3), ==, 4); - g_assert (g_strv_contains ((const char **) apps3, "org.test.app")); - g_assert (g_strv_contains ((const char **) apps3, "org.test.bapp")); - g_assert (g_strv_contains ((const char **) apps3, "org.test.capp")); - g_assert (g_strv_contains ((const char **) apps3, "org.test.eapp")); - } - - flatpak_db_update (db); - - /* Verify after serialize */ - { - g_autoptr(FlatpakDbEntry) entry5 = NULL; - g_autoptr(FlatpakDbEntry) entry6 = NULL; - g_autoptr(FlatpakDbEntry) entry7 = NULL; - g_autofree const char **apps2 = NULL; - g_auto(GStrv) apps3 = NULL; - g_autofree const char **permissions1 = NULL; - g_autofree const char **permissions2 = NULL; - g_autofree const char **permissions3 = NULL; - - entry5 = flatpak_db_lookup (db, "foo"); - permissions1 = flatpak_db_entry_list_permissions (entry5, "org.test.app"); - g_assert (g_strv_length ((char **) permissions1) == 3); - g_assert (g_strv_contains (permissions1, "read")); - g_assert (g_strv_contains (permissions1, "write")); - g_assert (g_strv_contains (permissions1, "execute")); - - entry6 = flatpak_db_lookup (db, "bar"); - permissions2 = flatpak_db_entry_list_permissions (entry6, "org.test.dapp"); - g_assert (g_strv_length ((char **) permissions2) == 0); - - entry7 = flatpak_db_lookup (db, "gazonk"); - permissions3 = flatpak_db_entry_list_permissions (entry7, "org.test.eapp"); - g_assert (g_strv_length ((char **) permissions3) == 3); - g_assert (g_strv_contains (permissions3, "read")); - g_assert (g_strv_contains (permissions3, "write")); - g_assert (g_strv_contains (permissions3, "execute")); - - apps2 = flatpak_db_entry_list_apps (entry6); - g_assert_cmpint (g_strv_length ((char **) apps2), ==, 1); - g_assert (g_strv_contains (apps2, "org.test.app")); - - apps3 = flatpak_db_list_apps (db); - g_assert_cmpint (g_strv_length (apps3), ==, 4); - g_assert (g_strv_contains ((const char **) apps3, "org.test.app")); - g_assert (g_strv_contains ((const char **) apps3, "org.test.bapp")); - g_assert (g_strv_contains ((const char **) apps3, "org.test.capp")); - g_assert (g_strv_contains ((const char **) apps3, "org.test.eapp")); - } -} - -int -main (int argc, char **argv) -{ - g_test_init (&argc, &argv, NULL); - - g_test_add_func ("/db/open", test_db_open); - g_test_add_func ("/db/serialize", test_serialize); - g_test_add_func ("/db/modify", test_modify); - - return g_test_run (); -} |