Skip to content

Commit d8acbd2

Browse files
joshuamegnauth54jackpot51
authored andcommitted
Fix extracting password protected archives
Closes: #1157 The fix splits the "canceled" and "failed" states for OperationError. It also preserves that state because some functions overwrote the state by rewrapping the error.
1 parent cf2e2fa commit d8acbd2

7 files changed

Lines changed: 343 additions & 191 deletions

File tree

i18n/en/cosmic_files.ftl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,7 @@ no-history = No items in history.
185185
pending = Pending
186186
progress = {$percent}%
187187
progress-cancelled = {$percent}%, cancelled
188+
progress-failed = {$percent}%, failed
188189
progress-paused = {$percent}%, paused
189190
failed = Failed
190191
complete = Complete

src/archive.rs

Lines changed: 39 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -47,53 +47,58 @@ pub fn extract(
4747
controller: &Controller,
4848
) -> Result<(), OperationError> {
4949
let mime = mime_for_path(path, None, false);
50-
let controller = controller.clone();
5150
let password = password.clone();
5251
match mime.essence_str() {
53-
"application/gzip" | "application/x-compressed-tar" => OpReader::new(path, controller)
54-
.map(io::BufReader::new)
55-
.map(flate2::read::GzDecoder::new)
56-
.map(tar::Archive::new)
57-
.and_then(|mut archive| archive.unpack(&new_dir))
58-
.map_err(OperationError::from_str)?,
59-
"application/x-tar" => OpReader::new(path, controller)
52+
"application/gzip" | "application/x-compressed-tar" => {
53+
OpReader::new(path, controller.clone())
54+
.map(io::BufReader::new)
55+
.map(flate2::read::GzDecoder::new)
56+
.map(tar::Archive::new)
57+
.and_then(|mut archive| archive.unpack(new_dir))
58+
.map_err(|e| OperationError::from_err(e, controller))?
59+
}
60+
"application/x-tar" => OpReader::new(path, controller.clone())
6061
.map(io::BufReader::new)
6162
.map(tar::Archive::new)
62-
.and_then(|mut archive| archive.unpack(&new_dir))
63-
.map_err(OperationError::from_str)?,
63+
.and_then(|mut archive| archive.unpack(new_dir))
64+
.map_err(|e| OperationError::from_err(e, controller))?,
6465
"application/zip" => fs::File::open(path)
6566
.map(io::BufReader::new)
6667
.map(zip::ZipArchive::new)
67-
.map_err(OperationError::from_str)?
68-
.and_then(move |mut archive| zip_extract(&mut archive, &new_dir, password, controller))
68+
.map_err(|e| OperationError::from_err(e, controller))?
69+
.and_then(move |mut archive| {
70+
zip_extract(&mut archive, new_dir, password, controller.clone())
71+
})
6972
.map_err(|e| match e {
7073
ZipError::UnsupportedArchive(ZipError::PASSWORD_REQUIRED)
71-
| ZipError::InvalidPassword => OperationError {
72-
kind: OperationErrorType::PasswordRequired,
73-
},
74-
_ => OperationError::from_str(e),
74+
| ZipError::InvalidPassword => {
75+
OperationError::from_kind(OperationErrorType::PasswordRequired, controller)
76+
}
77+
_ => OperationError::from_err(e, controller),
7578
})?,
7679
#[cfg(feature = "bzip2")]
7780
"application/x-bzip"
7881
| "application/x-bzip-compressed-tar"
7982
| "application/x-bzip2"
80-
| "application/x-bzip2-compressed-tar" => OpReader::new(path, controller)
83+
| "application/x-bzip2-compressed-tar" => OpReader::new(path, controller.clone())
8184
.map(io::BufReader::new)
8285
.map(bzip2::read::BzDecoder::new)
8386
.map(tar::Archive::new)
84-
.and_then(|mut archive| archive.unpack(&new_dir))
85-
.map_err(OperationError::from_str)?,
87+
.and_then(|mut archive| archive.unpack(new_dir))
88+
.map_err(|e| OperationError::from_err(e, controller))?,
8689
#[cfg(feature = "xz2")]
87-
"application/x-xz" | "application/x-xz-compressed-tar" => OpReader::new(path, controller)
88-
.map(io::BufReader::new)
89-
.map(xz2::read::XzDecoder::new)
90-
.map(tar::Archive::new)
91-
.and_then(|mut archive| archive.unpack(&new_dir))
92-
.map_err(OperationError::from_str)?,
93-
_ => Err(OperationError::from_str(format!(
94-
"unsupported mime type {:?}",
95-
mime
96-
)))?,
90+
"application/x-xz" | "application/x-xz-compressed-tar" => {
91+
OpReader::new(path, controller.clone())
92+
.map(io::BufReader::new)
93+
.map(xz2::read::XzDecoder::new)
94+
.map(tar::Archive::new)
95+
.and_then(|mut archive| archive.unpack(new_dir))
96+
.map_err(|e| OperationError::from_err(e, controller))?
97+
}
98+
_ => Err(OperationError::from_err(
99+
format!("unsupported mime type {:?}", mime),
100+
controller,
101+
))?,
97102
}
98103
Ok(())
99104
}
@@ -135,19 +140,18 @@ fn zip_extract<R: io::Read + io::Seek, P: AsRef<Path>>(
135140
controller
136141
.check()
137142
.await
138-
.map_err(|err| io::Error::new(io::ErrorKind::Other, err))
143+
.map_err(|s| io::Error::other(OperationError::from_state(s, &controller)))
139144
})?;
140145

141146
controller.set_progress((i as f32) / total_files as f32);
142147

143148
let mut file = match &password {
144149
None => archive.by_index(i),
145150
Some(pwd) => archive.by_index_decrypt(i, pwd.as_bytes()),
146-
}
147-
.map_err(|e| e)?;
151+
}?;
148152
let filepath = file
149153
.enclosed_name()
150-
.ok_or(ZipError::InvalidArchive("Invalid file path".into()))?;
154+
.ok_or(ZipError::InvalidArchive("Invalid file path"))?;
151155

152156
let outpath = directory.as_ref().join(filepath);
153157

@@ -206,8 +210,7 @@ fn zip_extract<R: io::Read + io::Seek, P: AsRef<Path>>(
206210
let mut file = match &password {
207211
None => archive.by_index(i),
208212
Some(pwd) => archive.by_index_decrypt(i, pwd.as_bytes()),
209-
}
210-
.map_err(|e| e)?;
213+
}?;
211214

212215
// create all pending dirs
213216
while let Some(pending_dir) = pending_directory_creates.pop_front() {
@@ -226,7 +229,7 @@ fn zip_extract<R: io::Read + io::Seek, P: AsRef<Path>>(
226229
controller
227230
.check()
228231
.await
229-
.map_err(|err| io::Error::new(io::ErrorKind::Other, err))
232+
.map_err(|s| io::Error::other(OperationError::from_state(s, &controller)))
230233
})?;
231234

232235
let count = file.read(&mut buffer)?;

src/operation/controller.rs

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
1-
use crate::fl;
2-
31
use std::sync::{Arc, Mutex};
42
use tokio::sync::Notify;
53

6-
#[derive(Clone, Copy, Debug)]
4+
#[derive(Clone, Copy, Debug, PartialEq)]
75
pub enum ControllerState {
86
Cancelled,
7+
Failed,
98
Paused,
109
Running,
1110
}
@@ -37,10 +36,11 @@ impl Default for Controller {
3736
}
3837

3938
impl Controller {
40-
pub async fn check(&self) -> Result<(), String> {
39+
pub async fn check(&self) -> Result<(), ControllerState> {
4140
loop {
4241
match self.state() {
43-
ControllerState::Cancelled => return Err(fl!("cancelled")),
42+
ControllerState::Cancelled => return Err(ControllerState::Cancelled),
43+
ControllerState::Failed => return Err(ControllerState::Failed),
4444
ControllerState::Paused => (),
4545
ControllerState::Running => return Ok(()),
4646
}
@@ -74,6 +74,10 @@ impl Controller {
7474
self.set_state(ControllerState::Cancelled);
7575
}
7676

77+
pub fn is_failed(&self) -> bool {
78+
matches!(self.state(), ControllerState::Failed)
79+
}
80+
7781
pub fn is_paused(&self) -> bool {
7882
matches!(self.state(), ControllerState::Paused)
7983
}
@@ -83,7 +87,7 @@ impl Controller {
8387
}
8488

8589
pub fn unpause(&self) {
86-
if !self.is_cancelled() {
90+
if !self.is_cancelled() | !self.is_failed() {
8791
self.set_state(ControllerState::Running);
8892
}
8993
}
@@ -100,8 +104,8 @@ impl Clone for Controller {
100104

101105
impl Drop for Controller {
102106
fn drop(&mut self) {
103-
// Cancel operations if primary controller is dropped
104-
if self.primary {
107+
// Cancel operations if primary controller is dropped and controller is still running
108+
if self.primary && self.state() != ControllerState::Failed {
105109
self.cancel();
106110
}
107111
}

0 commit comments

Comments
 (0)