From 927c7eb59dd689cd0fbeb882a2ea821190afb682 Mon Sep 17 00:00:00 2001 From: Hocuri Date: Thu, 27 Aug 2020 15:02:00 +0200 Subject: [PATCH] Fix cancelling imex (#1855) Fix deltachat/deltachat-android#1579 Also: Make sure that if an error happens, the UI can show the error to the user --- src/dc_tools.rs | 19 +++++++++++++++ src/imex.rs | 63 +++++++++++++++++++++++++++++++++---------------- 2 files changed, 62 insertions(+), 20 deletions(-) diff --git a/src/dc_tools.rs b/src/dc_tools.rs index 9e03da711..04ba79595 100644 --- a/src/dc_tools.rs +++ b/src/dc_tools.rs @@ -9,7 +9,9 @@ use std::str::FromStr; use std::time::{Duration, SystemTime}; use async_std::path::{Path, PathBuf}; +use async_std::prelude::*; use async_std::{fs, io}; + use chrono::{Local, TimeZone}; use rand::{thread_rng, Rng}; @@ -296,6 +298,23 @@ pub(crate) async fn dc_delete_file(context: &Context, path: impl AsRef) -> } } +pub async fn dc_delete_files_in_dir(context: &Context, path: impl AsRef) { + match async_std::fs::read_dir(path).await { + Ok(mut read_dir) => { + while let Some(entry) = read_dir.next().await { + match entry { + Ok(file) => { + dc_delete_file(context, file.file_name()).await; + } + Err(e) => warn!(context, "Could not read file to delete: {}", e), + } + } + } + + Err(e) => warn!(context, "Could not read dir to delete: {}", e), + } +} + pub(crate) async fn dc_copy_file( context: &Context, src_path: impl AsRef, diff --git a/src/imex.rs b/src/imex.rs index f448f3fe2..1b8ea2510 100644 --- a/src/imex.rs +++ b/src/imex.rs @@ -82,18 +82,46 @@ pub async fn imex( what: ImexMode, param1: Option>, ) -> Result<()> { - use futures::future::FutureExt; - let cancel = context.alloc_ongoing().await?; - let res = imex_inner(context, what, param1) - .race(cancel.recv().map(|_| Err(format_err!("canceled")))) - .await; + + let res = async { + let success = imex_inner(context, what, param1).await; + match success { + Ok(()) => { + info!(context, "IMEX successfully completed"); + context.emit_event(EventType::ImexProgress(1000)); + Ok(()) + } + Err(err) => { + cleanup_aborted_imex(context, what).await; + error!(context, "{}", err); + context.emit_event(EventType::ImexProgress(0)); + bail!("IMEX FAILED to complete: {}", err); + } + } + } + .race(async { + cancel.recv().await.ok(); + cleanup_aborted_imex(context, what).await; + Err(format_err!("canceled")) + }) + .await; context.free_ongoing().await; res } +async fn cleanup_aborted_imex(context: &Context, what: ImexMode) { + if what == ImexMode::ImportBackup { + dc_delete_file(context, context.get_dbfile()).await; + dc_delete_files_in_dir(context, context.get_blobdir()).await; + } + if what == ImexMode::ExportBackup || what == ImexMode::ImportBackup { + context.sql.open(context, context.get_dbfile(), false).await; + } +} + /// Returns the filename of the backup found (otherwise an error) pub async fn has_backup(context: &Context, dir_name: impl AsRef) -> Result { let dir_name = dir_name.as_ref(); @@ -425,7 +453,7 @@ async fn imex_inner( } } - let success = match what { + match what { ImexMode::ExportSelfKeys => export_self_keys(context, path).await, ImexMode::ImportSelfKeys => import_self_keys(context, path).await, @@ -434,18 +462,6 @@ async fn imex_inner( ImexMode::ExportBackup => export_backup_old(context, path).await, // import_backup() will call import_backup_old() if this is an old backup. ImexMode::ImportBackup => import_backup(context, path).await, - }; - - match success { - Ok(()) => { - info!(context, "IMEX successfully completed"); - context.emit_event(EventType::ImexProgress(1000)); - Ok(()) - } - Err(err) => { - context.emit_event(EventType::ImexProgress(0)); - bail!("IMEX FAILED to complete: {}", err); - } } } @@ -632,6 +648,7 @@ async fn export_backup(context: &Context, dir: impl AsRef) -> Result<()> { // get a fine backup file name (the name includes the date so that multiple backup instances are possible) let now = time(); let (temp_path, dest_path) = get_next_backup_path_new(dir, now).await?; + let _d = DeleteOnDrop(temp_path.clone()); context .sql @@ -670,13 +687,19 @@ async fn export_backup(context: &Context, dir: impl AsRef) -> Result<()> { } Err(e) => { error!(context, "backup failed: {}", e); - // Not using dc_delete_file() here because it would send a DeletedBlobFile event - fs::remove_file(temp_path).await?; } } res } +struct DeleteOnDrop(PathBuf); +impl Drop for DeleteOnDrop { + fn drop(&mut self) { + let file = self.0.clone(); + // Not using dc_delete_file() here because it would send a DeletedBlobFile event + async_std::task::block_on(async move { fs::remove_file(file).await.ok() }); + } +} async fn export_backup_inner(context: &Context, temp_path: &PathBuf) -> Result<()> { let file = File::create(temp_path).await?;