flatpak_dir_update (FlatpakDir                           *self,
                    gboolean                              no_pull,
                    gboolean                              no_deploy,
                    gboolean                              no_static_deltas,
                    gboolean                              allow_downgrade,
                    gboolean                              app_hint,
                    gboolean                              install_hint,
                    FlatpakRemoteState                   *state,
                    FlatpakDecomposed                    *ref,
                    const char                           *commit,
                    const char                          **opt_subpaths,
                    const char                          **opt_previous_ids,
                    GFile                                *sideload_repo,
                    GBytes                               *require_metadata,
                    const char                           *token,
                    FlatpakProgress                      *progress,
                    GCancellable                         *cancellable,
                    GError                              **error)
{
  g_autoptr(GBytes) deploy_data = NULL;
  const char **subpaths = NULL;
  const char *empty_subpaths[] = {NULL};
  g_autofree char *url = NULL;
  FlatpakPullFlags flatpak_flags;
  g_autofree const char **old_subpaths = NULL;
  gboolean is_oci;

  /* This is calculated in check_for_update */
  g_assert (commit != NULL);

  flatpak_flags = FLATPAK_PULL_FLAGS_DOWNLOAD_EXTRA_DATA;
  if (allow_downgrade)
    flatpak_flags |= FLATPAK_PULL_FLAGS_ALLOW_DOWNGRADE;
  if (no_static_deltas)
    flatpak_flags |= FLATPAK_PULL_FLAGS_NO_STATIC_DELTAS;

  deploy_data = flatpak_dir_get_deploy_data (self, ref, FLATPAK_DEPLOY_VERSION_ANY,
                                             cancellable, NULL);

  if (deploy_data != NULL)
    old_subpaths = flatpak_deploy_data_get_subpaths (deploy_data);

  if (opt_subpaths)
    subpaths = opt_subpaths;
  else if (old_subpaths)
    subpaths = old_subpaths;
  else
    subpaths = empty_subpaths;

  if (!ostree_repo_remote_get_url (self->repo, state->remote_name, &url, error))
    return FALSE;

  if (*url == 0)
    return TRUE; /* Empty URL => disabled */

  is_oci = flatpak_dir_get_remote_oci (self, state->remote_name);

  if (flatpak_dir_use_system_helper (self, NULL))
    {
      const char *installation = flatpak_dir_get_id (self);
      g_autoptr(OstreeRepo) child_repo = NULL;
      g_auto(GLnxLockFile) child_repo_lock = { 0, };
      g_autofree char *child_repo_path = NULL;
      FlatpakHelperDeployFlags helper_flags = 0;
      gboolean gpg_verify_summary;
      gboolean gpg_verify;
      gboolean is_revokefs_pull = FALSE;

      if (allow_downgrade)
        return flatpak_fail_error (error, FLATPAK_ERROR_DOWNGRADE,
                                   _("Can't update to a specific commit without root permissions"));

      helper_flags = FLATPAK_HELPER_DEPLOY_FLAGS_UPDATE;

      if (!ostree_repo_remote_get_gpg_verify_summary (self->repo, state->remote_name,
                                                      &gpg_verify_summary, error))
        return FALSE;

      if (!ostree_repo_remote_get_gpg_verify (self->repo, state->remote_name,
                                              &gpg_verify, error))
        return FALSE;

      if (no_pull)
        {
          /* Nothing to do here */
        }
      else if (is_oci)
        {
          g_autoptr(FlatpakOciRegistry) registry = NULL;
          g_autoptr(GFile) registry_file = NULL;

          registry = flatpak_dir_create_system_child_oci_registry (self, &child_repo_lock, token, error);
          if (registry == NULL)
            return FALSE;

          registry_file = g_file_new_for_uri (flatpak_oci_registry_get_uri (registry));

          child_repo_path = g_file_get_path (registry_file);

          if (!flatpak_dir_mirror_oci (self, registry, state, flatpak_decomposed_get_ref (ref),
                                       commit, NULL, token, progress, cancellable, error))
            return FALSE;
        }
      else if (!gpg_verify_summary || !gpg_verify)
        {
          /* The remote is not gpg verified, so we don't want to allow installation via
             a download in the home directory, as there is no way to verify you're not
             injecting anything into the remote. However, in the case of a remote
             configured to a local filesystem we can just let the system helper do
             the installation, as it can then avoid network i/o and be certain the
             data comes from the right place.

             If @collection_id is non-%NULL, we can verify the refs in commit
             metadata, so don’t need to verify the summary. */
          if (g_str_has_prefix (url, "file:"))
            helper_flags |= FLATPAK_HELPER_DEPLOY_FLAGS_LOCAL_PULL;
          else
            return flatpak_fail_error (error, FLATPAK_ERROR_UNTRUSTED, _("Can't pull from untrusted non-gpg verified remote"));
        }
      else
        {
          /* First try to update using revokefs-fuse codepath. If it fails, try to update using a
           * temporary child-repo. Read flatpak_dir_install for more details on using revokefs-fuse */
          g_autofree gchar *src_dir = NULL;
          g_autofree gchar *mnt_dir = NULL;
          g_autoptr(GError) local_error = NULL;

          if (!flatpak_dir_setup_revokefs_fuse_mount (self,
                                                      ref,
                                                      installation,
                                                      &src_dir, &mnt_dir,
                                                      cancellable))
            {
              flatpak_dir_unmount_and_cancel_pull (self, FLATPAK_HELPER_CANCEL_PULL_FLAGS_NONE,
                                                   cancellable,
                                                   &child_repo, &child_repo_lock,
                                                   mnt_dir, src_dir);
            }
          else
            {
              g_autofree gchar *repo_basename = NULL;
              g_autoptr(GFile) mnt_dir_file = NULL;

              mnt_dir_file = g_file_new_for_path (mnt_dir);
              child_repo = flatpak_dir_create_child_repo (self, mnt_dir_file, &child_repo_lock, commit, &local_error);
              if (child_repo == NULL)
                {
                  g_warning ("Cannot create repo on revokefs mountpoint %s: %s", mnt_dir, local_error->message);
                  flatpak_dir_unmount_and_cancel_pull (self,
                                                       FLATPAK_HELPER_CANCEL_PULL_FLAGS_NONE,
                                                       cancellable,
                                                       &child_repo, &child_repo_lock,
                                                       mnt_dir, src_dir);
                  g_clear_error (&local_error);
                }
              else
                {
                  repo_basename = g_file_get_basename (ostree_repo_get_path (child_repo));
                  child_repo_path = g_build_filename (src_dir, repo_basename, NULL);
                  is_revokefs_pull = TRUE;
                }
            }

          /* Fallback if revokefs-fuse setup does not succeed. This makes the pull
           * temporarily use double disk-space. */
          if (!is_revokefs_pull)
            {
              /* We're pulling from a remote source, we do the network mirroring pull as a
                 user and hand back the resulting data to the system-helper, that trusts us
                 due to the GPG signatures in the repo */

              child_repo = flatpak_dir_create_system_child_repo (self, &child_repo_lock, commit, error);
              if (child_repo == NULL)
                return FALSE;
              else
                child_repo_path = g_file_get_path (ostree_repo_get_path (child_repo));
            }

          flatpak_flags |= FLATPAK_PULL_FLAGS_SIDELOAD_EXTRA_DATA;
          if (!flatpak_dir_pull (self, state, flatpak_decomposed_get_ref (ref),
                                 commit, subpaths, sideload_repo, require_metadata, token,
                                 child_repo,
                                 flatpak_flags, 0,
                                 progress, cancellable, error))
            {
              if (is_revokefs_pull)
                {
                  flatpak_dir_unmount_and_cancel_pull (self,
                                                       FLATPAK_HELPER_CANCEL_PULL_FLAGS_PRESERVE_PULL,
                                                       cancellable,
                                                       &child_repo, &child_repo_lock,
                                                       mnt_dir, src_dir);
                }

              return FALSE;
            }

          g_assert (child_repo_path != NULL);

          if (is_revokefs_pull &&
              !flatpak_dir_revokefs_fuse_unmount (&child_repo, &child_repo_lock, mnt_dir, &local_error))
            {
              g_warning ("Could not unmount revokefs-fuse filesystem at %s: %s", mnt_dir, local_error->message);
              flatpak_dir_unmount_and_cancel_pull (self,
                                                   FLATPAK_HELPER_CANCEL_PULL_FLAGS_PRESERVE_PULL,
                                                   cancellable,
                                                   &child_repo, &child_repo_lock,
                                                   mnt_dir, src_dir);
              return FALSE;
            }
        }

      if (no_deploy)
        helper_flags |= FLATPAK_HELPER_DEPLOY_FLAGS_NO_DEPLOY;

      if (app_hint)
        helper_flags |= FLATPAK_HELPER_DEPLOY_FLAGS_APP_HINT;

      if (install_hint)
        helper_flags |= FLATPAK_HELPER_DEPLOY_FLAGS_INSTALL_HINT;

      if (!flatpak_dir_system_helper_call_deploy (self,
                                                  child_repo_path ? child_repo_path : "",
                                                  helper_flags, flatpak_decomposed_get_ref (ref), state->remote_name,
                                                  subpaths, opt_previous_ids,
                                                  installation ? installation : "",
                                                  cancellable,
                                                  error))
        return FALSE;

      if (child_repo_path && !is_revokefs_pull)
        (void) glnx_shutil_rm_rf_at (AT_FDCWD, child_repo_path, NULL, NULL);

      return TRUE;
    }

  if (!no_pull)
    {
      if (!flatpak_dir_pull (self, state, flatpak_decomposed_get_ref (ref),
                             commit, subpaths, sideload_repo, require_metadata, token,
                             NULL, flatpak_flags, OSTREE_REPO_PULL_FLAGS_NONE,
                             progress, cancellable, error))
        return FALSE;

      /* Take this opportunity to clean up refs/mirrors/ since a prune will happen
       * after this update operation. See
       * https://github.com/flatpak/flatpak/issues/3222
       * Note: For the system-helper case we do this in handle_deploy()
       */
      if (!flatpak_dir_delete_mirror_refs (self, FALSE, cancellable, error))
        return FALSE;
    }

  if (!no_deploy)
    {
      if (!flatpak_dir_deploy_update (self, ref,
                                      /* We don't know the local commit id in the OCI case, and
                                         we only support one version anyway */
                                      is_oci ? NULL : commit,
                                      subpaths, opt_previous_ids,
                                      cancellable, error))
        return FALSE;
    }

  return TRUE;
}