1
2 /*
3 * NOTE: this is part of libzzipmmapped (i.e. it is not libzzip).
4 * ==================
5 *
6 * These routines are fully independent from the traditional zzip
7 * implementation. They assume a readonly mmapped sharedmem block
8 * representing a complete zip file. The functions show how to
9 * parse the structure, find files and return a decoded bytestream.
10 *
11 * These routines are a bit simple and really here for documenting
12 * the way to access a zip file. The complexity of zip access comes
13 * from staggered reading of bytes and reposition of a filepointer in
14 * a big archive with lots of files and long compressed datastreams.
15 * Plus varaints of drop-in stdio replacements, obfuscation routines,
16 * auto fileextensions, drop-in dirent replacements, and so on...
17 *
18 * Author:
19 * Guido Draheim <guidod@gmx.de>
20 *
21 * Copyright (c)Guido Draheim, use under copyleft (LGPL,MPL)
22 */
23
24 #define _ZZIP_DISK_FILE_STRUCT 1
25
26 #ifdef __linux__
27 #define _GNU_SOURCE _glibc_developers_are_idiots_to_call_strndup_gnu_specific_
28 #endif
29
30 #include <zzip/mmapped.h>
31 #include <zzip/format.h>
32 #include <zzip/fetch.h>
33 #include <zzip/__debug.h>
34 #include <zzip/__mmap.h>
35 #include <zzip/__string.h>
36 #include <zzip/__fnmatch.h>
37 #include <zzip/__errno.h>
38
39 #include <stdlib.h>
40 #include <sys/stat.h>
41
42 #if defined ZZIP_HAVE_UNISTD_H
43 #include <unistd.h>
44 #elif defined ZZIP_HAVE_IO_H
45 #include <io.h>
46 #endif
47
48 #if defined ZZIP_HAVE_STRING_H
49 #include <string.h>
50 #elif defined ZZIP_HAVE_STRINGS_H
51 #include <strings.h>
52 #endif
53
54
55 #if __STDC_VERSION__+0 > 199900L
56 #define ___
57 #define ____
58 #else
59 #define ___ {
60 #define ____ }
61 #endif
62
63 #define DEBUG 1
64 #ifdef DEBUG
65 #define debug1(msg) do { fprintf(stderr, "DEBUG: %s : " msg "\n", __func__); } while(0)
66 #define debug2(msg, arg1) do { fprintf(stderr, "DEBUG: %s : " msg "\n", __func__, arg1); } while(0)
67 #define debug3(msg, arg1, arg2) do { fprintf(stderr, "DEBUG: %s : " msg "\n", __func__, arg1, arg2); } while(0)
68 #define debug4(msg, arg1, arg2, arg3) do { fprintf(stderr, "DEBUG: %s : " msg "\n", __func__, arg1, arg2, arg3); } while(0)
69 #else
70 #define debug1(msg)
71 #define debug2(msg, arg1)
72 #define debug3(msg, arg1, arg2)
73 #define debug4(msg, arg1, arg2, arg3)
74 #endif
75
76 /** => zzip_disk_mmap
77 * This function does primary initialization of a disk-buffer struct.
78 *
79 * This function always returns 0 as success.
80 */
81 int
zzip_disk_init(ZZIP_DISK * disk,void * buffer,zzip_size_t buflen)82 zzip_disk_init(ZZIP_DISK * disk, void *buffer, zzip_size_t buflen)
83 {
84 disk->buffer = (zzip_byte_t *) buffer;
85 disk->endbuf = (zzip_byte_t *) buffer + buflen;
86 disk->reserved = 0;
87 disk->flags = 0;
88 disk->mapped = 0;
89 /* do not touch disk->user */
90 /* do not touch disk->code */
91 return 0;
92 }
93
94 /** => zzip_disk_mmap
95 * This function allocates a new disk-buffer with => malloc(3)
96 *
97 * This function may return null on errors (errno).
98 */
99 zzip__new__ ZZIP_DISK *
zzip_disk_new(void)100 zzip_disk_new(void)
101 {
102 ZZIP_DISK *disk = malloc(sizeof(ZZIP_DISK));
103 if (! disk)
104 return disk; /* ENOMEM */
105 zzip_disk_init(disk, 0, 0);
106 return disk;
107 }
108
109 /** turn a filehandle into a mmapped zip disk archive handle
110 *
111 * This function uses the given file-descriptor to detect the length of the
112 * file and calls the system => mmap(2) to put it in main memory. If it is
113 * successful then a newly allocated ZZIP_DISK* is returned with
114 * disk->buffer pointing to the mapview of the zipdisk content.
115 *
116 * This function may return null on errors (errno).
117 */
118 zzip__new__ ZZIP_DISK *
zzip_disk_mmap(int fd)119 zzip_disk_mmap(int fd)
120 {
121 struct stat st;
122 if (fstat(fd, &st) || ! st.st_size)
123 return 0; /* EACCESS */
124 ___ ZZIP_DISK *disk = zzip_disk_new();
125 if (! disk)
126 return 0; /* ENOMEM */
127 disk->buffer = _zzip_mmap(disk->mapped, fd, 0, st.st_size);
128 if (disk->buffer == MAP_FAILED)
129 {
130 free (disk);
131 return 0; /* EFAULT */
132 }
133 disk->endbuf = disk->buffer + st.st_size;
134 return disk;
135 ____;
136 }
137
138 /** => zzip_disk_mmap
139 * This function is the inverse of => zzip_disk_mmap and using the system
140 * munmap(2) on the buffer area and => free(3) on the ZZIP_DISK structure.
141 */
142 int
zzip_disk_munmap(ZZIP_DISK * disk)143 zzip_disk_munmap(ZZIP_DISK * disk)
144 {
145 if (! disk)
146 return 0;
147 _zzip_munmap(disk->mapped, disk->buffer, disk->endbuf - disk->buffer);
148 free(disk);
149 return 0;
150 }
151
152 /** => zzip_disk_mmap
153 *
154 * This function opens the given archive by name and turn the filehandle
155 * to => zzip_disk_mmap for bringing it to main memory. If it can not
156 * be => mmap(2)'ed then we slurp the whole file into a newly => malloc(2)'ed
157 * memory block. Only if that fails too then we return null. Since handling
158 * of disk->buffer is ambiguous it should not be snatched away please.
159 *
160 * This function may return null on errors (errno).
161 */
162 zzip__new__ ZZIP_DISK *
zzip_disk_open(char * filename)163 zzip_disk_open(char *filename)
164 {
165 # ifndef O_BINARY
166 # define O_BINARY 0
167 # endif
168 struct stat st;
169 if (stat(filename, &st) || ! st.st_size)
170 return 0; /* ENOENT */
171 ___ int fd = open(filename, O_RDONLY | O_BINARY);
172 if (fd <= 0)
173 return 0; /* EACCESS */
174 ___ ZZIP_DISK *disk = zzip_disk_mmap(fd);
175 if (disk)
176 return disk;
177 ___ zzip_byte_t *buffer = malloc(st.st_size);
178 if (! buffer)
179 {
180 return 0; /* ENOMEM */
181 }
182 if ((st.st_size < read(fd, buffer, st.st_size)))
183 {
184 free (buffer);
185 return 0; /* EIO */
186 }
187 disk = zzip_disk_new();
188 if (! disk)
189 {
190 free (buffer);
191 return 0; /* ENOMEM */
192 }
193 disk->buffer = buffer;
194 disk->endbuf = buffer + st.st_size;
195 disk->mapped = -1;
196 disk->flags |= ZZIP_DISK_FLAGS_OWNED_BUFFER;
197 return disk;
198 ____;
199 ____;
200 ____;
201 }
202
203 /** => zzip_disk_mmap
204 * This function will attach a buffer with a zip image
205 * that was acquired from another source than a file.
206 * Note that if zzip_disk_mmap fails then zzip_disk_open
207 * will fall back and try to read the full file to memory
208 * wrapping a ZZIP_DISK around the memory buffer just as
209 * this function will do. Note that this function will not
210 * own the buffer, it will neither be written nor free()d.
211 *
212 * This function may return null (errno).
213 */
214 zzip__new__ ZZIP_DISK *
zzip_disk_buffer(void * buffer,size_t buflen)215 zzip_disk_buffer(void *buffer, size_t buflen) {
216 ZZIP_DISK *disk = zzip_disk_new();
217 if (disk)
218 {
219 disk->buffer = (zzip_byte_t *) buffer;
220 disk->endbuf = (zzip_byte_t *) buffer + buflen;
221 disk->mapped = -1;
222 }
223 return disk;
224 }
225
226 /** => zzip_disk_mmap
227 *
228 * This function will release all data needed to access a (mmapped)
229 * zip archive, including any malloc()ed blocks, sharedmem mappings
230 * and it dumps the handle struct as well.
231 *
232 * This function returns 0 on success (or whatever => munmap says).
233 */
234 int
zzip_disk_close(ZZIP_DISK * disk)235 zzip_disk_close(ZZIP_DISK * disk)
236 {
237 if (! disk)
238 return 0;
239 if (disk->mapped != -1)
240 return zzip_disk_munmap(disk);
241 if (disk->flags & ZZIP_DISK_FLAGS_OWNED_BUFFER)
242 free(disk->buffer);
243 free(disk);
244 return 0;
245 }
246
247 /* ====================================================================== */
248 /* helper functions */
249
250
251 /** helper functions for (mmapped) zip access api
252 *
253 * This function augments the other zzip_disk_entry_* helpers: here we move
254 * a disk_entry pointer (as returned by _find* functions) into a pointer to
255 * the data block right after the file_header. Only disk->buffer would be
256 * needed to perform the seek but we check the mmapped range end as well.
257 *
258 * This function returns a pointer into disk->buffer or 0 on error (errno).
259 */
260 zzip_byte_t *
zzip_disk_entry_to_data(ZZIP_DISK * disk,struct zzip_disk_entry * entry)261 zzip_disk_entry_to_data(ZZIP_DISK * disk, struct zzip_disk_entry * entry)
262 {
263 struct zzip_file_header *file = zzip_disk_entry_to_file_header(disk, entry);
264 if (! file)
265 return 0; /* EBADMSG */
266 return zzip_file_header_to_data(file);
267 }
268
269 /** => zzip_disk_entry_to_data
270 * This function does half the job of => zzip_disk_entry_to_data where it
271 * can augment with => zzip_file_header_to_data helper from format/fetch.h
272 *
273 * This function returns a pointer into disk->buffer or 0 on error (errno).
274 */
275 struct zzip_file_header *
zzip_disk_entry_to_file_header(ZZIP_DISK * disk,struct zzip_disk_entry * entry)276 zzip_disk_entry_to_file_header(ZZIP_DISK * disk, struct zzip_disk_entry *entry)
277 {
278 zzip_byte_t *const ptr = disk->buffer + zzip_disk_entry_fileoffset(entry);
279 if (disk->buffer > ptr || ptr >= disk->endbuf)
280 {
281 debug2("file header: offset out of bounds (0x%llx)", (long long unsigned)(disk->buffer));
282 errno = EBADMSG;
283 return 0;
284 }
285 ___ struct zzip_file_header *file_header = (void *) ptr;
286 if (zzip_file_header_get_magic(file_header) != ZZIP_FILE_HEADER_MAGIC)
287 {
288 debug1("file header: bad magic");
289 errno = EBADMSG;
290 return 0;
291 }
292 return file_header;
293 ____;
294 }
295
296 /** => zzip_disk_entry_to_data
297 * This function is a big helper despite its little name: in a zip file the
298 * encoded filenames are usually NOT zero-terminated but for common usage
299 * with libc we need it that way. Secondly, the filename SHOULD be present
300 * in the zip central directory but if not then we fallback to the filename
301 * given in the file_header of each compressed data portion.
302 *
303 * This function returns a new string buffer, or null on error.
304 * If no name can be found then an empty string is returned.
305 */
306 zzip__new__ char *
zzip_disk_entry_strdup_name(ZZIP_DISK * disk,struct zzip_disk_entry * entry)307 zzip_disk_entry_strdup_name(ZZIP_DISK * disk, struct zzip_disk_entry *entry)
308 {
309 if (! disk || ! entry)
310 {
311 errno=EINVAL;
312 return 0;
313 }
314
315 ___ char *name = 0;
316 zzip_size_t len = len = zzip_disk_entry_namlen(entry);
317 if (len)
318 {
319 name = zzip_disk_entry_to_filename(entry);
320 }
321 else
322 {
323 struct zzip_file_header *file = zzip_disk_entry_to_file_header(disk, entry);
324 if (! file)
325 return 0; /* EBADMSG */
326
327 len = zzip_file_header_namlen(file);
328 if (! len)
329 {
330 /* neither a name in disk_entry nor in file_header */
331 return strdup(""); /* ENOMEM */
332 }
333 name = zzip_file_header_to_filename(file);
334 }
335
336 if ((zzip_byte_t *) name < disk->buffer ||
337 (zzip_byte_t *) name + len > disk->endbuf)
338 {
339 errno=EBADMSG;
340 return 0;
341 }
342
343 return _zzip_strndup(name, len); /* ENOMEM */
344 ____;
345 }
346
347 /** => zzip_disk_entry_to_data
348 * This function is similar creating a reference to a zero terminated
349 * string but it can only exist in the zip central directory entry.
350 *
351 * This function returns a new string buffer, or null on error (errno).
352 * If no name can be found then an empty string is returned.
353 */
354 zzip__new__ char *
zzip_disk_entry_strdup_comment(ZZIP_DISK * disk,struct zzip_disk_entry * entry)355 zzip_disk_entry_strdup_comment(ZZIP_DISK * disk, struct zzip_disk_entry *entry)
356 {
357 if (! disk || ! entry)
358 {
359 errno = EINVAL;
360 return 0;
361 }
362
363 ___ zzip_size_t len = zzip_disk_entry_comment(entry);
364 if (! len)
365 {
366 return strdup(""); /* ENOMEM */
367 }
368
369 ___ char *text = zzip_disk_entry_to_comment(entry);
370 if ((zzip_byte_t *) text < disk->buffer ||
371 (zzip_byte_t *) text + len > disk->endbuf)
372 {
373 errno = EBADMSG;
374 return 0;
375 }
376
377 return _zzip_strndup(text, len); /* ENOMEM */
378 ____;
379 ____;
380 }
381
382 /* ====================================================================== */
383
384 /** => zzip_disk_findfile
385 *
386 * This function is the first call of all the zip access functions here.
387 * It contains the code to find the first entry of the zip central directory.
388 * Here we require the mmapped block to represent a real zip file where the
389 * disk_trailer is _last_ in the file area, so that its position would be at
390 * a fixed offset from the end of the file area if not for the comment field
391 * allowed to be of variable length (which needs us to do a little search
392 * for the disk_tailer). However, in this simple implementation we disregard
393 * any disk_trailer info telling about multidisk archives, so we just return
394 * a pointer to the zip central directory.
395 *
396 * For an actual means, we are going to search backwards from the end
397 * of the mmaped block looking for the PK-magic signature of a
398 * disk_trailer. If we see one then we check the rootseek value to
399 * find the first disk_entry of the root central directory. If we find
400 * the correct PK-magic signature of a disk_entry over there then we
401 * assume we are done and we are going to return a pointer to that label.
402 *
403 * The return value is a pointer to the first zzip_disk_entry being checked
404 * to be within the bounds of the file area specified by the arguments. If
405 * no disk_trailer was found then null is returned, and likewise we only
406 * accept a disk_trailer with a seekvalue that points to a disk_entry and
407 * both parts have valid PK-magic parts. Beyond some sanity check we try to
408 * catch a common brokeness with zip archives that still allows us to find
409 * the start of the zip central directory.So this function may return null
410 * and sets errno.
411 */
412 struct zzip_disk_entry *
zzip_disk_findfirst(ZZIP_DISK * disk)413 zzip_disk_findfirst(ZZIP_DISK * disk)
414 {
415 DBG1("findfirst");
416 if (! disk)
417 {
418 DBG1("non arg");
419 errno = EINVAL;
420 return 0;
421 }
422 if (disk->buffer > disk->endbuf - sizeof(struct zzip_disk_trailer))
423 {
424 DBG1("not enough data for a disk trailer");
425 errno = EBADMSG;
426 return 0;
427 }
428 ___ zzip_byte_t *p = disk->endbuf - sizeof(struct zzip_disk_trailer);
429 for (; p >= disk->buffer; p--)
430 {
431 zzip_byte_t *root; /* (struct zzip_disk_entry*) */
432 zzip_size_t rootsize; /* Size of root central directory */
433
434 if (zzip_disk_trailer_check_magic(p))
435 {
436 struct zzip_disk_trailer *trailer = (struct zzip_disk_trailer *) p;
437 zzip_size_t rootseek = zzip_disk_trailer_get_rootseek(trailer);
438 rootsize = zzip_disk_trailer_get_rootsize(trailer);
439
440 root = disk->buffer + rootseek;
441 DBG2("disk rootseek at %lli", (long long)rootseek);
442 if (root > p)
443 {
444 /* the first disk_entry is after the disk_trailer? can't be! */
445 DBG2("have rootsize at %lli", (long long)rootsize);
446 if (disk->buffer + rootsize > p)
447 continue;
448 /* a common brokeness that can be fixed: we just assume the
449 * central directory was written directly before the trailer:*/
450 root = p - rootsize;
451 }
452 } else if (zzip_disk64_trailer_check_magic(p))
453 {
454 struct zzip_disk64_trailer *trailer =
455 (struct zzip_disk64_trailer *) p;
456 if (sizeof(void *) < 8)
457 {
458 DBG1("disk64 trailer in non-largefile part");
459 errno = EFBIG;
460 return 0;
461 }
462 zzip_size_t rootseek = zzip_disk64_trailer_get_rootseek(trailer);
463 rootsize = zzip_disk64_trailer_get_rootsize(trailer);
464 DBG2("disk64 rootseek at %lli", (long long)rootseek);
465 root = disk->buffer + rootseek;
466 if (root > p)
467 continue;
468 } else
469 {
470 continue;
471 }
472
473 DBG4("buffer %p root %p endbuf %p", disk->buffer, root, disk->endbuf);
474 if (root < disk->buffer)
475 {
476 DBG1("root before buffer should be impossible");
477 errno = EBADMSG;
478 return 0;
479 }
480 if (root >= disk->endbuf || (root + rootsize) >= disk->endbuf)
481 {
482 DBG1("root behind endbuf should be impossible");
483 errno = EBADMSG;
484 return 0;
485 }
486 if (zzip_disk_entry_check_magic(root))
487 {
488 DBG2("found the disk root %p", root);
489 return (struct zzip_disk_entry *) root;
490 }
491 } ____;
492 /* not found */
493 errno = ENOENT;
494 return 0;
495 }
496
497 /** => zzip_disk_findfile
498 *
499 * This function takes an existing disk_entry in the central root directory
500 * (e.g. from zzip_disk_findfirst) and returns the next entry within in
501 * the given bounds of the mmapped file area.
502 *
503 * This function returns null if no next entry can be found.
504 * This function may return null on errors. (errno = ENOENT|EINVAL|EBADMSG)
505 */
506 struct zzip_disk_entry *
zzip_disk_findnext(ZZIP_DISK * disk,struct zzip_disk_entry * entry)507 zzip_disk_findnext(ZZIP_DISK * disk, struct zzip_disk_entry *entry)
508 {
509 if (! disk || ! entry)
510 {
511 errno = EINVAL;
512 return 0;
513 }
514 if ((zzip_byte_t *) entry < disk->buffer ||
515 (zzip_byte_t *) entry > disk->endbuf - sizeof(entry) ||
516 ! zzip_disk_entry_check_magic(entry) ||
517 zzip_disk_entry_sizeto_end(entry) > 64 * 1024)
518 {
519 errno = EBADMSG;
520 return 0;
521 }
522 entry = zzip_disk_entry_to_next_entry(entry);
523 if ((zzip_byte_t *) entry > disk->endbuf - sizeof(entry) ||
524 ! zzip_disk_entry_check_magic(entry) ||
525 zzip_disk_entry_sizeto_end(entry) > 64 * 1024 ||
526 zzip_disk_entry_skipto_end(entry) + sizeof(entry) > disk->endbuf)
527 {
528 errno = ENOENT;
529 return 0;
530 }
531 return entry;
532 }
533
534 /** search for files in the (mmapped) zip central directory
535 *
536 * This function is given a filename as an additional argument, to find the
537 * disk_entry matching a given filename. The compare-function is usually
538 * strcmp or strcasecmp or perhaps strcoll, if null then strcmp is used.
539 * - use null as argument for "after"-entry when searching the first
540 * matching entry, otherwise the last returned value if you look for other
541 * entries with a special "compare" function (if null then a doubled search
542 * is rather useless with this variant of _findfile).
543 *
544 * This functionreturns the entry pointer.
545 * This function may return null on error. (errno = ENOMEM|EBADMSG|ENOENT)
546 */
547 struct zzip_disk_entry *
zzip_disk_findfile(ZZIP_DISK * disk,char * filename,struct zzip_disk_entry * after,zzip_strcmp_fn_t compare)548 zzip_disk_findfile(ZZIP_DISK * disk, char *filename,
549 struct zzip_disk_entry *after, zzip_strcmp_fn_t compare)
550 {
551 struct zzip_disk_entry *entry = (! after ? zzip_disk_findfirst(disk)
552 : zzip_disk_findnext(disk, after));
553 if (! compare)
554 compare = (zzip_strcmp_fn_t) ((disk->flags & ZZIP_DISK_FLAGS_MATCH_NOCASE) ?
555 (_zzip_strcasecmp) : (strcmp));
556 for (; entry; entry = zzip_disk_findnext(disk, entry))
557 {
558 /* filenames within zip files are often not null-terminated! */
559 char *realname = zzip_disk_entry_strdup_name(disk, entry);
560 if (! realname)
561 {
562 return 0; /* ENOMEM | EBADMSG */
563 }
564 if (! compare(filename, realname))
565 {
566 free(realname);
567 return entry; /* found */
568 }
569 free(realname);
570 }
571 errno = ENOENT;
572 return 0;
573 }
574
575 /** => zzip_disk_findfile
576 *
577 * This function uses a compare-function with an additional argument
578 * and it is called just like fnmatch(3) from POSIX.2 AD:1993), i.e.
579 * the argument filespec first and the ziplocal filename second with
580 * the integer-flags put in as third to the indirect call. If the
581 * platform has fnmatch available then null-compare will use that one
582 * and otherwise we fall back to mere strcmp, so if you need fnmatch
583 * searching then please provide an implementation somewhere else.
584 * - use null as argument for "after"-entry when searching the first
585 * matching entry, or the last disk_entry return-value to find the
586 * next entry matching the given filespec.
587 *
588 * This function will return the matching entry pointer.
589 * This function may return null on error. (errno = ENOMEM|EBADMSG|ENOENT)
590 */
591 struct zzip_disk_entry *
zzip_disk_findmatch(ZZIP_DISK * disk,char * filespec,struct zzip_disk_entry * after,zzip_fnmatch_fn_t compare,int flags)592 zzip_disk_findmatch(ZZIP_DISK * disk, char *filespec,
593 struct zzip_disk_entry *after,
594 zzip_fnmatch_fn_t compare, int flags)
595 {
596 struct zzip_disk_entry *entry = (! after ? zzip_disk_findfirst(disk)
597 : zzip_disk_findnext(disk, after));
598 if (! compare)
599 {
600 compare = (zzip_fnmatch_fn_t) _zzip_fnmatch;
601 if (disk->flags & ZZIP_DISK_FLAGS_MATCH_NOCASE)
602 flags |= _zzip_FNM_CASEFOLD;
603 }
604 for (; entry; entry = zzip_disk_findnext(disk, entry))
605 {
606 /* filenames within zip files are often not null-terminated! */
607 char *realname = zzip_disk_entry_strdup_name(disk, entry);
608 if (! realname)
609 {
610 return 0; /* ENOMEM | EBADMSG */
611 }
612 if (compare(filespec, realname, flags))
613 {
614 free(realname);
615 return entry; /* found */
616 }
617 free(realname);
618 }
619 errno = ENOENT;
620 return 0;
621 }
622
623 /* ====================================================================== */
624
625 /** => zzip_disk_fopen
626 *
627 * the ZZIP_DISK_FILE* is rather simple in just encapsulating the
628 * arguments given to this function plus a zlib deflate buffer.
629 * Note that the ZZIP_DISK pointer does already contain the full
630 * mmapped file area of a zip disk, so open()ing a file part within
631 * that area happens to be a lookup of its bounds and encoding. That
632 * information is memorized on the ZZIP_DISK_FILE so that subsequent
633 * _read() operations will be able to get the next data portion or
634 * return an eof condition for that file part wrapped in the zip archive.
635 *
636 * This function may return null on errors (errno = ENOMEM|EBADMSG).
637 */
638 zzip__new__ ZZIP_DISK_FILE *
zzip_disk_entry_fopen(ZZIP_DISK * disk,ZZIP_DISK_ENTRY * entry)639 zzip_disk_entry_fopen(ZZIP_DISK * disk, ZZIP_DISK_ENTRY * entry)
640 {
641 /* keep this in sync with zzip_mem_entry_fopen */
642 struct zzip_file_header *header =
643 zzip_disk_entry_to_file_header(disk, entry);
644 if (! header)
645 return 0; /* EBADMSG */
646 ___ ZZIP_DISK_FILE *file = malloc(sizeof(ZZIP_DISK_FILE));
647 if (! file)
648 return 0; /* ENOMEM */
649 file->buffer = disk->buffer;
650 file->endbuf = disk->endbuf;
651 file->avail = zzip_file_header_usize(header);
652
653 if (! file->avail || zzip_file_header_data_stored(header))
654 {
655 file->stored = zzip_file_header_to_data (header);
656 DBG2("stored size %i", (int) file->avail);
657 if (file->stored + file->avail >= disk->endbuf)
658 goto error;
659 return file;
660 }
661
662 ___ /* a ZIP64 extended block may follow. */
663 size_t csize = zzip_file_header_csize(header);
664 off_t offset = zzip_file_header_to_data(header);
665 if (csize == 0xFFFFu) {
666 struct zzip_extra_zip64* zip64 =
667 zzip_file_header_to_extras(header);
668 if (ZZIP_EXTRA_ZIP64_CHECK(zip64)) {
669 csize = zzip_extra_zip64_csize(zip64);
670 }
671 }
672 if (offset == 0xFFFFu) {
673 struct zzip_extra_zip64* zip64 =
674 zzip_file_header_to_extras(header);
675 if (ZZIP_EXTRA_ZIP64_CHECK(zip64)) {
676 offset = zzip_extra_zip64_offset(zip64);
677 }
678 }
679
680 file->stored = 0;
681 file->zlib.opaque = 0;
682 file->zlib.zalloc = Z_NULL;
683 file->zlib.zfree = Z_NULL;
684 file->zlib.avail_in = csize;
685 file->zlib.next_in = offset;
686 ____;
687
688 DBG2("compressed size %i", (int) file->zlib.avail_in);
689 if (file->zlib.next_in + file->zlib.avail_in >= disk->endbuf)
690 goto error;
691 if (file->zlib.next_in < disk->buffer)
692 goto error;
693
694 if (! zzip_file_header_data_deflated(header))
695 goto error;
696 if (inflateInit2(&file->zlib, -MAX_WBITS) != Z_OK)
697 goto error;
698
699 return file;
700 error:
701 free (file);
702 errno = EBADMSG;
703 return 0;
704 ____;
705 }
706
707 /** openening a file part wrapped within a (mmapped) zip archive
708 *
709 * This function opens a file found by name, so it does a search into
710 * the zip central directory with => zzip_disk_findfile and whatever
711 * is found first is given to => zzip_disk_entry_fopen
712 *
713 * This function may return null on errors (errno).
714 */
715 zzip__new__ ZZIP_DISK_FILE *
zzip_disk_fopen(ZZIP_DISK * disk,char * filename)716 zzip_disk_fopen(ZZIP_DISK * disk, char *filename)
717 {
718 ZZIP_DISK_ENTRY *entry = zzip_disk_findfile(disk, filename, 0, 0);
719 if (! entry)
720 return 0; /* EBADMSG */
721 return zzip_disk_entry_fopen(disk, entry);
722 }
723
724 /** => zzip_disk_fopen
725 *
726 * This function reads more bytes into the output buffer specified as
727 * arguments. The return value is null on eof or error, the stdio-like
728 * interface can not distinguish between these so you need to check
729 * with => zzip_disk_feof for the difference.
730 */
731 zzip_size_t
zzip_disk_fread(void * ptr,zzip_size_t sized,zzip_size_t nmemb,ZZIP_DISK_FILE * file)732 zzip_disk_fread(void *ptr, zzip_size_t sized, zzip_size_t nmemb,
733 ZZIP_DISK_FILE * file)
734 {
735 zzip_size_t size = sized * nmemb;
736 if (! ptr || ! sized || ! file)
737 return 0;
738 if (size > file->avail)
739 size = file->avail;
740 if (file->stored)
741 {
742 if (file->stored + size >= file->endbuf)
743 {
744 DBG1("try to read beyond end of file");
745 return 0; /* ESPIPE */
746 }
747 DBG3("copy stored %p %i", file->stored, (int)size);
748 memcpy(ptr, file->stored, size);
749 file->stored += size;
750 file->avail -= size;
751 return size;
752 }
753
754 file->zlib.avail_out = sized * nmemb;
755 file->zlib.next_out = ptr;
756 ___ zzip_size_t total_old = file->zlib.total_out;
757 ___ int err = inflate(&file->zlib, Z_NO_FLUSH);
758 if (err == Z_STREAM_END)
759 file->avail = 0;
760 else if (err == Z_OK)
761 file->avail -= file->zlib.total_out - total_old;
762 else
763 return 0;
764 return file->zlib.total_out - total_old;
765 ____;
766 ____;
767 }
768
769 /** => zzip_disk_fopen
770 * This function releases any zlib decoder info needed for decompression
771 * and dumps the ZZIP_DISK_FILE* then.
772 *
773 * This function always returns 0.
774 */
775 int
zzip_disk_fclose(ZZIP_DISK_FILE * file)776 zzip_disk_fclose(ZZIP_DISK_FILE * file)
777 {
778 if (file)
779 {
780 if (! file->stored)
781 inflateEnd(&file->zlib);
782 free(file);
783 }
784 return 0;
785 }
786
787 /** => zzip_disk_fopen
788 *
789 * This function allows to distinguish an error from an eof condition.
790 * Actually, if we found an error but we did already reach eof then we
791 * just keep on saying that it was an eof, so the app can just continue.
792 *
793 * This function returns EOF in case and 0 when not at the end
794 * of file.
795 */
796 int
zzip_disk_feof(ZZIP_DISK_FILE * file)797 zzip_disk_feof(ZZIP_DISK_FILE * file)
798 {
799 if (! file || ! file->avail)
800 return EOF;
801 return 0;
802 }
803