zip_extra_field_t * _zip_ef_parse(const zip_uint8_t *data, zip_uint16_t len, zip_flags_t flags, zip_error_t *error) { zip_buffer_t *buffer; zip_extra_field_t *ef, *ef2, *ef_head; if ((buffer = _zip_buffer_new((zip_uint8_t *)data, len)) == NULL) { zip_error_set(error, ZIP_ER_MEMORY, 0); return NULL; } ef_head = ef = NULL; while (_zip_buffer_ok(buffer) && !_zip_buffer_eof(buffer)) { zip_uint16_t fid, flen; zip_uint8_t *ef_data; fid = _zip_buffer_get_16(buffer); flen = _zip_buffer_get_16(buffer); ef_data = _zip_buffer_get(buffer, flen); if (ef_data == NULL) { break; } if ((ef2=_zip_ef_new(fid, flen, ef_data, flags)) == NULL) { zip_error_set(error, ZIP_ER_MEMORY, 0); _zip_buffer_free(buffer); _zip_ef_free(ef_head); return NULL; } if (ef_head) { ef->next = ef2; ef = ef2; } else ef_head = ef = ef2; } if (!_zip_buffer_eof(buffer)) { zip_error_set(error, ZIP_ER_INCONS, 0); _zip_buffer_free(buffer); _zip_ef_free(ef_head); return NULL; } _zip_buffer_free(buffer); return ef_head; }
int _zip_ef_write(zip_t *za, const zip_extra_field_t *ef, zip_flags_t flags) { zip_uint8_t b[4]; zip_buffer_t *buffer = _zip_buffer_new(b, sizeof(b)); if (buffer == NULL) { return -1; } for (; ef; ef=ef->next) { if (ef->flags & flags & ZIP_EF_BOTH) { _zip_buffer_set_offset(buffer, 0); _zip_buffer_put_16(buffer, ef->id); _zip_buffer_put_16(buffer, ef->size); if (!_zip_buffer_ok(buffer)) { zip_error_set(&za->error, ZIP_ER_INTERNAL, 0); _zip_buffer_free(buffer); return -1; } if (_zip_write(za, b, 4) < 0) { _zip_buffer_free(buffer); return -1; } if (ef->size > 0) { if (_zip_write(za, ef->data, ef->size) < 0) { _zip_buffer_free(buffer); return -1; } } } } _zip_buffer_free(buffer); return 0; }
bool _zip_ef_parse(const zip_uint8_t *data, zip_uint16_t len, zip_flags_t flags, zip_extra_field_t **ef_head_p, zip_error_t *error) { zip_buffer_t *buffer; zip_extra_field_t *ef, *ef2, *ef_head; if ((buffer = _zip_buffer_new((zip_uint8_t *)data, len)) == NULL) { zip_error_set(error, ZIP_ER_MEMORY, 0); return false; } ef_head = ef = NULL; while (_zip_buffer_ok(buffer) && _zip_buffer_left(buffer) >= 4) { zip_uint16_t fid, flen; zip_uint8_t *ef_data; fid = _zip_buffer_get_16(buffer); flen = _zip_buffer_get_16(buffer); ef_data = _zip_buffer_get(buffer, flen); if (ef_data == NULL) { zip_error_set(error, ZIP_ER_INCONS, 0); _zip_buffer_free(buffer); _zip_ef_free(ef_head); return false; } if ((ef2=_zip_ef_new(fid, flen, ef_data, flags)) == NULL) { zip_error_set(error, ZIP_ER_MEMORY, 0); _zip_buffer_free(buffer); _zip_ef_free(ef_head); return false; } if (ef_head) { ef->next = ef2; ef = ef2; } else ef_head = ef = ef2; } if (!_zip_buffer_eof(buffer)) { /* Android APK files align stored file data with padding in extra fields; ignore. */ /* see https://android.googlesource.com/platform/build/+/master/tools/zipalign/ZipAlign.cpp */ size_t glen = _zip_buffer_left(buffer); zip_uint8_t *garbage; garbage = _zip_buffer_get(buffer, glen); if (glen >= 4 || garbage == NULL || memcmp(garbage, "\0\0\0", glen) != 0) { zip_error_set(error, ZIP_ER_INCONS, 0); _zip_buffer_free(buffer); _zip_ef_free(ef_head); return false; } } _zip_buffer_free(buffer); if (ef_head_p) { *ef_head_p = ef_head; } else { _zip_ef_free(ef_head); } return true; }
static zip_cdir_t * _zip_read_eocd64(zip_source_t *src, zip_buffer_t *buffer, zip_uint64_t buf_offset, unsigned int flags, zip_error_t *error) { zip_cdir_t *cd; zip_uint64_t offset; zip_uint8_t eocd[EOCD64LEN]; zip_uint64_t eocd_offset; zip_uint64_t size, nentry, i, eocdloc_offset; bool free_buffer; zip_uint32_t num_disks, num_disks64, eocd_disk, eocd_disk64; eocdloc_offset = _zip_buffer_offset(buffer); _zip_buffer_get(buffer, 4); /* magic already verified */ num_disks = _zip_buffer_get_16(buffer); eocd_disk = _zip_buffer_get_16(buffer); eocd_offset = _zip_buffer_get_64(buffer); if (eocd_offset > ZIP_INT64_MAX || eocd_offset + EOCD64LEN < eocd_offset) { zip_error_set(error, ZIP_ER_SEEK, EFBIG); return NULL; } if (eocd_offset + EOCD64LEN > eocdloc_offset + buf_offset) { zip_error_set(error, ZIP_ER_INCONS, 0); return NULL; } if (eocd_offset >= buf_offset && eocd_offset + EOCD64LEN <= buf_offset + _zip_buffer_size(buffer)) { _zip_buffer_set_offset(buffer, eocd_offset - buf_offset); free_buffer = false; } else { if (zip_source_seek(src, (zip_int64_t)eocd_offset, SEEK_SET) < 0) { _zip_error_set_from_source(error, src); return NULL; } if ((buffer = _zip_buffer_new_from_source(src, EOCD64LEN, eocd, error)) == NULL) { return NULL; } free_buffer = true; } if (memcmp(_zip_buffer_get(buffer, 4), EOCD64_MAGIC, 4) != 0) { zip_error_set(error, ZIP_ER_INCONS, 0); if (free_buffer) { _zip_buffer_free(buffer); } return NULL; } size = _zip_buffer_get_64(buffer); if ((flags & ZIP_CHECKCONS) && size + eocd_offset + 12 != buf_offset + eocdloc_offset) { zip_error_set(error, ZIP_ER_INCONS, 0); if (free_buffer) { _zip_buffer_free(buffer); } return NULL; } _zip_buffer_get(buffer, 4); /* skip version made by/needed */ num_disks64 = _zip_buffer_get_32(buffer); eocd_disk64 = _zip_buffer_get_32(buffer); /* if eocd values are 0xffff, we have to use eocd64 values. otherwise, if the values are not the same, it's inconsistent; in any case, if the value is not 0, we don't support it */ if (num_disks == 0xffff) { num_disks = num_disks64; } if (eocd_disk == 0xffff) { eocd_disk = eocd_disk64; } if ((flags & ZIP_CHECKCONS) && (eocd_disk != eocd_disk64 || num_disks != num_disks64)) { zip_error_set(error, ZIP_ER_INCONS, 0); if (free_buffer) { _zip_buffer_free(buffer); } return NULL; } if (num_disks != 0 || eocd_disk != 0) { zip_error_set(error, ZIP_ER_MULTIDISK, 0); if (free_buffer) { _zip_buffer_free(buffer); } return NULL; } nentry = _zip_buffer_get_64(buffer); i = _zip_buffer_get_64(buffer); if (nentry != i) { zip_error_set(error, ZIP_ER_MULTIDISK, 0); if (free_buffer) { _zip_buffer_free(buffer); } return NULL; } size = _zip_buffer_get_64(buffer); offset = _zip_buffer_get_64(buffer); if (!_zip_buffer_ok(buffer)) { zip_error_set(error, ZIP_ER_INTERNAL, 0); if (free_buffer) { _zip_buffer_free(buffer); } return NULL; } if (free_buffer) { _zip_buffer_free(buffer); } if (offset > ZIP_INT64_MAX || offset+size < offset) { zip_error_set(error, ZIP_ER_SEEK, EFBIG); return NULL; } if (offset+size > buf_offset + eocd_offset) { /* cdir spans past EOCD record */ zip_error_set(error, ZIP_ER_INCONS, 0); return NULL; } if ((flags & ZIP_CHECKCONS) && offset+size != buf_offset + eocd_offset) { zip_error_set(error, ZIP_ER_INCONS, 0); return NULL; } if ((cd=_zip_cdir_new(nentry, error)) == NULL) return NULL; cd->is_zip64 = true; cd->size = size; cd->offset = offset; return cd; }
static zip_cdir_t * _zip_read_eocd64(zip_source_t *src, zip_buffer_t *buffer, zip_uint64_t buf_offset, unsigned int flags, zip_error_t *error) { zip_cdir_t *cd; zip_uint64_t offset; zip_uint8_t eocd[EOCD64LEN]; zip_uint64_t eocd_offset; zip_uint64_t size, nentry, i, eocdloc_offset; bool free_buffer; eocdloc_offset = _zip_buffer_offset(buffer); _zip_buffer_get(buffer, 8); /* magic and single disk already verified */ eocd_offset = _zip_buffer_get_64(buffer); if (eocd_offset > ZIP_INT64_MAX || eocd_offset + EOCD64LEN < eocd_offset) { zip_error_set(error, ZIP_ER_SEEK, EFBIG); return NULL; } if (eocd_offset + EOCD64LEN > eocdloc_offset + buf_offset) { zip_error_set(error, ZIP_ER_INCONS, 0); return NULL; } if (eocd_offset >= buf_offset && eocd_offset + EOCD64LEN <= buf_offset + _zip_buffer_size(buffer)) { _zip_buffer_set_offset(buffer, eocd_offset - buf_offset); free_buffer = false; } else { if (zip_source_seek(src, (zip_int64_t)eocd_offset, SEEK_SET) < 0) { _zip_error_set_from_source(error, src); return NULL; } if ((buffer = _zip_buffer_new_from_source(src, EOCD64LEN, eocd, error)) == NULL) { return NULL; } free_buffer = true; } if (memcmp(_zip_buffer_get(buffer, 4), EOCD64_MAGIC, 4) != 0) { zip_error_set(error, ZIP_ER_INCONS, 0); if (free_buffer) { _zip_buffer_free(buffer); } return NULL; } size = _zip_buffer_get_64(buffer); if ((flags & ZIP_CHECKCONS) && size + eocd_offset + 12 != buf_offset + eocdloc_offset) { zip_error_set(error, ZIP_ER_INCONS, 0); if (free_buffer) { _zip_buffer_free(buffer); } return NULL; } _zip_buffer_get(buffer, 12); /* skip version made by/needed and num disks */ nentry = _zip_buffer_get_64(buffer); i = _zip_buffer_get_64(buffer); if (nentry != i) { zip_error_set(error, ZIP_ER_MULTIDISK, 0); if (free_buffer) { _zip_buffer_free(buffer); } return NULL; } size = _zip_buffer_get_64(buffer); offset = _zip_buffer_get_64(buffer); if (!_zip_buffer_ok(buffer)) { zip_error_set(error, ZIP_ER_INTERNAL, 0); if (free_buffer) { _zip_buffer_free(buffer); } return NULL; } if (free_buffer) { _zip_buffer_free(buffer); } if (offset > ZIP_INT64_MAX || offset+size < offset) { zip_error_set(error, ZIP_ER_SEEK, EFBIG); return NULL; } if ((flags & ZIP_CHECKCONS) && offset+size != eocd_offset) { zip_error_set(error, ZIP_ER_INCONS, 0); return NULL; } if ((cd=_zip_cdir_new(nentry, error)) == NULL) return NULL; cd->size = size; cd->offset = offset; return cd; }