/** => zzip_entry_data_offset * This function is a big helper despite its little name: in a zip file the * encoded filenames are usually NOT zero-terminated but for common usage * with libc we need it that way. Secondly, the filename SHOULD be present * in the zip central directory but if not then we fallback to the filename * given in the file_header of each compressed data portion. */ char* _zzip_restrict zzip_entry_strdup_name(ZZIP_ENTRY* entry) { if (! entry) return 0; ___ zzip_size_t len; if ((len = zzip_disk_entry_namlen (disk_(entry)))) { char* name = malloc (len+1); if (! name) return 0; memcpy (name, entry->tail, len); name[len] = '\0'; return name; } ___ auto struct zzip_file_header header; if (zzip_entry_fread_file_header (entry, &header) && (( len = zzip_file_header_namlen(&header) ))) { char* name = malloc (len+1); if (! name) return 0; fread (name, 1, len, entry->diskfile); name[len] = '\0'; return name; } return 0; ____;____; }
/** => zzip_entry_data_offset * This functions read the correspoding struct zzip_file_header from * the zip disk of the given "entry". The returned off_t points to the * end of the file_header where the current fseek pointer has stopped. * This is used to immediatly parse out any filename/extras block following * the file_header. The return value is null on error. */ static zzip_off_t zzip_entry_fread_file_header (ZZIP_ENTRY* entry, struct zzip_file_header* file_header) { zzip_off_t offset = zzip_disk_entry_fileoffset (disk_(entry)); if (0 > offset || offset >= entry->disksize) return 0; fseeko (entry->diskfile, offset, SEEK_SET); return (fread (file_header, sizeof(*file_header), 1, entry->diskfile) ? offset+sizeof(*file_header) : 0 ); }
static int prescan_entry(ZZIP_ENTRY* entry) { assert (entry); ___ zzip_off_t tailsize = zzip_disk_entry_sizeof_tails (disk_(entry)); if (tailsize+1 > entry->tailalloc) { char* newtail = realloc (entry->tail, tailsize+1); if (! newtail) return ENOMEM; entry->tail = newtail; entry->tailalloc = tailsize+1; } fread (entry->tail, 1, tailsize, entry->diskfile); /* name + comment + extras */ return 0; ____; }
/** => zzip_entry_data_offset * This function is a big helper despite its little name: in a zip file the * encoded filenames are usually NOT zero-terminated but for common usage * with libc we need it that way. Secondly, the filename SHOULD be present * in the zip central directory but if not then we fallback to the filename * given in the file_header of each compressed data portion. */ zzip__new__ char * zzip_entry_strdup_name(ZZIP_ENTRY * entry) { if (! entry) return 0; ___ zzip_size_t len; if ((len = zzip_disk_entry_namlen(disk_(entry)))) { char *name = malloc(len + 1); if (! name) return 0; memcpy(name, entry->tail, len); name[len] = '\0'; return name; } ___ auto struct zzip_file_header header; if (zzip_entry_fread_file_header(entry, &header) && (len = zzip_file_header_namlen(&header))) { char *name = malloc(len + 1); if (! name) { return 0; } else { zzip_size_t n = fread(name, 1, len, entry->diskfile); if (n != len) { free (name); return 0; } name[n] = '\0'; return name; } } return 0; ____; ____; }
/** => zzip_entry_findfile * * This function is the first call of all the zip access functions here. * It contains the code to find the first entry of the zip central directory. * Here we require the stdio handle to represent a real zip file where the * disk_trailer is _last_ in the file area, so that its position would be at * a fixed offset from the end of the file area if not for the comment field * allowed to be of variable length (which needs us to do a little search * for the disk_tailer). However, in this simple implementation we disregard * any disk_trailer info telling about multidisk archives, so we just return * a pointer to the first entry in the zip central directory of that file. * * For an actual means, we are going to search backwards from the end * of the mmaped block looking for the PK-magic signature of a * disk_trailer. If we see one then we check the rootseek value to * find the first disk_entry of the root central directory. If we find * the correct PK-magic signature of a disk_entry over there then we * assume we are done and we are going to return a pointer to that label. * * The return value is a pointer to the first zzip_disk_entry being checked * to be within the bounds of the file area specified by the arguments. If * no disk_trailer was found then null is returned, and likewise we only * accept a disk_trailer with a seekvalue that points to a disk_entry and * both parts have valid PK-magic parts. Beyond some sanity check we try to * catch a common brokeness with zip archives that still allows us to find * the start of the zip central directory. */ zzip__new__ ZZIP_ENTRY * zzip_entry_findfirst(FILE * disk) { if (! disk) return 0; if (fseeko(disk, 0, SEEK_END) == -1) return 0; ___ zzip_off_t disksize = ftello(disk); if (disksize < (zzip_off_t) sizeof(struct zzip_disk_trailer)) return 0; /* we read out chunks of 8 KiB in the hope to match disk granularity */ ___ zzip_off_t pagesize = PAGESIZE; /* getpagesize() */ ___ ZZIP_ENTRY *entry = malloc(sizeof(*entry)); if (! entry) return 0; ___ unsigned char *buffer = malloc(pagesize); if (! buffer) goto nomem; assert(pagesize / 2 > (zzip_off_t) sizeof(struct zzip_disk_trailer)); /* at each step, we will fread a pagesize block which overlaps with the * previous read by means of pagesize/2 step at the end of the while(1) */ ___ zzip_off_t mapoffs = disksize & ~(pagesize - 1); ___ zzip_off_t mapsize = disksize - mapoffs; if (mapoffs && mapsize < pagesize / 2) { mapoffs -= pagesize / 2; mapsize += pagesize / 2; } while (1) { if (fseeko(disk, mapoffs, SEEK_SET) == -1) goto error; if (fread(buffer, 1, mapsize, disk) != mapsize) goto error; ___ unsigned char *p = buffer + mapsize - sizeof(struct zzip_disk_trailer); for (; p >= buffer; p--) { zzip_off_t root; /* (struct zzip_disk_entry*) */ if (zzip_disk_trailer_check_magic(p)) { root = zzip_disk_trailer_rootseek((struct zzip_disk_trailer *) p); if (root > disksize - (long) sizeof(struct zzip_disk_trailer)) { /* first disk_entry is after the disk_trailer? can't be! */ struct zzip_disk_trailer *trailer = (struct zzip_disk_trailer *) p; zzip_off_t rootsize = zzip_disk_trailer_rootsize(trailer); if (rootsize > mapoffs) continue; /* a common brokeness that can be fixed: we just assume the * central directory was written directly before : */ root = mapoffs - rootsize; } } else if (zzip_disk64_trailer_check_magic(p)) { struct zzip_disk64_trailer *trailer = (struct zzip_disk64_trailer *) p; if (sizeof(zzip_off_t) < 8) return 0; root = zzip_disk64_trailer_rootseek(trailer); } else continue; assert(0 <= root && root < mapsize); if (fseeko(disk, root, SEEK_SET) == -1) goto error; if (fread(disk_(entry), 1, sizeof(*disk_(entry)), disk) != sizeof(*disk_(entry))) goto error; if (zzip_disk_entry_check_magic(entry)) { free(buffer); entry->headseek = root; entry->diskfile = disk; entry->disksize = disksize; if (prescan_entry(entry)) goto nomem; return entry; } } ____; if (! mapoffs) break; assert(mapsize >= pagesize / 2); mapoffs -= pagesize / 2; /* mapsize += pagesize/2; */ mapsize = pagesize; /* if (mapsize > pagesize) ... */ if (disksize - mapoffs > 64 * 1024) break; } error: free(buffer); nomem: free(entry); ____; ____; ____; ____; ____; ____; return 0; }
/** => zzip_entry_findfile * * This function is the first call of all the zip access functions here. * It contains the code to find the first entry of the zip central directory. * Here we require the stdio handle to represent a real zip file where the * disk_trailer is _last_ in the file area, so that its position would be at * a fixed offset from the end of the file area if not for the comment field * allowed to be of variable length (which needs us to do a little search * for the disk_tailer). However, in this simple implementation we disregard * any disk_trailer info telling about multidisk archives, so we just return * a pointer to the first entry in the zip central directory of that file. * * For an actual means, we are going to search backwards from the end * of the mmaped block looking for the PK-magic signature of a * disk_trailer. If we see one then we check the rootseek value to * find the first disk_entry of the root central directory. If we find * the correct PK-magic signature of a disk_entry over there then we * assume we are done and we are going to return a pointer to that label. * * The return value is a pointer to the first zzip_disk_entry being checked * to be within the bounds of the file area specified by the arguments. If * no disk_trailer was found then null is returned, and likewise we only * accept a disk_trailer with a seekvalue that points to a disk_entry and * both parts have valid PK-magic parts. Beyond some sanity check we try to * catch a common brokeness with zip archives that still allows us to find * the start of the zip central directory. */ ZZIP_ENTRY* _zzip_restrict zzip_entry_findfirst(FILE* disk) { if (! disk) return 0; fseeko (disk, 0, SEEK_END); ___ zzip_off_t disksize = ftello (disk); if (disksize < (zzip_off_t) sizeof(struct zzip_disk_trailer)) return 0; /* we read out chunks of 8 KiB in the hope to match disk granularity */ ___ zzip_off_t pagesize = PAGESIZE; /* getpagesize() */ ___ ZZIP_ENTRY* entry = malloc (sizeof(*entry)); if (! entry) return 0; ___ char* buffer = malloc (pagesize); if (! buffer) goto nomem; assert (pagesize/2 > (zzip_off_t) sizeof (struct zzip_disk_trailer)); /* at each step, we will fread a pagesize block which overlaps with the * previous read by means of pagesize/2 step at the end of the while(1) */ ___ zzip_off_t mapoffs = disksize &~ (pagesize-1); ___ zzip_off_t mapsize = disksize - mapoffs; if (mapoffs && mapsize < pagesize/2) { mapoffs -= pagesize/2; mapsize += pagesize/2; } while(1) { fseeko (disk, mapoffs, SEEK_SET); fread (buffer, 1, mapsize, disk); ___ char* p = buffer + mapsize - sizeof(struct zzip_disk_trailer); for (; p >= buffer ; p--) { if (! zzip_disk_trailer_check_magic(p)) continue; ___ zzip_off_t root = zzip_disk_trailer_rootseek ((struct zzip_disk_trailer*)p); if ((char*) root > p) { /* the first disk_entry is after the disk_trailer? can't be! */ zzip_off_t rootsize = zzip_disk_trailer_rootsize ((struct zzip_disk_trailer*)p); if (rootsize > mapoffs) continue; /* a common brokeness that can be fixed: we just assume that * the central directory was written directly before : */ root = mapoffs - rootsize; } assert (0 <= root && root < mapsize); fseeko (disk, root, SEEK_SET); fread (disk_(entry), 1, sizeof(*disk_(entry)), disk); if (zzip_disk_entry_check_magic(entry)) { free (buffer); entry->headseek = root; entry->diskfile = disk; entry->disksize = disksize; ___ zzip_size_t tailsize = zzip_disk_entry_sizeof_tails (disk_(entry)); if (!( entry->tail = malloc (tailsize+1) )) goto nomem; fread (entry->tail, 1, tailsize, disk); entry->tailalloc = tailsize+1; return entry; ____; } ____; } ____; if (! mapoffs) break; assert (mapsize >= pagesize/2); mapoffs -= pagesize/2; /* mapsize += pagesize/2; */ mapsize = pagesize; /* if (mapsize > pagesize) ... */ if (disksize - mapoffs > 64*1024) break; } free (buffer); nomem: free (entry); ____;____;____;____;____;____; return 0; }
/** => zzip_entry_findfile * * This function takes an existing "entry" in the central root directory * (e.g. from zzip_entry_findfirst) and moves it to point to the next entry. * On error it returns 0, otherwise the old entry. If no further match is * found then null is returned and the entry already free()d. If you want * to stop searching for matches before that case then please call * => zzip_entry_free on the cursor struct ZZIP_ENTRY. */ ZZIP_ENTRY* _zzip_restrict zzip_entry_findnext(ZZIP_ENTRY* _zzip_restrict entry) { if (! entry) return entry; ___ zzip_off_t seek = entry->headseek + zzip_disk_entry_sizeto_end (disk_(entry)); if (seek + (zzip_off_t) sizeof(*disk_(entry)) > entry->disksize) goto err; fseeko (entry->diskfile, seek, SEEK_SET); fread (disk_(entry), 1, sizeof(*disk_(entry)), entry->diskfile); entry->headseek = seek; ___ zzip_off_t tailsize = zzip_disk_entry_sizeof_tails (disk_(entry)); if (tailsize+1 > entry->tailalloc) { char* newtail = realloc (entry->tail, tailsize+1); if (! newtail) goto err; entry->tail = newtail; entry->tailalloc = tailsize+1; } fread (entry->tail, 1, tailsize, entry->diskfile); return entry; ____; err: