11
11
#import " SUDiskImageUnarchiver.h"
12
12
#import " SUUnarchiverNotifier.h"
13
13
#import " SULog.h"
14
+ #import " SUErrors.h"
14
15
15
16
16
17
#include " AppKitPrevention.h"
@@ -50,11 +51,11 @@ - (instancetype)initWithArchivePath:(NSString *)archivePath extractionDirectory:
50
51
return self;
51
52
}
52
53
53
- - (void )unarchiveWithCompletionBlock : (void (^)(NSError * _Nullable))completionBlock progressBlock : (void (^ _Nullable)(double ))progressBlock
54
+ - (void )unarchiveWithCompletionBlock : (void (^)(NSError * _Nullable))completionBlock progressBlock : (void (^ _Nullable)(double ))progressBlock waitForCleanup : ( BOOL ) waitForCleanup
54
55
{
55
56
dispatch_async (dispatch_get_global_queue (DISPATCH_QUEUE_PRIORITY_DEFAULT, 0 ), ^{
56
57
SUUnarchiverNotifier *notifier = [[SUUnarchiverNotifier alloc ] initWithCompletionBlock: completionBlock progressBlock: progressBlock];
57
- [self extractDMGWithNotifier: notifier];
58
+ [self extractDMGWithNotifier: notifier waitForCleanup: waitForCleanup ];
58
59
});
59
60
}
60
61
@@ -78,7 +79,7 @@ - (BOOL)fileManager:(NSFileManager *)fileManager shouldCopyItemAtURL:(NSURL *)sr
78
79
}
79
80
80
81
// Called on a non-main thread.
81
- - (void )extractDMGWithNotifier : (SUUnarchiverNotifier *)notifier SPU_OBJC_DIRECT
82
+ - (void )extractDMGWithNotifier : (SUUnarchiverNotifier *)notifier waitForCleanup : ( BOOL ) waitForCleanup SPU_OBJC_DIRECT
82
83
{
83
84
@autoreleasepool {
84
85
BOOL mountedSuccessfully = NO ;
@@ -95,25 +96,32 @@ - (void)extractDMGWithNotifier:(SUUnarchiverNotifier *)notifier SPU_OBJC_DIRECT
95
96
// Note: this check does not follow symbolic links, which is what we want
96
97
while ([[NSURL fileURLWithPath: mountPoint] checkResourceIsReachableAndReturnError: NULL ]);
97
98
98
- NSData *promptData = [NSData dataWithBytes: " yes \n " length: 4 ];
99
+ NSMutableData *inputData = [NSMutableData data ];
99
100
100
- // Finder doesn't verify disk images anymore beyond the code signing signature (if available)
101
- // Opt out of the old CRC checksum checks
102
- NSMutableArray *arguments = [@[@" attach" , _archivePath, @" -mountpoint" , mountPoint, @" -noverify" , @" -nobrowse" , @" -noautoopen" ] mutableCopy ];
103
-
104
- if (_decryptionPassword) {
105
- NSMutableData *passwordData = [[_decryptionPassword dataUsingEncoding: NSUTF8StringEncoding] mutableCopy ];
101
+ // Prepare stdin data for passwords and license agreements
102
+ {
103
+ // If no password is supplied, we will still be asked a password.
104
+ // In that case we respond with an empty password.
105
+ NSData *decryptionPasswordData = [_decryptionPassword dataUsingEncoding: NSUTF8StringEncoding];
106
+ if (decryptionPasswordData != nil ) {
107
+ [inputData appendData: decryptionPasswordData];
108
+ }
109
+
106
110
// From the hdiutil docs:
107
111
// read a null-terminated passphrase from standard input
108
112
//
109
- // Add the null terminator, then the newline
110
- [passwordData appendData: [NSData dataWithBytes: " \0 " length: 1 ]];
111
- [passwordData appendData: promptData];
112
- promptData = passwordData;
113
+ // Add the null terminator
114
+ [inputData appendBytes: " \0 " length: 1 ];
113
115
114
- [arguments addObject: @" -stdinpass" ];
116
+ // Append prompt data for license agreements
117
+ [inputData appendBytes: " yes\n " length: 4 ];
115
118
}
116
119
120
+ // Finder doesn't verify disk images anymore beyond the code signing signature (if available)
121
+ // Opt out of the old CRC checksum checks
122
+ // Also always pass -stdinpass so we gracefully handle password protected disk images even if we aren't expecting them
123
+ NSArray *arguments = @[@" attach" , _archivePath, @" -mountpoint" , mountPoint, @" -noverify" , @" -nobrowse" , @" -noautoopen" , @" -stdinpass" ];
124
+
117
125
NSData *output = nil ;
118
126
NSInteger taskResult = -1 ;
119
127
@@ -153,15 +161,15 @@ - (void)extractDMGWithNotifier:(SUUnarchiverNotifier *)notifier SPU_OBJC_DIRECT
153
161
}
154
162
155
163
if (@available (macOS 10.15 , *)) {
156
- if (![inputPipe.fileHandleForWriting writeData: promptData error: &error]) {
164
+ if (![inputPipe.fileHandleForWriting writeData: inputData error: &error]) {
157
165
goto reportError;
158
166
}
159
167
}
160
168
#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_15
161
169
else
162
170
{
163
171
@try {
164
- [inputPipe.fileHandleForWriting writeData: promptData ];
172
+ [inputPipe.fileHandleForWriting writeData: inputData ];
165
173
} @catch (NSException *) {
166
174
goto reportError;
167
175
}
@@ -175,12 +183,16 @@ - (void)extractDMGWithNotifier:(SUUnarchiverNotifier *)notifier SPU_OBJC_DIRECT
175
183
176
184
taskResult = task.terminationStatus ;
177
185
}
178
-
186
+
179
187
if (taskResult != 0 ) {
180
188
NSString *resultStr = output ? [[NSString alloc ] initWithData: output encoding: NSUTF8StringEncoding] : nil ;
181
189
SULog (SULogLevelError, @" hdiutil failed with code: %ld data: <<%@ >>" , (long )taskResult, resultStr);
190
+
191
+ error = [NSError errorWithDomain: SUSparkleErrorDomain code: SUUnarchivingError userInfo: @{NSLocalizedDescriptionKey :[NSString stringWithFormat: @" Extraction failed due to hdiutil returning %ld status: %@ " , (long )taskResult, resultStr]}];
192
+
182
193
goto reportError;
183
194
}
195
+
184
196
mountedSuccessfully = YES ;
185
197
186
198
// Mounting can take some time, so increment progress
@@ -271,11 +283,11 @@ - (void)extractDMGWithNotifier:(SUUnarchiverNotifier *)notifier SPU_OBJC_DIRECT
271
283
272
284
[notifier notifyProgress: 1.0 ];
273
285
274
- [notifier notifySuccess ] ;
286
+ BOOL success = YES ;
275
287
goto finally;
276
288
277
289
reportError:
278
- [notifier notifyFailureWithError: error] ;
290
+ success = NO ;
279
291
280
292
finally:
281
293
if (mountedSuccessfully) {
@@ -289,10 +301,18 @@ - (void)extractDMGWithNotifier:(SUUnarchiverNotifier *)notifier SPU_OBJC_DIRECT
289
301
if (![task launchAndReturnError: &launchCleanupError]) {
290
302
SULog (SULogLevelError, @" Failed to unmount %@ " , mountPoint);
291
303
SULog (SULogLevelError, @" Error: %@ " , launchCleanupError);
304
+ } else if (waitForCleanup) {
305
+ [task waitUntilExit ];
292
306
}
293
307
} else {
294
308
SULog (SULogLevelError, @" Can't mount DMG %@ " , _archivePath);
295
309
}
310
+
311
+ if (success) {
312
+ [notifier notifySuccess ];
313
+ } else {
314
+ [notifier notifyFailureWithError: error];
315
+ }
296
316
}
297
317
}
298
318
0 commit comments