// make buffer with 32/64 bit Data Descriptor chunk, this follows files with incomplete headers ngx_chain_t* ngx_http_zip_data_descriptor_chain_link(ngx_http_request_t *r, ngx_http_zip_piece_t *piece, ngx_http_zip_range_t *range) { ngx_chain_t *link; ngx_buf_t *b; ngx_http_zip_file_t *file = piece->file; size_t struct_size = file->need_zip64? sizeof(ngx_zip_data_descriptor_zip64_t) : sizeof(ngx_zip_data_descriptor_t); union { ngx_zip_data_descriptor_t descriptor; ngx_zip_data_descriptor_zip64_t descriptor64; } data; if ((link = ngx_alloc_chain_link(r->pool)) == NULL || (b = ngx_calloc_buf(r->pool)) == NULL || (b->pos = ngx_palloc(r->pool, struct_size)) == NULL) return NULL; b->memory = 1; b->last = b->pos + struct_size; if (!file->need_zip64) { data.descriptor = ngx_zip_data_descriptor_template; data.descriptor.signature = htole32(data.descriptor.signature); data.descriptor.crc32 = htole32(file->crc32); data.descriptor.compressed_size = data.descriptor.uncompressed_size = htole32(file->size); } else { data.descriptor64 = ngx_zip_data_descriptor_zip64_template; data.descriptor64.signature = htole32(data.descriptor64.signature); data.descriptor64.crc32 = htole32(file->crc32); data.descriptor64.compressed_size = data.descriptor64.uncompressed_size = htole64(file->size); } ngx_memcpy(b->pos, &data, struct_size); ngx_http_zip_truncate_buffer(b, &piece->range, range); link->buf = b; link->next = NULL; return link; }
//make archive footer: Central Directory, Zip64 Central Directory End, Zip64 locator and Central Directory end chunks ngx_chain_t * ngx_http_zip_central_directory_chain_link(ngx_http_request_t *r, ngx_http_zip_ctx_t *ctx, ngx_http_zip_piece_t *piece, ngx_http_zip_range_t *range) { //nb: this is to be called only after 'generate pieces' ngx_chain_t *trailer; ngx_buf_t *trailer_buf; u_char *p; off_t cd_size; ngx_uint_t i; ngx_array_t *files; ngx_zip_end_of_central_directory_record_t eocdr; ngx_zip_zip64_end_of_central_directory_record_t eocdr64; ngx_zip_zip64_end_of_central_directory_locator_t locator64; if (!ctx || !ctx->cd_size || (trailer = ngx_alloc_chain_link(r->pool)) == NULL || (trailer_buf = ngx_calloc_buf(r->pool)) == NULL || (p = ngx_palloc(r->pool, ctx->cd_size)) == NULL) return NULL; files = &ctx->files; trailer->buf = trailer_buf; trailer->next = NULL; trailer_buf->pos = p; trailer_buf->last = p + ctx->cd_size; trailer_buf->last_buf = 1; trailer_buf->sync = 1; trailer_buf->memory = 1; for (i = 0; i < files->nelts; i++) p = ngx_http_zip_write_central_directory_entry(p, &((ngx_http_zip_file_t *)files->elts)[i], ctx); eocdr = ngx_zip_end_of_central_directory_record_template; eocdr.signature = htole32(eocdr.signature); if (files->nelts < NGX_MAX_UINT16_VALUE) { eocdr.disk_entries_n = htole16(files->nelts); eocdr.entries_n = htole16(files->nelts); } cd_size = ctx->cd_size - sizeof(ngx_zip_end_of_central_directory_record_t) - (!!ctx->zip64_used)*(sizeof(ngx_zip_zip64_end_of_central_directory_record_t) + sizeof(ngx_zip_zip64_end_of_central_directory_locator_t)); if (cd_size < (off_t) NGX_MAX_UINT32_VALUE) eocdr.size = htole32(cd_size); if (piece->range.start < (off_t) NGX_MAX_UINT32_VALUE) eocdr.offset = htole32(piece->range.start); if (ctx->zip64_used) { eocdr64 = ngx_zip_zip64_end_of_central_directory_record_template; eocdr64.signature = htole32(eocdr64.signature); eocdr64.size = htole64(eocdr64.size); eocdr64.version_made_by = htole16(eocdr64.version_made_by); eocdr64.version_needed = htole16(eocdr64.version_made_by); eocdr64.cd_n_entries_on_this_disk = eocdr64.cd_n_entries_total = htole64(files->nelts); eocdr64.cd_size = htole64(cd_size); eocdr64.cd_offset = htole64(piece->range.start); ngx_memcpy(p, &eocdr64, sizeof(ngx_zip_zip64_end_of_central_directory_record_t)); p += sizeof(ngx_zip_zip64_end_of_central_directory_record_t); locator64 = ngx_zip_zip64_end_of_central_directory_locator_template; locator64.signature = htole32(locator64.signature); locator64.disks_total_n = htole32(locator64.disks_total_n); locator64.cd_relative_offset = htole64(piece->range.start + cd_size); ngx_memcpy(p, &locator64, sizeof(ngx_zip_zip64_end_of_central_directory_locator_t)); p += sizeof(ngx_zip_zip64_end_of_central_directory_locator_t); } ngx_memcpy(p, &eocdr, sizeof(ngx_zip_end_of_central_directory_record_t)); ngx_http_zip_truncate_buffer(trailer->buf, &piece->range, range); return trailer; }
// make Local File Header chunk with extra fields ngx_chain_t* ngx_http_zip_file_header_chain_link(ngx_http_request_t *r, ngx_http_zip_ctx_t *ctx, ngx_http_zip_piece_t *piece, ngx_http_zip_range_t *range) { ngx_chain_t *link; ngx_buf_t *b; ngx_http_zip_file_t *file = piece->file; ngx_zip_extra_field_local_t extra_field_local; ngx_zip_extra_field_zip64_sizes_only_t extra_field_zip64; ngx_zip_local_file_header_t local_file_header; ngx_zip_extra_field_unicode_path_t extra_field_unicode_path; size_t len = sizeof(ngx_zip_local_file_header_t) + file->filename.len + sizeof(ngx_zip_extra_field_local_t) + (file->need_zip64? sizeof(ngx_zip_extra_field_zip64_sizes_only_t):0 + (ctx->unicode_path && file->filename_utf8.len ? (sizeof(ngx_zip_extra_field_unicode_path_t) + file->filename_utf8.len): 0)); if ((link = ngx_alloc_chain_link(r->pool)) == NULL || (b = ngx_calloc_buf(r->pool)) == NULL || (b->pos = ngx_pcalloc(r->pool, len)) == NULL) return NULL; b->memory = 1; b->last = b->pos + len; /* A note about the ZIP format: in order to appease all ZIP software I * could find, the local file header contains the file sizes but not the * CRC-32, even though setting the third bit of the general purpose bit * flag would indicate that all three fields should be zeroed out. */ local_file_header = ngx_zip_local_file_header_template; local_file_header.signature = htole32(local_file_header.signature); local_file_header.version = htole16(local_file_header.version); local_file_header.flags = htole16(local_file_header.flags); local_file_header.mtime = htole32(file->dos_time); local_file_header.filename_len = htole16(file->filename.len); if (ctx->native_charset) { local_file_header.flags &= htole16(~zip_utf8_flag); } extra_field_zip64 = ngx_zip_extra_field_zip64_sizes_only_template; extra_field_zip64.tag = htole16(extra_field_zip64.tag); extra_field_zip64.size = htole16(extra_field_zip64.size); if (file->need_zip64) { local_file_header.version = htole16(zip_version_zip64); local_file_header.extra_field_len = sizeof(ngx_zip_extra_field_zip64_sizes_only_t) + sizeof(ngx_zip_extra_field_local_t); extra_field_zip64.uncompressed_size = extra_field_zip64.compressed_size = htole64(file->size); } else { local_file_header.compressed_size = htole32(file->size); local_file_header.uncompressed_size = htole32(file->size); } extra_field_unicode_path = ngx_zip_extra_field_unicode_path_template; extra_field_unicode_path.tag = htole16(extra_field_unicode_path.tag); if (ctx->unicode_path && file->filename_utf8.len) { extra_field_unicode_path.crc32 = htole32(file->filename_utf8_crc32); extra_field_unicode_path.size = htole16(sizeof(ngx_zip_extra_field_unicode_path_t) + file->filename_utf8.len); local_file_header.extra_field_len += sizeof(ngx_zip_extra_field_unicode_path_t) + file->filename_utf8.len; } local_file_header.extra_field_len = htole16(local_file_header.extra_field_len); if (!file->missing_crc32) { local_file_header.flags &= htole16(~zip_missing_crc32_flag); local_file_header.crc32 = htole32(file->crc32); } extra_field_local = ngx_zip_extra_field_local_template; extra_field_local.tag = htole16(extra_field_local.tag); extra_field_local.size = htole16(extra_field_local.size); extra_field_local.mtime = htole32(file->unix_time); extra_field_local.atime = htole32(file->unix_time); ngx_memcpy(b->pos, &local_file_header, sizeof(ngx_zip_local_file_header_t)); ngx_memcpy(b->pos + sizeof(ngx_zip_local_file_header_t), file->filename.data, file->filename.len); ngx_memcpy(b->pos + sizeof(ngx_zip_local_file_header_t) + file->filename.len, &extra_field_local, sizeof(ngx_zip_extra_field_local_t)); if (file->need_zip64) { ngx_memcpy(b->pos + sizeof(ngx_zip_local_file_header_t) + file->filename.len + sizeof(ngx_zip_extra_field_local_t), &extra_field_zip64, sizeof(ngx_zip_extra_field_zip64_sizes_only_t)); if (ctx->unicode_path && file->filename_utf8.len) { ngx_memcpy(b->pos + sizeof(ngx_zip_local_file_header_t) + file->filename.len + sizeof(ngx_zip_extra_field_local_t) + sizeof(ngx_zip_extra_field_zip64_sizes_only_t), &extra_field_unicode_path, sizeof(ngx_zip_extra_field_unicode_path_t)); ngx_memcpy(b->pos + sizeof(ngx_zip_local_file_header_t) + file->filename.len + sizeof(ngx_zip_extra_field_local_t) + sizeof(ngx_zip_extra_field_zip64_sizes_only_t) + sizeof(ngx_zip_extra_field_unicode_path_t), file->filename_utf8.data, file->filename_utf8.len); } } else if (ctx->unicode_path && file->filename_utf8.len) { ngx_memcpy(b->pos + sizeof(ngx_zip_local_file_header_t) + file->filename.len + sizeof(ngx_zip_extra_field_local_t), &extra_field_unicode_path, sizeof(ngx_zip_extra_field_unicode_path_t)); ngx_memcpy(b->pos + sizeof(ngx_zip_local_file_header_t) + file->filename.len + sizeof(ngx_zip_extra_field_local_t) + sizeof(ngx_zip_extra_field_unicode_path_t), file->filename_utf8.data, file->filename_utf8.len); } ngx_http_zip_truncate_buffer(b, &piece->range, range); link->buf = b; link->next = NULL; return link; }
// make Local File Header chunk with extra fields ngx_chain_t* ngx_http_zip_file_header_chain_link(ngx_http_request_t *r, ngx_http_zip_ctx_t *ctx, ngx_http_zip_piece_t *piece, ngx_http_zip_range_t *range) { ngx_chain_t *link; ngx_buf_t *b; ngx_http_zip_file_t *file = piece->file; ngx_zip_extra_field_local_t extra_field_local; ngx_zip_extra_field_zip64_sizes_only_t extra_field_zip64; ngx_zip_local_file_header_t local_file_header; size_t len = sizeof(ngx_zip_local_file_header_t) + file->filename.len + sizeof(ngx_zip_extra_field_local_t) + (file->need_zip64? sizeof(ngx_zip_extra_field_zip64_sizes_only_t):0); if ((link = ngx_alloc_chain_link(r->pool)) == NULL || (b = ngx_calloc_buf(r->pool)) == NULL || (b->pos = ngx_pcalloc(r->pool, len)) == NULL) return NULL; b->memory = 1; b->last = b->pos + len; file->unix_time = time(NULL); file->dos_time = ngx_dos_time(file->unix_time); /* A note about the ZIP format: in order to appease all ZIP software I * could find, the local file header contains the file sizes but not the * CRC-32, even though setting the third bit of the general purpose bit * flag would indicate that all three fields should be zeroed out. */ local_file_header = ngx_zip_local_file_header_template; local_file_header.mtime = file->dos_time; local_file_header.filename_len = file->filename.len; if (file->need_zip64) { local_file_header.version = zip_version_zip64; local_file_header.extra_field_len = sizeof(ngx_zip_extra_field_zip64_sizes_only_t) + sizeof(ngx_zip_extra_field_local_t); extra_field_zip64 = ngx_zip_extra_field_zip64_sizes_only_template; extra_field_zip64.uncompressed_size = extra_field_zip64.compressed_size = file->size; } else { local_file_header.compressed_size = file->size; local_file_header.uncompressed_size = file->size; } if (!file->missing_crc32) { local_file_header.flags &= ~zip_missing_crc32_flag; local_file_header.crc32 = file->crc32; } extra_field_local = ngx_zip_extra_field_local_template; extra_field_local.mtime = file->unix_time; extra_field_local.atime = file->unix_time; ngx_memcpy(b->pos, &local_file_header, sizeof(ngx_zip_local_file_header_t)); ngx_memcpy(b->pos + sizeof(ngx_zip_local_file_header_t), file->filename.data, file->filename.len); ngx_memcpy(b->pos + sizeof(ngx_zip_local_file_header_t) + file->filename.len, &extra_field_local, sizeof(ngx_zip_extra_field_local_t)); if (file->need_zip64) ngx_memcpy(b->pos + sizeof(ngx_zip_local_file_header_t) + file->filename.len + sizeof(ngx_zip_extra_field_local_t), &extra_field_zip64, sizeof(ngx_zip_extra_field_zip64_sizes_only_t)); ngx_http_zip_truncate_buffer(b, &piece->range, range); link->buf = b; link->next = NULL; return link; }