Chromium Code Reviews| 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 #include <DiskArbitration/DiskArbitration.h> | |
| 8 #include <dispatch/dispatch.h> | |
| 9 | |
| 10 #import "Downloader.h" | |
| 11 | |
| 12 @interface Unpacker () { | |
| 13 DASessionRef session_; | |
| 14 dispatch_queue_t unpack_dq_; | |
| 15 | |
| 16 NSURL* temporaryDirectoryURL_; | |
| 17 NSString* mountPath_; | |
| 18 } | |
| 19 - (void)didFinishEjectingDisk:(DADiskRef)disk | |
| 20 WithDissenter:(DADissenterRef)dissenter; | |
|
Sidney San Martín
2016/08/17 22:09:06
W → w
Anna Zeng
2016/08/18 19:41:09
Done
| |
| 21 @end | |
| 22 | |
| 23 static void eject_callback(DADiskRef disk, | |
| 24 DADissenterRef dissenter, | |
| 25 void* context) { | |
| 26 Unpacker* unpacker = (__bridge Unpacker*)context; | |
| 27 [unpacker didFinishEjectingDisk:disk WithDissenter:dissenter]; | |
| 28 } | |
| 29 | |
| 30 static void unmount_callback(DADiskRef disk, | |
| 31 DADissenterRef dissenter, | |
| 32 void* context) { | |
| 33 if (dissenter) { | |
| 34 Unpacker* unpacker = (__bridge Unpacker*)context; | |
| 35 [unpacker didFinishEjectingDisk:disk WithDissenter:dissenter]; | |
| 36 } else { | |
| 37 DADiskEject(disk, kDADiskEjectOptionDefault, eject_callback, context); | |
| 38 } | |
| 39 } | |
| 40 | |
| 41 @implementation Unpacker | |
| 42 | |
| 43 @synthesize delegate = delegate_; | |
| 44 @synthesize appPath = appPath_; | |
| 45 | |
| 46 // TODO: presumably we'd pass in the authenticated/unauthenticated path in here; | |
| 47 // however we will change this to a different model that will allow for a | |
| 48 // separate authenticated process to run | |
| 49 - (id)initWithFinalAppPath:(NSString*)appPath { | |
| 50 if ((self = [super init])) { | |
| 51 appPath_ = appPath; | |
| 52 } | |
| 53 return self; | |
| 54 } | |
| 55 | |
| 56 // TODO: the failure delegate methods need to be revised to be more meaningfully | |
| 57 // deal with the errors (pipe in stderr / stdout) | |
| 58 - (void)mountDMGFromURL:(NSURL*)fileURL { | |
| 59 NSError* error = nil; | |
| 60 temporaryDirectoryURL_ = [[NSFileManager defaultManager] | |
| 61 URLForDirectory:NSItemReplacementDirectory | |
| 62 inDomain:NSUserDomainMask | |
| 63 appropriateForURL:[NSURL fileURLWithPath:@"/" isDirectory:YES] | |
| 64 create:YES | |
| 65 error:&error]; | |
| 66 if (error) { | |
| 67 if ([delegate_ respondsToSelector:@selector(unpacker:onMountFailure:)]) { | |
| 68 [delegate_ unpacker:self onMountFailure:error]; | |
| 69 } else if ([delegate_ respondsToSelector:@selector(unpacker:onFailure:)]) { | |
| 70 [delegate_ unpacker:self onFailure:error]; | |
| 71 } else { | |
| 72 NSLog(@"no methods to catch failure implemented"); | |
| 73 } | |
| 74 } | |
| 75 | |
| 76 NSURL* temporaryDiskImageURL = | |
| 77 [temporaryDirectoryURL_ URLByAppendingPathComponent:@"GoogleChrome.dmg"]; | |
| 78 mountPath_ = [[temporaryDirectoryURL_ URLByAppendingPathComponent:@"mnt" | |
| 79 isDirectory:YES] path]; | |
| 80 [[NSFileManager defaultManager] createDirectoryAtPath:mountPath_ | |
| 81 withIntermediateDirectories:YES | |
| 82 attributes:nil | |
| 83 error:&error]; | |
| 84 if (error) { | |
| 85 if ([delegate_ respondsToSelector:@selector(unpacker:onMountFailure:)]) { | |
| 86 [delegate_ unpacker:self onMountFailure:error]; | |
| 87 } else if ([delegate_ respondsToSelector:@selector(unpacker:onFailure:)]) { | |
| 88 [delegate_ unpacker:self onFailure:error]; | |
| 89 } else { | |
| 90 NSLog(@"no methods to catch failure implemented"); | |
| 91 } | |
| 92 } | |
| 93 | |
| 94 [[NSFileManager defaultManager] moveItemAtURL:fileURL | |
| 95 toURL:temporaryDiskImageURL | |
| 96 error:nil]; | |
| 97 | |
| 98 dispatch_semaphore_t mount_semaphore = dispatch_semaphore_create(0); | |
| 99 NSString* path = @"/usr/bin/hdiutil"; | |
| 100 NSArray* args = @[ | |
| 101 @"attach", temporaryDiskImageURL, @"-nobrowse", @"-mountpoint", mountPath_ | |
| 102 ]; | |
| 103 | |
| 104 // TODO: how can we make sure this task quits if the user exits the app early? | |
| 105 NSTask* mountTask = [[NSTask alloc] init]; | |
| 106 mountTask.launchPath = path; | |
| 107 mountTask.arguments = args; | |
| 108 mountTask.terminationHandler = ^void(NSTask* task) { | |
| 109 dispatch_semaphore_signal(mount_semaphore); | |
| 110 }; | |
| 111 [mountTask launch]; | |
| 112 dispatch_semaphore_wait(mount_semaphore, DISPATCH_TIME_FOREVER); | |
| 113 | |
| 114 if ([delegate_ respondsToSelector:@selector(unpacker:onMountSuccess:)]) { | |
| 115 [delegate_ unpacker:self onMountSuccess:mountPath_]; | |
| 116 } else { | |
| 117 [self extractChrome]; | |
| 118 } | |
| 119 } | |
| 120 | |
| 121 - (void)extractChrome { | |
| 122 NSString* diskAppPath = | |
| 123 [NSString pathWithComponents:@[ mountPath_, @"Google Chrome.app" ]]; | |
| 124 if (![[NSFileManager defaultManager] fileExistsAtPath:diskAppPath]) { | |
| 125 NSLog(@"File in DMG doesn't exist"); | |
| 126 [delegate_ unpacker:self onFailure:nil]; | |
| 127 } | |
| 128 | |
| 129 // TODO: add progress | |
| 130 NSError* error = nil; | |
| 131 if (![[NSFileManager defaultManager] copyItemAtPath:diskAppPath | |
| 132 toPath:appPath_ | |
| 133 error:&error]) { | |
| 134 NSLog(@"%@", error); | |
| 135 [delegate_ unpacker:self onFailure:error]; | |
| 136 } else { | |
| 137 [delegate_ unpacker:self onSuccess:appPath_]; | |
| 138 } | |
| 139 } | |
| 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 [[NSFileManager defaultManager] removeItemAtURL:temporaryDirectoryURL_ | |
| 177 error:&error]; | |
| 178 if (error) { | |
| 179 [delegate_ unpacker:self onUnmountFailure:error]; | |
| 180 } else { | |
| 181 [delegate_ unpacker:self onUnmountSuccess:mountPath_]; | |
| 182 } | |
| 183 } | |
| 184 } | |
| 185 | |
| 186 @end | |
| OLD | NEW |