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 |