Browse code

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

git-svn: trunk@3542

Tomasz Kojm authored on 2008/01/26 00:58:25
Showing 3 changed files
... ...
@@ -1,3 +1,8 @@
1
+Fri Jan 25 16:35:34 CET 2008 (tk)
2
+---------------------------------
3
+  * libclamav/cab.[ch]: rewrite file/folder handling code as a complete
4
+			and proper solution for bb#730
5
+
1 6
 Fri Jan 25 12:43:30 CET 2008 (tk)
2 7
 ---------------------------------
3 8
   * libclamav/cab.c: improve performance of cabinet extractor (bb#730)
... ...
@@ -1,4 +1,7 @@
1 1
 /*
2
+ *  Copyright (C) 2007-2008 Sourcefire, Inc.
3
+ *  Author: Tomasz Kojm <tkojm@clamav.net>
4
+ *
2 5
  *  Copyright (C) 2006 Tomasz Kojm <tkojm@clamav.net>
3 6
  *
4 7
  *  This code is based on the work of Stuart Caie and the official
... ...
@@ -157,6 +160,20 @@ void cab_free(struct cab_archive *cab)
157 157
 	struct cab_file *file;
158 158
 
159 159
 
160
+    if(cab->state) {
161
+	switch(cab->state->cmethod & 0x000f) {
162
+	    case 0x0001:
163
+		mszip_free(cab->state->stream);
164
+		break;
165
+	    case 0x0002:
166
+		qtm_free(cab->state->stream);
167
+		break;
168
+	    case 0x0003:
169
+		lzx_free(cab->state->stream);
170
+	}
171
+	free(cab->state);
172
+    }
173
+
160 174
     while(cab->folders) {
161 175
 	folder = cab->folders;
162 176
 	cab->folders = cab->folders->next;
... ...
@@ -505,36 +522,36 @@ static int cab_read(struct cab_file *file, unsigned char *buffer, int bytes)
505 505
 
506 506
     todo = bytes;
507 507
     while(todo > 0) {
508
-	left = file->state->end - file->state->pt;
508
+	left = file->cab->state->end - file->cab->state->pt;
509 509
 
510 510
 	if(left) {
511 511
 	    if(left > todo)
512 512
 		left = todo;
513 513
 
514
-	    memcpy(buffer, file->state->pt, left);
515
-	    file->state->pt += left;
514
+	    memcpy(buffer, file->cab->state->pt, left);
515
+	    file->cab->state->pt += left;
516 516
 	    buffer += left;
517 517
 	    todo -= left;
518 518
 
519 519
 	} else {
520
-	    if(file->state->blknum++ >= file->folder->nblocks) {
520
+	    if(file->cab->state->blknum++ >= file->folder->nblocks) {
521 521
 		file->error = CL_EFORMAT;
522 522
 		break;
523 523
 	    }
524 524
 
525
-	    file->error = cab_read_block(file->fd, file->state, file->cab->resdata);
525
+	    file->error = cab_read_block(file->fd, file->cab->state, file->cab->resdata);
526 526
 	    if(file->error)
527 527
 		return -1;
528 528
 
529 529
 	    if((file->folder->cmethod & 0x000f) == 0x0002) /* Quantum hack */
530
-		*file->state->end++ = 0xff;
530
+		*file->cab->state->end++ = 0xff;
531 531
 
532
-	    if(file->state->blknum >= file->folder->nblocks) {
532
+	    if(file->cab->state->blknum >= file->folder->nblocks) {
533 533
 		if((file->folder->cmethod & 0x000f) == 0x0003) { /* LZX hack */
534
-		    lzx_set_output_length(file->state->stream, (off_t) ((file->state->blknum - 1) * CAB_BLOCKMAX + file->state->outlen));
534
+		    lzx_set_output_length(file->cab->state->stream, (off_t) ((file->cab->state->blknum - 1) * CAB_BLOCKMAX + file->cab->state->outlen));
535 535
 		}
536 536
 	    } else {
537
-		if(file->state->outlen != CAB_BLOCKMAX) {
537
+		if(file->cab->state->outlen != CAB_BLOCKMAX) {
538 538
 		    cli_dbgmsg("cab_read: WARNING: partial data block\n");
539 539
 		}
540 540
 	    }
... ...
@@ -584,9 +601,52 @@ static int cab_unstore(struct cab_file *file, int bytes, uint8_t wflag)
584 584
     return CL_SUCCESS;
585 585
 }
586 586
 
587
+#define CAB_CHGFOLDER							\
588
+    if(!file->cab->actfol || (file->folder != file->cab->actfol)) {	\
589
+	if(file->cab->state) {						\
590
+	    switch(file->cab->state->cmethod & 0x000f) {		\
591
+		case 0x0001:						\
592
+		    mszip_free(file->cab->state->stream);		\
593
+		    break;						\
594
+		case 0x0002:						\
595
+		    qtm_free(file->cab->state->stream);			\
596
+		    break;						\
597
+		case 0x0003:						\
598
+		    lzx_free(file->cab->state->stream);			\
599
+	    }								\
600
+	    free(file->cab->state);					\
601
+	}								\
602
+	file->cab->actfol = file->folder;				\
603
+	if(lseek(file->fd, file->folder->offset, SEEK_SET) == -1) {	\
604
+	    cli_dbgmsg("cab_extract: Can't lseek to %u\n", (unsigned int) file->folder->offset);							\
605
+	    return CL_EFORMAT; /* truncated file? */			\
606
+	}								\
607
+	file->cab->state = (struct cab_state *) cli_calloc(1, sizeof(struct cab_state));								\
608
+	if(!file->cab->state) {						\
609
+	    cli_errmsg("cab_extract: Can't allocate memory for internal state\n");									\
610
+	    return CL_EMEM;						\
611
+	}								\
612
+	file->cab->state->cmethod = file->folder->cmethod;		\
613
+	switch(file->folder->cmethod & 0x000f) {			\
614
+	    case 0x0001:						\
615
+		file->cab->state->stream = (struct mszip_stream *) mszip_init(file->fd, file->ofd, 4096, 1, file, &cab_read);				\
616
+		break;							\
617
+	    case 0x0002:						\
618
+		file->cab->state->stream = (struct qtm_stream *) qtm_init(file->fd, file->ofd, (int) (file->folder->cmethod >> 8) & 0x1f, 4096, file, &cab_read);									\
619
+		break;							\
620
+	    case 0x0003:						\
621
+		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);									\
622
+	}								\
623
+	if(!file->cab->state->stream) {					\
624
+	    free(file->cab->state);					\
625
+	    close(file->ofd);						\
626
+	    return CL_EMSCAB;						\
627
+	}								\
628
+    }
629
+
630
+
587 631
 int cab_extract(struct cab_file *file, const char *name)
588 632
 {
589
-	struct cab_folder *folder;
590 633
 	int ret;
591 634
 
592 635
 
... ...
@@ -595,26 +655,15 @@ int cab_extract(struct cab_file *file, const char *name)
595 595
 	return CL_ENULLARG;
596 596
     }
597 597
 
598
-    if(!(folder = file->folder)) {
598
+    if(!file->folder) {
599 599
 	cli_errmsg("cab_extract: file->folder == NULL\n");
600 600
 	return CL_ENULLARG;
601 601
     }
602 602
 
603
-    if(lseek(file->fd, file->folder->offset, SEEK_SET) == -1) {
604
-	cli_errmsg("cab_extract: Can't lseek to %u\n", (unsigned int) file->folder->offset);
605
-	return CL_EIO;
606
-    }
607
-
608
-    file->state = (struct cab_state *) cli_calloc(1, sizeof(struct cab_state));
609
-    if(!file->state) {
610
-	cli_errmsg("cab_extract: Can't allocate memory for internal state\n");
611
-	return CL_EIO;
612
-    }
613
-
614 603
     file->ofd = open(name, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, S_IRWXU);
615 604
     if(file->ofd == -1) {
616 605
 	cli_errmsg("cab_extract: Can't open file %s in write mode\n", name);
617
-	free(file->state);
606
+	free(file->cab->state);
618 607
 	return CL_EIO;
619 608
     }
620 609
 
... ...
@@ -628,84 +677,20 @@ int cab_extract(struct cab_file *file, const char *name)
628 628
 
629 629
 	case 0x0001: /* MSZIP */
630 630
 	    cli_dbgmsg("CAB: Compression method: MSZIP\n");
631
-	    file->state->stream = (struct mszip_stream *) mszip_init(file->fd, file->ofd, 4096, 1, file, &cab_read);
632
-	    if(!file->state->stream) {
633
-		free(file->state);
634
-		close(file->ofd);
635
-		return CL_EMSCAB;
636
-	    }
637
-	    ret = mszip_decompress(file->state->stream, file->length);
638
-	    mszip_free(file->state->stream);
639
-	    if(ret < 0 && file->offset > 0) {
640
-		memset(file->state, 0, sizeof(struct cab_state));
641
-		file->state->stream = (struct mszip_stream *) mszip_init(file->fd, file->ofd, 4096, 1, file, &cab_read);
642
-		if(!file->state->stream) {
643
-		    free(file->state);
644
-		    close(file->ofd);
645
-		    return CL_EMSCAB;
646
-		}
647
-		((struct mszip_stream *) file->state->stream)->wflag = 0;
648
-		if(mszip_decompress(file->state->stream, file->offset) == CL_SUCCESS) {
649
-		    ((struct mszip_stream *) file->state->stream)->wflag = 1;
650
-		    ret = mszip_decompress(file->state->stream, file->length);
651
-		}
652
-		mszip_free(file->state->stream);
653
-	    }
631
+	    CAB_CHGFOLDER;
632
+	    ret = mszip_decompress(file->cab->state->stream, file->length);
654 633
 	    break;
655 634
 
656 635
 	case 0x0002: /* QUANTUM */
657 636
 	    cli_dbgmsg("CAB: Compression method: QUANTUM\n");
658
-	    file->state->stream = (struct qtm_stream *) qtm_init(file->fd, file->ofd, (int) (file->folder->cmethod >> 8) & 0x1f, 4096, file, &cab_read);
659
-	    if(!file->state->stream) {
660
-		free(file->state);
661
-		close(file->ofd);
662
-		return CL_EMSCAB;
663
-	    }
664
-	    ret = qtm_decompress(file->state->stream, file->length);
665
-	    qtm_free(file->state->stream);
666
-	    if(ret < 0 && file->offset > 0) {
667
-		memset(file->state, 0, sizeof(struct cab_state));
668
-		file->state->stream = (struct qtm_stream *) qtm_init(file->fd, file->ofd, (int) (file->folder->cmethod >> 8) & 0x1f, 4096, file, &cab_read);
669
-		if(!file->state->stream) {
670
-		    free(file->state);
671
-		    close(file->ofd);
672
-		    return CL_EMSCAB;
673
-		}
674
-		((struct qtm_stream *) file->state->stream)->wflag = 0;
675
-		if(qtm_decompress(file->state->stream, file->offset) == CL_SUCCESS) {
676
-		    ((struct qtm_stream *) file->state->stream)->wflag = 1;
677
-		    ret = qtm_decompress(file->state->stream, file->length);
678
-		}
679
-		qtm_free(file->state->stream);
680
-	    }
637
+	    CAB_CHGFOLDER;
638
+	    ret = qtm_decompress(file->cab->state->stream, file->length);
681 639
 	    break;
682 640
 
683 641
 	case 0x0003: /* LZX */
684 642
 	    cli_dbgmsg("CAB: Compression method: LZX\n");
685
-	    file->state->stream = (struct lzx_stream *) lzx_init(file->fd, file->ofd, (int) (file->folder->cmethod >> 8) & 0x1f, 0, 4096, 0, file, &cab_read);
686
-	    if(!file->state->stream) {
687
-		free(file->state);
688
-		close(file->ofd);
689
-		return CL_EMSCAB;
690
-	    }
691
-	    if(file->offset > 0) {
692
-		((struct lzx_stream *) file->state->stream)->wflag = 0;
693
-		ret = lzx_decompress(file->state->stream, file->offset);
694
-		((struct lzx_stream *) file->state->stream)->wflag = 1;
695
-		if(ret < 0) {
696
-		    lzx_free(file->state->stream);
697
-		    memset(file->state, 0, sizeof(struct cab_state));
698
-		    file->state->stream = (struct lzx_stream *) lzx_init(file->fd, file->ofd, (int) (file->folder->cmethod >> 8) & 0x1f, 0, 4096, 0, file, &cab_read);
699
-		    if(!file->state->stream) {
700
-			free(file->state);
701
-			close(file->ofd);
702
-			return CL_EMSCAB;
703
-		    }
704
-                    lseek(file->fd, file->folder->offset, SEEK_SET);
705
-		}
706
-	    }
707
-	    ret = lzx_decompress(file->state->stream, file->length);
708
-	    lzx_free(file->state->stream);
643
+	    CAB_CHGFOLDER;
644
+	    ret = lzx_decompress(file->cab->state->stream, file->length);
709 645
 	    break;
710 646
 
711 647
 	default:
... ...
@@ -713,7 +698,6 @@ int cab_extract(struct cab_file *file, const char *name)
713 713
 	    ret = CL_EFORMAT;
714 714
     }
715 715
 
716
-    free(file->state);
717 716
     close(file->ofd);
718 717
 
719 718
     return ret;
... ...
@@ -1,4 +1,7 @@
1 1
 /*
2
+ *  Copyright (C) 2007-2008 Sourcefire, Inc.
3
+ *  Author: Tomasz Kojm <tkojm@clamav.net>
4
+ *
2 5
  *  Copyright (C) 2006 Tomasz Kojm <tkojm@clamav.net>
3 6
  *
4 7
  *  This program is free software; you can redistribute it and/or modify
... ...
@@ -26,8 +29,9 @@
26 26
 #define CAB_INPUTMAX (CAB_BLOCKMAX + 6144)
27 27
 
28 28
 struct cab_archive {
29
-    struct cab_folder *folders;
29
+    struct cab_folder *folders, *actfol;
30 30
     struct cab_file *files;
31
+    struct cab_state *state;
31 32
     uint32_t length;
32 33
     uint16_t nfolders;
33 34
     uint16_t nfiles;
... ...
@@ -43,6 +47,7 @@ struct cab_state {
43 43
     uint16_t blklen;
44 44
     uint16_t outlen;
45 45
     uint16_t blknum;
46
+    uint16_t cmethod;
46 47
 };
47 48
 
48 49
 struct cab_file {
... ...
@@ -55,7 +60,6 @@ struct cab_file {
55 55
     struct cab_folder *folder;
56 56
     struct cab_file *next;
57 57
     struct cab_archive *cab;
58
-    struct cab_state *state;
59 58
     uint16_t attribs;
60 59
 };
61 60