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 |
| 11 #import "Downloader.h" |
| 12 |
| 13 @interface Unpacker () { |
| 14 NSURL* temporaryDirectoryURL_; |
| 15 NSString* mountPath_; |
| 16 |
| 17 NSTask* __weak mountTask_; |
| 18 |
| 19 DASessionRef session_; |
| 20 dispatch_queue_t unpack_dq_; |
| 21 } |
| 22 - (void)didFinishEjectingDisk:(DADiskRef)disk |
| 23 withDissenter:(DADissenterRef)dissenter; |
| 24 @end |
| 25 |
| 26 static void eject_callback(DADiskRef disk, |
| 27 DADissenterRef dissenter, |
| 28 void* context) { |
| 29 Unpacker* unpacker = (__bridge Unpacker*)context; |
| 30 [unpacker didFinishEjectingDisk:disk withDissenter:dissenter]; |
| 31 } |
| 32 |
| 33 static void unmount_callback(DADiskRef disk, |
| 34 DADissenterRef dissenter, |
| 35 void* context) { |
| 36 if (dissenter) { |
| 37 Unpacker* unpacker = (__bridge Unpacker*)context; |
| 38 [unpacker didFinishEjectingDisk:disk withDissenter:dissenter]; |
| 39 } else { |
| 40 DADiskEject(disk, kDADiskEjectOptionDefault, eject_callback, context); |
| 41 } |
| 42 } |
| 43 |
| 44 @implementation Unpacker |
| 45 |
| 46 @synthesize delegate = delegate_; |
| 47 @synthesize appPath = appPath_; |
| 48 |
| 49 - (void)cleanUp { |
| 50 [mountTask_ terminate]; |
| 51 // It's not the end of the world if this temporary directory is not removed |
| 52 // here. It will be deleted when the operating system itself decides to |
| 53 // anyway. |
| 54 [[NSFileManager defaultManager] removeItemAtURL:temporaryDirectoryURL_ |
| 55 error:nil]; |
| 56 [[NSNotificationCenter defaultCenter] removeObserver:self]; |
| 57 } |
| 58 |
| 59 // TODO: the failure delegate methods need to be revised to be more meaningfully |
| 60 // deal with the errors (pipe in stderr / stdout) |
| 61 - (void)mountDMGFromURL:(NSURL*)fileURL { |
| 62 NSError* error = nil; |
| 63 temporaryDirectoryURL_ = [[NSFileManager defaultManager] |
| 64 URLForDirectory:NSItemReplacementDirectory |
| 65 inDomain:NSUserDomainMask |
| 66 appropriateForURL:[NSURL fileURLWithPath:@"/" isDirectory:YES] |
| 67 create:YES |
| 68 error:&error]; |
| 69 if (error) { |
| 70 [delegate_ unpacker:self onMountFailure:error]; |
| 71 return; |
| 72 } |
| 73 |
| 74 NSURL* temporaryDiskImageURL = |
| 75 [temporaryDirectoryURL_ URLByAppendingPathComponent:@"GoogleChrome.dmg"]; |
| 76 mountPath_ = [[temporaryDirectoryURL_ URLByAppendingPathComponent:@"mnt" |
| 77 isDirectory:YES] path]; |
| 78 [[NSFileManager defaultManager] createDirectoryAtPath:mountPath_ |
| 79 withIntermediateDirectories:YES |
| 80 attributes:nil |
| 81 error:&error]; |
| 82 if (error) { |
| 83 [delegate_ unpacker:self onMountFailure:error]; |
| 84 return; |
| 85 } |
| 86 [[NSNotificationCenter defaultCenter] |
| 87 addObserver:self |
| 88 selector:@selector(cleanUp) |
| 89 name:NSApplicationWillTerminateNotification |
| 90 object:nil]; |
| 91 |
| 92 [[NSFileManager defaultManager] moveItemAtURL:fileURL |
| 93 toURL:temporaryDiskImageURL |
| 94 error:nil]; |
| 95 |
| 96 NSString* path = @"/usr/bin/hdiutil"; |
| 97 NSArray* args = @[ |
| 98 @"attach", temporaryDiskImageURL, @"-nobrowse", @"-noverify", |
| 99 @"-mountpoint", mountPath_ |
| 100 ]; |
| 101 |
| 102 NSTask* mountTask = [[NSTask alloc] init]; |
| 103 mountTask.launchPath = path; |
| 104 mountTask.arguments = args; |
| 105 mountTask.terminationHandler = ^void(NSTask* task) { |
| 106 [delegate_ unpacker:self onMountSuccess:mountPath_]; |
| 107 }; |
| 108 mountTask_ = mountTask; |
| 109 [mountTask launch]; |
| 110 } |
| 111 |
| 112 - (void)unmountDMG { |
| 113 session_ = DASessionCreate(nil); |
| 114 unpack_dq_ = |
| 115 dispatch_queue_create("com.google.chrome.unpack", DISPATCH_QUEUE_SERIAL); |
| 116 DASessionSetDispatchQueue(session_, unpack_dq_); |
| 117 DADiskRef child_disk = DADiskCreateFromVolumePath( |
| 118 nil, session_, |
| 119 (CFURLRef)[NSURL fileURLWithPath:mountPath_ isDirectory:YES]); |
| 120 DADiskRef whole_disk = DADiskCopyWholeDisk(child_disk); |
| 121 |
| 122 DADiskUnmount(whole_disk, |
| 123 kDADiskUnmountOptionWhole | kDADiskUnmountOptionForce, |
| 124 unmount_callback, (void*)self); |
| 125 |
| 126 CFRelease(whole_disk); |
| 127 CFRelease(child_disk); |
| 128 } |
| 129 |
| 130 - (void)didFinishEjectingDisk:(DADiskRef)disk |
| 131 withDissenter:(DADissenterRef)dissenter { |
| 132 DASessionSetDispatchQueue(session_, NULL); |
| 133 dispatch_release(unpack_dq_); |
| 134 CFRelease(session_); |
| 135 NSError* error = nil; |
| 136 if (dissenter) { |
| 137 DAReturn status = DADissenterGetStatus(dissenter); |
| 138 error = [NSError |
| 139 errorWithDomain:@"ChromeErrorDomain" |
| 140 code:err_get_code(status) |
| 141 userInfo:@{ |
| 142 NSLocalizedDescriptionKey : |
| 143 (__bridge NSString*)DADissenterGetStatusString(dissenter) |
| 144 }]; |
| 145 [delegate_ unpacker:self onUnmountFailure:error]; |
| 146 } else { |
| 147 [self cleanUp]; |
| 148 if (error) { |
| 149 [delegate_ unpacker:self onUnmountFailure:error]; |
| 150 } else { |
| 151 [delegate_ unpacker:self onUnmountSuccess:mountPath_]; |
| 152 } |
| 153 } |
| 154 } |
| 155 |
| 156 @end |
OLD | NEW |