/ src / private-frameworks / PackageKit / src / PKSignedContainer.m
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