1 
2 /*
3  * Author:
4  *      Guido Draheim <guidod@gmx.de>
5  *      Tomi Ollila <Tomi.Ollila@iki.fi>
6  *
7  * Copyright (c) Guido Draheim, use under copyleft (LGPL,MPL)
8  */
9 
10 #include <zzip/lib.h>           /* exported... */
11 #include <zzip/file.h>
12 
13 #include <string.h>
14 #include <sys/stat.h>
15 #include <errno.h>
16 #include <stdlib.h>
17 #include <ctype.h>
18 
19 #include <zzip/format.h>
20 #include <zzip/fetch.h>
21 #include <zzip/zzip32.h>
22 #include <zzip/__debug.h>
23 
24 #if 0
25 # if defined ZZIP_HAVE_IO_H
26 # include <io.h>                /* tell */
27 # else
28 # define tell(fd) lseek(fd,0,SEEK_CUR)
29 # endif
30 #else
31 #define tells(fd) seeks(fd,0,SEEK_CUR)
32 #endif
33 
34 /** end usage.
35  * This function is the direct call of => zzip_close(fp). It will cleanup the
36  * inflate-portion of => zlib and free the structure given.
37  *
38  * it is called quite from the error-cleanup parts
39  * of the various => _open functions.
40  *
41  * the .refcount is decreased and if zero the fp->dir is closed just as well.
42  */
43 int
zzip_file_close(ZZIP_FILE * fp)44 zzip_file_close(ZZIP_FILE * fp)
45 {
46     auto int self;
47     ZZIP_DIR *dir = fp->dir;
48 
49     if (fp->method)
50         inflateEnd(&fp->d_stream);      /* inflateEnd() can be called many times */
51 
52     if (dir->cache.locked == NULL)
53         dir->cache.locked = &self;
54 
55     if (fp->buf32k)
56     {
57         if (dir->cache.locked == &self && dir->cache.buf32k == NULL)
58             dir->cache.buf32k = fp->buf32k;
59         else
60             free(fp->buf32k);
61     }
62 
63     if (dir->currentfp == fp)
64         dir->currentfp = NULL;
65 
66     dir->refcount--;
67     /* ease to notice possible dangling reference errors */
68     memset(fp, 0, sizeof(*fp));
69 
70     if (dir->cache.locked == &self && dir->cache.fp == NULL)
71         dir->cache.fp = fp;
72     else
73         free(fp);
74 
75     if (dir->cache.locked == &self)
76         dir->cache.locked = NULL;
77 
78     if (! dir->refcount)
79         return zzip_dir_close(dir);
80     else
81         return 0;
82 }
83 
84 
85 static int
zzip_file_saveoffset(ZZIP_FILE * fp)86 zzip_file_saveoffset(ZZIP_FILE * fp)
87 {
88     if (fp)
89     {
90         int fd = fp->dir->fd;
91         zzip_off_t off = fp->io->fd.seeks(fd, 0, SEEK_CUR);
92 
93         if (off < 0)
94             return -1;
95 
96         fp->offset = off;
97     }
98     return 0;
99 }
100 
101 
102 /* user-definition */
103 #ifndef ZZIP_BACKSLASH_DIRSEP
104 #if defined HAVE_WINDOWS_H || defined ZZIP_HAVE_WINDOWS_H || defined _WIN32
105 #define ZZIP_BACKSLASH_DIRSEP 1
106 #elif defined ZZIP_CHECK_BACKSLASH_DIRSEPARATOR
107 #define ZZIP_BACKSLASH_DIRSEP 1
108 #else
109 #define ZZIP_BACKSLASH_DIRSEP 0
110 #endif
111 #endif
112 
113 static zzip_char_t*
strrchr_basename(zzip_char_t * name)114 strrchr_basename(zzip_char_t* name)
115 {
116     register zzip_char_t *n = strrchr(name, '/');
117     if (n) return n + 1;
118     return name;
119 }
120 
121 static zzip_char_t*
dirsep_basename(zzip_char_t * name)122 dirsep_basename(zzip_char_t* name)
123 {
124     register zzip_char_t *n = strrchr(name, '/');
125 
126     if (ZZIP_BACKSLASH_DIRSEP)
127     {
128         register zzip_char_t *m = strrchr(name, '\\');
129         if (!n || (m && n < m))
130             n = m;
131     }
132 
133     if (n) return n + 1;
134     return name;
135 }
136 
137 #if defined strcasecmp
138 #define dirsep_strcasecmp strcasecmp
139 #else
140 static int
dirsep_strcasecmp(zzip_char_t * s1,zzip_char_t * s2)141 dirsep_strcasecmp(zzip_char_t * s1, zzip_char_t * s2)
142 {
143     /* ASCII tolower - including mapping of backslash in normal slash */
144     static const char mapping[] = "@abcdefghijklmnopqrstuvwxyz[/]^_";
145     int c1, c2;
146 
147     while (*s1 && *s2)
148     {
149         c1 = (int) (unsigned char) *s1;
150         c2 = (int) (unsigned char) *s2;
151         if ((c1 & 0xE0) == 0x40)
152             c1 = mapping[c1 & 0x1f];
153         if ((c2 & 0xE0) == 0x40)
154             c2 = mapping[c2 & 0x1f];
155         if (c1 != c2)
156             return (c1 - c2);
157         s1++;
158         s2++;
159     }
160 
161     return (((int) (unsigned char) *s1) - ((int) (unsigned char) *s2));
162 }
163 #endif
164 
165 static int zzip_inflate_init(ZZIP_FILE *, struct zzip_dir_hdr *);
166 
167 /** start usage.
168  * This function opens an => ZZIP_FILE from an already open => ZZIP_DIR handle.
169  * Since we have a chance to reuse a cached => buf32k and => ZZIP_FILE memchunk
170  * this is the best choice to unpack multiple files.
171  *
172  * Note: the zlib supports 2..15 bit windowsize, hence we provide a 32k
173  *       memchunk here... just to be safe.
174  *
175  * On error it returns null and sets errcode in the ZZIP_DIR.
176  */
177 ZZIP_FILE *
zzip_file_open(ZZIP_DIR * dir,zzip_char_t * name,int o_mode)178 zzip_file_open(ZZIP_DIR * dir, zzip_char_t * name, int o_mode)
179 {
180     auto int self;
181     zzip_error_t err = 0;
182     struct zzip_file *fp = 0;
183     struct zzip_dir_hdr *hdr = dir->hdr0;
184     int (*filename_strcmp) (zzip_char_t *, zzip_char_t *);
185     zzip_char_t* (*filename_basename)(zzip_char_t*);
186 
187     filename_strcmp = (o_mode & ZZIP_CASELESS) ? dirsep_strcasecmp : strcmp;
188     filename_basename = (o_mode & ZZIP_CASELESS) ? dirsep_basename : strrchr_basename;
189 
190     if (! dir)
191         return NULL;
192     if (! dir->fd || dir->fd == -1)
193         { dir->errcode = EBADF; return NULL; }
194     if (! hdr)
195         { dir->errcode = ENOENT; return NULL; }
196 
197     if (o_mode & ZZIP_NOPATHS)
198         name = filename_basename(name);
199 
200     while (1)
201     {
202         register zzip_char_t *hdr_name = hdr->d_name;
203 
204         if (o_mode & ZZIP_NOPATHS)
205             hdr_name = filename_basename(hdr_name);
206 
207         HINT4("name='%s', compr=%d, size=%d\n",
208               hdr->d_name, hdr->d_compr, hdr->d_usize);
209 
210         if (! filename_strcmp(hdr_name, name))
211         {
212             switch (hdr->d_compr)
213             {
214             case 0:            /* store */
215             case 8:            /* inflate */
216                 break;
217             default:
218                 { err = ZZIP_UNSUPP_COMPR; goto error; }
219             }
220 
221             if (dir->cache.locked == NULL)
222                 dir->cache.locked = &self;
223 
224             if (dir->cache.locked == &self && dir->cache.fp)
225             {
226                 fp = dir->cache.fp;
227                 dir->cache.fp = NULL;
228                 /* memset(zfp, 0, sizeof *fp); cleared in zzip_file_close() */
229             } else
230             {
231                 if (! (fp = (ZZIP_FILE *) calloc(1, sizeof(*fp))))
232                     { err =  ZZIP_OUTOFMEM; goto error; }
233             }
234 
235             fp->dir = dir;
236             fp->io = dir->io;
237             dir->refcount++;
238 
239             if (dir->cache.locked == &self && dir->cache.buf32k)
240             {
241                 fp->buf32k = dir->cache.buf32k;
242                 dir->cache.buf32k = NULL;
243             } else
244             {
245                 if (! (fp->buf32k = (char *) malloc(ZZIP_32K)))
246                     { err = ZZIP_OUTOFMEM; goto error; }
247             }
248 
249             if (dir->cache.locked == &self)
250                 dir->cache.locked = NULL;
251             /*
252              * In order to support simultaneous open files in one zip archive
253              * we'll fix the fd offset when opening new file/changing which
254              * file to read...
255              */
256 
257             if (zzip_file_saveoffset(dir->currentfp) < 0)
258                 { err = ZZIP_DIR_SEEK; goto error; }
259 
260             fp->offset = hdr->d_off;
261             dir->currentfp = fp;
262 
263             if (dir->io->fd.seeks(dir->fd, hdr->d_off, SEEK_SET) < 0)
264                 { err = ZZIP_DIR_SEEK; goto error; }
265 
266             {
267                 /* skip local header - should test tons of other info,
268                  * but trust that those are correct */
269                 zzip_ssize_t dataoff;
270                 struct zzip_file_header *p = (void *) fp->buf32k;
271 
272                 dataoff = dir->io->fd.read(dir->fd, (void *) p, sizeof(*p));
273                 if (dataoff < (zzip_ssize_t) sizeof(*p))
274                     { err = ZZIP_DIR_READ;  goto error; }
275                 if (! zzip_file_header_check_magic(p))   /* PK\3\4 */
276                     { err = ZZIP_CORRUPTED; goto error; }
277 
278                 dataoff = zzip_file_header_sizeof_tail(p);
279 
280                 if (dir->io->fd.seeks(dir->fd, dataoff, SEEK_CUR) < 0)
281                     { err = ZZIP_DIR_SEEK; goto error; }
282 
283                 fp->dataoffset = dir->io->fd.tells(dir->fd);
284                 fp->usize = hdr->d_usize;
285                 fp->csize = hdr->d_csize;
286             }
287 
288             err = zzip_inflate_init(fp, hdr);
289             if (err)
290                 goto error;
291 
292             return fp;
293         } else
294         {
295             if (hdr->d_reclen == 0)
296                 break;
297             hdr = (struct zzip_dir_hdr *) ((char *) hdr + hdr->d_reclen);
298         }                       /*filename_strcmp */
299     }                           /*forever */
300     dir->errcode = ZZIP_ENOENT;
301     return NULL;
302   error:
303     if (fp)
304         zzip_file_close(fp);
305     dir->errcode = err;
306     return NULL;
307 }
308 
309 /** internal.
310  *  call => inflateInit and setup fp's iterator variables,
311  *  used by lowlevel => _open functions.
312  */
313 static int
zzip_inflate_init(ZZIP_FILE * fp,struct zzip_dir_hdr * hdr)314 zzip_inflate_init(ZZIP_FILE * fp, struct zzip_dir_hdr *hdr)
315 {
316     int err;
317 
318     fp->method = hdr->d_compr;
319     fp->restlen = hdr->d_usize;
320 
321     if (fp->method)
322     {
323         memset(&fp->d_stream, 0, sizeof(fp->d_stream));
324 
325         err = inflateInit2(&fp->d_stream, -MAX_WBITS);
326         if (err != Z_OK)
327             goto error;
328 
329         fp->crestlen = hdr->d_csize;
330     }
331     return 0;
332   error:
333     if (fp)
334         zzip_file_close(fp);
335     return err;
336 }
337 
338 /** end usage.
339  * This function closes the given ZZIP_FILE handle.
340  *
341  * If the ZZIP_FILE wraps a normal stat'fd then it is just that int'fd
342  * that is being closed and the otherwise empty ZZIP_FILE gets freed.
343  */
344 int
zzip_fclose(ZZIP_FILE * fp)345 zzip_fclose(ZZIP_FILE * fp)
346 {
347     if (! fp)
348         return 0;
349     if (! fp->dir)               /* stat fd */
350         { int r = fp->io->fd.close(fp->fd); free(fp); return r; }
351     else
352         return zzip_file_close(fp);
353 }
354 
355 /** => zzip_fclose
356  */
357 int
zzip_close(ZZIP_FILE * fp)358 zzip_close(ZZIP_FILE * fp)
359 {
360     return zzip_fclose(fp);
361 }
362 
363 /** read data.
364  * This function reads data from zip-contained file.
365  *
366  * This fuction works like => read(2) and will fill the given buffer with bytes from
367  * the opened file. It will return the number of bytes read, so if the => EOF
368  * is encountered you will be prompted with the number of bytes actually read.
369  *
370  * This is the routines that needs the => buf32k buffer, and it would have
371  * need for much more polishing but it does already work quite well.
372  *
373  * Note: the 32K buffer is rather big. The original inflate-algorithm
374  *       required just that but the latest zlib would work just fine with
375  *       a smaller buffer.
376  */
377 zzip_ssize_t
zzip_file_read(ZZIP_FILE * fp,void * buf,zzip_size_t len)378 zzip_file_read(ZZIP_FILE * fp, void *buf, zzip_size_t len)
379 {
380     ZZIP_DIR *dir;
381     zzip_size_t l;
382     zzip_ssize_t rv;
383 
384     if (! fp || ! fp->dir)
385         return 0;
386 
387     dir = fp->dir;
388     l = fp->restlen > len ? len : fp->restlen;
389     if (fp->restlen == 0)
390         return 0;
391 
392     /*
393      * If this is other handle than previous, save current seek pointer
394      * and read the file position of `this' handle.
395      */
396     if (dir->currentfp != fp)
397     {
398         if (zzip_file_saveoffset(dir->currentfp) < 0
399             || fp->io->fd.seeks(dir->fd, fp->offset, SEEK_SET) < 0)
400             { dir->errcode = ZZIP_DIR_SEEK; return -1; }
401         else
402             { dir->currentfp = fp; }
403     }
404 
405     /* if more methods is to be supported, change this to `switch ()' */
406     if (fp->method)             /* method != 0   == 8, inflate */
407     {
408         fp->d_stream.avail_out = l;
409         fp->d_stream.next_out = (unsigned char *) buf;
410 
411         do
412         {
413             int err;
414             zzip_size_t startlen;
415 
416             if (fp->crestlen > 0 && fp->d_stream.avail_in == 0)
417             {
418                 zzip_size_t cl = (fp->crestlen < ZZIP_32K ?
419                                   fp->crestlen : ZZIP_32K);
420                 /*  zzip_size_t cl =
421                  *      fp->crestlen > 128 ? 128 : fp->crestlen;
422                  */
423                 zzip_ssize_t i = fp->io->fd.read(dir->fd, fp->buf32k, cl);
424 
425                 if (i <= 0)
426                 {
427                     dir->errcode = ZZIP_DIR_READ;
428                     /* or ZZIP_DIR_READ_EOF ? */
429                     return -1;
430                 }
431                 fp->crestlen -= i;
432                 fp->d_stream.avail_in = i;
433                 fp->d_stream.next_in = (unsigned char *) fp->buf32k;
434             }
435 
436             startlen = fp->d_stream.total_out;
437             err = inflate(&fp->d_stream, Z_NO_FLUSH);
438 
439             if (err == Z_STREAM_END)
440                 { fp->restlen = 0; }
441             else if (err == Z_OK)
442                 { fp->restlen -= (fp->d_stream.total_out - startlen); }
443             else
444                 { dir->errcode = err; return -1; }
445         }
446         while (fp->restlen && fp->d_stream.avail_out);
447 
448         return l - fp->d_stream.avail_out;
449     } else
450     {                           /* method == 0 -- unstore */
451         rv = fp->io->fd.read(dir->fd, buf, l);
452         if (rv > 0)
453             { fp->restlen-= rv; }
454         else if (rv < 0)
455             { dir->errcode = ZZIP_DIR_READ; }
456         return rv;
457     }
458 }
459 
460 /** read data.
461  * This function will read(2) data from a real/zipped file.
462  *
463  * This function is the replacement for => read(2) will fill the given buffer with
464  * bytes from the opened file. It will return the number of bytes read, so if the EOF
465  * is encountered you will be prompted with the number of bytes actually read.
466  *
467  * If the file-handle is wrapping a stat'able file then it will actually just
468  * perform a normal => read(2)-call, otherwise => zzip_file_read is called
469  * to decompress the data stream and any error is mapped to => errno(3).
470  */
471 zzip_ssize_t
zzip_read(ZZIP_FILE * fp,void * buf,zzip_size_t len)472 zzip_read(ZZIP_FILE * fp, void *buf, zzip_size_t len)
473 {
474     if (! fp)
475         return 0;
476     if (! fp->dir)
477         { return fp->io->fd.read(fp->fd, buf, len); }    /* stat fd */
478     else
479     {
480         register zzip_ssize_t v;
481 
482         v = zzip_file_read(fp, buf, len);
483         if (v == -1)
484             { errno = zzip_errno(fp->dir->errcode); }
485         return v;
486     }
487 }
488 
489 static zzip_size_t
zzip_pread_fallback(ZZIP_FILE * file,void * ptr,zzip_size_t size,zzip_off_t offset)490 zzip_pread_fallback(ZZIP_FILE *file, void *ptr, zzip_size_t size,
491                     zzip_off_t offset)
492 {
493     zzip_off_t new_offset = zzip_seek(file, offset, SEEK_SET);
494     if (new_offset < 0)
495         return -1;
496 
497     return zzip_read(file, ptr, size);
498 }
499 
500 zzip_size_t
zzip_pread(ZZIP_FILE * file,void * ptr,zzip_size_t size,zzip_off_t offset)501 zzip_pread(ZZIP_FILE *file, void *ptr, zzip_size_t size, zzip_off_t offset)
502 {
503 #ifdef ZZIP_HAVE_PREAD
504     if (file->dir == NULL) {
505         /* reading from a regular file */
506         return pread(file->fd, ptr, size, offset);
507     } else if (file->method == 0) {
508         /* uncompressed: can read directly from the ZIP file using
509            pread() */
510         offset += file->dataoffset;
511         return pread(file->dir->fd, ptr, size, offset);
512     } else {
513 #endif
514         /* compressed (or no pread() system call): fall back to
515            zzip_seek() + zzip_read() */
516         return zzip_pread_fallback(file, ptr, size, offset);
517 #ifdef ZZIP_HAVE_PREAD
518     }
519 #endif
520 }
521 
522 /** => zzip_read
523  */
524 zzip_size_t
zzip_fread(void * ptr,zzip_size_t size,zzip_size_t nmemb,ZZIP_FILE * file)525 zzip_fread(void *ptr, zzip_size_t size, zzip_size_t nmemb, ZZIP_FILE * file)
526 {
527     if (! size)
528         size = 1;
529     return zzip_read(file, ptr, size * nmemb) / size;
530 }
531 
532 
533 #define ZZIP_WRONLY             O_WRONLY
534 #define ZZIP_EXCL               O_EXCL
535 
536 #if     defined                 O_SYNC
537 #define ZZIP_SYNC               O_SYNC
538 #else
539 #define ZZIP_SYNC               0
540 #endif
541 
542 #if     defined                 O_NONBLOCK
543 #define ZZIP_NONBLOCK           O_NONBLOCK
544 #elif   defined                 O_NDELAY
545 #define ZZIP_NOCTTY             O_NDELAY
546 #else
547 #define ZZIP_NOCTTY             0
548 #endif
549 
550 /* ------------------------------------------------------------------- */
551 
552 /** start usage.                                            also: fopen(2)
553  * This function will => fopen(3) a real/zipped file.
554  *
555  * It has some magic functionality builtin - it will first try to open
556  * the given <em>filename</em> as a normal file. If it does not
557  * exist, the given path to the filename (if any) is split into
558  * its directory-part and the file-part. A ".zip" extension is
559  * then added to the directory-part to create the name of a
560  * zip-archive. That zip-archive (if it exists) is being searched
561  * for the file-part, and if found a zzip-handle is returned.
562  *
563  * Note that if the file is found in the normal fs-directory the
564  * returned structure is mostly empty and the => zzip_read call will
565  * use the libc => read(2) to obtain data. Otherwise a => zzip_file_open
566  * is performed and any error mapped to => errno(3).
567  *
568  * unlike the posix-wrapper => zzip_open the mode-argument is
569  * a string which allows for more freedom to support the extra
570  * zzip modes called ZZIP_CASEINSENSITIVE and ZZIP_IGNOREPATH.
571  * Currently, this => zzip_fopen call will convert the following
572  * characters in the mode-string into their corrsponding mode-bits:
573  * * <code> "r" : O_RDONLY : </code> read-only
574  * * <code> "b" : O_BINARY : </code> binary (win32 specific)
575  * * <code> "f" : O_NOCTTY : </code> no char device (unix)
576  * * <code> "i" : ZZIP_CASELESS : </code> inside zip file
577  * * <code> "*" : ZZIP_NOPATHS : </code> inside zip file only
578  * all other modes will be ignored for zip-contained entries
579  * but they are transferred for compatibility and portability,
580  * including these extra sugar bits:
581  * * <code> "x" : O_EXCL :</code> fail if file did exist
582  * * <code> "s" : O_SYNC :</code> synchronized access
583  * * <code> "n" : O_NONBLOCK :</code> nonblocking access
584  * * <code> "z#" : compression level :</code> for zlib
585  * * <code> "g#" : group access :</code> unix access bits
586  * * <code> "u#" : owner access :</code> unix access bits
587  * * <code> "o#" : world access :</code> unix access bits
588  * ... the access bits are in traditional unix bit format
589  * with 7 = read/write/execute, 6 = read/write, 4 = read-only.
590  *
591  * The default access mode is 0664, and the compression level
592  * is ignored since the lib can not yet write zip files, otherwise
593  * it would be the initialisation value for the zlib deflateInit
594  * where 0 = no-compression, 1 = best-speed, 9 = best-compression.
595  *
596  * This function returns a new zzip-handle (use => zzip_close to return
597  * it). On error this function will return null setting => errno(3).
598  */
599 ZZIP_FILE *
zzip_fopen(zzip_char_t * filename,zzip_char_t * mode)600 zzip_fopen(zzip_char_t * filename, zzip_char_t * mode)
601 {
602     return zzip_freopen(filename, mode, 0);
603 }
604 
605 /** => zzip_fopen
606  *
607  * This function receives an additional argument pointing to
608  * a ZZIP_FILE* being already in use. If this extra argument is
609  * null then this function is identical with calling => zzip_fopen
610  *
611  * Per default, the old file stream is closed and only the internal
612  * structures associated with it are kept. These internal structures
613  * may be reused for the return value, and this is a lot quicker when
614  * the filename matches a zipped file that is incidentally in the very
615  * same zip arch as the old filename wrapped in the stream struct.
616  *
617  * That's simply because the zip arch's central directory does not
618  * need to be read again. As an extension for this function, if the
619  * mode-string contains a "q" then the old stream is not closed but
620  * left untouched, instead it is only given as a hint that a new
621  * file handle may share/copy the zip arch structures of the old file
622  * handle if that is possible, i.e when they are in the same zip arch.
623  *
624  * This function returns a new zzip-handle (use => zzip_close to return
625  * it). On error this function will return null setting => errno(3).
626  */
627 ZZIP_FILE *
zzip_freopen(zzip_char_t * filename,zzip_char_t * mode,ZZIP_FILE * stream)628 zzip_freopen(zzip_char_t * filename, zzip_char_t * mode, ZZIP_FILE * stream)
629 {
630     int o_flags = 0;
631     int o_modes = 0664;
632 
633     if (! mode)
634         mode = "rb";
635 
636 #   ifndef O_BINARY
637 #   define O_BINARY 0
638 #   endif
639 #   ifndef O_NOCTTY
640 #   define O_NOCTTY 0
641 #   endif
642 #   ifndef O_SYNC
643 #   define O_SYNC 0
644 #   endif
645 #   ifndef O_NONBLOCK
646 #   define O_NONBLOCK 0
647 #   endif
648 
649     for (; *mode; mode++)
650     {
651         switch (*mode)
652         {
653 	    /* *INDENT-OFF* */
654 	case '0': case '1': case '2': case '3': case '4':
655 	case '5': case '6': case '7': case '8': case '9':
656 	    continue; /* ignore if not attached to other info */
657         case 'r': o_flags |= mode[1] == '+' ? O_RDWR : O_RDONLY; break;
658         case 'w': o_flags |= mode[1] == '+' ? O_RDWR : O_WRONLY;
659                   o_flags |= O_TRUNC; break;
660         case 'b': o_flags |= O_BINARY; break;
661         case 'f': o_flags |= O_NOCTTY; break;
662         case 'i': o_modes |= ZZIP_CASELESS; break;
663         case '*': o_modes |= ZZIP_NOPATHS; break;
664         case 'x': o_flags |= O_EXCL; break;
665         case 's': o_flags |= O_SYNC; break;
666         case 'n': o_flags |= O_NONBLOCK; break;
667 	case 'o': o_modes &=~ 07;
668                   o_modes |= ((mode[1] - '0')) & 07; continue;
669 	case 'g': o_modes &=~ 070;
670                   o_modes |= ((mode[1] - '0') << 3) & 070; continue;
671 	case 'u': o_modes &=~ 0700;
672                   o_modes |= ((mode[1] - '0') << 6) & 0700; continue;
673 	case 'q': o_modes |= ZZIP_FACTORY; break;
674 	case 'z': /* compression level */
675 	    continue; /* currently ignored, just for write mode */
676 	    /* *INDENT-ON* */
677         }
678     }
679 
680     {
681         ZZIP_FILE *fp =
682             zzip_open_shared_io(stream, filename, o_flags, o_modes, 0, 0);
683 
684         if (! (o_modes & ZZIP_FACTORY) && stream)
685             zzip_file_close(stream);
686 
687         return fp;
688     }
689 }
690 
691 /** start usage.
692  * This function will => open(2) a real/zipped file
693  *
694  * It has some magic functionality builtin - it will first try to open
695  * the given <em>filename</em> as a normal file. If it does not
696  * exist, the given path to the filename (if any) is split into
697  * its directory-part and the file-part. A ".zip" extension is
698  * then added to the directory-part to create the name of a
699  * zip-archive. That zip-archive (if it exists) is being searched
700  * for the file-part, and if found a zzip-handle is returned.
701  *
702  * Note that if the file is found in the normal fs-directory the
703  * returned structure is mostly empty and the => zzip_read call will
704  * use the libc => read(2) to obtain data. Otherwise a => zzip_file_open
705  * is performed and any error mapped to => errno(3).
706  *
707  * There was a possibility to transfer zziplib-specific openmodes
708  * through o_flags but you should please not use them anymore and
709  * look into => zzip_open_ext_io to submit them down. This function
710  * is shallow in that it just extracts the zzipflags and calls
711  * * <code>zzip_open_ext_io(filename, o_flags, zzipflags|0664, 0, 0) </code>
712  * you must stop using this extra functionality (not well known anyway)
713  * since zzip_open might be later usable to open files for writing
714  * in which case the _EXTRAFLAGS will get in conflict.
715  *
716  * compare with  => open(2) and => zzip_fopen
717  */
718 ZZIP_FILE *
zzip_open(zzip_char_t * filename,int o_flags)719 zzip_open(zzip_char_t * filename, int o_flags)
720 {
721     /* backward compatibility */
722     int o_modes = 0664;
723 
724     if (o_flags & ZZIP_CASEINSENSITIVE)
725         {  o_flags ^= ZZIP_CASEINSENSITIVE; o_modes |= ZZIP_CASELESS; }
726     if (o_flags & ZZIP_IGNOREPATH)
727         {  o_flags ^= ZZIP_IGNOREPATH;      o_modes |= ZZIP_NOPATHS; }
728     return zzip_open_ext_io(filename, o_flags, o_modes, 0, 0);
729 }
730 
731 /* ZZIP_ONLYZIP won't work on platforms with sizeof(int) == 16bit */
732 #if ZZIP_SIZEOF_INT+0 == 2
733 #undef ZZIP_ONLYZIP
734 #endif
735 
736 /** => zzip_open
737  *
738  * This function uses explicit ext and io instead of the internal
739  * defaults, setting them to zero is equivalent to => zzip_open
740  *
741  * note that the two flag types have been split into an o_flags
742  * (for fcntl-like openflags) and o_modes where the latter shall
743  * carry the zzip_flags and possibly accessmodes for unix filesystems.
744  * Since this version of zziplib can not write zipfiles, it is not
745  * yet used for anything else than zzip-specific modeflags.
746  *
747  * This function returns a new zzip-handle (use => zzip_close to return
748  * it). On error this function will return null setting => errno(3).
749  *
750  * If any ext_io handlers were used then the referenced structure
751  * should be static as the allocated ZZIP_FILE does not copy them.
752  */
753 ZZIP_FILE *
zzip_open_ext_io(zzip_char_t * filename,int o_flags,int o_modes,zzip_strings_t * ext,zzip_plugin_io_t io)754 zzip_open_ext_io(zzip_char_t * filename, int o_flags, int o_modes,
755                  zzip_strings_t * ext, zzip_plugin_io_t io)
756 {
757     return zzip_open_shared_io(0, filename, o_flags, o_modes, ext, io);
758 }
759 
760 /** => zzip_open
761  *
762  * This function takes an extra stream argument - if a handle has been
763  * then ext/io can be left null and the new stream handle will pick up
764  * the ext/io. This should be used only in specific environment however
765  * since => zzip_file_real does not store any ext-sequence.
766  *
767  * The benefit for this function comes in when the old file handle
768  * was openened from a file within a zip archive. When the new file
769  * is in the same zip archive then the internal zzip_dir structures
770  * will be shared. It is even quicker, as no check needs to be done
771  * anymore trying to guess the zip archive place in the filesystem,
772  * here we just check whether the zip archive's filepath is a prefix
773  * part of the filename to be opened.
774  *
775  * Note that this function is also used by => zzip_freopen that
776  * will unshare the old handle, thereby possibly closing the handle.
777  *
778  * This function returns a new zzip-handle (use => zzip_close to return
779  * it). On error this function will return null setting => errno(3).
780  */
781 ZZIP_FILE *
zzip_open_shared_io(ZZIP_FILE * stream,zzip_char_t * filename,int o_flags,int o_modes,zzip_strings_t * ext,zzip_plugin_io_t io)782 zzip_open_shared_io(ZZIP_FILE * stream,
783                     zzip_char_t * filename, int o_flags, int o_modes,
784                     zzip_strings_t * ext, zzip_plugin_io_t io)
785 {
786     if (stream && stream->dir)
787     {
788         if (! ext)
789             ext = stream->dir->fileext;
790         if (! io)
791             io = stream->dir->io;
792     }
793     if (! io)
794         io = zzip_get_default_io();
795 
796     if (o_modes & (ZZIP_PREFERZIP | ZZIP_ONLYZIP))
797         goto try_zzip;
798   try_real:
799     /* prefer an existing real file */
800     {
801         zzip_plugin_io_t os = (o_modes & ZZIP_ALLOWREAL)
802             ? zzip_get_default_io() : io;
803         int fd = (os->fd.open)(filename, o_flags);        /* io->fd.open */
804 
805         if (fd != -1)
806         {
807             ZZIP_FILE *fp = calloc(1, sizeof(ZZIP_FILE));
808 
809             if (! fp)
810                 { os->fd.close(fd); return 0; }  /* io->fd.close */
811 
812             fp->fd = fd;
813             fp->io = os;
814             return fp;
815         }
816         if (o_modes & ZZIP_PREFERZIP)
817             return 0;
818     }
819   try_zzip:
820 
821     /* if the user had it in place of a normal xopen, then
822      * we better defend this lib against illegal usage */
823     if (o_flags & (O_CREAT | O_WRONLY))
824         { errno = EINVAL; return 0; }
825     if (o_flags & (O_RDWR))
826         { o_flags ^= O_RDWR; o_flags |= O_RDONLY; }
827 
828     /* this is just for backward compatibility -and strictly needed to
829      * prepare ourselves for more options and more options later on... */
830     /*# if (o_modes & ZZIP_CASELESS) { o_flags |= ZZIP_CASEINSENSITIVE; } */
831     /*# if (o_modes & ZZIP_NOPATHS)  { o_flags |= ZZIP_IGNOREPATH; } */
832 
833     /* see if we can open a file that is a zip file */
834     {
835         char basename[PATH_MAX];
836         char *p;
837         int filename_len = strlen(filename);
838 
839         if (filename_len >= PATH_MAX)
840             { errno = ENAMETOOLONG; return 0; }
841         memcpy(basename, filename, filename_len + 1);
842 
843         /* see if we can share the same zip directory */
844         if (stream && stream->dir && stream->dir->realname)
845         {
846             zzip_size_t len = strlen(stream->dir->realname);
847 
848             if (! memcmp(filename, stream->dir->realname, len) &&
849                 filename[len] == '/' && filename[len + 1])
850             {
851                 ZZIP_FILE *fp =
852                     zzip_file_open(stream->dir, filename + len + 1, o_modes);
853                 if (! fp)
854                     { errno = zzip_errno (stream->dir->errcode); }
855                 return fp;
856             }
857         }
858 
859         /* per each slash in filename, check if it there is a zzip around */
860         while ((p = strrchr(basename, '/')))
861         {
862             zzip_error_t e = 0;
863             ZZIP_DIR *dir;
864             ZZIP_FILE *fp;
865             int fd;
866 
867             *p = '\0';
868             /* i.e. cut at path separator == possible zipfile basename */
869             fd = __zzip_try_open(basename, o_flags | O_RDONLY | O_BINARY,
870                                  ext, io);
871             if (fd == -1)
872                 { continue; }
873 
874             /* found zip-file ....  now try to parse it */
875             dir = zzip_dir_fdopen_ext_io(fd, &e, ext, io);
876             if (e)
877                 { errno = zzip_errno(e); io->fd.close(fd); return 0; }
878 
879             /* (p - basename) is the lenghtof zzip_dir part of the filename */
880             fp = zzip_file_open(dir, filename + (p - basename) + 1, o_modes);
881             if (! fp)
882                 { errno = zzip_errno(dir->errcode); }
883             else
884                 { if (! dir->realname) dir->realname = strdup (basename); }
885 
886             zzip_dir_close(dir);
887             /* note: since (fp) is attached that (dir) will survive */
888             /* but (dir) is implicitly closed on next zzip_close(fp) */
889 
890             return fp;
891         }
892 
893         if (o_modes & ZZIP_PREFERZIP)
894             goto try_real;
895         else
896             { errno = ENOENT; return 0; }
897     }
898 }
899 
900 #if defined ZZIP_LARGEFILE_RENAME && defined EOVERFLOW && defined PIC
901 
902 /* DLL compatibility layer - so that 32bit code can link with this lib too */
903 
904 #undef zzip_open_shared_io      /* zzip_open_shared_io64 */
905 #undef zzip_open_ext_io         /* zzip_open_ext_io64 */
906 #undef zzip_opendir_ext_io      /* zzip_opendir_ext_io64 */
907 
908 ZZIP_FILE *zzip_open_shared_io(ZZIP_FILE * stream,
909                                zzip_char_t * name, int o_flags,
910                                int o_modes, zzip_strings_t * ext,
911                                zzip_plugin_io_t io);
912 ZZIP_FILE *zzip_open_ext_io(zzip_char_t * name, int o_flags,
913                             int o_modes, zzip_strings_t * ext,
914                             zzip_plugin_io_t io);
915 ZZIP_DIR *zzip_opendir_ext_io(zzip_char_t * name, int o_modes,
916                               zzip_strings_t * ext, zzip_plugin_io_t io);
917 
918 ZZIP_FILE *
zzip_open_shared_io(ZZIP_FILE * stream,zzip_char_t * name,int o_flags,int o_modes,zzip_strings_t * ext,zzip_plugin_io_t io)919 zzip_open_shared_io(ZZIP_FILE * stream,
920                     zzip_char_t * name, int o_flags,
921                     int o_modes, zzip_strings_t * ext, zzip_plugin_io_t io)
922 {
923     if (! io)
924         return zzip_open_shared_io64(stream, name, o_flags, o_modes, ext, io);
925     errno = EOVERFLOW;
926     return NULL;
927 }
928 
929 ZZIP_FILE *
zzip_open_ext_io(zzip_char_t * name,int o_flags,int o_modes,zzip_strings_t * ext,zzip_plugin_io_t io)930 zzip_open_ext_io(zzip_char_t * name, int o_flags, int o_modes,
931                  zzip_strings_t * ext, zzip_plugin_io_t io)
932 {
933     if (! io)
934         return zzip_open_ext_io64(name, o_flags, o_modes, ext, io);
935     errno = EOVERFLOW;
936     return NULL;
937 }
938 
939 ZZIP_DIR *
zzip_opendir_ext_io(zzip_char_t * name,int o_modes,zzip_strings_t * ext,zzip_plugin_io_t io)940 zzip_opendir_ext_io(zzip_char_t * name, int o_modes,
941                     zzip_strings_t * ext, zzip_plugin_io_t io)
942 {
943     if (! io)
944         return zzip_opendir_ext_io64(name, o_modes, ext, io);
945     else
946         { errno = EOVERFLOW; return NULL; }
947 }
948 
949 #endif /* ZZIP_LARGEFILE_RENAME && EOVERFLOW && PIC */
950 
951 /* ------------------------------------------------------------------- */
952 
953 /** rewind.
954  *
955  * This function will rewind a real/zipped file.
956  *
957  * It seeks to the beginning of this file's data in the zip,
958  * or the beginning of the file for a stat'fd.
959  */
960 int
zzip_rewind(ZZIP_FILE * fp)961 zzip_rewind(ZZIP_FILE * fp)
962 {
963     ZZIP_DIR *dir;
964     int err;
965 
966     if (! fp)
967         return -1;
968 
969     if (! fp->dir)
970     {                           /* stat fd */
971         fp->io->fd.seeks(fp->fd, 0, SEEK_SET);
972         return 0;
973     }
974 
975     dir = fp->dir;
976     /*
977      * If this is other handle than previous, save current seek pointer
978      */
979     if (dir->currentfp != fp)
980     {
981         if (zzip_file_saveoffset(dir->currentfp) < 0)
982             { dir->errcode = ZZIP_DIR_SEEK; return -1; }
983         else
984             { dir->currentfp = fp; }
985     }
986 
987     /* seek to beginning of this file */
988     if (fp->io->fd.seeks(dir->fd, fp->dataoffset, SEEK_SET) < 0)
989         return -1;
990 
991     /* reset the inflate init stuff */
992     fp->restlen = fp->usize;
993     fp->offset = fp->dataoffset;
994 
995     if (fp->method)
996     {                           /* method == 8, deflate */
997         err = inflateReset(&fp->d_stream);
998         if (err != Z_OK)
999             goto error;
1000 
1001         /* start over at next inflate with a fresh read() */
1002         fp->d_stream.avail_in = 0;
1003         fp->crestlen = fp->csize;
1004     }
1005 
1006     return 0;
1007 
1008   error:
1009     if (fp)
1010         zzip_file_close(fp);
1011     return err;
1012 }
1013 
1014 /** seek.
1015  *
1016  * This function will perform a => lseek(2) operation on a real/zipped file
1017  *
1018  * It will try to seek to the offset specified by offset, relative to whence,
1019  * which is one of SEEK_SET, SEEK_CUR or SEEK_END.
1020  *
1021  * If the file-handle is wrapping a stat'able file then it will actually just
1022  * perform a normal => lseek(2)-call. Otherwise the relative offset
1023  * is calculated, negative offsets are transformed into positive ones
1024  * by rewinding the file, and then data is read until the offset is
1025  * reached.  This can make the function terribly slow, but this is
1026  * how gzio implements it, so I'm not sure there is a better way
1027  * without using the internals of the algorithm.
1028  */
1029 zzip_off_t
zzip_seek(ZZIP_FILE * fp,zzip_off_t offset,int whence)1030 zzip_seek(ZZIP_FILE * fp, zzip_off_t offset, int whence)
1031 {
1032     zzip_off_t cur_pos, rel_ofs, read_size, ofs;
1033     ZZIP_DIR *dir;
1034 
1035     if (! fp)
1036         return -1;
1037 
1038     if (! fp->dir)
1039     {                           /* stat fd */
1040         return fp->io->fd.seeks(fp->fd, offset, whence);
1041     }
1042 
1043     cur_pos = zzip_tell(fp);
1044 
1045     /* calculate relative offset */
1046     switch (whence)
1047     {
1048     case SEEK_SET:             /* from beginning */
1049         rel_ofs = offset - cur_pos;
1050         break;
1051     case SEEK_CUR:             /* from current */
1052         rel_ofs = offset;
1053         break;
1054     case SEEK_END:             /* from end */
1055         rel_ofs = fp->usize + offset - cur_pos;
1056         break;
1057     default:                   /* something wrong */
1058         return -1;
1059     }
1060 
1061     if (rel_ofs == 0)
1062         return cur_pos;         /* don't have to move */
1063 
1064     if (rel_ofs < 0)
1065     {                           /* convert backward into forward */
1066         if (zzip_rewind(fp) == -1)
1067             return -1;
1068 
1069         read_size = cur_pos + rel_ofs;
1070         cur_pos = 0;
1071     } else
1072     {                           /* amount to read is positive relative offset */
1073         read_size = rel_ofs;
1074     }
1075 
1076     if (read_size < 0)          /* bad offset, before beginning of file */
1077         return -1;
1078 
1079     if (read_size + cur_pos > (zzip_off_t) fp->usize)   /* bad offset, past EOF */
1080         return -1;
1081 
1082     if (read_size == 0)         /* nothing to read */
1083         return cur_pos;
1084 
1085     dir = fp->dir;
1086     /*
1087      * If this is other handle than previous, save current seek pointer
1088      * and read the file position of `this' handle.
1089      */
1090     if (dir->currentfp != fp)
1091     {
1092         if (zzip_file_saveoffset(dir->currentfp) < 0
1093             || fp->io->fd.seeks(dir->fd, fp->offset, SEEK_SET) < 0)
1094             { dir->errcode = ZZIP_DIR_SEEK; return -1; }
1095         else
1096             { dir->currentfp = fp; }
1097     }
1098 
1099     if (fp->method == 0)
1100     {                           /* unstore, just lseek relatively */
1101         ofs = fp->io->fd.tells(dir->fd);
1102         ofs = fp->io->fd.seeks(dir->fd, read_size, SEEK_CUR);
1103         if (ofs > 0)
1104         {                       /* readjust from beginning of file */
1105             ofs -= fp->dataoffset;
1106             fp->restlen = fp->usize - ofs;
1107         }
1108         return ofs;
1109     } else
1110     {                           /* method == 8, inflate */
1111         char *buf;
1112 
1113         /*FIXME: use a static buffer! */
1114         buf = (char *) malloc(ZZIP_32K);
1115         if (! buf)
1116             return -1;
1117 
1118         while (read_size > 0)
1119         {
1120             zzip_off_t size = ZZIP_32K;
1121 
1122             if (read_size < size /*32K */ )
1123                 size = read_size;
1124 
1125             size = zzip_file_read(fp, buf, (zzip_size_t) size);
1126             if (size <= 0)
1127                 { free(buf); return -1; }
1128 
1129             read_size -= size;
1130         }
1131 
1132         free(buf);
1133     }
1134 
1135     return zzip_tell(fp);
1136 }
1137 
1138 /** tell.
1139  *
1140  * This function will => tell(2) the current position in a real/zipped file
1141  *
1142  * It will return the current offset within the real/zipped file,
1143  * measured in uncompressed bytes for the zipped-file case.
1144  *
1145  * If the file-handle is wrapping a stat'able file then it will actually just
1146  * perform a normal => tell(2)-call, otherwise the offset is
1147  * calculated from the amount of data left and the total uncompressed
1148  * size;
1149  */
1150 zzip_off_t
zzip_tell(ZZIP_FILE * fp)1151 zzip_tell(ZZIP_FILE * fp)
1152 {
1153     if (! fp)
1154         return -1;
1155 
1156     if (! fp->dir)               /* stat fd */
1157         return fp->io->fd.tells(fp->fd);
1158 
1159     /* current uncompressed offset is uncompressed size - data left */
1160     return (fp->usize - fp->restlen);
1161 }
1162 
1163 #ifndef EOVERFLOW
1164 #define EOVERFLOW EFBIG
1165 #endif
1166 
1167 /** => zzip_tell
1168  * This function is provided for users who can not use any largefile-mode.
1169  */
1170 long
zzip_tell32(ZZIP_FILE * fp)1171 zzip_tell32(ZZIP_FILE * fp)
1172 {
1173     if (sizeof(zzip_off_t) == sizeof(long))
1174     {
1175         return zzip_tell(fp);
1176     } else
1177     {
1178         off_t off = zzip_tell(fp);
1179         if (off >= 0) {
1180             register long off32 = off;
1181             if (off32 == off) return off32;
1182             errno = EOVERFLOW;
1183         }
1184         return -1;
1185     }
1186 }
1187 
1188 /** => zzip_seek
1189  * This function is provided for users who can not use any largefile-mode.
1190  */
1191 long
zzip_seek32(ZZIP_FILE * fp,long offset,int whence)1192 zzip_seek32(ZZIP_FILE * fp, long offset, int whence)
1193 {
1194     if (sizeof(zzip_off_t) == sizeof(long))
1195     {
1196         return zzip_seek(fp, offset, whence);
1197     } else
1198     {
1199         off_t off = zzip_seek(fp, offset, whence);
1200         if (off >= 0) {
1201             register long off32 = off;
1202             if (off32 == off) return off32;
1203             errno = EOVERFLOW;
1204         }
1205         return -1;
1206     }
1207 }
1208 
1209 /*
1210  * Local variables:
1211  * c-file-style: "stroustrup"
1212  * End:
1213  */
1214