OLD | NEW |
(Empty) | |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #import "Unpacker.h" |
| 6 |
| 7 #import <AppKit/AppKit.h> |
| 8 #include <DiskArbitration/DiskArbitration.h> |
| 9 #include <dispatch/dispatch.h> |
| 10 #include <Security/Security.h> |
| 11 |
| 12 #import "Downloader.h" |
| 13 |
| 14 @interface Unpacker () { |
| 15 NSURL* temporaryDirectoryURL_; |
| 16 NSString* mountPath_; |
| 17 |
| 18 NSTask* __weak mountTask_; |
| 19 |
| 20 DASessionRef session_; |
| 21 dispatch_queue_t unpack_dq_; |
| 22 } |
| 23 - (void)didFinishEjectingDisk:(DADiskRef)disk |
| 24 withDissenter:(DADissenterRef)dissenter; |
| 25 @end |
| 26 |
| 27 static void eject_callback(DADiskRef disk, |
| 28 DADissenterRef dissenter, |
| 29 void* context) { |
| 30 Unpacker* unpacker = (__bridge Unpacker*)context; |
| 31 [unpacker didFinishEjectingDisk:disk withDissenter:dissenter]; |
| 32 } |
| 33 |
| 34 static void unmount_callback(DADiskRef disk, |
| 35 DADissenterRef dissenter, |
| 36 void* context) { |
| 37 if (dissenter) { |
| 38 Unpacker* unpacker = (__bridge Unpacker*)context; |
| 39 [unpacker didFinishEjectingDisk:disk withDissenter:dissenter]; |
| 40 } else { |
| 41 DADiskEject(disk, kDADiskEjectOptionDefault, eject_callback, context); |
| 42 } |
| 43 } |
| 44 |
| 45 @implementation Unpacker |
| 46 |
| 47 @synthesize delegate = delegate_; |
| 48 @synthesize appPath = appPath_; |
| 49 |
| 50 - (void)cleanUp { |
| 51 [mountTask_ terminate]; |
| 52 // It's not the end of the world if this temporary directory is not removed |
| 53 // here. It will be deleted when the operating system itself decides to |
| 54 // anyway. |
| 55 [[NSFileManager defaultManager] removeItemAtURL:temporaryDirectoryURL_ |
| 56 error:nil]; |
| 57 [[NSNotificationCenter defaultCenter] removeObserver:self]; |
| 58 } |
| 59 |
| 60 // TODO: the failure delegate methods need to be revised to be more meaningfully |
| 61 // deal with the errors (pipe in stderr / stdout) |
| 62 - (void)mountDMGFromURL:(NSURL*)fileURL { |
| 63 NSError* error = nil; |
| 64 temporaryDirectoryURL_ = [[NSFileManager defaultManager] |
| 65 URLForDirectory:NSItemReplacementDirectory |
| 66 inDomain:NSUserDomainMask |
| 67 appropriateForURL:[NSURL fileURLWithPath:@"/" isDirectory:YES] |
| 68 create:YES |
| 69 error:&error]; |
| 70 if (error) { |
| 71 [delegate_ unpacker:self onMountFailure:error]; |
| 72 return; |
| 73 } |
| 74 |
| 75 NSURL* temporaryDiskImageURL = |
| 76 [temporaryDirectoryURL_ URLByAppendingPathComponent:@"GoogleChrome.dmg"]; |
| 77 mountPath_ = [[temporaryDirectoryURL_ URLByAppendingPathComponent:@"mnt" |
| 78 isDirectory:YES] path]; |
| 79 [[NSFileManager defaultManager] createDirectoryAtPath:mountPath_ |
| 80 withIntermediateDirectories:YES |
| 81 attributes:nil |
| 82 error:&error]; |
| 83 if (error) { |
| 84 [delegate_ unpacker:self onMountFailure:error]; |
| 85 return; |
| 86 } |
| 87 [[NSNotificationCenter defaultCenter] |
| 88 addObserver:self |
| 89 selector:@selector(cleanUp) |
| 90 name:NSApplicationWillTerminateNotification |
| 91 object:nil]; |
| 92 |
| 93 [[NSFileManager defaultManager] moveItemAtURL:fileURL |
| 94 toURL:temporaryDiskImageURL |
| 95 error:nil]; |
| 96 |
| 97 // FIXME: type mismatch using these coderefs? |
| 98 SecStaticCodeRef diskStaticCode; |
| 99 SecRequirementRef diskRequirement; |
| 100 SecStaticCodeCreateWithPath((CFURLRef)CFBridgingRetain(temporaryDiskImageURL), |
| 101 kSecCSDefaultFlags, &diskStaticCode); |
| 102 // TODO: what requirement goes in here?? |
| 103 SecRequirementCreateWithString((CFStringRef) @"", kSecCSDefaultFlags, |
| 104 &diskRequirement); |
| 105 if (SecCodeCheckValidity(diskStaticCode, kSecCSDefaultFlags, |
| 106 diskRequirement) != errSecSuccess) { |
| 107 NSLog(@"verification failed"); |
| 108 } |
| 109 |
| 110 NSString* path = @"/usr/bin/hdiutil"; |
| 111 NSArray* args = @[ |
| 112 @"attach", temporaryDiskImageURL, @"-nobrowse", @"-noverify", |
| 113 @"-mountpoint", mountPath_ |
| 114 ]; |
| 115 |
| 116 NSTask* mountTask = [[NSTask alloc] init]; |
| 117 mountTask.launchPath = path; |
| 118 mountTask.arguments = args; |
| 119 mountTask.terminationHandler = ^void(NSTask* task) { |
| 120 NSError* error = nil; |
| 121 NSString* diskAppPath = |
| 122 [NSString pathWithComponents:@[ mountPath_, @"Google Chrome.app" ]]; |
| 123 NSString* tempAppPath = [[temporaryDirectoryURL_ |
| 124 URLByAppendingPathComponent:@"Google Chrome.app"] path]; |
| 125 [[NSFileManager defaultManager] copyItemAtPath:diskAppPath |
| 126 toPath:tempAppPath |
| 127 error:&error]; |
| 128 if (error) { |
| 129 [delegate_ unpacker:self onMountFailure:error]; |
| 130 } else { |
| 131 [delegate_ unpacker:self onMountSuccess:tempAppPath]; |
| 132 } |
| 133 }; |
| 134 mountTask_ = mountTask; |
| 135 [mountTask launch]; |
| 136 } |
| 137 |
| 138 // TODO: make this immediately follow from mount and copy |
| 139 // on unpack success / on unpack failure / on cleanup completion |
| 140 |
| 141 - (void)unmountDMG { |
| 142 session_ = DASessionCreate(nil); |
| 143 unpack_dq_ = |
| 144 dispatch_queue_create("com.google.chrome.unpack", DISPATCH_QUEUE_SERIAL); |
| 145 DASessionSetDispatchQueue(session_, unpack_dq_); |
| 146 DADiskRef child_disk = DADiskCreateFromVolumePath( |
| 147 nil, session_, |
| 148 (CFURLRef)[NSURL fileURLWithPath:mountPath_ isDirectory:YES]); |
| 149 DADiskRef whole_disk = DADiskCopyWholeDisk(child_disk); |
| 150 |
| 151 DADiskUnmount(whole_disk, |
| 152 kDADiskUnmountOptionWhole | kDADiskUnmountOptionForce, |
| 153 unmount_callback, (void*)self); |
| 154 |
| 155 CFRelease(whole_disk); |
| 156 CFRelease(child_disk); |
| 157 } |
| 158 |
| 159 - (void)didFinishEjectingDisk:(DADiskRef)disk |
| 160 withDissenter:(DADissenterRef)dissenter { |
| 161 DASessionSetDispatchQueue(session_, NULL); |
| 162 dispatch_release(unpack_dq_); |
| 163 CFRelease(session_); |
| 164 NSError* error = nil; |
| 165 if (dissenter) { |
| 166 DAReturn status = DADissenterGetStatus(dissenter); |
| 167 error = [NSError |
| 168 errorWithDomain:@"ChromeErrorDomain" |
| 169 code:err_get_code(status) |
| 170 userInfo:@{ |
| 171 NSLocalizedDescriptionKey : |
| 172 (__bridge NSString*)DADissenterGetStatusString(dissenter) |
| 173 }]; |
| 174 [delegate_ unpacker:self onUnmountFailure:error]; |
| 175 } else { |
| 176 [self cleanUp]; |
| 177 if (error) { |
| 178 [delegate_ unpacker:self onUnmountFailure:error]; |
| 179 } else { |
| 180 [delegate_ unpacker:self onUnmountSuccess:mountPath_]; |
| 181 } |
| 182 } |
| 183 } |
| 184 |
| 185 @end |
OLD | NEW |