3 * NOTE: this is part of libzzipmmapped (i.e. it is not libzzip).
6 * The mem_disk cache will parse the central information of a zip archive
7 * and store it internally. One the one hand it allows to find files
8 * faster - no disk access is required and endian conversion is not
9 * needed. If zzip is compiled with zip extensions then it is about
10 * the only way to build maintainable code around the zip format.
12 * Note that 64bit support is almost entirely living in extension
13 * blocks as well as different character encodings and file access
14 * control bits that are mostly platform specific.
17 * Guido Draheim <guidod@gmx.de>
19 * Copyright (c) 1999,2000,2001,2002,2003 Guido Draheim
20 * All rights reserved,
21 * use under the restrictions of the
22 * Lesser GNU General Public License
23 * or alternatively the restrictions
24 * of the Mozilla Public License 1.1
26 #define _ZZIP_DISK_FILE_STRUCT 1
28 #include <zzip/types.h>
36 #include <zzip/format.h>
37 #include <zzip/fetch.h>
38 #include <zzip/mmapped.h>
39 #include <zzip/memdisk.h>
40 #include <zzip/__fnmatch.h>
45 static const char *error
[] = {
47 # define _zzip_mem_disk_open_fail 1
48 "zzip_mem_disk_open: zzip_disk_open did fail",
49 # define _zzip_mem_disk_fdopen_fail 2
50 "zzip_mem_disk_fdopen: zzip_disk_mmap did fail"
51 # define _zzip_mem_disk_buffer_fail 3
52 "zzip_mem_disk_buffer: zzip_disk_buffer did fail",
56 #define ZZIP_EXTRA_zip64 0x0001
57 typedef struct _zzip_extra_zip64
58 { /* ZIP64 extended information extra field */
59 zzip_byte_t z_datatype
[2]; /* Tag for this "extra" block type */
60 zzip_byte_t z_datasize
[2]; /* Size of this "extra" block */
61 zzip_byte_t z_usize
[8]; /* Original uncompressed file size */
62 zzip_byte_t z_csize
[8]; /* Size of compressed data */
63 zzip_byte_t z_offset
[8]; /* Offset of local header record */
64 zzip_byte_t z_diskstart
[4]; /* Number of the disk for file start */
69 static zzip__new__ ZZIP_MEM_ENTRY
*
70 zzip_mem_entry_new(ZZIP_DISK
* disk
, ZZIP_DISK_ENTRY
* entry
);
72 zzip_mem_entry_free(ZZIP_MEM_ENTRY
* _zzip_restrict item
);
74 zzip__new__ ZZIP_MEM_DISK
*
75 zzip_mem_disk_new(void)
77 return calloc(1, sizeof(ZZIP_MEM_DISK
));
80 /** create new diskdir handle.
81 * wraps underlying zzip_disk_open. */
82 zzip__new__ ZZIP_MEM_DISK
*
83 zzip_mem_disk_open(char *filename
)
85 ZZIP_DISK
*disk
= zzip_disk_open(filename
);
87 { perror(error
[_zzip_mem_disk_open_fail
]); return 0; }
88 ___ ZZIP_MEM_DISK
*dir
= zzip_mem_disk_new();
89 zzip_mem_disk_load(dir
, disk
);
94 /** create new diskdir handle.
95 * wraps underlying zzip_disk_open. */
96 zzip__new__ ZZIP_MEM_DISK
*
97 zzip_mem_disk_fdopen(int fd
)
99 ZZIP_DISK
*disk
= zzip_disk_mmap(fd
);
101 { perror(error
[_zzip_mem_disk_fdopen_fail
]); return 0; }
102 ___ ZZIP_MEM_DISK
*dir
= zzip_mem_disk_new();
103 zzip_mem_disk_load(dir
, disk
);
108 /** create new diskdir handle.
109 * wraps underlying zzip_disk_buffer. */
110 zzip__new__ ZZIP_MEM_DISK
*
111 zzip_mem_disk_buffer(char *buffer
, size_t buflen
)
113 ZZIP_DISK
*disk
= zzip_disk_buffer(buffer
, buflen
);
115 { perror(error
[_zzip_mem_disk_buffer_fail
]); return 0; }
116 ___ ZZIP_MEM_DISK
*dir
= zzip_mem_disk_new();
117 zzip_mem_disk_load(dir
, disk
);
122 /** parse central dir.
123 * creates an internal copy of each entry converted to the local platform.
124 * returns: number of entries, or -1 on error (setting errno)
127 zzip_mem_disk_load(ZZIP_MEM_DISK
* dir
, ZZIP_DISK
* disk
)
130 { errno
=EINVAL
; return -1; }
132 zzip_mem_disk_unload(dir
);
134 ___
struct zzip_disk_entry
*entry
= zzip_disk_findfirst(disk
);
135 for (; entry
; entry
= zzip_disk_findnext(disk
, entry
))
137 ZZIP_MEM_ENTRY
*item
= zzip_mem_entry_new(disk
, entry
);
142 dir
->last
->zz_next
= item
; /* chain last */
147 dir
->last
= item
; /* to earlier */
155 zzip_mem_disk_unload(dir
);
159 /** convert a zip disk entry to internal format.
160 * creates a new item parsing the information out of the various places
161 * in the zip archive. This is a good place to extend functionality if
162 * you have a project with extra requirements as you can push more bits
163 * right into the diskdir_entry for later usage in higher layers.
164 * returns: new item, or null on error (setting errno)
166 zzip__new__ ZZIP_MEM_ENTRY
*
167 zzip_mem_entry_new(ZZIP_DISK
* disk
, ZZIP_DISK_ENTRY
* entry
)
169 if (! disk
|| ! entry
)
170 { errno
=EINVAL
; return 0; }
171 ___ ZZIP_MEM_ENTRY
*item
= calloc(1, sizeof(*item
));
173 return 0; /* errno=ENOMEM; */
174 ___
struct zzip_file_header
*header
=
175 zzip_disk_entry_to_file_header(disk
, entry
);
176 /* there is a number of duplicated information in the file header
177 * or the disk entry block. Theoretically some part may be missing
178 * that exists in the other, ... but we will prefer the disk entry.
180 item
->zz_comment
= zzip_disk_entry_strdup_comment(disk
, entry
);
181 item
->zz_name
= zzip_disk_entry_strdup_name(disk
, entry
);
182 item
->zz_data
= zzip_file_header_to_data(header
);
183 item
->zz_flags
= zzip_disk_entry_get_flags(entry
);
184 item
->zz_compr
= zzip_disk_entry_get_compr(entry
);
185 item
->zz_mktime
= zzip_disk_entry_get_mktime(entry
);
186 item
->zz_crc32
= zzip_disk_entry_get_crc32(entry
);
187 item
->zz_csize
= zzip_disk_entry_get_csize(entry
);
188 item
->zz_usize
= zzip_disk_entry_get_usize(entry
);
189 item
->zz_diskstart
= zzip_disk_entry_get_diskstart(entry
);
190 item
->zz_filetype
= zzip_disk_entry_get_filetype(entry
);
192 { /* copy the extra blocks to memory as well */
193 int /* */ ext1
= zzip_disk_entry_get_extras(entry
);
194 char *_zzip_restrict ptr1
= zzip_disk_entry_to_extras(entry
);
195 int /* */ ext2
= zzip_file_header_get_extras(header
);
196 char *_zzip_restrict ptr2
= zzip_file_header_to_extras(header
);
200 void *mem
= malloc(ext1
+ 2);
201 item
->zz_ext
[1] = mem
;
202 memcpy(mem
, ptr1
, ext1
);
203 ((char *) (mem
))[ext1
+ 0] = 0;
204 ((char *) (mem
))[ext1
+ 1] = 0;
208 void *mem
= malloc(ext2
+ 2);
209 item
->zz_ext
[2] = mem
;
210 memcpy(mem
, ptr2
, ext2
);
211 ((char *) (mem
))[ext2
+ 0] = 0;
212 ((char *) (mem
))[ext2
+ 1] = 0;
216 /* override sizes/offsets with zip64 values for largefile support */
217 zzip_extra_zip64
*block
= (zzip_extra_zip64
*)
218 zzip_mem_entry_extra_block(item
, ZZIP_EXTRA_zip64
);
221 item
->zz_usize
= __zzip_get64(block
->z_usize
);
222 item
->zz_csize
= __zzip_get64(block
->z_csize
);
223 item
->zz_offset
= __zzip_get64(block
->z_offset
);
224 item
->zz_diskstart
= __zzip_get32(block
->z_diskstart
);
228 * All information from the central directory entry is now in memory.
229 * Effectivly that allows us to modify it and write it back to disk.
236 /* find an extra block for the given datatype code.
237 * We assume that the central directory has been preparsed to memory.
240 zzip_mem_entry_extra_block(ZZIP_MEM_ENTRY
* entry
, short datatype
)
245 ZZIP_EXTRA_BLOCK
*ext
= entry
->zz_ext
[i
];
248 while (*(short *) (ext
->z_datatype
))
250 if (datatype
== zzip_extra_block_get_datatype(ext
))
254 ___
char *e
= (char *) ext
;
255 e
+= zzip_extra_block_headerlength
;
256 e
+= zzip_extra_block_get_datasize(ext
);
268 zzip_mem_entry_free(ZZIP_MEM_ENTRY
* _zzip_restrict item
)
273 if (item
->zz_ext
[0]) free (item
->zz_ext
[0]);
274 if (item
->zz_ext
[1]) free (item
->zz_ext
[1]);
275 if (item
->zz_ext
[2]) free (item
->zz_ext
[2]);
276 if (item
->zz_comment
) free (item
->zz_comment
);
277 if (item
->zz_name
) free (item
->zz_name
);
284 zzip_mem_disk_unload(ZZIP_MEM_DISK
* dir
)
286 ZZIP_MEM_ENTRY
*item
= dir
->list
;
289 ZZIP_MEM_ENTRY
*next
= item
->zz_next
;
290 zzip_mem_entry_free(item
);
293 dir
->list
= dir
->last
= 0;
294 zzip_disk_close(dir
->disk
);
299 zzip_mem_disk_close(ZZIP_MEM_DISK
* _zzip_restrict dir
)
303 zzip_mem_disk_unload(dir
);
304 zzip_disk_close(dir
->disk
);
311 foo(short zz_datatype
)
316 case 0x0001: /* ZIP64 extended information extra field */
317 case 0x0007: /* AV Info */
318 case 0x0008: /* Reserved for future Unicode file name data (PFS) */
319 case 0x0009: /* OS/2 */
320 case 0x000a: /* NTFS */
321 case 0x000c: /* OpenVMS */
322 case 0x000d: /* Unix */
323 case 0x000e: /* Reserved for file stream and fork descriptors */
324 case 0x000f: /* Patch Descriptor */
325 case 0x0014: /* PKCS#7 Store for X.509 Certificates */
326 case 0x0015: /* X.509 Certificate ID and Signature for file */
327 case 0x0016: /* X.509 Certificate ID for Central Directory */
328 case 0x0017: /* Strong Encryption Header */
329 case 0x0018: /* Record Management Controls */
330 case 0x0019: /* PKCS#7 Encryption Recipient Certificate List */
331 case 0x0065: /* IBM S/390, AS/400 attributes - uncompressed */
332 case 0x0066: /* Reserved for IBM S/390, AS/400 attr - compressed */
333 case 0x07c8: /* Macintosh */
334 case 0x2605: /* ZipIt Macintosh */
335 case 0x2705: /* ZipIt Macintosh 1.3.5+ */
336 case 0x2805: /* ZipIt Macintosh 1.3.5+ */
337 case 0x334d: /* Info-ZIP Macintosh */
338 case 0x4341: /* Acorn/SparkFS */
339 case 0x4453: /* Windows NT security descriptor (binary ACL) */
340 case 0x4704: /* VM/CMS */
341 case 0x470f: /* MVS */
342 case 0x4b46: /* FWKCS MD5 (see below) */
343 case 0x4c41: /* OS/2 access control list (text ACL) */
344 case 0x4d49: /* Info-ZIP OpenVMS */
345 case 0x4f4c: /* Xceed original location extra field */
346 case 0x5356: /* AOS/VS (ACL) */
347 case 0x5455: /* extended timestamp */
348 case 0x554e: /* Xceed unicode extra field */
349 case 0x5855: /* Info-ZIP Unix (original, also OS/2, NT, etc) */
350 case 0x6542: /* BeOS/BeBox */
351 case 0x756e: /* ASi Unix */
352 case 0x7855: /* Info-ZIP Unix (new) */
353 case 0xfd4a: /* SMS/QDOS */
360 zzip_mem_disk_findfile(ZZIP_MEM_DISK
* dir
,
361 char *filename
, ZZIP_MEM_ENTRY
* after
,
362 zzip_strcmp_fn_t compare
)
364 ZZIP_MEM_ENTRY
*entry
= (! after
? dir
->list
: after
->zz_next
);
366 compare
= (zzip_strcmp_fn_t
) (strcmp
);
367 for (; entry
; entry
= entry
->zz_next
)
369 if (! compare(filename
, entry
->zz_name
))
378 zzip_mem_disk_findmatch(ZZIP_MEM_DISK
* dir
,
379 char *filespec
, ZZIP_MEM_ENTRY
* after
,
380 zzip_fnmatch_fn_t compare
, int flags
)
382 ZZIP_MEM_ENTRY
*entry
= (! after
? dir
->list
: after
->zz_next
);
384 compare
= (zzip_fnmatch_fn_t
) _zzip_fnmatch
;
385 for (; entry
; entry
= entry
->zz_next
)
387 if (! compare(filespec
, entry
->zz_name
, flags
))
395 zzip__new__ ZZIP_MEM_DISK_FILE
*
396 zzip_mem_entry_fopen(ZZIP_MEM_DISK
* dir
, ZZIP_MEM_ENTRY
* entry
)
398 /* keep this in sync with zzip_disk_entry_fopen */
399 ZZIP_DISK_FILE
*file
= malloc(sizeof(ZZIP_MEM_DISK_FILE
));
402 file
->buffer
= dir
->disk
->buffer
;
403 file
->endbuf
= dir
->disk
->endbuf
;
404 file
->avail
= zzip_mem_entry_usize(entry
);
406 if (! file
->avail
|| zzip_mem_entry_data_stored(entry
))
407 { file
->stored
= zzip_mem_entry_to_data (entry
); return file
; }
410 file
->zlib
.opaque
= 0;
411 file
->zlib
.zalloc
= Z_NULL
;
412 file
->zlib
.zfree
= Z_NULL
;
413 file
->zlib
.avail_in
= zzip_mem_entry_csize(entry
);
414 file
->zlib
.next_in
= zzip_mem_entry_to_data(entry
);
416 if (! zzip_mem_entry_data_deflated(entry
) ||
417 inflateInit2(&file
->zlib
, -MAX_WBITS
) != Z_OK
)
418 { free (file
); return 0; }
423 zzip__new__ ZZIP_MEM_DISK_FILE
*
424 zzip_mem_disk_fopen(ZZIP_MEM_DISK
* dir
, char *filename
)
426 ZZIP_MEM_ENTRY
*entry
= zzip_mem_disk_findfile(dir
, filename
, 0, 0);
430 return zzip_mem_entry_fopen(dir
, entry
);
434 zzip_mem_disk_fread(void *ptr
, _zzip_size_t size
, _zzip_size_t nmemb
,
435 ZZIP_MEM_DISK_FILE
* file
)
437 return zzip_disk_fread(ptr
, size
, nmemb
, file
);
441 zzip_mem_disk_fclose(ZZIP_MEM_DISK_FILE
* file
)
443 return zzip_disk_fclose(file
);
447 zzip_mem_disk_feof(ZZIP_MEM_DISK_FILE
* file
)
449 return zzip_disk_feof(file
);
452 /* convert dostime of entry to unix time_t */
454 zzip_disk_entry_get_mktime(ZZIP_DISK_ENTRY
* entry
)
456 uint16_t dostime
= ZZIP_GET16(entry
->z_dostime
.time
);
457 uint16_t dosdate
= ZZIP_GET16(entry
->z_dostime
.date
);
459 date
.tm_sec
= (dostime
) & 0x1F; /* bits 0..4 */
460 date
.tm_min
= (dostime
>> 5) & 0x3F; /* bits 5..10 */
461 date
.tm_hour
= (dostime
>> 11); /* bits 11..15 */
462 date
.tm_mday
= (dosdate
) & 0x1F; /* bits 16..20 */
463 date
.tm_mon
= (dosdate
>> 5) & 0xF; /* bits 21..24 */
464 date
.tm_year
= (dosdate
>> 9) + 80; /* bits 25..31 */
465 return mktime(&date
); /* well, unix has that function... */