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