@@ -27,6 +27,8 @@ use apple_flat_package::PkgReader;
2727use bzip2:: read:: BzDecoder ;
2828use directories:: BaseDirs ;
2929use flate2:: read:: GzDecoder ;
30+ use fs2:: FileExt ;
31+ use fs_extra:: dir:: { move_dir, CopyOptions } ;
3032use regex:: Regex ;
3133use std:: fs;
3234use std:: fs:: File ;
@@ -53,6 +55,7 @@ const MSI: &str = "msi";
5355const XZ : & str = "xz" ;
5456const SEVEN_ZIP_HEADER : & [ u8 ; 6 ] = b"7z\xBC \xAF \x27 \x1C " ;
5557const UNCOMPRESS_MACOS_ERR_MSG : & str = "{} files are only supported in macOS" ;
58+ const LOCK_FILE : & str = "sm.lock" ;
5659
5760#[ derive( Hash , Eq , PartialEq , Debug ) ]
5861pub struct BrowserPath {
@@ -69,6 +72,35 @@ impl BrowserPath {
6972 }
7073}
7174
75+ pub struct Lock {
76+ file : File ,
77+ path : PathBuf ,
78+ }
79+
80+ impl Lock {
81+ fn acquire ( log : & Logger , target : & Path , single_file : Option < String > ) -> Result < Self , Error > {
82+ let lock_folder = if single_file. is_some ( ) {
83+ create_parent_path_if_not_exists ( target) ?;
84+ target. parent ( ) . unwrap ( )
85+ } else {
86+ create_path_if_not_exists ( target) ?;
87+ target
88+ } ;
89+ let path = lock_folder. join ( LOCK_FILE ) ;
90+ let file = File :: create ( & path) ?;
91+
92+ log. trace ( format ! ( "Using lock file at {}" , path. display( ) ) ) ;
93+ file. lock_exclusive ( ) . unwrap_or_default ( ) ;
94+
95+ Ok ( Self { file, path } )
96+ }
97+
98+ fn release ( & mut self ) {
99+ self . file . unlock ( ) . unwrap_or_default ( ) ;
100+ fs:: remove_file ( & self . path ) . unwrap_or_default ( ) ;
101+ }
102+ }
103+
72104pub fn create_parent_path_if_not_exists ( path : & Path ) -> Result < ( ) , Error > {
73105 if let Some ( p) = path. parent ( ) {
74106 create_path_if_not_exists ( p) ?;
@@ -120,6 +152,9 @@ pub fn uncompress(
120152 extension
121153 ) ) ;
122154
155+ // Acquire file lock to prevent race conditions accessing the cache folder by concurrent SM processes
156+ let mut lock = Lock :: acquire ( log, target, single_file. clone ( ) ) ?;
157+
123158 if extension. eq_ignore_ascii_case ( ZIP ) {
124159 unzip ( compressed_file, target, log, single_file) ?
125160 } else if extension. eq_ignore_ascii_case ( GZ ) {
@@ -143,7 +178,7 @@ pub fn uncompress(
143178 } else if extension. eq_ignore_ascii_case ( EXE ) {
144179 uncompress_sfx ( compressed_file, target, log) ?
145180 } else if extension. eq_ignore_ascii_case ( DEB ) {
146- uncompress_deb ( compressed_file, target, log, os , volume. unwrap_or_default ( ) ) ?
181+ uncompress_deb ( compressed_file, target, log, volume. unwrap_or_default ( ) ) ?
147182 } else if extension. eq_ignore_ascii_case ( MSI ) {
148183 install_msi ( compressed_file, log, os) ?
149184 } else if extension. eq_ignore_ascii_case ( XML ) || extension. eq_ignore_ascii_case ( HTML ) {
@@ -158,6 +193,8 @@ pub fn uncompress(
158193 extension
159194 ) ) ) ;
160195 }
196+
197+ lock. release ( ) ;
161198 Ok ( ( ) )
162199}
163200
@@ -176,15 +213,23 @@ pub fn uncompress_sfx(compressed_file: &str, target: &Path, log: &Logger) -> Res
176213 sevenz_rust:: decompress ( file_reader, zip_parent) . unwrap ( ) ;
177214
178215 let zip_parent_str = path_to_string ( zip_parent) ;
179- let target_str = path_to_string ( target) ;
180216 let core_str = format ! ( r"{}\core" , zip_parent_str) ;
217+ move_folder_content ( & core_str, & target, & log) ?;
218+
219+ Ok ( ( ) )
220+ }
221+
222+ pub fn move_folder_content ( source : & str , target : & Path , log : & Logger ) -> Result < ( ) , Error > {
181223 log. trace ( format ! (
182- "Moving extracted files and folders from {} to {}" ,
183- core_str, target_str
224+ "Moving files and folders from {} to {}" ,
225+ source,
226+ target. display( )
184227 ) ) ;
185228 create_parent_path_if_not_exists ( target) ?;
186- fs:: rename ( & core_str, & target_str) ?;
187-
229+ let mut options = CopyOptions :: new ( ) ;
230+ options. content_only = true ;
231+ options. skip_exist = true ;
232+ move_dir ( source, target, & options) ?;
188233 Ok ( ( ) )
189234}
190235
@@ -261,7 +306,6 @@ pub fn uncompress_deb(
261306 compressed_file : & str ,
262307 target : & Path ,
263308 log : & Logger ,
264- os : & str ,
265309 label : & str ,
266310) -> Result < ( ) , Error > {
267311 let zip_parent = Path :: new ( compressed_file) . parent ( ) . unwrap ( ) ;
@@ -276,20 +320,8 @@ pub fn uncompress_deb(
276320 deb_pkg. data ( ) ?. unpack ( zip_parent) ?;
277321
278322 let zip_parent_str = path_to_string ( zip_parent) ;
279- let target_str = path_to_string ( target) ;
280323 let opt_edge_str = format ! ( "{}/opt/microsoft/{}" , zip_parent_str, label) ;
281- let opt_edge_mv = format ! ( "mv {} {}" , opt_edge_str, target_str) ;
282- let command = Command :: new_single ( opt_edge_mv. clone ( ) ) ;
283- log. trace ( format ! (
284- "Moving extracted files and folders from {} to {}" ,
285- opt_edge_str, target_str
286- ) ) ;
287- create_parent_path_if_not_exists ( target) ?;
288- run_shell_command_by_os ( os, command) ?;
289- let target_path = Path :: new ( target) ;
290- if target_path. parent ( ) . unwrap ( ) . read_dir ( ) ?. next ( ) . is_none ( ) {
291- fs:: rename ( & opt_edge_str, & target_str) ?;
292- }
324+ move_folder_content ( & opt_edge_str, & target, & log) ?;
293325
294326 Ok ( ( ) )
295327}
0 commit comments