]> granicus.if.org Git - zziplib/blob - zzip/zip.c
patches from Mike Frysinger
[zziplib] / zzip / zip.c
1 /*
2  * Author: 
3  *      Guido Draheim <guidod@gmx.de>
4  *      Tomi Ollila <too@iki.fi>
5  *
6  * Copyright (c) 1999,2000,2001,2002,2003 Guido Draheim
7  *          All rights reserved,
8  *          use under the restrictions of the 
9  *          Lesser GNU General Public License
10  *          or alternatively the restrictions 
11  *          of the Mozilla Public License 1.1
12  */
13
14 #include <zzip/lib.h>                                  /* archive handling */
15 #include <zzip/file.h>
16 #include <zzip/format.h>
17 #include <zzip/fetch.h>
18
19 #include <ctype.h>
20 #include <errno.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <fcntl.h>
24 #ifdef ZZIP_HAVE_SYS_STAT_H
25 #include <sys/stat.h>
26 #endif
27
28 #include <zzip/__mmap.h>
29 #include <zzip/__debug.h>
30
31 #define __sizeof(X) ((zzip_ssize_t)(sizeof(X)))
32
33 /* per default, we use a little hack to correct bad z_rootseek parts */
34 #define ZZIP_CORRECT_ROOTSEEK 1
35
36 #if (__GNUC__ == 3 && __GNUC_MINOR__ >= 3) || (__GNUC__ >= 4)
37 # ifdef DEBUG
38 # warning suppress a warning where the compiler should have optimized instead.
39 # endif
40 #define _255 254
41 #else
42 #define _255 255
43 #endif
44
45 #define ZZIP_DISK64_TRAILER
46
47 #ifdef ZZIP_DISK64_TRAILER
48 struct _disk_trailer {
49     void*         zz_tail;
50     void*         zz_for_correct_rootseek; // ZZIP_CORRECT_ROOTSEEK
51     zzip_off64_t  zz_entries; 
52     zzip_off64_t  zz_finalentries;
53     zzip_off64_t  zz_rootseek;
54     zzip_off64_t  zz_rootsize;
55 };
56 #define _disk_trailer_entries(__p) ((__p)->zz_entries)
57 #define _disk_trailer_localentries(__p) ((__p)->zz_entries)
58 #define _disk_trailer_finalentries(__p) ((__p)->zz_entries)
59 #define _disk_trailer_rootseek(__p) ((__p)->zz_rootseek)
60 #define _disk_trailer_rootsize(__p) ((__p)->zz_rootsize)
61 #define _disk_trailer_set_rootseek(__p,__v) ((__p)->rootseek = (__v))
62
63 #else
64 #define _disk_trailer zzip_disk_trailer
65 #define _disk_trailer_entries zzip_disk_trailer_entries
66 #define _disk_trailer_localentries zzip_disk_trailer_localentries
67 #define _disk_trailer_finalentries zzip_disk_trailer_finalentries
68 #define _disk_trailer_rootseek zzip_disk_trailer_rootseek
69 #define _disk_trailer_rootsize zzip_disk_trailer_rootsize
70 #define _disk_trailer_set_rootseek zzip_disk_trailer_set_rootseek
71 #define __zzip_fetch_disk_trailer __zzip_find_disk_trailer
72 #endif
73
74 /* ---------------------------  internals  -------------------------------- */
75 /* internal functions of zziplib, avoid at all cost, changes w/o warning.
76  * we do export them for debugging purpose and special external tools
77  * which know what they do and which can adapt from version to version
78  */
79
80 int __zzip_fetch_disk_trailer( int fd, zzip_off_t filesize, 
81                               struct _disk_trailer * _zzip_restrict trailer,
82                               zzip_plugin_io_t io);
83 int __zzip_parse_root_directory( int fd, 
84                                  struct _disk_trailer * trailer, 
85                                  struct zzip_dir_hdr ** hdr_return,
86                                  zzip_plugin_io_t io);
87
88 _zzip_inline char* __zzip_aligned4(char* p);
89
90 /* ------------------------  harden routines ------------------------------ */
91
92 #ifdef ZZIP_HARDEN
93 /*
94  * check for inconsistent values in trailer and prefer lower seek value
95  * - we fix values assuming the root directory was written at the end
96  * and it is just before the zip trailer. Therefore, ...
97  */
98 _zzip_inline static void __fixup_rootseek(
99     zzip_off_t offset_of_trailer,
100     struct _disk_trailer* trailer)
101 {
102     if (                    _disk_trailer_rootseek (trailer) >
103         offset_of_trailer - _disk_trailer_rootsize (trailer) &&
104         offset_of_trailer > _disk_trailer_rootsize (trailer))
105     {
106         register zzip_off_t offset;
107         offset = offset_of_trailer - _disk_trailer_rootsize (trailer);
108         _disk_trailer_set_rootseek (trailer, offset);
109         HINT2("new rootseek=%li", (long) _disk_trailer_rootseek (trailer));
110     }
111 }
112 #define __correct_rootseek(A,B,C)
113
114 #elif defined ZZIP_CORRECT_ROOTSEEK
115 /* store the seekvalue of the trailer into the "z_magic" field and with 
116  * a 64bit off_t we overwrite z_disk/z_finaldisk as well. If you change
117  * anything in zziplib or dump the trailer structure then watch out that
118  * these are still unused, so that this code may still (ab)use those. */
119 #define __fixup_rootseek(_offset_of_trailer, _trailer)          \
120                       *(zzip_off_t*)_trailer = _offset_of_trailer;
121 #define __correct_rootseek( _u_rootseek, _u_rootsize, _trailer) \
122     if (_u_rootseek > *(zzip_off_t*)_trailer - _u_rootsize)     \
123         _u_rootseek = *(zzip_off_t*)_trailer - _u_rootsize;
124 #else
125 #define __fixup_rootseek(A,B) 
126 #define __correct_rootseek(A,B,C)
127 #endif
128
129
130 #ifdef DEBUG
131 _zzip_inline static void __debug_dir_hdr (struct zzip_dir_hdr* hdr)
132 {
133     if (sizeof(struct zzip_dir_hdr) > sizeof(struct zzip_disk_entry))
134     { WARN1("internal sizeof-mismatch may break wreakage"); }
135     /*  the internal directory structure is never bigger than the
136      *  external zip central directory space had been beforehand
137      *  (as long as the following assertion holds...) 
138      */
139
140     if (((zzip_off_t)hdr)&3)
141     { NOTE1("this machine's malloc(3) returns sth. not u32-aligned"); }
142     /* we assume that if this machine's malloc has returned a non-aligned 
143      * memory block, then it is actually safe to access misaligned data, and 
144      * since it does only affect the first hdr it should not even bring about
145      * too much of that cpu's speed penalty
146      */
147 }
148 #else
149 #define __debug_dir_hdr(X)
150 #endif
151
152 /* -------------------------- low-level interface -------------------------- */
153
154 #if defined BUFSIZ 
155 #if BUFSIZ == 1024 || BUFSIZ == 512 || BUFSIZ == 256
156 #define ZZIP_BUFSIZ BUFSIZ
157 #endif
158 #endif
159
160 #ifndef ZZIP_BUFSIZ
161 #define ZZIP_BUFSIZ 512
162 /* #define ZZIP_BUFSIZ 64 */ /* for testing */
163 #endif
164
165 /**
166  * This function is used by => zzip_file_open. It tries to find
167  * the zip's central directory info that is usually a few
168  * bytes off the end of the file.
169  */
170 int 
171 __zzip_fetch_disk_trailer(int fd, zzip_off_t filesize, 
172                           struct _disk_trailer * _zzip_restrict trailer,
173                           zzip_plugin_io_t io)
174 {
175 #ifdef DEBUG
176 #define return(val) { e=val; HINT2("%s", zzip_strerror(e)); goto cleanup; }
177 #else
178 #define return(val) { e=val; goto cleanup; }
179 #endif
180     register int e;
181     
182 #ifndef _LOWSTK
183     auto char buffer[2*ZZIP_BUFSIZ];
184     char* buf = buffer;
185 #else
186     char* buf = malloc(2*ZZIP_BUFSIZ);
187 #endif
188     zzip_off_t offset = 0;
189     zzip_ssize_t maplen = 0; /* mmap(),read(),getpagesize() use size_t !! */
190     char* fd_map = 0;
191
192     if (!trailer)
193         { return(EINVAL); }
194   
195     if (filesize < __sizeof(struct zzip_disk_trailer))
196         { return(ZZIP_DIR_TOO_SHORT); }
197           
198     if (!buf)
199         { return(ZZIP_OUTOFMEM); }
200
201     offset = filesize; /* a.k.a. old offset */
202     while(1) /* outer loop */
203     {
204         register unsigned char* mapped;
205
206          if (offset <= 0) { return(ZZIP_DIR_EDH_MISSING); }
207
208          /* trailer cannot be farther away than 64K from fileend */
209          if (filesize-offset > 64*1024) 
210              { return(ZZIP_DIR_EDH_MISSING); }
211
212         /* the new offset shall overlap with the area after the old offset! */
213         if (USE_MMAP && io->fd.sys)
214         {
215             zzip_off_t mapoff = offset;
216             { 
217                 zzip_ssize_t pagesize = _zzip_getpagesize (io->fd.sys);
218                 if (pagesize < ZZIP_BUFSIZ) goto non_mmap; /* an error? */
219                 if (mapoff == filesize && filesize > pagesize) 
220                     mapoff -= pagesize;
221                 if (mapoff < pagesize) {
222                     maplen = (zzip_ssize_t)mapoff + pagesize; mapoff = 0;
223                 } else {               
224                     mapoff -= pagesize; maplen = 2*pagesize; 
225                     if ((zzip_ssize_t)mapoff & (pagesize-1)) { /*only 1. run */
226                         pagesize -= (zzip_ssize_t)mapoff & (pagesize-1);
227                         mapoff += pagesize;
228                         maplen -= pagesize;
229                     }   
230                 }
231                 if (mapoff + maplen > filesize) maplen = filesize - mapoff;
232             }
233
234             fd_map = _zzip_mmap(io->fd.sys, fd, mapoff, (zzip_size_t)maplen);
235             if (fd_map == MAP_FAILED) goto non_mmap;
236             mapped = (unsigned char*) fd_map; offset = mapoff; /* success */
237             HINT3("mapped *%p len=%li", fd_map, (long) maplen);
238         } else {
239         non_mmap:
240             fd_map = 0; /* have no mmap */
241             {
242                 zzip_off_t pagesize = ZZIP_BUFSIZ;
243                 if (offset == filesize && filesize > pagesize)
244                     offset -= pagesize;
245                 if (offset < pagesize) {
246                     maplen = (zzip_ssize_t)offset + pagesize; offset = 0;
247                 } else {
248                     offset -= pagesize; maplen = 2*pagesize;
249                     if ((zzip_ssize_t)offset & (pagesize-1)) { /*on 1st run*/
250                         pagesize -= (zzip_ssize_t)offset & (pagesize-1);
251                         offset += pagesize;
252                         maplen -= pagesize; 
253                     }    
254                 }
255                 if (offset + maplen > filesize) maplen = filesize - offset;
256             }
257             
258             if (io->fd.seeks(fd, offset, SEEK_SET) < 0)
259                 { return(ZZIP_DIR_SEEK); }
260             if (io->fd.read(fd, buf, (zzip_size_t)maplen) < maplen)
261                 { return(ZZIP_DIR_READ); }
262             mapped = (unsigned char*) buf; /* success */
263             HINT5("offs=$%lx len=%li filesize=%li pagesize=%i", 
264                   (long)offset, (long)maplen, (long)filesize, ZZIP_BUFSIZ);
265         }
266
267         {/* now, check for the trailer-magic, hopefully near the end of file */
268             register unsigned char* end = mapped + maplen;
269             register unsigned char* tail;
270             for (tail = end-1; (tail >= mapped); tail--)
271             {
272                 if ((*tail == 'P') && /* quick pre-check for trailer magic */
273                     end-tail >= __sizeof(struct zzip_disk_trailer)-2 &&
274                     zzip_disk_trailer_check_magic(tail))
275                 {
276 #                  ifndef ZZIP_DISK64_TRAILER
277                     /* if the file-comment is not present, it happens
278                        that the z_comment field often isn't either */
279                     if (end-tail >= __sizeof(*trailer))
280                     {
281                         memcpy (trailer, tail, sizeof(*trailer)); 
282                     }else{
283                         memcpy (trailer, tail, sizeof(*trailer)-2);
284                         trailer->z_comment[0] = 0; 
285                         trailer->z_comment[1] = 0;
286                     }
287 #                  else
288                     struct zzip_disk_trailer* orig = (struct zzip_disk_trailer*) tail;
289                     trailer->zz_tail = tail;
290                     trailer->zz_entries = zzip_disk_trailer_localentries (orig);
291                     trailer->zz_finalentries = zzip_disk_trailer_finalentries (orig);
292                     trailer->zz_rootseek = zzip_disk_trailer_rootseek (orig);
293                     trailer->zz_rootsize = zzip_disk_trailer_rootsize (orig);
294 #                  endif
295
296                     __fixup_rootseek (offset + tail-mapped, trailer);
297                     { return(0); }
298                 } else if ((*tail == 'P') && 
299                     end-tail >= __sizeof(struct zzip_disk64_trailer)-2 &&
300                     zzip_disk64_trailer_check_magic(tail))
301                 {
302 #                  ifndef ZZIP_DISK64_TRAILER
303                     return(ZZIP_DIR_LARGEFILE);
304 #                  else
305                     struct zzip_disk64_trailer* orig = (struct zzip_disk64_trailer*) tail;
306                     trailer->zz_tail = tail;
307                     trailer->zz_entries = zzip_disk64_trailer_localentries (orig);
308                     trailer->zz_finalentries = zzip_disk64_trailer_finalentries (orig);
309                     trailer->zz_rootseek = zzip_disk64_trailer_rootseek (orig);
310                     trailer->zz_rootsize = zzip_disk64_trailer_rootsize (orig);
311                     { return(0); }
312 #                  endif
313                 }
314             }
315         }
316         
317          if (USE_MMAP && fd_map) 
318          { 
319              HINT3("unmap *%p len=%li",  fd_map, (long) maplen);
320              _zzip_munmap(io->fd.sys, fd_map, (zzip_size_t)maplen); 
321              fd_map = 0; 
322          }
323     } /*outer loop*/
324                
325  cleanup:
326     if (USE_MMAP && fd_map)
327     { 
328         HINT3("unmap *%p len=%li",  fd_map, (long) maplen);
329         _zzip_munmap(io->fd.sys, fd_map, (zzip_size_t)maplen); 
330     }
331 #   ifdef _LOWSTK
332     free(buf);
333 #   endif
334 #   undef return
335     return e; 
336 }
337
338 /*
339  * making pointer alignments to values that can be handled as structures
340  * is tricky. We assume here that an align(4) is sufficient even for
341  * 64 bit machines. Note that binary operations are not usually allowed
342  * to pointer types but we do need only the lower bits in this implementation,
343  * so we can just cast the value to a long value.
344  */
345 _zzip_inline char* __zzip_aligned4(char* p)
346 {
347 #define aligned4   __zzip_aligned4
348     p += ((long)p)&1;            /* warnings about truncation of a "pointer" */
349     p += ((long)p)&2;            /* to a "long int" may be safely ignored :) */
350     return p;
351 }
352
353 /**
354  * This function is used by => zzip_file_open, it is usually called after
355  * => __zzip_find_disk_trailer. It will parse the zip's central directory
356  * information and create a zziplib private directory table in
357  * memory.
358  */
359 int 
360 __zzip_parse_root_directory(int fd, 
361     struct _disk_trailer * trailer, 
362     struct zzip_dir_hdr ** hdr_return,
363     zzip_plugin_io_t io)
364 {
365     auto struct zzip_disk_entry dirent;
366     struct zzip_dir_hdr * hdr;
367     struct zzip_dir_hdr * hdr0;
368     uint16_t * p_reclen = 0;
369     unsigned long entries;           /* 32bit is enough */
370     zzip_off64_t zz_offset;          /* offset from start of root directory */
371     char* fd_map = 0; 
372     zzip_off64_t zz_fd_gap = 0;
373     zzip_off64_t zz_entries  = _disk_trailer_localentries (trailer);   
374     zzip_off64_t zz_rootsize = _disk_trailer_rootsize (trailer);  
375     zzip_off64_t zz_rootseek = _disk_trailer_rootseek (trailer);
376     __correct_rootseek (zz_rootseek, zz_rootsize, trailer);
377
378     hdr0 = (struct zzip_dir_hdr*) malloc(zz_rootsize);
379     if (!hdr0) 
380         return ZZIP_DIRSIZE;
381     hdr = hdr0;                  __debug_dir_hdr (hdr);
382
383     if (USE_MMAP && io->fd.sys)
384     {
385         zz_fd_gap = zz_rootseek & (_zzip_getpagesize(io->fd.sys)-1) ;
386         HINT4(" fd_gap=%ld, mapseek=0x%lx, maplen=%ld", (long)(zz_fd_gap),
387               (long)(zz_rootseek-zz_fd_gap), (long)(zz_rootsize+zz_fd_gap));
388         fd_map = _zzip_mmap(io->fd.sys, fd,
389                             zz_rootseek-zz_fd_gap, zz_rootsize+zz_fd_gap);
390         /* if mmap failed we will fallback to seek/read mode */
391         if (fd_map == MAP_FAILED) { 
392             NOTE2("map failed: %s",strerror(errno)); 
393             fd_map=0; 
394         }else{
395             HINT3("mapped *%p len=%li", fd_map, (long)(zz_rootsize+zz_fd_gap));
396         }
397     }
398
399     for (entries=zz_entries, zz_offset=0; entries; entries--)
400     {
401         register struct zzip_disk_entry * d;
402         uint16_t u_extras, u_comment, u_namlen;
403
404         if (fd_map) 
405         { d = (void*)(fd_map+zz_fd_gap+zz_offset); } /* fd_map+fd_gap==u_rootseek */
406         else
407         {
408             if (io->fd.seeks(fd, zz_rootseek+zz_offset, SEEK_SET) < 0)
409                 return ZZIP_DIR_SEEK;
410             if (io->fd.read(fd, &dirent, sizeof(dirent)) < __sizeof(dirent))
411                 return ZZIP_DIR_READ;
412             d = &dirent;
413         }
414
415         if ((zzip_off64_t)(zz_offset+sizeof(*d)) > zz_rootsize ||
416             (zzip_off64_t)(zz_offset+sizeof(*d)) < 0)
417         { FAIL4("%li's entry stretches beyond root directory (O:%li R:%li)", 
418                 (long)entries, (long)(zz_offset), (long)zz_rootsize); break;}
419
420 #       if 0 && defined DEBUG
421         zzip_debug_xbuf ((unsigned char*) d, sizeof(*d) + 8);
422 #       endif        
423         
424         u_extras  = zzip_disk_entry_get_extras (d);
425         u_comment = zzip_disk_entry_get_comment (d); 
426         u_namlen  = zzip_disk_entry_get_namlen (d); 
427         HINT5("offset=0x%lx, size %ld, dirent *%p, hdr %p\n",
428               (long)(zz_offset+zz_rootseek), (long)zz_rootsize, d, hdr);
429
430         /* writes over the read buffer, Since the structure where data is
431            copied is smaller than the data in buffer this can be done.
432            It is important that the order of setting the fields is considered
433            when filling the structure, so that some data is not trashed in
434            first structure read.
435            at the end the whole copied list of structures  is copied into
436            newly allocated buffer */
437         hdr->d_crc32 = zzip_disk_entry_get_crc32 (d);
438         hdr->d_csize = zzip_disk_entry_get_csize (d); 
439         hdr->d_usize = zzip_disk_entry_get_usize (d);
440         hdr->d_off   = zzip_disk_entry_get_offset (d);
441         hdr->d_compr = zzip_disk_entry_get_compr (d);
442         if (hdr->d_compr > _255) hdr->d_compr = 255;
443
444         if ((zzip_off64_t)(zz_offset+sizeof(*d) + u_namlen) > zz_rootsize ||
445             (zzip_off64_t)(zz_offset+sizeof(*d) + u_namlen) < 0)
446         { FAIL4("%li's name stretches beyond root directory (O:%li N:%li)", 
447                 (long)entries, (long)(zz_offset), (long)(u_namlen)); break; }
448
449         if (fd_map) 
450         {  memcpy(hdr->d_name, fd_map+zz_fd_gap + zz_offset+sizeof(*d), u_namlen); }
451         else { io->fd.read(fd, hdr->d_name, u_namlen); }
452         hdr->d_name[u_namlen] = '\0'; 
453         hdr->d_namlen = u_namlen;
454     
455         /* update offset by the total length of this entry -> next entry */
456         zz_offset += sizeof(*d) + u_namlen + u_extras + u_comment;
457     
458         if (zz_offset > zz_rootsize)
459         { FAIL3("%li's entry stretches beyond root directory (O:%li)", 
460                 (long)entries, (long)(zz_offset)); entries--; break; }
461
462         HINT5("file %ld { compr=%d crc32=$%x offset=%d", 
463               (long)entries,  hdr->d_compr, hdr->d_crc32, hdr->d_off);
464         HINT5("csize=%d usize=%d namlen=%d extras=%d", 
465               hdr->d_csize, hdr->d_usize, u_namlen, u_extras);
466         HINT5("comment=%d name='%s' %s <sizeof %d> } ", 
467               u_comment, hdr->d_name, "",(int) sizeof(*d));
468   
469         p_reclen = &hdr->d_reclen;
470     
471         {   register char* p = (char*) hdr; 
472             register char* q = aligned4 (p + sizeof(*hdr) + u_namlen + 1);
473             *p_reclen = (uint16_t)(q - p);
474             hdr = (struct zzip_dir_hdr*) q;
475         }
476     }/*for*/
477     
478     if (USE_MMAP && fd_map) 
479     {
480         HINT3("unmap *%p len=%li",   fd_map, (long)(zz_rootsize+zz_fd_gap));
481         _zzip_munmap(io->fd.sys, fd_map, zz_rootsize+zz_fd_gap);
482     }
483     
484     if (p_reclen)
485     {
486         *p_reclen = 0; /* mark end of list */
487     
488         if (hdr_return) 
489             *hdr_return = hdr0;
490     } /* else zero (sane) entries */
491     return (entries ?  ZZIP_CORRUPTED : 0);
492 }
493
494 /* ------------------------- high-level interface ------------------------- */
495
496 #ifndef O_BINARY
497 #define O_BINARY 0
498 #endif
499
500 static zzip_strings_t* zzip_get_default_ext(void)
501 {
502     static zzip_strings_t ext [] =
503     {
504        ".zip", ".ZIP", /* common extension */
505 #  ifdef ZZIP_USE_ZIPLIKES
506        ".pk3", ".PK3", /* ID Software's Quake3 zipfiles */
507        ".jar", ".JAR", /* Java zipfiles */ 
508 #  endif
509        0
510     };
511
512     return ext;
513 }
514
515 /**
516  * allocate a new ZZIP_DIR handle and do basic 
517  * initializations before usage by => zzip_dir_fdopen
518  * => zzip_dir_open => zzip_file_open or through
519  * => zzip_open
520  * (ext==null flags uses { ".zip" , ".ZIP" } )
521  * (io ==null flags use of posix io defaults)
522  */
523 ZZIP_DIR*
524 zzip_dir_alloc_ext_io (zzip_strings_t* ext, const zzip_plugin_io_t io)
525 {
526     ZZIP_DIR* dir;
527     if ((dir = (ZZIP_DIR *)calloc(1, sizeof(*dir))) == NULL)
528         return 0; 
529
530     /* dir->fileext is currently unused - so what, still initialize it */
531     dir->fileext = ext ? ext : zzip_get_default_ext();
532     dir->io = io ? io : zzip_get_default_io ();
533     return dir;
534 }
535
536 /** => zzip_dir_alloc_ext_io
537  * this function is obsolete - it was generally used for implementation
538  * and exported to let other code build on it. It is now advised to
539  * use => zzip_dir_alloc_ext_io now on explicitly, just set that second
540  * argument to zero to achieve the same functionality as the old style.
541  */
542 ZZIP_DIR*
543 zzip_dir_alloc (zzip_strings_t* fileext)
544 {
545     return zzip_dir_alloc_ext_io (fileext, 0);
546 }
547
548 /**
549  * will free the zzip_dir handle unless there are still 
550  * zzip_files attached (that may use its cache buffer).
551  * This is the inverse of => zzip_dir_alloc , and both
552  * are helper functions used implicitly in other zzipcalls
553  * e.g. => zzip_dir_close = zzip_close 
554  *
555  * returns zero on sucess
556  * returns the refcount when files are attached.
557  */
558 int 
559 zzip_dir_free(ZZIP_DIR * dir)
560 {
561     if (dir->refcount)
562         return (dir->refcount); /* still open files attached */
563
564     if (dir->fd >= 0)      dir->io->fd.close(dir->fd);
565     if (dir->hdr0)         free(dir->hdr0);
566     if (dir->cache.fp)     free(dir->cache.fp);
567     if (dir->cache.buf32k) free(dir->cache.buf32k);
568     if (dir->realname)     free(dir->realname);
569     free(dir);
570     return 0;
571 }
572
573 /** => zzip_dir_free
574  * It will also => free(2) the => ZZIP_DIR-handle given. 
575  * the counterpart for => zzip_dir_open
576  * see also => zzip_dir_free
577  */
578 int 
579 zzip_dir_close(ZZIP_DIR * dir)
580 {
581     dir->refcount &=~ 0x10000000; /* explicit dir close */
582     return zzip_dir_free(dir);
583 }
584
585 /** 
586  * used by the => zzip_dir_open and zzip_opendir(2) call. Opens the
587  * zip-archive as specified with the fd which points to an
588  * already openend file. This function then search and parse
589  * the zip's central directory.
590  *  
591  * NOTE: refcount is zero, so an _open/_close pair will also delete 
592  *       this _dirhandle 
593  */
594 ZZIP_DIR * 
595 zzip_dir_fdopen(int fd, zzip_error_t * errcode_p)
596 {
597     return zzip_dir_fdopen_ext_io(fd, errcode_p, 0, 0);
598 }
599
600 static zzip_error_t __zzip_dir_parse (ZZIP_DIR* dir); /* forward */
601
602 /** => zzip_dir_fdopen
603  * this function uses explicit ext and io instead of the internal 
604  * defaults, setting these to zero is equivalent to => zzip_dir_fdopen
605  */
606 ZZIP_DIR * 
607 zzip_dir_fdopen_ext_io(int fd, zzip_error_t * errcode_p,
608                        zzip_strings_t* ext, const zzip_plugin_io_t io)
609 {
610     zzip_error_t rv;
611     ZZIP_DIR * dir;
612
613     if ((dir = zzip_dir_alloc_ext_io (ext, io)) == NULL)
614         { rv = ZZIP_OUTOFMEM; goto error; }
615
616     dir->fd = fd;
617     if ((rv = __zzip_dir_parse (dir)))
618         goto error;
619
620     dir->hdr = dir->hdr0;
621     dir->refcount |= 0x10000000; 
622   
623     if (errcode_p) *errcode_p = rv;
624     return dir;
625 error:
626     if (dir) zzip_dir_free(dir);
627     if (errcode_p) *errcode_p = rv;
628     return NULL;
629 }
630
631 static zzip_error_t
632 __zzip_dir_parse (ZZIP_DIR* dir)
633 {
634     zzip_error_t rv;
635     zzip_off_t filesize;
636     struct _disk_trailer trailer;
637     /* if (! dir || dir->fd < 0) 
638      *     { rv = EINVAL; goto error; } 
639      */
640
641     HINT2("------------------ fd=%i", (int) dir->fd);
642     if ((filesize = dir->io->fd.filesize(dir->fd)) < 0)
643         { rv = ZZIP_DIR_STAT; goto error; }
644
645     HINT2("------------------ filesize=%ld", (long) filesize);
646     if ((rv = __zzip_fetch_disk_trailer(dir->fd, filesize, &trailer, 
647                                        dir->io)) != 0)
648         { goto error; }
649                 
650     HINT5("directory = { entries= %ld/%ld, size= %ld, seek= %ld } ", 
651           (long) _disk_trailer_localentries (&trailer),
652           (long) _disk_trailer_finalentries (&trailer),
653           (long) _disk_trailer_rootsize (&trailer),
654           (long) _disk_trailer_rootseek (&trailer));
655     
656     if ( (rv = __zzip_parse_root_directory(dir->fd, &trailer, &dir->hdr0, 
657                                            dir->io)) != 0)
658         { goto error; }
659  error:
660     return rv;
661 }
662
663 /**
664  * will attach a .zip extension and tries to open it
665  * the with => open(2). This is a helper function for
666  * => zzip_dir_open, => zzip_opendir and => zzip_open.
667  */
668 int
669 __zzip_try_open(zzip_char_t* filename, int filemode, 
670                 zzip_strings_t* ext, zzip_plugin_io_t io)
671 {
672     auto char file[PATH_MAX];
673     int fd;
674     zzip_size_t len = strlen (filename);
675     
676     if (len+4 >= PATH_MAX) return -1;
677     memcpy(file, filename, len+1);
678
679     if (!io) io = zzip_get_default_io();
680     if (!ext) ext = zzip_get_default_ext();
681
682     for ( ; *ext ; ++ext)
683     {
684         strcpy (file+len, *ext);
685         fd = io->fd.open(file, filemode);
686         if (fd != -1) return fd;
687     }
688     return -1;
689 }    
690
691 /**
692  * Opens the zip-archive (if available).
693  * the two ext_io arguments will default to use posix io and 
694  * a set of default fileext that can atleast add .zip ext itself.
695  */
696 ZZIP_DIR* 
697 zzip_dir_open(zzip_char_t* filename, zzip_error_t* e)
698 {
699     return zzip_dir_open_ext_io (filename, e, 0, 0);
700 }
701
702 /** => zzip_dir_open
703  * this function uses explicit ext and io instead of the internal 
704  * defaults. Setting these to zero is equivalent to => zzip_dir_open
705  */
706 ZZIP_DIR* 
707 zzip_dir_open_ext_io(zzip_char_t* filename, zzip_error_t* e,
708                      zzip_strings_t* ext, zzip_plugin_io_t io)
709 {
710     int fd;
711
712     if (!io) io = zzip_get_default_io();
713     if (!ext) ext = zzip_get_default_ext();
714
715     fd = io->fd.open(filename, O_RDONLY|O_BINARY);
716     if (fd != -1) 
717       { return zzip_dir_fdopen_ext_io(fd, e, ext, io); }
718     else
719     {
720         fd = __zzip_try_open(filename, O_RDONLY|O_BINARY, ext, io);
721         if (fd != -1) 
722           { return zzip_dir_fdopen_ext_io(fd, e, ext, io); }
723         else
724         {
725             if (e) { *e = ZZIP_DIR_OPEN; } 
726             return 0; 
727         }
728     }
729 }
730
731 /** => zzip_dir_open
732  * fills the dirent-argument with the values and 
733  * increments the read-pointer of the dir-argument.
734  * 
735  * returns 0 if there no entry (anymore).
736  */
737 int
738 zzip_dir_read(ZZIP_DIR * dir, ZZIP_DIRENT * d )
739 {
740     if (! dir || ! dir->hdr || ! d) return 0;
741
742     d->d_compr = dir->hdr->d_compr;
743     d->d_csize = dir->hdr->d_csize;
744     d->st_size = dir->hdr->d_usize;
745     d->d_name  = dir->hdr->d_name;
746
747     if (! dir->hdr->d_reclen) 
748     { dir->hdr = 0; }
749     else  
750     { dir->hdr = (struct zzip_dir_hdr *)((char *)dir->hdr + dir->hdr->d_reclen); }
751   
752     return 1;
753 }
754
755 /* 
756  * Local variables:
757  * c-file-style: "stroustrup"
758  * End:
759  */