2015-03-23 11:17:58 +00:00
|
|
|
/*
|
|
|
|
* Copyright © 2014 Red Hat, Inc
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
|
|
* License as published by the Free Software Foundation; either
|
|
|
|
* version 2 of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This library is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* Lesser General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
|
|
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*
|
|
|
|
* Authors:
|
|
|
|
* Alexander Larsson <alexl@redhat.com>
|
|
|
|
*/
|
|
|
|
|
2014-12-18 09:14:46 +00:00
|
|
|
#include "config.h"
|
|
|
|
|
2014-12-19 10:18:27 +00:00
|
|
|
#include <string.h>
|
2015-01-16 17:46:08 +00:00
|
|
|
#include <fcntl.h>
|
2015-02-11 13:30:53 +00:00
|
|
|
#include <stdio.h>
|
2014-12-19 10:18:27 +00:00
|
|
|
|
2014-12-18 09:14:46 +00:00
|
|
|
#include <gio/gio.h>
|
|
|
|
#include "libgsystem.h"
|
2015-03-20 15:21:19 +00:00
|
|
|
#include "libglnx/libglnx.h"
|
2014-12-18 09:14:46 +00:00
|
|
|
|
|
|
|
#include "xdg-app-dir.h"
|
2015-01-09 10:46:07 +00:00
|
|
|
#include "xdg-app-utils.h"
|
2015-12-07 11:26:44 +00:00
|
|
|
#include "xdg-app-run.h"
|
2014-12-18 09:14:46 +00:00
|
|
|
|
2015-01-16 17:46:08 +00:00
|
|
|
#include "errno.h"
|
|
|
|
|
2014-12-18 09:14:46 +00:00
|
|
|
struct XdgAppDir {
|
|
|
|
GObject parent;
|
|
|
|
|
|
|
|
gboolean user;
|
|
|
|
GFile *basedir;
|
|
|
|
OstreeRepo *repo;
|
|
|
|
};
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
GObjectClass parent_class;
|
|
|
|
} XdgAppDirClass;
|
|
|
|
|
2015-05-12 08:25:09 +00:00
|
|
|
struct XdgAppDeploy {
|
|
|
|
GObject parent;
|
|
|
|
|
|
|
|
GFile *dir;
|
|
|
|
GKeyFile *metadata;
|
2015-09-11 14:07:31 +00:00
|
|
|
XdgAppContext *system_overrides;
|
|
|
|
XdgAppContext *user_overrides;
|
2015-05-12 08:25:09 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
GObjectClass parent_class;
|
|
|
|
} XdgAppDeployClass;
|
|
|
|
|
|
|
|
|
2014-12-18 09:14:46 +00:00
|
|
|
G_DEFINE_TYPE (XdgAppDir, xdg_app_dir, G_TYPE_OBJECT)
|
2015-05-12 08:25:09 +00:00
|
|
|
G_DEFINE_TYPE (XdgAppDeploy, xdg_app_deploy, G_TYPE_OBJECT)
|
2014-12-18 09:14:46 +00:00
|
|
|
|
2014-12-18 10:06:37 +00:00
|
|
|
G_DEFINE_QUARK (xdg-app-dir-error-quark, xdg_app_dir_error)
|
|
|
|
|
2014-12-18 09:14:46 +00:00
|
|
|
enum {
|
|
|
|
PROP_0,
|
|
|
|
|
|
|
|
PROP_USER,
|
|
|
|
PROP_PATH
|
|
|
|
};
|
|
|
|
|
2014-12-18 10:06:37 +00:00
|
|
|
#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")
|
|
|
|
|
2015-05-12 08:25:09 +00:00
|
|
|
static void
|
|
|
|
xdg_app_deploy_finalize (GObject *object)
|
|
|
|
{
|
|
|
|
XdgAppDeploy *self = XDG_APP_DEPLOY (object);
|
|
|
|
|
|
|
|
g_clear_object (&self->dir);
|
|
|
|
g_clear_pointer (&self->metadata, g_key_file_unref);
|
2015-12-07 11:25:48 +00:00
|
|
|
g_clear_pointer (&self->system_overrides, g_key_file_unref);
|
|
|
|
g_clear_pointer (&self->user_overrides, g_key_file_unref);
|
2015-05-12 08:25:09 +00:00
|
|
|
|
|
|
|
G_OBJECT_CLASS (xdg_app_deploy_parent_class)->finalize (object);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
xdg_app_deploy_class_init (XdgAppDeployClass *klass)
|
|
|
|
{
|
|
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
|
|
|
|
|
|
object_class->finalize = xdg_app_deploy_finalize;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
xdg_app_deploy_init (XdgAppDeploy *self)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
GFile *
|
|
|
|
xdg_app_deploy_get_dir (XdgAppDeploy *deploy)
|
|
|
|
{
|
|
|
|
return g_object_ref (deploy->dir);
|
|
|
|
}
|
|
|
|
|
|
|
|
GFile *
|
|
|
|
xdg_app_deploy_get_files (XdgAppDeploy *deploy)
|
|
|
|
{
|
|
|
|
return g_file_get_child (deploy->dir, "files");
|
|
|
|
}
|
|
|
|
|
2015-09-11 11:01:39 +00:00
|
|
|
XdgAppContext *
|
|
|
|
xdg_app_deploy_get_overrides (XdgAppDeploy *deploy)
|
|
|
|
{
|
|
|
|
XdgAppContext *overrides = xdg_app_context_new ();
|
|
|
|
|
2015-09-11 14:07:31 +00:00
|
|
|
if (deploy->system_overrides)
|
|
|
|
xdg_app_context_merge (overrides, deploy->system_overrides);
|
2015-09-11 11:01:39 +00:00
|
|
|
|
2015-09-11 14:07:31 +00:00
|
|
|
if (deploy->user_overrides)
|
|
|
|
xdg_app_context_merge (overrides, deploy->user_overrides);
|
2015-09-11 11:01:39 +00:00
|
|
|
|
|
|
|
return overrides;
|
|
|
|
}
|
|
|
|
|
2015-05-12 08:25:09 +00:00
|
|
|
GKeyFile *
|
|
|
|
xdg_app_deploy_get_metadata (XdgAppDeploy *deploy)
|
|
|
|
{
|
|
|
|
return g_key_file_ref (deploy->metadata);
|
|
|
|
}
|
|
|
|
|
|
|
|
static XdgAppDeploy *
|
|
|
|
xdg_app_deploy_new (GFile *dir, GKeyFile *metadata)
|
|
|
|
{
|
|
|
|
XdgAppDeploy *deploy;
|
|
|
|
|
|
|
|
deploy = g_object_new (XDG_APP_TYPE_DEPLOY, NULL);
|
|
|
|
deploy->dir = g_object_ref (dir);
|
|
|
|
deploy->metadata = g_key_file_ref (metadata);
|
|
|
|
|
|
|
|
return deploy;
|
|
|
|
}
|
|
|
|
|
2014-12-18 09:14:46 +00:00
|
|
|
GFile *
|
|
|
|
xdg_app_get_system_base_dir_location (void)
|
|
|
|
{
|
2015-02-06 10:59:38 +00:00
|
|
|
return g_file_new_for_path (XDG_APP_SYSTEMDIR);
|
2014-12-18 09:14:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
GFile *
|
|
|
|
xdg_app_get_user_base_dir_location (void)
|
|
|
|
{
|
2015-03-20 15:21:19 +00:00
|
|
|
g_autofree char *base = g_build_filename (g_get_user_data_dir (), "xdg-app", NULL);
|
2014-12-18 09:14:46 +00:00
|
|
|
return g_file_new_for_path (base);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
xdg_app_dir_finalize (GObject *object)
|
|
|
|
{
|
|
|
|
XdgAppDir *self = XDG_APP_DIR (object);
|
|
|
|
|
|
|
|
g_clear_object (&self->repo);
|
|
|
|
g_clear_object (&self->basedir);
|
|
|
|
|
|
|
|
G_OBJECT_CLASS (xdg_app_dir_parent_class)->finalize (object);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
xdg_app_dir_set_property(GObject *object,
|
|
|
|
guint prop_id,
|
|
|
|
const GValue *value,
|
|
|
|
GParamSpec *pspec)
|
|
|
|
{
|
|
|
|
XdgAppDir *self = XDG_APP_DIR (object);
|
|
|
|
|
|
|
|
switch (prop_id)
|
|
|
|
{
|
|
|
|
case PROP_PATH:
|
|
|
|
/* Canonicalize */
|
|
|
|
self->basedir = g_file_new_for_path (gs_file_get_path_cached (g_value_get_object (value)));
|
|
|
|
break;
|
|
|
|
case PROP_USER:
|
|
|
|
self->user = g_value_get_boolean (value);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
xdg_app_dir_get_property(GObject *object,
|
|
|
|
guint prop_id,
|
|
|
|
GValue *value,
|
|
|
|
GParamSpec *pspec)
|
|
|
|
{
|
|
|
|
XdgAppDir *self = XDG_APP_DIR (object);
|
|
|
|
|
|
|
|
switch (prop_id)
|
|
|
|
{
|
|
|
|
case PROP_PATH:
|
|
|
|
g_value_set_object (value, self->basedir);
|
|
|
|
break;
|
|
|
|
case PROP_USER:
|
|
|
|
g_value_set_boolean (value, self->user);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
xdg_app_dir_class_init (XdgAppDirClass *klass)
|
|
|
|
{
|
|
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
|
|
|
|
|
|
object_class->get_property = xdg_app_dir_get_property;
|
|
|
|
object_class->set_property = xdg_app_dir_set_property;
|
|
|
|
object_class->finalize = xdg_app_dir_finalize;
|
|
|
|
|
|
|
|
g_object_class_install_property (object_class,
|
|
|
|
PROP_USER,
|
|
|
|
g_param_spec_boolean ("user",
|
|
|
|
"",
|
|
|
|
"",
|
|
|
|
FALSE,
|
|
|
|
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
|
|
|
|
g_object_class_install_property (object_class,
|
|
|
|
PROP_PATH,
|
|
|
|
g_param_spec_object ("path",
|
|
|
|
"",
|
|
|
|
"",
|
|
|
|
G_TYPE_FILE,
|
|
|
|
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
xdg_app_dir_init (XdgAppDir *self)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
gboolean
|
|
|
|
xdg_app_dir_is_user (XdgAppDir *self)
|
|
|
|
{
|
|
|
|
return self->user;
|
|
|
|
}
|
|
|
|
|
|
|
|
GFile *
|
|
|
|
xdg_app_dir_get_path (XdgAppDir *self)
|
|
|
|
{
|
|
|
|
return self->basedir;
|
|
|
|
}
|
|
|
|
|
2015-09-11 14:07:31 +00:00
|
|
|
GKeyFile *
|
|
|
|
xdg_app_load_override_keyfile (const char *app_id, gboolean user, GError **error)
|
2015-09-11 11:01:39 +00:00
|
|
|
{
|
|
|
|
g_autoptr(GFile) base_dir = NULL;
|
|
|
|
g_autoptr(GFile) override_dir = NULL;
|
|
|
|
g_autoptr(GFile) file = NULL;
|
|
|
|
g_autofree char *metadata_contents = NULL;
|
|
|
|
gsize metadata_size;
|
2015-09-11 14:07:31 +00:00
|
|
|
g_autoptr(GKeyFile) metakey = g_key_file_new ();
|
2015-09-11 11:01:39 +00:00
|
|
|
|
|
|
|
if (user)
|
|
|
|
base_dir = xdg_app_get_user_base_dir_location ();
|
|
|
|
else
|
|
|
|
base_dir = xdg_app_get_system_base_dir_location ();
|
|
|
|
|
|
|
|
override_dir = g_file_get_child (base_dir, "overrides");
|
|
|
|
file = g_file_get_child (override_dir, app_id);
|
|
|
|
|
|
|
|
if (g_file_load_contents (file, NULL,
|
|
|
|
&metadata_contents, &metadata_size, NULL, NULL))
|
|
|
|
{
|
|
|
|
|
|
|
|
if (!g_key_file_load_from_data (metakey,
|
|
|
|
metadata_contents, metadata_size,
|
2015-09-11 14:07:31 +00:00
|
|
|
0, error))
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return g_steal_pointer (&metakey);
|
|
|
|
}
|
|
|
|
|
|
|
|
XdgAppContext *
|
|
|
|
xdg_app_load_override_file (const char *app_id, gboolean user, GError **error)
|
|
|
|
{
|
|
|
|
XdgAppContext *overrides = xdg_app_context_new ();
|
|
|
|
g_autoptr(GKeyFile) metakey = NULL;
|
|
|
|
|
|
|
|
metakey = xdg_app_load_override_keyfile (app_id, user, error);
|
|
|
|
if (metakey == NULL)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (!xdg_app_context_load_metadata (overrides, metakey, error))
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
return g_steal_pointer (&overrides);
|
|
|
|
}
|
|
|
|
|
|
|
|
gboolean
|
|
|
|
xdg_app_save_override_keyfile (GKeyFile *metakey,
|
|
|
|
const char *app_id,
|
|
|
|
gboolean user,
|
|
|
|
GError **error)
|
|
|
|
{
|
|
|
|
g_autoptr(GFile) base_dir = NULL;
|
|
|
|
g_autoptr(GFile) override_dir = NULL;
|
|
|
|
g_autoptr(GFile) file = NULL;
|
|
|
|
g_autofree char *filename = NULL;
|
|
|
|
g_autofree char *parent = NULL;
|
|
|
|
|
|
|
|
if (user)
|
|
|
|
base_dir = xdg_app_get_user_base_dir_location ();
|
|
|
|
else
|
|
|
|
base_dir = xdg_app_get_system_base_dir_location ();
|
|
|
|
|
|
|
|
override_dir = g_file_get_child (base_dir, "overrides");
|
|
|
|
file = g_file_get_child (override_dir, app_id);
|
|
|
|
|
|
|
|
filename = g_file_get_path (file);
|
|
|
|
parent = g_path_get_dirname (filename);
|
|
|
|
if (g_mkdir_with_parents (parent, 0755))
|
|
|
|
{
|
|
|
|
glnx_set_error_from_errno (error);
|
|
|
|
return FALSE;
|
2015-09-11 11:01:39 +00:00
|
|
|
}
|
2015-09-11 14:07:31 +00:00
|
|
|
|
|
|
|
return g_key_file_save_to_file (metakey, filename, error);
|
2015-09-11 11:01:39 +00:00
|
|
|
}
|
|
|
|
|
2015-05-12 08:25:09 +00:00
|
|
|
XdgAppDeploy *
|
|
|
|
xdg_app_dir_load_deployed (XdgAppDir *self,
|
|
|
|
const char *ref,
|
|
|
|
const char *checksum,
|
|
|
|
GCancellable *cancellable,
|
|
|
|
GError **error)
|
|
|
|
{
|
|
|
|
g_autoptr(GFile) deploy_dir = NULL;
|
|
|
|
g_autoptr(GKeyFile) metakey = NULL;
|
|
|
|
g_autoptr(GFile) metadata = NULL;
|
2015-09-11 11:01:39 +00:00
|
|
|
g_auto(GStrv) ref_parts = NULL;
|
2015-05-12 08:25:09 +00:00
|
|
|
g_autofree char *metadata_contents = NULL;
|
2015-09-11 11:01:39 +00:00
|
|
|
XdgAppDeploy *deploy;
|
2015-05-12 08:25:09 +00:00
|
|
|
gsize metadata_size;
|
|
|
|
|
|
|
|
deploy_dir = xdg_app_dir_get_if_deployed (self, ref, checksum, cancellable);
|
|
|
|
if (deploy_dir == NULL)
|
|
|
|
{
|
|
|
|
g_set_error (error, XDG_APP_DIR_ERROR, XDG_APP_DIR_ERROR_NOT_DEPLOYED, "%s not installed", ref);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
metadata = g_file_get_child (deploy_dir, "metadata");
|
|
|
|
if (!g_file_load_contents (metadata, cancellable, &metadata_contents, &metadata_size, NULL, error))
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
metakey = g_key_file_new ();
|
|
|
|
if (!g_key_file_load_from_data (metakey, metadata_contents, metadata_size, 0, error))
|
|
|
|
return NULL;
|
|
|
|
|
2015-09-11 11:01:39 +00:00
|
|
|
deploy = xdg_app_deploy_new (deploy_dir, metakey);
|
|
|
|
|
|
|
|
ref_parts = g_strsplit (ref, "/", -1);
|
|
|
|
g_assert (g_strv_length (ref_parts) == 4);
|
|
|
|
|
|
|
|
/* Only apps have overrides */
|
|
|
|
if (strcmp (ref_parts[0], "app") == 0)
|
|
|
|
{
|
|
|
|
/* Only load system overrides for system installed apps */
|
|
|
|
if (!self->user)
|
2015-09-11 14:07:31 +00:00
|
|
|
{
|
|
|
|
deploy->system_overrides = xdg_app_load_override_file (ref_parts[1], FALSE, error);
|
|
|
|
if (deploy->system_overrides == NULL)
|
|
|
|
return NULL;
|
|
|
|
}
|
2015-09-11 11:01:39 +00:00
|
|
|
|
|
|
|
/* Always load user overrides */
|
2015-09-11 14:07:31 +00:00
|
|
|
deploy->user_overrides = xdg_app_load_override_file (ref_parts[1], TRUE, error);
|
|
|
|
if (deploy->user_overrides == NULL)
|
|
|
|
return NULL;
|
2015-09-11 11:01:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return deploy;
|
2015-05-12 08:25:09 +00:00
|
|
|
}
|
|
|
|
|
2014-12-18 09:14:46 +00:00
|
|
|
GFile *
|
|
|
|
xdg_app_dir_get_deploy_dir (XdgAppDir *self,
|
|
|
|
const char *ref)
|
|
|
|
{
|
|
|
|
return g_file_resolve_relative_path (self->basedir, ref);
|
|
|
|
}
|
|
|
|
|
2015-01-09 10:46:07 +00:00
|
|
|
GFile *
|
|
|
|
xdg_app_dir_get_exports_dir (XdgAppDir *self)
|
|
|
|
{
|
2015-02-05 21:31:01 +00:00
|
|
|
return g_file_get_child (self->basedir, "exports");
|
|
|
|
}
|
|
|
|
|
|
|
|
GFile *
|
|
|
|
xdg_app_dir_get_removed_dir (XdgAppDir *self)
|
|
|
|
{
|
|
|
|
return g_file_get_child (self->basedir, ".removed");
|
2015-01-09 10:46:07 +00:00
|
|
|
}
|
|
|
|
|
2014-12-18 09:14:46 +00:00
|
|
|
OstreeRepo *
|
|
|
|
xdg_app_dir_get_repo (XdgAppDir *self)
|
|
|
|
{
|
|
|
|
return self->repo;
|
|
|
|
}
|
|
|
|
|
2015-06-01 11:27:41 +00:00
|
|
|
char *
|
|
|
|
xdg_app_dir_get_origin (XdgAppDir *self,
|
|
|
|
const char *ref,
|
|
|
|
GCancellable *cancellable,
|
|
|
|
GError **error)
|
|
|
|
{
|
|
|
|
g_autoptr(GFile) deploy_base = NULL;
|
|
|
|
g_autoptr(GFile) origin = NULL;
|
|
|
|
char *repository = NULL;
|
|
|
|
|
|
|
|
deploy_base = xdg_app_dir_get_deploy_dir (self, ref);
|
|
|
|
if (!g_file_query_exists (deploy_base, cancellable))
|
|
|
|
{
|
2015-09-28 14:54:24 +00:00
|
|
|
xdg_app_fail (error, "%s is not installed", ref);
|
2015-06-01 11:27:41 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
origin = g_file_get_child (deploy_base, "origin");
|
|
|
|
if (!g_file_load_contents (origin, cancellable, &repository, NULL, NULL, error))
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
return repository;
|
|
|
|
}
|
|
|
|
|
2014-12-18 09:14:46 +00:00
|
|
|
gboolean
|
|
|
|
xdg_app_dir_ensure_path (XdgAppDir *self,
|
|
|
|
GCancellable *cancellable,
|
|
|
|
GError **error)
|
|
|
|
{
|
|
|
|
return gs_file_ensure_directory (self->basedir, TRUE, cancellable, error);
|
|
|
|
}
|
|
|
|
|
|
|
|
gboolean
|
|
|
|
xdg_app_dir_ensure_repo (XdgAppDir *self,
|
|
|
|
GCancellable *cancellable,
|
|
|
|
GError **error)
|
|
|
|
{
|
|
|
|
gboolean ret = FALSE;
|
2015-03-20 15:21:19 +00:00
|
|
|
g_autoptr(GFile) repodir = NULL;
|
|
|
|
g_autoptr(OstreeRepo) repo = NULL;
|
2014-12-18 09:14:46 +00:00
|
|
|
|
|
|
|
if (self->repo == NULL)
|
|
|
|
{
|
|
|
|
if (!xdg_app_dir_ensure_path (self, cancellable, error))
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
repodir = g_file_get_child (self->basedir, "repo");
|
|
|
|
repo = ostree_repo_new (repodir);
|
|
|
|
|
|
|
|
if (!g_file_query_exists (repodir, cancellable))
|
|
|
|
{
|
|
|
|
if (!ostree_repo_create (repo,
|
|
|
|
self->user ? OSTREE_REPO_MODE_BARE_USER : OSTREE_REPO_MODE_BARE,
|
|
|
|
cancellable, error))
|
|
|
|
{
|
|
|
|
gs_shutil_rm_rf (repodir, cancellable, NULL);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (!ostree_repo_open (repo, cancellable, error))
|
2015-02-07 17:02:04 +00:00
|
|
|
{
|
2015-03-20 15:21:19 +00:00
|
|
|
g_autofree char *repopath = NULL;
|
2015-02-07 17:02:04 +00:00
|
|
|
|
|
|
|
repopath = g_file_get_path (repodir);
|
|
|
|
g_prefix_error (error, "While opening repository %s: ", repopath);
|
|
|
|
goto out;
|
|
|
|
}
|
2014-12-18 09:14:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
self->repo = g_object_ref (repo);
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = TRUE;
|
|
|
|
out:
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2014-12-18 10:06:37 +00:00
|
|
|
gboolean
|
|
|
|
xdg_app_dir_pull (XdgAppDir *self,
|
|
|
|
const char *repository,
|
|
|
|
const char *ref,
|
|
|
|
GCancellable *cancellable,
|
|
|
|
GError **error)
|
|
|
|
{
|
|
|
|
gboolean ret = FALSE;
|
|
|
|
GSConsole *console = NULL;
|
2015-03-20 15:21:19 +00:00
|
|
|
g_autoptr(OstreeAsyncProgress) progress = NULL;
|
2014-12-18 10:06:37 +00:00
|
|
|
const char *refs[2];
|
|
|
|
|
|
|
|
if (!xdg_app_dir_ensure_repo (self, cancellable, error))
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
console = gs_console_get ();
|
|
|
|
if (console)
|
|
|
|
{
|
|
|
|
gs_console_begin_status_line (console, "", NULL, NULL);
|
2015-01-12 15:49:06 +00:00
|
|
|
progress = ostree_async_progress_new_and_connect (ostree_repo_pull_default_console_progress_changed, console);
|
2014-12-18 10:06:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
refs[0] = ref;
|
|
|
|
refs[1] = NULL;
|
|
|
|
if (!ostree_repo_pull (self->repo, repository,
|
|
|
|
(char **)refs, OSTREE_REPO_PULL_FLAGS_NONE,
|
|
|
|
progress,
|
|
|
|
cancellable, error))
|
2015-02-07 17:02:04 +00:00
|
|
|
{
|
|
|
|
g_prefix_error (error, "While pulling %s from remote %s: ", ref, repository);
|
|
|
|
goto out;
|
|
|
|
}
|
2014-12-18 10:06:37 +00:00
|
|
|
|
2015-01-21 04:07:21 +00:00
|
|
|
if (console)
|
|
|
|
gs_console_end_status_line (console, NULL, NULL);
|
|
|
|
|
2014-12-18 10:06:37 +00:00
|
|
|
ret = TRUE;
|
|
|
|
out:
|
|
|
|
return ret;
|
2015-03-10 15:26:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
char *
|
|
|
|
xdg_app_dir_current_ref (XdgAppDir *self,
|
|
|
|
const char *name,
|
|
|
|
GCancellable *cancellable)
|
|
|
|
{
|
2015-03-20 15:21:19 +00:00
|
|
|
g_autoptr(GFile) base = NULL;
|
|
|
|
g_autoptr(GFile) dir = NULL;
|
|
|
|
g_autoptr(GFile) current_link = NULL;
|
|
|
|
g_autoptr(GFileInfo) file_info = NULL;
|
2015-03-10 15:26:51 +00:00
|
|
|
|
|
|
|
base = g_file_get_child (xdg_app_dir_get_path (self), "app");
|
|
|
|
dir = g_file_get_child (base, name);
|
|
|
|
|
|
|
|
current_link = g_file_get_child (dir, "current");
|
|
|
|
|
|
|
|
file_info = g_file_query_info (current_link, OSTREE_GIO_FAST_QUERYINFO,
|
|
|
|
G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
|
|
|
|
cancellable, NULL);
|
|
|
|
if (file_info == NULL)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
return g_strconcat ("app/", name, "/", g_file_info_get_symlink_target (file_info), NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
gboolean
|
|
|
|
xdg_app_dir_drop_current_ref (XdgAppDir *self,
|
|
|
|
const char *name,
|
|
|
|
GCancellable *cancellable,
|
|
|
|
GError **error)
|
|
|
|
{
|
2015-03-20 15:21:19 +00:00
|
|
|
g_autoptr(GFile) base = NULL;
|
|
|
|
g_autoptr(GFile) dir = NULL;
|
|
|
|
g_autoptr(GFile) current_link = NULL;
|
2015-03-10 15:26:51 +00:00
|
|
|
|
|
|
|
base = g_file_get_child (xdg_app_dir_get_path (self), "app");
|
|
|
|
dir = g_file_get_child (base, name);
|
|
|
|
|
|
|
|
current_link = g_file_get_child (dir, "current");
|
|
|
|
|
|
|
|
return g_file_delete (current_link, cancellable, error);
|
|
|
|
}
|
|
|
|
|
|
|
|
gboolean
|
|
|
|
xdg_app_dir_make_current_ref (XdgAppDir *self,
|
|
|
|
const char *ref,
|
|
|
|
GCancellable *cancellable,
|
|
|
|
GError **error)
|
|
|
|
{
|
2015-03-20 15:21:19 +00:00
|
|
|
g_autoptr(GFile) base = NULL;
|
|
|
|
g_autoptr(GFile) dir = NULL;
|
|
|
|
g_autoptr(GFile) current_link = NULL;
|
2015-08-31 07:51:48 +00:00
|
|
|
g_auto(GStrv) ref_parts = NULL;
|
2015-03-20 15:21:19 +00:00
|
|
|
g_autofree char *rest = NULL;
|
2015-03-10 15:26:51 +00:00
|
|
|
gboolean ret = FALSE;
|
|
|
|
|
|
|
|
ref_parts = g_strsplit (ref, "/", -1);
|
|
|
|
|
|
|
|
g_assert (g_strv_length (ref_parts) == 4);
|
|
|
|
g_assert (strcmp (ref_parts[0], "app") == 0);
|
|
|
|
|
|
|
|
base = g_file_get_child (xdg_app_dir_get_path (self), ref_parts[0]);
|
|
|
|
dir = g_file_get_child (base, ref_parts[1]);
|
|
|
|
|
|
|
|
current_link = g_file_get_child (dir, "current");
|
|
|
|
|
|
|
|
g_file_delete (current_link, cancellable, NULL);
|
|
|
|
|
|
|
|
if (*ref_parts[3] != 0)
|
|
|
|
{
|
|
|
|
rest = g_strdup_printf ("%s/%s", ref_parts[2], ref_parts[3]);
|
|
|
|
if (!g_file_make_symbolic_link (current_link, rest, cancellable, error))
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = TRUE;
|
|
|
|
|
|
|
|
out:
|
|
|
|
return ret;
|
2014-12-18 10:06:37 +00:00
|
|
|
}
|
|
|
|
|
2015-03-10 14:30:53 +00:00
|
|
|
static int
|
|
|
|
strvcmp(char **a, char **b)
|
|
|
|
{
|
|
|
|
return strcmp (*a, *b);
|
|
|
|
}
|
|
|
|
|
|
|
|
gboolean
|
|
|
|
xdg_app_dir_list_refs_for_name (XdgAppDir *self,
|
|
|
|
const char *kind,
|
|
|
|
const char *name,
|
|
|
|
char ***refs_out,
|
|
|
|
GCancellable *cancellable,
|
|
|
|
GError **error)
|
|
|
|
{
|
|
|
|
gboolean ret = FALSE;
|
2015-03-20 15:21:19 +00:00
|
|
|
g_autoptr(GFile) base = NULL;
|
|
|
|
g_autoptr(GFile) dir = NULL;
|
|
|
|
g_autoptr(GFileEnumerator) dir_enum = NULL;
|
|
|
|
g_autoptr(GFileInfo) child_info = NULL;
|
2015-03-10 14:30:53 +00:00
|
|
|
GError *temp_error = NULL;
|
2015-03-20 15:21:19 +00:00
|
|
|
g_autoptr(GPtrArray) refs = NULL;
|
2015-03-10 14:30:53 +00:00
|
|
|
|
|
|
|
base = g_file_get_child (xdg_app_dir_get_path (self), kind);
|
|
|
|
dir = g_file_get_child (base, name);
|
|
|
|
|
|
|
|
refs = g_ptr_array_new ();
|
|
|
|
|
|
|
|
if (!g_file_query_exists (dir, cancellable))
|
|
|
|
{
|
|
|
|
ret = TRUE;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
dir_enum = g_file_enumerate_children (dir, OSTREE_GIO_FAST_QUERYINFO,
|
|
|
|
G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
|
|
|
|
cancellable, error);
|
|
|
|
if (!dir_enum)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
while ((child_info = g_file_enumerator_next_file (dir_enum, cancellable, &temp_error)))
|
|
|
|
{
|
2015-03-20 15:21:19 +00:00
|
|
|
g_autoptr(GFile) child = NULL;
|
|
|
|
g_autoptr(GFileEnumerator) dir_enum2 = NULL;
|
|
|
|
g_autoptr(GFileInfo) child_info2 = NULL;
|
2015-03-10 14:30:53 +00:00
|
|
|
const char *arch;
|
|
|
|
|
|
|
|
arch = g_file_info_get_name (child_info);
|
|
|
|
|
|
|
|
if (g_file_info_get_file_type (child_info) != G_FILE_TYPE_DIRECTORY ||
|
|
|
|
strcmp (arch, "data") == 0 /* There used to be a data dir here, lets ignore it */)
|
|
|
|
{
|
|
|
|
g_clear_object (&child_info);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
child = g_file_get_child (dir, arch);
|
|
|
|
g_clear_object (&dir_enum2);
|
|
|
|
dir_enum2 = g_file_enumerate_children (child, OSTREE_GIO_FAST_QUERYINFO,
|
|
|
|
G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
|
|
|
|
cancellable, error);
|
|
|
|
if (!dir_enum2)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
while ((child_info2 = g_file_enumerator_next_file (dir_enum2, cancellable, &temp_error)))
|
|
|
|
{
|
|
|
|
const char *branch;
|
|
|
|
|
|
|
|
if (g_file_info_get_file_type (child_info2) == G_FILE_TYPE_DIRECTORY)
|
|
|
|
{
|
|
|
|
branch = g_file_info_get_name (child_info2);
|
|
|
|
g_ptr_array_add (refs,
|
|
|
|
g_strdup_printf ("%s/%s/%s/%s", kind, name, arch, branch));
|
|
|
|
}
|
|
|
|
|
|
|
|
g_clear_object (&child_info2);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (temp_error != NULL)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
g_clear_object (&child_info);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (temp_error != NULL)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
g_ptr_array_sort (refs, (GCompareFunc)strvcmp);
|
|
|
|
|
|
|
|
ret = TRUE;
|
|
|
|
|
|
|
|
out:
|
|
|
|
if (ret)
|
|
|
|
{
|
|
|
|
g_ptr_array_add (refs, NULL);
|
|
|
|
*refs_out = (char **)g_ptr_array_free (refs, FALSE);
|
|
|
|
refs = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (temp_error != NULL)
|
|
|
|
g_propagate_error (error, temp_error);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
gboolean
|
|
|
|
xdg_app_dir_list_refs (XdgAppDir *self,
|
|
|
|
const char *kind,
|
|
|
|
char ***refs_out,
|
|
|
|
GCancellable *cancellable,
|
|
|
|
GError **error)
|
|
|
|
{
|
|
|
|
gboolean ret = FALSE;
|
2015-03-20 15:21:19 +00:00
|
|
|
g_autoptr(GFile) base;
|
|
|
|
g_autoptr(GFileEnumerator) dir_enum = NULL;
|
|
|
|
g_autoptr(GFileInfo) child_info = NULL;
|
2015-03-10 14:30:53 +00:00
|
|
|
GError *temp_error = NULL;
|
2015-03-20 15:21:19 +00:00
|
|
|
g_autoptr(GPtrArray) refs = NULL;
|
2015-03-10 14:30:53 +00:00
|
|
|
|
|
|
|
refs = g_ptr_array_new ();
|
|
|
|
|
|
|
|
base = g_file_get_child (xdg_app_dir_get_path (self), kind);
|
|
|
|
|
|
|
|
if (!g_file_query_exists (base, cancellable))
|
|
|
|
{
|
|
|
|
ret = TRUE;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
dir_enum = g_file_enumerate_children (base, OSTREE_GIO_FAST_QUERYINFO,
|
|
|
|
G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
|
|
|
|
cancellable, error);
|
|
|
|
if (!dir_enum)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
while ((child_info = g_file_enumerator_next_file (dir_enum, cancellable, &temp_error)))
|
|
|
|
{
|
|
|
|
gchar **sub_refs = NULL;
|
|
|
|
const char *name;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (g_file_info_get_file_type (child_info) != G_FILE_TYPE_DIRECTORY)
|
|
|
|
{
|
|
|
|
g_clear_object (&child_info);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
name = g_file_info_get_name (child_info);
|
|
|
|
|
|
|
|
if (!xdg_app_dir_list_refs_for_name (self, kind, name, &sub_refs, cancellable, error))
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
for (i = 0; sub_refs[i] != NULL; i++)
|
|
|
|
g_ptr_array_add (refs, sub_refs[i]);
|
|
|
|
g_free (sub_refs);
|
|
|
|
|
|
|
|
g_clear_object (&child_info);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (temp_error != NULL)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
ret = TRUE;
|
|
|
|
|
|
|
|
g_ptr_array_sort (refs, (GCompareFunc)strvcmp);
|
|
|
|
|
|
|
|
out:
|
|
|
|
if (ret)
|
|
|
|
{
|
|
|
|
g_ptr_array_add (refs, NULL);
|
|
|
|
*refs_out = (char **)g_ptr_array_free (refs, FALSE);
|
|
|
|
refs = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (temp_error != NULL)
|
|
|
|
g_propagate_error (error, temp_error);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2014-12-19 10:18:27 +00:00
|
|
|
char *
|
2014-12-19 10:21:47 +00:00
|
|
|
xdg_app_dir_read_active (XdgAppDir *self,
|
2014-12-19 10:18:27 +00:00
|
|
|
const char *ref,
|
|
|
|
GCancellable *cancellable)
|
|
|
|
{
|
2015-03-20 15:21:19 +00:00
|
|
|
g_autoptr(GFile) deploy_base = NULL;
|
|
|
|
g_autoptr(GFile) active_link = NULL;
|
|
|
|
g_autoptr(GFileInfo) file_info = NULL;
|
2014-12-19 10:18:27 +00:00
|
|
|
|
|
|
|
deploy_base = xdg_app_dir_get_deploy_dir (self, ref);
|
2014-12-19 10:21:47 +00:00
|
|
|
active_link = g_file_get_child (deploy_base, "active");
|
2014-12-19 10:18:27 +00:00
|
|
|
|
2014-12-19 10:21:47 +00:00
|
|
|
file_info = g_file_query_info (active_link, OSTREE_GIO_FAST_QUERYINFO,
|
2014-12-19 10:18:27 +00:00
|
|
|
G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
|
|
|
|
cancellable, NULL);
|
|
|
|
if (file_info == NULL)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
return g_strdup (g_file_info_get_symlink_target (file_info));
|
|
|
|
}
|
|
|
|
|
|
|
|
gboolean
|
2014-12-19 10:21:47 +00:00
|
|
|
xdg_app_dir_set_active (XdgAppDir *self,
|
2014-12-19 10:18:27 +00:00
|
|
|
const char *ref,
|
|
|
|
const char *checksum,
|
|
|
|
GCancellable *cancellable,
|
|
|
|
GError **error)
|
|
|
|
{
|
|
|
|
gboolean ret = FALSE;
|
2015-03-20 15:21:19 +00:00
|
|
|
g_autoptr(GFile) deploy_base = NULL;
|
|
|
|
g_autofree char *tmpname = NULL;
|
|
|
|
g_autoptr(GFile) active_tmp_link = NULL;
|
|
|
|
g_autoptr(GFile) active_link = NULL;
|
|
|
|
g_autoptr (GError) my_error = NULL;
|
2014-12-19 10:18:27 +00:00
|
|
|
|
|
|
|
deploy_base = xdg_app_dir_get_deploy_dir (self, ref);
|
2014-12-19 10:21:47 +00:00
|
|
|
active_link = g_file_get_child (deploy_base, "active");
|
2014-12-19 10:18:27 +00:00
|
|
|
|
|
|
|
if (checksum != NULL)
|
|
|
|
{
|
2014-12-19 10:21:47 +00:00
|
|
|
tmpname = gs_fileutil_gen_tmp_name (".active-", NULL);
|
|
|
|
active_tmp_link = g_file_get_child (deploy_base, tmpname);
|
|
|
|
if (!g_file_make_symbolic_link (active_tmp_link, checksum, cancellable, error))
|
2014-12-19 10:18:27 +00:00
|
|
|
goto out;
|
|
|
|
|
2014-12-19 10:21:47 +00:00
|
|
|
if (!gs_file_rename (active_tmp_link,
|
|
|
|
active_link,
|
2014-12-19 10:18:27 +00:00
|
|
|
cancellable, error))
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2014-12-19 10:21:47 +00:00
|
|
|
if (!g_file_delete (active_link, cancellable, &my_error) &&
|
2014-12-19 10:18:27 +00:00
|
|
|
!g_error_matches (my_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
|
|
|
|
{
|
|
|
|
g_propagate_error (error, my_error);
|
|
|
|
my_error = NULL;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = TRUE;
|
|
|
|
out:
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-01-15 20:44:22 +00:00
|
|
|
gboolean
|
|
|
|
xdg_app_dir_run_triggers (XdgAppDir *self,
|
|
|
|
GCancellable *cancellable,
|
|
|
|
GError **error)
|
|
|
|
{
|
|
|
|
gboolean ret = FALSE;
|
2015-03-20 15:21:19 +00:00
|
|
|
g_autoptr(GFileEnumerator) dir_enum = NULL;
|
|
|
|
g_autoptr(GFileInfo) child_info = NULL;
|
|
|
|
g_autoptr(GFile) triggersdir = NULL;
|
2015-01-15 20:44:22 +00:00
|
|
|
GError *temp_error = NULL;
|
|
|
|
|
|
|
|
g_debug ("running triggers");
|
|
|
|
|
|
|
|
triggersdir = g_file_new_for_path (XDG_APP_TRIGGERDIR);
|
|
|
|
|
|
|
|
dir_enum = g_file_enumerate_children (triggersdir, "standard::type,standard::name",
|
|
|
|
0, cancellable, error);
|
|
|
|
if (!dir_enum)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
while ((child_info = g_file_enumerator_next_file (dir_enum, cancellable, &temp_error)) != NULL)
|
|
|
|
{
|
2015-03-20 15:21:19 +00:00
|
|
|
g_autoptr(GFile) child = NULL;
|
2015-01-15 20:44:22 +00:00
|
|
|
const char *name;
|
|
|
|
GError *trigger_error = NULL;
|
|
|
|
|
|
|
|
name = g_file_info_get_name (child_info);
|
|
|
|
|
|
|
|
child = g_file_get_child (triggersdir, name);
|
|
|
|
|
|
|
|
if (g_file_info_get_file_type (child_info) == G_FILE_TYPE_REGULAR &&
|
|
|
|
g_str_has_suffix (name, ".trigger"))
|
|
|
|
{
|
2015-03-20 15:21:19 +00:00
|
|
|
g_autoptr(GPtrArray) argv_array = NULL;
|
2015-01-15 20:44:22 +00:00
|
|
|
|
|
|
|
g_debug ("running trigger %s", name);
|
|
|
|
|
|
|
|
argv_array = g_ptr_array_new_with_free_func (g_free);
|
|
|
|
g_ptr_array_add (argv_array, g_strdup (HELPER));
|
|
|
|
g_ptr_array_add (argv_array, g_strdup ("-a"));
|
|
|
|
g_ptr_array_add (argv_array, g_file_get_path (self->basedir));
|
|
|
|
g_ptr_array_add (argv_array, g_strdup ("-e"));
|
|
|
|
g_ptr_array_add (argv_array, g_strdup ("-F"));
|
|
|
|
g_ptr_array_add (argv_array, g_strdup ("/usr"));
|
|
|
|
g_ptr_array_add (argv_array, g_file_get_path (child));
|
|
|
|
g_ptr_array_add (argv_array, NULL);
|
|
|
|
|
|
|
|
if (!g_spawn_sync ("/",
|
|
|
|
(char **)argv_array->pdata,
|
|
|
|
NULL,
|
|
|
|
G_SPAWN_DEFAULT,
|
|
|
|
NULL, NULL,
|
|
|
|
NULL, NULL,
|
|
|
|
NULL, &trigger_error))
|
|
|
|
{
|
|
|
|
g_warning ("Error running trigger %s: %s", name, trigger_error->message);
|
|
|
|
g_clear_error (&trigger_error);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
g_clear_object (&child_info);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (temp_error != NULL)
|
|
|
|
{
|
|
|
|
g_propagate_error (error, temp_error);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = TRUE;
|
|
|
|
out:
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2015-01-16 17:46:08 +00:00
|
|
|
static gboolean
|
|
|
|
read_fd (int fd,
|
|
|
|
struct stat *stat_buf,
|
|
|
|
gchar **contents,
|
|
|
|
gsize *length,
|
|
|
|
GError **error)
|
|
|
|
{
|
|
|
|
gchar *buf;
|
|
|
|
gsize bytes_read;
|
|
|
|
gsize size;
|
|
|
|
gsize alloc_size;
|
|
|
|
|
|
|
|
size = stat_buf->st_size;
|
|
|
|
|
|
|
|
alloc_size = size + 1;
|
|
|
|
buf = g_try_malloc (alloc_size);
|
|
|
|
|
|
|
|
if (buf == NULL)
|
|
|
|
{
|
|
|
|
g_set_error (error,
|
|
|
|
G_FILE_ERROR,
|
|
|
|
G_FILE_ERROR_NOMEM,
|
|
|
|
"not enough memory");
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
bytes_read = 0;
|
|
|
|
while (bytes_read < size)
|
|
|
|
{
|
|
|
|
gssize rc;
|
|
|
|
|
|
|
|
rc = read (fd, buf + bytes_read, size - bytes_read);
|
|
|
|
|
|
|
|
if (rc < 0)
|
|
|
|
{
|
|
|
|
if (errno != EINTR)
|
|
|
|
{
|
|
|
|
int save_errno = errno;
|
|
|
|
|
|
|
|
g_free (buf);
|
|
|
|
g_set_error (error,
|
|
|
|
G_FILE_ERROR,
|
|
|
|
g_file_error_from_errno (save_errno),
|
|
|
|
"Failed to read from exported file");
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (rc == 0)
|
|
|
|
break;
|
|
|
|
else
|
|
|
|
bytes_read += rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
buf[bytes_read] = '\0';
|
|
|
|
|
|
|
|
if (length)
|
|
|
|
*length = bytes_read;
|
|
|
|
|
|
|
|
*contents = buf;
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2015-06-23 08:52:28 +00:00
|
|
|
/* This is conservative, but lets us avoid escaping most
|
|
|
|
regular Exec= lines, which is nice as that can sometimes
|
|
|
|
cause problems for apps launching desktop files. */
|
|
|
|
static gboolean
|
|
|
|
need_quotes (const char *str)
|
|
|
|
{
|
|
|
|
const char *p;
|
|
|
|
|
|
|
|
for (p = str; *p; p++)
|
|
|
|
{
|
|
|
|
if (!g_ascii_isalnum (*p) &&
|
|
|
|
strchr ("-_%.=:/@", *p) == NULL)
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static char *
|
|
|
|
maybe_quote (const char *str)
|
|
|
|
{
|
|
|
|
if (need_quotes (str))
|
|
|
|
return g_shell_quote (str);
|
|
|
|
return g_strdup (str);
|
|
|
|
}
|
|
|
|
|
2015-01-16 17:46:08 +00:00
|
|
|
static gboolean
|
|
|
|
export_desktop_file (const char *app,
|
|
|
|
const char *branch,
|
|
|
|
const char *arch,
|
|
|
|
int parent_fd,
|
|
|
|
const char *name,
|
|
|
|
struct stat *stat_buf,
|
|
|
|
char **target,
|
|
|
|
GCancellable *cancellable,
|
|
|
|
GError **error)
|
|
|
|
{
|
|
|
|
gboolean ret = FALSE;
|
2015-03-20 15:36:17 +00:00
|
|
|
glnx_fd_close int desktop_fd = -1;
|
2015-03-20 15:21:19 +00:00
|
|
|
g_autofree char *tmpfile_name = NULL;
|
|
|
|
g_autoptr(GOutputStream) out_stream = NULL;
|
|
|
|
g_autofree gchar *data = NULL;
|
2015-01-16 17:46:08 +00:00
|
|
|
gsize data_len;
|
2015-03-20 15:21:19 +00:00
|
|
|
g_autofree gchar *new_data = NULL;
|
2015-01-16 17:46:08 +00:00
|
|
|
gsize new_data_len;
|
2015-03-20 15:21:19 +00:00
|
|
|
g_autoptr(GKeyFile) keyfile = NULL;
|
|
|
|
g_autofree gchar *old_exec = NULL;
|
2015-01-16 17:46:08 +00:00
|
|
|
gint old_argc;
|
2015-08-31 07:51:48 +00:00
|
|
|
g_auto(GStrv) old_argv = NULL;
|
|
|
|
g_auto(GStrv) groups = NULL;
|
2015-01-16 17:46:08 +00:00
|
|
|
GString *new_exec = NULL;
|
2015-06-23 08:52:28 +00:00
|
|
|
g_autofree char *escaped_app = maybe_quote (app);
|
|
|
|
g_autofree char *escaped_branch = maybe_quote (branch);
|
|
|
|
g_autofree char *escaped_arch = maybe_quote (arch);
|
2015-01-19 09:21:09 +00:00
|
|
|
int i;
|
2015-01-16 17:46:08 +00:00
|
|
|
|
|
|
|
if (!gs_file_openat_noatime (parent_fd, name, &desktop_fd, cancellable, error))
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
if (!read_fd (desktop_fd, stat_buf, &data, &data_len, error))
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
keyfile = g_key_file_new ();
|
|
|
|
if (!g_key_file_load_from_data (keyfile, data, data_len, G_KEY_FILE_KEEP_TRANSLATIONS, error))
|
|
|
|
goto out;
|
|
|
|
|
2015-02-11 13:29:16 +00:00
|
|
|
if (g_str_has_suffix (name, ".service"))
|
|
|
|
{
|
2015-03-20 15:21:19 +00:00
|
|
|
g_autofree gchar *dbus_name = NULL;
|
|
|
|
g_autofree gchar *expected_dbus_name = g_strndup (name, strlen (name) - strlen (".service"));
|
2015-02-11 13:29:16 +00:00
|
|
|
|
|
|
|
dbus_name = g_key_file_get_string (keyfile, "D-BUS Service", "Name", NULL);
|
|
|
|
|
|
|
|
if (dbus_name == NULL || strcmp (dbus_name, expected_dbus_name) != 0)
|
|
|
|
{
|
2015-09-28 14:54:24 +00:00
|
|
|
xdg_app_fail (error, "dbus service file %s has wrong name", name);
|
2015-02-11 13:29:16 +00:00
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-01-19 09:21:09 +00:00
|
|
|
groups = g_key_file_get_groups (keyfile, NULL);
|
2015-01-16 17:46:08 +00:00
|
|
|
|
2015-01-19 09:21:09 +00:00
|
|
|
for (i = 0; groups[i] != NULL; i++)
|
2015-01-16 17:46:08 +00:00
|
|
|
{
|
2015-01-19 10:14:20 +00:00
|
|
|
g_key_file_remove_key (keyfile, groups[i], "TryExec", NULL);
|
|
|
|
|
|
|
|
/* Remove this to make sure nothing tries to execute it outside the sandbox*/
|
2015-01-19 09:21:09 +00:00
|
|
|
g_key_file_remove_key (keyfile, groups[i], "X-GNOME-Bugzilla-ExtraInfoScript", NULL);
|
|
|
|
|
|
|
|
new_exec = g_string_new ("");
|
2015-06-23 08:52:28 +00:00
|
|
|
g_string_append_printf (new_exec, XDG_APP_BINDIR"/xdg-app run --branch=%s --arch=%s", escaped_branch, escaped_arch);
|
2015-01-19 09:21:09 +00:00
|
|
|
|
2015-01-19 10:14:20 +00:00
|
|
|
old_exec = g_key_file_get_string (keyfile, groups[i], "Exec", NULL);
|
2015-01-19 09:21:09 +00:00
|
|
|
if (old_exec && g_shell_parse_argv (old_exec, &old_argc, &old_argv, NULL) && old_argc >= 1)
|
|
|
|
{
|
|
|
|
int i;
|
2015-06-23 08:52:28 +00:00
|
|
|
g_autofree char *command = maybe_quote (old_argv[0]);
|
2015-01-16 17:46:08 +00:00
|
|
|
|
2015-02-13 10:45:13 +00:00
|
|
|
g_string_append_printf (new_exec, " --command=%s", command);
|
2015-01-16 17:46:08 +00:00
|
|
|
|
2015-01-19 09:21:09 +00:00
|
|
|
g_string_append (new_exec, " ");
|
|
|
|
g_string_append (new_exec, escaped_app);
|
2015-01-16 17:46:08 +00:00
|
|
|
|
2015-01-19 09:21:09 +00:00
|
|
|
for (i = 1; i < old_argc; i++)
|
|
|
|
{
|
2015-06-23 08:52:28 +00:00
|
|
|
g_autofree char *arg = maybe_quote (old_argv[i]);
|
2015-01-19 09:21:09 +00:00
|
|
|
g_string_append (new_exec, " ");
|
|
|
|
g_string_append (new_exec, arg);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
2015-01-16 17:46:08 +00:00
|
|
|
{
|
|
|
|
g_string_append (new_exec, " ");
|
2015-01-19 09:21:09 +00:00
|
|
|
g_string_append (new_exec, escaped_app);
|
2015-01-16 17:46:08 +00:00
|
|
|
}
|
|
|
|
|
2015-01-19 09:21:09 +00:00
|
|
|
g_key_file_set_string (keyfile, groups[i], G_KEY_FILE_DESKTOP_KEY_EXEC, new_exec->str);
|
|
|
|
}
|
2015-01-16 17:46:08 +00:00
|
|
|
|
|
|
|
new_data = g_key_file_to_data (keyfile, &new_data_len, error);
|
|
|
|
if (new_data == NULL)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
if (!gs_file_open_in_tmpdir_at (parent_fd, 0755, &tmpfile_name, &out_stream, cancellable, error))
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
if (!g_output_stream_write_all (out_stream, new_data, new_data_len, NULL, cancellable, error))
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
if (!g_output_stream_close (out_stream, cancellable, error))
|
|
|
|
goto out;
|
|
|
|
|
2015-03-20 15:53:32 +00:00
|
|
|
if (target)
|
|
|
|
*target = g_steal_pointer (&tmpfile_name);
|
2015-01-16 17:46:08 +00:00
|
|
|
|
|
|
|
ret = TRUE;
|
|
|
|
out:
|
|
|
|
|
|
|
|
if (new_exec != NULL)
|
|
|
|
g_string_free (new_exec, TRUE);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
2015-03-09 16:13:04 +00:00
|
|
|
rewrite_export_dir (const char *app,
|
|
|
|
const char *branch,
|
|
|
|
const char *arch,
|
|
|
|
int source_parent_fd,
|
|
|
|
const char *source_name,
|
|
|
|
GCancellable *cancellable,
|
|
|
|
GError **error)
|
|
|
|
{
|
|
|
|
gboolean ret = FALSE;
|
2015-03-20 15:41:06 +00:00
|
|
|
g_auto(GLnxDirFdIterator) source_iter = {0};
|
2015-03-20 15:21:19 +00:00
|
|
|
g_autoptr(GHashTable) visited_children = NULL;
|
2015-03-09 16:13:04 +00:00
|
|
|
struct dirent *dent;
|
|
|
|
|
2015-03-20 15:41:06 +00:00
|
|
|
if (!glnx_dirfd_iterator_init_at (source_parent_fd, source_name, FALSE, &source_iter, error))
|
2015-03-09 16:13:04 +00:00
|
|
|
goto out;
|
|
|
|
|
|
|
|
visited_children = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
|
|
|
|
|
|
|
|
while (TRUE)
|
|
|
|
{
|
|
|
|
struct stat stbuf;
|
|
|
|
|
2015-03-20 15:41:06 +00:00
|
|
|
if (!glnx_dirfd_iterator_next_dent (&source_iter, &dent, cancellable, error))
|
2015-03-09 16:13:04 +00:00
|
|
|
goto out;
|
|
|
|
|
|
|
|
if (dent == NULL)
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (g_hash_table_contains (visited_children, dent->d_name))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/* Avoid processing the same file again if it was re-created during an export */
|
|
|
|
g_hash_table_insert (visited_children, g_strdup (dent->d_name), GINT_TO_POINTER(1));
|
|
|
|
|
|
|
|
if (fstatat (source_iter.fd, dent->d_name, &stbuf, AT_SYMLINK_NOFOLLOW) == -1)
|
|
|
|
{
|
2015-03-20 15:45:09 +00:00
|
|
|
if (errno == ENOENT)
|
2015-03-09 16:13:04 +00:00
|
|
|
continue;
|
|
|
|
else
|
|
|
|
{
|
2015-03-20 15:45:09 +00:00
|
|
|
glnx_set_error_from_errno (error);
|
2015-03-09 16:13:04 +00:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (S_ISDIR (stbuf.st_mode))
|
|
|
|
{
|
|
|
|
if (!rewrite_export_dir (app, branch, arch,
|
|
|
|
source_iter.fd, dent->d_name,
|
|
|
|
cancellable, error))
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
else if (S_ISREG (stbuf.st_mode))
|
|
|
|
{
|
|
|
|
if (!xdg_app_has_name_prefix (dent->d_name, app))
|
|
|
|
{
|
|
|
|
g_warning ("Non-prefixed filename %s in app %s, removing.\n", dent->d_name, app);
|
|
|
|
if (unlinkat (source_iter.fd, dent->d_name, 0) != 0 && errno != ENOENT)
|
|
|
|
{
|
2015-03-20 15:45:09 +00:00
|
|
|
glnx_set_error_from_errno (error);
|
2015-03-09 16:13:04 +00:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (g_str_has_suffix (dent->d_name, ".desktop") || g_str_has_suffix (dent->d_name, ".service"))
|
|
|
|
{
|
2015-03-20 15:21:19 +00:00
|
|
|
g_autofree gchar *new_name = NULL;
|
2015-03-09 16:13:04 +00:00
|
|
|
|
|
|
|
if (!export_desktop_file (app, branch, arch, source_iter.fd, dent->d_name, &stbuf, &new_name, cancellable, error))
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
g_hash_table_insert (visited_children, g_strdup (new_name), GINT_TO_POINTER(1));
|
|
|
|
|
|
|
|
if (renameat (source_iter.fd, new_name, source_iter.fd, dent->d_name) != 0)
|
|
|
|
{
|
2015-03-20 15:45:09 +00:00
|
|
|
glnx_set_error_from_errno (error);
|
2015-03-09 16:13:04 +00:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
g_warning ("Not exporting file %s of unsupported type\n", dent->d_name);
|
|
|
|
if (unlinkat (source_iter.fd, dent->d_name, 0) != 0 && errno != ENOENT)
|
|
|
|
{
|
2015-03-20 15:45:09 +00:00
|
|
|
glnx_set_error_from_errno (error);
|
2015-03-09 16:13:04 +00:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = TRUE;
|
|
|
|
out:
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
gboolean
|
|
|
|
xdg_app_rewrite_export_dir (const char *app,
|
|
|
|
const char *branch,
|
|
|
|
const char *arch,
|
|
|
|
GFile *source,
|
|
|
|
GCancellable *cancellable,
|
|
|
|
GError **error)
|
|
|
|
{
|
|
|
|
gboolean ret = FALSE;
|
|
|
|
|
|
|
|
/* The fds are closed by this call */
|
|
|
|
if (!rewrite_export_dir (app, branch, arch,
|
|
|
|
AT_FDCWD, gs_file_get_path_cached (source),
|
|
|
|
cancellable, error))
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
ret = TRUE;
|
|
|
|
|
|
|
|
out:
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
export_dir (int source_parent_fd,
|
2015-01-16 17:46:08 +00:00
|
|
|
const char *source_name,
|
|
|
|
const char *source_symlink_prefix,
|
|
|
|
const char *source_relpath,
|
|
|
|
int destination_parent_fd,
|
|
|
|
const char *destination_name,
|
|
|
|
GCancellable *cancellable,
|
|
|
|
GError **error)
|
|
|
|
{
|
|
|
|
gboolean ret = FALSE;
|
|
|
|
int res;
|
2015-03-20 15:41:06 +00:00
|
|
|
g_auto(GLnxDirFdIterator) source_iter = {0};
|
2015-03-20 15:36:17 +00:00
|
|
|
glnx_fd_close int destination_dfd = -1;
|
2015-01-16 17:46:08 +00:00
|
|
|
struct dirent *dent;
|
|
|
|
|
2015-03-20 15:41:06 +00:00
|
|
|
if (!glnx_dirfd_iterator_init_at (source_parent_fd, source_name, FALSE, &source_iter, error))
|
2015-01-16 17:46:08 +00:00
|
|
|
goto out;
|
|
|
|
|
|
|
|
do
|
2015-09-11 13:30:39 +00:00
|
|
|
res = mkdirat (destination_parent_fd, destination_name, 0755);
|
2015-01-16 17:46:08 +00:00
|
|
|
while (G_UNLIKELY (res == -1 && errno == EINTR));
|
|
|
|
if (res == -1)
|
|
|
|
{
|
|
|
|
if (errno != EEXIST)
|
|
|
|
{
|
2015-03-20 15:45:09 +00:00
|
|
|
glnx_set_error_from_errno (error);
|
2015-01-16 17:46:08 +00:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!gs_file_open_dir_fd_at (destination_parent_fd, destination_name,
|
|
|
|
&destination_dfd,
|
|
|
|
cancellable, error))
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
while (TRUE)
|
|
|
|
{
|
|
|
|
struct stat stbuf;
|
|
|
|
|
2015-03-20 15:41:06 +00:00
|
|
|
if (!glnx_dirfd_iterator_next_dent (&source_iter, &dent, cancellable, error))
|
2015-01-16 17:46:08 +00:00
|
|
|
goto out;
|
|
|
|
|
|
|
|
if (dent == NULL)
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (fstatat (source_iter.fd, dent->d_name, &stbuf, AT_SYMLINK_NOFOLLOW) == -1)
|
|
|
|
{
|
2015-03-20 15:45:09 +00:00
|
|
|
if (errno == ENOENT)
|
2015-01-16 17:46:08 +00:00
|
|
|
continue;
|
|
|
|
else
|
|
|
|
{
|
2015-03-20 15:45:09 +00:00
|
|
|
glnx_set_error_from_errno (error);
|
2015-01-16 17:46:08 +00:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (S_ISDIR (stbuf.st_mode))
|
|
|
|
{
|
2015-03-20 15:21:19 +00:00
|
|
|
g_autofree gchar *child_symlink_prefix = g_build_filename ("..", source_symlink_prefix, dent->d_name, NULL);
|
|
|
|
g_autofree gchar *child_relpath = g_strconcat (source_relpath, dent->d_name, "/", NULL);
|
2015-01-16 17:46:08 +00:00
|
|
|
|
2015-03-09 16:13:04 +00:00
|
|
|
if (!export_dir (source_iter.fd, dent->d_name, child_symlink_prefix, child_relpath, destination_dfd, dent->d_name,
|
2015-01-16 17:46:08 +00:00
|
|
|
cancellable, error))
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
else if (S_ISREG (stbuf.st_mode))
|
|
|
|
{
|
2015-03-20 15:21:19 +00:00
|
|
|
g_autofree gchar *target = NULL;
|
2015-01-16 17:46:08 +00:00
|
|
|
|
2015-02-11 13:30:53 +00:00
|
|
|
target = g_build_filename (source_symlink_prefix, dent->d_name, NULL);
|
|
|
|
|
2015-01-16 17:46:08 +00:00
|
|
|
if (unlinkat (destination_dfd, dent->d_name, 0) != 0 && errno != ENOENT)
|
|
|
|
{
|
2015-03-20 15:45:09 +00:00
|
|
|
glnx_set_error_from_errno (error);
|
2015-01-16 17:46:08 +00:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (symlinkat (target, destination_dfd, dent->d_name) != 0)
|
|
|
|
{
|
2015-03-20 15:45:09 +00:00
|
|
|
glnx_set_error_from_errno (error);
|
2015-01-16 17:46:08 +00:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = TRUE;
|
|
|
|
out:
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
gboolean
|
2015-03-09 16:13:04 +00:00
|
|
|
xdg_app_export_dir (GFile *source,
|
2015-01-16 17:46:08 +00:00
|
|
|
GFile *destination,
|
|
|
|
const char *symlink_prefix,
|
|
|
|
GCancellable *cancellable,
|
|
|
|
GError **error)
|
|
|
|
{
|
|
|
|
gboolean ret = FALSE;
|
|
|
|
|
|
|
|
if (!gs_file_ensure_directory (destination, TRUE, cancellable, error))
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
/* The fds are closed by this call */
|
2015-03-09 16:13:04 +00:00
|
|
|
if (!export_dir (AT_FDCWD, gs_file_get_path_cached (source), symlink_prefix, "",
|
2015-01-16 17:46:08 +00:00
|
|
|
AT_FDCWD, gs_file_get_path_cached (destination),
|
|
|
|
cancellable, error))
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
ret = TRUE;
|
|
|
|
|
|
|
|
out:
|
|
|
|
return ret;
|
|
|
|
}
|
2015-01-15 20:44:22 +00:00
|
|
|
|
2015-03-10 09:14:12 +00:00
|
|
|
gboolean
|
2015-01-19 11:12:30 +00:00
|
|
|
xdg_app_dir_update_exports (XdgAppDir *self,
|
2015-03-11 08:53:47 +00:00
|
|
|
const char *changed_app,
|
2015-01-19 11:12:30 +00:00
|
|
|
GCancellable *cancellable,
|
|
|
|
GError **error)
|
|
|
|
{
|
|
|
|
gboolean ret = FALSE;
|
2015-03-20 15:21:19 +00:00
|
|
|
g_autoptr(GFile) exports = NULL;
|
|
|
|
g_autofree char *current_ref = NULL;
|
|
|
|
g_autofree char *active_id = NULL;
|
|
|
|
g_autofree char *symlink_prefix = NULL;
|
2015-01-19 11:12:30 +00:00
|
|
|
|
|
|
|
exports = xdg_app_dir_get_exports_dir (self);
|
|
|
|
|
2015-03-11 08:53:47 +00:00
|
|
|
if (!gs_file_ensure_directory (exports, TRUE, cancellable, error))
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
if (changed_app &&
|
|
|
|
(current_ref = xdg_app_dir_current_ref (self, changed_app, cancellable)) &&
|
|
|
|
(active_id = xdg_app_dir_read_active (self, current_ref, cancellable)))
|
2015-01-19 11:12:30 +00:00
|
|
|
{
|
2015-03-20 15:21:19 +00:00
|
|
|
g_autoptr(GFile) deploy_base = NULL;
|
|
|
|
g_autoptr(GFile) active = NULL;
|
|
|
|
g_autoptr(GFile) export = NULL;
|
2015-01-19 11:12:30 +00:00
|
|
|
|
2015-03-11 08:53:47 +00:00
|
|
|
deploy_base = xdg_app_dir_get_deploy_dir (self, current_ref);
|
|
|
|
active = g_file_get_child (deploy_base, active_id);
|
|
|
|
export = g_file_get_child (active, "export");
|
|
|
|
|
|
|
|
if (g_file_query_exists (export, cancellable))
|
|
|
|
{
|
|
|
|
symlink_prefix = g_build_filename ("..", "app", changed_app, "current", "active", "export", NULL);
|
|
|
|
if (!xdg_app_export_dir (export, exports,
|
|
|
|
symlink_prefix,
|
|
|
|
cancellable,
|
|
|
|
error))
|
|
|
|
goto out;
|
|
|
|
}
|
2015-01-19 11:12:30 +00:00
|
|
|
}
|
|
|
|
|
2015-03-11 08:53:47 +00:00
|
|
|
if (!xdg_app_remove_dangling_symlinks (exports, cancellable, error))
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
if (!xdg_app_dir_run_triggers (self, cancellable, error))
|
|
|
|
goto out;
|
|
|
|
|
2015-01-19 11:12:30 +00:00
|
|
|
ret = TRUE;
|
|
|
|
|
|
|
|
out:
|
|
|
|
return ret;
|
|
|
|
}
|
2015-01-15 20:44:22 +00:00
|
|
|
|
2014-12-18 10:06:37 +00:00
|
|
|
gboolean
|
|
|
|
xdg_app_dir_deploy (XdgAppDir *self,
|
|
|
|
const char *ref,
|
2014-12-19 08:35:38 +00:00
|
|
|
const char *checksum,
|
2014-12-18 10:06:37 +00:00
|
|
|
GCancellable *cancellable,
|
|
|
|
GError **error)
|
|
|
|
{
|
|
|
|
gboolean ret = FALSE;
|
2015-03-20 15:21:19 +00:00
|
|
|
g_autofree char *resolved_ref = NULL;
|
|
|
|
g_autoptr(GFile) root = NULL;
|
|
|
|
g_autoptr(GFileInfo) file_info = NULL;
|
|
|
|
g_autoptr(GFile) deploy_base = NULL;
|
|
|
|
g_autoptr(GFile) checkoutdir = NULL;
|
|
|
|
g_autoptr(GFile) dotref = NULL;
|
|
|
|
g_autoptr(GFile) export = NULL;
|
2014-12-18 10:06:37 +00:00
|
|
|
|
|
|
|
if (!xdg_app_dir_ensure_repo (self, cancellable, error))
|
|
|
|
goto out;
|
|
|
|
|
2015-01-17 02:49:54 +00:00
|
|
|
deploy_base = xdg_app_dir_get_deploy_dir (self, ref);
|
|
|
|
|
2014-12-19 08:35:38 +00:00
|
|
|
if (checksum == NULL)
|
2014-12-18 10:06:37 +00:00
|
|
|
{
|
2015-11-16 07:25:47 +00:00
|
|
|
g_autofree char *origin = xdg_app_dir_get_origin (self, ref, NULL, NULL);
|
|
|
|
g_autofree char *origin_and_ref = NULL;
|
|
|
|
|
|
|
|
/* 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 (origin)
|
|
|
|
origin_and_ref = g_strdup_printf ("%s:%s", origin, ref);
|
|
|
|
else
|
|
|
|
origin_and_ref = g_strdup (ref);
|
|
|
|
|
|
|
|
g_debug ("No checksum specified, getting tip of %s", origin_and_ref);
|
|
|
|
if (!ostree_repo_resolve_rev (self->repo, origin_and_ref, FALSE, &resolved_ref, error))
|
2015-02-07 17:02:04 +00:00
|
|
|
{
|
|
|
|
g_prefix_error (error, "While trying to resolve ref %s: ", ref);
|
|
|
|
goto out;
|
|
|
|
}
|
2014-12-18 10:06:37 +00:00
|
|
|
|
2014-12-19 08:35:38 +00:00
|
|
|
checksum = resolved_ref;
|
2015-11-16 07:25:47 +00:00
|
|
|
g_debug ("tip resolved to: %s", checksum);
|
2014-12-18 10:06:37 +00:00
|
|
|
}
|
2015-01-17 02:49:54 +00:00
|
|
|
else
|
|
|
|
{
|
2015-03-20 15:21:19 +00:00
|
|
|
g_autoptr(GFile) root = NULL;
|
|
|
|
g_autofree char *commit = NULL;
|
2014-12-18 10:06:37 +00:00
|
|
|
|
2015-01-17 02:49:54 +00:00
|
|
|
g_debug ("Looking for checksum %s in local repo", checksum);
|
|
|
|
if (!ostree_repo_read_commit (self->repo, checksum, &root, &commit, cancellable, NULL))
|
|
|
|
{
|
|
|
|
GSConsole *console = NULL;
|
2015-03-20 15:21:19 +00:00
|
|
|
g_autoptr(OstreeAsyncProgress) progress = NULL;
|
2015-01-17 02:49:54 +00:00
|
|
|
const char *refs[2];
|
2015-03-20 15:21:19 +00:00
|
|
|
g_autoptr(GFile) origin = NULL;
|
|
|
|
g_autofree char *repository = NULL;
|
2015-01-17 02:49:54 +00:00
|
|
|
|
|
|
|
refs[0] = checksum;
|
|
|
|
refs[1] = NULL;
|
|
|
|
|
|
|
|
origin = g_file_get_child (deploy_base, "origin");
|
|
|
|
if (!g_file_load_contents (origin, cancellable, &repository, NULL, NULL, error))
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
g_debug ("Pulling checksum %s from remote %s", checksum, repository);
|
|
|
|
|
|
|
|
console = gs_console_get ();
|
|
|
|
if (console)
|
|
|
|
{
|
|
|
|
gs_console_begin_status_line (console, "", NULL, NULL);
|
|
|
|
progress = ostree_async_progress_new_and_connect (ostree_repo_pull_default_console_progress_changed, console);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!ostree_repo_pull (self->repo, repository,
|
|
|
|
(char **)refs, OSTREE_REPO_PULL_FLAGS_NONE,
|
|
|
|
progress,
|
|
|
|
cancellable, error))
|
2015-02-07 17:02:04 +00:00
|
|
|
{
|
|
|
|
g_prefix_error (error, "Failed to pull %s from remote %s: ", checksum, repository);
|
|
|
|
goto out;
|
|
|
|
}
|
2015-01-17 02:49:54 +00:00
|
|
|
}
|
|
|
|
}
|
2014-12-18 10:06:37 +00:00
|
|
|
|
2014-12-19 08:35:38 +00:00
|
|
|
checkoutdir = g_file_get_child (deploy_base, checksum);
|
2014-12-18 10:06:37 +00:00
|
|
|
if (g_file_query_exists (checkoutdir, cancellable))
|
|
|
|
{
|
|
|
|
g_set_error (error, XDG_APP_DIR_ERROR,
|
|
|
|
XDG_APP_DIR_ERROR_ALREADY_DEPLOYED,
|
2014-12-19 08:35:38 +00:00
|
|
|
"%s version %s already deployed", ref, checksum);
|
2014-12-18 10:06:37 +00:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2014-12-19 08:35:38 +00:00
|
|
|
if (!ostree_repo_read_commit (self->repo, checksum, &root, NULL, cancellable, error))
|
2015-02-07 17:02:04 +00:00
|
|
|
{
|
|
|
|
g_prefix_error (error, "Failed to read commit %s: ", checksum);
|
|
|
|
goto out;
|
|
|
|
}
|
2014-12-18 10:06:37 +00:00
|
|
|
|
|
|
|
file_info = g_file_query_info (root, OSTREE_GIO_FAST_QUERYINFO,
|
|
|
|
G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
|
|
|
|
cancellable, error);
|
|
|
|
if (file_info == NULL)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
if (!ostree_repo_checkout_tree (self->repo,
|
|
|
|
self->user ? OSTREE_REPO_CHECKOUT_MODE_USER : OSTREE_REPO_CHECKOUT_MODE_NONE,
|
|
|
|
OSTREE_REPO_CHECKOUT_OVERWRITE_NONE,
|
|
|
|
checkoutdir,
|
|
|
|
OSTREE_REPO_FILE (root), file_info,
|
|
|
|
cancellable, error))
|
2015-02-07 17:02:04 +00:00
|
|
|
{
|
2015-03-20 15:21:19 +00:00
|
|
|
g_autofree char *rootpath = NULL;
|
|
|
|
g_autofree char *checkoutpath = NULL;
|
2015-02-07 17:02:04 +00:00
|
|
|
|
|
|
|
rootpath = g_file_get_path (root);
|
|
|
|
checkoutpath = g_file_get_path (checkoutdir);
|
|
|
|
g_prefix_error (error, "While trying to checkout %s into %s: ", rootpath, checkoutpath);
|
|
|
|
goto out;
|
|
|
|
}
|
2014-12-18 10:06:37 +00:00
|
|
|
|
2015-02-05 17:15:35 +00:00
|
|
|
dotref = g_file_resolve_relative_path (checkoutdir, "files/.ref");
|
|
|
|
if (!g_file_replace_contents (dotref, "", 0, NULL, FALSE,
|
|
|
|
G_FILE_CREATE_NONE, NULL, cancellable, error))
|
|
|
|
goto out;
|
|
|
|
|
2015-03-10 09:14:12 +00:00
|
|
|
export = g_file_get_child (checkoutdir, "export");
|
|
|
|
if (g_file_query_exists (export, cancellable))
|
2015-01-09 10:46:07 +00:00
|
|
|
{
|
2015-08-31 07:51:48 +00:00
|
|
|
g_auto(GStrv) ref_parts = NULL;
|
2015-01-09 10:46:07 +00:00
|
|
|
|
2015-03-10 09:14:12 +00:00
|
|
|
ref_parts = g_strsplit (ref, "/", -1);
|
2015-03-09 16:13:04 +00:00
|
|
|
|
2015-03-10 09:14:12 +00:00
|
|
|
if (!xdg_app_rewrite_export_dir (ref_parts[1], ref_parts[3], ref_parts[2], export,
|
|
|
|
cancellable,
|
|
|
|
error))
|
|
|
|
goto out;
|
2015-01-09 10:46:07 +00:00
|
|
|
}
|
2014-12-18 10:06:37 +00:00
|
|
|
|
2014-12-19 10:21:47 +00:00
|
|
|
if (!xdg_app_dir_set_active (self, ref, checksum, cancellable, error))
|
2014-12-18 10:06:37 +00:00
|
|
|
goto out;
|
|
|
|
|
2014-12-19 10:18:27 +00:00
|
|
|
ret = TRUE;
|
|
|
|
out:
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2015-01-22 19:42:23 +00:00
|
|
|
gboolean
|
|
|
|
xdg_app_dir_collect_deployed_refs (XdgAppDir *self,
|
|
|
|
const char *type,
|
|
|
|
const char *name_prefix,
|
|
|
|
const char *branch,
|
|
|
|
const char *arch,
|
|
|
|
GHashTable *hash,
|
|
|
|
GCancellable *cancellable,
|
|
|
|
GError **error)
|
|
|
|
{
|
|
|
|
gboolean ret = FALSE;
|
2015-03-20 15:21:19 +00:00
|
|
|
g_autoptr(GFile) dir = NULL;
|
|
|
|
g_autoptr(GFileEnumerator) dir_enum = NULL;
|
|
|
|
g_autoptr(GFileInfo) child_info = NULL;
|
2015-01-22 19:42:23 +00:00
|
|
|
GError *temp_error = NULL;
|
|
|
|
|
|
|
|
dir = g_file_get_child (self->basedir, type);
|
|
|
|
if (!g_file_query_exists (dir, cancellable))
|
|
|
|
return TRUE;
|
|
|
|
|
|
|
|
dir_enum = g_file_enumerate_children (dir, OSTREE_GIO_FAST_QUERYINFO,
|
|
|
|
G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
|
|
|
|
cancellable,
|
|
|
|
error);
|
|
|
|
if (!dir_enum)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
while ((child_info = g_file_enumerator_next_file (dir_enum, cancellable, &temp_error)) != NULL)
|
|
|
|
{
|
|
|
|
const char *name = g_file_info_get_name (child_info);
|
|
|
|
|
|
|
|
if (g_file_info_get_file_type (child_info) == G_FILE_TYPE_DIRECTORY &&
|
|
|
|
name[0] != '.' && (name_prefix == NULL || g_str_has_prefix (name, name_prefix)))
|
|
|
|
{
|
2015-03-20 15:21:19 +00:00
|
|
|
g_autoptr(GFile) child1 = g_file_get_child (dir, name);
|
|
|
|
g_autoptr(GFile) child2 = g_file_get_child (child1, branch);
|
|
|
|
g_autoptr(GFile) child3 = g_file_get_child (child2, arch);
|
|
|
|
g_autoptr(GFile) active = g_file_get_child (child3, "active");
|
2015-01-22 19:42:23 +00:00
|
|
|
|
|
|
|
if (g_file_query_exists (active, cancellable))
|
|
|
|
g_hash_table_add (hash, g_strdup (name));
|
|
|
|
}
|
|
|
|
|
|
|
|
g_clear_object (&child_info);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (temp_error != NULL)
|
|
|
|
{
|
|
|
|
g_propagate_error (error, temp_error);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = TRUE;
|
|
|
|
out:
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2014-12-19 10:18:27 +00:00
|
|
|
gboolean
|
|
|
|
xdg_app_dir_list_deployed (XdgAppDir *self,
|
|
|
|
const char *ref,
|
|
|
|
char ***deployed_checksums,
|
|
|
|
GCancellable *cancellable,
|
|
|
|
GError **error)
|
|
|
|
{
|
|
|
|
gboolean ret = FALSE;
|
2015-03-20 15:21:19 +00:00
|
|
|
g_autoptr(GFile) deploy_base = NULL;
|
|
|
|
g_autoptr(GPtrArray) checksums = NULL;
|
2014-12-19 10:18:27 +00:00
|
|
|
GError *temp_error = NULL;
|
2015-03-20 15:21:19 +00:00
|
|
|
g_autoptr(GFileEnumerator) dir_enum = NULL;
|
|
|
|
g_autoptr(GFile) child = NULL;
|
|
|
|
g_autoptr(GFileInfo) child_info = NULL;
|
2015-10-19 08:39:25 +00:00
|
|
|
g_autoptr(GError) my_error = NULL;
|
2014-12-19 10:18:27 +00:00
|
|
|
|
|
|
|
deploy_base = xdg_app_dir_get_deploy_dir (self, ref);
|
|
|
|
|
|
|
|
checksums = g_ptr_array_new_with_free_func (g_free);
|
|
|
|
|
|
|
|
dir_enum = g_file_enumerate_children (deploy_base, OSTREE_GIO_FAST_QUERYINFO,
|
|
|
|
G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
|
|
|
|
cancellable,
|
2015-10-19 08:39:25 +00:00
|
|
|
&my_error);
|
2014-12-19 10:18:27 +00:00
|
|
|
if (!dir_enum)
|
2015-10-19 08:39:25 +00:00
|
|
|
{
|
|
|
|
if (g_error_matches (my_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
|
|
|
|
ret = TRUE; /* Success, but empty */
|
|
|
|
else
|
|
|
|
g_propagate_error (error, g_steal_pointer (&my_error));
|
|
|
|
goto out;
|
|
|
|
}
|
2014-12-19 10:18:27 +00:00
|
|
|
|
|
|
|
while ((child_info = g_file_enumerator_next_file (dir_enum, cancellable, &temp_error)) != NULL)
|
|
|
|
{
|
|
|
|
const char *name;
|
|
|
|
|
|
|
|
name = g_file_info_get_name (child_info);
|
|
|
|
|
|
|
|
g_clear_object (&child);
|
|
|
|
child = g_file_get_child (deploy_base, name);
|
|
|
|
|
|
|
|
if (g_file_info_get_file_type (child_info) == G_FILE_TYPE_DIRECTORY &&
|
|
|
|
name[0] != '.' &&
|
|
|
|
strlen (name) == 64)
|
|
|
|
g_ptr_array_add (checksums, g_strdup (name));
|
|
|
|
|
|
|
|
g_clear_object (&child_info);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (temp_error != NULL)
|
|
|
|
{
|
|
|
|
g_propagate_error (error, temp_error);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = TRUE;
|
2015-10-19 08:39:25 +00:00
|
|
|
|
2014-12-19 10:18:27 +00:00
|
|
|
out:
|
2015-10-19 08:39:25 +00:00
|
|
|
if (ret)
|
|
|
|
{
|
|
|
|
g_ptr_array_add (checksums, NULL);
|
|
|
|
*deployed_checksums = (char **)g_ptr_array_free (g_steal_pointer (&checksums), FALSE);
|
|
|
|
}
|
|
|
|
|
2014-12-19 10:18:27 +00:00
|
|
|
return ret;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2015-02-05 21:31:01 +00:00
|
|
|
static gboolean
|
|
|
|
dir_is_locked (GFile *dir)
|
|
|
|
{
|
2015-03-20 15:36:17 +00:00
|
|
|
glnx_fd_close int ref_fd = -1;
|
2015-02-05 21:31:01 +00:00
|
|
|
struct flock lock = {0};
|
2015-03-20 15:21:19 +00:00
|
|
|
g_autoptr(GFile) reffile = NULL;
|
2015-02-05 21:31:01 +00:00
|
|
|
|
|
|
|
reffile = g_file_resolve_relative_path (dir, "files/.ref");
|
|
|
|
|
|
|
|
ref_fd = open (gs_file_get_path_cached (reffile), O_RDWR | O_CLOEXEC);
|
|
|
|
if (ref_fd != -1)
|
|
|
|
{
|
|
|
|
lock.l_type = F_WRLCK;
|
|
|
|
lock.l_whence = SEEK_SET;
|
|
|
|
lock.l_start = 0;
|
|
|
|
lock.l_len = 0;
|
|
|
|
|
|
|
|
if (fcntl (ref_fd, F_GETLK, &lock) == 0)
|
|
|
|
return lock.l_type != F_UNLCK;
|
|
|
|
}
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2014-12-19 10:18:27 +00:00
|
|
|
gboolean
|
|
|
|
xdg_app_dir_undeploy (XdgAppDir *self,
|
|
|
|
const char *ref,
|
|
|
|
const char *checksum,
|
2015-02-05 21:31:01 +00:00
|
|
|
gboolean force_remove,
|
2014-12-19 10:18:27 +00:00
|
|
|
GCancellable *cancellable,
|
|
|
|
GError **error)
|
|
|
|
{
|
|
|
|
gboolean ret = FALSE;
|
2015-03-20 15:21:19 +00:00
|
|
|
g_autoptr(GFile) deploy_base = NULL;
|
|
|
|
g_autoptr(GFile) checkoutdir = NULL;
|
|
|
|
g_autoptr(GFile) removed_subdir = NULL;
|
|
|
|
g_autoptr(GFile) removed_dir = NULL;
|
|
|
|
g_autofree char *tmpname = NULL;
|
|
|
|
g_autofree char *active = NULL;
|
2014-12-19 10:18:27 +00:00
|
|
|
int i;
|
|
|
|
|
|
|
|
g_assert (ref != NULL);
|
|
|
|
g_assert (checksum != NULL);
|
|
|
|
|
|
|
|
deploy_base = xdg_app_dir_get_deploy_dir (self, ref);
|
|
|
|
|
|
|
|
checkoutdir = g_file_get_child (deploy_base, checksum);
|
|
|
|
if (!g_file_query_exists (checkoutdir, cancellable))
|
|
|
|
{
|
|
|
|
g_set_error (error, XDG_APP_DIR_ERROR,
|
|
|
|
XDG_APP_DIR_ERROR_ALREADY_UNDEPLOYED,
|
|
|
|
"%s version %s already undeployed", ref, checksum);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!xdg_app_dir_ensure_repo (self, cancellable, error))
|
|
|
|
goto out;
|
|
|
|
|
2014-12-19 10:21:47 +00:00
|
|
|
active = xdg_app_dir_read_active (self, ref, cancellable);
|
|
|
|
if (active != NULL && strcmp (active, checksum) == 0)
|
2014-12-19 10:18:27 +00:00
|
|
|
{
|
2015-08-31 07:51:48 +00:00
|
|
|
g_auto(GStrv) deployed_checksums = NULL;
|
2014-12-19 10:18:27 +00:00
|
|
|
const char *some_deployment;
|
|
|
|
|
2014-12-19 10:21:47 +00:00
|
|
|
/* We're removing the active deployment, start by repointing that
|
2014-12-19 10:18:27 +00:00
|
|
|
to another deployment if one exists */
|
|
|
|
|
|
|
|
if (!xdg_app_dir_list_deployed (self, ref,
|
|
|
|
&deployed_checksums,
|
|
|
|
cancellable, error))
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
some_deployment = NULL;
|
|
|
|
for (i = 0; deployed_checksums[i] != NULL; i++)
|
|
|
|
{
|
|
|
|
if (strcmp (deployed_checksums[i], checksum) == 0)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
some_deployment = deployed_checksums[i];
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2014-12-19 10:21:47 +00:00
|
|
|
if (!xdg_app_dir_set_active (self, ref, some_deployment, cancellable, error))
|
2014-12-19 10:18:27 +00:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2015-02-05 21:31:01 +00:00
|
|
|
removed_dir = xdg_app_dir_get_removed_dir (self);
|
|
|
|
if (!gs_file_ensure_directory (removed_dir, TRUE, cancellable, error))
|
|
|
|
goto out;
|
2014-12-19 10:18:27 +00:00
|
|
|
|
2015-02-05 21:31:01 +00:00
|
|
|
tmpname = gs_fileutil_gen_tmp_name ("", checksum);
|
|
|
|
removed_subdir = g_file_get_child (removed_dir, tmpname);
|
2014-12-19 10:18:27 +00:00
|
|
|
|
|
|
|
if (!gs_file_rename (checkoutdir,
|
2015-02-05 21:31:01 +00:00
|
|
|
removed_subdir,
|
2014-12-18 10:06:37 +00:00
|
|
|
cancellable, error))
|
|
|
|
goto out;
|
|
|
|
|
2015-02-05 21:31:01 +00:00
|
|
|
if (force_remove || !dir_is_locked (removed_subdir))
|
|
|
|
{
|
2015-05-13 15:07:24 +00:00
|
|
|
GError *tmp_error = NULL;
|
|
|
|
|
|
|
|
if (!gs_shutil_rm_rf (removed_subdir, cancellable, &tmp_error))
|
|
|
|
{
|
2015-05-14 21:49:54 +00:00
|
|
|
g_warning ("Unable to remove old checkout: %s\n", tmp_error->message);
|
2015-05-13 15:07:24 +00:00
|
|
|
g_error_free (tmp_error);
|
|
|
|
}
|
2015-02-05 21:31:01 +00:00
|
|
|
}
|
2014-12-19 10:18:27 +00:00
|
|
|
|
2014-12-18 10:06:37 +00:00
|
|
|
ret = TRUE;
|
|
|
|
out:
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2015-02-05 21:50:38 +00:00
|
|
|
gboolean
|
|
|
|
xdg_app_dir_cleanup_removed (XdgAppDir *self,
|
|
|
|
GCancellable *cancellable,
|
|
|
|
GError **error)
|
|
|
|
{
|
|
|
|
gboolean ret = FALSE;
|
2015-03-20 15:21:19 +00:00
|
|
|
g_autoptr(GFile) removed_dir = NULL;
|
|
|
|
g_autoptr(GFileEnumerator) dir_enum = NULL;
|
|
|
|
g_autoptr(GFileInfo) child_info = NULL;
|
2015-02-05 21:50:38 +00:00
|
|
|
GError *temp_error = NULL;
|
|
|
|
|
|
|
|
removed_dir = xdg_app_dir_get_removed_dir (self);
|
|
|
|
if (!g_file_query_exists (removed_dir, cancellable))
|
|
|
|
return TRUE;
|
|
|
|
|
|
|
|
dir_enum = g_file_enumerate_children (removed_dir, OSTREE_GIO_FAST_QUERYINFO,
|
|
|
|
G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
|
|
|
|
cancellable,
|
|
|
|
error);
|
|
|
|
if (!dir_enum)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
while ((child_info = g_file_enumerator_next_file (dir_enum, cancellable, &temp_error)) != NULL)
|
|
|
|
{
|
|
|
|
const char *name = g_file_info_get_name (child_info);
|
2015-03-20 15:21:19 +00:00
|
|
|
g_autoptr(GFile) child = g_file_get_child (removed_dir, name);
|
2015-02-05 21:50:38 +00:00
|
|
|
|
|
|
|
if (g_file_info_get_file_type (child_info) == G_FILE_TYPE_DIRECTORY &&
|
|
|
|
!dir_is_locked (child))
|
|
|
|
{
|
2015-05-13 15:07:24 +00:00
|
|
|
GError *tmp_error = NULL;
|
|
|
|
if (!gs_shutil_rm_rf (child, cancellable, &tmp_error))
|
|
|
|
{
|
2015-05-14 21:49:54 +00:00
|
|
|
g_warning ("Unable to remove old checkout: %s\n", tmp_error->message);
|
2015-05-13 15:07:24 +00:00
|
|
|
g_error_free (tmp_error);
|
|
|
|
}
|
2015-02-05 21:50:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
g_clear_object (&child_info);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (temp_error != NULL)
|
|
|
|
{
|
|
|
|
g_propagate_error (error, temp_error);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = TRUE;
|
|
|
|
out:
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-12-19 10:18:27 +00:00
|
|
|
gboolean
|
|
|
|
xdg_app_dir_prune (XdgAppDir *self,
|
|
|
|
GCancellable *cancellable,
|
|
|
|
GError **error)
|
|
|
|
{
|
|
|
|
gboolean ret = FALSE;
|
|
|
|
gint objects_total, objects_pruned;
|
|
|
|
guint64 pruned_object_size_total;
|
2015-03-20 15:21:19 +00:00
|
|
|
g_autofree char *formatted_freed_size = NULL;
|
2014-12-19 10:18:27 +00:00
|
|
|
|
|
|
|
if (!xdg_app_dir_ensure_repo (self, cancellable, error))
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
if (!ostree_repo_prune (self->repo,
|
|
|
|
OSTREE_REPO_PRUNE_FLAGS_REFS_ONLY,
|
|
|
|
0,
|
|
|
|
&objects_total,
|
|
|
|
&objects_pruned,
|
|
|
|
&pruned_object_size_total,
|
|
|
|
cancellable, error))
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
formatted_freed_size = g_format_size_full (pruned_object_size_total, 0);
|
|
|
|
g_debug ("Pruned %d/%d objects, size %s", objects_total, objects_pruned, formatted_freed_size);
|
|
|
|
|
|
|
|
ret = TRUE;
|
|
|
|
out:
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2014-12-18 16:51:37 +00:00
|
|
|
GFile *
|
|
|
|
xdg_app_dir_get_if_deployed (XdgAppDir *self,
|
|
|
|
const char *ref,
|
2014-12-19 08:35:38 +00:00
|
|
|
const char *checksum,
|
2014-12-18 16:51:37 +00:00
|
|
|
GCancellable *cancellable)
|
|
|
|
{
|
2015-03-20 15:21:19 +00:00
|
|
|
g_autoptr(GFile) deploy_base = NULL;
|
|
|
|
g_autoptr(GFile) deploy_dir = NULL;
|
2014-12-18 16:51:37 +00:00
|
|
|
|
|
|
|
deploy_base = xdg_app_dir_get_deploy_dir (self, ref);
|
2014-12-19 10:21:47 +00:00
|
|
|
deploy_dir = g_file_get_child (deploy_base, checksum ? checksum : "active");
|
2014-12-18 16:51:37 +00:00
|
|
|
|
|
|
|
if (g_file_query_file_type (deploy_dir, G_FILE_QUERY_INFO_NONE, cancellable) == G_FILE_TYPE_DIRECTORY)
|
|
|
|
return g_object_ref (deploy_dir);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2014-12-18 09:14:46 +00:00
|
|
|
XdgAppDir*
|
|
|
|
xdg_app_dir_new (GFile *path, gboolean user)
|
|
|
|
{
|
|
|
|
return g_object_new (XDG_APP_TYPE_DIR, "path", path, "user", user, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
XdgAppDir *
|
|
|
|
xdg_app_dir_get_system (void)
|
|
|
|
{
|
|
|
|
static XdgAppDir *system = NULL;
|
|
|
|
|
|
|
|
if (system == NULL)
|
|
|
|
{
|
2015-03-20 15:21:19 +00:00
|
|
|
g_autoptr(GFile) path = xdg_app_get_system_base_dir_location ();
|
2014-12-18 09:14:46 +00:00
|
|
|
system = xdg_app_dir_new (path, FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
return g_object_ref (system);
|
|
|
|
}
|
|
|
|
|
|
|
|
XdgAppDir *
|
|
|
|
xdg_app_dir_get_user (void)
|
|
|
|
{
|
|
|
|
static XdgAppDir *user = NULL;
|
|
|
|
|
|
|
|
if (user == NULL)
|
|
|
|
{
|
2015-03-20 15:21:19 +00:00
|
|
|
g_autoptr(GFile) path = xdg_app_get_user_base_dir_location ();
|
2015-01-08 12:50:37 +00:00
|
|
|
user = xdg_app_dir_new (path, TRUE);
|
2014-12-18 09:14:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return g_object_ref (user);
|
|
|
|
}
|
|
|
|
|
|
|
|
XdgAppDir *
|
|
|
|
xdg_app_dir_get (gboolean user)
|
|
|
|
{
|
|
|
|
if (user)
|
|
|
|
return xdg_app_dir_get_user ();
|
|
|
|
else
|
|
|
|
return xdg_app_dir_get_system ();
|
|
|
|
}
|
2015-12-04 09:48:05 +00:00
|
|
|
|
2015-12-04 10:33:30 +00:00
|
|
|
static char *
|
|
|
|
get_group (const char *remote_name)
|
|
|
|
{
|
|
|
|
return g_strdup_printf ("remote \"%s\"", remote_name);
|
|
|
|
}
|
|
|
|
|
2015-12-04 09:48:05 +00:00
|
|
|
char *
|
|
|
|
xdg_app_dir_get_remote_title (XdgAppDir *self,
|
|
|
|
const char *remote_name)
|
|
|
|
{
|
|
|
|
GKeyFile *config = ostree_repo_get_config (self->repo);
|
2015-12-04 10:33:30 +00:00
|
|
|
g_autofree char *group = get_group (remote_name);
|
2015-12-04 09:48:05 +00:00
|
|
|
|
|
|
|
if (config)
|
|
|
|
return g_key_file_get_string (config, group, "xa.title", NULL);
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2015-12-04 10:33:30 +00:00
|
|
|
gboolean
|
|
|
|
xdg_app_dir_get_remote_noenumerate (XdgAppDir *self,
|
|
|
|
const char *remote_name)
|
|
|
|
{
|
|
|
|
GKeyFile *config = ostree_repo_get_config (self->repo);
|
|
|
|
g_autofree char *group = get_group (remote_name);
|
|
|
|
|
|
|
|
if (config)
|
|
|
|
return g_key_file_get_boolean (config, group, "xa.noenumerate", NULL);
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2015-12-04 09:48:05 +00:00
|
|
|
char **
|
|
|
|
xdg_app_dir_list_remotes (XdgAppDir *self,
|
|
|
|
GCancellable *cancellable,
|
|
|
|
GError **error)
|
|
|
|
{
|
|
|
|
char **res;
|
|
|
|
|
|
|
|
if (!xdg_app_dir_ensure_repo (self, cancellable, error))
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
res = ostree_repo_remote_list (self->repo, NULL);
|
|
|
|
if (res == NULL)
|
|
|
|
res = g_new0 (char *, 1); /* Return empty array, not error */
|
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
gboolean
|
|
|
|
xdg_app_dir_list_remote_refs (XdgAppDir *self,
|
|
|
|
const char *remote,
|
|
|
|
GHashTable **refs,
|
|
|
|
GCancellable *cancellable,
|
|
|
|
GError **error)
|
|
|
|
{
|
|
|
|
g_autoptr(GError) my_error = NULL;
|
|
|
|
if (error == NULL)
|
|
|
|
error = &my_error;
|
|
|
|
|
|
|
|
if (!xdg_app_dir_ensure_repo (self, cancellable, error))
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
if (!ostree_repo_remote_list_refs (self->repo, remote,
|
|
|
|
refs, cancellable, error))
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
2015-12-04 10:09:00 +00:00
|
|
|
|
|
|
|
char *
|
|
|
|
xdg_app_dir_fetch_remote_title (XdgAppDir *self,
|
|
|
|
const char *remote,
|
|
|
|
GCancellable *cancellable,
|
|
|
|
GError **error)
|
|
|
|
{
|
|
|
|
g_autoptr(GError) my_error = NULL;
|
|
|
|
g_autoptr(GBytes) summary_bytes = NULL;
|
|
|
|
g_autoptr(GVariant) summary = NULL;
|
|
|
|
g_autoptr(GVariant) extensions = NULL;
|
|
|
|
GVariantDict dict;
|
|
|
|
g_autofree char *title = NULL;
|
|
|
|
|
|
|
|
if (error == NULL)
|
|
|
|
error = &my_error;
|
|
|
|
|
|
|
|
if (!xdg_app_dir_ensure_repo (self, cancellable, error))
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (!ostree_repo_remote_fetch_summary (self->repo, remote,
|
|
|
|
&summary_bytes, NULL,
|
|
|
|
cancellable, error))
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
if (summary_bytes == NULL)
|
|
|
|
{
|
|
|
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
|
|
|
"Remote title not available; server has no summary file");
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
summary = g_variant_new_from_bytes (OSTREE_SUMMARY_GVARIANT_FORMAT,
|
|
|
|
summary_bytes, FALSE);
|
|
|
|
extensions = g_variant_get_child_value (summary, 1);
|
|
|
|
|
|
|
|
g_variant_dict_init (&dict, extensions);
|
|
|
|
g_variant_dict_lookup (&dict, "xa.title", "s", &title);
|
|
|
|
g_variant_dict_end (&dict);
|
|
|
|
|
|
|
|
if (title == NULL)
|
|
|
|
{
|
|
|
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
|
|
|
|
"Remote title not set");
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
return g_steal_pointer (&title);
|
|
|
|
}
|