rewrite file/folder handling code as a complete and proper solution for bb#730

git-svn: trunk@3542
This commit is contained in:
Tomasz Kojm
2008-01-25 15:58:25 +00:00
parent 0514d610eb
commit 9518ee069d
3 changed files with 89 additions and 96 deletions

View File

@@ -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)

View File

@@ -1,4 +1,7 @@
/*
* Copyright (C) 2007-2008 Sourcefire, Inc.
* Author: Tomasz Kojm <tkojm@clamav.net>
*
* Copyright (C) 2006 Tomasz Kojm <tkojm@clamav.net>
*
* 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;

View File

@@ -1,4 +1,7 @@
/*
* Copyright (C) 2007-2008 Sourcefire, Inc.
* Author: Tomasz Kojm <tkojm@clamav.net>
*
* Copyright (C) 2006 Tomasz Kojm <tkojm@clamav.net>
*
* 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;
};