]> granicus.if.org Git - zziplib/blob - zzip/fseeko.c
using mksite.sh for doc build
[zziplib] / zzip / fseeko.c
1 /*
2  * These routines are fully independent from the traditional zzip
3  * implementation. They assume a readonly seekable stdio handle
4  * representing a complete zip file. The functions show how to 
5  * parse the structure, find files and return a decoded bytestream.
6  *
7  * These routines are a bit simple and really here for documenting
8  * the way to access a zip file. The complexity of zip access comes
9  * from staggered reading of bytes and reposition of a filepointer in
10  * a big archive with lots of files and long compressed datastreams.
11  * Plus varaints of drop-in stdio replacements, obfuscation routines,
12  * auto fileextensions, drop-in dirent replacements, and so on...
13  *
14  * btw, we can _not_ use fgetpos/fsetpos since an fpos_t has no asserted
15  * relation to a linear seek value as specified in zip info headers. In
16  * general it is not a problem if your system has no fseeko/ftello pair
17  * since we can fallback to fseek/ftell which limits the zip disk size
18  * to 2MiBs but the zip-storable seek values are 32bit limited anyway.
19  *
20  * Author: 
21  *      Guido Draheim <guidod@gmx.de>
22  *
23  * Copyright (c) 2003,2004 Guido Draheim
24  *          All rights reserved,
25  *          use under the restrictions of the 
26  *          Lesser GNU General Public License
27  *          or alternatively the restrictions 
28  *          of the Mozilla Public License 1.1
29  */
30
31 #define _LARGEFILE_SOURCE
32
33 #include <zzip/fseeko.h>
34 #include <assert.h>
35 #include <stdlib.h>
36 #include <sys/stat.h>
37
38 #ifdef ZZIP_HAVE_FNMATCH_H
39 #include <fnmatch.h>
40 #endif
41
42 #if   defined ZZIP_HAVE_STRING_H
43 #include <string.h>
44 #elif defined ZZIP_HAVE_STRINGS_H
45 #include <strings.h>
46 #endif
47
48 #include <zlib.h>
49 #include <zzip/format.h>
50 #include <zzip/fetch.h>
51 #include <zzip/__mmap.h>
52
53 #if __STDC_VERSION__+0 > 199900L
54 #define ___
55 #define ____
56 #else
57 #define ___ {
58 #define ____ }
59 #endif
60
61 #ifndef ZZIP_HAVE_FSEEKO
62 #define fseeko fseek
63 #define ftello ftello
64 #endif
65
66 /* note that the struct zzip_entry inherits the zzip_disk_entry values
67  * and usually carries a copy of its values (in disk format!). To make the
68  * following code more readable, we use a shorthand notation for the
69  * upcast needed in C (not needed in C++) as "disk_(entry)".
70  */
71 #ifdef __cplusplus
72 #define disk_(_entry_) _entry_
73 #else
74 #define disk_(_entry_) (& (_entry_)->head)
75 #endif
76
77 #ifdef __cplusplus
78 struct zzip_entry : public struct zzip_disk_entry
79 {
80     char*             _zzip_restrict tail;
81     zzip_off_t                  tailalloc;   /* the allocated size of tail */
82     FILE*                        diskfile;   /* a file reference */
83     zzip_off_t                   disksize;   /* the size of the file */
84     zzip_off_t                   headseek;   /* the offset within the file */
85 };
86 #else
87 struct zzip_entry /* : struct zzip_disk_entry */
88 {
89     struct zzip_disk_entry           head;
90     char*             _zzip_restrict tail;
91     zzip_off_t                  tailalloc;   /* the allocated size of tail */
92     FILE*                        diskfile;   /* a file reference */
93     zzip_off_t                   disksize;   /* the size of the file */
94     zzip_off_t                   headseek;   /* the offset within the file */
95 };
96 #endif
97
98 /* we try to round all seeks to the pagesize - since we do not use
99  * the sys/mmap interface we have to guess a good value here: */
100 #define PAGESIZE 8192
101
102 /* ====================================================================== */
103 /*                      helper functions                                  */
104
105 /** => zzip_entry_data_offset
106  * This functions read the correspoding struct zzip_file_header from 
107  * the zip disk of the given "entry". The returned off_t points to the
108  * end of the file_header where the current fseek pointer has stopped.
109  * This is used to immediatly parse out any filename/extras block following
110  * the file_header. The return value is null on error.
111  */
112 static zzip_off_t
113 zzip_entry_fread_file_header (ZZIP_ENTRY* entry, 
114                              struct zzip_file_header* file_header)
115 {
116     zzip_off_t offset = zzip_disk_entry_fileoffset (disk_(entry));
117     if (0 > offset || offset >= entry->disksize) return 0;
118     fseeko (entry->diskfile, offset, SEEK_SET);
119     return (fread (file_header, sizeof(*file_header), 1, entry->diskfile)
120             ? offset+sizeof(*file_header) : 0 );
121 }
122
123 /** helper functions for (fseeko) zip access api
124  *
125  * This functions returns the seekval offset of the data portion of the
126  * file referenced by the given zzip_entry. It requires an intermediate
127  * check of the file_header structure (i.e. it reads it from disk). After 
128  * this call, the contained diskfile readposition is already set to the 
129  * data_offset returned here. On error -1 is returned.
130  */
131 zzip_off_t
132 zzip_entry_data_offset(ZZIP_ENTRY* entry)
133 {
134     struct zzip_file_header file_header;
135     if (! entry) return -1;
136     ___ zzip_off_t offset = 
137         zzip_entry_fread_file_header (entry, & file_header);
138     if (! offset) return -1;
139     offset += zzip_file_header_sizeof_tails (& file_header);
140     fseeko (entry->diskfile, offset, SEEK_SET);
141     return offset; ____;
142 }
143
144 /** => zzip_entry_data_offset
145  * This function is a big helper despite its little name: in a zip file the
146  * encoded filenames are usually NOT zero-terminated but for common usage
147  * with libc we need it that way. Secondly, the filename SHOULD be present
148  * in the zip central directory but if not then we fallback to the filename
149  * given in the file_header of each compressed data portion.
150  */
151 char* _zzip_restrict
152 zzip_entry_strdup_name(ZZIP_ENTRY* entry)
153 {
154     if (! entry) return 0;
155
156     ___ zzip_size_t len;
157     if ((len = zzip_disk_entry_namlen (disk_(entry))))
158     {
159         char* name = malloc (len+1);
160         if (! name) return 0;
161         memcpy (name, entry->tail, len);
162         name[len] = '\0';
163         return name;
164     }
165     ___ auto struct zzip_file_header header;
166     if (zzip_entry_fread_file_header (entry, &header) &&
167         (( len = zzip_file_header_namlen(&header) )))
168     {
169         char* name = malloc (len+1);
170         if (! name) return 0;
171         fread (name, 1, len, entry->diskfile);
172         name[len] = '\0';
173         return name;
174     }
175     return 0;
176     ____;____;
177 }
178
179 /* ====================================================================== */
180
181 /** => zzip_entry_findfile
182  *
183  * This function is the first call of all the zip access functions here.
184  * It contains the code to find the first entry of the zip central directory. 
185  * Here we require the stdio handle to represent a real zip file where the
186  * disk_trailer is _last_ in the file area, so that its position would be at 
187  * a fixed offset from the end of the file area if not for the comment field 
188  * allowed to be of variable length (which needs us to do a little search
189  * for the disk_tailer). However, in this simple implementation we disregard
190  * any disk_trailer info telling about multidisk archives, so we just return
191  * a pointer to the first entry in the zip central directory of that file.
192  * 
193  * For an actual means, we are going to search backwards from the end 
194  * of the mmaped block looking for the PK-magic signature of a 
195  * disk_trailer. If we see one then we check the rootseek value to
196  * find the first disk_entry of the root central directory. If we find
197  * the correct PK-magic signature of a disk_entry over there then we 
198  * assume we are done and we are going to return a pointer to that label.
199  *
200  * The return value is a pointer to the first zzip_disk_entry being checked
201  * to be within the bounds of the file area specified by the arguments. If
202  * no disk_trailer was found then null is returned, and likewise we only 
203  * accept a disk_trailer with a seekvalue that points to a disk_entry and 
204  * both parts have valid PK-magic parts. Beyond some sanity check we try to
205  * catch a common brokeness with zip archives that still allows us to find
206  * the start of the zip central directory.
207  */
208 ZZIP_ENTRY* _zzip_restrict
209 zzip_entry_findfirst(FILE* disk)
210 {
211     if (! disk) return 0;
212     fseeko (disk, 0, SEEK_END);
213     ___ zzip_off_t disksize = ftello (disk);
214     if (disksize < (zzip_off_t) sizeof(struct zzip_disk_trailer)) return 0;
215     /* we read out chunks of 8 KiB in the hope to match disk granularity */
216     ___ zzip_off_t pagesize = PAGESIZE; /* getpagesize() */
217     ___ ZZIP_ENTRY* entry = malloc (sizeof(*entry));  if (! entry) return 0;
218     ___ char* buffer = malloc (pagesize);             if (! buffer) goto nomem;
219
220     assert (pagesize/2 > (zzip_off_t) sizeof (struct zzip_disk_trailer));
221     /* at each step, we will fread a pagesize block which overlaps with the
222      * previous read by means of pagesize/2 step at the end of the while(1) */
223     ___ zzip_off_t mapoffs = disksize &~ (pagesize-1);
224     ___ zzip_off_t mapsize = disksize - mapoffs;
225     if (mapoffs && mapsize < pagesize/2) { 
226         mapoffs -= pagesize/2; mapsize += pagesize/2; }
227     while(1) 
228     {
229         fseeko (disk, mapoffs, SEEK_SET);
230         fread (buffer, 1, mapsize, disk);
231         ___ char* p = buffer + mapsize - sizeof(struct zzip_disk_trailer);
232         for (; p >= buffer ; p--)
233         {
234             if (! zzip_disk_trailer_check_magic(p)) continue;
235             ___ zzip_off_t root = 
236                 zzip_disk_trailer_rootseek ((struct zzip_disk_trailer*)p);
237             if ((char*) root > p) 
238             {   /* the first disk_entry is after the disk_trailer? can't be! */
239                 zzip_off_t rootsize =
240                     zzip_disk_trailer_rootsize ((struct zzip_disk_trailer*)p);
241                 if (rootsize > mapoffs) continue;
242                 /* a common brokeness that can be fixed: we just assume that 
243                  * the central directory was written directly before : */
244                 root = mapoffs - rootsize;
245             }
246             assert (0 <= root && root < mapsize);
247             fseeko (disk, root, SEEK_SET);
248             fread (disk_(entry), 1, sizeof(*disk_(entry)), disk);
249             if (zzip_disk_entry_check_magic(entry)) 
250             {
251                 free (buffer);
252                 entry->headseek = root;
253                 entry->diskfile = disk;
254                 entry->disksize = disksize;
255                 ___ zzip_size_t tailsize = 
256                     zzip_disk_entry_sizeof_tails (disk_(entry));
257                 if (!( entry->tail = malloc (tailsize+1) )) goto nomem;
258                 fread (entry->tail, 1, tailsize, disk);
259                 entry->tailalloc = tailsize+1;
260                 return entry; ____;
261             }
262             ____;
263         } ____;
264         if (! mapoffs) break;             assert (mapsize >= pagesize/2);
265         mapoffs -= pagesize/2;            /* mapsize += pagesize/2; */
266         mapsize = pagesize;               /* if (mapsize > pagesize) ... */
267         if (disksize - mapoffs > 64*1024) break;
268     }
269     free (buffer);
270  nomem:
271     free (entry);                         ____;____;____;____;____;____;
272     return 0;
273 }
274
275 /** => zzip_entry_findfile
276  *
277  * This function takes an existing "entry" in the central root directory
278  * (e.g. from zzip_entry_findfirst) and moves it to point to the next entry.
279  * On error it returns 0, otherwise the old entry. If no further match is
280  * found then null is returned and the entry already free()d. If you want
281  * to stop searching for matches before that case then please call 
282  * => zzip_entry_free on the cursor struct ZZIP_ENTRY.
283  */
284 ZZIP_ENTRY* _zzip_restrict
285 zzip_entry_findnext(ZZIP_ENTRY* _zzip_restrict entry)
286 {
287     if (! entry) return entry;
288     ___ zzip_off_t seek = 
289         entry->headseek + zzip_disk_entry_sizeto_end (disk_(entry));
290     if (seek + (zzip_off_t) sizeof(*disk_(entry)) > entry->disksize) goto err;
291     fseeko (entry->diskfile, seek, SEEK_SET);
292     fread (disk_(entry), 1, sizeof(*disk_(entry)), entry->diskfile);
293     entry->headseek = seek;
294     ___ zzip_off_t tailsize = zzip_disk_entry_sizeof_tails (disk_(entry));
295     if (tailsize+1 > entry->tailalloc) 
296     {
297         char* newtail = realloc (entry->tail, tailsize+1);
298         if (! newtail) goto err; 
299         entry->tail = newtail;
300         entry->tailalloc = tailsize+1;
301     }
302     fread (entry->tail, 1, tailsize, entry->diskfile);
303     return entry; ____;
304  err:
305     zzip_entry_free (entry);
306     return 0; ____;
307 }
308
309 /** => zzip_entry_findfile
310  * this function releases the malloc()ed areas needed for zzip_entry, the
311  * pointer is invalid afterwards. This function has #define synonyms of
312  * zzip_entry_findlast(), zzip_entry_findlastfile(), zzip_entry_findlastmatch()
313  */
314 int
315 zzip_entry_free(ZZIP_ENTRY* entry)
316 {
317     if (! entry) return 0;
318     free (entry->tail);
319     free (entry);
320     return 1;
321 }
322
323 /** search for files in the (fseeko) zip central directory
324  *
325  * This function is given a filename as an additional argument, to find the 
326  * disk_entry matching a given filename. The compare-function is usually 
327  * strcmp or strcasecmp or perhaps strcoll, if null then strcmp is used. 
328  * - use null as argument for "old"-entry when searching the first 
329  * matching entry, otherwise the last returned value if you look for other
330  * entries with a special "compare" function (if null then a doubled search
331  * is rather useless with this variant of _findfile). If no further entry is
332  * found then null is returned and any "old"-entry gets already free()d.
333  */
334 ZZIP_ENTRY* _zzip_restrict
335 zzip_entry_findfile(FILE* disk, char* filename, 
336                     ZZIP_ENTRY* _zzip_restrict entry, 
337                     zzip_strcmp_fn_t compare)
338 {
339     if (! filename || ! disk) return 0;
340     entry = ! entry ? zzip_entry_findfirst (disk) 
341         : zzip_entry_findnext (entry);
342
343     if (! compare) compare = (zzip_strcmp_fn_t)(strcmp);
344     for (; entry ; entry = zzip_entry_findnext (entry))
345     {
346         /* filenames within zip files are often not null-terminated! */
347         char* realname = zzip_entry_strdup_name (entry);
348         if (realname && ! compare(filename, realname))
349         {
350             free (realname);
351             return entry;
352         }
353         free (realname);
354     }
355     return 0;
356 }
357
358 #ifdef ZZIP_HAVE_FNMATCH_H
359 #define _zzip_fnmatch fnmatch
360 # ifdef FNM_CASEFOLD
361 # define _zzip_fnmatch_CASEFOLD FNM_CASEFOLD
362 # else
363 # define _zzip_fnmatch_CASEFOLD 0
364 # endif
365 #else
366 # define _zzip_fnmatch_CASEFOLD 0
367 /* if your system does not have fnmatch, we fall back to strcmp: */
368 static int _zzip_fnmatch(char* pattern, char* string, int flags)
369
370     puts ("<zzip:strcmp>");
371     return strcmp (pattern, string); 
372 }
373 #endif
374
375 /** => zzip_entry_findfile
376  *
377  * This function uses a compare-function with an additional argument
378  * and it is called just like fnmatch(3) from POSIX.2 AD:1993), i.e.
379  * the argument filespec first and the ziplocal filename second with
380  * the integer-flags put in as third to the indirect call. If the
381  * platform has fnmatch available then null-compare will use that one
382  * and otherwise we fall back to mere strcmp, so if you need fnmatch
383  * searching then please provide an implementation somewhere else.
384  * - use null as argument for "after"-entry when searching the first 
385  * matching entry, or the last disk_entry return-value to find the
386  * next entry matching the given filespec. If no further entry is
387  * found then null is returned and any "old"-entry gets already free()d.
388  */
389 ZZIP_ENTRY* _zzip_restrict
390 zzip_entry_findmatch(FILE* disk, char* filespec, 
391                      ZZIP_ENTRY* _zzip_restrict entry,
392                      zzip_fnmatch_fn_t compare, int flags)
393 {
394     if (! filespec || ! disk) return 0;
395     entry = ! entry ? zzip_entry_findfirst (disk) 
396         : zzip_entry_findnext (entry);
397
398     if (! compare) compare = (zzip_fnmatch_fn_t) _zzip_fnmatch; 
399     for (; entry ; entry = zzip_entry_findnext (entry))
400     {
401         /* filenames within zip files are often not null-terminated! */
402         char* realname = zzip_entry_strdup_name(entry);
403         if (realname && ! compare(filespec, realname, flags))
404         {
405             free (realname);
406             return entry;
407         }
408         free (realname);
409     }
410     return 0;
411 }
412
413 /* ====================================================================== */
414
415 /**
416  * typedef struct zzip_disk_file ZZIP_ENTRY_FILE;
417  */
418 struct zzip_entry_file /* : zzip_file_header */
419 {
420     struct zzip_file_header header;    /* fopen detected header */
421     ZZIP_ENTRY* entry;                 /* fopen entry */
422     zzip_off_t  data;                  /* for stored blocks */
423     zzip_size_t avail;                 /* memorized for checks on EOF */
424     zzip_size_t compressed;            /* compressed flag and datasize */
425     zzip_size_t dataoff;               /* offset from data start */
426     z_stream zlib;                     /* for inflated blocks */
427     char     buffer[PAGESIZE];         /* work buffer for inflate algorithm */
428 };
429
430 /** open a file within a zip disk for reading
431  *
432  * This function does take an "entry" argument and copies it (or just takes
433  * it over as owner) to a new ZZIP_ENTRY_FILE handle structure. That
434  * structure contains also a zlib buffer for decoding. This function does
435  * seek to the file_header of the given "entry" and validates it for the
436  * data buffer following it. We do also prefetch some data from the data
437  * buffer thereby trying to match the disk pagesize for faster access later.
438  * The => zzip_entry_fread will then read in chunks of pagesizes which is
439  * the size of the internal readahead buffer. If an error occurs then null
440  * is returned.
441  */
442 ZZIP_ENTRY_FILE* _zzip_restrict
443 zzip_entry_fopen (ZZIP_ENTRY* entry, int takeover)
444 {
445     if (! entry) return 0;
446     if (! takeover) 
447     {
448         ZZIP_ENTRY* found = malloc (sizeof(*entry));
449         if (! found) return 0;
450         memcpy (found, entry, sizeof(*entry));
451         found->tail = malloc (found->tailalloc);
452         if (! found->tail) { free (found); return 0; }
453         memcpy (found->tail, entry->tail, entry->tailalloc);
454         entry = found;
455     }
456     ___ ZZIP_ENTRY_FILE* file = malloc(sizeof(*file));
457     if (! file) goto fail1;
458     file->entry = entry;
459     if (! zzip_entry_fread_file_header (entry, &file->header))
460         goto fail2;
461     file->avail = zzip_file_header_usize (&file->header);
462     file->data = zzip_entry_data_offset (entry);
463     file->dataoff = 0;
464
465     if (! file->avail || zzip_file_header_data_stored (&file->header))
466     { file->compressed = 0; return file; }
467
468     file->compressed = zzip_file_header_csize (&file->header);
469     file->zlib.opaque = 0;
470     file->zlib.zalloc = Z_NULL;
471     file->zlib.zfree = Z_NULL;
472
473     ___ zzip_off_t seek = file->data;
474     seek += sizeof(file->buffer); seek -= seek & (sizeof(file->buffer)-1);
475     assert (file->data < seek); /* pre-read to next PAGESIZE boundary... */
476     fseeko (file->entry->diskfile, file->data + file->dataoff, SEEK_SET);
477     file->zlib.next_in = file->buffer;
478     file->zlib.avail_in = fread (file->buffer, 1, seek - file->data,
479                                  file->entry->diskfile);
480     file->dataoff += file->zlib.avail_in; ____;
481
482     if (! zzip_file_header_data_deflated (&file->header) ||
483         inflateInit2 (& file->zlib, -MAX_WBITS) != Z_OK) goto fail2;
484
485     return file;
486  fail2:
487     free (file);
488  fail1:
489     zzip_entry_free (entry);
490     return 0; ____;
491 }
492
493 /** => zzip_entry_fopen
494  *
495  * This function opens a file found by name, so it does a search into
496  * the zip central directory with => zzip_entry_findfile and whatever
497  * is found first is given to => zzip_entry_fopen
498  */
499 ZZIP_ENTRY_FILE* _zzip_restrict
500 zzip_entry_ffile (FILE* disk, char* filename)
501 {
502     ZZIP_ENTRY* entry = zzip_entry_findfile (disk, filename, 0, 0);
503     if (! entry) return 0; else return zzip_entry_fopen (entry, 1);
504 }
505
506
507 /** => zzip_entry_fopen
508  *
509  * This function reads more bytes into the output buffer specified as
510  * arguments. The return value is null on eof or error, the stdio-like
511  * interface can not distinguish between these so you need to check
512  * with => zzip_entry_feof for the difference.
513  */
514 zzip_size_t
515 zzip_entry_fread (void* ptr, zzip_size_t sized, zzip_size_t nmemb,
516                   ZZIP_ENTRY_FILE* file)
517 {
518      zzip_size_t size = sized*nmemb;
519     if (! file->compressed)
520     {
521         if (size > file->avail) size = file->avail;
522         fread (ptr, 1, size, file->entry->diskfile);
523         file->dataoff += size;
524         file->avail -= size;
525         return size;
526     }
527     
528     file->zlib.avail_out = size;
529     file->zlib.next_out = ptr;
530     ___ zzip_size_t total_old = file->zlib.total_out;
531     while (1) 
532     {
533         if (! file->zlib.avail_in) 
534         {   
535             size = file->compressed - file->dataoff;
536             if (size > sizeof(file->buffer)) size = sizeof(file->buffer);
537             /* fseek (file->data + file->dataoff, file->entry->diskfile); */
538             file->zlib.avail_in = fread (file->buffer, 1, size,
539                                          file->entry->diskfile);
540             file->zlib.next_in = file->buffer;
541             file->dataoff += file->zlib.avail_in;
542         }
543         if (! file->zlib.avail_in) return 0;
544         
545         ___ int err = inflate (& file->zlib, Z_NO_FLUSH);
546         if (err == Z_STREAM_END)
547             file->avail = 0;
548         else if (err == Z_OK)
549             file->avail -= file->zlib.total_out - total_old;
550         else
551             return 0;
552         ____;
553         if (file->zlib.avail_out && ! file->zlib.avail_in) continue;
554         return file->zlib.total_out - total_old;
555     }____;
556 }
557
558 /** => zzip_entry_fopen
559  * This function releases any zlib decoder info needed for decompression
560  * and dumps the ZZIP_ENTRY_FILE struct then.
561  */
562 int
563 zzip_entry_fclose (ZZIP_ENTRY_FILE* file)
564 {
565     if (file->compressed)
566         inflateEnd (& file->zlib);
567     zzip_entry_free (file->entry);
568     free (file);
569     return 0;
570 }
571
572 /** => zzip_entry_fopen
573  *
574  * This function allows to distinguish an error from an eof condition. 
575  * Actually, if we found an error but we did already reach eof then we
576  * just keep on saying that it was an eof, so the app can just continue.
577  */ 
578 int
579 zzip_entry_feof (ZZIP_ENTRY_FILE* file)
580 {
581     return ! file || ! file->avail;
582 }