[gnome-software/wip/hughsie/symlinks: 1/2] Use a symlink cache for the flatpak AppStream support



commit 29ace513def5c8f5f618897274eb786a3759ff76
Author: Richard Hughes <richard hughsie com>
Date:   Tue Jun 7 17:57:29 2016 +0100

    Use a symlink cache for the flatpak AppStream support
    
    The idea is to encode the scope and user-specified origin in the filename as
    flatpak remotes have metadata with the filename `appstream.xml.gz` that have
    origin harcoded to 'flatpak'. This requires the corresponding appstream-glib
    functionality to work.
    
    The gnome-software process will keep the symlink cache up to date. I'm using
    the AppStream-specified ~/.local/share/app-info/xmls at the moment, but if we
    decide that the data is actually gnome-software specific then it can move.

 src/plugins/Makefile.am           |    4 +
 src/plugins/gs-appstream.c        |    8 +-
 src/plugins/gs-flatpak-symlinks.c |  239 +++++++++++++++++++++++++++++++++++++
 src/plugins/gs-flatpak-symlinks.h |   36 ++++++
 src/plugins/gs-flatpak.c          |   11 ++
 src/plugins/gs-plugin-appstream.c |    2 -
 6 files changed, 296 insertions(+), 4 deletions(-)
---
diff --git a/src/plugins/Makefile.am b/src/plugins/Makefile.am
index c5389c2..c551f61 100644
--- a/src/plugins/Makefile.am
+++ b/src/plugins/Makefile.am
@@ -168,6 +168,8 @@ libgs_plugin_flatpak_system_la_SOURCES =            \
        gs-appstream.h                                  \
        gs-flatpak.c                                    \
        gs-flatpak.h                                    \
+       gs-flatpak-symlinks.c                           \
+       gs-flatpak-symlinks.h                           \
        gs-plugin-flatpak-system.c
 libgs_plugin_flatpak_system_la_LIBADD = $(GS_PLUGIN_LIBS) $(FLATPAK_LIBS)
 libgs_plugin_flatpak_system_la_LDFLAGS = -module -avoid-version
@@ -178,6 +180,8 @@ libgs_plugin_flatpak_user_la_SOURCES =                      \
        gs-appstream.h                                  \
        gs-flatpak.c                                    \
        gs-flatpak.h                                    \
+       gs-flatpak-symlinks.c                           \
+       gs-flatpak-symlinks.h                           \
        gs-plugin-flatpak-user.c
 libgs_plugin_flatpak_user_la_LIBADD = $(GS_PLUGIN_LIBS) $(FLATPAK_LIBS)
 libgs_plugin_flatpak_user_la_LDFLAGS = -module -avoid-version
diff --git a/src/plugins/gs-appstream.c b/src/plugins/gs-appstream.c
index 08794b4..ffbfd7f 100644
--- a/src/plugins/gs-appstream.c
+++ b/src/plugins/gs-appstream.c
@@ -535,8 +535,12 @@ gs_appstream_refine_app (GsPlugin *plugin,
 
        /* set origin */
        if (as_app_get_origin (item) != NULL &&
-           gs_app_get_origin (app) == NULL) {
-               gs_app_set_origin (app, as_app_get_origin (item));
+           gs_app_get_origin (app) == NULL ) {
+               tmp = as_app_get_origin (item);
+               if (g_str_has_prefix (tmp, "flatpak:"))
+                       gs_app_set_origin (app, tmp + 8);
+               if (g_str_has_prefix (tmp, "user-flatpak:"))
+                       gs_app_set_origin (app, tmp + 13);
        }
 
        /* set description */
diff --git a/src/plugins/gs-flatpak-symlinks.c b/src/plugins/gs-flatpak-symlinks.c
new file mode 100644
index 0000000..08ec3db
--- /dev/null
+++ b/src/plugins/gs-flatpak-symlinks.c
@@ -0,0 +1,239 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2016 Richard Hughes <richard hughsie com>
+ *
+ * Licensed under the GNU General Public License Version 2
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <config.h>
+
+#include <flatpak.h>
+#include <string.h>
+
+#include "gs-flatpak-symlinks.h"
+
+static gboolean
+gs_flatpak_symlinks_remote_valid (FlatpakRemote *xremote)
+{
+       if (xremote == NULL)
+               return FALSE;
+       if (flatpak_remote_get_disabled (xremote))
+               return FALSE;
+       if (flatpak_remote_get_noenumerate (xremote))
+               return FALSE;
+       return TRUE;
+}
+
+/* encode the symlink name with ${scope}:${name}[.xml.gz] */
+static gboolean
+gs_flatpak_symlinks_check_exist (FlatpakRemote *xremote,
+                                const gchar *cache_dir,
+                                const gchar *prefix,
+                                const gchar *kind,
+                                GError **error)
+{
+       g_autofree gchar *appstream_dir_fn = NULL;
+       g_autofree gchar *flatpak_remote_fn = NULL;
+       g_autofree gchar *subdir = NULL;
+       g_autofree gchar *symlink_source = NULL;
+       g_autofree gchar *symlink_target = NULL;
+       g_autofree gchar *xml_dir = NULL;
+       g_autoptr(GFile) appstream_dir = NULL;
+
+       /* get the AppStream data location */
+       appstream_dir = flatpak_remote_get_appstream_dir (xremote, NULL);
+       if (appstream_dir == NULL) {
+               g_debug ("no appstream dir for %s, skipping",
+                        flatpak_remote_get_name (xremote));
+               return TRUE;
+       }
+
+       /* ensure all the remotes have an XML symlink */
+       appstream_dir_fn = g_file_get_path (appstream_dir);
+       subdir = g_build_filename (cache_dir, kind, NULL);
+       if (g_strcmp0 (kind, "xmls") == 0) {
+               flatpak_remote_fn = g_strdup_printf ("%s:%s.xml.gz",
+                                                    prefix,
+                                                    flatpak_remote_get_name (xremote));
+               symlink_target = g_build_filename (appstream_dir_fn,
+                                                  "appstream.xml.gz",
+                                                  NULL);
+       } else {
+               flatpak_remote_fn = g_strdup_printf ("%s:%s",
+                                                    prefix,
+                                                    flatpak_remote_get_name (xremote));
+               symlink_target = g_build_filename (appstream_dir_fn,
+                                                  "icons",
+                                                  NULL);
+       }
+       if (!g_file_test (symlink_target, G_FILE_TEST_EXISTS)) {
+               g_debug ("remote %s has no %s, skipping",
+                        flatpak_remote_get_name (xremote),
+                        symlink_target);
+               return TRUE;
+       }
+       symlink_source = g_build_filename (subdir,
+                                          flatpak_remote_fn,
+                                          NULL);
+       if (!gs_mkdir_parent (symlink_source, error))
+               return FALSE;
+
+       /* check XML symbolic link is correct */
+       if (g_file_test (symlink_source, G_FILE_TEST_EXISTS)) {
+               g_autofree gchar *symlink_target_actual = NULL;
+
+               /* same */
+               symlink_target_actual = g_file_read_link (symlink_source, NULL);
+               if (g_strcmp0 (symlink_target_actual, symlink_target) == 0) {
+                       g_debug ("symlink %s already points to %s",
+                                symlink_source, symlink_target);
+                       return TRUE;
+               }
+               g_warning ("symlink incorrect expected %s target to "
+                          "be %s, got %s, deleting",
+                          symlink_source,
+                          symlink_target,
+                          symlink_target_actual);
+               if (!gs_utils_unlink (symlink_target_actual, error))
+                       return FALSE;
+       }
+
+       /* create it if required */
+       if (!g_file_test (symlink_source, G_FILE_TEST_EXISTS)) {
+               g_debug ("creating missing symbolic link from %s to %s",
+                        symlink_source, symlink_target);
+               if (!gs_utils_symlink (symlink_target, symlink_source, error))
+                       return FALSE;
+       }
+       return TRUE;
+}
+
+/* encode the symlink name with ${scope}:${name}, i.e. the origin */
+static gboolean
+gs_flatpak_symlinks_check_valid (FlatpakInstallation *installation,
+                                const gchar *cache_dir,
+                                const gchar *prefix,
+                                const gchar *kind,
+                                GCancellable *cancellable,
+                                GError **error)
+{
+       const gchar *tmp;
+       g_autofree gchar *subdir = NULL;
+       g_autoptr(GDir) dir = NULL;
+
+       subdir = g_build_filename (cache_dir, kind, NULL);
+       if (!g_file_test (subdir, G_FILE_TEST_EXISTS))
+               return TRUE;
+       dir = g_dir_open (subdir, 0, error);
+       if (dir == NULL)
+               return FALSE;
+       while ((tmp = g_dir_read_name (dir)) != NULL) {
+               gchar *str;
+               g_autofree gchar *fn = NULL;
+               g_autofree gchar *origin = NULL;
+               g_autofree gchar *prefix_colon = g_strdup_printf ("%s:", prefix);
+               g_autoptr(FlatpakRemote) xremote = NULL;
+
+               if (!g_str_has_prefix (tmp, prefix_colon))
+                       continue;
+               if (!g_file_test (tmp, G_FILE_TEST_IS_SYMLINK))
+                       continue;
+
+               /* can we find a valid remote for this file */
+               origin = g_strdup (tmp + strlen (prefix_colon));
+               str = g_strrstr (origin, ".xml.gz");
+               if (str != NULL)
+                       *str = '\0';
+               xremote = flatpak_installation_get_remote_by_name (installation,
+                                                                  origin,
+                                                                  cancellable,
+                                                                  NULL);
+               if (gs_flatpak_symlinks_remote_valid (xremote)) {
+                       g_debug ("%s remote symlink is valid", origin);
+                       continue;
+               }
+               fn = g_build_filename (subdir, tmp, NULL);
+               g_debug ("deleting %s symlink as no longer valid", fn);
+               if (!gs_utils_unlink (fn, error))
+                       return FALSE;
+       }
+       return TRUE;
+}
+
+gboolean
+gs_flatpak_symlinks_rebuild (FlatpakInstallation *installation,
+                            GCancellable *cancellable,
+                            GError **error)
+{
+       const gchar *prefix = "flatpak";
+       guint i;
+       g_autofree gchar *cache_dir = NULL;
+       g_autoptr(GPtrArray) xremotes = NULL;
+
+       /* use the correct symlink target */
+       cache_dir = g_build_filename (g_get_user_data_dir (),
+                                     "app-info",
+                                     NULL);
+       if (flatpak_installation_get_is_user (installation))
+               prefix = "user-flatpak";
+
+       /* go through each remote checking the symlink is in place */
+       xremotes = flatpak_installation_list_remotes (installation,
+                                                     cancellable,
+                                                     error);
+       if (xremotes == NULL)
+               return FALSE;
+       for (i = 0; i < xremotes->len; i++) {
+               FlatpakRemote *xremote = g_ptr_array_index (xremotes, i);
+               if (!gs_flatpak_symlinks_remote_valid (xremote))
+                       continue;
+               g_debug ("found remote %s:%s",
+                        prefix,
+                        flatpak_remote_get_name (xremote));
+               if (!gs_flatpak_symlinks_check_exist (xremote,
+                                                     cache_dir,
+                                                     prefix,
+                                                     "xmls",
+                                                     error))
+                       return FALSE;
+               if (!gs_flatpak_symlinks_check_exist (xremote,
+                                                     cache_dir,
+                                                     prefix,
+                                                     "icons",
+                                                     error))
+                       return FALSE;
+       }
+
+       /* go through each symlink and check the remote still valid */
+       if (!gs_flatpak_symlinks_check_valid (installation,
+                                             cache_dir,
+                                             prefix,
+                                             "xmls",
+                                             cancellable,
+                                             error))
+               return FALSE;
+       if (!gs_flatpak_symlinks_check_valid (installation,
+                                             cache_dir,
+                                             prefix,
+                                             "icons",
+                                             cancellable,
+                                             error))
+               return FALSE;
+
+       /* success */
+       return TRUE;
+}
diff --git a/src/plugins/gs-flatpak-symlinks.h b/src/plugins/gs-flatpak-symlinks.h
new file mode 100644
index 0000000..aeda415
--- /dev/null
+++ b/src/plugins/gs-flatpak-symlinks.h
@@ -0,0 +1,36 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2016 Richard Hughes <richard hughsie com>
+ *
+ * Licensed under the GNU General Public License Version 2
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef __GS_FLATPAK_SYMLINKS_H
+#define __GS_FLATPAK_SYMLINKS_H
+
+#include <gnome-software.h>
+
+G_BEGIN_DECLS
+
+gboolean        gs_flatpak_symlinks_rebuild    (FlatpakInstallation    *installation,
+                                                GCancellable           *cancellable,
+                                                GError                 **error);
+
+G_END_DECLS
+
+#endif /* __GS_FLATPAK_SYMLINKS_H */
+
diff --git a/src/plugins/gs-flatpak.c b/src/plugins/gs-flatpak.c
index 188dc9c..61b845c 100644
--- a/src/plugins/gs-flatpak.c
+++ b/src/plugins/gs-flatpak.c
@@ -33,6 +33,7 @@
 
 #include "gs-appstream.h"
 #include "gs-flatpak.h"
+#include "gs-flatpak-symlinks.h"
 
 struct _GsFlatpak {
        GObject                  parent_instance;
@@ -51,7 +52,13 @@ gs_plugin_flatpak_changed_cb (GFileMonitor *monitor,
                              GFileMonitorEvent event_type,
                              GsFlatpak *self)
 {
+       g_autoptr(GError) error = NULL;
+
        gs_plugin_updates_changed (self->plugin);
+
+       /* ensure the AppStream symlink cache is up to date */
+       if (!gs_flatpak_symlinks_rebuild (self->installation, NULL, &error))
+               g_warning ("failed to check symlinks: %s", error->message);
 }
 
 gboolean
@@ -93,6 +100,10 @@ gs_flatpak_setup (GsFlatpak *self, GCancellable *cancellable, GError **error)
        g_signal_connect (self->monitor, "changed",
                          G_CALLBACK (gs_plugin_flatpak_changed_cb), self);
 
+       /* ensure the AppStream symlink cache is up to date */
+       if (!gs_flatpak_symlinks_rebuild (self->installation, cancellable, error))
+               return FALSE;
+
        /* success */
        return TRUE;
 }
diff --git a/src/plugins/gs-plugin-appstream.c b/src/plugins/gs-plugin-appstream.c
index dc63a93..3c9eaf7 100644
--- a/src/plugins/gs-plugin-appstream.c
+++ b/src/plugins/gs-plugin-appstream.c
@@ -153,8 +153,6 @@ gs_plugin_setup (GsPlugin *plugin, GCancellable *cancellable, GError **error)
                                     AS_STORE_LOAD_FLAG_APP_INFO_USER |
                                     AS_STORE_LOAD_FLAG_APPDATA |
                                     AS_STORE_LOAD_FLAG_DESKTOP |
-                                    AS_STORE_LOAD_FLAG_FLATPAK_USER |
-                                    AS_STORE_LOAD_FLAG_FLATPAK_SYSTEM |
                                     AS_STORE_LOAD_FLAG_APP_INSTALL,
                                     NULL,
                                     error);


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]