3 * Guido Draheim <guidod@gmx.de>
4 * Tomi Ollila <too@iki.fi>
6 * Copyright (c) 1999,2000,2001,2002,2003 Guido Draheim
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
14 #include <zzip/lib.h> /* archive handling */
15 #include <zzip/file.h>
16 #include <zzip/format.h>
17 #include <zzip/fetch.h>
24 #ifdef ZZIP_HAVE_SYS_STAT_H
28 #include <zzip/__mmap.h>
29 #include <zzip/__debug.h>
31 #define __sizeof(X) ((zzip_ssize_t)(sizeof(X)))
33 /* per default, we use a little hack to correct bad z_rootseek parts */
34 #define ZZIP_CORRECT_ROOTSEEK 1
37 /* --------------------------- internals -------------------------------- */
38 /* internal functions of zziplib, avoid at all cost, changes w/o warning.
39 * we do export them for debugging purpose and special external tools
40 * which know what they do and which can adapt from version to version
43 int __zzip_find_disk_trailer( int fd, zzip_off_t filesize,
44 struct zzip_disk_trailer * trailer,
46 int __zzip_parse_root_directory( int fd,
47 struct zzip_disk_trailer * trailer,
48 struct zzip_dir_hdr ** hdr_return,
51 _zzip_inline char* __zzip_aligned4(char* p);
53 /* ------------------------ harden routines ------------------------------ */
57 * check for inconsistent values in trailer and prefer lower seek value
58 * - we fix values assuming the root directory was written at the end
59 * and it is just before the zip trailer. Therefore, ...
61 _zzip_inline static void __fixup_rootseek(
62 zzip_off_t offset_of_trailer,
63 struct zzip_disk_trailer* trailer)
65 if ( zzip_disk_trailer_rootseek (trailer) >
66 offset_of_trailer - zzip_disk_trailer_rootsize (trailer) &&
67 offset_of_trailer > zzip_disk_trailer_rootsize (trailer))
69 register zzip_off_t offset;
70 offset = offset_of_trailer - zzip_disk_trailer_rootsize (trailer);
71 zzip_disk_trailer_set_rootseek (trailer, offset);
72 HINT2("new rootseek=%li", (long) zzip_disk_trailer_rootseek (trailer));
75 #define __correct_rootseek(A,B,C)
77 #elif defined ZZIP_CORRECT_ROOTSEEK
78 /* store the seekvalue of the trailer into the "z_magic" field and with
79 * a 64bit off_t we overwrite z_disk/z_finaldisk as well. If you change
80 * anything in zziplib or dump the trailer structure then watch out that
81 * these are still unused, so that this code may still (ab)use those. */
82 #define __fixup_rootseek(_offset_of_trailer, _trailer) \
83 *(zzip_off_t*)_trailer = _offset_of_trailer;
84 #define __correct_rootseek( _u_rootseek, _u_rootsize, _trailer) \
85 if (_u_rootseek > *(zzip_off_t*)_trailer - _u_rootsize) \
86 _u_rootseek = *(zzip_off_t*)_trailer - _u_rootsize;
88 #define __fixup_rootseek(A,B)
89 #define __correct_rootseek(A,B,C)
94 _zzip_inline static void __debug_dir_hdr (struct zzip_dir_hdr* hdr)
96 if (sizeof(struct zzip_dir_hdr) > sizeof(struct zzip_disk_entry))
97 { WARN1("internal sizeof-mismatch may break wreakage"); }
98 /* the internal directory structure is never bigger than the
99 * external zip central directory space had been beforehand
100 * (as long as the following assertion holds...)
103 if (((unsigned)hdr)&3)
104 { NOTE1("this machine's malloc(3) returns sth. not u32-aligned"); }
105 /* we assume that if this machine's malloc has returned a non-aligned
106 * memory block, then it is actually safe to access misaligned data, and
107 * since it does only affect the first hdr it should not even bring about
108 * too much of that cpu's speed penalty
112 #define __debug_dir_hdr(X)
115 /* -------------------------- low-level interface -------------------------- */
118 #if BUFSIZ == 1024 || BUFSIZ == 512 || BUFSIZ == 256
119 #define ZZIP_BUFSIZ BUFSIZ
124 #define ZZIP_BUFSIZ 512
125 /* #define ZZIP_BUFSIZ 64 */ /* for testing */
129 * This function is used by => zzip_file_open. It tries to find
130 * the zip's central directory info that is usually a few
131 * bytes off the end of the file.
134 __zzip_find_disk_trailer(int fd, zzip_off_t filesize,
135 struct zzip_disk_trailer * trailer,
139 #define return(val) { e=val; HINT2("%s", zzip_strerror(e)); goto cleanup; }
141 #define return(val) { e=val; goto cleanup; }
146 auto char buffer[2*ZZIP_BUFSIZ];
149 char* buf = malloc(2*ZZIP_BUFSIZ);
151 zzip_off_t offset = 0;
152 zzip_ssize_t maplen = 0; /* mmap(),read(),getpagesize() use size_t !! */
158 if (filesize < __sizeof(struct zzip_disk_trailer))
159 { return(ZZIP_DIR_TOO_SHORT); }
162 { return(ZZIP_OUTOFMEM); }
164 offset = filesize; /* a.k.a. old offset */
165 while(1) /* outer loop */
167 register unsigned char* mapped;
169 if (offset <= 0) { return(ZZIP_DIR_EDH_MISSING); }
171 /* trailer cannot be farther away than 64K from fileend */
172 if (filesize-offset > 64*1024)
173 { return(ZZIP_DIR_EDH_MISSING); }
175 /* the new offset shall overlap with the area after the old offset! */
176 if (USE_MMAP && io->fd.sys)
178 zzip_off_t mapoff = offset;
180 zzip_ssize_t pagesize = _zzip_getpagesize (io->fd.sys);
181 if (pagesize < ZZIP_BUFSIZ) goto non_mmap; /* an error? */
182 if (mapoff == filesize && filesize > pagesize)
184 if (mapoff < pagesize) {
185 maplen = (zzip_ssize_t)mapoff + pagesize; mapoff = 0;
187 mapoff -= pagesize; maplen = 2*pagesize;
188 if ((zzip_ssize_t)mapoff & (pagesize-1)) { /*only 1. run */
189 pagesize -= (zzip_ssize_t)mapoff & (pagesize-1);
194 if (mapoff + maplen > filesize) maplen = filesize - mapoff;
197 fd_map = _zzip_mmap(io->fd.sys, fd, mapoff, (zzip_size_t)maplen);
198 if (fd_map == MAP_FAILED) goto non_mmap;
199 mapped = (unsigned char*) fd_map; offset = mapoff; /* success */
200 HINT3("mapped *%p len=%li", fd_map, (long) maplen);
203 fd_map = 0; /* have no mmap */
205 zzip_off_t pagesize = ZZIP_BUFSIZ;
206 if (offset == filesize && filesize > pagesize)
208 if (offset < pagesize) {
209 maplen = (zzip_ssize_t)offset + pagesize; offset = 0;
211 offset -= pagesize; maplen = 2*pagesize;
212 if ((zzip_ssize_t)offset & (pagesize-1)) { /*on 1st run*/
213 pagesize -= (zzip_ssize_t)offset & (pagesize-1);
218 if (offset + maplen > filesize) maplen = filesize - offset;
221 if (io->fd.seeks(fd, offset, SEEK_SET) < 0)
222 { return(ZZIP_DIR_SEEK); }
223 if (io->fd.read(fd, buf, (zzip_size_t)maplen) < maplen)
224 { return(ZZIP_DIR_READ); }
225 mapped = (unsigned char*) buf; /* success */
226 HINT5("offs=$%lx len=%li filesize=%li pagesize=%i",
227 (long)offset, (long)maplen, (long)filesize, ZZIP_BUFSIZ);
230 {/* now, check for the trailer-magic, hopefully near the end of file */
231 register unsigned char* end = mapped + maplen;
232 register unsigned char* tail;
233 for (tail = end-1; (tail >= mapped); tail--)
235 if ((*tail == 'P') && /* quick pre-check for trailer magic */
236 end-tail >= __sizeof(*trailer)-2 &&
237 ZZIP_DISK_TRAILER_CHECKMAGIC(tail))
239 /* if the file-comment is not present, it happens
240 that the z_comment field often isn't either */
241 if (end-tail >= __sizeof(*trailer))
243 memcpy (trailer, tail, sizeof(*trailer));
245 memcpy (trailer, tail, sizeof(*trailer)-2);
246 trailer->z_comment[0] = 0;
247 trailer->z_comment[1] = 0;
250 __fixup_rootseek (offset + tail-mapped, trailer);
256 if (USE_MMAP && fd_map)
258 HINT3("unmap *%p len=%li", fd_map, (long) maplen);
259 _zzip_munmap(io->fd.sys, fd_map, (zzip_size_t)maplen);
265 if (USE_MMAP && fd_map)
267 HINT3("unmap *%p len=%li", fd_map, (long) maplen);
268 _zzip_munmap(io->fd.sys, fd_map, (zzip_size_t)maplen);
278 * making pointer alignments to values that can be handled as structures
279 * is tricky. We assume here that an align(4) is sufficient even for
280 * 64 bit machines. Note that binary operations are not usually allowed
281 * to pointer types but we do need only the lower bits in this implementation,
282 * so we can just cast the value to a long value.
284 _zzip_inline char* __zzip_aligned4(char* p)
286 #define aligned4 __zzip_aligned4
287 p += ((long)p)&1; /* warnings about truncation of a "pointer" */
288 p += ((long)p)&2; /* to a "long int" may be safely ignored :) */
293 * This function is used by => zzip_file_open, it is usually called after
294 * => __zzip_find_disk_trailer. It will parse the zip's central directory
295 * information and create a zziplib private directory table in
299 __zzip_parse_root_directory(int fd,
300 struct zzip_disk_trailer * trailer,
301 struct zzip_dir_hdr ** hdr_return,
304 auto struct zzip_disk_entry dirent;
305 struct zzip_dir_hdr * hdr;
306 struct zzip_dir_hdr * hdr0;
307 uint16_t * p_reclen = 0;
309 long offset; /* offset from start of root directory */
312 uint16_t u_entries = zzip_disk_trailer_get_entries (trailer);
313 uint32_t u_rootsize = zzip_disk_trailer_get_rootsize (trailer);
314 uint32_t u_rootseek = zzip_disk_trailer_get_rootseek (trailer);
315 __correct_rootseek (u_rootseek, u_rootsize, trailer);
317 hdr0 = (struct zzip_dir_hdr*) malloc(u_rootsize);
320 hdr = hdr0; __debug_dir_hdr (hdr);
322 if (USE_MMAP && io->fd.sys)
324 fd_gap = u_rootseek & (_zzip_getpagesize(io->fd.sys)-1) ;
325 HINT4(" mapseek=0x%x, maplen=%d, fd_gap=%d",
326 u_rootseek-fd_gap, u_rootsize+fd_gap, fd_gap);
327 fd_map = _zzip_mmap(io->fd.sys, fd,
328 u_rootseek-fd_gap, u_rootsize+fd_gap);
329 /* if mmap failed we will fallback to seek/read mode */
330 if (fd_map == MAP_FAILED) {
331 NOTE2("map failed: %s",strerror(errno));
334 HINT3("mapped *%p len=%i", fd_map, u_rootsize+fd_gap);
338 for (entries=u_entries, offset=0; entries > 0; entries--)
340 register struct zzip_disk_entry * d;
341 uint16_t u_extras, u_comment, u_namlen;
344 { d = (void*)(fd_map+fd_gap+offset); } /* fd_map+fd_gap==u_rootseek */
347 if (io->fd.seeks(fd, u_rootseek+offset, SEEK_SET) < 0)
348 return ZZIP_DIR_SEEK;
349 if (io->fd.read(fd, &dirent, sizeof(dirent)) < __sizeof(dirent))
350 return ZZIP_DIR_READ;
354 if (offset+sizeof(*d) > u_rootsize)
355 { FAIL2("%i's entry stretches beyond root directory", entries); break;}
357 # if 0 && defined DEBUG
358 zzip_debug_xbuf ((unsigned char*) d, sizeof(*d) + 8);
361 u_extras = zzip_disk_entry_get_extras (d);
362 u_comment = zzip_disk_entry_get_comment (d);
363 u_namlen = zzip_disk_entry_get_namlen (d);
364 HINT5("offset=0x%lx, size %ld, dirent *%p, hdr %p\n",
365 offset+u_rootseek, (long)u_rootsize, d, hdr);
367 /* writes over the read buffer, Since the structure where data is
368 copied is smaller than the data in buffer this can be done.
369 It is important that the order of setting the fields is considered
370 when filling the structure, so that some data is not trashed in
371 first structure read.
372 at the end the whole copied list of structures is copied into
373 newly allocated buffer */
374 hdr->d_crc32 = zzip_disk_entry_get_crc32 (d);
375 hdr->d_csize = zzip_disk_entry_get_csize (d);
376 hdr->d_usize = zzip_disk_entry_get_usize (d);
377 hdr->d_off = zzip_disk_entry_get_offset (d);
378 hdr->d_compr = zzip_disk_entry_get_compr (d);
379 if (hdr->d_compr > 255) hdr->d_compr = 255;
381 if (offset+sizeof(*d) + u_namlen > u_rootsize)
382 { FAIL2("%i's name stretches beyond root directory", entries); break;}
385 { memcpy(hdr->d_name, fd_map+fd_gap + offset+sizeof(*d), u_namlen); }
386 else { io->fd.read(fd, hdr->d_name, u_namlen); }
387 hdr->d_name[u_namlen] = '\0';
388 hdr->d_namlen = u_namlen;
390 /* update offset by the total length of this entry -> next entry */
391 offset += sizeof(*d) + u_namlen + u_extras + u_comment;
393 if (offset > (long)u_rootsize)
394 { FAIL2("%i's end beyond root directory", entries); entries--; break;}
396 HINT5("file %d { compr=%d crc32=$%x offset=%d",
397 entries, hdr->d_compr, hdr->d_crc32, hdr->d_off);
398 HINT5("csize=%d usize=%d namlen=%d extras=%d",
399 hdr->d_csize, hdr->d_usize, u_namlen, u_extras);
400 HINT5("comment=%d name='%s' %s <sizeof %d> } ",
401 u_comment, hdr->d_name, "",(int) sizeof(*d));
403 p_reclen = &hdr->d_reclen;
405 { register char* p = (char*) hdr;
406 register char* q = aligned4 (p + sizeof(*hdr) + u_namlen + 1);
407 *p_reclen = (uint16_t)(q - p);
408 hdr = (struct zzip_dir_hdr*) q;
412 if (USE_MMAP && fd_map)
414 HINT3("unmap *%p len=%i", fd_map, u_rootsize+fd_gap);
415 _zzip_munmap(io->fd.sys, fd_map, u_rootsize+fd_gap);
420 *p_reclen = 0; /* mark end of list */
424 } /* else zero (sane) entries */
425 return (entries ? ZZIP_CORRUPTED : 0);
428 /* ------------------------- high-level interface ------------------------- */
434 static zzip_strings_t* zzip_get_default_ext(void)
436 static zzip_strings_t ext [] =
438 ".zip", ".ZIP", /* common extension */
439 # ifdef ZZIP_USE_ZIPLIKES
440 ".pk3", ".PK3", /* ID Software's Quake3 zipfiles */
441 ".jar", ".JAR", /* Java zipfiles */
450 * allocate a new ZZIP_DIR handle and do basic
451 * initializations before usage by => zzip_dir_fdopen
452 * => zzip_dir_open => zzip_file_open or through
454 * (ext==null flags uses { ".zip" , ".ZIP" } )
455 * (io ==null flags use of posix io defaults)
458 zzip_dir_alloc_ext_io (zzip_strings_t* ext, const zzip_plugin_io_t io)
461 if ((dir = (ZZIP_DIR *)calloc(1, sizeof(*dir))) == NULL)
464 /* dir->fileext is currently unused - so what, still initialize it */
465 dir->fileext = ext ? ext : zzip_get_default_ext();
466 dir->io = io ? io : zzip_get_default_io ();
470 /** => zzip_dir_alloc_ext_io
471 * this function is obsolete - it was generally used for implementation
472 * and exported to let other code build on it. It is now advised to
473 * use => zzip_dir_alloc_ext_io now on explicitly, just set that second
474 * argument to zero to achieve the same functionality as the old style.
477 zzip_dir_alloc (zzip_strings_t* fileext)
479 return zzip_dir_alloc_ext_io (fileext, 0);
483 * will free the zzip_dir handle unless there are still
484 * zzip_files attached (that may use its cache buffer).
485 * This is the inverse of => zzip_dir_alloc , and both
486 * are helper functions used implicitly in other zzipcalls
487 * e.g. => zzip_dir_close = zzip_close
489 * returns zero on sucess
490 * returns the refcount when files are attached.
493 zzip_dir_free(ZZIP_DIR * dir)
496 return (dir->refcount); /* still open files attached */
498 if (dir->fd >= 0) dir->io->fd.close(dir->fd);
499 if (dir->hdr0) free(dir->hdr0);
500 if (dir->cache.fp) free(dir->cache.fp);
501 if (dir->cache.buf32k) free(dir->cache.buf32k);
502 if (dir->realname) free(dir->realname);
508 * It will also => free(2) the => ZZIP_DIR-handle given.
509 * the counterpart for => zzip_dir_open
510 * see also => zzip_dir_free
513 zzip_dir_close(ZZIP_DIR * dir)
515 dir->refcount &=~ 0x10000000; /* explicit dir close */
516 return zzip_dir_free(dir);
520 * used by the => zzip_dir_open and zzip_opendir(2) call. Opens the
521 * zip-archive as specified with the fd which points to an
522 * already openend file. This function then search and parse
523 * the zip's central directory.
525 * NOTE: refcount is zero, so an _open/_close pair will also delete
529 zzip_dir_fdopen(int fd, zzip_error_t * errcode_p)
531 return zzip_dir_fdopen_ext_io(fd, errcode_p, 0, 0);
534 static zzip_error_t __zzip_dir_parse (ZZIP_DIR* dir); /* forward */
536 /** => zzip_dir_fdopen
537 * this function uses explicit ext and io instead of the internal
538 * defaults, setting these to zero is equivalent to => zzip_dir_fdopen
541 zzip_dir_fdopen_ext_io(int fd, zzip_error_t * errcode_p,
542 zzip_strings_t* ext, const zzip_plugin_io_t io)
547 if ((dir = zzip_dir_alloc_ext_io (ext, io)) == NULL)
548 { rv = ZZIP_OUTOFMEM; goto error; }
551 if ((rv = __zzip_dir_parse (dir)))
554 dir->hdr = dir->hdr0;
555 dir->refcount |= 0x10000000;
557 if (errcode_p) *errcode_p = rv;
560 if (dir) zzip_dir_free(dir);
561 if (errcode_p) *errcode_p = rv;
566 __zzip_dir_parse (ZZIP_DIR* dir)
570 struct zzip_disk_trailer trailer;
571 /* if (! dir || dir->fd < 0)
572 * { rv = EINVAL; goto error; }
575 HINT2("------------------ fd=%i", (int) dir->fd);
576 if ((filesize = dir->io->fd.filesize(dir->fd)) < 0)
577 { rv = ZZIP_DIR_STAT; goto error; }
579 HINT2("------------------ filesize=%ld", (long) filesize);
580 if ((rv = __zzip_find_disk_trailer(dir->fd, filesize, &trailer,
584 HINT5("directory = { entries= %ld/%ld, size= %ld, seek= %ld } ",
585 (long) zzip_disk_trailer_localentries (&trailer),
586 (long) zzip_disk_trailer_finalentries (&trailer),
587 (long) zzip_disk_trailer_rootsize (&trailer),
588 (long) zzip_disk_trailer_rootseek (&trailer));
590 if ( (rv = __zzip_parse_root_directory(dir->fd, &trailer, &dir->hdr0,
598 * will attach a .zip extension and tries to open it
599 * the with => open(2). This is a helper function for
600 * => zzip_dir_open, => zzip_opendir and => zzip_open.
603 __zzip_try_open(zzip_char_t* filename, int filemode,
604 zzip_strings_t* ext, zzip_plugin_io_t io)
606 auto char file[PATH_MAX];
608 zzip_size_t len = strlen (filename);
610 if (len+4 >= PATH_MAX) return -1;
611 memcpy(file, filename, len+1);
613 if (!io) io = zzip_get_default_io();
614 if (!ext) ext = zzip_get_default_ext();
616 for ( ; *ext ; ++ext)
618 strcpy (file+len, *ext);
619 fd = io->fd.open(file, filemode);
620 if (fd != -1) return fd;
626 * Opens the zip-archive (if available).
627 * the two ext_io arguments will default to use posix io and
628 * a set of default fileext that can atleast add .zip ext itself.
631 zzip_dir_open(zzip_char_t* filename, zzip_error_t* e)
633 return zzip_dir_open_ext_io (filename, e, 0, 0);
637 * this function uses explicit ext and io instead of the internal
638 * defaults. Setting these to zero is equivalent to => zzip_dir_open
641 zzip_dir_open_ext_io(zzip_char_t* filename, zzip_error_t* e,
642 zzip_strings_t* ext, zzip_plugin_io_t io)
646 if (!io) io = zzip_get_default_io();
647 if (!ext) ext = zzip_get_default_ext();
649 fd = io->fd.open(filename, O_RDONLY|O_BINARY);
651 { return zzip_dir_fdopen_ext_io(fd, e, ext, io); }
654 fd = __zzip_try_open(filename, O_RDONLY|O_BINARY, ext, io);
656 { return zzip_dir_fdopen_ext_io(fd, e, ext, io); }
659 if (e) { *e = ZZIP_DIR_OPEN; }
666 * fills the dirent-argument with the values and
667 * increments the read-pointer of the dir-argument.
669 * returns 0 if there no entry (anymore).
672 zzip_dir_read(ZZIP_DIR * dir, ZZIP_DIRENT * d )
674 if (! dir || ! dir->hdr || ! d) return 0;
676 d->d_compr = dir->hdr->d_compr;
677 d->d_csize = dir->hdr->d_csize;
678 d->st_size = dir->hdr->d_usize;
679 d->d_name = dir->hdr->d_name;
681 if (! dir->hdr->d_reclen)
684 { dir->hdr = (struct zzip_dir_hdr *)((char *)dir->hdr + dir->hdr->d_reclen); }
691 * c-file-style: "stroustrup"