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