PKSignedContainer.m
1 /* 2 This file is part of Darling. 3 4 Copyright (C) 2017 Lubos Dolezel 5 6 Darling is free software: you can redistribute it and/or modify 7 it under the terms of the GNU General Public License as published by 8 the Free Software Foundation, either version 3 of the License, or 9 (at your option) any later version. 10 11 Darling is distributed in the hope that it will be useful, 12 but WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 GNU General Public License for more details. 15 16 You should have received a copy of the GNU General Public License 17 along with Darling. If not, see <http://www.gnu.org/licenses/>. 18 */ 19 20 #import <PackageKit/PackageKit.h> 21 #include <stdlib.h> 22 #include <string.h> 23 #include <unistd.h> 24 #include <fcntl.h> 25 #include <pthread/private.h> 26 27 // This class is (will) be implemented to allow applications such as 28 // https://github.com/danzimm/xip_extract/blob/master/xip_extract.m 29 // to work. 30 31 static int open_cb(struct archive *a, void *_client_data) 32 { 33 return ARCHIVE_OK; 34 } 35 36 static int close_cb(struct archive *a, void *_client_data) 37 { 38 return ARCHIVE_OK; 39 } 40 41 struct ReadContext 42 { 43 xar_stream* xs; 44 uint64_t blockLength; 45 46 char inbuf[4096]; 47 char outbuf[4096 * 1024]; 48 }; 49 50 static ssize_t read_cb(struct archive *a, void *_client_data, const void **_buffer) 51 { 52 struct ReadContext* rc = (struct ReadContext*) _client_data; 53 54 if (!rc->blockLength) 55 { 56 // TODO 57 } 58 return 0; 59 } 60 61 static NSError* makeError(NSURL* url, NSString* errorString) 62 { 63 NSDictionary* userInfo = @{ 64 NSLocalizedDescriptionKey: errorString, 65 NSFilePathErrorKey: [url absoluteString] 66 }; 67 return [[NSError alloc] initWithDomain:@"PackageKit" 68 code: 0 69 userInfo: userInfo]; 70 } 71 72 static inline BOOL xar_read(char *buffer, uint32_t size, xar_stream *stream) { 73 stream->next_out = buffer; 74 stream->avail_out = size; 75 while (stream->avail_out) 76 { 77 if (xar_extract_tostream(stream) != XAR_STREAM_OK) 78 return NO; 79 } 80 return YES; 81 } 82 83 static inline uint64_t xar_read_64(xar_stream *stream) { 84 char t[8]; 85 xar_read(t, 8, stream); 86 return __builtin_bswap64(*(uint64_t *)t); 87 } 88 89 static int copy_data(struct archive *ar, struct archive *aw) 90 { 91 int r; 92 const void *buff; 93 size_t size; 94 off_t offset; 95 96 for (;;) 97 { 98 r = archive_read_data_block(ar, &buff, &size, &offset); 99 if (r == ARCHIVE_EOF) 100 return (ARCHIVE_OK); 101 if (r != ARCHIVE_OK) 102 return (r); 103 r = archive_write_data_block(aw, buff, size, offset); 104 if (r != ARCHIVE_OK) 105 { 106 NSLog(@"archive_write_data_block error\n"); 107 return (r); 108 } 109 } 110 } 111 112 @implementation PKSignedContainer 113 114 - (instancetype)initForReadingFromContainerAtURL:(NSURL *)url 115 error:(NSError **)error 116 { 117 const char* path = [url fileSystemRepresentation]; 118 119 _xar = xar_open(path, READ); 120 121 if (!_xar) 122 { 123 *error = makeError(url, @"Cannot open input XIP (XAR error)"); 124 return nil; 125 } 126 127 xar_iter_t i = xar_iter_new(); 128 _xar_file = xar_file_first(_xar, i); 129 char *xarPath; 130 131 while (strncmp((xarPath = xar_get_path(_xar_file)), "Content", 7) && (_xar_file = xar_file_next(i))) 132 free(xarPath); 133 134 free(xarPath); 135 xar_iter_free(i); 136 137 if (!_xar_file) 138 { 139 *error = makeError(url, @"Not a XIP, 'Content' not found"); 140 return nil; 141 } 142 143 if (xar_verify(_xar, _xar_file) != XAR_STREAM_OK) 144 { 145 *error = makeError(url, @"xar_verify failed"); 146 return nil; 147 } 148 149 if (xar_extract_tostream_init(_xar, _xar_file, &_xar_stream) != XAR_STREAM_OK) 150 { 151 *error = makeError(url, @"XAR init failed"); 152 return nil; 153 } 154 155 char header[4]; 156 xar_read(header, sizeof(header), &_xar_stream); 157 158 if (strncmp(header, "pbzx", 4) != 0) 159 { 160 *error = makeError(url, @"Not a pbzx stream"); 161 return nil; 162 } 163 164 xar_extract_tostream_end(&_xar_stream); 165 166 return self; 167 } 168 169 - (void)dealloc 170 { 171 lzma_end(&_zs); 172 173 if (_xar) 174 xar_close(_xar); 175 if (_archive) 176 archive_read_finish(_archive); 177 } 178 179 - (void)cancelOperation:(id)arg1 180 { 181 _abort = YES; 182 } 183 184 - (void)startUnarchivingAtPath:(NSString *)path 185 notifyOnQueue:(dispatch_queue_t)queue 186 progress:(void (^)(double, NSString *))progressBlock 187 finish:(void (^)(BOOL))finishBlock 188 { 189 if (_archive) 190 { 191 NSLog(@"PackageKit: Unarchiving already in progress\n"); 192 dispatch_async(queue, ^{ 193 finishBlock(NO); 194 }); 195 return; 196 } 197 198 int dfd = open([path UTF8String], O_RDONLY | O_DIRECTORY); 199 if (dfd == -1) 200 { 201 NSLog(@"PackageKit: target directory cannot be opened\n"); 202 dispatch_async(queue, ^{ 203 finishBlock(NO); 204 }); 205 return; 206 } 207 208 if (lzma_stream_decoder(&_zs, UINT64_MAX, LZMA_CONCATENATED) != LZMA_OK) 209 { 210 close(dfd); 211 NSLog(@"PackageKit: LZMA init failed\n"); 212 dispatch_async(queue, ^{ 213 finishBlock(NO); 214 }); 215 return; 216 } 217 218 char header[4]; 219 xar_read(header, sizeof(header), &_xar_stream); 220 _flags = xar_read_64(&_xar_stream); 221 222 _archive = archive_read_new(); 223 224 archive_read_support_format_cpio(_archive); 225 226 dispatch_queue_t q = dispatch_queue_create("org.darlinghq.PackageKit", NULL); 227 228 _abort = NO; 229 dispatch_async(q, ^{ 230 BOOL result = YES; 231 struct ReadContext* rc = (struct ReadContext*) malloc(sizeof(struct ReadContext)); 232 233 rc->xs = &_xar_stream; 234 rc->blockLength = 0; 235 236 pthread_fchdir_np(dfd); 237 close(dfd); 238 239 int err = archive_read_open(_archive, (__bridge void*) self, open_cb, read_cb, close_cb); 240 if (err != ARCHIVE_OK) 241 { 242 NSLog(@"archive_read_open error\n"); 243 result = NO; 244 goto out; 245 } 246 247 struct archive* ext = archive_write_disk_new(); 248 249 while (!_abort) 250 { 251 struct archive_entry *entry; 252 int r = archive_read_next_header(_archive, &entry); 253 254 if (r == ARCHIVE_EOF) 255 break; 256 257 if (r != ARCHIVE_OK) 258 { 259 NSLog(@"archive_read_next_header failed\n"); 260 result = NO; 261 break; 262 } 263 264 r = archive_write_header(ext, entry); 265 if (r != ARCHIVE_OK) 266 { 267 NSLog(@"archive_write_header failed\n"); 268 result = NO; 269 break; 270 } 271 r = copy_data(_archive, ext); 272 if (r != ARCHIVE_OK) 273 { 274 NSLog(@"failed to write data\n"); 275 result = NO; 276 break; 277 } 278 279 r = archive_write_finish_entry(ext); 280 if (r != ARCHIVE_OK) 281 { 282 NSLog(@"archive_write_finish_entry failed\n"); 283 result = NO; 284 break; 285 } 286 287 // TODO: report progress 288 } 289 290 out: 291 archive_write_finish(ext); 292 archive_read_close(_archive); 293 archive_read_finish(_archive); 294 _archive = NULL; 295 free(rc); 296 pthread_fchdir_np(-1); 297 298 dispatch_async(queue, ^{ 299 finishBlock(result); 300 }); 301 }); 302 303 #if !__has_feature(objc_arc) 304 dispatch_release(q); 305 #endif 306 } 307 308 - (xar_stream*)_xarStream 309 { 310 return &_xar_stream; 311 } 312 313 @end