1- use std:: path:: Path ;
1+ use std:: path:: { Path , PathBuf } ;
22use std:: pin:: Pin ;
33
4- use crate :: Error ;
54use futures:: StreamExt ;
65use rustc_hash:: FxHashSet ;
76use tokio_util:: compat:: { FuturesAsyncReadCompatExt , TokioAsyncReadCompatExt } ;
87use tracing:: warn;
8+
99use uv_distribution_filename:: SourceDistExtension ;
1010
11+ use crate :: Error ;
12+
1113const DEFAULT_BUF_SIZE : usize = 128 * 1024 ;
1214
1315/// Unpack a `.zip` archive into the target directory, without requiring `Seek`.
@@ -19,6 +21,24 @@ pub async fn unzip<R: tokio::io::AsyncRead + Unpin>(
1921 reader : R ,
2022 target : impl AsRef < Path > ,
2123) -> Result < ( ) , Error > {
24+ /// Sanitize a filename for use on Windows.
25+ fn sanitize ( filename : & str ) -> PathBuf {
26+ filename
27+ . replace ( '\\' , "/" )
28+ . split ( '/' )
29+ . map ( |segment| {
30+ sanitize_filename:: sanitize_with_options (
31+ segment,
32+ sanitize_filename:: Options {
33+ windows : cfg ! ( windows) ,
34+ truncate : false ,
35+ replacement : "" ,
36+ } ,
37+ )
38+ } )
39+ . collect ( )
40+ }
41+
2242 let target = target. as_ref ( ) ;
2343 let mut reader = futures:: io:: BufReader :: with_capacity ( DEFAULT_BUF_SIZE , reader. compat ( ) ) ;
2444 let mut zip = async_zip:: base:: read:: stream:: ZipFileReader :: new ( & mut reader) ;
@@ -28,7 +48,7 @@ pub async fn unzip<R: tokio::io::AsyncRead + Unpin>(
2848 while let Some ( mut entry) = zip. next_with_entry ( ) . await ? {
2949 // Construct the (expected) path to the file on-disk.
3050 let path = entry. reader ( ) . entry ( ) . filename ( ) . as_str ( ) ?;
31- let path = target. join ( path) ;
51+ let path = target. join ( sanitize ( path) ) ;
3252 let is_dir = entry. reader ( ) . entry ( ) . dir ( ) ?;
3353
3454 // Either create the directory or write the file to disk.
@@ -84,7 +104,7 @@ pub async fn unzip<R: tokio::io::AsyncRead + Unpin>(
84104 if has_any_executable_bit != 0 {
85105 // Construct the (expected) path to the file on-disk.
86106 let path = entry. filename ( ) . as_str ( ) ?;
87- let path = target. join ( path) ;
107+ let path = target. join ( sanitize ( path) ) ;
88108
89109 let permissions = fs_err:: tokio:: metadata ( & path) . await ?. permissions ( ) ;
90110 if permissions. mode ( ) & 0o111 != 0o111 {
0 commit comments