static struct cdir_entry * find_cdir_entry (struct cdir_entry *entry, int count, const char *name) { size_t name_size = strlen(name); while (count--) { if (letoh16(entry->filename_size) == name_size && !memcmp(entry->data, name, name_size)) return entry; entry = (struct cdir_entry *)((char *)entry + cdir_entry_size(entry)); } return NULL; }
static uint32_t find_lowest_offset(struct cdir_entry *entry, int count) { uint32_t lowest_offset = zip_size; while (count--) { uint32_t entry_offset = le32toh(entry->offset); if (lowest_offset > entry_offset) lowest_offset = entry_offset; if (le32toh(entry->signature) != 0x02014b50) { printf("invalid signature on cdir_entry! (count=%d)\n", count); exit(-1); } entry = (struct cdir_entry *)((char *)entry + cdir_entry_size(entry)); } return lowest_offset; }
static int repack(bool flatten, const char *dstpath, const char *srcpath) { char *src_zip = (char *)map_file(srcpath); if (!src_zip) { printf("Could not open zip file.\n"); return -1; } int fd = creat(dstpath, 0777); if (fd == -1) { printf("can't open output file\n"); return -1; } struct cdir_end *dirend = (struct cdir_end *)(src_zip + zip_size - sizeof(*dirend)); while ((void *)dirend > src_zip && le32toh(dirend->signature) != 0x06054b50) dirend = (struct cdir_end *)((char *)dirend - 1); if (le32toh(dirend->signature) != 0x06054b50) { printf("couldn't find end of central directory record!\n"); return -1; } uint32_t cdir_offset = le32toh(dirend->cdir_offset); uint16_t cdir_entries = le16toh(dirend->cdir_entries); uint32_t cdir_size = le32toh(dirend->cdir_size); TRACE("Found %d entries. cdir offset at %d\n", cdir_entries, cdir_offset); struct cdir_entry *cdir_start = (struct cdir_entry *)(src_zip + cdir_offset); struct cdir_entry *new_cdir_start = (struct cdir_entry *)malloc(cdir_size); if (!new_cdir_start) { TRACE("couldn't allocate central directory copy\n"); return -1; } memcpy(new_cdir_start, cdir_start, cdir_size); uint32_t lowest_offset = find_lowest_offset(cdir_start, cdir_entries); uint32_t out_offset = simple_write(fd, src_zip, lowest_offset); struct cdir_entry *current_entry = new_cdir_start; uint16_t i = cdir_entries; while (i--) { struct local_file_header *file = (struct local_file_header *)(src_zip + le32toh(current_entry->offset)); int rc; if (flatten) rc = flatten_entry(fd, file, current_entry, out_offset); else rc = squeeze_entry(fd, file, current_entry, out_offset); if (rc) return rc; current_entry = (struct cdir_entry *)((char *)current_entry + cdir_entry_size(current_entry)); } uint32_t new_cdir_offset; if (cdir_offset < lowest_offset) { TRACE("Doing in place cdir replacement at %d\n", cdir_offset); new_cdir_offset = cdir_offset; lseek(fd, SEEK_SET, cdir_offset); simple_write(fd, (char *)new_cdir_start, cdir_size); lseek(fd, SEEK_END, 0); } else { new_cdir_offset = out_offset; TRACE("Appending cdir at %d\n", new_cdir_offset); simple_write(fd, (char *)new_cdir_start, cdir_size); } struct cdir_end end; memcpy(&end, dirend, sizeof(end)); end.cdir_offset = htole32(new_cdir_offset); simple_write(fd, (char *)&end, sizeof(end)); close(fd); munmap(src_zip, zip_size); return 0; }