Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Added field filters and decompression memory preallocation

  • Loading branch information...
commit aaa500ae2de95f77d18d5b5895d00e12317477a2 1 parent a378f00
@vkholodkov authored
Showing with 742 additions and 292 deletions.
  1. +519 −119 ngx_http_unzip_filter_module.c
  2. +5 −0 ngx_http_upload.h
  3. +218 −173 ngx_http_upload_module.c
View
638 ngx_http_unzip_filter_module.c
@@ -12,15 +12,17 @@
#define NGX_UNZIP_MALFORMED NGX_UPLOAD_MALFORMED
#define LOCAL_DATA_HEADER_SIGNATURE 0x04034b50
-#define ARCHIVE_EXTRA_DATA_SIGNATURE 0x08064b50
#define CENTRAL_FILE_HEADER_SIGNATURE 0x02014b50
#define DIGITAL_SIGNATURE_HEADER_SIGNATURE 0x05054b50
#define END_OF_CENTRAL_DIR_SIGNATURE 0x06054b50
+#define EXTRA_DATA_RECORD_SIGNATURE 0x08064b50
#define SIGNATURE_LEN 4
#define LOCAL_DATA_HEADER_LEN 30
#define FILE_HEADER_LEN 46
#define DATA_DESCRIPTOR_LEN 12
#define END_OF_CENTRAL_DIR_LEN 18
+#define DIGITAL_SIGNATURE_HEADER_LEN 6
+#define EXTRA_DATA_RECORD_LEN 8
#define ZIP_METHOD_STORED 0
#define ZIP_METHOD_DEFLATED 8
@@ -60,8 +62,8 @@ typedef struct {
typedef struct {
ngx_bufs_t bufs;
- size_t wbits;
- size_t max_file_name_len;
+ ssize_t wbits;
+ ssize_t max_file_name_len;
unsigned int recursive:1;
} ngx_unzip_conf_t;
@@ -73,11 +75,16 @@ typedef enum {
unzip_state_file_data,
unzip_state_unlimited_file_data,
unzip_state_data_descriptor,
- unzip_state_decryption_header,
unzip_state_extra_data_record,
+ unzip_state_extra_data_record_data,
unzip_state_file_header,
+ unzip_state_file_header_file_name,
+ unzip_state_file_header_extra_field,
+ unzip_state_file_header_file_comment,
unzip_state_digital_sig_header,
+ unzip_state_digital_sig,
unzip_state_central_directory_end,
+ unzip_state_archive_comment,
unzip_state_finish
} ngx_unzip_state_e;
@@ -88,10 +95,10 @@ typedef struct {
uint16_t last_mod_time;
uint16_t last_mod_date;
uint32_t crc32;
- size_t compressed_size;
- size_t uncompressed_size;
- size_t file_name_len;
- size_t extra_field_len;
+ ssize_t compressed_size;
+ ssize_t uncompressed_size;
+ ssize_t file_name_len;
+ ssize_t extra_field_len;
} ngx_unzip_local_data_header_t;
typedef struct {
@@ -108,68 +115,74 @@ typedef struct {
size_t extra_field_len;
size_t file_comment_len;
uint16_t disk_number_start;
- uint16_t internal_file_attributes;
- uint16_t external_file_attributes;
+ uint16_t internal_file_attrs;
+ uint16_t external_file_attrs;
off_t relative_offset;
-} ngx_unzip_file_data_header_t;
+} ngx_unzip_file_header_t;
typedef struct {
size_t data_size;
-} ngx_unzip_digital_signature_t;
+} ngx_unzip_digital_sig_t;
typedef struct {
- uint16_t version_made_by;
- uint16_t version_needed;
- uint16_t flags;
- uint16_t compression_method;
- uint16_t last_mod_time;
- uint16_t last_mod_date;
- uint32_t crc32;
- size_t compressed_size;
- size_t uncompressed_size;
- size_t file_name_len;
size_t extra_field_len;
- size_t file_comment_len;
- uint16_t disk_number_start;
- uint16_t internal_file_attrs;
- uint16_t external_file_attrs;
- off_t relative_offset;
+} ngx_unzip_extra_data_record_t;
+
+typedef struct {
+ uint16_t disk_number;
+ uint16_t number_of_disk_with_start_of_dir;
+ uint16_t number_of_entries_on_this_disk;
+ uint16_t total_number_of_entries;
+ size_t size_of_directory;
+ size_t offset_of_start;
+ uint16_t archive_comment_len;
} ngx_unzip_end_of_central_dir_t;
+typedef union {
+ ngx_unzip_local_data_header_t local_data_header;
+ ngx_unzip_file_header_t file_header;
+ ngx_unzip_digital_sig_t digital_sig;
+ ngx_unzip_end_of_central_dir_t end_of_central_dir;
+ ngx_unzip_extra_data_record_t extra_data_record;
+} ngx_unzip_record_t;
+
typedef struct ngx_unzip_ctx_s {
struct ngx_unzip_ctx_s *parent;
ngx_unzip_state_e state;
- size_t current_field_len;
- size_t current_field_pos;
+ off_t current_field_len;
+ off_t current_field_pos;
ngx_pool_t *pool;
ngx_log_t *log;
- u_char accumulator[512];
+ u_char accumulator[sizeof(ngx_unzip_record_t)];
u_char *current_field;
u_char *current_field_ptr;
- uint16_t version_needed;
- uint16_t flags;
- uint16_t compression_method;
- uint16_t last_mod_time;
- uint16_t last_mod_date;
- uint32_t crc32;
- size_t compressed_size;
- size_t uncompressed_size;
- size_t file_name_len;
- size_t extra_field_len;
+ union {
+ ngx_unzip_local_data_header_t local_data_header;
+ ngx_unzip_file_header_t file_header;
+ ngx_unzip_digital_sig_t digital_sig;
+ ngx_unzip_end_of_central_dir_t end_of_central_dir;
+ ngx_unzip_extra_data_record_t extra_data_record;
+ };
ngx_str_t file_name;
+ void *preallocated;
+ char *free_mem;
+ ngx_uint_t allocated;
+
z_stream stream;
ngx_buf_t *output_buffer;
ngx_chain_t *output_chain;
+ ngx_chain_t *buf_pool;
ngx_http_upload_ctx_t *upload_ctx;
+ ngx_upload_field_filter_t *next_field_filter;
ngx_upload_content_filter_t *next_content_filter;
ngx_unzip_decompression_method_t *decompression_method;
@@ -178,6 +191,17 @@ typedef struct ngx_unzip_ctx_s {
unsigned int discard_data:1;
} ngx_unzip_ctx_t;
+static void *ngx_http_unzip_filter_alloc(void *opaque, u_int items,
+ u_int size);
+static void ngx_http_unzip_filter_free(void *opaque, void *address);
+static void ngx_http_unzip_error(ngx_unzip_ctx_t *ctx);
+void ngx_http_unzip_split_chain(ngx_chain_t *cl, off_t *limit, ngx_buf_t *buf, ngx_chain_t *newcl);
+ngx_int_t
+ngx_http_unzip_chain_copy_range(ngx_chain_t *chain, ngx_chain_t **copy, off_t *limit,
+ ngx_pool_t *pool, ngx_chain_t **free);
+void ngx_http_unzip_reclaim_chain(ngx_chain_t *cl, ngx_chain_t **free);
+void ngx_http_unzip_chain_advance(ngx_chain_t *chain, ngx_chain_t *copy);
+
static char * ngx_http_unzip_command(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
static void *ngx_http_unzip_create_loc_conf(ngx_conf_t *cf);
@@ -187,10 +211,10 @@ static void ngx_http_unzip_abort_handler(ngx_http_upload_ctx_t *u);
static ngx_int_t ngx_http_unzip_data_handler(ngx_http_upload_ctx_t *u,
ngx_chain_t *chain);
-static ngx_int_t ngx_http_unzip_retrieve_start(ngx_unzip_ctx_t *ctx);
-static void ngx_http_unzip_retrieve_finish(ngx_unzip_ctx_t *ctx);
-static void ngx_http_unzip_retrieve_abort(ngx_unzip_ctx_t *ctx);
-static ngx_int_t ngx_http_unzip_retrieve_process_chain(ngx_unzip_ctx_t *ctx, ngx_chain_t *chain);
+static ngx_int_t ngx_http_unzip_extract_start(ngx_unzip_ctx_t *ctx);
+static void ngx_http_unzip_extract_finish(ngx_unzip_ctx_t *ctx);
+static void ngx_http_unzip_extract_abort(ngx_unzip_ctx_t *ctx);
+static ngx_int_t ngx_http_unzip_extract_process_chain(ngx_unzip_ctx_t *ctx, ngx_chain_t *chain);
static ngx_int_t ngx_http_unzip_inflate_start(ngx_unzip_ctx_t *ctx);
static void ngx_http_unzip_inflate_finish(ngx_unzip_ctx_t *ctx);
@@ -207,10 +231,10 @@ static ngx_unzip_decompression_method_t /* {{{ */
ngx_unzip_decompression_methods[] = {
{ ZIP_METHOD_STORED,
- ngx_http_unzip_retrieve_start,
- ngx_http_unzip_retrieve_finish,
- ngx_http_unzip_retrieve_abort,
- ngx_http_unzip_retrieve_process_chain },
+ ngx_http_unzip_extract_start,
+ ngx_http_unzip_extract_finish,
+ ngx_http_unzip_extract_abort,
+ ngx_http_unzip_extract_process_chain },
{ ZIP_METHOD_DEFLATED,
ngx_http_unzip_inflate_start,
@@ -379,16 +403,23 @@ ngx_http_unzip_process_chain(ngx_unzip_ctx_t *ctx, ngx_chain_t *chain) {
case LOCAL_DATA_HEADER_SIGNATURE:
ctx->state = unzip_state_local_data_header;
break;
- case ARCHIVE_EXTRA_DATA_SIGNATURE:
- ctx->state = unzip_state_local_data_header;
+ case EXTRA_DATA_RECORD_SIGNATURE:
+ ctx->state = unzip_state_extra_data_record;
break;
case CENTRAL_FILE_HEADER_SIGNATURE:
ctx->state = unzip_state_file_header;
break;
+ case END_OF_CENTRAL_DIR_SIGNATURE:
+ ctx->state = unzip_state_central_directory_end;
+ break;
case DIGITAL_SIGNATURE_HEADER_SIGNATURE:
ctx->state = unzip_state_digital_sig_header;
break;
default:
+ ngx_log_error(NGX_LOG_ALERT, ctx->log, 0
+ , "unknown signature in ZIP file: %uxD"
+ , (uint32_t)EXTRACT_LONG(ctx->current_field)
+ );
return NGX_UNZIP_MALFORMED;
}
}
@@ -407,46 +438,61 @@ ngx_http_unzip_process_chain(ngx_unzip_ctx_t *ctx, ngx_chain_t *chain) {
if(ctx->current_field_pos == ctx->current_field_len) {
ctx->current_field_pos = 0;
- ctx->version_needed = EXTRACT_SHORT(ctx->current_field);
- ctx->flags = EXTRACT_SHORT(ctx->current_field + 2);
- ctx->compression_method = EXTRACT_SHORT(ctx->current_field + 4);
- ctx->last_mod_time = EXTRACT_SHORT(ctx->current_field + 6);
- ctx->last_mod_date = EXTRACT_SHORT(ctx->current_field + 8);
- ctx->crc32 = EXTRACT_LONG(ctx->current_field + 10);
- ctx->compressed_size = EXTRACT_LONG(ctx->current_field + 14);
- ctx->uncompressed_size = EXTRACT_LONG(ctx->current_field + 18);
-
- ctx->file_name_len = EXTRACT_SHORT(ctx->current_field + 22);
- ctx->extra_field_len = EXTRACT_SHORT(ctx->current_field + 24);
-
- if(uzcf->max_file_name_len > 0 && ctx->file_name_len > uzcf->max_file_name_len) {
- ngx_log_error(NGX_LOG_ALERT, ctx->log, 0,
- "file name in is too long: %u", ctx->file_name_len);
+ ctx->local_data_header.version_needed = EXTRACT_SHORT(ctx->current_field);
+ ctx->local_data_header.flags = EXTRACT_SHORT(ctx->current_field + 2);
+ ctx->local_data_header.compression_method = EXTRACT_SHORT(ctx->current_field + 4);
+ ctx->local_data_header.last_mod_time = EXTRACT_SHORT(ctx->current_field + 6);
+ ctx->local_data_header.last_mod_date = EXTRACT_SHORT(ctx->current_field + 8);
+ ctx->local_data_header.crc32 = EXTRACT_LONG(ctx->current_field + 10);
+ ctx->local_data_header.compressed_size = EXTRACT_LONG(ctx->current_field + 14);
+ ctx->local_data_header.uncompressed_size = EXTRACT_LONG(ctx->current_field + 18);
+
+ ctx->local_data_header.file_name_len = EXTRACT_SHORT(ctx->current_field + 22);
+ ctx->local_data_header.extra_field_len = EXTRACT_SHORT(ctx->current_field + 24);
+
+ if(uzcf->max_file_name_len > 0 &&
+ ctx->local_data_header.file_name_len > uzcf->max_file_name_len)
+ {
+ ngx_log_error(NGX_LOG_ALERT, ctx->log, 0
+ , "file name in is too long: %u"
+ , ctx->local_data_header.file_name_len
+ );
return NGX_UNZIP_MALFORMED;
}
if(ngx_http_unzip_set_decompression_method(ctx,
- ctx->compression_method) != NGX_OK)
+ ctx->local_data_header.compression_method) != NGX_OK)
{
return NGX_UNZIP_MALFORMED;
}
- if(ctx->version_needed > ZIP_VERSION)
+ if(ctx->local_data_header.version_needed > ZIP_VERSION)
+ {
+ ngx_log_error(NGX_LOG_ALERT, ctx->log, 0
+ , "more recent version of unzip implementation required: %u, have %u"
+ , ctx->local_data_header.version_needed, ZIP_VERSION
+ );
+ return NGX_UNZIP_MALFORMED;
+ }
+
+ if(ctx->local_data_header.flags
+ & (ZIP_FLAG_ENCRYPTED|ZIP_FLAG_STRONG_ENCRYPTION|ZIP_FLAG_LOCAL_HEADER_OBFUSCATED))
{
- ngx_log_error(NGX_LOG_ALERT, ctx->log, 0,
- "more recent version of unzip implementation required: %u, have %u", ctx->version_needed, ZIP_VERSION);
+ ngx_log_error(NGX_LOG_INFO, ctx->log, 0
+ , "skipping encrypted ZIP file"
+ );
return NGX_UNZIP_MALFORMED;
}
- if(ctx->file_name_len > 0)
+ if(ctx->local_data_header.file_name_len > 0)
ctx->state = unzip_state_file_name;
- else if(ctx->extra_field_len > 0)
+ else if(ctx->local_data_header.extra_field_len > 0)
ctx->state = unzip_state_extra_field;
- else if(ctx->flags & ZIP_FLAG_UNLIMITED)
+ else if(ctx->local_data_header.flags & ZIP_FLAG_UNLIMITED)
ctx->state = unzip_state_unlimited_file_data;
- else if(ctx->compressed_size > 0)
+ else if(ctx->local_data_header.compressed_size > 0)
ctx->state = unzip_state_file_data;
- else if(ctx->flags & ZIP_FLAG_UNLIMITED)
+ else if(ctx->local_data_header.flags & ZIP_FLAG_UNLIMITED)
ctx->state = unzip_state_data_descriptor;
else
ctx->state = unzip_state_signature;
@@ -454,15 +500,15 @@ ngx_http_unzip_process_chain(ngx_unzip_ctx_t *ctx, ngx_chain_t *chain) {
break; /* }}} */
case unzip_state_file_name: /* {{{ */
if(ctx->current_field_pos == 0) {
- ctx->file_name.len = ctx->file_name_len;
+ ctx->file_name.len = ctx->local_data_header.file_name_len;
- ctx->file_name.data = ngx_palloc(ctx->pool, ctx->file_name_len);
+ ctx->file_name.data = ngx_palloc(ctx->pool, ctx->file_name.len);
if(ctx->file_name.data == NULL) {
return NGX_UPLOAD_NOMEM;
}
- ctx->current_field_len = ctx->file_name_len;
+ ctx->current_field_len = ctx->local_data_header.file_name_len;
ctx->current_field_ptr = ctx->current_field = ctx->file_name.data;
}
@@ -476,13 +522,13 @@ ngx_http_unzip_process_chain(ngx_unzip_ctx_t *ctx, ngx_chain_t *chain) {
return NGX_UNZIP_MALFORMED;
}
- if(ctx->extra_field_len > 0)
+ if(ctx->local_data_header.extra_field_len > 0)
ctx->state = unzip_state_extra_field;
- else if(ctx->flags & ZIP_FLAG_UNLIMITED)
+ else if(ctx->local_data_header.flags & ZIP_FLAG_UNLIMITED)
ctx->state = unzip_state_unlimited_file_data;
- else if(ctx->compressed_size > 0)
+ else if(ctx->local_data_header.compressed_size > 0)
ctx->state = unzip_state_file_data;
- else if(ctx->flags & ZIP_FLAG_UNLIMITED)
+ else if(ctx->local_data_header.flags & ZIP_FLAG_UNLIMITED)
ctx->state = unzip_state_data_descriptor;
else
ctx->state = unzip_state_signature;
@@ -490,7 +536,7 @@ ngx_http_unzip_process_chain(ngx_unzip_ctx_t *ctx, ngx_chain_t *chain) {
break; /* }}} */
case unzip_state_extra_field:
if(ctx->current_field_pos == 0) {
- ctx->current_field_len = ctx->extra_field_len;
+ ctx->current_field_len = ctx->local_data_header.extra_field_len;
}
ctx->current_field_pos++;
@@ -498,11 +544,11 @@ ngx_http_unzip_process_chain(ngx_unzip_ctx_t *ctx, ngx_chain_t *chain) {
if(ctx->current_field_pos == ctx->current_field_len) {
ctx->current_field_pos = 0;
- if(ctx->flags & ZIP_FLAG_UNLIMITED)
+ if(ctx->local_data_header.flags & ZIP_FLAG_UNLIMITED)
ctx->state = unzip_state_unlimited_file_data;
- else if(ctx->compressed_size > 0)
+ else if(ctx->local_data_header.compressed_size > 0)
ctx->state = unzip_state_file_data;
- else if(ctx->flags & ZIP_FLAG_UNLIMITED)
+ else if(ctx->local_data_header.flags & ZIP_FLAG_UNLIMITED)
ctx->state = unzip_state_data_descriptor;
else
ctx->state = unzip_state_signature;
@@ -510,7 +556,7 @@ ngx_http_unzip_process_chain(ngx_unzip_ctx_t *ctx, ngx_chain_t *chain) {
break;
case unzip_state_file_data: /* {{{ */
if(ctx->current_field_pos == 0) {
- ctx->current_field_len = ctx->compressed_size;
+ ctx->current_field_len = ctx->local_data_header.compressed_size;
if(ctx->decompression_method->start(ctx) != NGX_OK) {
ctx->discard_data = 1;
@@ -591,16 +637,44 @@ ngx_http_unzip_process_chain(ngx_unzip_ctx_t *ctx, ngx_chain_t *chain) {
if(ctx->current_field_pos == ctx->current_field_len) {
ctx->current_field_pos = 0;
- ctx->crc32 = EXTRACT_LONG(ctx->current_field);
- ctx->compressed_size = EXTRACT_LONG(ctx->current_field + 4);
- ctx->uncompressed_size = EXTRACT_LONG(ctx->current_field + 8);
+ ctx->local_data_header.crc32 = EXTRACT_LONG(ctx->current_field);
+ ctx->local_data_header.compressed_size = EXTRACT_LONG(ctx->current_field + 4);
+ ctx->local_data_header.uncompressed_size = EXTRACT_LONG(ctx->current_field + 8);
ctx->state = unzip_state_signature;
}
break;
- case unzip_state_decryption_header:
- break;
case unzip_state_extra_data_record:
+ if(ctx->current_field_pos == 0) {
+ ctx->current_field_len = EXTRA_DATA_RECORD_LEN - SIGNATURE_LEN;
+ ctx->current_field_ptr = ctx->current_field = ctx->accumulator;
+ }
+
+ *ctx->current_field_ptr++ = *buf->pos;
+ ctx->current_field_pos++;
+
+ if(ctx->current_field_pos == ctx->current_field_len) {
+ ctx->current_field_pos = 0;
+
+ ctx->extra_data_record.extra_field_len = EXTRACT_LONG(ctx->current_field);
+
+ if(ctx->extra_data_record.extra_field_len > 0)
+ ctx->state = unzip_state_extra_data_record_data;
+ else
+ ctx->state = unzip_state_signature;
+ }
+ break;
+ case unzip_state_extra_data_record_data:
+ if(ctx->current_field_pos == 0) {
+ ctx->current_field_len = ctx->extra_data_record.extra_field_len;
+ }
+
+ ctx->current_field_pos++;
+
+ if(ctx->current_field_pos == ctx->current_field_len) {
+ ctx->current_field_pos = 0;
+ ctx->state = unzip_state_signature;
+ }
break;
case unzip_state_file_header:
if(ctx->current_field_pos == 0) {
@@ -613,10 +687,115 @@ ngx_http_unzip_process_chain(ngx_unzip_ctx_t *ctx, ngx_chain_t *chain) {
if(ctx->current_field_pos == ctx->current_field_len) {
ctx->current_field_pos = 0;
+
+ ctx->file_header.version_made_by = EXTRACT_SHORT(ctx->current_field);
+ ctx->file_header.version_needed = EXTRACT_SHORT(ctx->current_field + 2);
+ ctx->file_header.flags = EXTRACT_SHORT(ctx->current_field + 4);
+ ctx->file_header.compression_method = EXTRACT_SHORT(ctx->current_field + 6);
+ ctx->file_header.last_mod_time = EXTRACT_SHORT(ctx->current_field + 8);
+ ctx->file_header.last_mod_date = EXTRACT_SHORT(ctx->current_field + 10);
+ ctx->file_header.crc32 = EXTRACT_LONG(ctx->current_field + 12);
+ ctx->file_header.compressed_size = EXTRACT_LONG(ctx->current_field + 16);
+ ctx->file_header.uncompressed_size = EXTRACT_LONG(ctx->current_field + 20);
+
+ ctx->file_header.file_name_len = EXTRACT_SHORT(ctx->current_field + 24);
+ ctx->file_header.extra_field_len = EXTRACT_SHORT(ctx->current_field + 26);
+ ctx->file_header.file_comment_len = EXTRACT_SHORT(ctx->current_field + 38);
+
+ ctx->file_header.disk_number_start = EXTRACT_SHORT(ctx->current_field + 40);
+ ctx->file_header.internal_file_attrs = EXTRACT_SHORT(ctx->current_field + 42);
+ ctx->file_header.external_file_attrs = EXTRACT_LONG(ctx->current_field + 46);
+ ctx->file_header.relative_offset = EXTRACT_LONG(ctx->current_field + 50);
+
+ if(ctx->file_header.file_name_len > 0)
+ ctx->state = unzip_state_file_header_file_name;
+ else if(ctx->file_header.extra_field_len > 0)
+ ctx->state = unzip_state_file_header_extra_field;
+ else if(ctx->file_header.file_comment_len > 0)
+ ctx->state = unzip_state_file_header_file_comment;
+ else
+ ctx->state = unzip_state_signature;
+ }
+ break;
+ case unzip_state_file_header_file_name:
+ if(ctx->current_field_pos == 0) {
+ ctx->current_field_len = ctx->file_header.file_name_len;
+ }
+
+ ctx->current_field_pos++;
+
+ if(ctx->current_field_pos == ctx->current_field_len) {
+ ctx->current_field_pos = 0;
+
+ if(ctx->file_header.extra_field_len > 0)
+ ctx->state = unzip_state_file_header_extra_field;
+ else if(ctx->file_header.file_comment_len > 0)
+ ctx->state = unzip_state_file_header_file_comment;
+ else
+ ctx->state = unzip_state_signature;
+ }
+ break;
+ case unzip_state_file_header_extra_field:
+ if(ctx->current_field_pos == 0) {
+ ctx->current_field_len = ctx->file_header.extra_field_len;
+ }
+
+ ctx->current_field_pos++;
+
+ if(ctx->current_field_pos == ctx->current_field_len) {
+ ctx->current_field_pos = 0;
+
+ if(ctx->file_header.file_comment_len > 0)
+ ctx->state = unzip_state_file_header_file_comment;
+ else
+ ctx->state = unzip_state_signature;
+ }
+ break;
+ case unzip_state_file_header_file_comment:
+ if(ctx->current_field_pos == 0) {
+ ctx->current_field_len = ctx->file_header.extra_field_len;
+
+ ctx->next_field_filter->start(ctx->upload_ctx);
+ }
+
+ ctx->current_field_pos++;
+
+ if(ctx->current_field_pos == ctx->current_field_len) {
+ ctx->current_field_pos = 0;
ctx->state = unzip_state_signature;
}
break;
case unzip_state_digital_sig_header:
+ if(ctx->current_field_pos == 0) {
+ ctx->current_field_len = DIGITAL_SIGNATURE_HEADER_LEN;
+ ctx->current_field_ptr = ctx->current_field = ctx->accumulator;
+ }
+
+ ctx->current_field_pos++;
+ *ctx->current_field_ptr++ = *buf->pos;
+
+ if(ctx->current_field_pos == ctx->current_field_len) {
+ ctx->current_field_pos = 0;
+
+ ctx->digital_sig.data_size = EXTRACT_LONG(ctx->current_field);
+
+ if(ctx->digital_sig.data_size > 0)
+ ctx->state = unzip_state_digital_sig;
+ else
+ ctx->state = unzip_state_signature;
+ }
+ break;
+ case unzip_state_digital_sig:
+ if(ctx->current_field_pos == 0) {
+ ctx->current_field_len = ctx->digital_sig.data_size;
+ }
+
+ ctx->current_field_pos++;
+
+ if(ctx->current_field_pos == ctx->current_field_len) {
+ ctx->current_field_pos = 0;
+ ctx->state = unzip_state_signature;
+ }
break;
case unzip_state_central_directory_end:
if(ctx->current_field_pos == 0) {
@@ -629,6 +808,32 @@ ngx_http_unzip_process_chain(ngx_unzip_ctx_t *ctx, ngx_chain_t *chain) {
if(ctx->current_field_pos == ctx->current_field_len) {
ctx->current_field_pos = 0;
+
+ ctx->end_of_central_dir.disk_number = EXTRACT_SHORT(ctx->current_field);
+ ctx->end_of_central_dir.number_of_disk_with_start_of_dir = EXTRACT_SHORT(ctx->current_field + 2);
+ ctx->end_of_central_dir.number_of_entries_on_this_disk = EXTRACT_SHORT(ctx->current_field + 4);
+ ctx->end_of_central_dir.total_number_of_entries = EXTRACT_SHORT(ctx->current_field + 6);
+ ctx->end_of_central_dir.size_of_directory = EXTRACT_LONG(ctx->current_field + 8);
+ ctx->end_of_central_dir.offset_of_start = EXTRACT_LONG(ctx->current_field + 12);
+ ctx->end_of_central_dir.archive_comment_len = EXTRACT_SHORT(ctx->current_field + 16);
+
+ if(ctx->end_of_central_dir.archive_comment_len > 0)
+ ctx->state = unzip_state_archive_comment;
+ else
+ ctx->state = unzip_state_signature;
+ }
+ break;
+ case unzip_state_archive_comment:
+ if(ctx->current_field_pos == 0) {
+ ctx->current_field_len = ctx->end_of_central_dir.archive_comment_len;
+
+ ctx->next_field_filter->start(ctx->upload_ctx);
+ }
+
+ ctx->current_field_pos++;
+
+ if(ctx->current_field_pos == ctx->current_field_len) {
+ ctx->current_field_pos = 0;
ctx->state = unzip_state_signature;
}
break;
@@ -654,9 +859,14 @@ ngx_http_unzip_start_handler(ngx_http_upload_ctx_t *u) {
parent = ctx;
- ctx = ngx_pcalloc(u->request->pool, sizeof(ngx_unzip_ctx_t));
- if (ctx == NULL) {
- return NGX_UPLOAD_NOMEM;
+ if(ctx == NULL) {
+ ctx = ngx_pcalloc(u->request->pool, sizeof(ngx_unzip_ctx_t));
+ if (ctx == NULL) {
+ return NGX_UPLOAD_NOMEM;
+ }
+
+ ctx->buf_pool = NULL;
+ ctx->preallocated = NULL;
}
ctx->parent = parent;
@@ -665,6 +875,7 @@ ngx_http_unzip_start_handler(ngx_http_upload_ctx_t *u) {
ctx->state = unzip_state_signature;
ctx->upload_ctx = u;
+ ctx->next_field_filter = ngx_upload_get_next_field_filter(u);
ctx->next_content_filter = ngx_upload_get_next_content_filter(u);
ctx->pool = u->request->pool;
@@ -779,10 +990,11 @@ static ngx_int_t /* {{{ ngx_http_unzip_inflate_start */
ngx_http_unzip_inflate_start(ngx_unzip_ctx_t *ctx) {
ngx_int_t rc;
ngx_unzip_conf_t *uzcf;
+ unsigned int wbits, memlevel;
- ctx->stream.zalloc = Z_NULL;
- ctx->stream.zfree = Z_NULL;
- ctx->stream.opaque = Z_NULL;
+ ctx->stream.zalloc = ngx_http_unzip_filter_alloc;
+ ctx->stream.zfree = ngx_http_unzip_filter_free;
+ ctx->stream.opaque = ctx;
ctx->stream.avail_in = 0;
ctx->stream.next_in = Z_NULL;
@@ -807,6 +1019,42 @@ ngx_http_unzip_inflate_start(ngx_unzip_ctx_t *ctx) {
ctx->output_chain->next = NULL;
}
+ if (ctx->preallocated == NULL) {
+ wbits = uzcf->wbits;
+ memlevel = MAX_MEM_LEVEL - 1; //uzcf->memlevel;
+
+ if (ctx->local_data_header.uncompressed_size > 0) {
+
+ /* the actual zlib window size is smaller by 262 bytes */
+
+ while (ctx->local_data_header.uncompressed_size < ((1 << (wbits - 1)) - 262)) {
+ wbits--;
+ memlevel--;
+ }
+ }
+
+ /*
+ * We preallocate a memory for zlib in one buffer (200K-400K), this
+ * decreases a number of malloc() and free() calls and also probably
+ * decreases a number of syscalls (sbrk() and so on).
+ * Besides we free this memory as soon as the gzipping will complete
+ * and do not wait while a whole response will be sent to a client.
+ *
+ * 8K is for zlib deflate_state, it takes
+ * *) 5816 bytes on i386 and sparc64 (32-bit mode)
+ * *) 5920 bytes on amd64 and sparc64
+ */
+
+ ctx->allocated = 8192 + (1 << (wbits + 2)) + (1 << (memlevel + 9));
+
+ ctx->preallocated = ngx_palloc(ctx->pool, ctx->allocated);
+ if (ctx->preallocated == NULL) {
+ return NGX_ERROR;
+ }
+
+ ctx->free_mem = ctx->preallocated;
+ }
+
rc = inflateInit2(&ctx->stream, -uzcf->wbits);
if (rc != Z_OK) {
@@ -840,6 +1088,9 @@ static void /* {{{ ngx_http_unzip_inflate_finish */
ngx_http_unzip_inflate_finish(ngx_unzip_ctx_t *ctx) {
ngx_crc32_final(ctx->calculated_crc32);
+ ctx->allocated += (ctx->free_mem - (char*)ctx->preallocated);
+ ctx->free_mem = ctx->preallocated;
+
if(ctx->next_content_filter->finish)
ctx->next_content_filter->finish(ctx->upload_ctx);
@@ -851,6 +1102,9 @@ ngx_http_unzip_inflate_finish(ngx_unzip_ctx_t *ctx) {
static void /* {{{ ngx_http_unzip_inflate_abort */
ngx_http_unzip_inflate_abort(ngx_unzip_ctx_t *ctx) {
+ ctx->allocated += ctx->free_mem - (char*)ctx->preallocated;
+ ctx->free_mem = ctx->preallocated;
+
if(ctx->next_content_filter->abort)
ctx->next_content_filter->abort(ctx->upload_ctx);
@@ -920,17 +1174,10 @@ ngx_http_unzip_inflate_process_chain(ngx_unzip_ctx_t *ctx, ngx_chain_t *chain) {
return NGX_AGAIN;
} /* }}} */
-static ngx_int_t /* {{{ ngx_http_unzip_retrieve_start */
-ngx_http_unzip_retrieve_start(ngx_unzip_ctx_t *ctx) {
- ngx_int_t rc;
-
- rc = ngx_upload_set_file_name(ctx->upload_ctx, &ctx->file_name);
-
- if(rc != NGX_OK)
- return rc;
-
+static ngx_int_t /* {{{ ngx_http_unzip_extract_start */
+ngx_http_unzip_extract_start(ngx_unzip_ctx_t *ctx) {
ngx_log_error(NGX_LOG_INFO, ctx->log, 0,
- "started retrieving file \"%V\"", &ctx->file_name);
+ "started extracting file \"%V\"", &ctx->file_name);
if(ctx->next_content_filter->start)
return ctx->next_content_filter->start(ctx->upload_ctx);
@@ -938,33 +1185,46 @@ ngx_http_unzip_retrieve_start(ngx_unzip_ctx_t *ctx) {
return NGX_OK;
} /* }}} */
-static void /* {{{ ngx_http_unzip_retrieve_finish */
-ngx_http_unzip_retrieve_finish(ngx_unzip_ctx_t *ctx) {
+static void /* {{{ ngx_http_unzip_extract_finish */
+ngx_http_unzip_extract_finish(ngx_unzip_ctx_t *ctx) {
if(ctx->next_content_filter->finish)
ctx->next_content_filter->finish(ctx->upload_ctx);
ngx_log_error(NGX_LOG_INFO, ctx->log, 0,
- "finished retrieving file \"%V\"", &ctx->file_name);
+ "finished extracting file \"%V\"", &ctx->file_name);
} /* }}} */
-static void /* {{{ ngx_http_unzip_retrieve_abort */
-ngx_http_unzip_retrieve_abort(ngx_unzip_ctx_t *ctx) {
+static void /* {{{ ngx_http_unzip_extract_abort */
+ngx_http_unzip_extract_abort(ngx_unzip_ctx_t *ctx) {
if(ctx->next_content_filter->abort)
ctx->next_content_filter->abort(ctx->upload_ctx);
ngx_log_error(NGX_LOG_INFO, ctx->log, 0,
- "aborted retrieving file \"%V\"", &ctx->file_name);
+ "aborted extracting file \"%V\"", &ctx->file_name);
} /* }}} */
-static ngx_int_t /* {{{ ngx_http_unzip_retrieve_process_chain */
-ngx_http_unzip_retrieve_process_chain(ngx_unzip_ctx_t *ctx, ngx_chain_t *chain) {
- ngx_chain_t *cl;
+static ngx_int_t /* {{{ ngx_http_unzip_extract_process_chain */
+ngx_http_unzip_extract_process_chain(ngx_unzip_ctx_t *ctx, ngx_chain_t *chain) {
+ ngx_chain_t *copy;
+ off_t limit = ctx->current_field_len - ctx->current_field_pos;
+ ngx_int_t rc;
if(ctx->next_content_filter->process_chain) {
- for(cl = chain; cl; cl = cl->next)
- ctx->current_field_pos += (cl->buf->pos - cl->buf->last);
+ rc = ngx_http_unzip_chain_copy_range(chain, &copy, &limit, ctx->pool, &ctx->buf_pool);
+
+ if(rc != NGX_OK) {
+ return rc;
+ }
+
+ ctx->current_field_pos = ctx->current_field_len - limit;
- return ctx->next_content_filter->process_chain(ctx->upload_ctx, chain);
+ rc = ctx->next_content_filter->process_chain(ctx->upload_ctx, copy);
+
+ ngx_http_unzip_chain_advance(chain, copy);
+
+ ngx_http_unzip_reclaim_chain(copy, &ctx->buf_pool);
+
+ return rc;
}else
return NGX_OK;
} /* }}} */
@@ -1024,3 +1284,143 @@ ngx_http_unzip_parse_file_name(ngx_unzip_ctx_t *ctx, ngx_str_t *file_name) {
return NGX_OK;
} /* }}} */
+static void * /* {{{ ngx_http_unzip_filter_alloc */
+ngx_http_unzip_filter_alloc(void *opaque, u_int items, u_int size)
+{
+ ngx_unzip_ctx_t *ctx = opaque;
+
+ void *p;
+ ngx_uint_t alloc;
+
+ alloc = items * size;
+
+ if (alloc % 512 != 0) {
+
+ /*
+ * The zlib deflate_state allocation, it takes about 6K,
+ * we allocate 8K. Other allocations are divisible by 512.
+ */
+
+ alloc = (alloc + ngx_pagesize - 1) & ~(ngx_pagesize - 1);
+ }
+
+ if (alloc <= ctx->allocated) {
+ p = ctx->free_mem;
+ ctx->free_mem += alloc;
+ ctx->allocated -= alloc;
+
+ ngx_log_debug4(NGX_LOG_DEBUG_HTTP, ctx->upload_ctx->request->connection->log, 0,
+ "unzip alloc: n:%ud s:%ud a:%ud p:%p",
+ items, size, alloc, p);
+
+ return p;
+ }
+
+ ngx_log_error(NGX_LOG_ALERT, ctx->upload_ctx->request->connection->log, 0,
+ "unzip filter failed to use preallocated memory: %ud of %ud",
+ items * size, ctx->allocated);
+ p = ngx_palloc(ctx->pool, items * size);
+
+ return p;
+} /* }}} */
+
+static void /* {{{ ngx_http_unzip_filter_free */
+ngx_http_unzip_filter_free(void *opaque, void *address)
+{
+} /* }}} */
+
+ngx_int_t /* {{{ ngx_http_unzip_chain_copy_range */
+ngx_http_unzip_chain_copy_range(ngx_chain_t *chain, ngx_chain_t **copy, off_t *limit, ngx_pool_t *pool, ngx_chain_t **free) {
+ ngx_chain_t *cl, *last, *r, *new;
+ ngx_buf_t *buf;
+
+ if(chain == NULL || *limit == 0) {
+ *copy = NULL;
+ return NGX_OK;
+ }
+
+ last = r = NULL;
+
+ for(cl = chain; cl; cl = cl->next) {
+ if(*free == NULL) {
+ buf = ngx_palloc(pool, sizeof(ngx_buf_t));
+
+ if (buf == NULL) {
+ return NGX_UPLOAD_NOMEM;
+ }
+
+ *free = ngx_palloc(pool, sizeof(ngx_chain_t));
+
+ if (buf == NULL) {
+ return NGX_UPLOAD_NOMEM;
+ }
+
+ (*free)->buf = buf;
+ (*free)->next = NULL;
+ }
+
+ new = *free;
+ *free = (*free)->next;
+
+ ngx_memcpy(new->buf, cl->buf, sizeof(ngx_buf_t));
+
+ new->next = NULL;
+
+ if(last == NULL) {
+ r = new;
+ last = new;
+ }else{
+ last->next = new;
+ last = new;
+ }
+
+ if(cl->buf->last - cl->buf->pos > *limit) {
+ new->buf->last = new->buf->pos + *limit;
+
+ *limit -= cl->buf->last - cl->buf->pos;
+
+ break;
+ }
+
+ *limit -= cl->buf->last - cl->buf->pos;
+ }
+
+ *copy = r;
+ return NGX_OK;
+} /* }}} */
+
+void /* {{{ ngx_http_unzip_reclaim_chain */
+ngx_http_unzip_reclaim_chain(ngx_chain_t *cl, ngx_chain_t **free) {
+ ngx_chain_t *next;
+
+ while(cl) {
+ next = cl->next;
+
+ if(*free == NULL) {
+ *free = cl;
+ cl->next = NULL;
+ }else{
+ cl->next = *free;
+ *free = cl;
+ }
+
+ cl = next;
+ }
+} /* }}} */
+
+void /* {{{ ngx_http_unzip_chain_advance */
+ngx_http_unzip_chain_advance(ngx_chain_t *chain, ngx_chain_t *copy) {
+ ngx_chain_t *c1 = chain, *c2 = copy;
+
+ while(c2) {
+ if(c1 == NULL) {
+ break;
+ }
+
+ c1->buf->pos = c2->buf->last;
+
+ c1 = c1->next;
+ c2 = c2->next;
+ }
+} /* }}} */
+
View
5 ngx_http_upload.h
@@ -85,6 +85,8 @@ typedef struct {
ngx_int_t (*process_chain)(struct ngx_http_upload_ctx_s *upload_ctx, ngx_chain_t *chain);
} ngx_upload_content_filter_t;
+typedef ngx_upload_content_filter_t ngx_upload_field_filter_t;
+
/*
* Mapping of a content type to slave location config
*/
@@ -199,6 +201,9 @@ ngx_int_t ngx_upload_set_file_name(ngx_http_upload_ctx_t *ctx, ngx_str_t *file_n
ngx_int_t ngx_upload_set_content_type(ngx_http_upload_ctx_t *ctx, ngx_str_t *content_type);
ngx_int_t ngx_upload_set_archive_path(ngx_http_upload_ctx_t *ctx, ngx_str_t *archive_path);
+ngx_upload_field_filter_t*
+ngx_upload_get_next_field_filter(ngx_http_upload_ctx_t *ctx);
+
ngx_upload_content_filter_t*
ngx_upload_get_next_content_filter(ngx_http_upload_ctx_t *ctx);
View
391 ngx_http_upload_module.c
@@ -34,6 +34,13 @@ static void ngx_http_upload_abort_handler(ngx_http_upload_ctx_t *u);
static ngx_int_t ngx_http_upload_process_chain(ngx_http_upload_ctx_t *u,
ngx_chain_t *chain);
+
+static ngx_int_t ngx_http_upload_field_start(ngx_http_upload_ctx_t *u);
+static void ngx_http_upload_field_finish(ngx_http_upload_ctx_t *u);
+static void ngx_http_upload_field_abort(ngx_http_upload_ctx_t *u);
+static ngx_int_t ngx_http_upload_field_process_chain(ngx_http_upload_ctx_t *u,
+ ngx_chain_t *chain);
+
static ngx_int_t ngx_http_upload_append_field(ngx_http_upload_ctx_t *u,
ngx_str_t *name, ngx_str_t *value);
@@ -53,12 +60,6 @@ static void ngx_upload_cleanup_handler(void *data);
static char *ngx_http_upload_filter_block(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
-
-ngx_int_t ngx_http_unzip_start_handler(ngx_http_upload_ctx_t *u);
-void ngx_http_unzip_finish_handler(ngx_http_upload_ctx_t *u);
-void ngx_http_unzip_abort_handler(ngx_http_upload_ctx_t *u);
-ngx_int_t ngx_http_unzip_data_handler(ngx_http_upload_ctx_t *u,
- u_char *buf, size_t len);
/*
* upload_init_ctx
*
@@ -139,12 +140,19 @@ static ngx_int_t upload_parse_content_type(ngx_http_upload_ctx_t *upload_ctx, ng
*/
static ngx_int_t upload_process_buf(ngx_http_upload_ctx_t *upload_ctx, u_char *start, u_char *end);
-static ngx_upload_content_filter_t ngx_write_content_filter = {
+static ngx_upload_content_filter_t ngx_write_content_filter = { /* {{{ */
ngx_http_upload_start_handler,
ngx_http_upload_finish_handler,
ngx_http_upload_abort_handler,
ngx_http_upload_process_chain
-};
+}; /* }}} */
+
+static ngx_upload_field_filter_t ngx_write_field_filter = { /* {{{ */
+ ngx_http_upload_field_start,
+ ngx_http_upload_field_finish,
+ ngx_http_upload_field_abort,
+ ngx_http_upload_field_process_chain
+}; /* }}} */
static ngx_command_t ngx_http_upload_commands[] = { /* {{{ */
@@ -530,156 +538,115 @@ static ngx_int_t ngx_http_upload_start_handler(ngx_http_upload_ctx_t *u) { /* {{
ngx_int_t rc;
ngx_err_t err;
ngx_http_upload_field_template_t *t;
- ngx_http_upload_field_filter_t *f;
ngx_str_t field_name, field_value;
- ngx_uint_t pass_field;
ngx_upload_cleanup_t *ucln;
- if(u->is_file) {
- u->cln = ngx_pool_cleanup_add(r->pool, sizeof(ngx_upload_cleanup_t));
-
- if(u->cln == NULL)
- return NGX_UPLOAD_NOMEM;
+ u->cln = ngx_pool_cleanup_add(r->pool, sizeof(ngx_upload_cleanup_t));
- file->name.len = path->name.len + 1 + path->len + 10;
+ if(u->cln == NULL)
+ return NGX_UPLOAD_NOMEM;
- file->name.data = ngx_palloc(u->request->pool, file->name.len + 1);
+ file->name.len = path->name.len + 1 + path->len + 10;
- if(file->name.data == NULL)
- return NGX_UPLOAD_NOMEM;
+ file->name.data = ngx_palloc(u->request->pool, file->name.len + 1);
- ngx_memcpy(file->name.data, path->name.data, path->name.len);
+ if(file->name.data == NULL)
+ return NGX_UPLOAD_NOMEM;
- file->log = r->connection->log;
+ ngx_memcpy(file->name.data, path->name.data, path->name.len);
- for(;;) {
- n = (uint32_t) ngx_next_temp_number(0);
+ file->log = r->connection->log;
- (void) ngx_sprintf(file->name.data + path->name.len + 1 + path->len,
- "%010uD%Z", n);
+ for(;;) {
+ n = (uint32_t) ngx_next_temp_number(0);
- ngx_create_hashed_filename(path, file->name.data, file->name.len);
+ (void) ngx_sprintf(file->name.data + path->name.len + 1 + path->len,
+ "%010uD%Z", n);
- ngx_log_debug1(NGX_LOG_DEBUG_CORE, file->log, 0,
- "hashed path: %s", file->name.data);
+ ngx_create_hashed_filename(path, file->name.data, file->name.len);
- file->fd = ngx_open_tempfile(file->name.data, 1, ulcf->store_access);
+ ngx_log_debug1(NGX_LOG_DEBUG_CORE, file->log, 0,
+ "hashed path: %s", file->name.data);
- if (file->fd != NGX_INVALID_FILE) {
- file->offset = 0;
- break;
- }
+ file->fd = ngx_open_tempfile(file->name.data, 1, ulcf->store_access);
- err = ngx_errno;
+ if (file->fd != NGX_INVALID_FILE) {
+ file->offset = 0;
+ break;
+ }
- if (err == NGX_EEXIST) {
- n = (uint32_t) ngx_next_temp_number(1);
- continue;
- }
+ err = ngx_errno;
- ngx_log_error(NGX_LOG_ERR, r->connection->log, ngx_errno,
- "failed to create output file \"%V\" for \"%V\"", &file->name, &u->file_name);
- return NGX_UPLOAD_IOERROR;
+ if (err == NGX_EEXIST) {
+ n = (uint32_t) ngx_next_temp_number(1);
+ continue;
}
- u->cln->handler = ngx_upload_cleanup_handler;
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, ngx_errno,
+ "failed to create output file \"%V\" for \"%V\"", &file->name, &u->file_name);
+ return NGX_UPLOAD_IOERROR;
+ }
- ucln = u->cln->data;
- ucln->fd = file->fd;
- ucln->filename = file->name.data;
- ucln->log = r->connection->log;
- ucln->headers_out = &r->headers_out;
- ucln->cleanup_statuses = ulcf->cleanup_statuses;
- ucln->aborted = 0;
-
- if(ulcf->field_templates) {
- t = ulcf->field_templates->elts;
- for (i = 0; i < ulcf->field_templates->nelts; i++) {
-
- if (t[i].field_lengths == NULL) {
- field_name = t[i].value.key;
- }else{
- if (ngx_http_script_run(r, &field_name, t[i].field_lengths->elts, 0,
- t[i].field_values->elts) == NULL)
- {
- rc = NGX_UPLOAD_SCRIPTERROR;
- goto cleanup_file;
- }
- }
+ u->cln->handler = ngx_upload_cleanup_handler;
- if (t[i].value_lengths == NULL) {
- field_value = t[i].value.value;
- }else{
- if (ngx_http_script_run(r, &field_value, t[i].value_lengths->elts, 0,
- t[i].value_values->elts) == NULL)
- {
- rc = NGX_UPLOAD_SCRIPTERROR;
- goto cleanup_file;
- }
- }
+ ucln = u->cln->data;
+ ucln->fd = file->fd;
+ ucln->filename = file->name.data;
+ ucln->log = r->connection->log;
+ ucln->headers_out = &r->headers_out;
+ ucln->cleanup_statuses = ulcf->cleanup_statuses;
+ ucln->aborted = 0;
- rc = ngx_http_upload_append_field(u, &field_name, &field_value);
+ if(ulcf->field_templates) {
+ t = ulcf->field_templates->elts;
+ for (i = 0; i < ulcf->field_templates->nelts; i++) {
- if(rc != NGX_OK)
+ if (t[i].field_lengths == NULL) {
+ field_name = t[i].value.key;
+ }else{
+ if (ngx_http_script_run(r, &field_name, t[i].field_lengths->elts, 0,
+ t[i].field_values->elts) == NULL)
+ {
+ rc = NGX_UPLOAD_SCRIPTERROR;
goto cleanup_file;
+ }
}
- }
-
- if(u->md5_ctx != NULL)
- MD5Init(&u->md5_ctx->md5);
-
- if(u->sha1_ctx != NULL)
- SHA1_Init(&u->sha1_ctx->sha1);
-
- if(u->calculate_crc32)
- ngx_crc32_init(u->crc32);
- ngx_log_error(NGX_LOG_INFO, r->connection->log, 0
- , "started writing file \"%V\" to \"%V\" (field \"%V\", content type \"%V\")"
- , &u->file_name
- , &u->output_file.name
- , &u->field_name
- , &u->content_type
- );
- }else{
- pass_field = 0;
-
- if(ulcf->field_filters) {
- f = ulcf->field_filters->elts;
- for (i = 0; i < ulcf->field_filters->nelts; i++) {
-#if (NGX_PCRE)
- rc = ngx_regex_exec(f[i].regex, &u->field_name, NULL, 0);
-
- if (rc != NGX_REGEX_NO_MATCHED && rc < 0) {
- return NGX_UPLOAD_SCRIPTERROR;
+ if (t[i].value_lengths == NULL) {
+ field_value = t[i].value.value;
+ }else{
+ if (ngx_http_script_run(r, &field_value, t[i].value_lengths->elts, 0,
+ t[i].value_values->elts) == NULL)
+ {
+ rc = NGX_UPLOAD_SCRIPTERROR;
+ goto cleanup_file;
}
-
- /*
- * If at least one filter succeeds, we pass the field
- */
- if(rc == 0)
- pass_field = 1;
-#else
- if(ngx_strncmp(f[i].text.data, u->field_name.data, u->field_name.len) == 0)
- pass_field = 1;
-#endif
}
- }
- if(pass_field && u->field_name.len > 0) {
- /*
- * Here we do a small hack: the content of a normal field
- * is not known until ngx_http_upload_flush_output_buffer
- * is called. We pass empty field value to simplify things.
- */
- rc = ngx_http_upload_append_field(u, &u->field_name, &ngx_http_upload_empty_field_value);
+ rc = ngx_http_upload_append_field(u, &field_name, &field_value);
if(rc != NGX_OK)
- return rc;
- }else
- u->discard_data = 1;
+ goto cleanup_file;
+ }
}
+ if(u->md5_ctx != NULL)
+ MD5Init(&u->md5_ctx->md5);
+
+ if(u->sha1_ctx != NULL)
+ SHA1_Init(&u->sha1_ctx->sha1);
+
+ if(u->calculate_crc32)
+ ngx_crc32_init(u->crc32);
+
+ ngx_log_error(NGX_LOG_INFO, r->connection->log, 0
+ , "started writing file \"%V\" to \"%V\" (field \"%V\", content type \"%V\")"
+ , &u->file_name
+ , &u->output_file.name
+ , &u->field_name
+ , &u->content_type
+ );
+
return NGX_OK;
cleanup_file:
@@ -798,63 +765,28 @@ static void ngx_http_upload_abort_handler(ngx_http_upload_ctx_t *u) { /* {{{ */
} /* }}} */
static ngx_int_t ngx_http_upload_process_chain(ngx_http_upload_ctx_t *u, ngx_chain_t *chain) { /* {{{ */
- ngx_buf_t *b;
ngx_chain_t *cl;
- if(u->is_file) {
- if(u->md5_ctx || u->sha1_ctx) {
- for(cl = chain; cl && !cl->buf->last_in_chain; cl = cl->next) {
- if(u->md5_ctx)
- MD5Update(&u->md5_ctx->md5, cl->buf->pos, cl->buf->last - cl->buf->pos);
+ for(cl = chain; cl && !cl->buf->last_in_chain; cl = cl->next) {
+ if(u->md5_ctx)
+ MD5Update(&u->md5_ctx->md5, cl->buf->pos, cl->buf->last - cl->buf->pos);
- if(u->sha1_ctx)
- SHA1_Update(&u->sha1_ctx->sha1, cl->buf->pos, cl->buf->last - cl->buf->pos);
+ if(u->sha1_ctx)
+ SHA1_Update(&u->sha1_ctx->sha1, cl->buf->pos, cl->buf->last - cl->buf->pos);
- if(u->calculate_crc32)
- ngx_crc32_update(&u->crc32, cl->buf->pos, cl->buf->last - cl->buf->pos);
- }
- }
+ if(u->calculate_crc32)
+ ngx_crc32_update(&u->crc32, cl->buf->pos, cl->buf->last - cl->buf->pos);
- if(ngx_write_chain_to_file(&u->output_file, chain, u->output_file.offset,
- u->request->pool) == NGX_ERROR) {
+ if(ngx_write_file(&u->output_file, cl->buf->pos, cl->buf->last - cl->buf->pos,
+ u->output_file.offset) == NGX_ERROR)
+ {
return NGX_UPLOAD_IOERROR;
}
- for(cl = chain; cl; cl = cl->next)
- cl->buf->pos = cl->buf->last;
-
- return NGX_OK;
- }else{
- for(cl = chain; cl && !cl->buf->last_in_chain; cl = cl->next) {
- b = ngx_create_temp_buf(u->request->pool, cl->buf->last - cl->buf->pos);
-
- if (b == NULL) {
- return NGX_ERROR;
- }
-
- cl = ngx_alloc_chain_link(u->request->pool);
- if (cl == NULL) {
- return NGX_ERROR;
- }
-
- b->last_in_chain = 0;
-
- cl->buf = b;
- cl->next = NULL;
-
- b->last = ngx_cpymem(b->last, cl->buf->pos, cl->buf->last - cl->buf->pos);
-
- if(u->chain == NULL) {
- u->chain = cl;
- u->last = cl;
- }else{
- u->last->next = cl;
- u->last = cl;
- }
- }
-
- return NGX_OK;
+ cl->buf->pos = cl->buf->last;
}
+
+ return NGX_OK;
} /* }}} */
static void /* {{{ ngx_http_upload_append_str */
@@ -925,6 +857,106 @@ ngx_http_upload_append_field(ngx_http_upload_ctx_t *u, ngx_str_t *name, ngx_str_
return NGX_OK;
} /* }}} */
+static ngx_int_t ngx_http_upload_field_start(ngx_http_upload_ctx_t *u) { /* {{{ */
+ ngx_http_request_t *r = u->request;
+ ngx_http_upload_loc_conf_t *ulcf = ngx_http_get_module_loc_conf(r, ngx_http_upload_module);
+
+ ngx_uint_t i;
+ ngx_int_t rc;
+ ngx_http_upload_field_filter_t *f;
+ ngx_uint_t pass_field;
+
+ pass_field = 0;
+
+ if(ulcf->field_filters) {
+ f = ulcf->field_filters->elts;
+ for (i = 0; i < ulcf->field_filters->nelts; i++) {
+#if (NGX_PCRE)
+ rc = ngx_regex_exec(f[i].regex, &u->field_name, NULL, 0);
+
+ if (rc != NGX_REGEX_NO_MATCHED && rc < 0) {
+ return NGX_UPLOAD_SCRIPTERROR;
+ }
+
+ /*
+ * If at least one filter succeeds, we pass the field
+ */
+ if(rc == 0)
+ pass_field = 1;
+#else
+ if(ngx_strncmp(f[i].text.data, u->field_name.data, u->field_name.len) == 0)
+ pass_field = 1;
+#endif
+ }
+ }
+
+ if(pass_field && u->field_name.len > 0) {
+ /*
+ * Here we do a small hack: the content of a normal field
+ * is not known until ngx_http_upload_flush_output_buffer
+ * is called. We pass empty field value to simplify things.
+ */
+ rc = ngx_http_upload_append_field(u, &u->field_name, &ngx_http_upload_empty_field_value);
+
+ if(rc != NGX_OK)
+ return rc;
+ }else
+ u->discard_data = 1;
+
+ return NGX_OK;
+} /* }}} */
+
+static void ngx_http_upload_field_finish(ngx_http_upload_ctx_t *u) { /* {{{ */
+ // Checkpoint current output chain state
+ u->checkpoint = u->last;
+} /* }}} */
+
+static void ngx_http_upload_field_abort(ngx_http_upload_ctx_t *u) { /* {{{ */
+ // Rollback output chain to the previous consistant state
+ if(u->checkpoint != NULL) {
+ u->last = u->checkpoint;
+ u->last->next = NULL;
+ }else{
+ u->chain = u->last = NULL;
+ u->first_part = 1;
+ }
+} /* }}} */
+
+static ngx_int_t ngx_http_upload_field_process_chain(ngx_http_upload_ctx_t *u, ngx_chain_t *chain) { /* {{{ */
+ ngx_buf_t *b;
+ ngx_chain_t *cl;
+
+ for(cl = chain; cl && !cl->buf->last_in_chain; cl = cl->next) {
+ b = ngx_create_temp_buf(u->request->pool, cl->buf->last - cl->buf->pos);
+
+ if (b == NULL) {
+ return NGX_ERROR;
+ }
+
+ cl = ngx_alloc_chain_link(u->request->pool);
+ if (cl == NULL) {
+ return NGX_ERROR;
+ }
+
+ b->last_in_chain = 0;
+
+ cl->buf = b;
+ cl->next = NULL;
+
+ b->last = ngx_cpymem(b->last, cl->buf->pos, cl->buf->last - cl->buf->pos);
+
+ if(u->chain == NULL) {
+ u->chain = cl;
+ u->last = cl;
+ }else{
+ u->last->next = cl;
+ u->last = cl;
+ }
+ }
+
+ return NGX_OK;
+} /* }}} */
+
static void * /* {{{ ngx_http_upload_create_loc_conf */
ngx_http_upload_create_loc_conf(ngx_conf_t *cf)
{
@@ -1580,6 +1612,11 @@ ngx_http_upload_pass(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
return NGX_CONF_OK;
} /* }}} */
+ngx_upload_field_filter_t* /* {{{ ngx_upload_get_next_content_filter */
+ngx_upload_get_next_field_filter(ngx_http_upload_ctx_t *ctx) {
+ return &ngx_write_field_filter;
+} /* }}} */
+
ngx_upload_content_filter_t* /* {{{ ngx_upload_get_next_content_filter */
ngx_upload_get_next_content_filter(ngx_http_upload_ctx_t *ctx) {
ngx_upload_content_filter_t **cflt;
@@ -2180,6 +2217,7 @@ ngx_upload_set_content_filter(ngx_http_upload_ctx_t *u, ngx_str_t *content_type)
static ngx_int_t upload_start_file(ngx_http_upload_ctx_t *upload_ctx) { /* {{{ */
ngx_str_t exten, content_type = ngx_null_string;
ngx_upload_content_filter_t* cflt;
+ ngx_upload_field_filter_t* fflt;
upload_ctx->start_part_f = ngx_http_upload_start_handler;
upload_ctx->finish_part_f = ngx_http_upload_finish_handler;
@@ -2202,6 +2240,13 @@ static ngx_int_t upload_start_file(ngx_http_upload_ctx_t *upload_ctx) { /* {{{ *
upload_ctx->finish_part_f = cflt->finish;
upload_ctx->abort_part_f = cflt->abort;
upload_ctx->process_chain_f = cflt->process_chain;
+ }else{
+ fflt = ngx_upload_get_next_field_filter(upload_ctx);
+
+ upload_ctx->start_part_f = fflt->start;
+ upload_ctx->finish_part_f = fflt->finish;
+ upload_ctx->abort_part_f = fflt->abort;
+ upload_ctx->process_chain_f = fflt->process_chain;
}
// Call user-defined event handler
Please sign in to comment.
Something went wrong with that request. Please try again.