diff --git a/app/flatpak-builtins-install.c b/app/flatpak-builtins-install.c index 95d98606..505ec5e0 100644 --- a/app/flatpak-builtins-install.c +++ b/app/flatpak-builtins-install.c @@ -51,6 +51,7 @@ static gboolean opt_app; static gboolean opt_bundle; static gboolean opt_from; static gboolean opt_yes; +static gboolean opt_reinstall; static GOptionEntry options[] = { { "arch", 0, 0, G_OPTION_ARG_STRING, &opt_arch, N_("Arch to install for"), N_("ARCH") }, @@ -66,6 +67,7 @@ static GOptionEntry options[] = { { "gpg-file", 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &opt_gpg_file, N_("Check bundle signatures with GPG key from FILE (- for stdin)"), N_("FILE") }, { "subpath", 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &opt_subpaths, N_("Only install this subpath"), N_("PATH") }, { "assumeyes", 'y', 0, G_OPTION_ARG_NONE, &opt_yes, N_("Automatically answer yes for all questions"), NULL }, + { "reinstall", 0, 0, G_OPTION_ARG_NONE, &opt_reinstall, N_("Uninstall first if already installed"), NULL }, { NULL } }; @@ -286,7 +288,7 @@ install_bundle (FlatpakDir *dir, return FALSE; transaction = flatpak_transaction_new (dir, opt_yes, opt_no_pull, opt_no_deploy, - opt_no_static_deltas, !opt_no_deps, !opt_no_related); + opt_no_static_deltas, !opt_no_deps, !opt_no_related, opt_reinstall); if (!flatpak_transaction_add_install_bundle (transaction, file, gpg_data, error)) return FALSE; @@ -440,7 +442,7 @@ install_from (FlatpakDir *dir, g_print (_("Installing: %s\n"), slash + 1); transaction = flatpak_transaction_new (clone, opt_yes, opt_no_pull, opt_no_deploy, - opt_no_static_deltas, !opt_no_deps, !opt_no_related); + opt_no_static_deltas, !opt_no_deps, !opt_no_related, opt_reinstall); if (!flatpak_transaction_add_install (transaction, remote, ref, (const char **)opt_subpaths, error)) return FALSE; @@ -510,7 +512,7 @@ flatpak_builtin_install (int argc, char **argv, GCancellable *cancellable, GErro kinds = flatpak_kinds_from_bools (opt_app, opt_runtime); transaction = flatpak_transaction_new (dir, opt_yes, opt_no_pull, opt_no_deploy, - opt_no_static_deltas, !opt_no_deps, !opt_no_related); + opt_no_static_deltas, !opt_no_deps, !opt_no_related, opt_reinstall); for (i = 0; i < n_prefs; i++) { diff --git a/app/flatpak-builtins-update.c b/app/flatpak-builtins-update.c index fe73d955..68f0e7f7 100644 --- a/app/flatpak-builtins-update.c +++ b/app/flatpak-builtins-update.c @@ -187,7 +187,7 @@ flatpak_builtin_update (int argc, { FlatpakTransaction *transaction = flatpak_transaction_new (g_ptr_array_index (dirs, k), opt_yes, opt_no_pull, opt_no_deploy, - opt_no_static_deltas, !opt_no_deps, !opt_no_related); + opt_no_static_deltas, !opt_no_deps, !opt_no_related, FALSE); g_ptr_array_add (transactions, transaction); } diff --git a/app/flatpak-transaction.c b/app/flatpak-transaction.c index 080fc792..dc722d3d 100644 --- a/app/flatpak-transaction.c +++ b/app/flatpak-transaction.c @@ -60,6 +60,7 @@ struct FlatpakTransaction { gboolean no_static_deltas; gboolean add_deps; gboolean add_related; + gboolean reinstall; }; @@ -170,7 +171,8 @@ flatpak_transaction_new (FlatpakDir *dir, gboolean no_deploy, gboolean no_static_deltas, gboolean add_deps, - gboolean add_related) + gboolean add_related, + gboolean reinstall) { FlatpakTransaction *t = g_new0 (FlatpakTransaction, 1); @@ -183,6 +185,7 @@ flatpak_transaction_new (FlatpakDir *dir, t->no_static_deltas = no_static_deltas; t->add_deps = add_deps; t->add_related = add_related; + t->reinstall = reinstall; return t; } @@ -482,10 +485,20 @@ flatpak_transaction_add_ref (FlatpakTransaction *self, else if (kind == FLATPAK_TRANSACTION_OP_KIND_INSTALL) { g_assert (remote != NULL); - if (dir_ref_is_installed (self->dir, ref, NULL, NULL)) + if (!self->reinstall && + dir_ref_is_installed (self->dir, ref, &origin, NULL)) { - g_printerr (_("%s already installed, skipping\n"), pref); - return TRUE; + if (strcmp (remote, origin) == 0) + { + g_printerr (_("%s already installed, skipping\n"), pref); + return TRUE; + } + else + { + g_set_error (error, FLATPAK_ERROR, FLATPAK_ERROR_ALREADY_INSTALLED, + _("%s is already installed from other remote (%s)"), pref, origin); + return FALSE; + } } } @@ -665,7 +678,7 @@ flatpak_transaction_run (FlatpakTransaction *self, { FlatpakTransactionOp *op = l->data; g_autoptr(GError) local_error = NULL; - gboolean res; + gboolean res = TRUE; const char *pref; const char *opname; FlatpakTransactionOpKind kind; @@ -704,6 +717,7 @@ flatpak_transaction_run (FlatpakTransaction *self, self->no_pull, self->no_deploy, self->no_static_deltas, + self->reinstall, op->ref, op->remote, (const char **)op->subpaths, progress, diff --git a/app/flatpak-transaction.h b/app/flatpak-transaction.h index 5eaf7d13..4e51572f 100644 --- a/app/flatpak-transaction.h +++ b/app/flatpak-transaction.h @@ -34,7 +34,8 @@ FlatpakTransaction *flatpak_transaction_new (FlatpakDir *dir, gboolean no_deploy, gboolean no_static_deltas, gboolean add_deps, - gboolean add_related); + gboolean add_related, + gboolean reinstall); void flatpak_transaction_free (FlatpakTransaction *self); gboolean flatpak_transaction_update_metadata (FlatpakTransaction *self, gboolean all_remotes, diff --git a/common/flatpak-dir.c b/common/flatpak-dir.c index 2ac25e6a..ee2315e4 100644 --- a/common/flatpak-dir.c +++ b/common/flatpak-dir.c @@ -5598,11 +5598,24 @@ flatpak_dir_deploy (FlatpakDir *self, return TRUE; } +/* -origin remotes are deleted when the last ref refering to it is undeployed */ +static void +maybe_prune_remote (FlatpakDir *self, + const char *remote) +{ + if (remote != NULL && + g_str_has_suffix (remote, "-origin") && + flatpak_dir_get_remote_noenumerate (self, remote) && + !flatpak_dir_remote_has_deploys (self, remote)) + ostree_repo_remote_delete (self->repo, remote, NULL, NULL); +} + gboolean flatpak_dir_deploy_install (FlatpakDir *self, const char *ref, const char *origin, const char **subpaths, + gboolean reinstall, GCancellable *cancellable, GError **error) { @@ -5613,6 +5626,7 @@ flatpak_dir_deploy_install (FlatpakDir *self, gboolean ret = FALSE; g_autoptr(GError) local_error = NULL; g_auto(GStrv) ref_parts = g_strsplit (ref, "/", -1); + g_autofree char *remove_ref_from_remote = NULL; if (!flatpak_dir_lock (self, &lock, cancellable, error)) @@ -5621,9 +5635,33 @@ flatpak_dir_deploy_install (FlatpakDir *self, old_deploy_dir = flatpak_dir_get_if_deployed (self, ref, NULL, cancellable); if (old_deploy_dir != NULL) { - g_set_error (error, FLATPAK_ERROR, FLATPAK_ERROR_ALREADY_INSTALLED, - _("%s branch %s already installed"), ref_parts[1], ref_parts[3]); - goto out; + if (reinstall) + { + g_autofree char *old_active = flatpak_dir_read_active (self, ref, cancellable); + g_autoptr(GVariant) old_deploy = NULL; + const char *old_origin; + + old_deploy = flatpak_load_deploy_data (old_deploy_dir, cancellable, error); + if (old_deploy == NULL) + goto out; + + /* If the old install was from a different remote, remove the ref */ + old_origin = flatpak_deploy_data_get_origin (old_deploy); + if (strcmp (old_origin, origin) != 0) + remove_ref_from_remote = g_strdup (old_origin); + + g_debug ("Removing old deployment for reinstall"); + if (!flatpak_dir_undeploy (self, ref, old_active, + TRUE, FALSE, + cancellable, error)) + goto out; + } + else + { + g_set_error (error, FLATPAK_ERROR, FLATPAK_ERROR_ALREADY_INSTALLED, + _("%s branch %s already installed"), ref_parts[1], ref_parts[3]); + goto out; + } } deploy_base = flatpak_dir_get_deploy_dir (self, ref); @@ -5652,6 +5690,15 @@ flatpak_dir_deploy_install (FlatpakDir *self, goto out; } + /* Remove old ref if the reinstalled was from a different remote */ + if (remove_ref_from_remote != NULL) + { + if (!flatpak_dir_remove_ref (self, remove_ref_from_remote, ref, cancellable, error)) + goto out; + + maybe_prune_remote (self, remove_ref_from_remote); + } + /* Release lock before doing possibly slow prune */ glnx_release_lock_file (&lock); @@ -5898,6 +5945,7 @@ flatpak_dir_install (FlatpakDir *self, gboolean no_pull, gboolean no_deploy, gboolean no_static_deltas, + gboolean reinstall, const char *ref, const char *remote_name, const char **opt_subpaths, @@ -6060,6 +6108,9 @@ flatpak_dir_install (FlatpakDir *self, if (no_deploy) helper_flags |= FLATPAK_HELPER_DEPLOY_FLAGS_NO_DEPLOY; + if (reinstall) + helper_flags |= FLATPAK_HELPER_DEPLOY_FLAGS_REINSTALL; + g_debug ("Calling system helper: Deploy"); if (!flatpak_system_helper_call_deploy_sync (system_helper, child_repo_path ? child_repo_path : "", @@ -6089,7 +6140,7 @@ flatpak_dir_install (FlatpakDir *self, if (!no_deploy) { if (!flatpak_dir_deploy_install (self, ref, remote_name, opt_subpaths, - cancellable, error)) + reinstall, cancellable, error)) return FALSE; } @@ -6312,7 +6363,7 @@ flatpak_dir_install_bundle (FlatpakDir *self, } else { - if (!flatpak_dir_deploy_install (self, ref, remote, NULL, cancellable, error)) + if (!flatpak_dir_deploy_install (self, ref, remote, NULL, FALSE, cancellable, error)) return FALSE; } @@ -6800,11 +6851,7 @@ flatpak_dir_uninstall (FlatpakDir *self, glnx_release_lock_file (&lock); - if (repository != NULL && - g_str_has_suffix (repository, "-origin") && - flatpak_dir_get_remote_noenumerate (self, repository) && - !flatpak_dir_remote_has_deploys (self, repository)) - ostree_repo_remote_delete (self->repo, repository, NULL, NULL); + maybe_prune_remote (self, repository); if (!keep_ref) flatpak_dir_prune (self, cancellable, NULL); diff --git a/common/flatpak-dir.h b/common/flatpak-dir.h index 8e3f3244..19dc0ad8 100644 --- a/common/flatpak-dir.h +++ b/common/flatpak-dir.h @@ -88,9 +88,10 @@ typedef enum { FLATPAK_HELPER_DEPLOY_FLAGS_UPDATE = 1 << 0, FLATPAK_HELPER_DEPLOY_FLAGS_NO_DEPLOY = 1 << 1, FLATPAK_HELPER_DEPLOY_FLAGS_LOCAL_PULL = 1 << 2, + FLATPAK_HELPER_DEPLOY_FLAGS_REINSTALL = 1 << 3, } FlatpakHelperDeployFlags; -#define FLATPAK_HELPER_DEPLOY_FLAGS_ALL (FLATPAK_HELPER_DEPLOY_FLAGS_UPDATE|FLATPAK_HELPER_DEPLOY_FLAGS_NO_DEPLOY|FLATPAK_HELPER_DEPLOY_FLAGS_LOCAL_PULL) +#define FLATPAK_HELPER_DEPLOY_FLAGS_ALL (FLATPAK_HELPER_DEPLOY_FLAGS_UPDATE|FLATPAK_HELPER_DEPLOY_FLAGS_NO_DEPLOY|FLATPAK_HELPER_DEPLOY_FLAGS_LOCAL_PULL|FLATPAK_HELPER_DEPLOY_FLAGS_REINSTALL) typedef enum { FLATPAK_HELPER_UNINSTALL_FLAGS_NONE = 0, @@ -436,12 +437,14 @@ gboolean flatpak_dir_deploy_install (FlatpakDir *self, const char *ref, const char *origin, const char **subpaths, + gboolean reinstall, GCancellable *cancellable, GError **error); gboolean flatpak_dir_install (FlatpakDir *self, gboolean no_pull, gboolean no_deploy, gboolean no_static_deltas, + gboolean reinstall, const char *ref, const char *remote_name, const char **subpaths, diff --git a/lib/flatpak-installation.c b/lib/flatpak-installation.c index f7de69ee..d12f116f 100644 --- a/lib/flatpak-installation.c +++ b/lib/flatpak-installation.c @@ -1434,6 +1434,7 @@ flatpak_installation_install_full (FlatpakInstallation *self, (flags & FLATPAK_INSTALL_FLAGS_NO_PULL) != 0, (flags & FLATPAK_INSTALL_FLAGS_NO_DEPLOY) != 0, (flags & FLATPAK_INSTALL_FLAGS_NO_STATIC_DELTAS) != 0, + FALSE, ref, remote_name, (const char **)subpaths, ostree_progress, cancellable, error)) goto out; diff --git a/system-helper/flatpak-system-helper.c b/system-helper/flatpak-system-helper.c index 6c26fd1e..dc069b91 100644 --- a/system-helper/flatpak-system-helper.c +++ b/system-helper/flatpak-system-helper.c @@ -159,6 +159,7 @@ handle_deploy (FlatpakSystemHelper *object, gboolean is_oci; gboolean no_deploy; gboolean local_pull; + gboolean reinstall; g_autoptr(GMainContext) main_context = NULL; g_autofree char *url = NULL; @@ -187,6 +188,7 @@ handle_deploy (FlatpakSystemHelper *object, is_update = (arg_flags & FLATPAK_HELPER_DEPLOY_FLAGS_UPDATE) != 0; no_deploy = (arg_flags & FLATPAK_HELPER_DEPLOY_FLAGS_NO_DEPLOY) != 0; local_pull = (arg_flags & FLATPAK_HELPER_DEPLOY_FLAGS_LOCAL_PULL) != 0; + reinstall = (arg_flags & FLATPAK_HELPER_DEPLOY_FLAGS_REINSTALL) != 0; deploy_dir = flatpak_dir_get_if_deployed (system, arg_ref, NULL, NULL); @@ -195,18 +197,23 @@ handle_deploy (FlatpakSystemHelper *object, g_autofree char *real_origin = NULL; if (!is_update) { - /* Can't install already installed app */ - g_dbus_method_invocation_return_error (invocation, FLATPAK_ERROR, FLATPAK_ERROR_ALREADY_INSTALLED, - "%s is already installed", arg_ref); - return TRUE; + if (!reinstall) + { + /* Can't install already installed app */ + g_dbus_method_invocation_return_error (invocation, FLATPAK_ERROR, FLATPAK_ERROR_ALREADY_INSTALLED, + "%s is already installed", arg_ref); + return TRUE; + } } - - real_origin = flatpak_dir_get_origin (system, arg_ref, NULL, NULL); - if (g_strcmp0 (real_origin, arg_origin) != 0) + else { - g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, - "Wrong origin %s for update", arg_origin); - return TRUE; + real_origin = flatpak_dir_get_origin (system, arg_ref, NULL, NULL); + if (g_strcmp0 (real_origin, arg_origin) != 0) + { + g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, + "Wrong origin %s for update", arg_origin); + return TRUE; + } } } else if (!deploy_dir && is_update) @@ -389,6 +396,7 @@ handle_deploy (FlatpakSystemHelper *object, { if (!flatpak_dir_deploy_install (system, arg_ref, arg_origin, (const char **) arg_subpaths, + reinstall, NULL, &error)) { g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_FAILED, diff --git a/tests/test-repo.sh b/tests/test-repo.sh index f588ce5b..2628c87b 100755 --- a/tests/test-repo.sh +++ b/tests/test-repo.sh @@ -28,7 +28,7 @@ if [ x${USE_COLLECTIONS_IN_CLIENT-} == xyes ] || [ x${USE_COLLECTIONS_IN_SERVER- skip_without_p2p fi -echo "1..10" +echo "1..12" #Regular repo setup_repo @@ -76,6 +76,14 @@ fi install_repo test-gpg2 echo "ok with alternative gpg key" +if ${FLATPAK} ${U} install test-repo org.test.Platform 2> install-error-log; then + assert_not_reached "Should not be able to install again from different remote without reinstall" +fi +echo "ok failed to install again from different remote" + +${FLATPAK} ${U} install --reinstall test-repo org.test.Platform +echo "ok re-install" + ${FLATPAK} ${U} uninstall org.test.Platform org.test.Hello if ${FLATPAK} ${U} install test-missing-gpg-repo org.test.Platform 2> install-error-log; then