Explorar el Código

fix: extract an entire source archive into a specified path is not working if the archive includes dirs. (#2997)

Co-authored-by: Lucas Nogueira <lucas@tauri.studio>
Chaoqian Xu hace 3 años
padre
commit
156cdbc9f8
Se han modificado 2 ficheros con 28 adiciones y 4 borrados
  1. 23 3
      core/tauri/src/api/file/extract.rs
  2. 5 1
      core/tauri/src/async_runtime.rs

+ 23 - 3
core/tauri/src/api/file/extract.rs

@@ -119,9 +119,29 @@ impl<'a> Extract<'a> {
         let mut archive = zip::ZipArchive::new(source)?;
         for i in 0..archive.len() {
           let mut file = archive.by_index(i)?;
-          let path = into_dir.join(file.name());
-          let mut output = fs::File::create(path)?;
-          io::copy(&mut file, &mut output)?;
+          // Decode the file name from raw bytes instead of using file.name() directly.
+          // file.name() uses String::from_utf8_lossy() which may return messy characters
+          // such as: 爱交易.app/, that does not work as expected.
+          // Here we require the file name must be a valid UTF-8.
+          let file_name = String::from_utf8(file.name_raw().to_vec())?;
+          let out_path = into_dir.join(&file_name);
+          if file.is_dir() {
+            fs::create_dir_all(&out_path)?;
+          } else {
+            if let Some(out_path_parent) = out_path.parent() {
+              fs::create_dir_all(&out_path_parent)?;
+            }
+            let mut out_file = fs::File::create(&out_path)?;
+            io::copy(&mut file, &mut out_file)?;
+          }
+          // Get and Set permissions
+          #[cfg(unix)]
+          {
+            use std::os::unix::fs::PermissionsExt;
+            if let Some(mode) = file.unix_mode() {
+              fs::set_permissions(&out_path, fs::Permissions::from_mode(mode))?;
+            }
+          }
         }
       }
     };

+ 5 - 1
core/tauri/src/async_runtime.rs

@@ -326,7 +326,11 @@ mod tests {
   #[tokio::test]
   async fn handle_abort() {
     let handle = handle();
-    let join = handle.spawn(async { 5 });
+    let join = handle.spawn(async {
+      // Here we sleep 1 second to ensure this task to be uncompleted when abort() invoked.
+      tokio::time::sleep(tokio::time::Duration::from_secs(1)).await;
+      5
+    });
     join.abort();
     if let crate::Error::JoinError(raw_box) = join.await.unwrap_err() {
       let raw_error = raw_box.downcast::<tokio::task::JoinError>().unwrap();