From 9518ee069d003c910eb2c8bccd58dfaa9593c4a5 Mon Sep 17 00:00:00 2001 From: Tomasz Kojm Date: Fri, 25 Jan 2008 15:58:25 +0000 Subject: [PATCH] rewrite file/folder handling code as a complete and proper solution for bb#730 git-svn: trunk@3542 --- ChangeLog | 5 ++ libclamav/cab.c | 172 ++++++++++++++++++++++-------------------------- libclamav/cab.h | 8 ++- 3 files changed, 89 insertions(+), 96 deletions(-) diff --git a/ChangeLog b/ChangeLog index 2ac843fef..2ae53c285 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +Fri Jan 25 16:35:34 CET 2008 (tk) +--------------------------------- + * libclamav/cab.[ch]: rewrite file/folder handling code as a complete + and proper solution for bb#730 + Fri Jan 25 12:43:30 CET 2008 (tk) --------------------------------- * libclamav/cab.c: improve performance of cabinet extractor (bb#730) diff --git a/libclamav/cab.c b/libclamav/cab.c index d5a73f847..e791363a5 100644 --- a/libclamav/cab.c +++ b/libclamav/cab.c @@ -1,4 +1,7 @@ /* + * Copyright (C) 2007-2008 Sourcefire, Inc. + * Author: Tomasz Kojm + * * Copyright (C) 2006 Tomasz Kojm * * This code is based on the work of Stuart Caie and the official @@ -157,6 +160,20 @@ void cab_free(struct cab_archive *cab) struct cab_file *file; + if(cab->state) { + switch(cab->state->cmethod & 0x000f) { + case 0x0001: + mszip_free(cab->state->stream); + break; + case 0x0002: + qtm_free(cab->state->stream); + break; + case 0x0003: + lzx_free(cab->state->stream); + } + free(cab->state); + } + while(cab->folders) { folder = cab->folders; cab->folders = cab->folders->next; @@ -505,36 +522,36 @@ static int cab_read(struct cab_file *file, unsigned char *buffer, int bytes) todo = bytes; while(todo > 0) { - left = file->state->end - file->state->pt; + left = file->cab->state->end - file->cab->state->pt; if(left) { if(left > todo) left = todo; - memcpy(buffer, file->state->pt, left); - file->state->pt += left; + memcpy(buffer, file->cab->state->pt, left); + file->cab->state->pt += left; buffer += left; todo -= left; } else { - if(file->state->blknum++ >= file->folder->nblocks) { + if(file->cab->state->blknum++ >= file->folder->nblocks) { file->error = CL_EFORMAT; break; } - file->error = cab_read_block(file->fd, file->state, file->cab->resdata); + file->error = cab_read_block(file->fd, file->cab->state, file->cab->resdata); if(file->error) return -1; if((file->folder->cmethod & 0x000f) == 0x0002) /* Quantum hack */ - *file->state->end++ = 0xff; + *file->cab->state->end++ = 0xff; - if(file->state->blknum >= file->folder->nblocks) { + if(file->cab->state->blknum >= file->folder->nblocks) { if((file->folder->cmethod & 0x000f) == 0x0003) { /* LZX hack */ - lzx_set_output_length(file->state->stream, (off_t) ((file->state->blknum - 1) * CAB_BLOCKMAX + file->state->outlen)); + lzx_set_output_length(file->cab->state->stream, (off_t) ((file->cab->state->blknum - 1) * CAB_BLOCKMAX + file->cab->state->outlen)); } } else { - if(file->state->outlen != CAB_BLOCKMAX) { + if(file->cab->state->outlen != CAB_BLOCKMAX) { cli_dbgmsg("cab_read: WARNING: partial data block\n"); } } @@ -584,9 +601,52 @@ static int cab_unstore(struct cab_file *file, int bytes, uint8_t wflag) return CL_SUCCESS; } +#define CAB_CHGFOLDER \ + if(!file->cab->actfol || (file->folder != file->cab->actfol)) { \ + if(file->cab->state) { \ + switch(file->cab->state->cmethod & 0x000f) { \ + case 0x0001: \ + mszip_free(file->cab->state->stream); \ + break; \ + case 0x0002: \ + qtm_free(file->cab->state->stream); \ + break; \ + case 0x0003: \ + lzx_free(file->cab->state->stream); \ + } \ + free(file->cab->state); \ + } \ + file->cab->actfol = file->folder; \ + if(lseek(file->fd, file->folder->offset, SEEK_SET) == -1) { \ + cli_dbgmsg("cab_extract: Can't lseek to %u\n", (unsigned int) file->folder->offset); \ + return CL_EFORMAT; /* truncated file? */ \ + } \ + file->cab->state = (struct cab_state *) cli_calloc(1, sizeof(struct cab_state)); \ + if(!file->cab->state) { \ + cli_errmsg("cab_extract: Can't allocate memory for internal state\n"); \ + return CL_EMEM; \ + } \ + file->cab->state->cmethod = file->folder->cmethod; \ + switch(file->folder->cmethod & 0x000f) { \ + case 0x0001: \ + file->cab->state->stream = (struct mszip_stream *) mszip_init(file->fd, file->ofd, 4096, 1, file, &cab_read); \ + break; \ + case 0x0002: \ + file->cab->state->stream = (struct qtm_stream *) qtm_init(file->fd, file->ofd, (int) (file->folder->cmethod >> 8) & 0x1f, 4096, file, &cab_read); \ + break; \ + case 0x0003: \ + file->cab->state->stream = (struct lzx_stream *) lzx_init(file->fd, file->ofd, (int) (file->folder->cmethod >> 8) & 0x1f, 0, 4096, 0, file, &cab_read); \ + } \ + if(!file->cab->state->stream) { \ + free(file->cab->state); \ + close(file->ofd); \ + return CL_EMSCAB; \ + } \ + } + + int cab_extract(struct cab_file *file, const char *name) { - struct cab_folder *folder; int ret; @@ -595,26 +655,15 @@ int cab_extract(struct cab_file *file, const char *name) return CL_ENULLARG; } - if(!(folder = file->folder)) { + if(!file->folder) { cli_errmsg("cab_extract: file->folder == NULL\n"); return CL_ENULLARG; } - if(lseek(file->fd, file->folder->offset, SEEK_SET) == -1) { - cli_errmsg("cab_extract: Can't lseek to %u\n", (unsigned int) file->folder->offset); - return CL_EIO; - } - - file->state = (struct cab_state *) cli_calloc(1, sizeof(struct cab_state)); - if(!file->state) { - cli_errmsg("cab_extract: Can't allocate memory for internal state\n"); - return CL_EIO; - } - file->ofd = open(name, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, S_IRWXU); if(file->ofd == -1) { cli_errmsg("cab_extract: Can't open file %s in write mode\n", name); - free(file->state); + free(file->cab->state); return CL_EIO; } @@ -628,84 +677,20 @@ int cab_extract(struct cab_file *file, const char *name) case 0x0001: /* MSZIP */ cli_dbgmsg("CAB: Compression method: MSZIP\n"); - file->state->stream = (struct mszip_stream *) mszip_init(file->fd, file->ofd, 4096, 1, file, &cab_read); - if(!file->state->stream) { - free(file->state); - close(file->ofd); - return CL_EMSCAB; - } - ret = mszip_decompress(file->state->stream, file->length); - mszip_free(file->state->stream); - if(ret < 0 && file->offset > 0) { - memset(file->state, 0, sizeof(struct cab_state)); - file->state->stream = (struct mszip_stream *) mszip_init(file->fd, file->ofd, 4096, 1, file, &cab_read); - if(!file->state->stream) { - free(file->state); - close(file->ofd); - return CL_EMSCAB; - } - ((struct mszip_stream *) file->state->stream)->wflag = 0; - if(mszip_decompress(file->state->stream, file->offset) == CL_SUCCESS) { - ((struct mszip_stream *) file->state->stream)->wflag = 1; - ret = mszip_decompress(file->state->stream, file->length); - } - mszip_free(file->state->stream); - } + CAB_CHGFOLDER; + ret = mszip_decompress(file->cab->state->stream, file->length); break; case 0x0002: /* QUANTUM */ cli_dbgmsg("CAB: Compression method: QUANTUM\n"); - file->state->stream = (struct qtm_stream *) qtm_init(file->fd, file->ofd, (int) (file->folder->cmethod >> 8) & 0x1f, 4096, file, &cab_read); - if(!file->state->stream) { - free(file->state); - close(file->ofd); - return CL_EMSCAB; - } - ret = qtm_decompress(file->state->stream, file->length); - qtm_free(file->state->stream); - if(ret < 0 && file->offset > 0) { - memset(file->state, 0, sizeof(struct cab_state)); - file->state->stream = (struct qtm_stream *) qtm_init(file->fd, file->ofd, (int) (file->folder->cmethod >> 8) & 0x1f, 4096, file, &cab_read); - if(!file->state->stream) { - free(file->state); - close(file->ofd); - return CL_EMSCAB; - } - ((struct qtm_stream *) file->state->stream)->wflag = 0; - if(qtm_decompress(file->state->stream, file->offset) == CL_SUCCESS) { - ((struct qtm_stream *) file->state->stream)->wflag = 1; - ret = qtm_decompress(file->state->stream, file->length); - } - qtm_free(file->state->stream); - } + CAB_CHGFOLDER; + ret = qtm_decompress(file->cab->state->stream, file->length); break; case 0x0003: /* LZX */ cli_dbgmsg("CAB: Compression method: LZX\n"); - file->state->stream = (struct lzx_stream *) lzx_init(file->fd, file->ofd, (int) (file->folder->cmethod >> 8) & 0x1f, 0, 4096, 0, file, &cab_read); - if(!file->state->stream) { - free(file->state); - close(file->ofd); - return CL_EMSCAB; - } - if(file->offset > 0) { - ((struct lzx_stream *) file->state->stream)->wflag = 0; - ret = lzx_decompress(file->state->stream, file->offset); - ((struct lzx_stream *) file->state->stream)->wflag = 1; - if(ret < 0) { - lzx_free(file->state->stream); - memset(file->state, 0, sizeof(struct cab_state)); - file->state->stream = (struct lzx_stream *) lzx_init(file->fd, file->ofd, (int) (file->folder->cmethod >> 8) & 0x1f, 0, 4096, 0, file, &cab_read); - if(!file->state->stream) { - free(file->state); - close(file->ofd); - return CL_EMSCAB; - } - lseek(file->fd, file->folder->offset, SEEK_SET); - } - } - ret = lzx_decompress(file->state->stream, file->length); - lzx_free(file->state->stream); + CAB_CHGFOLDER; + ret = lzx_decompress(file->cab->state->stream, file->length); break; default: @@ -713,7 +698,6 @@ int cab_extract(struct cab_file *file, const char *name) ret = CL_EFORMAT; } - free(file->state); close(file->ofd); return ret; diff --git a/libclamav/cab.h b/libclamav/cab.h index 377ce08da..7f6c72ff3 100644 --- a/libclamav/cab.h +++ b/libclamav/cab.h @@ -1,4 +1,7 @@ /* + * Copyright (C) 2007-2008 Sourcefire, Inc. + * Author: Tomasz Kojm + * * Copyright (C) 2006 Tomasz Kojm * * This program is free software; you can redistribute it and/or modify @@ -26,8 +29,9 @@ #define CAB_INPUTMAX (CAB_BLOCKMAX + 6144) struct cab_archive { - struct cab_folder *folders; + struct cab_folder *folders, *actfol; struct cab_file *files; + struct cab_state *state; uint32_t length; uint16_t nfolders; uint16_t nfiles; @@ -43,6 +47,7 @@ struct cab_state { uint16_t blklen; uint16_t outlen; uint16_t blknum; + uint16_t cmethod; }; struct cab_file { @@ -55,7 +60,6 @@ struct cab_file { struct cab_folder *folder; struct cab_file *next; struct cab_archive *cab; - struct cab_state *state; uint16_t attribs; };