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