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