diff --git a/pressure-vessel/flatpak-bwrap-private.h b/pressure-vessel/flatpak-bwrap-private.h index 03c83a3ee798d367ef5da56ebf50af73d2dfefdd..44818bfc013f04b9cb876234d9d4b92fabd5301e 100644 --- a/pressure-vessel/flatpak-bwrap-private.h +++ b/pressure-vessel/flatpak-bwrap-private.h @@ -1,6 +1,6 @@ /* * Taken from Flatpak - * Last updated: Flatpak 1.15.8 + * Last updated: Flatpak 1.15.9 * * Copyright © 2014-2018 Red Hat, Inc * SPDX-License-Identifier: LGPL-2.1-or-later diff --git a/pressure-vessel/flatpak-bwrap.c b/pressure-vessel/flatpak-bwrap.c index 392711480ab8fcd946a688c37f76cde27ead917a..54e0b696d73604ed253f29c172f96c17b563d12a 100644 --- a/pressure-vessel/flatpak-bwrap.c +++ b/pressure-vessel/flatpak-bwrap.c @@ -1,6 +1,6 @@ /* vi:set et sw=2 sts=2 cin cino=t0,f0,(0,{s,>2s,n-s,^-s,e-s: * Taken from Flatpak - * Last updated: Flatpak 1.15.8 + * Last updated: Flatpak 1.15.9 * * Copyright © 2014-2018 Red Hat, Inc * SPDX-License-Identifier: LGPL-2.1-or-later diff --git a/pressure-vessel/flatpak-common-types-private.h b/pressure-vessel/flatpak-common-types-private.h index b24aa061c6ed0167c06661e8053eac5bcbf9012f..b943a2eefd9a33436bcb6b012afa559e4ffbccd2 100644 --- a/pressure-vessel/flatpak-common-types-private.h +++ b/pressure-vessel/flatpak-common-types-private.h @@ -1,6 +1,6 @@ /* * Taken from Flatpak - * Last updated: Flatpak 1.15.8 + * Last updated: Flatpak 1.15.9 * Copyright © 2015 Red Hat, Inc * SPDX-License-Identifier: LGPL-2.1-or-later * diff --git a/pressure-vessel/flatpak-context-private.h b/pressure-vessel/flatpak-context-private.h index e7bee68c153a09e26e06093665ff38fec6b8cb3b..b9ab61b606f9dd1745c9625d4c459fb687ffae64 100644 --- a/pressure-vessel/flatpak-context-private.h +++ b/pressure-vessel/flatpak-context-private.h @@ -1,6 +1,6 @@ /* * Taken from Flatpak - * Last updated: Flatpak 1.15.8 + * Last updated: Flatpak 1.15.9 * * Copyright © 2014-2018 Red Hat, Inc * SPDX-License-Identifier: LGPL-2.1-or-later @@ -179,6 +179,13 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC (FlatpakContext, flatpak_context_free) GFile *flatpak_get_user_base_dir_location (void); GFile *flatpak_get_data_dir (const char *app_id); +gboolean flatpak_context_get_allowed_exports (FlatpakContext *context, + const char *source_path, + const char *app_id, + char ***allowed_extensions_out, + char ***allowed_prefixes_out, + gboolean *require_exact_match_out); + #endif #endif /* __FLATPAK_CONTEXT_H__ */ diff --git a/pressure-vessel/flatpak-error.h b/pressure-vessel/flatpak-error.h index 3b9c9a9390b966ab80655d7fd880964fad77291c..97ce8866fe8c79d8366b3f760eaeefd40cd795c2 100644 --- a/pressure-vessel/flatpak-error.h +++ b/pressure-vessel/flatpak-error.h @@ -1,6 +1,6 @@ /* flatpak-error.c * Adapted from Flatpak - * Last updated: Flatpak 1.14.8 + * Last updated: Flatpak 1.15.9 * * Copyright (C) 2015 Red Hat, Inc * SPDX-License-Identifier: LGPL-2.0-or-later diff --git a/pressure-vessel/flatpak-exports-private.h b/pressure-vessel/flatpak-exports-private.h index a356e20698712782cf2767cf92969fd3e841950b..ae2b01fbf71520c9cb2386d492717d4c19b6a864 100644 --- a/pressure-vessel/flatpak-exports-private.h +++ b/pressure-vessel/flatpak-exports-private.h @@ -1,6 +1,6 @@ /* * Taken from Flatpak - * Last updated: Flatpak 1.15.8 + * Last updated: Flatpak 1.15.9 * Copyright © 2014-2018 Red Hat, Inc * SPDX-License-Identifier: LGPL-2.1-or-later * diff --git a/pressure-vessel/flatpak-exports.c b/pressure-vessel/flatpak-exports.c index 805fad4fb5dbfdcb05ad2e05b525f1dcf5614701..adc754bfd1f5d516e789f0f91e55f651144549a8 100644 --- a/pressure-vessel/flatpak-exports.c +++ b/pressure-vessel/flatpak-exports.c @@ -1,6 +1,6 @@ /* vi:set et sw=2 sts=2 cin cino=t0,f0,(0,{s,>2s,n-s,^-s,e-s: * Taken from Flatpak - * Last updated: Flatpak 1.15.8 + * Last updated: Flatpak 1.15.9 * Copyright © 2014-2019 Red Hat, Inc * SPDX-License-Identifier: LGPL-2.1-or-later * @@ -49,10 +49,6 @@ #include "flatpak-metadata-private.h" #include "flatpak-utils-base-private.h" #include "flatpak-utils-private.h" -#if 0 -#include "flatpak-dir-private.h" -#include "flatpak-systemd-dbus-generated.h" -#endif #include "flatpak-error.h" /* pressure-vessel-specific */ diff --git a/pressure-vessel/flatpak-glib-backports-private.h b/pressure-vessel/flatpak-glib-backports-private.h index 48935cf4a18fe6497f489d1b16f818de76498c84..2f843bbe69dd2b99e883d7150ee19947800b1863 100644 --- a/pressure-vessel/flatpak-glib-backports-private.h +++ b/pressure-vessel/flatpak-glib-backports-private.h @@ -1,6 +1,6 @@ /* * Taken from Flatpak - * Last updated: Flatpak 1.15.8 + * Last updated: Flatpak 1.15.9 * * Copyright 2013 Allison Karlitskaya * Copyright 2013 Collabora Ltd. diff --git a/pressure-vessel/flatpak-glib-backports.c b/pressure-vessel/flatpak-glib-backports.c index b91271f147a2b48fa362d8a13447764baeee80ba..e0bea9a3346063a2482402d73081544bac87c600 100644 --- a/pressure-vessel/flatpak-glib-backports.c +++ b/pressure-vessel/flatpak-glib-backports.c @@ -1,7 +1,7 @@ /* vi:set et sw=2 sts=2 cin cino=t0,f0,(0,{s,>2s,n-s,^-s,e-s: * * Taken from Flatpak - * Last updated: Flatpak 1.15.8 + * Last updated: Flatpak 1.15.9 * * Copyright 1995-1998 Free Software Foundation, Inc. * Copyright 2004 Hidetoshi Tajima diff --git a/pressure-vessel/flatpak-metadata-private.h b/pressure-vessel/flatpak-metadata-private.h index 429f81aa12b2fa5bba863618a51499a68434328c..0f140b20a69d61bb2cfb9988b6b2e7a4bc6ea542 100644 --- a/pressure-vessel/flatpak-metadata-private.h +++ b/pressure-vessel/flatpak-metadata-private.h @@ -1,6 +1,6 @@ /* * Taken from Flatpak - * Last updated: Flatpak 1.15.8 + * Last updated: Flatpak 1.15.9 * * Copyright 2014 Red Hat, Inc * SPDX-License-Identifier: LGPL-2.1-or-later diff --git a/pressure-vessel/flatpak-portal.h b/pressure-vessel/flatpak-portal.h index 065eeb0dcd7b36ffad3e33d499c1d81b90be19ff..ef97b4c65f2a08ba429f0667dd4504a9b9fc09ea 100644 --- a/pressure-vessel/flatpak-portal.h +++ b/pressure-vessel/flatpak-portal.h @@ -1,6 +1,6 @@ /* * Taken from Flatpak - * Last updated: Flatpak 1.15.8 + * Last updated: Flatpak 1.15.9 * * Copyright © 2018 Red Hat, Inc * SPDX-License-Identifier: LGPL-2.1-or-later diff --git a/pressure-vessel/flatpak-run-dbus-private.h b/pressure-vessel/flatpak-run-dbus-private.h index 33ca66af89e8d893499c68445e2291bb80fcee2f..24ed52baad493e8af20a12a65065adfc25d84c30 100644 --- a/pressure-vessel/flatpak-run-dbus-private.h +++ b/pressure-vessel/flatpak-run-dbus-private.h @@ -1,6 +1,6 @@ /* * Taken from Flatpak - * Last updated: Flatpak 1.15.8 + * Last updated: Flatpak 1.15.9 * Copyright © 2014 Red Hat, Inc * SPDX-License-Identifier: LGPL-2.1-or-later * diff --git a/pressure-vessel/flatpak-run-dbus.c b/pressure-vessel/flatpak-run-dbus.c index 2e66d1ecf214072b0114acee932168156dcf8be9..0f8af933d767445ade019fc848712c0e1d252d6a 100644 --- a/pressure-vessel/flatpak-run-dbus.c +++ b/pressure-vessel/flatpak-run-dbus.c @@ -1,6 +1,6 @@ /* vi:set et sw=2 sts=2 cin cino=t0,f0,(0,{s,>2s,n-s,^-s,e-s: * Taken from Flatpak - * Last updated: Flatpak 1.15.8 + * Last updated: Flatpak 1.15.9 * Copyright © 2014-2019 Red Hat, Inc * SPDX-License-Identifier: LGPL-2.1-or-later * @@ -460,6 +460,8 @@ flatpak_run_add_a11y_dbus_args (FlatpakBwrap *app_bwrap, flatpak_bwrap_add_args (proxy_arg_bwrap, a11y_address, proxy_socket, "--filter", "--sloppy-names", + "--broadcast=org.a11y.atspi.Registry.EventListenerRegistered=@/org/a11y/atspi/registry", + "--broadcast=org.a11y.atspi.Registry.EventListenerDeregistered=@/org/a11y/atspi/registry", "--call=org.a11y.atspi.Registry=org.a11y.atspi.Socket.Embed@/org/a11y/atspi/accessible/root", "--call=org.a11y.atspi.Registry=org.a11y.atspi.Socket.Unembed@/org/a11y/atspi/accessible/root", "--call=org.a11y.atspi.Registry=org.a11y.atspi.Registry.GetRegisteredEvents@/org/a11y/atspi/registry", diff --git a/pressure-vessel/flatpak-run-private.h b/pressure-vessel/flatpak-run-private.h index f59bc5bad790b80c28fe5882f6095d5939d71ab4..debcaffc99d73331c4427f81bb7347dcdd8893ea 100644 --- a/pressure-vessel/flatpak-run-private.h +++ b/pressure-vessel/flatpak-run-private.h @@ -1,6 +1,6 @@ /* * Cut-down version of common/flatpak-run-private.h from Flatpak - * Last updated: Flatpak 1.15.8 + * Last updated: Flatpak 1.15.9 * * Copyright © 2017-2022 Collabora Ltd. * Copyright © 2014-2022 Red Hat, Inc diff --git a/pressure-vessel/flatpak-run-pulseaudio-private.h b/pressure-vessel/flatpak-run-pulseaudio-private.h index ea52ab652f1734c94a7d7ee9301107e9f84a007c..049d39693be0e16dc91c3ad088af53c838505eed 100644 --- a/pressure-vessel/flatpak-run-pulseaudio-private.h +++ b/pressure-vessel/flatpak-run-pulseaudio-private.h @@ -1,6 +1,6 @@ /* * Taken from Flatpak - * Last updated: Flatpak 1.15.8 + * Last updated: Flatpak 1.15.9 * Copyright © 2014 Red Hat, Inc * SPDX-License-Identifier: LGPL-2.1-or-later * diff --git a/pressure-vessel/flatpak-run-pulseaudio.c b/pressure-vessel/flatpak-run-pulseaudio.c index 73b5d6c6535ac000bf5ea07985b22b9f6dce1627..dea2ef72947687c6a15a7203bb7eb986f77dcc01 100644 --- a/pressure-vessel/flatpak-run-pulseaudio.c +++ b/pressure-vessel/flatpak-run-pulseaudio.c @@ -1,6 +1,6 @@ /* vi:set et sw=2 sts=2 cin cino=t0,f0,(0,{s,>2s,n-s,^-s,e-s: * Taken from Flatpak - * Last updated: Flatpak 1.15.8 + * Last updated: Flatpak 1.15.9 * Copyright © 2014-2019 Red Hat, Inc * SPDX-License-Identifier: LGPL-2.1-or-later * diff --git a/pressure-vessel/flatpak-run-sockets-private.h b/pressure-vessel/flatpak-run-sockets-private.h index 7d7015aa4265f981a002ce177be28d674683df83..fd71d35163dc49b8a008b42dfa02d1302d0131f7 100644 --- a/pressure-vessel/flatpak-run-sockets-private.h +++ b/pressure-vessel/flatpak-run-sockets-private.h @@ -1,6 +1,6 @@ /* * Taken from Flatpak - * Last updated: Flatpak 1.15.8 + * Last updated: Flatpak 1.15.9 * Copyright © 2014 Red Hat, Inc * SPDX-License-Identifier: LGPL-2.1-or-later * diff --git a/pressure-vessel/flatpak-run-sockets.c b/pressure-vessel/flatpak-run-sockets.c index 439b265b27a7f4a012f1acb87c1ab682ea64a7d1..c1f18f502cba90489f07f73b5a2f5c41ef120435 100644 --- a/pressure-vessel/flatpak-run-sockets.c +++ b/pressure-vessel/flatpak-run-sockets.c @@ -1,6 +1,6 @@ /* vi:set et sw=2 sts=2 cin cino=t0,f0,(0,{s,>2s,n-s,^-s,e-s: * Taken from Flatpak - * Last updated: Flatpak 1.15.8 + * Last updated: Flatpak 1.15.9 * Copyright © 2014-2019 Red Hat, Inc * SPDX-License-Identifier: LGPL-2.1-or-later * diff --git a/pressure-vessel/flatpak-run-wayland-private.h b/pressure-vessel/flatpak-run-wayland-private.h index ccf2e5baae439281d800f29f487cd76e9af70b68..d084a215aaeb28ec19015e2e039fefd423a99c64 100644 --- a/pressure-vessel/flatpak-run-wayland-private.h +++ b/pressure-vessel/flatpak-run-wayland-private.h @@ -1,6 +1,6 @@ /* * Taken from Flatpak - * Last updated: Flatpak 1.15.8 + * Last updated: Flatpak 1.15.9 * Copyright © 2014 Red Hat, Inc * SPDX-License-Identifier: LGPL-2.1-or-later * @@ -33,6 +33,10 @@ G_BEGIN_DECLS gboolean flatpak_run_add_wayland_args (FlatpakBwrap *bwrap, +#if 0 + const char *app_id, + const char *instance_id, +#endif gboolean inherit_wayland_socket); G_END_DECLS diff --git a/pressure-vessel/flatpak-run-wayland.c b/pressure-vessel/flatpak-run-wayland.c index 4904869e5f969827bcc48833e250e754a68509e6..3f11b431c0d28b0f0a62c206c694196537d5e26f 100644 --- a/pressure-vessel/flatpak-run-wayland.c +++ b/pressure-vessel/flatpak-run-wayland.c @@ -1,6 +1,6 @@ /* vi:set et sw=2 sts=2 cin cino=t0,f0,(0,{s,>2s,n-s,^-s,e-s: * Taken from Flatpak - * Last updated: Flatpak 1.15.8 + * Last updated: Flatpak 1.15.9 * Copyright © 2014-2019 Red Hat, Inc * SPDX-License-Identifier: LGPL-2.1-or-later * @@ -241,6 +241,10 @@ out: */ gboolean flatpak_run_add_wayland_args (FlatpakBwrap *bwrap, +#if 0 + const char *app_id, + const char *instance_id, +#endif gboolean inherit_wayland_socket) { const char *wayland_display; diff --git a/pressure-vessel/flatpak-run-x11-private.h b/pressure-vessel/flatpak-run-x11-private.h index 996db9c46aafff13561ba9edbfd9e64ddac656aa..a1784dabe68b79d6242b50ea5289e8b33148aa56 100644 --- a/pressure-vessel/flatpak-run-x11-private.h +++ b/pressure-vessel/flatpak-run-x11-private.h @@ -1,6 +1,6 @@ /* * Taken from Flatpak - * Last updated: Flatpak 1.15.8 + * Last updated: Flatpak 1.15.9 * Copyright © 2014 Red Hat, Inc * SPDX-License-Identifier: LGPL-2.1-or-later * diff --git a/pressure-vessel/flatpak-run-x11.c b/pressure-vessel/flatpak-run-x11.c index b991e428bf0e6252397e96e218a83615eeb87bd2..d3097ccc22ade4803ba602edaa918c2d01c21930 100644 --- a/pressure-vessel/flatpak-run-x11.c +++ b/pressure-vessel/flatpak-run-x11.c @@ -1,6 +1,6 @@ /* vi:set et sw=2 sts=2 cin cino=t0,f0,(0,{s,>2s,n-s,^-s,e-s: * Taken from Flatpak - * Last updated: Flatpak 1.15.8 + * Last updated: Flatpak 1.15.9 * Copyright © 2014-2019 Red Hat, Inc * SPDX-License-Identifier: LGPL-2.1-or-later * diff --git a/pressure-vessel/flatpak-run.c b/pressure-vessel/flatpak-run.c index f41d78876a5e24bae51768601e7c59bde25fe54b..4bad23ff148ed1d57e92e0fa6334c3a74101cb67 100644 --- a/pressure-vessel/flatpak-run.c +++ b/pressure-vessel/flatpak-run.c @@ -1,6 +1,6 @@ /* vi:set et sw=2 sts=2 cin cino=t0,f0,(0,{s,>2s,n-s,^-s,e-s: * Cut-down version of common/flatpak-run.c from Flatpak - * Last updated: Flatpak 1.15.8 + * Last updated: Flatpak 1.15.9 * * Copyright © 2017-2022 Collabora Ltd. * Copyright © 2014-2022 Red Hat, Inc @@ -62,12 +62,16 @@ #include <gio/gio.h> #include "libglnx.h" +#if 0 +#include "flatpak-dbus-generated.h" +#endif #include "flatpak-run-dbus-private.h" #include "flatpak-run-private.h" #include "flatpak-run-sockets-private.h" #include "flatpak-utils-base-private.h" #if 0 #include "flatpak-dir-private.h" +#include "flatpak-dir-utils-private.h" #include "flatpak-instance-private.h" #endif #include "flatpak-systemd-dbus-generated.h" @@ -83,6 +87,12 @@ #if 0 +typedef FlatpakSessionHelper AutoFlatpakSessionHelper; +G_DEFINE_AUTOPTR_CLEANUP_FUNC (AutoFlatpakSessionHelper, g_object_unref) + +typedef XdpDbusDocuments AutoXdpDbusDocuments; +G_DEFINE_AUTOPTR_CLEANUP_FUNC (AutoXdpDbusDocuments, g_object_unref) + static int flatpak_extension_compare_by_path (gconstpointer _a, gconstpointer _b) @@ -1629,12 +1639,14 @@ add_tzdata_args (FlatpakBwrap *bwrap, /* Check for runtime /usr/share/zoneinfo */ if (runtime_zoneinfo != NULL && g_file_query_exists (runtime_zoneinfo, NULL)) { + const char *tzdir = flatpak_get_tzdir (); + /* Check for host /usr/share/zoneinfo */ - if (g_file_test ("/usr/share/zoneinfo", G_FILE_TEST_IS_DIR)) + if (g_file_test (tzdir, G_FILE_TEST_IS_DIR)) { /* Here we assume the host timezone file exist in the host data */ flatpak_bwrap_add_args (bwrap, - "--ro-bind", "/usr/share/zoneinfo", "/usr/share/zoneinfo", + "--ro-bind", tzdir, "/usr/share/zoneinfo", "--symlink", localtime_content, "/etc/localtime", NULL); } @@ -2182,6 +2194,16 @@ flatpak_run_setup_usr_links (FlatpakBwrap *bwrap, } } +/* Directories in /sys to share with the sandbox if accessible. */ +static const char *const sysfs_dirs[] = +{ + "/sys/block", + "/sys/bus", + "/sys/class", + "/sys/dev", + "/sys/devices" +}; + gboolean flatpak_run_setup_base_argv (FlatpakBwrap *bwrap, GFile *runtime_files, @@ -2201,6 +2223,7 @@ flatpak_run_setup_base_argv (FlatpakBwrap *bwrap, gboolean parent_expose_pids = (flags & FLATPAK_RUN_FLAG_PARENT_EXPOSE_PIDS) != 0; gboolean parent_share_pids = (flags & FLATPAK_RUN_FLAG_PARENT_SHARE_PIDS) != 0; gboolean bwrap_unprivileged = flatpak_bwrap_is_unprivileged (); + gsize i; /* Disable recursive userns for all flatpak processes, as we need this * to guarantee that the sandbox can't restructure the filesystem. @@ -2269,16 +2292,21 @@ flatpak_run_setup_base_argv (FlatpakBwrap *bwrap, "--perms", "0700", "--dir", run_dir, "--setenv", "XDG_RUNTIME_DIR", run_dir, "--symlink", "../run", "/var/run", - "--ro-bind", "/sys/block", "/sys/block", - "--ro-bind", "/sys/bus", "/sys/bus", - "--ro-bind", "/sys/class", "/sys/class", - "--ro-bind", "/sys/dev", "/sys/dev", - "--ro-bind", "/sys/devices", "/sys/devices", "--ro-bind-try", "/proc/self/ns/user", "/run/.userns", /* glib uses this like /etc/timezone */ "--symlink", "/etc/timezone", "/var/db/zoneinfo", NULL); + for (i = 0; i < G_N_ELEMENTS (sysfs_dirs); i++) + { + const char *dir = sysfs_dirs[i]; + + if (access (dir, R_OK|X_OK) == 0) + flatpak_bwrap_add_args (bwrap, "--ro-bind", dir, dir, NULL); + else + g_info ("Not sharing %s with sandbox: %s", dir, g_strerror (errno)); + } + if (flags & FLATPAK_RUN_FLAG_DIE_WITH_PARENT) flatpak_bwrap_add_args (bwrap, "--die-with-parent", diff --git a/pressure-vessel/flatpak-session-helper.h b/pressure-vessel/flatpak-session-helper.h index ca2611cb385e7418bed6eaa8560e9a67182a3ffc..9414beb80ee17a766b83c2f790807f59e81d2cd0 100644 --- a/pressure-vessel/flatpak-session-helper.h +++ b/pressure-vessel/flatpak-session-helper.h @@ -1,6 +1,6 @@ /* * Taken from Flatpak - * Last updated: Flatpak 1.15.8 + * Last updated: Flatpak 1.15.9 * * Copyright © 2021 Collabora Ltd. * SPDX-License-Identifier: LGPL-2.1-or-later diff --git a/pressure-vessel/flatpak-utils-base-private.h b/pressure-vessel/flatpak-utils-base-private.h index de0db1160c98edffae1b77746fd1416bd12c0b7b..96c25a7c4a669f468bf9d7f37dcf136ea661c2e0 100644 --- a/pressure-vessel/flatpak-utils-base-private.h +++ b/pressure-vessel/flatpak-utils-base-private.h @@ -1,6 +1,6 @@ /* * Taken from Flatpak - * Last updated: Flatpak 1.15.8 + * Last updated: Flatpak 1.15.9 * Copyright © 2019 Red Hat, Inc * SPDX-License-Identifier: LGPL-2.1-or-later * @@ -32,7 +32,9 @@ # define G_DBUS_METHOD_INVOCATION_UNHANDLED FALSE #endif -char *flatpak_get_timezone (void); +const char * flatpak_get_tzdir (void); + +char * flatpak_get_timezone (void); char * flatpak_readlink (const char *path, GError **error); diff --git a/pressure-vessel/flatpak-utils-base.c b/pressure-vessel/flatpak-utils-base.c index 57c2d00a270f1ef3dc365408c4b61518d3b662dd..c921cb401ab6c6b7171c4acc49f5c80879d2ad5a 100644 --- a/pressure-vessel/flatpak-utils-base.c +++ b/pressure-vessel/flatpak-utils-base.c @@ -1,6 +1,6 @@ /* vi:set et sw=2 sts=2 cin cino=t0,f0,(0,{s,>2s,n-s,^-s,e-s: * Taken from Flatpak - * Last updated: Flatpak 1.15.8 + * Last updated: Flatpak 1.15.9 * Copyright © 2019 Red Hat, Inc * SPDX-License-Identifier: LGPL-2.1-or-later * @@ -31,17 +31,23 @@ #include <gio/gio.h> #include "libglnx.h" -/* To keep this more similar to the original file, we explicitly disable - * this warning rather than fixing it */ -#pragma GCC diagnostic ignored "-Wshadow" +const char * +flatpak_get_tzdir (void) +{ + const gchar *tzdir; + + tzdir = getenv ("TZDIR"); + if (tzdir) + return tzdir; + + return "/usr/share/zoneinfo"; +} char * flatpak_get_timezone (void) { g_autofree gchar *symlink = NULL; gchar *etc_timezone = NULL; - const gchar *tzdir; - const gchar *default_tzdir = "/usr/share/zoneinfo"; symlink = flatpak_resolve_link ("/etc/localtime", NULL); if (symlink != NULL) @@ -49,11 +55,10 @@ flatpak_get_timezone (void) /* Resolve relative path */ g_autofree gchar *canonical = flatpak_canonicalize_filename (symlink); char *canonical_suffix; + const gchar *tzdir = flatpak_get_tzdir (); /* Strip the prefix and slashes if possible. */ - - tzdir = getenv ("TZDIR"); - if (tzdir != NULL && g_str_has_prefix (canonical, tzdir)) + if (g_str_has_prefix (canonical, tzdir)) { canonical_suffix = canonical + strlen (tzdir); while (*canonical_suffix == '/') @@ -61,15 +66,6 @@ flatpak_get_timezone (void) return g_strdup (canonical_suffix); } - - if (g_str_has_prefix (canonical, default_tzdir)) - { - canonical_suffix = canonical + strlen (default_tzdir); - while (*canonical_suffix == '/') - canonical_suffix++; - - return g_strdup (canonical_suffix); - } } if (g_file_get_contents ("/etc/timezone", &etc_timezone, diff --git a/pressure-vessel/flatpak-utils-private.h b/pressure-vessel/flatpak-utils-private.h index eddab7c4f78891cbad711c86207c8c8b1622946f..37c02b2c3dad256b5a49a2d9a3987ba3deb712b5 100644 --- a/pressure-vessel/flatpak-utils-private.h +++ b/pressure-vessel/flatpak-utils-private.h @@ -1,6 +1,6 @@ /* - * A cut-down version of common/flatpak-utils from Flatpak - * Last updated: Flatpak 1.15.8 + * Taken from Flatpak + * Last updated: Flatpak 1.15.9 * * Copyright © 2014 Red Hat, Inc * SPDX-License-Identifier: LGPL-2.1-or-later @@ -28,26 +28,13 @@ #include <string.h> #include "libglnx.h" -#include <flatpak-common-types-private.h> #include <gio/gio.h> #include <gio/gunixfdlist.h> -#if 0 -#include "flatpak-dbus-generated.h" -#include "flatpak-document-dbus-generated.h" -#endif -#include "flatpak-context-private.h" #include "flatpak-error.h" #include "flatpak-glib-backports-private.h" -#if 0 -#include "flatpak-variant-private.h" -#include "flatpak-dir-private.h" -#include <ostree.h> -#endif #define AUTOFS_SUPER_MAGIC 0x0187 -#if 0 - #define FLATPAK_XA_CACHE_VERSION 2 /* version 1 added extra data download size */ /* version 2 added ot.ts timestamps (to new format) */ @@ -63,10 +50,6 @@ #define FLATPAK_SUMMARY_DIFF_HEADER "xadf" -#define FLATPAK_SUMMARY_HISTORY_LENGTH_DEFAULT 16 - -#endif - /* https://bugzilla.gnome.org/show_bug.cgi?id=766370 */ #if !GLIB_CHECK_VERSION (2, 49, 3) #define FLATPAK_VARIANT_BUILDER_INITIALIZER {{0, }} @@ -97,13 +80,13 @@ char * flatpak_get_real_xdg_runtime_dir (void); gboolean flatpak_has_path_prefix (const char *str, const char *prefix); -#if 0 const char * flatpak_path_match_prefix (const char *pattern, const char *path); const char * flatpak_get_arch (void); const char ** flatpak_get_arches (void); gboolean flatpak_is_linux32_arch (const char *arch); +const char *flatpak_get_compat_arch (const char *kernel_arch); const char *flatpak_get_compat_arch_reverse (const char *compat_arch); const char ** flatpak_get_gl_drivers (void); @@ -133,70 +116,6 @@ gboolean flatpak_variant_save (GFile *dest, GVariant *variant, GCancellable *cancellable, GError **error); -GVariant *flatpak_repo_load_summary (OstreeRepo *repo, - GError **error); -GVariant *flatpak_repo_load_summary_index (OstreeRepo *repo, - GError **error); -GVariant *flatpak_repo_load_digested_summary (OstreeRepo *repo, - const char *digest, - GError **error); -GPtrArray *flatpak_summary_match_subrefs (GVariant *summary, - const char *collection_id, - FlatpakDecomposed *ref); -gboolean flatpak_summary_lookup_ref (GVariant *summary, - const char *collection_id, - const char *ref, - char **out_checksum, - VarRefInfoRef *out_info); -gboolean flatpak_summary_find_ref_map (VarSummaryRef summary, - const char *collection_id, - VarRefMapRef *refs_out); -gboolean flatpak_var_ref_map_lookup_ref (VarRefMapRef ref_map, - const char *ref, - VarRefInfoRef *out_info); - -gboolean flatpak_get_allowed_exports (const char *source_path, - const char *app_id, - FlatpakContext *context, - char ***allowed_extensions_out, - char ***allowed_prefixes_out, - gboolean *require_exact_match_out); - -FlatpakDecomposed *flatpak_find_current_ref (const char *app_id, - GCancellable *cancellable, - GError **error); -GFile *flatpak_find_deploy_dir_for_ref (FlatpakDecomposed *ref, - FlatpakDir **dir_out, - GCancellable *cancellable, - GError **error); -GFile * flatpak_find_files_dir_for_ref (FlatpakDecomposed *ref, - GCancellable *cancellable, - GError **error); -GFile * flatpak_find_unmaintained_extension_dir_if_exists (const char *name, - const char *arch, - const char *branch, - GCancellable *cancellable); -FlatpakDeploy * flatpak_find_deploy_for_ref_in (GPtrArray *dirs, - const char *ref, - const char *commit, - GCancellable *cancellable, - GError **error); -FlatpakDeploy * flatpak_find_deploy_for_ref (const char *ref, - const char *commit, - FlatpakDir *opt_user_dir, - GCancellable *cancellable, - GError **error); -char ** flatpak_list_deployed_refs (const char *type, - const char *name_prefix, - const char *arch, - const char *branch, - GCancellable *cancellable, - GError **error); -char ** flatpak_list_unmaintained_refs (const char *name_prefix, - const char *branch, - const char *arch, - GCancellable *cancellable, - GError **error); gboolean flatpak_remove_dangling_symlinks (GFile *dir, GCancellable *cancellable, @@ -247,213 +166,24 @@ char *flatpak_keyfile_get_string_non_empty (GKeyFile *keyfile, const char *group, const char *key); -GKeyFile * flatpak_parse_repofile (const char *remote_name, - gboolean from_ref, - GKeyFile *keyfile, - GBytes **gpg_data_out, - GCancellable *cancellable, - GError **error); - -gboolean flatpak_repo_set_title (OstreeRepo *repo, - const char *title, - GError **error); -gboolean flatpak_repo_set_comment (OstreeRepo *repo, - const char *comment, - GError **error); -gboolean flatpak_repo_set_description (OstreeRepo *repo, - const char *description, - GError **error); -gboolean flatpak_repo_set_icon (OstreeRepo *repo, - const char *icon, - GError **error); -gboolean flatpak_repo_set_homepage (OstreeRepo *repo, - const char *homepage, - GError **error); -gboolean flatpak_repo_set_redirect_url (OstreeRepo *repo, - const char *redirect_url, - GError **error); -gboolean flatpak_repo_set_authenticator_name (OstreeRepo *repo, - const char *authenticator_name, - GError **error); -gboolean flatpak_repo_set_authenticator_install (OstreeRepo *repo, - gboolean authenticator_install, - GError **error); -gboolean flatpak_repo_set_authenticator_option (OstreeRepo *repo, - const char *key, - const char *value, - GError **error); -gboolean flatpak_repo_set_default_branch (OstreeRepo *repo, - const char *branch, - GError **error); -gboolean flatpak_repo_set_collection_id (OstreeRepo *repo, - const char *collection_id, - GError **error); -gboolean flatpak_repo_set_deploy_collection_id (OstreeRepo *repo, - gboolean deploy_collection_id, - GError **error); -gboolean flatpak_repo_set_deploy_sideload_collection_id (OstreeRepo *repo, - gboolean deploy_collection_id, - GError **error); -gboolean flatpak_repo_set_summary_history_length (OstreeRepo *repo, - guint length, - GError **error); -guint flatpak_repo_get_summary_history_length (OstreeRepo *repo); -gboolean flatpak_repo_set_gpg_keys (OstreeRepo *repo, - GBytes *bytes, - GError **error); - GBytes *flatpak_zlib_compress_bytes (GBytes *bytes, int level, GError **error); GBytes *flatpak_zlib_decompress_bytes (GBytes *bytes, GError **error); -GBytes *flatpak_summary_apply_diff (GBytes *old, - GBytes *diff, - GError **error); - -typedef enum { - FLATPAK_REPO_UPDATE_FLAG_NONE = 0, - FLATPAK_REPO_UPDATE_FLAG_DISABLE_INDEX = 1 << 0, -} FlatpakRepoUpdateFlags; - -gboolean flatpak_repo_update (OstreeRepo *repo, - FlatpakRepoUpdateFlags flags, - const char **gpg_key_ids, - const char *gpg_homedir, - GCancellable *cancellable, - GError **error); -gboolean flatpak_repo_collect_sizes (OstreeRepo *repo, - GFile *root, - guint64 *installed_size, - guint64 *download_size, - GCancellable *cancellable, - GError **error); -GVariant *flatpak_commit_get_extra_data_sources (GVariant *commitv, - GError **error); -GVariant *flatpak_repo_get_extra_data_sources (OstreeRepo *repo, - const char *rev, - GCancellable *cancellable, - GError **error); -void flatpak_repo_parse_extra_data_sources (GVariant *extra_data_sources, - int index, - const char **name, - guint64 *download_size, - guint64 *installed_size, - const guchar **sha256, - const char **uri); -gboolean flatpak_mtree_ensure_dir_metadata (OstreeRepo *repo, - OstreeMutableTree *mtree, - GCancellable *cancellable, - GError **error); -gboolean flatpak_mtree_create_symlink (OstreeRepo *repo, - OstreeMutableTree *parent, - const char *name, - const char *target, - GError **error); -gboolean flatpak_mtree_add_file_from_bytes (OstreeRepo *repo, - GBytes *bytes, - OstreeMutableTree *parent, - const char *filename, - GCancellable *cancellable, - GError **error); -gboolean flatpak_mtree_create_dir (OstreeRepo *repo, - OstreeMutableTree *parent, - const char *name, - OstreeMutableTree **dir_out, - GError **error); - -GVariant *flatpak_bundle_load (GFile *file, - char **commit, - FlatpakDecomposed **ref, - char **origin, - char **runtime_repo, - char **app_metadata, - guint64 *installed_size, - GBytes **gpg_keys, - char **collection_id, - GError **error); - -gboolean flatpak_pull_from_bundle (OstreeRepo *repo, - GFile *file, - const char *remote, - const char *ref, - gboolean require_gpg_signature, - GCancellable *cancellable, - GError **error); - -typedef void (*FlatpakOciPullProgress) (guint64 total_size, - guint64 pulled_size, - guint32 n_layers, - guint32 pulled_layers, - gpointer data); - -char * flatpak_pull_from_oci (OstreeRepo *repo, - FlatpakOciRegistry *registry, - const char *oci_repository, - const char *digest, - const char *delta_url, - FlatpakOciManifest *manifest, - FlatpakOciImage *image_config, - const char *remote, - const char *ref, - FlatpakPullFlags flags, - FlatpakOciPullProgress progress_cb, - gpointer progress_data, - GCancellable *cancellable, - GError **error); - -gboolean flatpak_mirror_image_from_oci (FlatpakOciRegistry *dst_registry, - FlatpakOciRegistry *registry, - const char *oci_repository, - const char *digest, - const char *remote, - const char *ref, - const char *delta_url, - OstreeRepo *repo, - FlatpakOciPullProgress progress_cb, - gpointer progress_data, - GCancellable *cancellable, - GError **error); - -typedef struct -{ - char *id; - char *installed_id; - char *commit; - FlatpakDecomposed *ref; - char *directory; - char *files_path; - char *subdir_suffix; - char *add_ld_path; - char **merge_dirs; - int priority; - gboolean needs_tmpfs; - gboolean is_unmaintained; -} FlatpakExtension; - -void flatpak_extension_free (FlatpakExtension *extension); - void flatpak_parse_extension_with_tag (const char *extension, char **name, char **tag); -GList *flatpak_list_extensions (GKeyFile *metakey, - const char *arch, - const char *branch); -#endif - gboolean flatpak_argument_needs_quoting (const char *arg); char * flatpak_quote_argv (const char *argv[], gssize len); -#if 0 gboolean flatpak_file_arg_has_suffix (const char *arg, const char *suffix); -#endif const char *flatpak_file_get_path_cached (GFile *file); -#if 0 GFile *flatpak_build_file_va (GFile *base, va_list args); GFile *flatpak_build_file (GFile *base, @@ -477,13 +207,11 @@ gboolean flatpak_cp_a (GFile *src, FlatpakCpFlags flags, GCancellable *cancellable, GError **error); -#endif gboolean flatpak_mkdir_p (GFile *dir, GCancellable *cancellable, GError **error); -#if 0 gboolean flatpak_rm_rf (GFile *dir, GCancellable *cancellable, GError **error); @@ -505,7 +233,6 @@ gboolean flatpak_open_in_tmpdir_at (int tmpdir_fd, GOutputStream **out_stream, GCancellable *cancellable, GError **error); -#endif gboolean flatpak_buffer_to_sealed_memfd_or_tmpfile (GLnxTmpfile *tmpf, const char *name, @@ -513,7 +240,6 @@ gboolean flatpak_buffer_to_sealed_memfd_or_tmpfile (GLnxTmpfile *tmpf, size_t len, GError **error); -#if 0 static inline void flatpak_temp_dir_destroy (void *p) { @@ -528,7 +254,6 @@ flatpak_temp_dir_destroy (void *p) typedef GFile FlatpakTempDir; G_DEFINE_AUTOPTR_CLEANUP_FUNC (FlatpakTempDir, flatpak_temp_dir_destroy) -#endif typedef GMainContext GMainContextPopDefault; static inline void @@ -558,97 +283,8 @@ flatpak_main_context_new_default (void) G_DEFINE_AUTOPTR_CLEANUP_FUNC (GMainContextPopDefault, flatpak_main_context_pop_default_destroy) -#if 0 -typedef OstreeRepo FlatpakRepoTransaction; - -static inline void -flatpak_repo_transaction_cleanup (void *p) -{ - OstreeRepo *repo = p; - - if (repo) - { - g_autoptr(GError) error = NULL; - if (!ostree_repo_abort_transaction (repo, NULL, &error)) - g_warning ("Error aborting ostree transaction: %s", error->message); - g_object_unref (repo); - } -} - -static inline FlatpakRepoTransaction * -flatpak_repo_transaction_start (OstreeRepo *repo, - GCancellable *cancellable, - GError **error) -{ - if (!ostree_repo_prepare_transaction (repo, NULL, cancellable, error)) - return NULL; - return (FlatpakRepoTransaction *) g_object_ref (repo); -} -G_DEFINE_AUTOPTR_CLEANUP_FUNC (FlatpakRepoTransaction, flatpak_repo_transaction_cleanup) - #define AUTOLOCK(name) G_GNUC_UNUSED __attribute__((cleanup (flatpak_auto_unlock_helper))) GMutex * G_PASTE (auto_unlock, __LINE__) = flatpak_auto_lock_helper (&G_LOCK_NAME (name)) -/* This uses a weird Auto prefix to avoid conflicts with later added autogenerated autoptr support, per: - * https://git.gnome.org/browse/glib/commit/?id=1c6cd5f0a3104aa9b62c7f1d3086181f63e71b59 - */ -typedef FlatpakSessionHelper AutoFlatpakSessionHelper; -G_DEFINE_AUTOPTR_CLEANUP_FUNC (AutoFlatpakSessionHelper, g_object_unref) -typedef XdpDbusDocuments AutoXdpDbusDocuments; -G_DEFINE_AUTOPTR_CLEANUP_FUNC (AutoXdpDbusDocuments, g_object_unref) - -typedef struct FlatpakXml FlatpakXml; - -struct FlatpakXml -{ - gchar *element_name; /* NULL == text */ - char **attribute_names; - char **attribute_values; - char *text; - FlatpakXml *parent; - FlatpakXml *first_child; - FlatpakXml *last_child; - FlatpakXml *next_sibling; -}; - -FlatpakXml *flatpak_xml_new (const gchar *element_name); -FlatpakXml *flatpak_xml_new_text (const gchar *text); -void flatpak_xml_add (FlatpakXml *parent, - FlatpakXml *node); -void flatpak_xml_free (FlatpakXml *node); -FlatpakXml *flatpak_xml_parse (GInputStream * in, - gboolean compressed, - GCancellable *cancellable, - GError **error); -void flatpak_xml_to_string (FlatpakXml *node, - GString *res); -FlatpakXml *flatpak_xml_unlink (FlatpakXml *node, - FlatpakXml *prev_sibling); -FlatpakXml *flatpak_xml_find (FlatpakXml *node, - const char *type, - FlatpakXml **prev_child_out); - -G_DEFINE_AUTOPTR_CLEANUP_FUNC (FlatpakXml, flatpak_xml_free); - -FlatpakXml *flatpak_appstream_xml_new (void); -gboolean flatpak_appstream_xml_migrate (FlatpakXml *source, - FlatpakXml *dest, - const char *ref, - const char *id, - GKeyFile *metadata); -gboolean flatpak_appstream_xml_root_to_data (FlatpakXml *appstream_root, - GBytes **uncompressed, - GBytes **compressed, - GError **error); -gboolean flatpak_repo_generate_appstream (OstreeRepo *repo, - const char **gpg_key_ids, - const char *gpg_homedir, - guint64 timestamp, - GCancellable *cancellable, - GError **error); -void flatpak_appstream_xml_filter (FlatpakXml *appstream, - GRegex *allow_refs, - GRegex *deny_refs); - char * flatpak_filter_glob_to_regexp (const char *glob, gboolean runtime_only, GError **error); gboolean flatpak_parse_filters (const char *data, GRegex **allow_refs_out, @@ -668,22 +304,6 @@ gboolean flatpak_allocate_tmpdir (int tmpdir_dfd, GCancellable *cancellable, GError **error); -static inline void -flatpak_ostree_progress_finish (OstreeAsyncProgress *progress) -{ - if (progress != NULL) - { - ostree_async_progress_finish (progress); - g_object_unref (progress); - } -} - -typedef OstreeAsyncProgress OstreeAsyncProgressFinish; -G_DEFINE_AUTOPTR_CLEANUP_FUNC (OstreeAsyncProgressFinish, flatpak_ostree_progress_finish); - - -void flatpak_log_dir_access (FlatpakDir *dir); - gboolean flatpak_check_required_version (const char *ref, GKeyFile *metakey, GError **error); @@ -697,15 +317,6 @@ char * flatpak_dconf_path_for_app_id (const char *app_id); gboolean flatpak_dconf_path_is_similar (const char *path1, const char *path2); -gboolean flatpak_repo_resolve_rev (OstreeRepo *repo, - const char *collection_id, /* nullable */ - const char *remote_name, /* nullable */ - const char *ref_name, - gboolean allow_noent, - char **out_rev, - GCancellable *cancellable, - GError **error); - static inline void null_safe_g_ptr_array_unref (gpointer data) { @@ -715,12 +326,10 @@ null_safe_g_ptr_array_unref (gpointer data) GStrv flatpak_parse_env_block (const char *data, gsize length, GError **error); -#endif int flatpak_envp_cmp (const void *p1, const void *p2); -#if 0 gboolean flatpak_str_is_integer (const char *s); gboolean flatpak_uri_equal (const char *uri1, @@ -734,8 +343,6 @@ typedef enum { char * flatpak_escape_string (const char *s, FlatpakEscapeFlags flags); -void flatpak_print_escaped_string (const char *s, - FlatpakEscapeFlags flags); gboolean flatpak_validate_path_characters (const char *path, GError **error); @@ -743,6 +350,5 @@ gboolean flatpak_validate_path_characters (const char *path, gboolean running_under_sudo (void); #define FLATPAK_MESSAGE_ID "c7b39b1e006b464599465e105b361485" -#endif #endif /* __FLATPAK_UTILS_H__ */ diff --git a/pressure-vessel/flatpak-utils.c b/pressure-vessel/flatpak-utils.c index 2b8bb6097dd567c4fd13584d26359723712e8fd9..d7e2169df041673ba3d8d1110d342f49d011bf3d 100644 --- a/pressure-vessel/flatpak-utils.c +++ b/pressure-vessel/flatpak-utils.c @@ -1,6 +1,6 @@ /* vi:set et sw=2 sts=2 cin cino=t0,f0,(0,{s,>2s,n-s,^-s,e-s: - * A cut-down version of common/flatpak-utils from Flatpak - * Last updated: Flatpak 1.15.8 + * Taken from Flatpak + * Last updated: Flatpak 1.15.9 * * Copyright © 1995-1998 Free Software Foundation, Inc. * Copyright © 2014-2019 Red Hat, Inc @@ -47,32 +47,16 @@ #include <glib.h> #include <gio/gunixoutputstream.h> -#include <gio/gunixinputstream.h> -#if 0 -#include "flatpak-dir-private.h" -#endif #include "flatpak-error.h" -#if 0 -#include "flatpak-oci-registry-private.h" -#include "flatpak-progress-private.h" -#endif -#include "flatpak-run-private.h" #include "flatpak-utils-base-private.h" #include "flatpak-utils-private.h" -#if 0 -#include "flatpak-variant-impl-private.h" -#endif #include "libglnx.h" #if 0 #include "valgrind-private.h" -#endif - -/* To keep this more similar to the original file, we explicitly disable - * this warning rather than fixing it */ -#pragma GCC diagnostic ignored "-Wshadow" - +#else #define RUNNING_ON_VALGRIND 0 +#endif /* This is also here so the common code can report these errors to the lib */ static const GDBusErrorEntry flatpak_error_entries[] = { @@ -103,21 +87,6 @@ static const GDBusErrorEntry flatpak_error_entries[] = { {FLATPAK_ERROR_NOT_AUTHORIZED, "org.freedesktop.Flatpak.Error.NotAuthorized"}, /* Since: 1.7.3 */ }; -#if 0 - -typedef struct archive FlatpakAutoArchiveRead; -G_DEFINE_AUTOPTR_CLEANUP_FUNC (FlatpakAutoArchiveRead, archive_read_free) - -static void -propagate_libarchive_error (GError **error, - struct archive *a) -{ - g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, - "%s", archive_error_string (a)); -} - -#endif - GQuark flatpak_error_quark (void) { @@ -144,8 +113,6 @@ flatpak_fail_error (GError **error, FlatpakError code, const char *fmt, ...) return FALSE; } -#if 0 - GBytes * flatpak_zlib_compress_bytes (GBytes *bytes, int level, @@ -217,8 +184,6 @@ flatpak_read_stream (GInputStream *in, return g_memory_output_stream_steal_as_bytes (G_MEMORY_OUTPUT_STREAM (mem_stream)); } -#endif - gint flatpak_strcmp0_ptr (gconstpointer a, gconstpointer b) @@ -273,8 +238,6 @@ flatpak_has_path_prefix (const char *str, } } -#if 0 - /* Returns end of matching path prefix, or NULL if no match */ const char * flatpak_path_match_prefix (const char *pattern, @@ -351,7 +314,7 @@ static const char * flatpak_get_kernel_arch (void) { static struct utsname buf; - static char *arch = NULL; + static const char *arch = NULL; char *m; if (arch != NULL) @@ -455,7 +418,7 @@ static struct { "aarch64", "arm" }, }; -static const char * +const char * flatpak_get_compat_arch (const char *kernel_arch) { int i; @@ -651,6 +614,8 @@ flatpak_get_gtk_theme (void) return (const char *) gtk_theme; } +#if 0 +/* Not used in pressure-vessel, use _srt_check_bwrap() instead */ const char * flatpak_get_bwrap (void) { @@ -673,76 +638,7 @@ flatpak_bwrap_is_unprivileged (void) stat (path, &st) == 0 && (st.st_mode & S_ISUID) == 0; } - -gboolean -flatpak_get_allowed_exports (const char *source_path, - const char *app_id, - FlatpakContext *context, - char ***allowed_extensions_out, - char ***allowed_prefixes_out, - gboolean *require_exact_match_out) -{ - g_autoptr(GPtrArray) allowed_extensions = g_ptr_array_new_with_free_func (g_free); - g_autoptr(GPtrArray) allowed_prefixes = g_ptr_array_new_with_free_func (g_free); - gboolean require_exact_match = FALSE; - - g_ptr_array_add (allowed_prefixes, g_strdup_printf ("%s.*", app_id)); - - if (strcmp (source_path, "share/applications") == 0) - { - g_ptr_array_add (allowed_extensions, g_strdup (".desktop")); - } - else if (flatpak_has_path_prefix (source_path, "share/icons")) - { - g_ptr_array_add (allowed_extensions, g_strdup (".svgz")); - g_ptr_array_add (allowed_extensions, g_strdup (".png")); - g_ptr_array_add (allowed_extensions, g_strdup (".svg")); - g_ptr_array_add (allowed_extensions, g_strdup (".ico")); - } - else if (strcmp (source_path, "share/dbus-1/services") == 0) - { - g_auto(GStrv) owned_dbus_names = flatpak_context_get_session_bus_policy_allowed_own_names (context); - - g_ptr_array_add (allowed_extensions, g_strdup (".service")); - - for (GStrv iter = owned_dbus_names; *iter != NULL; ++iter) - g_ptr_array_add (allowed_prefixes, g_strdup (*iter)); - - /* We need an exact match with no extra garbage, because the filename refers to busnames - * and we can *only* match exactly these */ - require_exact_match = TRUE; - } - else if (strcmp (source_path, "share/gnome-shell/search-providers") == 0) - { - g_ptr_array_add (allowed_extensions, g_strdup (".ini")); - } - else if (strcmp (source_path, "share/mime/packages") == 0) - { - g_ptr_array_add (allowed_extensions, g_strdup (".xml")); - } - else if (strcmp (source_path, "share/metainfo") == 0 || - strcmp (source_path, "share/appdata") == 0) - { - g_ptr_array_add (allowed_extensions, g_strdup (".xml")); - } - else - return FALSE; - - g_ptr_array_add (allowed_extensions, NULL); - g_ptr_array_add (allowed_prefixes, NULL); - - if (allowed_extensions_out) - *allowed_extensions_out = (char **) g_ptr_array_free (g_steal_pointer (&allowed_extensions), FALSE); - - if (allowed_prefixes_out) - *allowed_prefixes_out = (char **) g_ptr_array_free (g_steal_pointer (&allowed_prefixes), FALSE); - - if (require_exact_match_out) - *require_exact_match_out = require_exact_match; - - return TRUE; -} - +#endif static char * line_get_word (char **line) @@ -975,297 +871,6 @@ flatpak_filters_allow_ref (GRegex *allow_refs, return FALSE; } -char ** -flatpak_list_deployed_refs (const char *type, - const char *name_prefix, - const char *arch, - const char *branch, - GCancellable *cancellable, - GError **error) -{ - gchar **ret = NULL; - g_autoptr(GPtrArray) names = NULL; - g_autoptr(GHashTable) hash = NULL; - g_autoptr(FlatpakDir) user_dir = NULL; - g_autoptr(GPtrArray) system_dirs = NULL; - int i; - - hash = g_hash_table_new_full ((GHashFunc)flatpak_decomposed_hash, (GEqualFunc)flatpak_decomposed_equal, (GDestroyNotify)flatpak_decomposed_unref, NULL); - - user_dir = flatpak_dir_get_user (); - system_dirs = flatpak_dir_get_system_list (cancellable, error); - if (system_dirs == NULL) - goto out; - - if (!flatpak_dir_collect_deployed_refs (user_dir, type, name_prefix, - arch, branch, hash, cancellable, - error)) - goto out; - - for (i = 0; i < system_dirs->len; i++) - { - FlatpakDir *system_dir = g_ptr_array_index (system_dirs, i); - if (!flatpak_dir_collect_deployed_refs (system_dir, type, name_prefix, - arch, branch, hash, cancellable, - error)) - goto out; - } - - names = g_ptr_array_new (); - - GLNX_HASH_TABLE_FOREACH (hash, FlatpakDecomposed *, ref) - { - g_ptr_array_add (names, flatpak_decomposed_dup_id (ref)); - } - - g_ptr_array_sort (names, flatpak_strcmp0_ptr); - g_ptr_array_add (names, NULL); - - ret = (char **) g_ptr_array_free (names, FALSE); - names = NULL; - -out: - return ret; -} - -char ** -flatpak_list_unmaintained_refs (const char *name_prefix, - const char *arch, - const char *branch, - GCancellable *cancellable, - GError **error) -{ - gchar **ret = NULL; - g_autoptr(GPtrArray) names = NULL; - g_autoptr(GHashTable) hash = NULL; - g_autoptr(FlatpakDir) user_dir = NULL; - const char *key; - GHashTableIter iter; - g_autoptr(GPtrArray) system_dirs = NULL; - int i; - - hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); - - user_dir = flatpak_dir_get_user (); - - if (!flatpak_dir_collect_unmaintained_refs (user_dir, name_prefix, - arch, branch, hash, cancellable, - error)) - return NULL; - - system_dirs = flatpak_dir_get_system_list (cancellable, error); - if (system_dirs == NULL) - return NULL; - - for (i = 0; i < system_dirs->len; i++) - { - FlatpakDir *system_dir = g_ptr_array_index (system_dirs, i); - - if (!flatpak_dir_collect_unmaintained_refs (system_dir, name_prefix, - arch, branch, hash, cancellable, - error)) - return NULL; - } - - names = g_ptr_array_new (); - g_hash_table_iter_init (&iter, hash); - while (g_hash_table_iter_next (&iter, (gpointer *) &key, NULL)) - g_ptr_array_add (names, g_strdup (key)); - - g_ptr_array_sort (names, flatpak_strcmp0_ptr); - g_ptr_array_add (names, NULL); - - ret = (char **) g_ptr_array_free (names, FALSE); - names = NULL; - - return ret; -} - -GFile * -flatpak_find_deploy_dir_for_ref (FlatpakDecomposed *ref, - FlatpakDir **dir_out, - GCancellable *cancellable, - GError **error) -{ - g_autoptr(FlatpakDir) user_dir = NULL; - g_autoptr(GPtrArray) system_dirs = NULL; - FlatpakDir *dir = NULL; - g_autoptr(GFile) deploy = NULL; - - user_dir = flatpak_dir_get_user (); - system_dirs = flatpak_dir_get_system_list (cancellable, error); - if (system_dirs == NULL) - return NULL; - - dir = user_dir; - deploy = flatpak_dir_get_if_deployed (dir, ref, NULL, cancellable); - if (deploy == NULL) - { - int i; - for (i = 0; deploy == NULL && i < system_dirs->len; i++) - { - dir = g_ptr_array_index (system_dirs, i); - deploy = flatpak_dir_get_if_deployed (dir, ref, NULL, cancellable); - if (deploy != NULL) - break; - } - } - - if (deploy == NULL) - { - flatpak_fail_error (error, FLATPAK_ERROR_NOT_INSTALLED, _("%s not installed"), flatpak_decomposed_get_ref (ref)); - return NULL; - } - - if (dir_out) - *dir_out = g_object_ref (dir); - return g_steal_pointer (&deploy); -} - -GFile * -flatpak_find_files_dir_for_ref (FlatpakDecomposed *ref, - GCancellable *cancellable, - GError **error) -{ - g_autoptr(GFile) deploy = NULL; - - deploy = flatpak_find_deploy_dir_for_ref (ref, NULL, cancellable, error); - if (deploy == NULL) - return NULL; - - return g_file_get_child (deploy, "files"); -} - -GFile * -flatpak_find_unmaintained_extension_dir_if_exists (const char *name, - const char *arch, - const char *branch, - GCancellable *cancellable) -{ - g_autoptr(FlatpakDir) user_dir = NULL; - g_autoptr(GFile) extension_dir = NULL; - g_autoptr(GError) local_error = NULL; - - user_dir = flatpak_dir_get_user (); - - extension_dir = flatpak_dir_get_unmaintained_extension_dir_if_exists (user_dir, name, arch, branch, cancellable); - if (extension_dir == NULL) - { - g_autoptr(GPtrArray) system_dirs = NULL; - int i; - - system_dirs = flatpak_dir_get_system_list (cancellable, &local_error); - if (system_dirs == NULL) - { - g_warning ("Could not get the system installations: %s", local_error->message); - return NULL; - } - - for (i = 0; i < system_dirs->len; i++) - { - FlatpakDir *system_dir = g_ptr_array_index (system_dirs, i); - extension_dir = flatpak_dir_get_unmaintained_extension_dir_if_exists (system_dir, name, arch, branch, cancellable); - if (extension_dir != NULL) - break; - } - } - - if (extension_dir == NULL) - return NULL; - - return g_steal_pointer (&extension_dir); -} - -FlatpakDecomposed * -flatpak_find_current_ref (const char *app_id, - GCancellable *cancellable, - GError **error) -{ - g_autoptr(FlatpakDecomposed) current_ref = NULL; - g_autoptr(FlatpakDir) user_dir = flatpak_dir_get_user (); - int i; - - current_ref = flatpak_dir_current_ref (user_dir, app_id, NULL); - if (current_ref == NULL) - { - g_autoptr(GPtrArray) system_dirs = NULL; - - system_dirs = flatpak_dir_get_system_list (cancellable, error); - if (system_dirs == NULL) - return FALSE; - - for (i = 0; i < system_dirs->len; i++) - { - FlatpakDir *dir = g_ptr_array_index (system_dirs, i); - current_ref = flatpak_dir_current_ref (dir, app_id, cancellable); - if (current_ref != NULL) - break; - } - } - - if (current_ref) - return g_steal_pointer (¤t_ref); - - flatpak_fail_error (error, FLATPAK_ERROR_NOT_INSTALLED, _("%s not installed"), app_id); - return NULL; -} - -FlatpakDeploy * -flatpak_find_deploy_for_ref_in (GPtrArray *dirs, - const char *ref_str, - const char *commit, - GCancellable *cancellable, - GError **error) -{ - FlatpakDeploy *deploy = NULL; - int i; - g_autoptr(GError) my_error = NULL; - - g_autoptr(FlatpakDecomposed) ref = flatpak_decomposed_new_from_ref (ref_str, error); - if (ref == NULL) - return NULL; - - for (i = 0; deploy == NULL && i < dirs->len; i++) - { - FlatpakDir *dir = g_ptr_array_index (dirs, i); - - flatpak_log_dir_access (dir); - g_clear_error (&my_error); - deploy = flatpak_dir_load_deployed (dir, ref, commit, cancellable, &my_error); - } - - if (deploy == NULL) - g_propagate_error (error, g_steal_pointer (&my_error)); - - return deploy; -} - -FlatpakDeploy * -flatpak_find_deploy_for_ref (const char *ref, - const char *commit, - FlatpakDir *opt_user_dir, - GCancellable *cancellable, - GError **error) -{ - g_autoptr(GPtrArray) dirs = NULL; - - dirs = flatpak_dir_get_system_list (cancellable, error); - if (dirs == NULL) - return NULL; - - /* If an custom dir was passed, use that instead of the user dir. - * This is used when running apply-extra-data where if the target - * is a custom installation location the regular user one may not - * have the (possibly just installed in this transaction) runtime. - */ - if (opt_user_dir) - g_ptr_array_insert (dirs, 0, g_object_ref (opt_user_dir)); - else - g_ptr_array_insert (dirs, 0, flatpak_dir_get_user ()); - - return flatpak_find_deploy_for_ref_in (dirs, ref, commit, cancellable, error); -} - static gboolean remove_dangling_symlinks (int parent_fd, const char *name, @@ -1402,8 +1007,6 @@ flatpak_switch_symlink_and_remove (const char *symlink_path, return flatpak_fail (error, "flatpak_switch_symlink_and_remove looped too many times"); } -#endif - gboolean flatpak_argument_needs_quoting (const char *arg) { @@ -1450,8 +1053,6 @@ flatpak_quote_argv (const char *argv[], return g_string_free (res, FALSE); } -#if 0 - /* This is useful, because it handles escaped characters in uris, and ? arguments at the end of the uri */ gboolean flatpak_file_arg_has_suffix (const char *arg, const char *suffix) @@ -1491,12 +1092,6 @@ flatpak_build_file (GFile *base, ...) return res; } -#endif - -#if !GLIB_CHECK_VERSION(2, 34, 0) -G_LOCK_DEFINE_STATIC (path_cached); -#endif - const char * flatpak_file_get_path_cached (GFile *file) { @@ -1506,7 +1101,6 @@ flatpak_file_get_path_cached (GFile *file) if (G_UNLIKELY (_file_path_quark == 0)) _file_path_quark = g_quark_from_static_string ("flatpak-file-path"); -#if GLIB_CHECK_VERSION(2, 34, 0) do { path = g_object_get_qdata ((GObject *) file, _file_path_quark); @@ -1523,30 +1117,10 @@ flatpak_file_get_path_cached (GFile *file) } } while (path == NULL); -#else - G_LOCK (path_cached); - { - path = g_object_get_qdata ((GObject *) file, _file_path_quark); - - if (path == NULL) - { - g_autofree char *new_path = g_file_get_path (file); - - path = new_path; - - if (new_path != NULL) - g_object_set_qdata_full ((GObject *) file, _file_path_quark, - g_steal_pointer (&new_path), g_free); - } - } - G_UNLOCK (path_cached); -#endif return path; } -#if 0 - gboolean flatpak_openat_noatime (int dfd, const char *name, @@ -1832,8 +1406,6 @@ flatpak_canonicalize_permissions (int parent_dfd, return _flatpak_canonicalize_permissions (parent_dfd, rel_path, TRUE, uid, gid, error); } -#endif - /* Make a directory, and its parent. Don't error if it already exists. * If you want a failure mode with EEXIST, use g_file_make_directory_with_parents. */ gboolean @@ -1848,8 +1420,6 @@ flatpak_mkdir_p (GFile *dir, error); } -#if 0 - gboolean flatpak_rm_rf (GFile *dir, GCancellable *cancellable, @@ -1879,8 +1449,6 @@ flatpak_file_rename (GFile *from, return TRUE; } -#endif - /* If memfd_create() is available, generate a sealed memfd with contents of * @str. Otherwise use an O_TMPFILE @tmpf in anonymous mode, write @str to * @tmpf, and lseek() back to the start. See also similar uses in e.g. @@ -1931,8 +1499,6 @@ flatpak_buffer_to_sealed_memfd_or_tmpfile (GLnxTmpfile *tmpf, return TRUE; } -#if 0 - gboolean flatpak_open_in_tmpdir_at (int tmpdir_fd, int mode, @@ -2033,5230 +1599,121 @@ flatpak_variant_save (GFile *dest, return TRUE; } -/* This special cases the ref lookup which by doing a - bsearch since the array is sorted */ +char * +flatpak_keyfile_get_string_non_empty (GKeyFile *keyfile, + const char *group, + const char *key) +{ + g_autofree char *value = NULL; + + value = g_key_file_get_string (keyfile, group, key, NULL); + if (value != NULL && *value == '\0') + g_clear_pointer (&value, g_free); + + return g_steal_pointer (&value); +} + gboolean -flatpak_var_ref_map_lookup_ref (VarRefMapRef ref_map, - const char *ref, - VarRefInfoRef *out_info) +flatpak_extension_matches_reason (const char *extension_id, + const char *reasons, + gboolean default_value) { - gsize imax, imin; - gsize imid; - gsize n; + const char *extension_basename; + g_auto(GStrv) reason_list = NULL; + size_t i; - g_return_val_if_fail (out_info != NULL, FALSE); + if (reasons == NULL || *reasons == 0) + return default_value; - n = var_ref_map_get_length (ref_map); - if (n == 0) + extension_basename = strrchr (extension_id, '.'); + if (extension_basename == NULL) return FALSE; + extension_basename += 1; - imax = n - 1; - imin = 0; - while (imax >= imin) - { - VarRefMapEntryRef entry; - const char *cur; - int cmp; - - imid = (imin + imax) / 2; + reason_list = g_strsplit (reasons, ";", -1); - entry = var_ref_map_get_at (ref_map, imid); - cur = var_ref_map_entry_get_ref (entry); + for (i = 0; reason_list[i]; ++i) + { + const char *reason = reason_list[i]; - cmp = strcmp (cur, ref); - if (cmp < 0) + if (strcmp (reason, "active-gl-driver") == 0) { - imin = imid + 1; + /* handled below */ + const char **gl_drivers = flatpak_get_gl_drivers (); + size_t j; + + for (j = 0; gl_drivers[j]; j++) + { + if (strcmp (gl_drivers[j], extension_basename) == 0) + return TRUE; + } } - else if (cmp > 0) + else if (strcmp (reason, "active-gtk-theme") == 0) { - if (imid == 0) - break; - imax = imid - 1; - } - else - { - *out_info = var_ref_map_entry_get_info (entry); - return TRUE; - } - } - - return FALSE; -} - -/* Find the list of refs which belong to the given @collection_id in @summary. - * If @collection_id is %NULL, the main refs list from the summary will be - * returned. If @collection_id doesn’t match any collection IDs in the summary - * file, %FALSE will be returned. */ -gboolean -flatpak_summary_find_ref_map (VarSummaryRef summary, - const char *collection_id, - VarRefMapRef *refs_out) -{ - VarMetadataRef metadata = var_summary_get_metadata (summary); - const char *summary_collection_id; - - summary_collection_id = var_metadata_lookup_string (metadata, "ostree.summary.collection-id", NULL); - - if (collection_id == NULL || g_strcmp0 (collection_id, summary_collection_id) == 0) - { - if (refs_out) - *refs_out = var_summary_get_ref_map (summary); - return TRUE; - } - else if (collection_id != NULL) - { - VarVariantRef collection_map_v; - if (var_metadata_lookup (metadata, "ostree.summary.collection-map", NULL, &collection_map_v)) - { - VarCollectionMapRef collection_map = var_collection_map_from_variant (collection_map_v); - return var_collection_map_lookup (collection_map, collection_id, NULL, refs_out); - } - } - - return FALSE; -} - -/* This matches all refs from @collection_id that have ref, followed by '.' as prefix */ -GPtrArray * -flatpak_summary_match_subrefs (GVariant *summary_v, - const char *collection_id, - FlatpakDecomposed *ref) -{ - GPtrArray *res = g_ptr_array_new_with_free_func ((GDestroyNotify)flatpak_decomposed_unref); - gsize n, i; - g_autofree char *parts_prefix = NULL; - g_autofree char *ref_prefix = NULL; - g_autofree char *ref_suffix = NULL; - VarSummaryRef summary; - VarRefMapRef ref_map; - - summary = var_summary_from_gvariant (summary_v); - - /* Work out which refs list to use, based on the @collection_id. */ - if (flatpak_summary_find_ref_map (summary, collection_id, &ref_map)) - { - /* Match against the refs. */ - g_autofree char *id = flatpak_decomposed_dup_id (ref); - g_autofree char *arch = flatpak_decomposed_dup_arch (ref); - g_autofree char *branch = flatpak_decomposed_dup_branch (ref); - parts_prefix = g_strconcat (id, ".", NULL); - - ref_prefix = g_strconcat (flatpak_decomposed_get_kind_str (ref), "/", NULL); - ref_suffix = g_strconcat ("/", arch, "/", branch, NULL); - - n = var_ref_map_get_length (ref_map); - for (i = 0; i < n; i++) - { - VarRefMapEntryRef entry = var_ref_map_get_at (ref_map, i); - const char *cur; - const char *id_start; - const char *id_suffix; - const char *id_end; - - cur = var_ref_map_entry_get_ref (entry); - - /* Must match type */ - if (!g_str_has_prefix (cur, ref_prefix)) - continue; - - /* Must match arch & branch */ - if (!g_str_has_suffix (cur, ref_suffix)) - continue; - - id_start = strchr (cur, '/'); - if (id_start == NULL) - continue; - id_start += 1; - - id_end = strchr (id_start, '/'); - if (id_end == NULL) - continue; - - /* But only prefix of id */ - if (!g_str_has_prefix (id_start, parts_prefix)) - continue; - - /* And no dots (we want to install prefix.$ID, but not prefix.$ID.Sources) */ - id_suffix = id_start + strlen (parts_prefix); - if (memchr (id_suffix, '.', id_end - id_suffix) != NULL) - continue; - - FlatpakDecomposed *d = flatpak_decomposed_new_from_ref (cur, NULL); - if (d) - g_ptr_array_add (res, d); - } - } - - return g_steal_pointer (&res); -} - -gboolean -flatpak_summary_lookup_ref (GVariant *summary_v, - const char *collection_id, - const char *ref, - char **out_checksum, - VarRefInfoRef *out_info) -{ - VarSummaryRef summary; - VarRefMapRef ref_map; - VarRefInfoRef info; - const guchar *checksum_bytes; - gsize checksum_bytes_len; - - summary = var_summary_from_gvariant (summary_v); - - /* Work out which refs list to use, based on the @collection_id. */ - if (!flatpak_summary_find_ref_map (summary, collection_id, &ref_map)) - return FALSE; - - if (!flatpak_var_ref_map_lookup_ref (ref_map, ref, &info)) - return FALSE; - - checksum_bytes = var_ref_info_peek_checksum (info, &checksum_bytes_len); - if (G_UNLIKELY (checksum_bytes_len != OSTREE_SHA256_DIGEST_LEN)) - return FALSE; - - if (out_checksum) - *out_checksum = ostree_checksum_from_bytes (checksum_bytes); - - if (out_info) - *out_info = info; - - return TRUE; -} - -char * -flatpak_keyfile_get_string_non_empty (GKeyFile *keyfile, - const char *group, - const char *key) -{ - g_autofree char *value = NULL; - - value = g_key_file_get_string (keyfile, group, key, NULL); - if (value != NULL && *value == '\0') - g_clear_pointer (&value, g_free); - - return g_steal_pointer (&value); -} - -GKeyFile * -flatpak_parse_repofile (const char *remote_name, - gboolean from_ref, - GKeyFile *keyfile, - GBytes **gpg_data_out, - GCancellable *cancellable, - GError **error) -{ - g_autoptr(GBytes) gpg_data = NULL; - g_autofree char *uri = NULL; - g_autofree char *title = NULL; - g_autofree char *gpg_key = NULL; - g_autofree char *collection_id = NULL; - g_autofree char *default_branch = NULL; - g_autofree char *comment = NULL; - g_autofree char *description = NULL; - g_autofree char *icon = NULL; - g_autofree char *homepage = NULL; - g_autofree char *filter = NULL; - g_autofree char *subset = NULL; - g_autofree char *authenticator_name = NULL; - gboolean nodeps; - const char *source_group; - g_autofree char *version = NULL; - - if (from_ref) - source_group = FLATPAK_REF_GROUP; - else - source_group = FLATPAK_REPO_GROUP; - - GKeyFile *config = g_key_file_new (); - g_autofree char *group = g_strdup_printf ("remote \"%s\"", remote_name); - - if (!g_key_file_has_group (keyfile, source_group)) - { - flatpak_fail_error (error, FLATPAK_ERROR_INVALID_DATA, _("Invalid %s: Missing group ‘%s’"), - from_ref ? ".flatpakref" : ".flatpakrepo", source_group); - return NULL; - } - - uri = g_key_file_get_string (keyfile, source_group, - FLATPAK_REPO_URL_KEY, NULL); - if (uri == NULL) - { - flatpak_fail_error (error, FLATPAK_ERROR_INVALID_DATA, _("Invalid %s: Missing key ‘%s’"), - from_ref ? ".flatpakref" : ".flatpakrepo", FLATPAK_REPO_URL_KEY); - return NULL; - } - - version = g_key_file_get_string (keyfile, FLATPAK_REPO_GROUP, - FLATPAK_REPO_VERSION_KEY, NULL); - if (version != NULL && strcmp (version, "1") != 0) - { - flatpak_fail_error (error, FLATPAK_ERROR_INVALID_DATA, - _("Invalid version %s, only 1 supported"), version); - return NULL; - } - - g_key_file_set_string (config, group, "url", uri); - - subset = g_key_file_get_locale_string (keyfile, source_group, - FLATPAK_REPO_SUBSET_KEY, NULL, NULL); - if (subset != NULL) - g_key_file_set_string (config, group, "xa.subset", subset); - - /* Don't use the title from flatpakref files; that's the title of the app */ - if (!from_ref) - title = g_key_file_get_locale_string (keyfile, FLATPAK_REPO_GROUP, - FLATPAK_REPO_TITLE_KEY, NULL, NULL); - if (title != NULL) - g_key_file_set_string (config, group, "xa.title", title); - - default_branch = g_key_file_get_locale_string (keyfile, source_group, - FLATPAK_REPO_DEFAULT_BRANCH_KEY, NULL, NULL); - if (default_branch != NULL) - g_key_file_set_string (config, group, "xa.default-branch", default_branch); - - nodeps = g_key_file_get_boolean (keyfile, source_group, - FLATPAK_REPO_NODEPS_KEY, NULL); - if (nodeps) - g_key_file_set_boolean (config, group, "xa.nodeps", TRUE); - - gpg_key = g_key_file_get_string (keyfile, source_group, - FLATPAK_REPO_GPGKEY_KEY, NULL); - if (gpg_key != NULL) - { - guchar *decoded; - gsize decoded_len; - - gpg_key = g_strstrip (gpg_key); - decoded = g_base64_decode (gpg_key, &decoded_len); - if (decoded_len < 10) /* Check some minimal size so we don't get crap */ - { - g_free (decoded); - flatpak_fail_error (error, FLATPAK_ERROR_INVALID_DATA, _("Invalid gpg key")); - return NULL; - } - - gpg_data = g_bytes_new_take (decoded, decoded_len); - g_key_file_set_boolean (config, group, "gpg-verify", TRUE); - } - else - { - g_key_file_set_boolean (config, group, "gpg-verify", FALSE); - } - - /* We have a hierarchy of keys for setting the collection ID, which all have - * the same effect. The only difference is which versions of Flatpak support - * them, and therefore what P2P implementation is enabled by them: - * DeploySideloadCollectionID: supported by Flatpak >= 1.12.8 (1.7.1 - * introduced sideload support but this key was added late) - * DeployCollectionID: supported by Flatpak >= 1.0.6 (but fully supported in - * >= 1.2.0) - * CollectionID: supported by Flatpak >= 0.9.8 - */ - collection_id = flatpak_keyfile_get_string_non_empty (keyfile, source_group, - FLATPAK_REPO_DEPLOY_SIDELOAD_COLLECTION_ID_KEY); - if (collection_id == NULL) - collection_id = flatpak_keyfile_get_string_non_empty (keyfile, source_group, - FLATPAK_REPO_DEPLOY_COLLECTION_ID_KEY); - if (collection_id == NULL) - collection_id = flatpak_keyfile_get_string_non_empty (keyfile, source_group, - FLATPAK_REPO_COLLECTION_ID_KEY); - if (collection_id != NULL) - { - if (gpg_key == NULL) - { - flatpak_fail_error (error, FLATPAK_ERROR_INVALID_DATA, _("Collection ID requires GPG key to be provided")); - return NULL; - } - - g_key_file_set_string (config, group, "collection-id", collection_id); - } - - g_key_file_set_boolean (config, group, "gpg-verify-summary", - (gpg_key != NULL)); - - authenticator_name = g_key_file_get_string (keyfile, FLATPAK_REPO_GROUP, - FLATPAK_REPO_AUTHENTICATOR_NAME_KEY, NULL); - if (authenticator_name) - g_key_file_set_string (config, group, "xa.authenticator-name", authenticator_name); - - if (g_key_file_has_key (keyfile, FLATPAK_REPO_GROUP, FLATPAK_REPO_AUTHENTICATOR_INSTALL_KEY, NULL)) - { - gboolean authenticator_install = g_key_file_get_boolean (keyfile, FLATPAK_REPO_GROUP, - FLATPAK_REPO_AUTHENTICATOR_INSTALL_KEY, NULL); - g_key_file_set_boolean (config, group, "xa.authenticator-install", authenticator_install); - } - - comment = g_key_file_get_string (keyfile, FLATPAK_REPO_GROUP, - FLATPAK_REPO_COMMENT_KEY, NULL); - if (comment) - g_key_file_set_string (config, group, "xa.comment", comment); - - description = g_key_file_get_string (keyfile, FLATPAK_REPO_GROUP, - FLATPAK_REPO_DESCRIPTION_KEY, NULL); - if (description) - g_key_file_set_string (config, group, "xa.description", description); - - icon = g_key_file_get_string (keyfile, FLATPAK_REPO_GROUP, - FLATPAK_REPO_ICON_KEY, NULL); - if (icon) - g_key_file_set_string (config, group, "xa.icon", icon); - - homepage = g_key_file_get_string (keyfile, FLATPAK_REPO_GROUP, - FLATPAK_REPO_HOMEPAGE_KEY, NULL); - if (homepage) - g_key_file_set_string (config, group, "xa.homepage", homepage); - - filter = g_key_file_get_string (keyfile, FLATPAK_REPO_GROUP, - FLATPAK_REPO_FILTER_KEY, NULL); - if (filter) - g_key_file_set_string (config, group, "xa.filter", filter); - else - g_key_file_set_string (config, group, "xa.filter", ""); /* Default to override any pre-existing filters */ - - *gpg_data_out = g_steal_pointer (&gpg_data); - - return g_steal_pointer (&config); -} - -gboolean -flatpak_repo_set_title (OstreeRepo *repo, - const char *title, - GError **error) -{ - g_autoptr(GKeyFile) config = NULL; - - config = ostree_repo_copy_config (repo); - - if (title) - g_key_file_set_string (config, "flatpak", "title", title); - else - g_key_file_remove_key (config, "flatpak", "title", NULL); - - if (!ostree_repo_write_config (repo, config, error)) - return FALSE; - - return TRUE; -} - -gboolean -flatpak_repo_set_comment (OstreeRepo *repo, - const char *comment, - GError **error) -{ - g_autoptr(GKeyFile) config = NULL; - - config = ostree_repo_copy_config (repo); - - if (comment) - g_key_file_set_string (config, "flatpak", "comment", comment); - else - g_key_file_remove_key (config, "flatpak", "comment", NULL); - - if (!ostree_repo_write_config (repo, config, error)) - return FALSE; - - return TRUE; -} - -gboolean -flatpak_repo_set_description (OstreeRepo *repo, - const char *description, - GError **error) -{ - g_autoptr(GKeyFile) config = NULL; - - config = ostree_repo_copy_config (repo); - - if (description) - g_key_file_set_string (config, "flatpak", "description", description); - else - g_key_file_remove_key (config, "flatpak", "description", NULL); - - if (!ostree_repo_write_config (repo, config, error)) - return FALSE; - - return TRUE; -} - - -gboolean -flatpak_repo_set_icon (OstreeRepo *repo, - const char *icon, - GError **error) -{ - g_autoptr(GKeyFile) config = NULL; - - config = ostree_repo_copy_config (repo); - - if (icon) - g_key_file_set_string (config, "flatpak", "icon", icon); - else - g_key_file_remove_key (config, "flatpak", "icon", NULL); - - if (!ostree_repo_write_config (repo, config, error)) - return FALSE; - - return TRUE; -} - -gboolean -flatpak_repo_set_homepage (OstreeRepo *repo, - const char *homepage, - GError **error) -{ - g_autoptr(GKeyFile) config = NULL; - - config = ostree_repo_copy_config (repo); - - if (homepage) - g_key_file_set_string (config, "flatpak", "homepage", homepage); - else - g_key_file_remove_key (config, "flatpak", "homepage", NULL); - - if (!ostree_repo_write_config (repo, config, error)) - return FALSE; - - return TRUE; -} - -gboolean -flatpak_repo_set_redirect_url (OstreeRepo *repo, - const char *redirect_url, - GError **error) -{ - g_autoptr(GKeyFile) config = NULL; - - config = ostree_repo_copy_config (repo); - - if (redirect_url) - g_key_file_set_string (config, "flatpak", "redirect-url", redirect_url); - else - g_key_file_remove_key (config, "flatpak", "redirect-url", NULL); - - if (!ostree_repo_write_config (repo, config, error)) - return FALSE; - - return TRUE; -} - -gboolean -flatpak_repo_set_authenticator_name (OstreeRepo *repo, - const char *authenticator_name, - GError **error) -{ - g_autoptr(GKeyFile) config = NULL; - - config = ostree_repo_copy_config (repo); - - if (authenticator_name) - g_key_file_set_string (config, "flatpak", "authenticator-name", authenticator_name); - else - g_key_file_remove_key (config, "flatpak", "authenticator-name", NULL); - - if (!ostree_repo_write_config (repo, config, error)) - return FALSE; - - return TRUE; -} - -gboolean -flatpak_repo_set_authenticator_install (OstreeRepo *repo, - gboolean authenticator_install, - GError **error) -{ - g_autoptr(GKeyFile) config = NULL; - - config = ostree_repo_copy_config (repo); - - g_key_file_set_boolean (config, "flatpak", "authenticator-install", authenticator_install); - - if (!ostree_repo_write_config (repo, config, error)) - return FALSE; - - return TRUE; -} - -gboolean -flatpak_repo_set_authenticator_option (OstreeRepo *repo, - const char *key, - const char *value, - GError **error) -{ - g_autoptr(GKeyFile) config = NULL; - g_autofree char *full_key = g_strdup_printf ("authenticator-options.%s", key); - - config = ostree_repo_copy_config (repo); - - if (value) - g_key_file_set_string (config, "flatpak", full_key, value); - else - g_key_file_remove_key (config, "flatpak", full_key, NULL); - - if (!ostree_repo_write_config (repo, config, error)) - return FALSE; - - return TRUE; -} - -gboolean -flatpak_repo_set_deploy_collection_id (OstreeRepo *repo, - gboolean deploy_collection_id, - GError **error) -{ - g_autoptr(GKeyFile) config = NULL; - - config = ostree_repo_copy_config (repo); - g_key_file_set_boolean (config, "flatpak", "deploy-collection-id", deploy_collection_id); - return ostree_repo_write_config (repo, config, error); -} - -gboolean -flatpak_repo_set_deploy_sideload_collection_id (OstreeRepo *repo, - gboolean deploy_collection_id, - GError **error) -{ - g_autoptr(GKeyFile) config = NULL; - - config = ostree_repo_copy_config (repo); - g_key_file_set_boolean (config, "flatpak", "deploy-sideload-collection-id", deploy_collection_id); - return ostree_repo_write_config (repo, config, error); -} - -gboolean -flatpak_repo_set_gpg_keys (OstreeRepo *repo, - GBytes *bytes, - GError **error) -{ - g_autoptr(GKeyFile) config = NULL; - g_autofree char *value_base64 = NULL; - - config = ostree_repo_copy_config (repo); - - value_base64 = g_base64_encode (g_bytes_get_data (bytes, NULL), g_bytes_get_size (bytes)); - - g_key_file_set_string (config, "flatpak", "gpg-keys", value_base64); - - if (!ostree_repo_write_config (repo, config, error)) - return FALSE; - - return TRUE; -} - -gboolean -flatpak_repo_set_default_branch (OstreeRepo *repo, - const char *branch, - GError **error) -{ - g_autoptr(GKeyFile) config = NULL; - - config = ostree_repo_copy_config (repo); - - if (branch) - g_key_file_set_string (config, "flatpak", "default-branch", branch); - else - g_key_file_remove_key (config, "flatpak", "default-branch", NULL); - - if (!ostree_repo_write_config (repo, config, error)) - return FALSE; - - return TRUE; -} - -gboolean -flatpak_repo_set_collection_id (OstreeRepo *repo, - const char *collection_id, - GError **error) -{ - g_autoptr(GKeyFile) config = NULL; - - if (!ostree_repo_set_collection_id (repo, collection_id, error)) - return FALSE; - - config = ostree_repo_copy_config (repo); - if (!ostree_repo_write_config (repo, config, error)) - return FALSE; - - return TRUE; -} - -gboolean -flatpak_repo_set_summary_history_length (OstreeRepo *repo, - guint length, - GError **error) -{ - g_autoptr(GKeyFile) config = NULL; - - config = ostree_repo_copy_config (repo); - - if (length) - g_key_file_set_integer (config, "flatpak", "summary-history-length", length); - else - g_key_file_remove_key (config, "flatpak", "summary-history-length", NULL); - - if (!ostree_repo_write_config (repo, config, error)) - return FALSE; - - return TRUE; -} - -guint -flatpak_repo_get_summary_history_length (OstreeRepo *repo) -{ - GKeyFile *config = ostree_repo_get_config (repo); - int length; - - length = g_key_file_get_integer (config, "flatpak", "sumary-history-length", NULL); - - if (length <= 0) - return FLATPAK_SUMMARY_HISTORY_LENGTH_DEFAULT; - - return length; -} - -GVariant * -flatpak_commit_get_extra_data_sources (GVariant *commitv, - GError **error) -{ - g_autoptr(GVariant) commit_metadata = NULL; - g_autoptr(GVariant) extra_data_sources = NULL; - - commit_metadata = g_variant_get_child_value (commitv, 0); - extra_data_sources = g_variant_lookup_value (commit_metadata, - "xa.extra-data-sources", - G_VARIANT_TYPE ("a(ayttays)")); - - if (extra_data_sources == NULL) - { - g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, - _("No extra data sources")); - return NULL; - } - - return g_steal_pointer (&extra_data_sources); -} - - -GVariant * -flatpak_repo_get_extra_data_sources (OstreeRepo *repo, - const char *rev, - GCancellable *cancellable, - GError **error) -{ - g_autoptr(GVariant) commitv = NULL; - - if (!ostree_repo_load_variant (repo, - OSTREE_OBJECT_TYPE_COMMIT, - rev, &commitv, error)) - return NULL; - - return flatpak_commit_get_extra_data_sources (commitv, error); -} - -void -flatpak_repo_parse_extra_data_sources (GVariant *extra_data_sources, - int index, - const char **name, - guint64 *download_size, - guint64 *installed_size, - const guchar **sha256, - const char **uri) -{ - g_autoptr(GVariant) sha256_v = NULL; - g_variant_get_child (extra_data_sources, index, "(^aytt@ay&s)", - name, - download_size, - installed_size, - &sha256_v, - uri); - - if (download_size) - *download_size = GUINT64_FROM_BE (*download_size); - - if (installed_size) - *installed_size = GUINT64_FROM_BE (*installed_size); - - if (sha256) - *sha256 = ostree_checksum_bytes_peek (sha256_v); -} - -#define OSTREE_GIO_FAST_QUERYINFO ("standard::name,standard::type,standard::size,standard::is-symlink,standard::symlink-target," \ - "unix::device,unix::inode,unix::mode,unix::uid,unix::gid,unix::rdev") - -static gboolean -_flatpak_repo_collect_sizes (OstreeRepo *repo, - GFile *file, - GFileInfo *file_info, - guint64 *installed_size, - guint64 *download_size, - GCancellable *cancellable, - GError **error) -{ - g_autoptr(GFileEnumerator) dir_enum = NULL; - GFileInfo *child_info_tmp; - g_autoptr(GError) temp_error = NULL; - - if (file_info != NULL && g_file_info_get_file_type (file_info) == G_FILE_TYPE_REGULAR) - { - const char *checksum = ostree_repo_file_get_checksum (OSTREE_REPO_FILE (file)); - guint64 obj_size; - guint64 file_size = g_file_info_get_size (file_info); - - if (installed_size) - *installed_size += ((file_size + 511) / 512) * 512; - - if (download_size) - { - g_autoptr(GInputStream) input = NULL; - GInputStream *base_input; - g_autoptr(GError) local_error = NULL; - - if (!ostree_repo_query_object_storage_size (repo, - OSTREE_OBJECT_TYPE_FILE, checksum, - &obj_size, cancellable, &local_error)) - { - int fd; - struct stat stbuf; - - /* Ostree does not look at the staging directory when querying storage - size, so may return a NOT_FOUND error here. We work around this - by loading the object and walking back until we find the original - fd which we can fstat(). */ - if (!g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) - return FALSE; - - if (!ostree_repo_load_file (repo, checksum, &input, NULL, NULL, NULL, error)) - return FALSE; - - base_input = input; - while (G_IS_FILTER_INPUT_STREAM (base_input)) - base_input = g_filter_input_stream_get_base_stream (G_FILTER_INPUT_STREAM (base_input)); - - if (!G_IS_UNIX_INPUT_STREAM (base_input)) - return flatpak_fail (error, "Unable to find size of commit %s, not an unix stream", checksum); - - fd = g_unix_input_stream_get_fd (G_UNIX_INPUT_STREAM (base_input)); - - if (fstat (fd, &stbuf) != 0) - return glnx_throw_errno_prefix (error, "Can't find commit size: "); - - obj_size = stbuf.st_size; - } - - *download_size += obj_size; - } - } - - if (file_info == NULL || g_file_info_get_file_type (file_info) == G_FILE_TYPE_DIRECTORY) - { - dir_enum = g_file_enumerate_children (file, OSTREE_GIO_FAST_QUERYINFO, - G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, - cancellable, error); - if (!dir_enum) - return FALSE; - - - while ((child_info_tmp = g_file_enumerator_next_file (dir_enum, cancellable, &temp_error))) - { - g_autoptr(GFileInfo) child_info = child_info_tmp; - const char *name = g_file_info_get_name (child_info); - g_autoptr(GFile) child = g_file_get_child (file, name); - - if (!_flatpak_repo_collect_sizes (repo, child, child_info, installed_size, download_size, cancellable, error)) - return FALSE; - } - } - - return TRUE; -} - -gboolean -flatpak_repo_collect_sizes (OstreeRepo *repo, - GFile *root, - guint64 *installed_size, - guint64 *download_size, - GCancellable *cancellable, - GError **error) -{ - /* Initialize the sums */ - if (installed_size) - *installed_size = 0; - if (download_size) - *download_size = 0; - return _flatpak_repo_collect_sizes (repo, root, NULL, installed_size, download_size, cancellable, error); -} - - -static void -flatpak_repo_collect_extra_data_sizes (OstreeRepo *repo, - const char *rev, - guint64 *installed_size, - guint64 *download_size) -{ - g_autoptr(GVariant) extra_data_sources = NULL; - gsize n_extra_data; - int i; - - extra_data_sources = flatpak_repo_get_extra_data_sources (repo, rev, NULL, NULL); - if (extra_data_sources == NULL) - return; - - n_extra_data = g_variant_n_children (extra_data_sources); - if (n_extra_data == 0) - return; - - for (i = 0; i < n_extra_data; i++) - { - guint64 extra_download_size; - guint64 extra_installed_size; - - flatpak_repo_parse_extra_data_sources (extra_data_sources, i, - NULL, - &extra_download_size, - &extra_installed_size, - NULL, NULL); - if (installed_size) - *installed_size += extra_installed_size; - if (download_size) - *download_size += extra_download_size; - } -} - -/* Loads the old compat summary file from a local repo */ -GVariant * -flatpak_repo_load_summary (OstreeRepo *repo, - GError **error) -{ - glnx_autofd int fd = -1; - g_autoptr(GMappedFile) mfile = NULL; - g_autoptr(GBytes) bytes = NULL; - - fd = openat (ostree_repo_get_dfd (repo), "summary", O_RDONLY | O_CLOEXEC); - if (fd < 0) - { - glnx_set_error_from_errno (error); - return NULL; - } - - mfile = g_mapped_file_new_from_fd (fd, FALSE, error); - if (!mfile) - return NULL; - - bytes = g_mapped_file_get_bytes (mfile); - - return g_variant_ref_sink (g_variant_new_from_bytes (OSTREE_SUMMARY_GVARIANT_FORMAT, bytes, TRUE)); -} - -GVariant * -flatpak_repo_load_summary_index (OstreeRepo *repo, - GError **error) -{ - glnx_autofd int fd = -1; - g_autoptr(GMappedFile) mfile = NULL; - g_autoptr(GBytes) bytes = NULL; - - fd = openat (ostree_repo_get_dfd (repo), "summary.idx", O_RDONLY | O_CLOEXEC); - if (fd < 0) - { - glnx_set_error_from_errno (error); - return NULL; - } - - mfile = g_mapped_file_new_from_fd (fd, FALSE, error); - if (!mfile) - return NULL; - - bytes = g_mapped_file_get_bytes (mfile); - - return g_variant_ref_sink (g_variant_new_from_bytes (FLATPAK_SUMMARY_INDEX_GVARIANT_FORMAT, bytes, TRUE)); -} - -static gboolean -flatpak_repo_save_compat_summary (OstreeRepo *repo, - GVariant *summary, - time_t *out_old_sig_mtime, - GCancellable *cancellable, - GError **error) -{ - int repo_dfd = ostree_repo_get_dfd (repo); - struct stat stbuf; - time_t old_sig_mtime = 0; - GLnxFileReplaceFlags flags; - - flags = GLNX_FILE_REPLACE_INCREASING_MTIME; - if (ostree_repo_get_disable_fsync (repo)) - flags |= GLNX_FILE_REPLACE_NODATASYNC; - else - flags |= GLNX_FILE_REPLACE_DATASYNC_NEW; - - if (!glnx_file_replace_contents_at (repo_dfd, "summary", - g_variant_get_data (summary), - g_variant_get_size (summary), - flags, - cancellable, error)) - return FALSE; - - if (fstatat (repo_dfd, "summary.sig", &stbuf, AT_SYMLINK_NOFOLLOW) == 0) - old_sig_mtime = stbuf.st_mtime; - - if (unlinkat (repo_dfd, "summary.sig", 0) != 0 && - G_UNLIKELY (errno != ENOENT)) - { - glnx_set_error_from_errno (error); - return FALSE; - } - - *out_old_sig_mtime = old_sig_mtime; - return TRUE; -} - -static gboolean -flatpak_repo_save_summary_index (OstreeRepo *repo, - GVariant *index, - const char *index_digest, - GBytes *index_sig, - GCancellable *cancellable, - GError **error) -{ - int repo_dfd = ostree_repo_get_dfd (repo); - GLnxFileReplaceFlags flags; - - if (index == NULL) - { - if (unlinkat (repo_dfd, "summary.idx", 0) != 0 && - G_UNLIKELY (errno != ENOENT)) - { - glnx_set_error_from_errno (error); - return FALSE; - } - if (unlinkat (repo_dfd, "summary.idx.sig", 0) != 0 && - G_UNLIKELY (errno != ENOENT)) - { - glnx_set_error_from_errno (error); - return FALSE; - } - - return TRUE; - } - - flags = GLNX_FILE_REPLACE_INCREASING_MTIME; - if (ostree_repo_get_disable_fsync (repo)) - flags |= GLNX_FILE_REPLACE_NODATASYNC; - else - flags |= GLNX_FILE_REPLACE_DATASYNC_NEW; - - if (index_sig) - { - g_autofree char *path = g_strconcat ("summaries/", index_digest, ".idx.sig", NULL); - - if (!glnx_shutil_mkdir_p_at (repo_dfd, "summaries", - 0775, cancellable, error)) - return FALSE; - - if (!glnx_file_replace_contents_at (repo_dfd, path, - g_bytes_get_data (index_sig, NULL), - g_bytes_get_size (index_sig), - flags, - cancellable, error)) - return FALSE; - } - - if (!glnx_file_replace_contents_at (repo_dfd, "summary.idx", - g_variant_get_data (index), - g_variant_get_size (index), - flags, - cancellable, error)) - return FALSE; - - /* Update the non-indexed summary.idx.sig file that was introduced in 1.9.1 but - * was made unnecessary in 1.9.3. Lets keep it for a while until everyone updates - */ - if (index_sig) - { - if (!glnx_file_replace_contents_at (repo_dfd, "summary.idx.sig", - g_bytes_get_data (index_sig, NULL), - g_bytes_get_size (index_sig), - flags, - cancellable, error)) - return FALSE; - } - else - { - if (unlinkat (repo_dfd, "summary.idx.sig", 0) != 0 && - G_UNLIKELY (errno != ENOENT)) - { - glnx_set_error_from_errno (error); - return FALSE; - } - } - - return TRUE; -} - -GVariant * -flatpak_repo_load_digested_summary (OstreeRepo *repo, - const char *digest, - GError **error) -{ - glnx_autofd int fd = -1; - g_autoptr(GMappedFile) mfile = NULL; - g_autoptr(GBytes) bytes = NULL; - g_autoptr(GBytes) compressed_bytes = NULL; - g_autofree char *path = NULL; - g_autofree char *filename = NULL; - - filename = g_strconcat (digest, ".gz", NULL); - path = g_build_filename ("summaries", filename, NULL); - - fd = openat (ostree_repo_get_dfd (repo), path, O_RDONLY | O_CLOEXEC); - if (fd < 0) - { - glnx_set_error_from_errno (error); - return NULL; - } - - mfile = g_mapped_file_new_from_fd (fd, FALSE, error); - if (!mfile) - return NULL; - - compressed_bytes = g_mapped_file_get_bytes (mfile); - bytes = flatpak_zlib_decompress_bytes (compressed_bytes, error); - if (bytes == NULL) - return NULL; - - return g_variant_ref_sink (g_variant_new_from_bytes (OSTREE_SUMMARY_GVARIANT_FORMAT, bytes, TRUE)); -} - -static char * -flatpak_repo_save_digested_summary (OstreeRepo *repo, - const char *name, - GVariant *summary, - GCancellable *cancellable, - GError **error) -{ - int repo_dfd = ostree_repo_get_dfd (repo); - g_autofree char *digest = NULL; - g_autofree char *filename = NULL; - g_autofree char *path = NULL; - g_autoptr(GBytes) data = NULL; - g_autoptr(GBytes) compressed_data = NULL; - struct stat stbuf; - - if (!glnx_shutil_mkdir_p_at (repo_dfd, "summaries", - 0775, - cancellable, - error)) - return NULL; - - digest = g_compute_checksum_for_data (G_CHECKSUM_SHA256, - g_variant_get_data (summary), - g_variant_get_size (summary)); - filename = g_strconcat (digest, ".gz", NULL); - - path = g_build_filename ("summaries", filename, NULL); - - /* Check for pre-existing (non-truncated) copy and avoid re-writing it */ - if (fstatat (repo_dfd, path, &stbuf, 0) == 0 && - stbuf.st_size != 0) - { - g_info ("Reusing digested summary at %s for %s", path, name); - return g_steal_pointer (&digest); - } - - data = g_variant_get_data_as_bytes (summary); - compressed_data = flatpak_zlib_compress_bytes (data, -1, error); - if (compressed_data == NULL) - return NULL; - - if (!glnx_file_replace_contents_at (repo_dfd, path, - g_bytes_get_data (compressed_data, NULL), - g_bytes_get_size (compressed_data), - ostree_repo_get_disable_fsync (repo) ? GLNX_FILE_REPLACE_NODATASYNC : GLNX_FILE_REPLACE_DATASYNC_NEW, - cancellable, error)) - return NULL; - - g_info ("Wrote digested summary at %s for %s", path, name); - return g_steal_pointer (&digest); -} - -static gboolean -flatpak_repo_save_digested_summary_delta (OstreeRepo *repo, - const char *from_digest, - const char *to_digest, - GBytes *delta, - GCancellable *cancellable, - GError **error) -{ - int repo_dfd = ostree_repo_get_dfd (repo); - g_autofree char *path = NULL; - g_autofree char *filename = g_strconcat (from_digest, "-", to_digest, ".delta", NULL); - struct stat stbuf; - - if (!glnx_shutil_mkdir_p_at (repo_dfd, "summaries", - 0775, - cancellable, - error)) - return FALSE; - - path = g_build_filename ("summaries", filename, NULL); - - /* Check for pre-existing copy of same size and avoid re-writing it */ - if (fstatat (repo_dfd, path, &stbuf, 0) == 0 && - stbuf.st_size == g_bytes_get_size (delta)) - { - g_info ("Reusing digested summary-diff for %s", filename); - return TRUE; - } - - if (!glnx_file_replace_contents_at (repo_dfd, path, - g_bytes_get_data (delta, NULL), - g_bytes_get_size (delta), - ostree_repo_get_disable_fsync (repo) ? GLNX_FILE_REPLACE_NODATASYNC : GLNX_FILE_REPLACE_DATASYNC_NEW, - cancellable, error)) - return FALSE; - - g_info ("Wrote digested summary delta at %s", path); - return TRUE; -} - - -typedef struct -{ - guint64 installed_size; - guint64 download_size; - char *metadata_contents; - GPtrArray *subsets; - GVariant *sparse_data; - gsize commit_size; - guint64 commit_timestamp; -} CommitData; - -static void -commit_data_free (gpointer data) -{ - CommitData *rev_data = data; - - if (rev_data->subsets) - g_ptr_array_unref (rev_data->subsets); - g_free (rev_data->metadata_contents); - if (rev_data->sparse_data) - g_variant_unref (rev_data->sparse_data); - g_free (rev_data); -} - -G_DEFINE_AUTOPTR_CLEANUP_FUNC (CommitData, commit_data_free); - -static GHashTable * -commit_data_cache_new (void) -{ - return g_hash_table_new_full (g_str_hash, g_str_equal, g_free, commit_data_free); -} - -static GHashTable * -populate_commit_data_cache (OstreeRepo *repo, - GVariant *index_v) -{ - - VarSummaryIndexRef index = var_summary_index_from_gvariant (index_v); - VarMetadataRef index_metadata = var_summary_index_get_metadata (index); - VarSummaryIndexSubsummariesRef subsummaries = var_summary_index_get_subsummaries (index); - gsize n_subsummaries = var_summary_index_subsummaries_get_length (subsummaries); - guint32 cache_version; - g_autoptr(GHashTable) commit_data_cache = commit_data_cache_new (); - - cache_version = GUINT32_FROM_LE (var_metadata_lookup_uint32 (index_metadata, "xa.cache-version", 0)); - if (cache_version < FLATPAK_XA_CACHE_VERSION) - { - /* Need to re-index to get all data */ - g_info ("Old summary cache version %d, not using cache", cache_version); - return NULL; - } - - for (gsize i = 0; i < n_subsummaries; i++) - { - VarSummaryIndexSubsummariesEntryRef entry = var_summary_index_subsummaries_get_at (subsummaries, i); - const char *name = var_summary_index_subsummaries_entry_get_key (entry); - const char *s; - g_autofree char *subset = NULL; - VarSubsummaryRef subsummary = var_summary_index_subsummaries_entry_get_value (entry); - gsize checksum_bytes_len; - const guchar *checksum_bytes; - g_autofree char *digest = NULL; - g_autoptr(GVariant) summary_v = NULL; - VarSummaryRef summary; - VarRefMapRef ref_map; - gsize n_refs; - - checksum_bytes = var_subsummary_peek_checksum (subsummary, &checksum_bytes_len); - if (G_UNLIKELY (checksum_bytes_len != OSTREE_SHA256_DIGEST_LEN)) - { - g_info ("Invalid checksum for digested summary, not using cache"); - return NULL; - } - digest = ostree_checksum_from_bytes (checksum_bytes); - - s = strrchr (name, '-'); - if (s != NULL) - subset = g_strndup (name, s - name); - else - subset = g_strdup (""); - - summary_v = flatpak_repo_load_digested_summary (repo, digest, NULL); - if (summary_v == NULL) - { - g_info ("Failed to load digested summary %s, not using cache", digest); - return NULL; - } - - /* Note that all summaries refered to by the index is in new format */ - summary = var_summary_from_gvariant (summary_v); - ref_map = var_summary_get_ref_map (summary); - n_refs = var_ref_map_get_length (ref_map); - for (gsize j = 0; j < n_refs; j++) - { - VarRefMapEntryRef e = var_ref_map_get_at (ref_map, j); - const char *ref = var_ref_map_entry_get_ref (e); - VarRefInfoRef info = var_ref_map_entry_get_info (e); - VarMetadataRef commit_metadata = var_ref_info_get_metadata (info); - guint64 commit_size = var_ref_info_get_commit_size (info); - const guchar *commit_bytes; - gsize commit_bytes_len; - g_autofree char *rev = NULL; - CommitData *rev_data; - VarVariantRef xa_data_v; - VarCacheDataRef xa_data; - - if (!flatpak_is_app_runtime_or_appstream_ref (ref)) - continue; - - commit_bytes = var_ref_info_peek_checksum (info, &commit_bytes_len); - if (G_UNLIKELY (commit_bytes_len != OSTREE_SHA256_DIGEST_LEN)) - continue; - - if (!var_metadata_lookup (commit_metadata, "xa.data", NULL, &xa_data_v) || - !var_variant_is_type (xa_data_v, G_VARIANT_TYPE ("(tts)"))) - { - g_info ("Missing xa.data for ref %s, not using cache", ref); - return NULL; - } - - xa_data = var_cache_data_from_variant (xa_data_v); - - rev = ostree_checksum_from_bytes (commit_bytes); - rev_data = g_hash_table_lookup (commit_data_cache, rev); - if (rev_data == NULL) - { - g_auto(GVariantBuilder) sparse_builder = FLATPAK_VARIANT_BUILDER_INITIALIZER; - g_variant_builder_init (&sparse_builder, G_VARIANT_TYPE_VARDICT); - gboolean has_sparse = FALSE; - - rev_data = g_new0 (CommitData, 1); - rev_data->installed_size = var_cache_data_get_installed_size (xa_data); - rev_data->download_size = var_cache_data_get_download_size (xa_data); - rev_data->metadata_contents = g_strdup (var_cache_data_get_metadata (xa_data)); - rev_data->commit_size = commit_size; - rev_data->commit_timestamp = GUINT64_FROM_BE (var_metadata_lookup_uint64 (commit_metadata, OSTREE_COMMIT_TIMESTAMP2, 0)); - - /* Get sparse data */ - gsize len = var_metadata_get_length (commit_metadata); - for (gsize k = 0; k < len; k++) - { - VarMetadataEntryRef m = var_metadata_get_at (commit_metadata, k); - const char *m_key = var_metadata_entry_get_key (m); - if (!g_str_has_prefix (m_key, "ot.") && - !g_str_has_prefix (m_key, "ostree.") && - strcmp (m_key, "xa.data") != 0) - { - VarVariantRef v = var_metadata_entry_get_value (m); - g_autoptr(GVariant) vv = g_variant_ref_sink (var_variant_dup_to_gvariant (v)); - g_autoptr(GVariant) child = g_variant_get_child_value (vv, 0); - g_variant_builder_add (&sparse_builder, "{sv}", m_key, child); - has_sparse = TRUE; - } - } - - if (has_sparse) - rev_data->sparse_data = g_variant_ref_sink (g_variant_builder_end (&sparse_builder)); - - g_hash_table_insert (commit_data_cache, g_strdup (rev), (CommitData *)rev_data); - } - - if (*subset != 0) - { - if (rev_data->subsets == NULL) - rev_data->subsets = g_ptr_array_new_with_free_func (g_free); - - if (!flatpak_g_ptr_array_contains_string (rev_data->subsets, subset)) - g_ptr_array_add (rev_data->subsets, g_strdup (subset)); - } - } - } - - return g_steal_pointer (&commit_data_cache); -} - -static CommitData * -read_commit_data (OstreeRepo *repo, - const char *ref, - const char *rev, - GCancellable *cancellable, - GError **error) -{ - g_autoptr(GFile) root = NULL; - g_autoptr(GFile) metadata = NULL; - guint64 installed_size = 0; - guint64 download_size = 0; - g_autofree char *metadata_contents = NULL; - g_autofree char *commit = NULL; - g_autoptr(GVariant) commit_v = NULL; - g_autoptr(GVariant) commit_metadata = NULL; - g_autoptr(GPtrArray) subsets = NULL; - CommitData *rev_data; - const char *eol = NULL; - const char *eol_rebase = NULL; - int token_type = -1; - g_autoptr(GVariant) extra_data_sources = NULL; - guint32 n_extra_data = 0; - guint64 total_extra_data_download_size = 0; - g_autoptr(GVariantIter) subsets_iter = NULL; - - if (!ostree_repo_read_commit (repo, rev, &root, &commit, NULL, error)) - return NULL; - - if (!ostree_repo_load_commit (repo, commit, &commit_v, NULL, error)) - return NULL; - - commit_metadata = g_variant_get_child_value (commit_v, 0); - if (!g_variant_lookup (commit_metadata, "xa.metadata", "s", &metadata_contents)) - { - metadata = g_file_get_child (root, "metadata"); - if (!g_file_load_contents (metadata, cancellable, &metadata_contents, NULL, NULL, NULL)) - metadata_contents = g_strdup (""); - } - - if (g_variant_lookup (commit_metadata, "xa.installed-size", "t", &installed_size) && - g_variant_lookup (commit_metadata, "xa.download-size", "t", &download_size)) - { - installed_size = GUINT64_FROM_BE (installed_size); - download_size = GUINT64_FROM_BE (download_size); - } - else - { - if (!flatpak_repo_collect_sizes (repo, root, &installed_size, &download_size, cancellable, error)) - return NULL; - } - - if (g_variant_lookup (commit_metadata, "xa.subsets", "as", &subsets_iter)) - { - const char *subset; - subsets = g_ptr_array_new_with_free_func (g_free); - while (g_variant_iter_next (subsets_iter, "&s", &subset)) - g_ptr_array_add (subsets, g_strdup (subset)); - } - - flatpak_repo_collect_extra_data_sizes (repo, rev, &installed_size, &download_size); - - rev_data = g_new0 (CommitData, 1); - rev_data->installed_size = installed_size; - rev_data->download_size = download_size; - rev_data->metadata_contents = g_steal_pointer (&metadata_contents); - rev_data->subsets = g_steal_pointer (&subsets); - rev_data->commit_size = g_variant_get_size (commit_v); - rev_data->commit_timestamp = ostree_commit_get_timestamp (commit_v); - - g_variant_lookup (commit_metadata, OSTREE_COMMIT_META_KEY_ENDOFLIFE, "&s", &eol); - g_variant_lookup (commit_metadata, OSTREE_COMMIT_META_KEY_ENDOFLIFE_REBASE, "&s", &eol_rebase); - if (g_variant_lookup (commit_metadata, "xa.token-type", "i", &token_type)) - token_type = GINT32_FROM_LE(token_type); - - extra_data_sources = flatpak_commit_get_extra_data_sources (commit_v, NULL); - if (extra_data_sources) - { - n_extra_data = g_variant_n_children (extra_data_sources); - for (int i = 0; i < n_extra_data; i++) - { - guint64 extra_download_size; - flatpak_repo_parse_extra_data_sources (extra_data_sources, i, - NULL, - &extra_download_size, - NULL, - NULL, - NULL); - total_extra_data_download_size += extra_download_size; - } - } - - if (eol || eol_rebase || token_type >= 0 || n_extra_data > 0) - { - g_auto(GVariantBuilder) sparse_builder = FLATPAK_VARIANT_BUILDER_INITIALIZER; - g_variant_builder_init (&sparse_builder, G_VARIANT_TYPE_VARDICT); - if (eol) - g_variant_builder_add (&sparse_builder, "{sv}", FLATPAK_SPARSE_CACHE_KEY_ENDOFLINE, g_variant_new_string (eol)); - if (eol_rebase) - g_variant_builder_add (&sparse_builder, "{sv}", FLATPAK_SPARSE_CACHE_KEY_ENDOFLINE_REBASE, g_variant_new_string (eol_rebase)); - if (token_type >= 0) - g_variant_builder_add (&sparse_builder, "{sv}", FLATPAK_SPARSE_CACHE_KEY_TOKEN_TYPE, g_variant_new_int32 (GINT32_TO_LE(token_type))); - if (n_extra_data > 0) - g_variant_builder_add (&sparse_builder, "{sv}", FLATPAK_SPARSE_CACHE_KEY_EXTRA_DATA_SIZE, - g_variant_new ("(ut)", GUINT32_TO_LE(n_extra_data), GUINT64_TO_LE(total_extra_data_download_size))); - - rev_data->sparse_data = g_variant_ref_sink (g_variant_builder_end (&sparse_builder)); - } - - return rev_data; -} - -static void -_ostree_parse_delta_name (const char *delta_name, - char **out_from, - char **out_to) -{ - g_auto(GStrv) parts = g_strsplit (delta_name, "-", 2); - - if (parts[0] && parts[1]) - { - *out_from = g_steal_pointer (&parts[0]); - *out_to = g_steal_pointer (&parts[1]); - } - else - { - *out_from = NULL; - *out_to = g_steal_pointer (&parts[0]); - } -} - -static GString * -static_delta_path_base (const char *dir, - const char *from, - const char *to) -{ - guint8 csum_to[OSTREE_SHA256_DIGEST_LEN]; - char to_b64[44]; - guint8 csum_to_copy[OSTREE_SHA256_DIGEST_LEN]; - GString *ret = g_string_new (dir); - - ostree_checksum_inplace_to_bytes (to, csum_to); - ostree_checksum_b64_inplace_from_bytes (csum_to, to_b64); - ostree_checksum_b64_inplace_to_bytes (to_b64, csum_to_copy); - - g_assert (memcmp (csum_to, csum_to_copy, OSTREE_SHA256_DIGEST_LEN) == 0); - - if (from != NULL) - { - guint8 csum_from[OSTREE_SHA256_DIGEST_LEN]; - char from_b64[44]; - - ostree_checksum_inplace_to_bytes (from, csum_from); - ostree_checksum_b64_inplace_from_bytes (csum_from, from_b64); - - g_string_append_c (ret, from_b64[0]); - g_string_append_c (ret, from_b64[1]); - g_string_append_c (ret, '/'); - g_string_append (ret, from_b64 + 2); - g_string_append_c (ret, '-'); - } - - g_string_append_c (ret, to_b64[0]); - g_string_append_c (ret, to_b64[1]); - if (from == NULL) - g_string_append_c (ret, '/'); - g_string_append (ret, to_b64 + 2); - - return ret; -} - -static char * -_ostree_get_relative_static_delta_path (const char *from, - const char *to, - const char *target) -{ - GString *ret = static_delta_path_base ("deltas/", from, to); - - if (target != NULL) - { - g_string_append_c (ret, '/'); - g_string_append (ret, target); - } - - return g_string_free (ret, FALSE); -} - -static char * -_ostree_get_relative_static_delta_superblock_path (const char *from, - const char *to) -{ - return _ostree_get_relative_static_delta_path (from, to, "superblock"); -} - -static GVariant * -_ostree_repo_static_delta_superblock_digest (OstreeRepo *repo, - const char *from, - const char *to, - GCancellable *cancellable, - GError **error) -{ - g_autofree char *superblock = _ostree_get_relative_static_delta_superblock_path ((from && from[0]) ? from : NULL, to); - glnx_autofd int fd = -1; - guint8 digest[OSTREE_SHA256_DIGEST_LEN]; - gsize len; - gpointer data = NULL; - - if (!glnx_openat_rdonly (ostree_repo_get_dfd (repo), superblock, TRUE, &fd, error)) - return NULL; - - g_autoptr(GBytes) superblock_content = glnx_fd_readall_bytes (fd, cancellable, error); - if (!superblock_content) - return NULL; - - g_autoptr(GChecksum) checksum = g_checksum_new (G_CHECKSUM_SHA256); - g_checksum_update (checksum, g_bytes_get_data (superblock_content, NULL), g_bytes_get_size (superblock_content)); - len = sizeof digest; - g_checksum_get_digest (checksum, digest, &len); - - data = g_memdup2 (digest, len); - return g_variant_new_from_data (G_VARIANT_TYPE ("ay"), - data, len, - FALSE, g_free, data); -} - -static char * -appstream_ref_get_subset (const char *ref) -{ - if (!g_str_has_prefix (ref, "appstream2/")) - return NULL; - - const char *rest = ref + strlen ("appstream2/"); - const char *dash = strrchr (rest, '-'); - if (dash == NULL) - return NULL; - - return g_strndup (rest, dash - rest); -} - -char * -flatpak_get_arch_for_ref (const char *ref) -{ - if (g_str_has_prefix (ref, "appstream/") || - g_str_has_prefix (ref, "appstream2/")) - { - const char *rest = strchr (ref, '/') + 1; /* Guaranteed to exist per above check */ - const char *dash = strrchr (rest, '-'); /* Subset appstream refs are appstream2/$subset-$arch */ - if (dash != NULL) - rest = dash + 1; - return g_strdup (rest); - } - else if (g_str_has_prefix (ref, "app/") || - g_str_has_prefix (ref, "runtime/")) - { - const char *slash; - const char *arch; - - slash = strchr (ref, '/') + 1; /* Guaranteed to exist per above check */ - slash = strchr (slash, '/'); /* Skip id */ - if (slash == NULL) - return NULL; - arch = slash + 1; - - slash = strchr (arch, '/'); /* skip to end arch */ - if (slash == NULL) - return NULL; - - return g_strndup (arch, slash - arch); - } - - return NULL; -} - -typedef enum { - DIFF_OP_KIND_RESUSE_OLD, - DIFF_OP_KIND_SKIP_OLD, - DIFF_OP_KIND_DATA, -} DiffOpKind; - -typedef struct { - DiffOpKind kind; - gsize size; -} DiffOp; - -typedef struct { - const guchar *old_data; - const guchar *new_data; - - GArray *ops; - GArray *data; - - gsize last_old_offset; - gsize last_new_offset; -} DiffData; - -static gsize -match_bytes_at_start (const guchar *data1, - gsize data1_len, - const guchar *data2, - gsize data2_len) -{ - gsize len = 0; - gsize max_len = MIN (data1_len, data2_len); - - while (len < max_len) - { - if (*data1 != *data2) - break; - data1++; - data2++; - len++; - } - return len; -} - -static gsize -match_bytes_at_end (const guchar *data1, - gsize data1_len, - const guchar *data2, - gsize data2_len) -{ - gsize len = 0; - gsize max_len = MIN (data1_len, data2_len); - - data1 += data1_len - 1; - data2 += data2_len - 1; - - while (len < max_len) - { - if (*data1 != *data2) - break; - data1--; - data2--; - len++; - } - return len; -} - -static DiffOp * -diff_ensure_op (DiffData *data, - DiffOpKind kind) -{ - if (data->ops->len == 0 || - g_array_index (data->ops, DiffOp, data->ops->len-1).kind != kind) - { - DiffOp op = {kind, 0}; - g_array_append_val (data->ops, op); - } - - return &g_array_index (data->ops, DiffOp, data->ops->len-1); -} - -static void -diff_emit_reuse (DiffData *data, - gsize size) -{ - DiffOp *op; - - if (size == 0) - return; - - op = diff_ensure_op (data, DIFF_OP_KIND_RESUSE_OLD); - op->size += size; -} - -static void -diff_emit_skip (DiffData *data, - gsize size) -{ - DiffOp *op; - - if (size == 0) - return; - - op = diff_ensure_op (data, DIFF_OP_KIND_SKIP_OLD); - op->size += size; -} - -static void -diff_emit_data (DiffData *data, - gsize size, - const guchar *new_data) -{ - DiffOp *op; - - if (size == 0) - return; - - op = diff_ensure_op (data, DIFF_OP_KIND_DATA); - op->size += size; - - g_array_append_vals (data->data, new_data, size); -} - -static GBytes * -diff_encode (DiffData *data, GError **error) -{ - g_autoptr(GOutputStream) mem = g_memory_output_stream_new_resizable (); - g_autoptr(GDataOutputStream) out = g_data_output_stream_new (mem); - gsize ops_count = 0; - - g_data_output_stream_set_byte_order (out, G_DATA_STREAM_BYTE_ORDER_LITTLE_ENDIAN); - - /* Header */ - if (!g_output_stream_write_all (G_OUTPUT_STREAM (out), - FLATPAK_SUMMARY_DIFF_HEADER, 4, - NULL, NULL, error)) - return NULL; - - /* Write the ops count placeholder */ - if (!g_data_output_stream_put_uint32 (out, 0, NULL, error)) - return NULL; - - for (gsize i = 0; i < data->ops->len; i++) - { - DiffOp *op = &g_array_index (data->ops, DiffOp, i); - gsize size = op->size; - - while (size > 0) - { - /* We leave a nibble at the top for the op */ - guint32 opdata = (guint64)size & 0x0fffffff; - size -= opdata; - - opdata = opdata | ((0xf & op->kind) << 28); - - if (!g_data_output_stream_put_uint32 (out, opdata, NULL, error)) - return NULL; - ops_count++; - } - } - - /* Then add the data */ - if (data->data->len > 0 && - !g_output_stream_write_all (G_OUTPUT_STREAM (out), - data->data->data, data->data->len, - NULL, NULL, error)) - return NULL; - - /* Back-patch in the ops count */ - if (!g_seekable_seek (G_SEEKABLE(out), 4, G_SEEK_SET, NULL, error)) - return NULL; - - if (!g_data_output_stream_put_uint32 (out, ops_count, NULL, error)) - return NULL; - - if (!g_output_stream_close (G_OUTPUT_STREAM (out), NULL, error)) - return NULL; - - return g_memory_output_stream_steal_as_bytes (G_MEMORY_OUTPUT_STREAM (mem)); -} - -static void -diff_consume_block2 (DiffData *data, - gsize consume_old_offset, - gsize consume_old_size, - gsize produce_new_offset, - gsize produce_new_size) -{ - /* We consumed $consume_old_size bytes from $consume_old_offset to - produce $produce_new_size bytes at $produce_new_size */ - - /* First we copy old data for any matching prefix of the block */ - - gsize prefix_len = match_bytes_at_start (data->old_data + consume_old_offset, consume_old_size, - data->new_data + produce_new_offset, produce_new_size); - diff_emit_reuse (data, prefix_len); - - consume_old_size -= prefix_len; - consume_old_offset += prefix_len; - - produce_new_size -= prefix_len; - produce_new_offset += prefix_len; - - /* Then we find the matching suffix for the rest */ - gsize suffix_len = match_bytes_at_end (data->old_data + consume_old_offset, consume_old_size, - data->new_data + produce_new_offset, produce_new_size); - - /* Skip source data until suffix match */ - diff_emit_skip (data, consume_old_size - suffix_len); - - /* Copy new data until suffix match */ - diff_emit_data (data, produce_new_size - suffix_len, data->new_data + produce_new_offset); - - diff_emit_reuse (data, suffix_len); -} - -static void -diff_consume_block (DiffData *data, - gssize consume_old_offset, - gsize consume_old_size, - gssize produce_new_offset, - gsize produce_new_size) -{ - if (consume_old_offset == -1) - consume_old_offset = data->last_old_offset; - if (produce_new_offset == -1) - produce_new_offset = data->last_new_offset; - - /* We consumed $consume_old_size bytes from $consume_old_offset to - * produce $produce_new_size bytes at $produce_new_size, however - * while the emitted blocks are in order they may not cover the - * every byte, so we emit the inbetwen blocks separately. */ - - if (consume_old_offset != data->last_old_offset || - produce_new_offset != data->last_new_offset) - diff_consume_block2 (data, - data->last_old_offset, consume_old_offset - data->last_old_offset , - data->last_new_offset, produce_new_offset - data->last_new_offset); - - diff_consume_block2 (data, - consume_old_offset, consume_old_size, - produce_new_offset, produce_new_size); - - data->last_old_offset = consume_old_offset + consume_old_size; - data->last_new_offset = produce_new_offset + produce_new_size; -} - -GBytes * -flatpak_summary_apply_diff (GBytes *old, - GBytes *diff, - GError **error) -{ - g_autoptr(GBytes) uncompressed = NULL; - const guchar *diffdata; - gsize diff_size; - guint32 *ops; - guint32 n_ops; - gsize data_offset; - gsize data_size; - const guchar *data; - const guchar *old_data = g_bytes_get_data (old, NULL); - gsize old_size = g_bytes_get_size (old); - g_autoptr(GByteArray) res = g_byte_array_new (); - - uncompressed = flatpak_zlib_decompress_bytes (diff, error); - if (uncompressed == NULL) - { - g_prefix_error (error, "Invalid summary diff: "); - return NULL; - } - - diffdata = g_bytes_get_data (uncompressed, NULL); - diff_size = g_bytes_get_size (uncompressed); - - if (diff_size < 8 || - memcmp (diffdata, FLATPAK_SUMMARY_DIFF_HEADER, 4) != 0) - { - flatpak_fail (error, "Invalid summary diff"); - return NULL; - } - - n_ops = GUINT32_FROM_LE (*(guint32 *)(diffdata+4)); - ops = (guint32 *)(diffdata+8); - - data_offset = 4 + 4 + 4 * n_ops; - - /* All ops must fit in diff, and avoid wrapping the multiply */ - if (data_offset > diff_size || - (data_offset - 4 - 4) / 4 != n_ops) - { - flatpak_fail (error, "Invalid summary diff"); - return NULL; - } - - data = diffdata + data_offset; - data_size = diff_size - data_offset; - - for (gsize i = 0; i < n_ops; i++) - { - guint32 opdata = GUINT32_FROM_LE (ops[i]); - guint32 kind = (opdata & 0xf0000000) >> 28; - guint32 size = opdata & 0x0fffffff; - - switch (kind) - { - case DIFF_OP_KIND_RESUSE_OLD: - if (size > old_size) - { - flatpak_fail (error, "Invalid summary diff"); - return NULL; - } - g_byte_array_append (res, old_data, size); - old_data += size; - old_size -= size; - break; - case DIFF_OP_KIND_SKIP_OLD: - if (size > old_size) - { - flatpak_fail (error, "Invalid summary diff"); - return NULL; - } - old_data += size; - old_size -= size; - break; - case DIFF_OP_KIND_DATA: - if (size > data_size) - { - flatpak_fail (error, "Invalid summary diff"); - return NULL; - } - g_byte_array_append (res, data, size); - data += size; - data_size -= size; - break; - default: - flatpak_fail (error, "Invalid summary diff"); - return NULL; - } - } - - return g_byte_array_free_to_bytes (g_steal_pointer (&res)); -} - - -static GBytes * -flatpak_summary_generate_diff (GVariant *old_v, - GVariant *new_v, - GError **error) -{ - VarSummaryRef new, old; - VarRefMapRef new_refs, old_refs; - VarRefMapEntryRef new_entry, old_entry; - gsize new_len, old_len; - int new_i, old_i; - const char *old_ref, *new_ref; - g_autoptr(GArray) ops = g_array_new (FALSE, TRUE, sizeof (DiffOp)); - g_autoptr(GArray) data_bytes = g_array_new (FALSE, TRUE, 1); - g_autoptr(GBytes) diff_uncompressed = NULL; - g_autoptr(GBytes) diff_compressed = NULL; - DiffData data = { - g_variant_get_data (old_v), - g_variant_get_data (new_v), - ops, - data_bytes, - }; - - new = var_summary_from_gvariant (new_v); - old = var_summary_from_gvariant (old_v); - - new_refs = var_summary_get_ref_map (new); - old_refs = var_summary_get_ref_map (old); - - new_len = var_ref_map_get_length (new_refs); - old_len = var_ref_map_get_length (old_refs); - - new_i = old_i = 0; - while (new_i < new_len && old_i < old_len) - { - if (new_i == new_len) - { - /* Just old left */ - old_entry = var_ref_map_get_at (old_refs, old_i); - old_ref = var_ref_map_entry_get_ref (old_entry); - old_i++; - diff_consume_block (&data, - -1, 0, - (const guchar *)new_entry.base - (const guchar *)new.base, new_entry.size); - } - else if (old_i == old_len) - { - /* Just new left */ - new_entry = var_ref_map_get_at (new_refs, new_i); - new_ref = var_ref_map_entry_get_ref (new_entry); - diff_consume_block (&data, - (const guchar *)old_entry.base - (const guchar *)old.base, old_entry.size, - -1, 0); - - new_i++; - } - else - { - new_entry = var_ref_map_get_at (new_refs, new_i); - new_ref = var_ref_map_entry_get_ref (new_entry); - - old_entry = var_ref_map_get_at (old_refs, old_i); - old_ref = var_ref_map_entry_get_ref (old_entry); - - int cmp = strcmp (new_ref, old_ref); - if (cmp == 0) - { - /* same ref */ - diff_consume_block (&data, - (const guchar *)old_entry.base - (const guchar *)old.base, old_entry.size, - (const guchar *)new_entry.base - (const guchar *)new.base, new_entry.size); - old_i++; - new_i++; - } - else if (cmp < 0) - { - /* new added */ - diff_consume_block (&data, - -1, 0, - (const guchar *)new_entry.base - (const guchar *)new.base, new_entry.size); - new_i++; - } - else - { - /* old removed */ - diff_consume_block (&data, - (const guchar *)old_entry.base - (const guchar *)old.base, old_entry.size, - -1, 0); - old_i++; - } - } - } - - /* Flush till the end */ - diff_consume_block2 (&data, - data.last_old_offset, old.size - data.last_old_offset, - data.last_new_offset, new.size - data.last_new_offset); - - diff_uncompressed = diff_encode (&data, error); - if (diff_uncompressed == NULL) - return NULL; - - diff_compressed = flatpak_zlib_compress_bytes (diff_uncompressed, 9, error); - if (diff_compressed == NULL) - return NULL; - -#ifdef VALIDATE_DIFF - { - g_autoptr(GError) apply_error = NULL; - g_autoptr(GBytes) old_bytes = g_variant_get_data_as_bytes (old_v); - g_autoptr(GBytes) new_bytes = g_variant_get_data_as_bytes (new_v); - g_autoptr(GBytes) applied = flatpak_summary_apply_diff (old_bytes, diff_compressed, &apply_error); - g_assert (applied != NULL); - g_assert (g_bytes_equal (applied, new_bytes)); - } -#endif - - return g_steal_pointer (&diff_compressed); -} - -static void -variant_dict_merge (GVariantDict *dict, - GVariant *to_merge) -{ - GVariantIter iter; - gchar *key; - GVariant *value; - - if (to_merge) - { - g_variant_iter_init (&iter, to_merge); - while (g_variant_iter_next (&iter, "{sv}", &key, &value)) - { - g_variant_dict_insert_value (dict, key, value); - g_variant_unref (value); - g_free (key); - } - } -} - -static void -add_summary_metadata (OstreeRepo *repo, - GVariantBuilder *metadata_builder) -{ - GKeyFile *config; - g_autofree char *title = NULL; - g_autofree char *comment = NULL; - g_autofree char *description = NULL; - g_autofree char *homepage = NULL; - g_autofree char *icon = NULL; - g_autofree char *redirect_url = NULL; - g_autofree char *default_branch = NULL; - g_autofree char *remote_mode_str = NULL; - g_autofree char *authenticator_name = NULL; - g_autofree char *gpg_keys = NULL; - g_auto(GStrv) config_keys = NULL; - int authenticator_install = -1; - const char *collection_id; - gboolean deploy_collection_id = FALSE; - gboolean deploy_sideload_collection_id = FALSE; - gboolean tombstone_commits = FALSE; - - config = ostree_repo_get_config (repo); - - if (config) - { - remote_mode_str = g_key_file_get_string (config, "core", "mode", NULL); - tombstone_commits = g_key_file_get_boolean (config, "core", "tombstone-commits", NULL); - - title = g_key_file_get_string (config, "flatpak", "title", NULL); - comment = g_key_file_get_string (config, "flatpak", "comment", NULL); - description = g_key_file_get_string (config, "flatpak", "description", NULL); - homepage = g_key_file_get_string (config, "flatpak", "homepage", NULL); - icon = g_key_file_get_string (config, "flatpak", "icon", NULL); - default_branch = g_key_file_get_string (config, "flatpak", "default-branch", NULL); - gpg_keys = g_key_file_get_string (config, "flatpak", "gpg-keys", NULL); - redirect_url = g_key_file_get_string (config, "flatpak", "redirect-url", NULL); - deploy_sideload_collection_id = g_key_file_get_boolean (config, "flatpak", "deploy-sideload-collection-id", NULL); - deploy_collection_id = g_key_file_get_boolean (config, "flatpak", "deploy-collection-id", NULL); - authenticator_name = g_key_file_get_string (config, "flatpak", "authenticator-name", NULL); - if (g_key_file_has_key (config, "flatpak", "authenticator-install", NULL)) - authenticator_install = g_key_file_get_boolean (config, "flatpak", "authenticator-install", NULL); - - config_keys = g_key_file_get_keys (config, "flatpak", NULL, NULL); - } - - collection_id = ostree_repo_get_collection_id (repo); - - g_variant_builder_add (metadata_builder, "{sv}", "ostree.summary.mode", - g_variant_new_string (remote_mode_str ? remote_mode_str : "bare")); - g_variant_builder_add (metadata_builder, "{sv}", "ostree.summary.tombstone-commits", - g_variant_new_boolean (tombstone_commits)); - g_variant_builder_add (metadata_builder, "{sv}", "ostree.summary.indexed-deltas", - g_variant_new_boolean (TRUE)); - g_variant_builder_add (metadata_builder, "{sv}", "ostree.summary.last-modified", - g_variant_new_uint64 (GUINT64_TO_BE (g_get_real_time () / G_USEC_PER_SEC))); - - if (collection_id) - g_variant_builder_add (metadata_builder, "{sv}", "ostree.summary.collection-id", - g_variant_new_string (collection_id)); - - if (title) - g_variant_builder_add (metadata_builder, "{sv}", "xa.title", - g_variant_new_string (title)); - - if (comment) - g_variant_builder_add (metadata_builder, "{sv}", "xa.comment", - g_variant_new_string (comment)); - - if (description) - g_variant_builder_add (metadata_builder, "{sv}", "xa.description", - g_variant_new_string (description)); - - if (homepage) - g_variant_builder_add (metadata_builder, "{sv}", "xa.homepage", - g_variant_new_string (homepage)); - - if (icon) - g_variant_builder_add (metadata_builder, "{sv}", "xa.icon", - g_variant_new_string (icon)); - - if (redirect_url) - g_variant_builder_add (metadata_builder, "{sv}", "xa.redirect-url", - g_variant_new_string (redirect_url)); - - if (default_branch) - g_variant_builder_add (metadata_builder, "{sv}", "xa.default-branch", - g_variant_new_string (default_branch)); - - if (deploy_collection_id && collection_id != NULL) - g_variant_builder_add (metadata_builder, "{sv}", OSTREE_META_KEY_DEPLOY_COLLECTION_ID, - g_variant_new_string (collection_id)); - else if (deploy_sideload_collection_id && collection_id != NULL) - g_variant_builder_add (metadata_builder, "{sv}", "xa.deploy-collection-id", - g_variant_new_string (collection_id)); - else if (deploy_collection_id) - g_info ("Ignoring deploy-collection-id=true because no collection ID is set."); - - if (authenticator_name) - g_variant_builder_add (metadata_builder, "{sv}", "xa.authenticator-name", - g_variant_new_string (authenticator_name)); - - if (authenticator_install != -1) - g_variant_builder_add (metadata_builder, "{sv}", "xa.authenticator-install", - g_variant_new_boolean (authenticator_install)); - - g_variant_builder_add (metadata_builder, "{sv}", "xa.cache-version", - g_variant_new_uint32 (GUINT32_TO_LE (FLATPAK_XA_CACHE_VERSION))); - - if (config_keys != NULL) - { - for (int i = 0; config_keys[i] != NULL; i++) - { - const char *key = config_keys[i]; - g_autofree char *xa_key = NULL; - g_autofree char *value = NULL; - - if (!g_str_has_prefix (key, "authenticator-options.")) - continue; - - value = g_key_file_get_string (config, "flatpak", key, NULL); - if (value == NULL) - continue; - - xa_key = g_strconcat ("xa.", key, NULL); - g_variant_builder_add (metadata_builder, "{sv}", xa_key, - g_variant_new_string (value)); - } - } - - if (gpg_keys) - { - guchar *decoded; - gsize decoded_len; - - gpg_keys = g_strstrip (gpg_keys); - decoded = g_base64_decode (gpg_keys, &decoded_len); - - g_variant_builder_add (metadata_builder, "{sv}", "xa.gpg-keys", - g_variant_new_from_data (G_VARIANT_TYPE ("ay"), decoded, decoded_len, - TRUE, (GDestroyNotify) g_free, decoded)); - } -} - -static GVariant * -generate_summary (OstreeRepo *repo, - gboolean compat_format, - GHashTable *refs, - GHashTable *commit_data_cache, - GPtrArray *delta_names, - const char *subset, - const char **summary_arches, - GCancellable *cancellable, - GError **error) -{ - g_autoptr(GVariantBuilder) metadata_builder = g_variant_builder_new (G_VARIANT_TYPE_VARDICT); - g_autoptr(GVariantBuilder) ref_data_builder = g_variant_builder_new (G_VARIANT_TYPE ("a{s(tts)}")); - g_autoptr(GVariantBuilder) ref_sparse_data_builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sa{sv}}")); - g_autoptr(GVariantBuilder) refs_builder = g_variant_builder_new (G_VARIANT_TYPE ("a(s(taya{sv}))")); - g_autoptr(GVariantBuilder) summary_builder = g_variant_builder_new (OSTREE_SUMMARY_GVARIANT_FORMAT); - g_autoptr(GHashTable) summary_arches_ht = NULL; - g_autoptr(GHashTable) commits = NULL; - g_autoptr(GList) ordered_keys = NULL; - GList *l = NULL; - - /* In the new format this goes in the summary index instead */ - if (compat_format) - add_summary_metadata (repo, metadata_builder); - - ordered_keys = g_hash_table_get_keys (refs); - ordered_keys = g_list_sort (ordered_keys, (GCompareFunc) strcmp); - - if (summary_arches) - { - summary_arches_ht = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, NULL); - for (int i = 0; summary_arches[i] != NULL; i++) - { - const char *arch = summary_arches[i]; - - g_hash_table_add (summary_arches_ht, (char *)arch); - } - } - - /* Compute which commits to keep */ - commits = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, NULL); /* strings owned by ref */ - for (l = ordered_keys; l; l = l->next) - { - const char *ref = l->data; - const char *rev = g_hash_table_lookup (refs, ref); - g_autofree char *arch = NULL; - const CommitData *rev_data = NULL; - - if (summary_arches) - { - /* NOTE: Non-arched (unknown) refs get into all summary versions */ - arch = flatpak_get_arch_for_ref (ref); - if (arch != NULL && !g_hash_table_contains (summary_arches_ht, arch)) - continue; /* Filter this ref by arch */ - } - - rev_data = g_hash_table_lookup (commit_data_cache, rev); - if (*subset != 0) - { - /* Subset summaries keep the appstream2/$subset-$arch, and have no appstream/ compat branch */ - - if (g_str_has_prefix (ref, "appstream/")) - { - continue; /* No compat branch in subsets */ - } - else if (g_str_has_prefix (ref, "appstream2/")) - { - g_autofree char *ref_subset = appstream_ref_get_subset (ref); - if (ref_subset == NULL) - continue; /* Non-subset, ignore */ - - if (strcmp (subset, ref_subset) != 0) - continue; /* Different subset, ignore */ - - /* Otherwise, keep */ - } - else if (rev_data) - { - if (rev_data->subsets == NULL || - !flatpak_g_ptr_array_contains_string (rev_data->subsets, subset)) - continue; /* Ref is not in this subset */ - } - } - else - { - /* non-subset, keep everything but subset appstream refs */ - - g_autofree char *ref_subset = appstream_ref_get_subset (ref); - if (ref_subset != NULL) - continue; /* Subset appstream ref, ignore */ - } - - g_hash_table_add (commits, (char *)rev); - } - - /* Create refs list, metadata and sparse_data */ - for (l = ordered_keys; l; l = l->next) - { - const char *ref = l->data; - const char *rev = g_hash_table_lookup (refs, ref); - const CommitData *rev_data = NULL; - g_auto(GVariantDict) commit_metadata_builder = FLATPAK_VARIANT_BUILDER_INITIALIZER; - guint64 commit_size; - guint64 commit_timestamp; - - if (!g_hash_table_contains (commits, rev)) - continue; /* Filter out commit (by arch & subset) */ - - if (flatpak_is_app_runtime_or_appstream_ref (ref)) - rev_data = g_hash_table_lookup (commit_data_cache, rev); - - if (rev_data != NULL) - { - commit_size = rev_data->commit_size; - commit_timestamp = rev_data->commit_timestamp; - } - else - { - g_autoptr(GVariant) commit_obj = NULL; - if (!ostree_repo_load_variant (repo, OSTREE_OBJECT_TYPE_COMMIT, rev, &commit_obj, error)) - return NULL; - commit_size = g_variant_get_size (commit_obj); - commit_timestamp = ostree_commit_get_timestamp (commit_obj); - } - - g_variant_dict_init (&commit_metadata_builder, NULL); - if (!compat_format && rev_data) - { - g_variant_dict_insert (&commit_metadata_builder, "xa.data", "(tts)", - GUINT64_TO_BE (rev_data->installed_size), - GUINT64_TO_BE (rev_data->download_size), - rev_data->metadata_contents); - variant_dict_merge (&commit_metadata_builder, rev_data->sparse_data); - } - - /* For the new format summary we use a shorter name for the timestamp to save space */ - g_variant_dict_insert_value (&commit_metadata_builder, - compat_format ? OSTREE_COMMIT_TIMESTAMP : OSTREE_COMMIT_TIMESTAMP2, - g_variant_new_uint64 (GUINT64_TO_BE (commit_timestamp))); - - g_variant_builder_add_value (refs_builder, - g_variant_new ("(s(t@ay@a{sv}))", ref, - commit_size, - ostree_checksum_to_bytes_v (rev), - g_variant_dict_end (&commit_metadata_builder))); - - if (compat_format && rev_data) - { - g_variant_builder_add (ref_data_builder, "{s(tts)}", - ref, - GUINT64_TO_BE (rev_data->installed_size), - GUINT64_TO_BE (rev_data->download_size), - rev_data->metadata_contents); - if (rev_data->sparse_data) - g_variant_builder_add (ref_sparse_data_builder, "{s@a{sv}}", - ref, rev_data->sparse_data); - } - } - - if (delta_names) - { - g_auto(GVariantDict) deltas_builder = FLATPAK_VARIANT_BUILDER_INITIALIZER; - - g_variant_dict_init (&deltas_builder, NULL); - for (guint i = 0; i < delta_names->len; i++) - { - g_autofree char *from = NULL; - g_autofree char *to = NULL; - GVariant *digest; - - _ostree_parse_delta_name (delta_names->pdata[i], &from, &to); - - /* Only keep deltas going to a ref that is in the summary - * (i.e. not arch filtered or random) */ - if (!g_hash_table_contains (commits, to)) - continue; - - digest = _ostree_repo_static_delta_superblock_digest (repo, - (from && from[0]) ? from : NULL, - to, cancellable, error); - if (digest == NULL) - return FALSE; - - g_variant_dict_insert_value (&deltas_builder, delta_names->pdata[i], digest); - } - - if (delta_names->len > 0) - g_variant_builder_add (metadata_builder, "{sv}", "ostree.static-deltas", g_variant_dict_end (&deltas_builder)); - } - - if (compat_format) - { - /* Note: xa.cache doesn’t need to support collection IDs for the refs listed - * in it, because the xa.cache metadata is stored on the ostree-metadata ref, - * which is itself strongly bound to a collection ID — so that collection ID - * is bound to all the refs in xa.cache. If a client is using the xa.cache - * data from a summary file (rather than an ostree-metadata branch), they are - * too old to care about collection IDs anyway. */ - g_variant_builder_add (metadata_builder, "{sv}", "xa.cache", - g_variant_new_variant (g_variant_builder_end (ref_data_builder))); - g_variant_builder_add (metadata_builder, "{sv}", "xa.sparse-cache", - g_variant_builder_end (ref_sparse_data_builder)); - } - else - { - g_variant_builder_add (metadata_builder, "{sv}", "xa.summary-version", - g_variant_new_uint32 (GUINT32_TO_LE (FLATPAK_XA_SUMMARY_VERSION))); - } - - g_variant_builder_add_value (summary_builder, g_variant_builder_end (refs_builder)); - g_variant_builder_add_value (summary_builder, g_variant_builder_end (metadata_builder)); - - return g_variant_ref_sink (g_variant_builder_end (summary_builder)); -} - -static GVariant * -read_digested_summary (OstreeRepo *repo, - const char *digest, - GHashTable *digested_summary_cache, - GCancellable *cancellable, - GError **error) -{ - GVariant *cached; - g_autoptr(GVariant) loaded = NULL; - - cached = g_hash_table_lookup (digested_summary_cache, digest); - if (cached) - return g_variant_ref (cached); - - loaded = flatpak_repo_load_digested_summary (repo, digest, error); - if (loaded == NULL) - return NULL; - - g_hash_table_insert (digested_summary_cache, g_strdup (digest), g_variant_ref (loaded)); - - return g_steal_pointer (&loaded); -} - -static gboolean -add_to_history (OstreeRepo *repo, - GVariantBuilder *history_builder, - VarChecksumRef old_digest_vv, - GVariant *current_digest_v, - GVariant *current_content, - GHashTable *digested_summary_cache, - guint *history_len, - guint max_history_length, - GCancellable *cancellable, - GError **error) -{ - g_autoptr(GVariant) old_digest_v = g_variant_ref_sink (var_checksum_dup_to_gvariant (old_digest_vv)); - g_autofree char *old_digest = NULL; - g_autoptr(GVariant) old_content = NULL; - g_autofree char *current_digest = NULL; - g_autoptr(GBytes) subsummary_diff = NULL; - - /* Limit history length */ - if (*history_len >= max_history_length) - return TRUE; - - /* Avoid repeats in the history (in case nothing changed in subsummary) */ - if (g_variant_equal (old_digest_v, current_digest_v)) - return TRUE; - - old_digest = ostree_checksum_from_bytes_v (old_digest_v); - old_content = read_digested_summary (repo, old_digest, digested_summary_cache, cancellable, NULL); - if (old_content == NULL) - return TRUE; /* Only add parents that still exist */ - - subsummary_diff = flatpak_summary_generate_diff (old_content, current_content, error); - if (subsummary_diff == NULL) - return FALSE; - - current_digest = ostree_checksum_from_bytes_v (current_digest_v); - - if (!flatpak_repo_save_digested_summary_delta (repo, old_digest, current_digest, - subsummary_diff, cancellable, error)) - return FALSE; - - *history_len += 1; - g_variant_builder_add_value (history_builder, old_digest_v); - - return TRUE; -} - -static GVariant * -generate_summary_index (OstreeRepo *repo, - GVariant *old_index_v, - GHashTable *summaries, - GHashTable *digested_summaries, - GHashTable *digested_summary_cache, - const char **gpg_key_ids, - const char *gpg_homedir, - GCancellable *cancellable, - GError **error) -{ - g_autoptr(GVariantBuilder) metadata_builder = g_variant_builder_new (G_VARIANT_TYPE_VARDICT); - g_autoptr(GVariantBuilder) subsummary_builder = g_variant_builder_new (G_VARIANT_TYPE ("a{s(ayaaya{sv})}")); - g_autoptr(GVariantBuilder) index_builder = g_variant_builder_new (FLATPAK_SUMMARY_INDEX_GVARIANT_FORMAT); - g_autoptr(GVariant) index = NULL; - g_autoptr(GList) ordered_summaries = NULL; - guint max_history_length = flatpak_repo_get_summary_history_length (repo); - GList *l; - - add_summary_metadata (repo, metadata_builder); - - ordered_summaries = g_hash_table_get_keys (summaries); - ordered_summaries = g_list_sort (ordered_summaries, (GCompareFunc) strcmp); - for (l = ordered_summaries; l; l = l->next) - { - g_auto(GVariantDict) subsummary_metadata_builder = FLATPAK_VARIANT_BUILDER_INITIALIZER; - const char *subsummary = l->data; - const char *digest = g_hash_table_lookup (summaries, subsummary); - g_autoptr(GVariant) digest_v = g_variant_ref_sink (ostree_checksum_to_bytes_v (digest)); - g_autoptr(GVariantBuilder) history_builder = g_variant_builder_new (G_VARIANT_TYPE ("aay")); - g_autoptr(GVariant) subsummary_content = NULL; - - subsummary_content = read_digested_summary (repo, digest, digested_summary_cache, cancellable, error); - if (subsummary_content == NULL) - return NULL; /* This really should always be there as we're supposed to index it */ - - if (old_index_v) - { - VarSummaryIndexRef old_index = var_summary_index_from_gvariant (old_index_v); - VarSummaryIndexSubsummariesRef old_subsummaries = var_summary_index_get_subsummaries (old_index); - VarSubsummaryRef old_subsummary; - guint history_len = 0; - - if (var_summary_index_subsummaries_lookup (old_subsummaries, subsummary, NULL, &old_subsummary)) - { - VarChecksumRef parent = var_subsummary_get_checksum (old_subsummary); - - /* Add current as first in history */ - if (!add_to_history (repo, history_builder, parent, digest_v, subsummary_content, digested_summary_cache, - &history_len, max_history_length, cancellable, error)) - return FALSE; - - /* Add previous history */ - VarArrayofChecksumRef history = var_subsummary_get_history (old_subsummary); - gsize len = var_arrayof_checksum_get_length (history); - for (gsize i = 0; i < len; i++) - { - VarChecksumRef c = var_arrayof_checksum_get_at (history, i); - if (!add_to_history (repo, history_builder, c, digest_v, subsummary_content, digested_summary_cache, - &history_len, max_history_length, cancellable, error)) - return FALSE; - } - } - } - - g_variant_dict_init (&subsummary_metadata_builder, NULL); - g_variant_builder_add (subsummary_builder, "{s(@ay@aay@a{sv})}", - subsummary, - digest_v, - g_variant_builder_end (history_builder), - g_variant_dict_end (&subsummary_metadata_builder)); - } - - g_variant_builder_add_value (index_builder, g_variant_builder_end (subsummary_builder)); - g_variant_builder_add_value (index_builder, g_variant_builder_end (metadata_builder)); - - index = g_variant_ref_sink (g_variant_builder_end (index_builder)); - - return g_steal_pointer (&index); -} - -static gboolean -flatpak_repo_gc_digested_summaries (OstreeRepo *repo, - const char *index_digest, /* The digest of the current (new) index (if any) */ - const char *old_index_digest, /* The digest of the previous index (if any) */ - GHashTable *digested_summaries, /* generated */ - GHashTable *digested_summary_cache, /* generated + referenced */ - GCancellable *cancellable, - GError **error) -{ - g_auto(GLnxDirFdIterator) iter = {0}; - int repo_fd = ostree_repo_get_dfd (repo); - struct dirent *dent; - const char *ext; - g_autoptr(GError) local_error = NULL; - - if (!glnx_dirfd_iterator_init_at (repo_fd, "summaries", FALSE, &iter, &local_error)) - { - if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) - return TRUE; - - g_propagate_error (error, g_steal_pointer (&local_error)); - return FALSE; - } - - while (TRUE) - { - gboolean remove = FALSE; - - if (!glnx_dirfd_iterator_next_dent_ensure_dtype (&iter, &dent, cancellable, error)) - return FALSE; - - if (dent == NULL) - break; - - if (dent->d_type != DT_REG) - continue; - - /* Keep it if its an unexpected type */ - ext = strchr (dent->d_name, '.'); - if (ext != NULL) - { - if (strcmp (ext, ".gz") == 0 && strlen (dent->d_name) == 64 + 3) - { - g_autofree char *sha256 = g_strndup (dent->d_name, 64); - - /* Keep all the referenced summaries */ - if (g_hash_table_contains (digested_summary_cache, sha256)) - { - g_info ("Keeping referenced summary %s", dent->d_name); - continue; - } - /* Remove rest */ - remove = TRUE; - } - else if (strcmp (ext, ".delta") == 0) - { - const char *dash = strchr (dent->d_name, '-'); - if (dash != NULL && dash < ext && (ext - dash) == 1 + 64) - { - g_autofree char *to_sha256 = g_strndup (dash + 1, 64); - - /* Only keep deltas going to a generated summary */ - if (g_hash_table_contains (digested_summaries, to_sha256)) - { - g_info ("Keeping delta to generated summary %s", dent->d_name); - continue; - } - /* Remove rest */ - remove = TRUE; - } - } - else if (strcmp (ext, ".idx.sig") == 0) - { - g_autofree char *digest = g_strndup (dent->d_name, strlen (dent->d_name) - strlen (".idx.sig")); - - if (g_strcmp0 (digest, index_digest) == 0) - continue; /* Always keep current */ - - if (g_strcmp0 (digest, old_index_digest) == 0) - continue; /* Always keep previous one, to avoid some races */ - - /* Remove the rest */ - remove = TRUE; - } - } - - if (remove) - { - g_info ("Removing old digested summary file %s", dent->d_name); - if (unlinkat (iter.fd, dent->d_name, 0) != 0) - { - glnx_set_error_from_errno (error); - return FALSE; - } - } - else - g_info ("Keeping unexpected summary file %s", dent->d_name); - } - - return TRUE; -} - - -/* Update the metadata in the summary file for @repo, and then re-sign the file. - * If the repo has a collection ID set, additionally store the metadata on a - * contentless commit in a well-known branch, which is the preferred way of - * broadcasting per-repo metadata (putting it in the summary file is deprecated, - * but kept for backwards compatibility). - * - * Note that there are two keys for the collection ID: collection-id, and - * ostree.deploy-collection-id. If a client does not currently have a - * collection ID configured for this remote, it will *only* update its - * configuration from ostree.deploy-collection-id. This allows phased - * deployment of collection-based repositories. Clients will only update their - * configuration from an unset to a set collection ID once (otherwise the - * security properties of collection IDs are broken). */ -gboolean -flatpak_repo_update (OstreeRepo *repo, - FlatpakRepoUpdateFlags flags, - const char **gpg_key_ids, - const char *gpg_homedir, - GCancellable *cancellable, - GError **error) -{ - g_autoptr(GHashTable) commit_data_cache = NULL; - g_autoptr(GVariant) compat_summary = NULL; - g_autoptr(GVariant) summary_index = NULL; - g_autoptr(GVariant) old_index = NULL; - g_autoptr(GPtrArray) delta_names = NULL; - g_auto(GStrv) summary_arches = NULL; - g_autoptr(GHashTable) refs = NULL; - g_autoptr(GHashTable) arches = NULL; - g_autoptr(GHashTable) subsets = NULL; - g_autoptr(GHashTable) summaries = NULL; - g_autoptr(GHashTable) digested_summaries = NULL; - g_autoptr(GHashTable) digested_summary_cache = NULL; - g_autoptr(GBytes) index_sig = NULL; - time_t old_compat_sig_mtime; - GKeyFile *config; - gboolean disable_index = (flags & FLATPAK_REPO_UPDATE_FLAG_DISABLE_INDEX) != 0; - g_autofree char *index_digest = NULL; - g_autofree char *old_index_digest = NULL; - - config = ostree_repo_get_config (repo); - - if (!ostree_repo_list_refs_ext (repo, NULL, &refs, - OSTREE_REPO_LIST_REFS_EXT_EXCLUDE_REMOTES | OSTREE_REPO_LIST_REFS_EXT_EXCLUDE_MIRRORS, - cancellable, error)) - return FALSE; - - old_index = flatpak_repo_load_summary_index (repo, NULL); - if (old_index) - commit_data_cache = populate_commit_data_cache (repo, old_index); - - if (commit_data_cache == NULL) /* No index or failed to load it */ - commit_data_cache = commit_data_cache_new (); - - if (!ostree_repo_list_static_delta_names (repo, &delta_names, cancellable, error)) - return FALSE; - - if (config) - summary_arches = g_key_file_get_string_list (config, "flatpak", "summary-arches", NULL, NULL); - - summaries = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); - /* These are the ones we generated */ - digested_summaries = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify)g_variant_unref); - /* These are the ones generated or references */ - digested_summary_cache = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify)g_variant_unref); - - arches = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); - subsets = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); - g_hash_table_add (subsets, g_strdup ("")); /* Always have everything subset */ - - GLNX_HASH_TABLE_FOREACH_KV (refs, const char *, ref, const char *, rev) - { - g_autofree char *arch = flatpak_get_arch_for_ref (ref); - CommitData *rev_data = NULL; - - if (arch != NULL && - !g_hash_table_contains (arches, arch)) - g_hash_table_add (arches, g_steal_pointer (&arch)); - - /* Add CommitData for flatpak refs that we didn't already pre-populate */ - if (flatpak_is_app_runtime_or_appstream_ref (ref)) - { - rev_data = g_hash_table_lookup (commit_data_cache, rev); - if (rev_data == NULL) - { - rev_data = read_commit_data (repo, ref, rev, cancellable, error); - if (rev_data == NULL) - return FALSE; - - g_hash_table_insert (commit_data_cache, g_strdup (rev), (CommitData *)rev_data); - } - - for (int i = 0; rev_data->subsets != NULL && i < rev_data->subsets->len; i++) - { - const char *subset = g_ptr_array_index (rev_data->subsets, i); - if (!g_hash_table_contains (subsets, subset)) - g_hash_table_add (subsets, g_strdup (subset)); - } - } - } - - compat_summary = generate_summary (repo, TRUE, refs, commit_data_cache, delta_names, - "", (const char **)summary_arches, - cancellable, error); - if (compat_summary == NULL) - return FALSE; - - if (!disable_index) - { - GLNX_HASH_TABLE_FOREACH (subsets, const char *, subset) - { - GLNX_HASH_TABLE_FOREACH (arches, const char *, arch) - { - const char *arch_v[] = { arch, NULL }; - g_autofree char *name = NULL; - g_autofree char *digest = NULL; - - if (*subset == 0) - name = g_strdup (arch); - else - name = g_strconcat (subset, "-", arch, NULL); - - g_autoptr(GVariant) arch_summary = generate_summary (repo, FALSE, refs, commit_data_cache, NULL, subset, arch_v, - cancellable, error); - if (arch_summary == NULL) - return FALSE; - - digest = flatpak_repo_save_digested_summary (repo, name, arch_summary, cancellable, error); - if (digest == NULL) - return FALSE; - - g_hash_table_insert (digested_summaries, g_strdup (digest), g_variant_ref (arch_summary)); - /* Prime summary cache with generated summaries */ - g_hash_table_insert (digested_summary_cache, g_strdup (digest), g_variant_ref (arch_summary)); - g_hash_table_insert (summaries, g_steal_pointer (&name), g_steal_pointer (&digest)); - } - } - - summary_index = generate_summary_index (repo, old_index, summaries, digested_summaries, digested_summary_cache, - gpg_key_ids, gpg_homedir, - cancellable, error); - if (summary_index == NULL) - return FALSE; - } - - if (!ostree_repo_static_delta_reindex (repo, 0, NULL, cancellable, error)) - return FALSE; - - if (summary_index && gpg_key_ids) - { - g_autoptr(GBytes) index_bytes = g_variant_get_data_as_bytes (summary_index); - - if (!ostree_repo_gpg_sign_data (repo, index_bytes, - NULL, - gpg_key_ids, - gpg_homedir, - &index_sig, - cancellable, - error)) - return FALSE; - } - - if (summary_index) - index_digest = g_compute_checksum_for_data (G_CHECKSUM_SHA256, - g_variant_get_data (summary_index), - g_variant_get_size (summary_index)); - if (old_index) - old_index_digest = g_compute_checksum_for_data (G_CHECKSUM_SHA256, - g_variant_get_data (old_index), - g_variant_get_size (old_index)); - - /* Release the memory-mapped summary index file before replacing it, - to avoid failure on filesystems like cifs */ - g_clear_pointer (&old_index, g_variant_unref); - - if (!flatpak_repo_save_summary_index (repo, summary_index, index_digest, index_sig, cancellable, error)) - return FALSE; - - if (!flatpak_repo_save_compat_summary (repo, compat_summary, &old_compat_sig_mtime, cancellable, error)) - return FALSE; - - if (gpg_key_ids) - { - if (!ostree_repo_add_gpg_signature_summary (repo, - gpg_key_ids, - gpg_homedir, - cancellable, - error)) - return FALSE; - - - if (old_compat_sig_mtime != 0) - { - int repo_dfd = ostree_repo_get_dfd (repo); - struct stat stbuf; - - /* Ensure we increase (in sec precision) */ - if (fstatat (repo_dfd, "summary.sig", &stbuf, AT_SYMLINK_NOFOLLOW) == 0 && - stbuf.st_mtime <= old_compat_sig_mtime) - { - struct timespec ts[2] = { {0, UTIME_OMIT}, {old_compat_sig_mtime + 1, 0} }; - (void) utimensat (repo_dfd, "summary.sig", ts, AT_SYMLINK_NOFOLLOW); - } - } - } - - if (!disable_index && - !flatpak_repo_gc_digested_summaries (repo, index_digest, old_index_digest, digested_summaries, digested_summary_cache, cancellable, error)) - return FALSE; - - return TRUE; -} - -gboolean -flatpak_mtree_create_dir (OstreeRepo *repo, - OstreeMutableTree *parent, - const char *name, - OstreeMutableTree **dir_out, - GError **error) -{ - g_autoptr(OstreeMutableTree) dir = NULL; - - if (!ostree_mutable_tree_ensure_dir (parent, name, &dir, error)) - return FALSE; - - if (!flatpak_mtree_ensure_dir_metadata (repo, dir, NULL, error)) - return FALSE; - - *dir_out = g_steal_pointer (&dir); - return TRUE; -} - -gboolean -flatpak_mtree_create_symlink (OstreeRepo *repo, - OstreeMutableTree *parent, - const char *filename, - const char *target, - GError **error) -{ - g_autoptr(GFileInfo) file_info = g_file_info_new (); - g_autoptr(GInputStream) content_stream = NULL; - g_autofree guchar *raw_checksum = NULL; - g_autofree char *checksum = NULL; - guint64 length; - - g_file_info_set_name (file_info, filename); - g_file_info_set_file_type (file_info, G_FILE_TYPE_SYMBOLIC_LINK); - g_file_info_set_size (file_info, 0); - g_file_info_set_attribute_uint32 (file_info, "unix::uid", 0); - g_file_info_set_attribute_uint32 (file_info, "unix::gid", 0); - g_file_info_set_attribute_uint32 (file_info, "unix::mode", S_IFLNK | 0777); - - g_file_info_set_attribute_boolean (file_info, "standard::is-symlink", TRUE); - g_file_info_set_attribute_byte_string (file_info, "standard::symlink-target", target); - - if (!ostree_raw_file_to_content_stream (NULL, file_info, NULL, - &content_stream, &length, - NULL, error)) - return FALSE; - - if (!ostree_repo_write_content (repo, NULL, content_stream, length, - &raw_checksum, NULL, error)) - return FALSE; - - checksum = ostree_checksum_from_bytes (raw_checksum); - - if (!ostree_mutable_tree_replace_file (parent, filename, checksum, error)) - return FALSE; - - return TRUE; -} - -gboolean -flatpak_mtree_add_file_from_bytes (OstreeRepo *repo, - GBytes *bytes, - OstreeMutableTree *parent, - const char *filename, - GCancellable *cancellable, - GError **error) -{ - g_autoptr(GFileInfo) info = g_file_info_new (); - g_autoptr(GInputStream) memstream = NULL; - g_autoptr(GInputStream) content_stream = NULL; - g_autofree guchar *raw_checksum = NULL; - g_autofree char *checksum = NULL; - guint64 length; - - g_file_info_set_attribute_uint32 (info, "standard::type", G_FILE_TYPE_REGULAR); - g_file_info_set_attribute_uint64 (info, "standard::size", g_bytes_get_size (bytes)); - g_file_info_set_attribute_uint32 (info, "unix::uid", 0); - g_file_info_set_attribute_uint32 (info, "unix::gid", 0); - g_file_info_set_attribute_uint32 (info, "unix::mode", S_IFREG | 0644); - - memstream = g_memory_input_stream_new_from_bytes (bytes); - - if (!ostree_raw_file_to_content_stream (memstream, info, NULL, - &content_stream, &length, - cancellable, error)) - return FALSE; - - if (!ostree_repo_write_content (repo, NULL, content_stream, length, - &raw_checksum, cancellable, error)) - return FALSE; - - checksum = ostree_checksum_from_bytes (raw_checksum); - - if (!ostree_mutable_tree_replace_file (parent, filename, checksum, error)) - return FALSE; - - return TRUE; -} - -gboolean -flatpak_mtree_ensure_dir_metadata (OstreeRepo *repo, - OstreeMutableTree *mtree, - GCancellable *cancellable, - GError **error) -{ - g_autoptr(GVariant) dirmeta = NULL; - g_autoptr(GFileInfo) file_info = g_file_info_new (); - g_autofree guchar *csum = NULL; - g_autofree char *checksum = NULL; - - g_file_info_set_name (file_info, "/"); - g_file_info_set_file_type (file_info, G_FILE_TYPE_DIRECTORY); - g_file_info_set_attribute_uint32 (file_info, "unix::uid", 0); - g_file_info_set_attribute_uint32 (file_info, "unix::gid", 0); - g_file_info_set_attribute_uint32 (file_info, "unix::mode", 040755); - - dirmeta = ostree_create_directory_metadata (file_info, NULL); - if (!ostree_repo_write_metadata (repo, OSTREE_OBJECT_TYPE_DIR_META, NULL, - dirmeta, &csum, cancellable, error)) - return FALSE; - - checksum = ostree_checksum_from_bytes (csum); - ostree_mutable_tree_set_metadata_checksum (mtree, checksum); - - return TRUE; -} - -static gboolean -validate_component (FlatpakXml *component, - const char *ref, - const char *id, - char **tags, - const char *runtime, - const char *sdk) -{ - FlatpakXml *bundle, *text, *prev, *id_node, *id_text_node, *metadata, *value; - g_autofree char *id_text = NULL; - int i; - - if (g_strcmp0 (component->element_name, "component") != 0) - return FALSE; - - id_node = flatpak_xml_find (component, "id", NULL); - if (id_node == NULL) - return FALSE; - - id_text_node = flatpak_xml_find (id_node, NULL, NULL); - if (id_text_node == NULL || id_text_node->text == NULL) - return FALSE; - - id_text = g_strstrip (g_strdup (id_text_node->text)); - - /* Drop .desktop file suffix (unless the actual app id ends with .desktop) */ - if (g_str_has_suffix (id_text, ".desktop") && - !g_str_has_suffix (id, ".desktop")) - id_text[strlen (id_text) - strlen (".desktop")] = 0; - - if (!g_str_has_prefix (id_text, id)) - { - g_warning ("Invalid id %s", id_text); - return FALSE; - } - - while ((bundle = flatpak_xml_find (component, "bundle", &prev)) != NULL) - flatpak_xml_free (flatpak_xml_unlink (bundle, prev)); - - bundle = flatpak_xml_new ("bundle"); - bundle->attribute_names = g_new0 (char *, 2 * 4); - bundle->attribute_values = g_new0 (char *, 2 * 4); - bundle->attribute_names[0] = g_strdup ("type"); - bundle->attribute_values[0] = g_strdup ("flatpak"); - - i = 1; - if (runtime && !g_str_has_prefix (runtime, "runtime/")) - { - bundle->attribute_names[i] = g_strdup ("runtime"); - bundle->attribute_values[i] = g_strdup (runtime); - i++; - } - - if (sdk) - { - bundle->attribute_names[i] = g_strdup ("sdk"); - bundle->attribute_values[i] = g_strdup (sdk); - i++; - } - - text = flatpak_xml_new (NULL); - text->text = g_strdup (ref); - flatpak_xml_add (bundle, text); - - flatpak_xml_add (component, flatpak_xml_new_text (" ")); - flatpak_xml_add (component, bundle); - flatpak_xml_add (component, flatpak_xml_new_text ("\n ")); - - if (tags != NULL && tags[0] != NULL) - { - metadata = flatpak_xml_find (component, "metadata", NULL); - if (metadata == NULL) - { - metadata = flatpak_xml_new ("metadata"); - metadata->attribute_names = g_new0 (char *, 1); - metadata->attribute_values = g_new0 (char *, 1); - - flatpak_xml_add (component, flatpak_xml_new_text (" ")); - flatpak_xml_add (component, metadata); - flatpak_xml_add (component, flatpak_xml_new_text ("\n ")); - } - - value = flatpak_xml_new ("value"); - value->attribute_names = g_new0 (char *, 2); - value->attribute_values = g_new0 (char *, 2); - value->attribute_names[0] = g_strdup ("key"); - value->attribute_values[0] = g_strdup ("X-Flatpak-Tags"); - flatpak_xml_add (metadata, flatpak_xml_new_text ("\n ")); - flatpak_xml_add (metadata, value); - flatpak_xml_add (metadata, flatpak_xml_new_text ("\n ")); - - text = flatpak_xml_new (NULL); - text->text = g_strjoinv (",", tags); - flatpak_xml_add (value, text); - } - - return TRUE; -} - -gboolean -flatpak_appstream_xml_migrate (FlatpakXml *source, - FlatpakXml *dest, - const char *ref, - const char *id, - GKeyFile *metadata) -{ - FlatpakXml *source_components; - FlatpakXml *dest_components; - FlatpakXml *component; - FlatpakXml *prev_component; - gboolean migrated = FALSE; - g_auto(GStrv) tags = NULL; - g_autofree const char *runtime = NULL; - g_autofree const char *sdk = NULL; - const char *group; - - if (source->first_child == NULL || - source->first_child->next_sibling != NULL || - g_strcmp0 (source->first_child->element_name, "components") != 0) - return FALSE; - - if (g_str_has_prefix (ref, "app/")) - group = FLATPAK_METADATA_GROUP_APPLICATION; - else - group = FLATPAK_METADATA_GROUP_RUNTIME; - - tags = g_key_file_get_string_list (metadata, group, FLATPAK_METADATA_KEY_TAGS, - NULL, NULL); - runtime = g_key_file_get_string (metadata, group, - FLATPAK_METADATA_KEY_RUNTIME, NULL); - sdk = g_key_file_get_string (metadata, group, FLATPAK_METADATA_KEY_SDK, NULL); - - source_components = source->first_child; - dest_components = dest->first_child; - - component = source_components->first_child; - prev_component = NULL; - while (component != NULL) - { - FlatpakXml *next = component->next_sibling; - - if (validate_component (component, ref, id, tags, runtime, sdk)) - { - flatpak_xml_add (dest_components, - flatpak_xml_unlink (component, prev_component)); - migrated = TRUE; - } - else - { - prev_component = component; - } - - component = next; - } - - return migrated; -} - -static gboolean -copy_icon (const char *id, - GFile *icons_dir, - OstreeRepo *repo, - OstreeMutableTree *size_mtree, - const char *size, - GError **error) -{ - g_autofree char *icon_name = g_strconcat (id, ".png", NULL); - g_autoptr(GFile) size_dir = g_file_get_child (icons_dir, size); - g_autoptr(GFile) icon_file = g_file_get_child (size_dir, icon_name); - const char *checksum; - - if (!ostree_repo_file_ensure_resolved (OSTREE_REPO_FILE(icon_file), NULL)) - { - g_info ("No icon at size %s for %s", size, id); - return TRUE; - } - - checksum = ostree_repo_file_get_checksum (OSTREE_REPO_FILE(icon_file)); - if (!ostree_mutable_tree_replace_file (size_mtree, icon_name, checksum, error)) - return FALSE; - - return TRUE; -} - -static gboolean -extract_appstream (OstreeRepo *repo, - FlatpakXml *appstream_root, - FlatpakDecomposed *ref, - const char *id, - OstreeMutableTree *size1_mtree, - OstreeMutableTree *size2_mtree, - GCancellable *cancellable, - GError **error) -{ - g_autoptr(GFile) root = NULL; - g_autoptr(GFile) app_info_dir = NULL; - g_autoptr(GFile) xmls_dir = NULL; - g_autoptr(GFile) icons_dir = NULL; - g_autoptr(GFile) appstream_file = NULL; - g_autoptr(GFile) metadata = NULL; - g_autofree char *appstream_basename = NULL; - g_autoptr(GInputStream) in = NULL; - g_autoptr(FlatpakXml) xml_root = NULL; - g_autoptr(GKeyFile) keyfile = NULL; - - if (!ostree_repo_read_commit (repo, flatpak_decomposed_get_ref (ref), &root, NULL, NULL, error)) - return FALSE; - - keyfile = g_key_file_new (); - metadata = g_file_get_child (root, "metadata"); - if (g_file_query_exists (metadata, cancellable)) - { - g_autofree char *content = NULL; - gsize len; - - if (!g_file_load_contents (metadata, cancellable, &content, &len, NULL, error)) - return FALSE; - - if (!g_key_file_load_from_data (keyfile, content, len, G_KEY_FILE_NONE, error)) - return FALSE; - } - - app_info_dir = g_file_resolve_relative_path (root, "files/share/app-info"); - - xmls_dir = g_file_resolve_relative_path (app_info_dir, "xmls"); - icons_dir = g_file_resolve_relative_path (app_info_dir, "icons/flatpak"); - - appstream_basename = g_strconcat (id, ".xml.gz", NULL); - appstream_file = g_file_get_child (xmls_dir, appstream_basename); - - in = (GInputStream *) g_file_read (appstream_file, cancellable, error); - if (!in) - return FALSE; - - xml_root = flatpak_xml_parse (in, TRUE, cancellable, error); - if (xml_root == NULL) - return FALSE; - - if (flatpak_appstream_xml_migrate (xml_root, appstream_root, - flatpak_decomposed_get_ref (ref), id, keyfile)) - { - g_autoptr(GError) my_error = NULL; - FlatpakXml *components = appstream_root->first_child; - FlatpakXml *component = components->first_child; - - while (component != NULL) - { - FlatpakXml *component_id, *component_id_text_node; - g_autofree char *component_id_text = NULL; - char *component_id_suffix; - - if (g_strcmp0 (component->element_name, "component") != 0) - { - component = component->next_sibling; - continue; - } - - component_id = flatpak_xml_find (component, "id", NULL); - component_id_text_node = flatpak_xml_find (component_id, NULL, NULL); - - component_id_text = g_strstrip (g_strdup (component_id_text_node->text)); - - /* We're looking for a component that matches the app-id (id), but it - may have some further elements (separated by dot) and can also have - ".desktop" at the end which we need to strip out. Further complicating - things, some actual app ids ends in .desktop, such as org.telegram.desktop. */ - - component_id_suffix = component_id_text + strlen (id); /* Don't deref before we check for prefix match! */ - if (!g_str_has_prefix (component_id_text, id) || - (component_id_suffix[0] != 0 && component_id_suffix[0] != '.')) - { - component = component->next_sibling; - continue; - } - - if (g_str_has_suffix (component_id_suffix, ".desktop")) - component_id_suffix[strlen (component_id_suffix) - strlen (".desktop")] = 0; - - if (!copy_icon (component_id_text, icons_dir, repo, size1_mtree, "64x64", &my_error)) - { - g_print (_("Error copying 64x64 icon for component %s: %s\n"), component_id_text, my_error->message); - g_clear_error (&my_error); - } - - if (!copy_icon (component_id_text, icons_dir, repo, size2_mtree, "128x128", &my_error)) - { - g_print (_("Error copying 128x128 icon for component %s: %s\n"), component_id_text, my_error->message); - g_clear_error (&my_error); - } - - - /* We might match other prefixes, so keep on going */ - component = component->next_sibling; - } - } - - return TRUE; -} - -FlatpakXml * -flatpak_appstream_xml_new (void) -{ - FlatpakXml *appstream_root = NULL; - FlatpakXml *appstream_components; - - appstream_root = flatpak_xml_new ("root"); - appstream_components = flatpak_xml_new ("components"); - flatpak_xml_add (appstream_root, appstream_components); - flatpak_xml_add (appstream_components, flatpak_xml_new_text ("\n ")); - - appstream_components->attribute_names = g_new0 (char *, 3); - appstream_components->attribute_values = g_new0 (char *, 3); - appstream_components->attribute_names[0] = g_strdup ("version"); - appstream_components->attribute_values[0] = g_strdup ("0.8"); - appstream_components->attribute_names[1] = g_strdup ("origin"); - appstream_components->attribute_values[1] = g_strdup ("flatpak"); - - return appstream_root; -} - -gboolean -flatpak_appstream_xml_root_to_data (FlatpakXml *appstream_root, - GBytes **uncompressed, - GBytes **compressed, - GError **error) -{ - g_autoptr(GString) xml = NULL; - g_autoptr(GZlibCompressor) compressor = NULL; - g_autoptr(GOutputStream) out2 = NULL; - g_autoptr(GOutputStream) out = NULL; - - flatpak_xml_add (appstream_root->first_child, flatpak_xml_new_text ("\n")); - - xml = g_string_new (""); - flatpak_xml_to_string (appstream_root, xml); - - if (compressed) - { - compressor = g_zlib_compressor_new (G_ZLIB_COMPRESSOR_FORMAT_GZIP, -1); - out = g_memory_output_stream_new_resizable (); - out2 = g_converter_output_stream_new (out, G_CONVERTER (compressor)); - if (!g_output_stream_write_all (out2, xml->str, xml->len, - NULL, NULL, error)) - return FALSE; - if (!g_output_stream_close (out2, NULL, error)) - return FALSE; - } - - if (uncompressed) - *uncompressed = g_string_free_to_bytes (g_steal_pointer (&xml)); - - if (compressed) - *compressed = g_memory_output_stream_steal_as_bytes (G_MEMORY_OUTPUT_STREAM (out)); - - return TRUE; -} - -void -flatpak_appstream_xml_filter (FlatpakXml *appstream, - GRegex *allow_refs, - GRegex *deny_refs) -{ - FlatpakXml *components; - FlatpakXml *component; - FlatpakXml *prev_component, *old; - - for (components = appstream->first_child; - components != NULL; - components = components->next_sibling) - { - if (g_strcmp0 (components->element_name, "components") != 0) - continue; - - - prev_component = NULL; - component = components->first_child; - while (component != NULL) - { - FlatpakXml *bundle; - gboolean allow = FALSE; - - if (g_strcmp0 (component->element_name, "component") == 0) - { - bundle = flatpak_xml_find (component, "bundle", NULL); - if (bundle && bundle->first_child && bundle->first_child->text) - allow = flatpak_filters_allow_ref (allow_refs, deny_refs, bundle->first_child->text); - } - - if (allow) - { - prev_component = component; - component = component->next_sibling; - } - else - { - old = component; - - /* prev_component is same as before */ - component = component->next_sibling; - - flatpak_xml_unlink (old, prev_component); - flatpak_xml_free (old); - } - } - } -} - -/* This is similar to ostree_repo_list_refs(), but returns only valid flatpak - * refs, as FlatpakDecomposed. */ -static GHashTable * -flatpak_repo_list_flatpak_refs (OstreeRepo *repo, - GCancellable *cancellable, - GError **error) -{ - g_autoptr(GHashTable) refspecs = NULL; - g_autoptr(GHashTable) refs = NULL; - GHashTableIter iter; - gpointer key, value; - - if (!ostree_repo_list_refs_ext (repo, NULL, &refspecs, - OSTREE_REPO_LIST_REFS_EXT_EXCLUDE_REMOTES | OSTREE_REPO_LIST_REFS_EXT_EXCLUDE_MIRRORS, - cancellable, error)) - return NULL; - - refs = g_hash_table_new_full ((GHashFunc)flatpak_decomposed_hash, (GEqualFunc)flatpak_decomposed_equal, (GDestroyNotify)flatpak_decomposed_unref, g_free); - - g_hash_table_iter_init (&iter, refspecs); - while (g_hash_table_iter_next (&iter, &key, &value)) - { - const char *refstr = key; - const char *checksum = value; - FlatpakDecomposed *ref = NULL; - - ref = flatpak_decomposed_new_from_ref_take ((char *)refstr, NULL); - if (ref) - { - g_hash_table_iter_steal (&iter); - g_hash_table_insert (refs, ref, (char *)checksum); - } - } - - return g_steal_pointer (&refs); -} - -static gboolean -_flatpak_repo_generate_appstream (OstreeRepo *repo, - const char **gpg_key_ids, - const char *gpg_homedir, - FlatpakDecomposed **all_refs_keys, - guint n_keys, - GHashTable *all_commits, - const char *arch, - const char *subset, - guint64 timestamp, - GCancellable *cancellable, - GError **error) -{ - g_autoptr(FlatpakXml) appstream_root = NULL; - g_autoptr(GBytes) xml_data = NULL; - g_autoptr(GBytes) xml_gz_data = NULL; - g_autoptr(OstreeMutableTree) mtree = ostree_mutable_tree_new (); - g_autoptr(OstreeMutableTree) icons_mtree = NULL; - g_autoptr(OstreeMutableTree) icons_flatpak_mtree = NULL; - g_autoptr(OstreeMutableTree) size1_mtree = NULL; - g_autoptr(OstreeMutableTree) size2_mtree = NULL; - const char *compat_arch; - compat_arch = flatpak_get_compat_arch (arch); - const char *branch_names[] = { "appstream", "appstream2" }; - const char *collection_id; - - if (subset != NULL && *subset != 0) - g_info ("Generating appstream for %s, subset %s", arch, subset); - else - g_info ("Generating appstream for %s", arch); - - collection_id = ostree_repo_get_collection_id (repo); - - if (!flatpak_mtree_ensure_dir_metadata (repo, mtree, cancellable, error)) - return FALSE; - - if (!flatpak_mtree_create_dir (repo, mtree, "icons", &icons_mtree, error)) - return FALSE; - - if (!flatpak_mtree_create_dir (repo, icons_mtree, "64x64", &size1_mtree, error)) - return FALSE; - - if (!flatpak_mtree_create_dir (repo, icons_mtree, "128x128", &size2_mtree, error)) - return FALSE; - - /* For compatibility with libappstream we create a $origin ("flatpak") subdirectory with symlinks - * to the size directories thus matching the standard merged appstream layout if we assume the - * appstream has origin=flatpak, which flatpak-builder creates. - * - * See https://github.com/ximion/appstream/pull/224 for details. - */ - if (!flatpak_mtree_create_dir (repo, icons_mtree, "flatpak", &icons_flatpak_mtree, error)) - return FALSE; - if (!flatpak_mtree_create_symlink (repo, icons_flatpak_mtree, "64x64", "../64x64", error)) - return FALSE; - if (!flatpak_mtree_create_symlink (repo, icons_flatpak_mtree, "128x128", "../128x128", error)) - return FALSE; - - appstream_root = flatpak_appstream_xml_new (); - - for (int i = 0; i < n_keys; i++) - { - FlatpakDecomposed *ref = all_refs_keys[i]; - GVariant *commit_v = NULL; - VarMetadataRef commit_metadata; - g_autoptr(GError) my_error = NULL; - g_autofree char *id = NULL; - - if (!flatpak_decomposed_is_arch (ref, arch)) - { - g_autoptr(FlatpakDecomposed) main_ref = NULL; - - /* Include refs that don't match the main arch (e.g. x86_64), if they match - the compat arch (e.g. i386) and the main arch version is not in the repo */ - if (compat_arch != NULL && flatpak_decomposed_is_arch (ref, compat_arch)) - main_ref = flatpak_decomposed_new_from_decomposed (ref, 0, NULL, compat_arch, NULL, NULL); - - if (main_ref == NULL || - g_hash_table_lookup (all_commits, main_ref)) - continue; - } - - commit_v = g_hash_table_lookup (all_commits, ref); - g_assert (commit_v != NULL); - - commit_metadata = var_commit_get_metadata (var_commit_from_gvariant (commit_v)); - if (var_metadata_lookup (commit_metadata, OSTREE_COMMIT_META_KEY_ENDOFLIFE, NULL, NULL) || - var_metadata_lookup (commit_metadata, OSTREE_COMMIT_META_KEY_ENDOFLIFE_REBASE, NULL, NULL)) - { - g_info (_("%s is end-of-life, ignoring for appstream"), flatpak_decomposed_get_ref (ref)); - continue; - } - - if (*subset != 0) - { - VarVariantRef xa_subsets_v; - gboolean in_subset = FALSE; - - if (var_metadata_lookup (commit_metadata, "xa.subsets", NULL, &xa_subsets_v)) - { - VarArrayofstringRef xa_subsets = var_arrayofstring_from_variant (xa_subsets_v); - gsize len = var_arrayofstring_get_length (xa_subsets); - - for (gsize j = 0; j < len; j++) - { - const char *xa_subset = var_arrayofstring_get_at (xa_subsets, j); - if (strcmp (subset, xa_subset) == 0) - { - in_subset = TRUE; - break; - } - } - } - - if (!in_subset) - continue; - } - - id = flatpak_decomposed_dup_id (ref); - if (!extract_appstream (repo, appstream_root, - ref, id, size1_mtree, size2_mtree, - cancellable, &my_error)) - { - if (flatpak_decomposed_is_app (ref)) - g_print (_("No appstream data for %s: %s\n"), flatpak_decomposed_get_ref (ref), my_error->message); - continue; - } - } - - if (!flatpak_appstream_xml_root_to_data (appstream_root, &xml_data, &xml_gz_data, error)) - return FALSE; - - for (int i = 0; i < G_N_ELEMENTS (branch_names); i++) - { - gboolean skip_commit = FALSE; - const char *branch_prefix = branch_names[i]; - g_autoptr(GFile) root = NULL; - g_autofree char *branch = NULL; - g_autofree char *parent = NULL; - g_autofree char *commit_checksum = NULL; - - if (*subset != 0 && i == 0) - continue; /* No old-style branch for subsets */ - - if (*subset != 0) - branch = g_strdup_printf ("%s/%s-%s", branch_prefix, subset, arch); - else - branch = g_strdup_printf ("%s/%s", branch_prefix, arch); - - if (!flatpak_repo_resolve_rev (repo, collection_id, NULL, branch, TRUE, - &parent, cancellable, error)) - return FALSE; - - if (i == 0) - { - if (!flatpak_mtree_add_file_from_bytes (repo, xml_gz_data, mtree, "appstream.xml.gz", cancellable, error)) - return FALSE; - } - else - { - if (!ostree_mutable_tree_remove (mtree, "appstream.xml.gz", TRUE, error)) - return FALSE; - - if (!flatpak_mtree_add_file_from_bytes (repo, xml_data, mtree, "appstream.xml", cancellable, error)) - return FALSE; - } - - if (!ostree_repo_write_mtree (repo, mtree, &root, cancellable, error)) - return FALSE; - - /* No need to commit if nothing changed */ - if (parent) - { - g_autoptr(GFile) parent_root = NULL; - - if (!ostree_repo_read_commit (repo, parent, &parent_root, NULL, cancellable, error)) - return FALSE; - - if (g_file_equal (root, parent_root)) - { - skip_commit = TRUE; - g_info ("Not updating %s, no change", branch); - } - } - - if (!skip_commit) - { - g_autoptr(GVariantDict) metadata_dict = NULL; - g_autoptr(GVariant) metadata = NULL; - - /* Add bindings to the metadata. Do this even if P2P support is not - * enabled, as it might be enable for other flatpak builds. */ - metadata_dict = g_variant_dict_new (NULL); - g_variant_dict_insert (metadata_dict, "ostree.collection-binding", - "s", (collection_id != NULL) ? collection_id : ""); - g_variant_dict_insert_value (metadata_dict, "ostree.ref-binding", - g_variant_new_strv ((const gchar * const *) &branch, 1)); - metadata = g_variant_ref_sink (g_variant_dict_end (metadata_dict)); - - if (timestamp > 0) - { - if (!ostree_repo_write_commit_with_time (repo, parent, "Update", NULL, metadata, - OSTREE_REPO_FILE (root), - timestamp, - &commit_checksum, - cancellable, error)) - return FALSE; - } - else - { - if (!ostree_repo_write_commit (repo, parent, "Update", NULL, metadata, - OSTREE_REPO_FILE (root), - &commit_checksum, cancellable, error)) - return FALSE; - } - - if (gpg_key_ids) - { - for (int j = 0; gpg_key_ids[j] != NULL; j++) - { - const char *keyid = gpg_key_ids[j]; - - if (!ostree_repo_sign_commit (repo, - commit_checksum, - keyid, - gpg_homedir, - cancellable, - error)) - return FALSE; - } - } - - g_info ("Creating appstream branch %s", branch); - if (collection_id != NULL) - { - const OstreeCollectionRef collection_ref = { (char *) collection_id, branch }; - ostree_repo_transaction_set_collection_ref (repo, &collection_ref, commit_checksum); - } - else - { - ostree_repo_transaction_set_ref (repo, NULL, branch, commit_checksum); - } - } - } - - return TRUE; -} - - -gboolean -flatpak_repo_generate_appstream (OstreeRepo *repo, - const char **gpg_key_ids, - const char *gpg_homedir, - guint64 timestamp, - GCancellable *cancellable, - GError **error) -{ - g_autoptr(GHashTable) all_refs = NULL; - g_autoptr(GHashTable) all_commits = NULL; - g_autofree FlatpakDecomposed **all_refs_keys = NULL; - guint n_keys; - g_autoptr(GPtrArray) arches = NULL; /* (element-type utf8 utf8) */ - g_autoptr(GPtrArray) subsets = NULL; /* (element-type utf8 utf8) */ - g_autoptr(FlatpakRepoTransaction) transaction = NULL; - OstreeRepoTransactionStats stats; - - arches = g_ptr_array_new_with_free_func (g_free); - subsets = g_ptr_array_new_with_free_func (g_free); - - g_ptr_array_add (subsets, g_strdup ("")); - - all_refs = flatpak_repo_list_flatpak_refs (repo, cancellable, error); - if (all_refs == NULL) - return FALSE; - - all_commits = g_hash_table_new_full ((GHashFunc)flatpak_decomposed_hash, (GEqualFunc)flatpak_decomposed_equal, (GDestroyNotify)flatpak_decomposed_unref, (GDestroyNotify)g_variant_unref); - - GLNX_HASH_TABLE_FOREACH_KV (all_refs, FlatpakDecomposed *, ref, const char *, commit) - { - VarMetadataRef commit_metadata; - VarVariantRef xa_subsets_v; - const char *reverse_compat_arch; - char *new_arch = NULL; - g_autoptr(GVariant) commit_v = NULL; - - if (!ostree_repo_load_variant (repo, OSTREE_OBJECT_TYPE_COMMIT, commit, &commit_v, NULL)) - { - g_warning ("Couldn't load commit %s (ref %s)", commit, flatpak_decomposed_get_ref (ref)); - continue; - } - - g_hash_table_insert (all_commits, flatpak_decomposed_ref (ref), g_variant_ref (commit_v)); - - /* Compute list of subsets */ - commit_metadata = var_commit_get_metadata (var_commit_from_gvariant (commit_v)); - if (var_metadata_lookup (commit_metadata, "xa.subsets", NULL, &xa_subsets_v)) - { - VarArrayofstringRef xa_subsets = var_arrayofstring_from_variant (xa_subsets_v); - gsize len = var_arrayofstring_get_length (xa_subsets); - for (gsize j = 0; j < len; j++) - { - const char *subset = var_arrayofstring_get_at (xa_subsets, j); - - if (!flatpak_g_ptr_array_contains_string (subsets, subset)) - g_ptr_array_add (subsets, g_strdup (subset)); - } - } - - /* Compute list of arches */ - if (!flatpak_decomposed_is_arches (ref, arches->len, (const char **) arches->pdata)) - { - new_arch = flatpak_decomposed_dup_arch (ref); - g_ptr_array_add (arches, new_arch); - - /* If repo contains e.g. i386, also generated x86-64 appdata */ - reverse_compat_arch = flatpak_get_compat_arch_reverse (new_arch); - if (reverse_compat_arch != NULL && - !flatpak_g_ptr_array_contains_string (arches, reverse_compat_arch)) - g_ptr_array_add (arches, g_strdup (reverse_compat_arch)); - } - } - - g_ptr_array_sort (subsets, flatpak_strcmp0_ptr); - g_ptr_array_sort (arches, flatpak_strcmp0_ptr); - - all_refs_keys = (FlatpakDecomposed **) g_hash_table_get_keys_as_array (all_refs, &n_keys); - - /* Sort refs so that appdata order is stable for e.g. deltas */ - g_qsort_with_data (all_refs_keys, n_keys, sizeof (FlatpakDecomposed *), (GCompareDataFunc) flatpak_decomposed_strcmp_p, NULL); - - transaction = flatpak_repo_transaction_start (repo, cancellable, error); - if (transaction == NULL) - return FALSE; - - for (int l = 0; l < subsets->len; l++) - { - const char *subset = g_ptr_array_index (subsets, l); - - for (int k = 0; k < arches->len; k++) - { - const char *arch = g_ptr_array_index (arches, k); - - if (!_flatpak_repo_generate_appstream (repo, - gpg_key_ids, - gpg_homedir, - all_refs_keys, - n_keys, - all_commits, - arch, - subset, - timestamp, - cancellable, - error)) - return FALSE; - } - } - - if (!ostree_repo_commit_transaction (repo, &stats, cancellable, error)) - return FALSE; - - return TRUE; -} - -void -flatpak_extension_free (FlatpakExtension *extension) -{ - g_free (extension->id); - g_free (extension->installed_id); - g_free (extension->commit); - flatpak_decomposed_unref (extension->ref); - g_free (extension->directory); - g_free (extension->files_path); - g_free (extension->add_ld_path); - g_free (extension->subdir_suffix); - g_strfreev (extension->merge_dirs); - g_free (extension); -} - -static int -flatpak_extension_compare (gconstpointer _a, - gconstpointer _b) -{ - const FlatpakExtension *a = _a; - const FlatpakExtension *b = _b; - - return b->priority - a->priority; -} - -static FlatpakExtension * -flatpak_extension_new (const char *id, - const char *extension, - FlatpakDecomposed *ref, - const char *directory, - const char *add_ld_path, - const char *subdir_suffix, - char **merge_dirs, - GFile *files, - GFile *deploy_dir, - gboolean is_unmaintained, - OstreeRepo *repo) -{ - FlatpakExtension *ext = g_new0 (FlatpakExtension, 1); - g_autoptr(GBytes) deploy_data = NULL; - - ext->id = g_strdup (id); - ext->installed_id = g_strdup (extension); - ext->ref = flatpak_decomposed_ref (ref); - ext->directory = g_strdup (directory); - ext->files_path = g_file_get_path (files); - ext->add_ld_path = g_strdup (add_ld_path); - ext->subdir_suffix = g_strdup (subdir_suffix); - ext->merge_dirs = g_strdupv (merge_dirs); - ext->is_unmaintained = is_unmaintained; - - /* Unmaintained extensions won't have a deploy or commit; see - * https://github.com/flatpak/flatpak/issues/167 */ - if (deploy_dir && !is_unmaintained) - { - deploy_data = flatpak_load_deploy_data (deploy_dir, ref, repo, FLATPAK_DEPLOY_VERSION_ANY, NULL, NULL); - if (deploy_data) - ext->commit = g_strdup (flatpak_deploy_data_get_commit (deploy_data)); - } - - if (is_unmaintained) - ext->priority = 1000; - else - { - g_autoptr(GKeyFile) keyfile = g_key_file_new (); - g_autofree char *metadata_path = g_build_filename (ext->files_path, "../metadata", NULL); - - if (g_key_file_load_from_file (keyfile, metadata_path, G_KEY_FILE_NONE, NULL)) - ext->priority = g_key_file_get_integer (keyfile, - FLATPAK_METADATA_GROUP_EXTENSION_OF, - FLATPAK_METADATA_KEY_PRIORITY, - NULL); - } - - return ext; -} - -gboolean -flatpak_extension_matches_reason (const char *extension_id, - const char *reasons, - gboolean default_value) -{ - const char *extension_basename; - g_auto(GStrv) reason_list = NULL; - size_t i; - - if (reasons == NULL || *reasons == 0) - return default_value; - - extension_basename = strrchr (extension_id, '.'); - if (extension_basename == NULL) - return FALSE; - extension_basename += 1; - - reason_list = g_strsplit (reasons, ";", -1); - - for (i = 0; reason_list[i]; ++i) - { - const char *reason = reason_list[i]; - - if (strcmp (reason, "active-gl-driver") == 0) - { - /* handled below */ - const char **gl_drivers = flatpak_get_gl_drivers (); - size_t j; - - for (j = 0; gl_drivers[j]; j++) - { - if (strcmp (gl_drivers[j], extension_basename) == 0) - return TRUE; - } - } - else if (strcmp (reason, "active-gtk-theme") == 0) - { - const char *gtk_theme = flatpak_get_gtk_theme (); - if (strcmp (gtk_theme, extension_basename) == 0) - return TRUE; - } - else if (strcmp (reason, "have-intel-gpu") == 0) - { - /* Used for Intel VAAPI driver extension */ - if (flatpak_get_have_intel_gpu ()) - return TRUE; - } - else if (g_str_has_prefix (reason, "have-kernel-module-")) - { - const char *module_name = reason + strlen ("have-kernel-module-"); - - if (flatpak_get_have_kernel_module (module_name)) - return TRUE; - } - else if (g_str_has_prefix (reason, "on-xdg-desktop-")) - { - const char *desktop_name = reason + strlen ("on-xdg-desktop-"); - const char *current_desktop_var = g_getenv ("XDG_CURRENT_DESKTOP"); - g_auto(GStrv) current_desktop_names = NULL; - size_t j; - - if (!current_desktop_var) - continue; - - current_desktop_names = g_strsplit (current_desktop_var, ":", -1); - - for (j = 0; current_desktop_names[j]; ++j) - { - if (g_ascii_strcasecmp (desktop_name, current_desktop_names[j]) == 0) - return TRUE; - } - } - } - - return FALSE; -} - -static GList * -add_extension (GKeyFile *metakey, - const char *group, - const char *extension, - const char *arch, - const char *branch, - GList *res) -{ - FlatpakExtension *ext; - g_autofree char *directory = g_key_file_get_string (metakey, group, - FLATPAK_METADATA_KEY_DIRECTORY, - NULL); - g_autofree char *add_ld_path = g_key_file_get_string (metakey, group, - FLATPAK_METADATA_KEY_ADD_LD_PATH, - NULL); - g_auto(GStrv) merge_dirs = g_key_file_get_string_list (metakey, group, - FLATPAK_METADATA_KEY_MERGE_DIRS, - NULL, NULL); - g_autofree char *enable_if = g_key_file_get_string (metakey, group, - FLATPAK_METADATA_KEY_ENABLE_IF, - NULL); - g_autofree char *subdir_suffix = g_key_file_get_string (metakey, group, - FLATPAK_METADATA_KEY_SUBDIRECTORY_SUFFIX, - NULL); - g_autoptr(FlatpakDecomposed) ref = NULL; - gboolean is_unmaintained = FALSE; - g_autoptr(GFile) files = NULL; - g_autoptr(GFile) deploy_dir = NULL; - g_autoptr(FlatpakDir) dir = NULL; - - if (directory == NULL) - return res; - - ref = flatpak_decomposed_new_from_parts (FLATPAK_KINDS_RUNTIME, extension, arch, branch, NULL); - if (ref == NULL) - return res; - - files = flatpak_find_unmaintained_extension_dir_if_exists (extension, arch, branch, NULL); - - if (files == NULL) - { - deploy_dir = flatpak_find_deploy_dir_for_ref (ref, &dir, NULL, NULL); - if (deploy_dir) - files = g_file_get_child (deploy_dir, "files"); - } - else - is_unmaintained = TRUE; - - /* Prefer a full extension (org.freedesktop.Locale) over subdirectory ones (org.freedesktop.Locale.sv) */ - if (files != NULL) - { - if (flatpak_extension_matches_reason (extension, enable_if, TRUE)) - { - ext = flatpak_extension_new (extension, extension, ref, directory, - add_ld_path, subdir_suffix, merge_dirs, - files, deploy_dir, is_unmaintained, - is_unmaintained ? NULL : flatpak_dir_get_repo (dir)); - res = g_list_prepend (res, ext); - } - } - else if (g_key_file_get_boolean (metakey, group, - FLATPAK_METADATA_KEY_SUBDIRECTORIES, NULL)) - { - g_autofree char *prefix = g_strconcat (extension, ".", NULL); - g_auto(GStrv) ids = NULL; - g_auto(GStrv) unmaintained_refs = NULL; - int j; - - ids = flatpak_list_deployed_refs ("runtime", prefix, arch, branch, - NULL, NULL); - for (j = 0; ids != NULL && ids[j] != NULL; j++) - { - const char *id = ids[j]; - g_autofree char *extended_dir = g_build_filename (directory, id + strlen (prefix), NULL); - g_autoptr(FlatpakDecomposed) dir_ref = NULL; - g_autoptr(GFile) subdir_deploy_dir = NULL; - g_autoptr(GFile) subdir_files = NULL; - g_autoptr(FlatpakDir) subdir_dir = NULL; - - dir_ref = flatpak_decomposed_new_from_parts (FLATPAK_KINDS_RUNTIME, id, arch, branch, NULL); - if (dir_ref == NULL) - continue; - - subdir_deploy_dir = flatpak_find_deploy_dir_for_ref (dir_ref, &subdir_dir, NULL, NULL); - if (subdir_deploy_dir) - subdir_files = g_file_get_child (subdir_deploy_dir, "files"); - - if (subdir_files && flatpak_extension_matches_reason (id, enable_if, TRUE)) - { - ext = flatpak_extension_new (extension, id, dir_ref, extended_dir, - add_ld_path, subdir_suffix, merge_dirs, - subdir_files, subdir_deploy_dir, FALSE, - flatpak_dir_get_repo (subdir_dir)); - ext->needs_tmpfs = TRUE; - res = g_list_prepend (res, ext); - } - } - - unmaintained_refs = flatpak_list_unmaintained_refs (prefix, arch, branch, - NULL, NULL); - for (j = 0; unmaintained_refs != NULL && unmaintained_refs[j] != NULL; j++) - { - g_autofree char *extended_dir = g_build_filename (directory, unmaintained_refs[j] + strlen (prefix), NULL); - g_autoptr(FlatpakDecomposed) dir_ref = NULL; - g_autoptr(GFile) subdir_files = flatpak_find_unmaintained_extension_dir_if_exists (unmaintained_refs[j], arch, branch, NULL); - - dir_ref = flatpak_decomposed_new_from_parts (FLATPAK_KINDS_RUNTIME, unmaintained_refs[j], arch, branch, NULL); - if (dir_ref == NULL) - continue; - - if (subdir_files && flatpak_extension_matches_reason (unmaintained_refs[j], enable_if, TRUE)) - { - ext = flatpak_extension_new (extension, unmaintained_refs[j], dir_ref, - extended_dir, add_ld_path, subdir_suffix, - merge_dirs, subdir_files, NULL, TRUE, NULL); - ext->needs_tmpfs = TRUE; - res = g_list_prepend (res, ext); - } - } - } - - return res; -} - -void -flatpak_parse_extension_with_tag (const char *extension, - char **name, - char **tag) -{ - const char *tag_chr = strchr (extension, '@'); - - if (tag_chr) - { - if (name != NULL) - *name = g_strndup (extension, tag_chr - extension); - - /* Everything after the @ */ - if (tag != NULL) - *tag = g_strdup (tag_chr + 1); - - return; - } - - if (name != NULL) - *name = g_strdup (extension); - - if (tag != NULL) - *tag = NULL; -} - -GList * -flatpak_list_extensions (GKeyFile *metakey, - const char *arch, - const char *default_branch) -{ - g_auto(GStrv) groups = NULL; - int i, j; - GList *res; - - res = NULL; - - if (arch == NULL) - arch = flatpak_get_arch (); - - groups = g_key_file_get_groups (metakey, NULL); - for (i = 0; groups[i] != NULL; i++) - { - char *extension; - - if (g_str_has_prefix (groups[i], FLATPAK_METADATA_GROUP_PREFIX_EXTENSION) && - *(extension = (groups[i] + strlen (FLATPAK_METADATA_GROUP_PREFIX_EXTENSION))) != 0) - { - g_autofree char *version = g_key_file_get_string (metakey, groups[i], - FLATPAK_METADATA_KEY_VERSION, - NULL); - g_auto(GStrv) versions = g_key_file_get_string_list (metakey, groups[i], - FLATPAK_METADATA_KEY_VERSIONS, - NULL, NULL); - g_autofree char *name = NULL; - const char *default_branches[] = { default_branch, NULL}; - const char **branches; - - flatpak_parse_extension_with_tag (extension, &name, NULL); - - if (versions) - branches = (const char **) versions; - else - { - if (version) - default_branches[0] = version; - branches = default_branches; - } - - for (j = 0; branches[j] != NULL; j++) - res = add_extension (metakey, groups[i], name, arch, branches[j], res); - } - } - - return g_list_sort (g_list_reverse (res), flatpak_extension_compare); -} - -typedef struct -{ - FlatpakXml *current; -} XmlData; - -FlatpakXml * -flatpak_xml_new (const gchar *element_name) -{ - FlatpakXml *node = g_new0 (FlatpakXml, 1); - - node->element_name = g_strdup (element_name); - return node; -} - -FlatpakXml * -flatpak_xml_new_text (const gchar *text) -{ - FlatpakXml *node = g_new0 (FlatpakXml, 1); - - node->text = g_strdup (text); - return node; -} - -void -flatpak_xml_add (FlatpakXml *parent, FlatpakXml *node) -{ - node->parent = parent; - - if (parent->first_child == NULL) - parent->first_child = node; - else - parent->last_child->next_sibling = node; - parent->last_child = node; -} - -static void -xml_start_element (GMarkupParseContext *context, - const gchar *element_name, - const gchar **attribute_names, - const gchar **attribute_values, - gpointer user_data, - GError **error) -{ - XmlData *data = user_data; - FlatpakXml *node; - - node = flatpak_xml_new (element_name); - node->attribute_names = g_strdupv ((char **) attribute_names); - node->attribute_values = g_strdupv ((char **) attribute_values); - - flatpak_xml_add (data->current, node); - data->current = node; -} - -static void -xml_end_element (GMarkupParseContext *context, - const gchar *element_name, - gpointer user_data, - GError **error) -{ - XmlData *data = user_data; - - data->current = data->current->parent; -} - -static void -xml_text (GMarkupParseContext *context, - const gchar *text, - gsize text_len, - gpointer user_data, - GError **error) -{ - XmlData *data = user_data; - FlatpakXml *node; - - node = flatpak_xml_new (NULL); - node->text = g_strndup (text, text_len); - flatpak_xml_add (data->current, node); -} - -static void -xml_passthrough (GMarkupParseContext *context, - const gchar *passthrough_text, - gsize text_len, - gpointer user_data, - GError **error) -{ -} - -static GMarkupParser xml_parser = { - xml_start_element, - xml_end_element, - xml_text, - xml_passthrough, - NULL -}; - -void -flatpak_xml_free (FlatpakXml *node) -{ - FlatpakXml *child; - - if (node == NULL) - return; - - child = node->first_child; - while (child != NULL) - { - FlatpakXml *next = child->next_sibling; - flatpak_xml_free (child); - child = next; - } - - g_free (node->element_name); - g_free (node->text); - g_strfreev (node->attribute_names); - g_strfreev (node->attribute_values); - g_free (node); -} - - -void -flatpak_xml_to_string (FlatpakXml *node, GString *res) -{ - int i; - FlatpakXml *child; - - if (node->parent == NULL) - g_string_append (res, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"); - - if (node->element_name) - { - if (node->parent != NULL) - { - g_string_append (res, "<"); - g_string_append (res, node->element_name); - if (node->attribute_names) - { - for (i = 0; node->attribute_names[i] != NULL; i++) - { - g_string_append_printf (res, " %s=\"%s\"", - node->attribute_names[i], - node->attribute_values[i]); - } - } - if (node->first_child == NULL) - g_string_append (res, "/>"); - else - g_string_append (res, ">"); - } - - child = node->first_child; - while (child != NULL) - { - flatpak_xml_to_string (child, res); - child = child->next_sibling; - } - if (node->parent != NULL) - { - if (node->first_child != NULL) - g_string_append_printf (res, "</%s>", node->element_name); - } - } - else if (node->text) - { - g_autofree char *escaped = g_markup_escape_text (node->text, -1); - g_string_append (res, escaped); - } -} - -FlatpakXml * -flatpak_xml_unlink (FlatpakXml *node, - FlatpakXml *prev_sibling) -{ - FlatpakXml *parent = node->parent; - - if (parent == NULL) - return node; - - if (parent->first_child == node) - parent->first_child = node->next_sibling; - - if (parent->last_child == node) - parent->last_child = prev_sibling; - - if (prev_sibling) - prev_sibling->next_sibling = node->next_sibling; - - node->parent = NULL; - node->next_sibling = NULL; - - return node; -} - -FlatpakXml * -flatpak_xml_find (FlatpakXml *node, - const char *type, - FlatpakXml **prev_child_out) -{ - FlatpakXml *child = NULL; - FlatpakXml *prev_child = NULL; - - child = node->first_child; - prev_child = NULL; - while (child != NULL) - { - FlatpakXml *next = child->next_sibling; - - if (g_strcmp0 (child->element_name, type) == 0) - { - if (prev_child_out) - *prev_child_out = prev_child; - return child; - } - - prev_child = child; - child = next; - } - - return NULL; -} - - -FlatpakXml * -flatpak_xml_parse (GInputStream *in, - gboolean compressed, - GCancellable *cancellable, - GError **error) -{ - g_autoptr(GInputStream) real_in = NULL; - g_autoptr(FlatpakXml) xml_root = NULL; - XmlData data = { 0 }; - char buffer[32 * 1024]; - gssize len; - g_autoptr(GMarkupParseContext) ctx = NULL; - - if (compressed) - { - g_autoptr(GZlibDecompressor) decompressor = NULL; - decompressor = g_zlib_decompressor_new (G_ZLIB_COMPRESSOR_FORMAT_GZIP); - real_in = g_converter_input_stream_new (in, G_CONVERTER (decompressor)); - } - else - { - real_in = g_object_ref (in); - } - - xml_root = flatpak_xml_new ("root"); - data.current = xml_root; - - ctx = g_markup_parse_context_new (&xml_parser, - G_MARKUP_PREFIX_ERROR_POSITION, - &data, - NULL); - - while ((len = g_input_stream_read (real_in, buffer, sizeof (buffer), - cancellable, error)) > 0) - { - if (!g_markup_parse_context_parse (ctx, buffer, len, error)) - return NULL; - } - - if (len < 0) - return NULL; - - return g_steal_pointer (&xml_root); -} - -#define OSTREE_STATIC_DELTA_META_ENTRY_FORMAT "(uayttay)" -#define OSTREE_STATIC_DELTA_FALLBACK_FORMAT "(yaytt)" -#define OSTREE_STATIC_DELTA_SUPERBLOCK_FORMAT "(a{sv}tayay" OSTREE_COMMIT_GVARIANT_STRING "aya" OSTREE_STATIC_DELTA_META_ENTRY_FORMAT "a" OSTREE_STATIC_DELTA_FALLBACK_FORMAT ")" - -static inline guint64 -maybe_swap_endian_u64 (gboolean swap, - guint64 v) -{ - if (!swap) - return v; - return GUINT64_SWAP_LE_BE (v); -} - -static guint64 -flatpak_bundle_get_installed_size (GVariant *bundle, gboolean byte_swap) -{ - guint64 total_usize = 0; - g_autoptr(GVariant) meta_entries = NULL; - guint i, n_parts; - - g_variant_get_child (bundle, 6, "@a" OSTREE_STATIC_DELTA_META_ENTRY_FORMAT, &meta_entries); - n_parts = g_variant_n_children (meta_entries); - - for (i = 0; i < n_parts; i++) - { - guint32 version; - guint64 size, usize; - g_autoptr(GVariant) objects = NULL; - - g_variant_get_child (meta_entries, i, "(u@aytt@ay)", - &version, NULL, &size, &usize, &objects); - - total_usize += maybe_swap_endian_u64 (byte_swap, usize); - } - - return total_usize; -} - -GVariant * -flatpak_bundle_load (GFile *file, - char **commit, - FlatpakDecomposed **ref, - char **origin, - char **runtime_repo, - char **app_metadata, - guint64 *installed_size, - GBytes **gpg_keys, - char **collection_id, - GError **error) -{ - g_autoptr(GVariant) delta = NULL; - g_autoptr(GVariant) metadata = NULL; - g_autoptr(GBytes) bytes = NULL; - g_autoptr(GBytes) copy = NULL; - g_autoptr(GVariant) to_csum_v = NULL; - - guint8 endianness_char; - gboolean byte_swap = FALSE; - - GMappedFile *mfile = g_mapped_file_new (flatpak_file_get_path_cached (file), FALSE, error); - - if (mfile == NULL) - return NULL; - - bytes = g_mapped_file_get_bytes (mfile); - g_mapped_file_unref (mfile); - - delta = g_variant_new_from_bytes (G_VARIANT_TYPE (OSTREE_STATIC_DELTA_SUPERBLOCK_FORMAT), bytes, FALSE); - g_variant_ref_sink (delta); - - to_csum_v = g_variant_get_child_value (delta, 3); - if (!ostree_validate_structureof_csum_v (to_csum_v, error)) - return NULL; - - metadata = g_variant_get_child_value (delta, 0); - - if (g_variant_lookup (metadata, "ostree.endianness", "y", &endianness_char)) - { - int file_byte_order = G_BYTE_ORDER; - switch (endianness_char) - { - case 'l': - file_byte_order = G_LITTLE_ENDIAN; - break; - - case 'B': - file_byte_order = G_BIG_ENDIAN; - break; - - default: - break; - } - byte_swap = (G_BYTE_ORDER != file_byte_order); - } - - if (commit) - *commit = ostree_checksum_from_bytes_v (to_csum_v); - - if (installed_size) - *installed_size = flatpak_bundle_get_installed_size (delta, byte_swap); - - if (ref != NULL) - { - FlatpakDecomposed *the_ref = NULL; - g_autofree char *ref_str = NULL; - - if (!g_variant_lookup (metadata, "ref", "s", &ref_str)) - { - flatpak_fail_error (error, FLATPAK_ERROR_INVALID_DATA, _("Invalid bundle, no ref in metadata")); - return NULL; - } - - the_ref = flatpak_decomposed_new_from_ref (ref_str, error); - if (the_ref == NULL) - return NULL; - - g_clear_pointer (ref, flatpak_decomposed_unref); - *ref = the_ref; - } - - if (origin != NULL) - { - if (!g_variant_lookup (metadata, "origin", "s", origin)) - *origin = NULL; - } - - if (runtime_repo != NULL) - { - if (!g_variant_lookup (metadata, "runtime-repo", "s", runtime_repo)) - *runtime_repo = NULL; - } - - if (collection_id != NULL) - { - if (!g_variant_lookup (metadata, "collection-id", "s", collection_id)) - { - *collection_id = NULL; - } - else if (**collection_id == '\0') - { - g_free (*collection_id); - *collection_id = NULL; + const char *gtk_theme = flatpak_get_gtk_theme (); + if (strcmp (gtk_theme, extension_basename) == 0) + return TRUE; } - } - - if (app_metadata != NULL) - { - if (!g_variant_lookup (metadata, "metadata", "s", app_metadata)) - *app_metadata = NULL; - } - - if (gpg_keys != NULL) - { - g_autoptr(GVariant) gpg_value = g_variant_lookup_value (metadata, "gpg-keys", - G_VARIANT_TYPE ("ay")); - if (gpg_value) + else if (strcmp (reason, "have-intel-gpu") == 0) { - gsize n_elements; - const char *data = g_variant_get_fixed_array (gpg_value, &n_elements, 1); - *gpg_keys = g_bytes_new (data, n_elements); + /* Used for Intel VAAPI driver extension */ + if (flatpak_get_have_intel_gpu ()) + return TRUE; } - else + else if (g_str_has_prefix (reason, "have-kernel-module-")) { - *gpg_keys = NULL; - } - } - - /* Make a copy of the data so we can return it after freeing the file */ - copy = g_bytes_new (g_variant_get_data (metadata), - g_variant_get_size (metadata)); - return g_variant_ref_sink (g_variant_new_from_bytes (g_variant_get_type (metadata), - copy, - FALSE)); -} - -gboolean -flatpak_pull_from_bundle (OstreeRepo *repo, - GFile *file, - const char *remote, - const char *ref, - gboolean require_gpg_signature, - GCancellable *cancellable, - GError **error) -{ - gsize metadata_size = 0; - g_autofree char *metadata_contents = NULL; - g_autofree char *to_checksum = NULL; - g_autoptr(GFile) root = NULL; - g_autoptr(GFile) metadata_file = NULL; - g_autoptr(GInputStream) in = NULL; - g_autoptr(OstreeGpgVerifyResult) gpg_result = NULL; - g_autoptr(GError) my_error = NULL; - g_autoptr(GVariant) metadata = NULL; - gboolean metadata_valid; - g_autofree char *remote_collection_id = NULL; - g_autofree char *collection_id = NULL; - - metadata = flatpak_bundle_load (file, &to_checksum, NULL, NULL, NULL, &metadata_contents, NULL, NULL, &collection_id, error); - if (metadata == NULL) - return FALSE; - - if (metadata_contents != NULL) - metadata_size = strlen (metadata_contents); - - if (!ostree_repo_get_remote_option (repo, remote, "collection-id", NULL, - &remote_collection_id, NULL)) - remote_collection_id = NULL; - - if (remote_collection_id != NULL && collection_id != NULL && - strcmp (remote_collection_id, collection_id) != 0) - return flatpak_fail_error (error, FLATPAK_ERROR_INVALID_DATA, _("Collection ‘%s’ of bundle doesn’t match collection ‘%s’ of remote"), - collection_id, remote_collection_id); - - if (!ostree_repo_prepare_transaction (repo, NULL, cancellable, error)) - return FALSE; - - /* Don’t need to set the collection ID here, since the remote binds this ref to the collection. */ - ostree_repo_transaction_set_ref (repo, remote, ref, to_checksum); - - if (!ostree_repo_static_delta_execute_offline (repo, - file, - FALSE, - cancellable, - error)) - return FALSE; + const char *module_name = reason + strlen ("have-kernel-module-"); - gpg_result = ostree_repo_verify_commit_ext (repo, to_checksum, - NULL, NULL, cancellable, &my_error); - if (gpg_result == NULL) - { - /* no gpg signature, we ignore this *if* there is no gpg key - * specified in the bundle or by the user */ - if (g_error_matches (my_error, OSTREE_GPG_ERROR, OSTREE_GPG_ERROR_NO_SIGNATURE) && - !require_gpg_signature) - { - g_clear_error (&my_error); - } - else - { - g_propagate_error (error, g_steal_pointer (&my_error)); - return FALSE; + if (flatpak_get_have_kernel_module (module_name)) + return TRUE; } - } - else - { - /* If there is no valid gpg signature we fail, unless there is no gpg - key specified (on the command line or in the file) because then we - trust the source bundle. */ - if (ostree_gpg_verify_result_count_valid (gpg_result) == 0 && - require_gpg_signature) - return flatpak_fail_error (error, FLATPAK_ERROR_UNTRUSTED, _("GPG signatures found, but none are in trusted keyring")); - } - - if (!ostree_repo_read_commit (repo, to_checksum, &root, NULL, NULL, error)) - return FALSE; - - if (!ostree_repo_commit_transaction (repo, NULL, cancellable, error)) - return FALSE; - - /* We ensure that the actual installed metadata matches the one in the - header, because you may have made decisions on whether to install it or not - based on that data. */ - metadata_file = g_file_resolve_relative_path (root, "metadata"); - in = (GInputStream *) g_file_read (metadata_file, cancellable, NULL); - if (in != NULL) - { - g_autoptr(GMemoryOutputStream) data_stream = (GMemoryOutputStream *) g_memory_output_stream_new_resizable (); - - if (g_output_stream_splice (G_OUTPUT_STREAM (data_stream), in, - G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE, - cancellable, error) < 0) - return FALSE; - - metadata_valid = - metadata_contents != NULL && - metadata_size == g_memory_output_stream_get_data_size (data_stream) && - memcmp (metadata_contents, g_memory_output_stream_get_data (data_stream), metadata_size) == 0; - } - else - { - metadata_valid = (metadata_contents == NULL); - } - - if (!metadata_valid) - { - /* Immediately remove this broken commit */ - ostree_repo_set_ref_immediate (repo, remote, ref, NULL, cancellable, error); - return flatpak_fail_error (error, FLATPAK_ERROR_INVALID_DATA, _("Metadata in header and app are inconsistent")); - } - - return TRUE; -} - -typedef struct -{ - FlatpakOciPullProgress progress_cb; - gpointer progress_user_data; - guint64 total_size; - guint64 previous_layers_size; - guint32 n_layers; - guint32 pulled_layers; -} FlatpakOciPullProgressData; - -static void -oci_layer_progress (guint64 downloaded_bytes, - gpointer user_data) -{ - FlatpakOciPullProgressData *progress_data = user_data; - - if (progress_data->progress_cb) - progress_data->progress_cb (progress_data->total_size, progress_data->previous_layers_size + downloaded_bytes, - progress_data->n_layers, progress_data->pulled_layers, - progress_data->progress_user_data); -} - -gboolean -flatpak_mirror_image_from_oci (FlatpakOciRegistry *dst_registry, - FlatpakOciRegistry *registry, - const char *oci_repository, - const char *digest, - const char *remote, - const char *ref, - const char *delta_url, - OstreeRepo *repo, - FlatpakOciPullProgress progress_cb, - gpointer progress_user_data, - GCancellable *cancellable, - GError **error) -{ - FlatpakOciPullProgressData progress_data = { progress_cb, progress_user_data }; - g_autoptr(FlatpakOciVersioned) versioned = NULL; - FlatpakOciManifest *manifest = NULL; - g_autoptr(FlatpakOciDescriptor) manifest_desc = NULL; - g_autoptr(FlatpakOciManifest) delta_manifest = NULL; - g_autofree char *old_checksum = NULL; - g_autoptr(GVariant) old_commit = NULL; - g_autoptr(GFile) old_root = NULL; - OstreeRepoCommitState old_state = 0; - g_autofree char *old_diffid = NULL; - gsize versioned_size; - g_autoptr(FlatpakOciIndex) index = NULL; - g_autoptr(FlatpakOciImage) image_config = NULL; - int n_layers; - int i; - - if (!flatpak_oci_registry_mirror_blob (dst_registry, registry, oci_repository, TRUE, digest, NULL, NULL, NULL, cancellable, error)) - return FALSE; - - versioned = flatpak_oci_registry_load_versioned (dst_registry, NULL, digest, NULL, &versioned_size, cancellable, error); - if (versioned == NULL) - return FALSE; - - if (!FLATPAK_IS_OCI_MANIFEST (versioned)) - return flatpak_fail_error (error, FLATPAK_ERROR_INVALID_DATA, _("Image is not a manifest")); - - manifest = FLATPAK_OCI_MANIFEST (versioned); - - if (manifest->config.digest == NULL) - return flatpak_fail_error (error, FLATPAK_ERROR_INVALID_DATA, _("Image is not a manifest")); - - if (!flatpak_oci_registry_mirror_blob (dst_registry, registry, oci_repository, FALSE, manifest->config.digest, (const char **)manifest->config.urls, NULL, NULL, cancellable, error)) - return FALSE; - - image_config = flatpak_oci_registry_load_image_config (dst_registry, NULL, - manifest->config.digest, NULL, - NULL, cancellable, error); - if (image_config == NULL) - return FALSE; - - /* For deltas we ensure that the diffid and regular layers exists and match up */ - n_layers = flatpak_oci_manifest_get_n_layers (manifest); - if (n_layers == 0 || n_layers != flatpak_oci_image_get_n_layers (image_config)) - return flatpak_fail (error, _("Invalid OCI image config")); - - /* Look for delta manifest, and if it exists, the current (old) commit and its recorded diffid */ - if (flatpak_repo_resolve_rev (repo, NULL, remote, ref, FALSE, &old_checksum, NULL, NULL) && - ostree_repo_load_commit (repo, old_checksum, &old_commit, &old_state, NULL) && - (old_state == OSTREE_REPO_COMMIT_STATE_NORMAL) && - ostree_repo_read_commit (repo, old_checksum, &old_root, NULL, NULL, NULL)) - { - delta_manifest = flatpak_oci_registry_find_delta_manifest (registry, oci_repository, digest, delta_url, cancellable); - if (delta_manifest) + else if (g_str_has_prefix (reason, "on-xdg-desktop-")) { - VarMetadataRef commit_metadata = var_commit_get_metadata (var_commit_from_gvariant (old_commit)); - const char *raw_old_diffid = var_metadata_lookup_string (commit_metadata, "xa.diff-id", NULL); - if (raw_old_diffid != NULL) - old_diffid = g_strconcat ("sha256:", raw_old_diffid, NULL); - } - } - - for (i = 0; manifest->layers[i] != NULL; i++) - { - FlatpakOciDescriptor *layer = manifest->layers[i]; - FlatpakOciDescriptor *delta_layer = NULL; - - if (delta_manifest) - delta_layer = flatpak_oci_manifest_find_delta_for (delta_manifest, old_diffid, image_config->rootfs.diff_ids[i]); - - if (delta_layer) - progress_data.total_size += delta_layer->size; - else - progress_data.total_size += layer->size; - progress_data.n_layers++; - } - - if (progress_cb) - progress_cb (progress_data.total_size, 0, - progress_data.n_layers, progress_data.pulled_layers, - progress_user_data); - - for (i = 0; manifest->layers[i] != NULL; i++) - { - FlatpakOciDescriptor *layer = manifest->layers[i]; - FlatpakOciDescriptor *delta_layer = NULL; - - if (delta_manifest) - delta_layer = flatpak_oci_manifest_find_delta_for (delta_manifest, old_diffid, image_config->rootfs.diff_ids[i]); + const char *desktop_name = reason + strlen ("on-xdg-desktop-"); + const char *current_desktop_var = g_getenv ("XDG_CURRENT_DESKTOP"); + g_auto(GStrv) current_desktop_names = NULL; + size_t j; - if (delta_layer) - { - g_info ("Using OCI delta %s for layer %s", delta_layer->digest, layer->digest); - g_autofree char *delta_digest = NULL; - glnx_autofd int delta_fd = flatpak_oci_registry_download_blob (registry, oci_repository, FALSE, - delta_layer->digest, (const char **)delta_layer->urls, - oci_layer_progress, &progress_data, - cancellable, error); - if (delta_fd == -1) - return FALSE; + if (!current_desktop_var) + continue; - delta_digest = flatpak_oci_registry_apply_delta_to_blob (dst_registry, delta_fd, old_root, cancellable, error); - if (delta_digest == NULL) - return FALSE; + current_desktop_names = g_strsplit (current_desktop_var, ":", -1); - if (g_strcmp0 (delta_digest, image_config->rootfs.diff_ids[i]) != 0) - return flatpak_fail_error (error, FLATPAK_ERROR_INVALID_DATA, _("Wrong layer checksum, expected %s, was %s"), image_config->rootfs.diff_ids[i], delta_digest); - } - else - { - if (!flatpak_oci_registry_mirror_blob (dst_registry, registry, oci_repository, FALSE, layer->digest, (const char **)layer->urls, - oci_layer_progress, &progress_data, - cancellable, error)) - return FALSE; + for (j = 0; current_desktop_names[j]; ++j) + { + if (g_ascii_strcasecmp (desktop_name, current_desktop_names[j]) == 0) + return TRUE; + } } - - progress_data.pulled_layers++; - progress_data.previous_layers_size += delta_layer ? delta_layer->size : layer->size; } - index = flatpak_oci_registry_load_index (dst_registry, NULL, NULL); - if (index == NULL) - index = flatpak_oci_index_new (); - - manifest_desc = flatpak_oci_descriptor_new (versioned->mediatype, digest, versioned_size); - - flatpak_oci_index_add_manifest (index, ref, manifest_desc); - - if (!flatpak_oci_registry_save_index (dst_registry, index, cancellable, error)) - return FALSE; - - return TRUE; + return FALSE; } -char * -flatpak_pull_from_oci (OstreeRepo *repo, - FlatpakOciRegistry *registry, - const char *oci_repository, - const char *digest, - const char *delta_url, - FlatpakOciManifest *manifest, - FlatpakOciImage *image_config, - const char *remote, - const char *ref, - FlatpakPullFlags flags, - FlatpakOciPullProgress progress_cb, - gpointer progress_user_data, - GCancellable *cancellable, - GError **error) +void +flatpak_parse_extension_with_tag (const char *extension, + char **name, + char **tag) { - gboolean force_disable_deltas = (flags & FLATPAK_PULL_FLAGS_NO_STATIC_DELTAS) != 0; - g_autoptr(OstreeMutableTree) archive_mtree = NULL; - g_autoptr(GFile) archive_root = NULL; - g_autoptr(FlatpakOciManifest) delta_manifest = NULL; - g_autofree char *old_checksum = NULL; - g_autoptr(GVariant) old_commit = NULL; - g_autoptr(GFile) old_root = NULL; - OstreeRepoCommitState old_state = 0; - g_autofree char *old_diffid = NULL; - g_autofree char *commit_checksum = NULL; - const char *parent = NULL; - g_autofree char *subject = NULL; - g_autofree char *body = NULL; - g_autofree char *manifest_ref = NULL; - g_autofree char *full_ref = NULL; - const char *diffid; - guint64 timestamp = 0; - FlatpakOciPullProgressData progress_data = { progress_cb, progress_user_data }; - g_autoptr(GVariantBuilder) metadata_builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}")); - g_autoptr(GVariant) metadata = NULL; - GHashTable *labels; - int n_layers; - int i; - - g_assert (ref != NULL); - g_assert (g_str_has_prefix (digest, "sha256:")); - - labels = flatpak_oci_image_get_labels (image_config); - if (labels) - flatpak_oci_parse_commit_labels (labels, ×tamp, - &subject, &body, - &manifest_ref, NULL, NULL, - metadata_builder); - - if (manifest_ref == NULL) - { - flatpak_fail_error (error, FLATPAK_ERROR_INVALID_DATA, _("No ref specified for OCI image %s"), digest); - return NULL; - } - - if (strcmp (manifest_ref, ref) != 0) - { - flatpak_fail_error (error, FLATPAK_ERROR_INVALID_DATA, _("Wrong ref (%s) specified for OCI image %s, expected %s"), manifest_ref, digest, ref); - return NULL; - } - - g_variant_builder_add (metadata_builder, "{s@v}", "xa.alt-id", - g_variant_new_variant (g_variant_new_string (digest + strlen ("sha256:")))); - - /* For deltas we ensure that the diffid and regular layers exists and match up */ - n_layers = flatpak_oci_manifest_get_n_layers (manifest); - if (n_layers == 0 || n_layers != flatpak_oci_image_get_n_layers (image_config)) - { - flatpak_fail (error, _("Invalid OCI image config")); - return NULL; - } - - /* Assuming everyting looks good, we record the uncompressed checksum (the diff-id) of the last layer, - because that is what we can read back easily from the deploy dir, and thus is easy to use for applying deltas */ - diffid = image_config->rootfs.diff_ids[n_layers-1]; - if (diffid != NULL && g_str_has_prefix (diffid, "sha256:")) - g_variant_builder_add (metadata_builder, "{s@v}", "xa.diff-id", - g_variant_new_variant (g_variant_new_string (diffid + strlen ("sha256:")))); - - /* Look for delta manifest, and if it exists, the current (old) commit and its recorded diffid */ - if (!force_disable_deltas && - !flatpak_oci_registry_is_local (registry) && - flatpak_repo_resolve_rev (repo, NULL, remote, ref, FALSE, &old_checksum, NULL, NULL) && - ostree_repo_load_commit (repo, old_checksum, &old_commit, &old_state, NULL) && - (old_state == OSTREE_REPO_COMMIT_STATE_NORMAL) && - ostree_repo_read_commit (repo, old_checksum, &old_root, NULL, NULL, NULL)) - { - delta_manifest = flatpak_oci_registry_find_delta_manifest (registry, oci_repository, digest, delta_url, cancellable); - if (delta_manifest) - { - VarMetadataRef commit_metadata = var_commit_get_metadata (var_commit_from_gvariant (old_commit)); - const char *raw_old_diffid = var_metadata_lookup_string (commit_metadata, "xa.diff-id", NULL); - if (raw_old_diffid != NULL) - old_diffid = g_strconcat ("sha256:", raw_old_diffid, NULL); - } - } - - if (!ostree_repo_prepare_transaction (repo, NULL, cancellable, error)) - return NULL; - - /* There is no way to write a subset of the archive to a mtree, so instead - we write all of it and then build a new mtree with the subset */ - archive_mtree = ostree_mutable_tree_new (); - - for (i = 0; manifest->layers[i] != NULL; i++) - { - FlatpakOciDescriptor *layer = manifest->layers[i]; - FlatpakOciDescriptor *delta_layer = NULL; - - if (delta_manifest) - delta_layer = flatpak_oci_manifest_find_delta_for (delta_manifest, old_diffid, image_config->rootfs.diff_ids[i]); - - if (delta_layer) - progress_data.total_size += delta_layer->size; - else - progress_data.total_size += layer->size; - - progress_data.n_layers++; - } - - if (progress_cb) - progress_cb (progress_data.total_size, 0, - progress_data.n_layers, progress_data.pulled_layers, - progress_user_data); + const char *tag_chr = strchr (extension, '@'); - for (i = 0; manifest->layers[i] != NULL; i++) + if (tag_chr) { - FlatpakOciDescriptor *layer = manifest->layers[i]; - FlatpakOciDescriptor *delta_layer = NULL; - OstreeRepoImportArchiveOptions opts = { 0, }; - g_autoptr(FlatpakAutoArchiveRead) a = NULL; - glnx_autofd int layer_fd = -1; - glnx_autofd int blob_fd = -1; - g_autoptr(GChecksum) checksum = g_checksum_new (G_CHECKSUM_SHA256); - g_autoptr(GError) local_error = NULL; - const char *layer_checksum; - const char *expected_digest; - - if (delta_manifest) - delta_layer = flatpak_oci_manifest_find_delta_for (delta_manifest, old_diffid, image_config->rootfs.diff_ids[i]); - - opts.autocreate_parents = TRUE; - opts.ignore_unsupported_content = TRUE; - - if (delta_layer) - { - g_info ("Using OCI delta %s for layer %s", delta_layer->digest, layer->digest); - expected_digest = image_config->rootfs.diff_ids[i]; /* The delta recreates the uncompressed tar so use that digest */ - } - else - { - layer_fd = g_steal_fd (&blob_fd); - expected_digest = layer->digest; - } - - blob_fd = flatpak_oci_registry_download_blob (registry, oci_repository, FALSE, - delta_layer ? delta_layer->digest : layer->digest, - (const char **)(delta_layer ? delta_layer->urls : layer->urls), - oci_layer_progress, &progress_data, - cancellable, &local_error); - - if (blob_fd == -1 && delta_layer == NULL && - flatpak_oci_registry_is_local (registry) && - g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) - { - /* Pulling regular layer from local repo and its not there, try the uncompressed version. - * This happens when we deploy via system helper using oci deltas */ - expected_digest = image_config->rootfs.diff_ids[i]; - blob_fd = flatpak_oci_registry_download_blob (registry, oci_repository, FALSE, - image_config->rootfs.diff_ids[i], NULL, - oci_layer_progress, &progress_data, - cancellable, NULL); /* No error here, we report the first error if this failes */ - } - - if (blob_fd == -1) - { - g_propagate_error (error, g_steal_pointer (&local_error)); - goto error; - } - - g_clear_error (&local_error); - - if (delta_layer) - { - layer_fd = flatpak_oci_registry_apply_delta (registry, blob_fd, old_root, cancellable, error); - if (layer_fd == -1) - goto error; - } - else - { - layer_fd = g_steal_fd (&blob_fd); - } - - a = archive_read_new (); -#ifdef HAVE_ARCHIVE_READ_SUPPORT_FILTER_ALL - archive_read_support_filter_all (a); -#else - archive_read_support_compression_all (a); -#endif - archive_read_support_format_all (a); - - if (!flatpak_archive_read_open_fd_with_checksum (a, layer_fd, checksum, error)) - goto error; - - if (!ostree_repo_import_archive_to_mtree (repo, &opts, a, archive_mtree, NULL, cancellable, error)) - goto error; - - if (archive_read_close (a) != ARCHIVE_OK) - { - propagate_libarchive_error (error, a); - goto error; - } + if (name != NULL) + *name = g_strndup (extension, tag_chr - extension); - layer_checksum = g_checksum_get_string (checksum); - if (!g_str_has_prefix (expected_digest, "sha256:") || - strcmp (expected_digest + strlen ("sha256:"), layer_checksum) != 0) - { - flatpak_fail_error (error, FLATPAK_ERROR_INVALID_DATA, _("Wrong layer checksum, expected %s, was %s"), expected_digest, layer_checksum); - goto error; - } + /* Everything after the @ */ + if (tag != NULL) + *tag = g_strdup (tag_chr + 1); - progress_data.pulled_layers++; - progress_data.previous_layers_size += delta_layer ? delta_layer->size : layer->size; + return; } - if (!ostree_repo_write_mtree (repo, archive_mtree, &archive_root, cancellable, error)) - goto error; - - if (!ostree_repo_file_ensure_resolved ((OstreeRepoFile *) archive_root, error)) - goto error; - - metadata = g_variant_ref_sink (g_variant_builder_end (metadata_builder)); - if (!ostree_repo_write_commit_with_time (repo, - parent, - subject, - body, - metadata, - OSTREE_REPO_FILE (archive_root), - timestamp, - &commit_checksum, - cancellable, error)) - goto error; - - if (remote) - full_ref = g_strdup_printf ("%s:%s", remote, ref); - else - full_ref = g_strdup (ref); - - /* Don’t need to set the collection ID here, since the ref is bound to a - * collection via its remote. */ - ostree_repo_transaction_set_ref (repo, NULL, full_ref, commit_checksum); - - if (!ostree_repo_commit_transaction (repo, NULL, cancellable, error)) - return NULL; - - return g_steal_pointer (&commit_checksum); - -error: + if (name != NULL) + *name = g_strdup (extension); - ostree_repo_abort_transaction (repo, cancellable, NULL); - return NULL; + if (tag != NULL) + *tag = NULL; } /* This allocates and locks a subdir of the tmp dir, using an existing @@ -7487,23 +1944,7 @@ flatpak_g_ptr_array_contains_string (GPtrArray *array, const char *str) return FALSE; } -void -flatpak_log_dir_access (FlatpakDir *dir) -{ - if (dir != NULL) - { - GFile *dir_path = NULL; - g_autofree char *dir_path_str = NULL; - g_autofree char *dir_name = NULL; - - dir_path = flatpak_dir_get_path (dir); - if (dir_path != NULL) - dir_path_str = g_file_get_path (dir_path); - dir_name = flatpak_dir_get_name (dir); - g_info ("Opening %s flatpak installation at path %s", dir_name, dir_path_str); - } -} - +#if 0 gboolean flatpak_check_required_version (const char *ref, GKeyFile *metakey, @@ -7584,6 +2025,7 @@ flatpak_check_required_version (const char *ref, return TRUE; } +#endif static int dist (const char *s, int ls, const char *t, int lt, int i, int j, int *d) @@ -7640,69 +2082,6 @@ flatpak_levenshtein_distance (const char *s, return dist (s, ls, t, lt, 0, 0, d); } -/* Wrapper that uses ostree_repo_resolve_collection_ref() and on failure falls - * back to using ostree_repo_resolve_rev() for backwards compatibility. This - * means we support refs/heads/, refs/remotes/, and refs/mirrors/. */ -gboolean -flatpak_repo_resolve_rev (OstreeRepo *repo, - const char *collection_id, /* nullable */ - const char *remote_name, /* nullable */ - const char *ref_name, - gboolean allow_noent, - char **out_rev, - GCancellable *cancellable, - GError **error) -{ - g_autoptr(GError) local_error = NULL; - - if (collection_id != NULL) - { - /* Do a version check to ensure we have these: - * https://github.com/ostreedev/ostree/pull/1821 - * https://github.com/ostreedev/ostree/pull/1825 */ -#if OSTREE_CHECK_VERSION (2019, 2) - const OstreeCollectionRef c_r = - { - .collection_id = (char *) collection_id, - .ref_name = (char *) ref_name, - }; - OstreeRepoResolveRevExtFlags flags = remote_name == NULL ? - OSTREE_REPO_RESOLVE_REV_EXT_LOCAL_ONLY : - OSTREE_REPO_RESOLVE_REV_EXT_NONE; - if (ostree_repo_resolve_collection_ref (repo, &c_r, - allow_noent, - flags, - out_rev, - cancellable, NULL)) - return TRUE; -#endif - } - - /* There may be several remotes with the same branch (if we for - * instance changed the origin) so prepend the current origin to - * make sure we get the right one */ - if (remote_name != NULL) - { - g_autofree char *refspec = g_strdup_printf ("%s:%s", remote_name, ref_name); - ostree_repo_resolve_rev (repo, refspec, allow_noent, out_rev, &local_error); - } - else - ostree_repo_resolve_rev_ext (repo, ref_name, allow_noent, - OSTREE_REPO_RESOLVE_REV_EXT_NONE, out_rev, &local_error); - - if (local_error != NULL) - { - if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) - flatpak_fail_error (error, FLATPAK_ERROR_REF_NOT_FOUND, "%s", local_error->message); - else - g_propagate_error (error, g_steal_pointer (&local_error)); - - return FALSE; - } - - return TRUE; -} - /* Convert an app id to a dconf path in the obvious way. */ char * @@ -7845,8 +2224,6 @@ flatpak_parse_env_block (const char *data, return (GStrv) g_ptr_array_free (g_steal_pointer (&env_vars), FALSE); } -#endif - /** * flatpak_envp_cmp: * @p1: a `const char * const *` @@ -7916,8 +2293,6 @@ flatpak_envp_cmp (const void *p1, return ret; } -#if 0 - /* * Return %TRUE if @s consists of one or more digits. * This is the same as Python bytes.isdigit(). @@ -8092,5 +2467,3 @@ running_under_sudo (void) return FALSE; } - -#endif