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
This commit is contained in:
Hocuri
2020-08-27 15:02:00 +02:00
committed by GitHub
parent 6eef4066db
commit 927c7eb59d
2 changed files with 62 additions and 20 deletions

View File

@@ -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<Path>) ->
}
}
pub async fn dc_delete_files_in_dir(context: &Context, path: impl AsRef<Path>) {
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<Path>,

View File

@@ -82,18 +82,46 @@ pub async fn imex(
what: ImexMode,
param1: Option<impl AsRef<Path>>,
) -> 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<Path>) -> Result<String> {
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<Path>) -> 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<Path>) -> 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?;