@@ -1113,46 +1113,85 @@ fn copy_files_and_run(bytes: &[u8], extract_path: &Path) -> Result {
1113
1113
let mut extractor = Extract :: from_cursor ( archive, ArchiveFormat :: Tar ( Some ( Compression :: Gz ) ) ) ;
1114
1114
// the first file in the tar.gz will always be
1115
1115
// <app_name>/Contents
1116
- let tmp_dir = tempfile:: Builder :: new ( )
1117
- . prefix ( "tauri_current_app" )
1118
- . tempdir ( ) ?;
1119
1116
1120
- // create backup of our current app
1121
- Move :: from_source ( extract_path) . to_dest ( tmp_dir. path ( ) ) ?;
1117
+ // We'll extract the files to a temp directory first
1118
+ let tmp_extract_dir = tempfile:: Builder :: new ( )
1119
+ . prefix ( "tauri_updated_app" )
1120
+ . tempdir ( ) ?
1121
+ . into_path ( ) ;
1122
1122
1123
1123
// extract all the files
1124
1124
extractor. with_files ( |entry| {
1125
1125
let path = entry. path ( ) ?;
1126
1126
// skip the first folder (should be the app name)
1127
1127
let collected_path: PathBuf = path. iter ( ) . skip ( 1 ) . collect ( ) ;
1128
- let extraction_path = extract_path . join ( collected_path) ;
1128
+ let extraction_path = tmp_extract_dir . join ( collected_path) ;
1129
1129
1130
- // if something went wrong during the extraction, we should restore previous app
1131
1130
if let Err ( err) = entry. extract ( & extraction_path) {
1132
- for file in & extracted_files {
1133
- // delete all the files we extracted
1134
- if file. is_dir ( ) {
1135
- std:: fs:: remove_dir ( file) ?;
1136
- } else {
1137
- std:: fs:: remove_file ( file) ?;
1138
- }
1139
- }
1140
- Move :: from_source ( tmp_dir. path ( ) ) . to_dest ( extract_path) ?;
1131
+ std:: fs:: remove_dir_all ( & tmp_extract_dir) . ok ( ) ;
1141
1132
return Err ( crate :: api:: Error :: Extract ( err. to_string ( ) ) ) ;
1142
1133
}
1143
1134
1144
- extracted_files. push ( extraction_path) ;
1145
-
1146
1135
Ok ( false )
1147
1136
} ) ?;
1148
1137
1138
+ let tmp_backup_dir = tempfile:: Builder :: new ( )
1139
+ . prefix ( "tauri_current_app" )
1140
+ . tempdir ( ) ?;
1141
+
1142
+ // create backup of our current app
1143
+ let move_result = Move :: from_source ( extract_path) . to_dest ( tmp_backup_dir. path ( ) ) ;
1144
+ let need_authorization = if let Err ( err) = move_result {
1145
+ if is_permission_error ( & err) {
1146
+ true
1147
+ } else {
1148
+ std:: fs:: remove_dir_all ( & tmp_extract_dir) . ok ( ) ;
1149
+ return Err ( err. into ( ) ) ;
1150
+ }
1151
+ } else {
1152
+ false
1153
+ } ;
1154
+
1155
+ if need_authorization {
1156
+ // Ask for permission using AppleScript - run the two moves with admin privileges
1157
+ let script = format ! (
1158
+ "do shell script \" mv -f '{extract_path}' '{tmp_backup_dir}' && mv -f '{tmp_extract_dir}' '{extract_path}'\" with administrator privileges" ,
1159
+ tmp_extract_dir = tmp_extract_dir. display( ) ,
1160
+ extract_path = extract_path. display( ) ,
1161
+ tmp_backup_dir = tmp_backup_dir. path( ) . display( )
1162
+ ) ;
1163
+ let mut osascript = std:: process:: Command :: new ( "osascript" ) ;
1164
+ osascript. arg ( "-e" ) . arg ( script) ;
1165
+ let status = osascript. status ( ) ?;
1166
+ if !status. success ( ) {
1167
+ std:: fs:: remove_dir_all ( & tmp_extract_dir) . ok ( ) ;
1168
+ return Err ( Error :: Io ( std:: io:: Error :: new (
1169
+ std:: io:: ErrorKind :: PermissionDenied ,
1170
+ "Failed to move the new app into place" ,
1171
+ ) ) ) ;
1172
+ }
1173
+ } else {
1174
+ // move the new app to the target path
1175
+ Move :: from_source ( & tmp_extract_dir) . to_dest ( extract_path) ?;
1176
+ }
1177
+
1149
1178
let _ = std:: process:: Command :: new ( "touch" )
1150
1179
. arg ( extract_path)
1151
1180
. status ( ) ;
1152
1181
1153
1182
Ok ( ( ) )
1154
1183
}
1155
1184
1185
+ #[ cfg( target_os = "macos" ) ]
1186
+ fn is_permission_error ( err : & crate :: api:: Error ) -> bool {
1187
+ if let crate :: api:: Error :: Io ( io_err) = err {
1188
+ if io_err. kind ( ) == std:: io:: ErrorKind :: PermissionDenied {
1189
+ return true ;
1190
+ }
1191
+ }
1192
+ false
1193
+ }
1194
+
1156
1195
pub ( crate ) fn get_updater_target ( ) -> Option < & ' static str > {
1157
1196
if cfg ! ( target_os = "linux" ) {
1158
1197
Some ( "linux" )
0 commit comments