src/util/vfs/vfs-zip.c (view raw)
1/* Copyright (c) 2013-2014 Jeffrey Pfau
2 *
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6#include "util/vfs.h"
7
8#ifdef USE_LIBZIP
9#include <zip.h>
10
11struct VDirEntryZip {
12 struct VDirEntry d;
13 struct zip* z;
14 zip_int64_t index;
15};
16
17struct VDirZip {
18 struct VDir d;
19 struct zip* z;
20 struct VDirEntryZip dirent;
21};
22
23struct VFileZip {
24 struct VFile d;
25 struct zip_file* zf;
26 void* buffer;
27 size_t offset;
28 size_t bufferSize;
29 size_t readSize;
30 size_t fileSize;
31};
32
33enum {
34 BLOCK_SIZE = 1024
35};
36#else
37#ifdef USE_MINIZIP
38#include <minizip/unzip.h>
39#else
40#include "third-party/zlib/contrib/minizip/unzip.h"
41#endif
42#include "util/memory.h"
43
44struct VDirEntryZip {
45 struct VDirEntry d;
46 char name[PATH_MAX];
47 size_t fileSize;
48 unzFile z;
49};
50
51struct VDirZip {
52 struct VDir d;
53 unzFile z;
54 struct VDirEntryZip dirent;
55 bool hasNextFile;
56};
57
58struct VFileZip {
59 struct VFile d;
60 unzFile z;
61 void* buffer;
62 size_t fileSize;
63};
64#endif
65
66static bool _vfzClose(struct VFile* vf);
67static off_t _vfzSeek(struct VFile* vf, off_t offset, int whence);
68static ssize_t _vfzRead(struct VFile* vf, void* buffer, size_t size);
69static ssize_t _vfzWrite(struct VFile* vf, const void* buffer, size_t size);
70static void* _vfzMap(struct VFile* vf, size_t size, int flags);
71static void _vfzUnmap(struct VFile* vf, void* memory, size_t size);
72static void _vfzTruncate(struct VFile* vf, size_t size);
73static ssize_t _vfzSize(struct VFile* vf);
74static bool _vfzSync(struct VFile* vf, const void* buffer, size_t size);
75
76static bool _vdzClose(struct VDir* vd);
77static void _vdzRewind(struct VDir* vd);
78static struct VDirEntry* _vdzListNext(struct VDir* vd);
79static struct VFile* _vdzOpenFile(struct VDir* vd, const char* path, int mode);
80static struct VDir* _vdzOpenDir(struct VDir* vd, const char* path);
81static bool _vdzDeleteFile(struct VDir* vd, const char* path);
82
83static const char* _vdezName(struct VDirEntry* vde);
84static enum VFSType _vdezType(struct VDirEntry* vde);
85
86#ifndef USE_LIBZIP
87static voidpf _vfmzOpen(voidpf opaque, const char* filename, int mode) {
88 UNUSED(opaque);
89 int flags = 0;
90 switch (mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER) {
91 case ZLIB_FILEFUNC_MODE_READ:
92 flags = O_RDONLY;
93 break;
94 case ZLIB_FILEFUNC_MODE_WRITE:
95 flags = O_WRONLY;
96 break;
97 case ZLIB_FILEFUNC_MODE_READ | ZLIB_FILEFUNC_MODE_WRITE:
98 flags = O_RDWR;
99 break;
100 }
101 if (mode & ZLIB_FILEFUNC_MODE_CREATE) {
102 flags |= O_CREAT;
103 }
104 return VFileOpen(filename, flags);
105}
106
107static uLong _vfmzRead(voidpf opaque, voidpf stream, void* buf, uLong size) {
108 UNUSED(opaque);
109 struct VFile* vf = stream;
110 ssize_t r = vf->read(vf, buf, size);
111 if (r < 0) {
112 return 0;
113 }
114 return r;
115}
116
117int _vfmzClose(voidpf opaque, voidpf stream) {
118 UNUSED(opaque);
119 struct VFile* vf = stream;
120 return vf->close(vf);
121}
122
123int _vfmzError(voidpf opaque, voidpf stream) {
124 UNUSED(opaque);
125 struct VFile* vf = stream;
126 return vf->seek(vf, 0, SEEK_CUR) < 0;
127}
128
129long _vfmzTell(voidpf opaque, voidpf stream) {
130 UNUSED(opaque);
131 struct VFile* vf = stream;
132 return vf->seek(vf, 0, SEEK_CUR);
133}
134
135long _vfmzSeek(voidpf opaque, voidpf stream, uLong offset, int origin) {
136 UNUSED(opaque);
137 struct VFile* vf = stream;
138 return vf->seek(vf, offset, origin) < 0;
139}
140#endif
141
142struct VDir* VDirOpenZip(const char* path, int flags) {
143#ifndef USE_LIBZIP
144 UNUSED(flags);
145 zlib_filefunc_def ops = {
146 .zopen_file = _vfmzOpen,
147 .zread_file = _vfmzRead,
148 .zwrite_file = 0,
149 .ztell_file = _vfmzTell,
150 .zseek_file = _vfmzSeek,
151 .zclose_file = _vfmzClose,
152 .zerror_file = _vfmzError,
153 .opaque = 0
154 };
155 unzFile z = unzOpen2(path, &ops);
156 if (!z) {
157 return 0;
158 }
159#else
160 int zflags = 0;
161 if (flags & O_CREAT) {
162 zflags |= ZIP_CREATE;
163 }
164 if (flags & O_EXCL) {
165 zflags |= ZIP_EXCL;
166 }
167
168 struct zip* z = zip_open(path, zflags, 0);
169 if (!z) {
170 return 0;
171 }
172#endif
173 struct VDirZip* vd = malloc(sizeof(struct VDirZip));
174
175 vd->d.close = _vdzClose;
176 vd->d.rewind = _vdzRewind;
177 vd->d.listNext = _vdzListNext;
178 vd->d.openFile = _vdzOpenFile;
179 vd->d.openDir = _vdzOpenDir;
180 vd->d.deleteFile = _vdzDeleteFile;
181 vd->z = z;
182
183#ifndef USE_LIBZIP
184 vd->hasNextFile = true;
185#endif
186
187 vd->dirent.d.name = _vdezName;
188 vd->dirent.d.type = _vdezType;
189#ifdef USE_LIBZIP
190 vd->dirent.index = -1;
191#endif
192 vd->dirent.z = z;
193
194 return &vd->d;
195}
196
197#ifdef USE_LIBZIP
198bool _vfzClose(struct VFile* vf) {
199 struct VFileZip* vfz = (struct VFileZip*) vf;
200 if (zip_fclose(vfz->zf) < 0) {
201 return false;
202 }
203 free(vfz->buffer);
204 free(vfz);
205 return true;
206}
207
208off_t _vfzSeek(struct VFile* vf, off_t offset, int whence) {
209 struct VFileZip* vfz = (struct VFileZip*) vf;
210
211 size_t position;
212 switch (whence) {
213 case SEEK_SET:
214 position = offset;
215 break;
216 case SEEK_CUR:
217 if (offset < 0 && ((vfz->offset < (size_t) -offset) || (offset == INT_MIN))) {
218 return -1;
219 }
220 position = vfz->offset + offset;
221 break;
222 case SEEK_END:
223 if (offset < 0 && ((vfz->fileSize < (size_t) -offset) || (offset == INT_MIN))) {
224 return -1;
225 }
226 position = vfz->fileSize + offset;
227 break;
228 default:
229 return -1;
230 }
231
232 if (position <= vfz->offset) {
233 vfz->offset = position;
234 return position;
235 }
236
237 if (position <= vfz->fileSize) {
238 ssize_t read = vf->read(vf, 0, position - vfz->offset);
239 if (read < 0) {
240 return -1;
241 }
242 return vfz->offset;
243 }
244
245 return -1;
246}
247
248ssize_t _vfzRead(struct VFile* vf, void* buffer, size_t size) {
249 struct VFileZip* vfz = (struct VFileZip*) vf;
250
251 size_t bytesRead = 0;
252 if (!vfz->buffer) {
253 vfz->bufferSize = BLOCK_SIZE;
254 vfz->buffer = malloc(BLOCK_SIZE);
255 }
256
257 while (bytesRead < size) {
258 if (vfz->offset < vfz->readSize) {
259 size_t diff = vfz->readSize - vfz->offset;
260 void* start = &((uint8_t*) vfz->buffer)[vfz->offset];
261 if (diff > size - bytesRead) {
262 diff = size - bytesRead;
263 }
264 if (buffer) {
265 void* bufferOffset = &((uint8_t*) buffer)[bytesRead];
266 memcpy(bufferOffset, start, diff);
267 }
268 vfz->offset += diff;
269 bytesRead += diff;
270 if (diff == size) {
271 break;
272 }
273 }
274 // offset == readSize
275 if (vfz->readSize == vfz->bufferSize) {
276 vfz->bufferSize *= 2;
277 if (vfz->bufferSize > vfz->fileSize) {
278 vfz->bufferSize = vfz->fileSize;
279 }
280 vfz->buffer = realloc(vfz->buffer, vfz->bufferSize);
281 }
282 if (vfz->readSize < vfz->bufferSize) {
283 void* start = &((uint8_t*) vfz->buffer)[vfz->readSize];
284 size_t toRead = vfz->bufferSize - vfz->readSize;
285 if (toRead > BLOCK_SIZE) {
286 toRead = BLOCK_SIZE;
287 }
288 ssize_t zipRead = zip_fread(vfz->zf, start, toRead);
289 if (zipRead < 0) {
290 if (bytesRead == 0) {
291 return -1;
292 }
293 break;
294 }
295 if (zipRead == 0) {
296 break;
297 }
298 vfz->readSize += zipRead;
299 } else {
300 break;
301 }
302 }
303 return bytesRead;
304}
305
306ssize_t _vfzWrite(struct VFile* vf, const void* buffer, size_t size) {
307 // TODO
308 UNUSED(vf);
309 UNUSED(buffer);
310 UNUSED(size);
311 return -1;
312}
313
314void* _vfzMap(struct VFile* vf, size_t size, int flags) {
315 struct VFileZip* vfz = (struct VFileZip*) vf;
316
317 UNUSED(flags);
318 if (size > vfz->readSize) {
319 vf->read(vf, 0, size - vfz->readSize);
320 }
321 return vfz->buffer;
322}
323
324void _vfzUnmap(struct VFile* vf, void* memory, size_t size) {
325 UNUSED(vf);
326 UNUSED(memory);
327 UNUSED(size);
328}
329
330void _vfzTruncate(struct VFile* vf, size_t size) {
331 // TODO
332 UNUSED(vf);
333 UNUSED(size);
334}
335
336ssize_t _vfzSize(struct VFile* vf) {
337 struct VFileZip* vfz = (struct VFileZip*) vf;
338 return vfz->fileSize;
339}
340
341bool _vdzClose(struct VDir* vd) {
342 struct VDirZip* vdz = (struct VDirZip*) vd;
343 if (zip_close(vdz->z) < 0) {
344 return false;
345 }
346 free(vdz);
347 return true;
348}
349
350void _vdzRewind(struct VDir* vd) {
351 struct VDirZip* vdz = (struct VDirZip*) vd;
352 vdz->dirent.index = -1;
353}
354
355struct VDirEntry* _vdzListNext(struct VDir* vd) {
356 struct VDirZip* vdz = (struct VDirZip*) vd;
357 zip_int64_t maxIndex = zip_get_num_entries(vdz->z, 0);
358 if (maxIndex <= vdz->dirent.index + 1) {
359 return 0;
360 }
361 ++vdz->dirent.index;
362 return &vdz->dirent.d;
363}
364
365struct VFile* _vdzOpenFile(struct VDir* vd, const char* path, int mode) {
366 UNUSED(mode);
367 // TODO: support truncating, appending and creating, and write
368 struct VDirZip* vdz = (struct VDirZip*) vd;
369
370 if ((mode & O_RDWR) == O_RDWR) {
371 // libzip doesn't allow for random access, so read/write is impossible without
372 // reading the entire file first. This approach will be supported eventually.
373 return 0;
374 }
375
376 if (mode & O_WRONLY) {
377 // Write support is not yet implemented.
378 return 0;
379 }
380
381 struct zip_stat s;
382 if (zip_stat(vdz->z, path, 0, &s) < 0) {
383 return 0;
384 }
385
386 struct zip_file* zf = zip_fopen(vdz->z, path, 0);
387 if (!zf) {
388 return 0;
389 }
390
391 struct VFileZip* vfz = malloc(sizeof(struct VFileZip));
392 vfz->zf = zf;
393 vfz->buffer = 0;
394 vfz->offset = 0;
395 vfz->bufferSize = 0;
396 vfz->readSize = 0;
397 vfz->fileSize = s.size;
398
399 vfz->d.close = _vfzClose;
400 vfz->d.seek = _vfzSeek;
401 vfz->d.read = _vfzRead;
402 vfz->d.readline = VFileReadline;
403 vfz->d.write = _vfzWrite;
404 vfz->d.map = _vfzMap;
405 vfz->d.unmap = _vfzUnmap;
406 vfz->d.truncate = _vfzTruncate;
407 vfz->d.size = _vfzSize;
408 vfz->d.sync = _vfzSync;
409
410 return &vfz->d;
411}
412
413struct VDir* _vdzOpenDir(struct VDir* vd, const char* path) {
414 UNUSED(vd);
415 UNUSED(path);
416 return 0;
417}
418
419bool _vdzDeleteFile(struct VDir* vd, const char* path) {
420 UNUSED(vd);
421 UNUSED(path);
422 // TODO
423 return false;
424}
425
426bool _vfzSync(struct VFile* vf, const void* memory, size_t size) {
427 UNUSED(vf);
428 UNUSED(memory);
429 UNUSED(size);
430 return false;
431}
432
433const char* _vdezName(struct VDirEntry* vde) {
434 struct VDirEntryZip* vdez = (struct VDirEntryZip*) vde;
435 struct zip_stat s;
436 if (zip_stat_index(vdez->z, vdez->index, 0, &s) < 0) {
437 return 0;
438 }
439 return s.name;
440}
441
442static enum VFSType _vdezType(struct VDirEntry* vde) {
443 struct VDirEntryZip* vdez = (struct VDirEntryZip*) vde;
444 UNUSED(vdez);
445 return VFS_UNKNOWN;
446}
447#else
448bool _vfzClose(struct VFile* vf) {
449 struct VFileZip* vfz = (struct VFileZip*) vf;
450 unzCloseCurrentFile(vfz->z);
451 free(vfz->buffer);
452 free(vfz);
453 return true;
454}
455
456off_t _vfzSeek(struct VFile* vf, off_t offset, int whence) {
457 struct VFileZip* vfz = (struct VFileZip*) vf;
458
459 int64_t currentPos = unztell64(vfz->z);
460 int64_t pos;
461 switch (whence) {
462 case SEEK_SET:
463 pos = 0;
464 break;
465 case SEEK_CUR:
466 pos = unztell64(vfz->z);
467 break;
468 case SEEK_END:
469 pos = vfz->fileSize;
470 break;
471 default:
472 return -1;
473 }
474
475 if (pos < 0 || pos + offset < 0) {
476 return -1;
477 }
478 pos += offset;
479 if (currentPos > pos) {
480 unzCloseCurrentFile(vfz->z);
481 unzOpenCurrentFile(vfz->z);
482 currentPos = 0;
483 }
484 while (currentPos < pos) {
485 char tempBuf[1024];
486 ssize_t toRead = sizeof(tempBuf);
487 if (toRead > pos - currentPos) {
488 toRead = pos - currentPos;
489 }
490 ssize_t read = vf->read(vf, tempBuf, toRead);
491 if (read < toRead) {
492 return -1;
493 }
494 currentPos += read;
495 }
496
497 return unztell64(vfz->z);
498}
499
500ssize_t _vfzRead(struct VFile* vf, void* buffer, size_t size) {
501 struct VFileZip* vfz = (struct VFileZip*) vf;
502 return unzReadCurrentFile(vfz->z, buffer, size);
503}
504
505ssize_t _vfzWrite(struct VFile* vf, const void* buffer, size_t size) {
506 // TODO
507 UNUSED(vf);
508 UNUSED(buffer);
509 UNUSED(size);
510 return -1;
511}
512
513void* _vfzMap(struct VFile* vf, size_t size, int flags) {
514 struct VFileZip* vfz = (struct VFileZip*) vf;
515
516 // TODO
517 UNUSED(flags);
518
519 off_t pos = vf->seek(vf, 0, SEEK_CUR);
520 if (pos < 0) {
521 return 0;
522 }
523
524 vfz->buffer = anonymousMemoryMap(size);
525 if (!vfz->buffer) {
526 return 0;
527 }
528
529 unzCloseCurrentFile(vfz->z);
530 unzOpenCurrentFile(vfz->z);
531 vf->read(vf, vfz->buffer, size);
532 unzCloseCurrentFile(vfz->z);
533 unzOpenCurrentFile(vfz->z);
534 vf->seek(vf, pos, SEEK_SET);
535
536 return vfz->buffer;
537}
538
539void _vfzUnmap(struct VFile* vf, void* memory, size_t size) {
540 struct VFileZip* vfz = (struct VFileZip*) vf;
541
542 if (memory != vfz->buffer) {
543 return;
544 }
545
546 mappedMemoryFree(vfz->buffer, size);
547 vfz->buffer = 0;
548}
549
550void _vfzTruncate(struct VFile* vf, size_t size) {
551 // TODO
552 UNUSED(vf);
553 UNUSED(size);
554}
555
556ssize_t _vfzSize(struct VFile* vf) {
557 struct VFileZip* vfz = (struct VFileZip*) vf;
558 return vfz->fileSize;
559}
560
561bool _vdzClose(struct VDir* vd) {
562 struct VDirZip* vdz = (struct VDirZip*) vd;
563 if (unzClose(vdz->z) < 0) {
564 return false;
565 }
566 free(vdz);
567 return true;
568}
569
570void _vdzRewind(struct VDir* vd) {
571 struct VDirZip* vdz = (struct VDirZip*) vd;
572 vdz->hasNextFile = unzGoToFirstFile(vdz->z) == UNZ_OK;
573}
574
575struct VDirEntry* _vdzListNext(struct VDir* vd) {
576 struct VDirZip* vdz = (struct VDirZip*) vd;
577 if (!vdz->hasNextFile) {
578 return 0;
579 }
580 unz_file_info64 info;
581 int status = unzGetCurrentFileInfo64(vdz->z, &info, vdz->dirent.name, sizeof(vdz->dirent.name), 0, 0, 0, 0);
582 if (status < 0) {
583 return 0;
584 }
585 vdz->dirent.fileSize = info.uncompressed_size;
586 if (unzGoToNextFile(vdz->z) == UNZ_END_OF_LIST_OF_FILE) {
587 vdz->hasNextFile = false;
588 }
589 return &vdz->dirent.d;
590}
591
592struct VFile* _vdzOpenFile(struct VDir* vd, const char* path, int mode) {
593 UNUSED(mode);
594 struct VDirZip* vdz = (struct VDirZip*) vd;
595
596 if ((mode & O_ACCMODE) != O_RDONLY) {
597 // minizip implementation only supports read
598 return 0;
599 }
600
601 if (unzLocateFile(vdz->z, path, 0) != UNZ_OK) {
602 return 0;
603 }
604
605 if (unzOpenCurrentFile(vdz->z) < 0) {
606 return 0;
607 }
608
609 unz_file_info64 info;
610 int status = unzGetCurrentFileInfo64(vdz->z, &info, 0, 0, 0, 0, 0, 0);
611 if (status < 0) {
612 return 0;
613 }
614
615 struct VFileZip* vfz = malloc(sizeof(struct VFileZip));
616 vfz->z = vdz->z;
617 vfz->buffer = 0;
618 vfz->fileSize = info.uncompressed_size;
619
620 vfz->d.close = _vfzClose;
621 vfz->d.seek = _vfzSeek;
622 vfz->d.read = _vfzRead;
623 vfz->d.readline = VFileReadline;
624 vfz->d.write = _vfzWrite;
625 vfz->d.map = _vfzMap;
626 vfz->d.unmap = _vfzUnmap;
627 vfz->d.truncate = _vfzTruncate;
628 vfz->d.size = _vfzSize;
629 vfz->d.sync = _vfzSync;
630
631 return &vfz->d;
632}
633
634struct VDir* _vdzOpenDir(struct VDir* vd, const char* path) {
635 UNUSED(vd);
636 UNUSED(path);
637 return 0;
638}
639
640bool _vdzDeleteFile(struct VDir* vd, const char* path) {
641 UNUSED(vd);
642 UNUSED(path);
643 // TODO
644 return false;
645}
646
647bool _vfzSync(struct VFile* vf, const void* memory, size_t size) {
648 UNUSED(vf);
649 UNUSED(memory);
650 UNUSED(size);
651 return false;
652}
653
654const char* _vdezName(struct VDirEntry* vde) {
655 struct VDirEntryZip* vdez = (struct VDirEntryZip*) vde;
656 return vdez->name;
657}
658
659static enum VFSType _vdezType(struct VDirEntry* vde) {
660 struct VDirEntryZip* vdez = (struct VDirEntryZip*) vde;
661 UNUSED(vdez);
662 return VFS_UNKNOWN;
663}
664#endif