From f2a6c1db8d2b3f82a93bfe718a1a9190d5d88e37 Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Fri, 9 Feb 2018 10:13:37 +0100 Subject: [PATCH] Remove document portal This is now in xdg-desktop-portal. We keep a version of the document portal dbus XML so that we avoid weird build dependencies. Flatpak itself is technically not dependent on the document portal, but it is very much recommended that you use it. Closes: #1398 Approved by: alexlarsson --- .gitignore | 6 - Makefile.am | 2 - app/flatpak-builtins-document-export.c | 2 +- app/flatpak-builtins-document-info.c | 2 +- app/flatpak-builtins-document-list.c | 2 +- app/flatpak-builtins-document-unexport.c | 2 +- common/Makefile.am.inc | 20 +- common/flatpak-context.c | 1 - common/flatpak-db.c | 1224 --------- common/flatpak-db.h | 103 - common/flatpak-exports.c | 1 - common/flatpak-run.c | 2 +- common/flatpak-utils.h | 2 +- common/gvdb/.gitignore | 2 - common/gvdb/README | 7 - common/gvdb/gvdb-builder.c | 537 ---- common/gvdb/gvdb-builder.h | 60 - common/gvdb/gvdb-format.h | 85 - common/gvdb/gvdb-reader.c | 718 ----- common/gvdb/gvdb-reader.h | 63 - common/gvdb/gvdb.doap | 32 - data/Makefile.am.inc | 3 - ...reedesktop.impl.portal.PermissionStore.xml | 163 -- document-portal/Makefile.am.inc | 43 - ...rg.freedesktop.portal.Documents.service.in | 4 - .../xdg-document-portal.service.in | 7 - document-portal/xdp-enums.h | 25 - document-portal/xdp-fuse.c | 2369 ----------------- document-portal/xdp-fuse.h | 23 - document-portal/xdp-main.c | 1558 ----------- document-portal/xdp-util.c | 129 - document-portal/xdp-util.h | 33 - permission-store/Makefile.am.inc | 37 - ...top.impl.portal.PermissionStore.service.in | 4 - permission-store/permission-store.c | 146 - permission-store/xdg-permission-store.c | 468 ---- permission-store/xdg-permission-store.h | 28 - .../xdg-permission-store.service.in | 7 - tests/Makefile.am.inc | 41 +- tests/dbs/no_tables | Bin 32 -> 0 bytes tests/test-doc-portal.c | 505 ---- tests/testdb.c | 359 --- 42 files changed, 19 insertions(+), 8806 deletions(-) delete mode 100644 common/flatpak-db.c delete mode 100644 common/flatpak-db.h delete mode 100644 common/gvdb/.gitignore delete mode 100644 common/gvdb/README delete mode 100644 common/gvdb/gvdb-builder.c delete mode 100644 common/gvdb/gvdb-builder.h delete mode 100644 common/gvdb/gvdb-format.h delete mode 100644 common/gvdb/gvdb-reader.c delete mode 100644 common/gvdb/gvdb-reader.h delete mode 100644 common/gvdb/gvdb.doap delete mode 100644 data/org.freedesktop.impl.portal.PermissionStore.xml delete mode 100644 document-portal/Makefile.am.inc delete mode 100644 document-portal/org.freedesktop.portal.Documents.service.in delete mode 100644 document-portal/xdg-document-portal.service.in delete mode 100644 document-portal/xdp-enums.h delete mode 100644 document-portal/xdp-fuse.c delete mode 100644 document-portal/xdp-fuse.h delete mode 100644 document-portal/xdp-main.c delete mode 100644 document-portal/xdp-util.c delete mode 100644 document-portal/xdp-util.h delete mode 100644 permission-store/Makefile.am.inc delete mode 100644 permission-store/org.freedesktop.impl.portal.PermissionStore.service.in delete mode 100644 permission-store/permission-store.c delete mode 100644 permission-store/xdg-permission-store.c delete mode 100644 permission-store/xdg-permission-store.h delete mode 100644 permission-store/xdg-permission-store.service.in delete mode 100644 tests/dbs/no_tables delete mode 100644 tests/test-doc-portal.c delete mode 100644 tests/testdb.c diff --git a/.gitignore b/.gitignore index d14be5f2..dbe9d7ad 100644 --- a/.gitignore +++ b/.gitignore @@ -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 #include "libglnx/libglnx.h" -#include "document-portal/xdp-dbus.h" +#include "flatpak-document-dbus.h" #include 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 #include "libglnx/libglnx.h" -#include "document-portal/xdp-dbus.h" +#include "flatpak-document-dbus.h" #include 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 #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 #include "libglnx/libglnx.h" -#include "document-portal/xdp-dbus.h" +#include "flatpak-document-dbus.h" #include 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 . - * - * Authors: - * Alexander Larsson - */ - -#include "config.h" - -#include -#include -#include -#include -#include - -#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 . - * - * Authors: - * Alexander Larsson - */ - -#ifndef FLATPAK_DB_H -#define FLATPAK_DB_H - -#include - -#include "libglnx/libglnx.h" -#include - -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 #include #include "flatpak-dbus.h" +#include "flatpak-document-dbus.h" #include #include -#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 . - * - * Author: Ryan Lortie - */ - -#include "gvdb-builder.h" -#include "gvdb-format.h" - -#include -#include -#if !defined(G_OS_WIN32) || !defined(_MSC_VER) -#include -#endif -#include - - -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 . - * - * Author: Ryan Lortie - */ - -#ifndef __gvdb_builder_h__ -#define __gvdb_builder_h__ - -#include - -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 . - * - * Author: Ryan Lortie - */ - -#ifndef __gvdb_format_h__ -#define __gvdb_format_h__ - -#include - -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 . - * - * Author: Ryan Lortie - */ - -#include "gvdb-reader.h" -#include "gvdb-format.h" - -#include - -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 . - * - * Author: Ryan Lortie - */ - -#ifndef __gvdb_reader_h__ -#define __gvdb_reader_h__ - -#include - -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 @@ - - - - - gvdb - GVariant Database file - - 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). - - - - - Ryan Lortie - - ryanl - - - - 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 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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 - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#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 -#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 -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#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 - -#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 -#include -#include -#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 -#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 . - * - * Authors: - * Alexander Larsson - */ - -#include "config.h" - -#include -#include -#include -#include -#include -#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 . - * - * Authors: - * Alexander Larsson - */ - -#include "config.h" - -#include -#include -#include -#include -#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 . - * - * Authors: - * Alexander Larsson - */ - -#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 deleted file mode 100644 index c700bdb10c0ae12d2dd9e75604c1fc9e429326a3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 32 ZcmZ<{ODxJv%qwAl0tq0k0AXu@C;(fr1APDh 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 -#include -#include -#include -#include - -#include "libglnx/libglnx.h" - -#include -#include - -#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 -#include - -/* -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 (); -}