[Pkg-clamav-commits] [SCM] Debian repository for ClamAV branch, debian/unstable, updated. debian/0.95+dfsg-1-6156-g094ec9b

aCaB acab at clamav.net
Sun Apr 4 01:04:57 UTC 2010


The following commit has been merged in the debian/unstable branch:
commit bad8ee8efda5a30d8dbbd8d14a68fb6f6e6fd3c5
Merge: c12fe59e2d2e0d5c954d036cab5847b363be30c1 e78b518630ca9fa1f40522136b3cdc5a27321f5e
Author: aCaB <acab at clamav.net>
Date:   Thu Sep 17 18:30:03 2009 +0200

    merge master

diff --combined libclamav/ishield.c
index 90ffb13,d51f314..76667ac
--- a/libclamav/ishield.c
+++ b/libclamav/ishield.c
@@@ -24,12 -24,19 +24,12 @@@
  #include "clamav-config.h"
  #endif
  
 -#define _XOPEN_SOURCE 500
 -
  #include <sys/types.h>
  #include <sys/stat.h>
  #include <fcntl.h>
  #ifdef	HAVE_UNISTD_H
  #include <unistd.h>
  #endif
 -#if HAVE_MMAP
 -#ifdef HAVE_SYS_MMAN_H
 -#include <sys/mman.h>
 -#endif
 -#endif /* HAVE_MMAP */
  #if HAVE_STRING_H
  #include <string.h>
  #endif
@@@ -42,7 -49,6 +42,7 @@@
  #include "scanners.h"
  #include "cltypes.h"
  #include "others.h"
 +#include "fmap.h"
  #include "ishield.h"
  
  #ifndef O_BINARY
@@@ -65,14 -71,6 +65,14 @@@
  
  /* PACKED things go here */
  
 +struct IS_HDR {
 +    uint32_t magic; 
 +    uint32_t unk1; /* version ??? */
 +    uint32_t unk2; /* ??? */
 +    uint32_t data_off;
 +    uint32_t data_sz; /* ??? */
 +} __attribute__((packed));
 +
  struct IS_FB {
      char fname[0x104]; /* MAX_PATH */
      uint32_t unk1; /* 6 */
@@@ -189,22 -187,21 +189,22 @@@ struct IS_FILEITEM 
  
  
  
 -static int is_dump_and_scan(int desc, cli_ctx *ctx, off_t off, size_t fsize);
 +static int is_dump_and_scan(cli_ctx *ctx, off_t off, size_t fsize);
  static const uint8_t skey[] = { 0xec, 0xca, 0x79, 0xf8 }; /* ~0x13, ~0x35, ~0x86, ~0x07 */
  
  /* Extracts the content of MSI based IS */
 -int cli_scanishield_msi(int desc, cli_ctx *ctx, off_t off) {
 -    uint8_t buf[BUFSIZ];
 +int cli_scanishield_msi(cli_ctx *ctx, off_t off) {
 +    uint8_t *buf;
      unsigned int fcount, scanned = 0;
      int ret;
 +    struct F_MAP *map = *ctx->fmap;
  
      cli_dbgmsg("in ishield-msi\n");
 -    lseek(desc, off, SEEK_SET);
 -    if(cli_readn(desc, buf, 0x20) != 0x20) {
 +    if(!(buf = fmap_need_off_once(map, off, 0x20))) {
  	cli_dbgmsg("ishield-msi: short read for header\n");
  	return CL_CLEAN;
      }
 +    off += 0x20;
      if(cli_readint32(buf + 8) | cli_readint32(buf + 0xc) | cli_readint32(buf + 0x10) | cli_readint32(buf + 0x14) | cli_readint32(buf + 0x18) | cli_readint32(buf + 0x1c))
  	return CL_CLEAN;
      if(!(fcount = cli_readint32(buf))) {
@@@ -220,20 -217,16 +220,20 @@@
  	uint64_t csize;
  	z_stream z;
  
 -	if(cli_readn(desc, &fb, sizeof(fb)) != sizeof(fb)) {
 +	if(fmap_readn(map, &fb, off, sizeof(fb)) != sizeof(fb)) {
  	    cli_dbgmsg("ishield-msi: short read for fileblock\n");
  	    return CL_CLEAN;
  	}
 +	off += sizeof(fb);
  	fb.fname[sizeof(fb.fname)-1] = '\0';
  	csize = le64_to_host(fb.csize);
 -
 +	if(!CLI_ISCONTAINED(0, map->len, off, csize)) {
 +	    cli_dbgmsg("ishield-msi: next stream is out of file, giving up\n");
 +	    return CL_CLEAN;
 +	}
  	if(ctx->engine->maxfilesize && csize > ctx->engine->maxfilesize) {
- 	    cli_dbgmsg("ishield-msi: skipping stream due to size limits (%lu vs %lu)\n", csize, ctx->engine->maxfilesize);
+ 	    cli_dbgmsg("ishield-msi: skipping stream due to size limits (%lu vs %lu)\n", (unsigned long int) csize, (unsigned long int) ctx->engine->maxfilesize);
 -	    lseek(desc, csize, SEEK_CUR);
 +	    off += csize;
  	    continue;
  	}
  
@@@ -254,21 -247,20 +254,21 @@@
  	inflateInit(&z);
  	ret = CL_SUCCESS;
  	while(csize) {
 -	    unsigned int sz = csize < sizeof(buf) ? csize : sizeof(buf);
 -	    z.avail_in = cli_readn(desc, buf, sz);
 -	    if(z.avail_in <= 0) {
 +	    uint8_t buf2[BUFSIZ];
 +	    z.avail_in = MIN(csize, sizeof(buf2));
 +	    if(fmap_readn(map, buf2, off, z.avail_in) != z.avail_in) {
  		cli_dbgmsg("ishield-msi: premature EOS or read fail\n");
 -		break;    
 +		break;
  	    }
 +	    off += z.avail_in;
  	    for(i=0; i<z.avail_in; i++, lameidx++) {
 -		uint8_t c = buf[i];
 +		uint8_t c = buf2[i];
  		c = (c>>4) | (c<<4);
  		c ^= key[(lameidx & 0x3ff) % keylen];
 -		buf[i] = c;
 +		buf2[i] = c;
  	    }
  	    csize -= z.avail_in;
 -	    z.next_in = buf;
 +	    z.next_in = buf2;
  	    do {
  		int inf;
  		z.avail_out = sizeof(obuf);
@@@ -277,7 -269,7 +277,7 @@@
  		if(inf != Z_OK && inf != Z_STREAM_END && inf != Z_BUF_ERROR) {
  		    cli_dbgmsg("ishield-msi: bad stream\n");
  		    csize = 0;
 -		    lseek(desc, csize, SEEK_CUR);
 +		    off += csize;
  		    break;
  		}
  		if (cli_writen(ofd, obuf, sizeof(obuf) - z.avail_out) < 0) {
@@@ -286,8 -278,8 +286,8 @@@
  		    break;
  		}
  		if(ctx->engine->maxfilesize && z.total_out > ctx->engine->maxfilesize) {
- 		    cli_dbgmsg("ishield-msi: trimming output file due to size limits (%lu vs %lu)\n", z.total_out, ctx->engine->maxfilesize);
+ 		    cli_dbgmsg("ishield-msi: trimming output file due to size limits (%lu vs %lu)\n", z.total_out, (unsigned long int) ctx->engine->maxfilesize);
 -		    lseek(desc, csize, SEEK_CUR);
 +		    off += csize;
  		    csize = 0;
  		    break;
  		}
@@@ -333,47 -325,55 +333,47 @@@ struct IS_CABSTUFF 
  };
  
  static void md5str(uint8_t *sum);
 -static int is_parse_hdr(int desc, cli_ctx *ctx, struct IS_CABSTUFF *c);
 -static int is_extract_cab(int desc, cli_ctx *ctx, uint64_t off, uint64_t size, uint64_t csize);
 +static int is_parse_hdr(cli_ctx *ctx, struct IS_CABSTUFF *c);
 +static int is_extract_cab(cli_ctx *ctx, uint64_t off, uint64_t size, uint64_t csize);
  
  /* Extract the content of older (non-MSI) IS */
 -int cli_scanishield(int desc, cli_ctx *ctx, off_t off, size_t sz) {
 +int cli_scanishield(cli_ctx *ctx, off_t off, size_t sz) {
      char *fname, *path, *version, *strsz, *eostr, *data;
 -    char buf[2048];
 -    int rd, ret = CL_CLEAN;
 +    int ret = CL_CLEAN;
      long fsize;
      off_t coff = off;
      struct IS_CABSTUFF c = { NULL, -1, 0, 0 };
 +    struct F_MAP *map = *ctx->fmap;
  
      while(ret == CL_CLEAN) {
 -	rd = pread(desc, buf, sizeof(buf), coff);
 -	if(rd <= 0)
 -	    break;
 +	fname = fmap_need_offstr(map, coff, 2048);
 +	if(!fname) break;
 +	coff += strlen(fname) + 1;
  
 -	fname = buf;
 -	if(!*fname) break;
 -	path = memchr(fname, 0, rd);
 -	if(!path)
 -	    break;
 +	path = fmap_need_offstr(map, coff, 2048);
 +	if(!path) break;
 +	coff += strlen(path) + 1;
  
 -	path++;
 -	rd -= (path - buf);
 -	if(rd<=0 || !(version = memchr(path, 0, rd)))
 -	    break;
 +	version = fmap_need_offstr(map, coff, 2048);
 +	if(!version) break;
 +	coff += strlen(version) + 1;
  
 -	version++;
 -	rd -= (version - path);
 -	if(rd<=0 || !(strsz = memchr(version, 0, rd)))
 -	    break;
 +	strsz = fmap_need_offstr(map, coff, 2048);
 +	if(!strsz) break;
 +	coff += strlen(strsz) + 1;
  
 -	strsz++;
 -	rd -= (strsz - version);
 -	if(rd<=0 || !(data = memchr(strsz, 0, rd)))
 -	    break;
 +	data = &strsz[strlen(strsz) + 1];
  
 -	data++;
  	fsize = strtol(strsz, &eostr, 10);
  	if(fsize < 0 || fsize == LONG_MAX ||
  	   !*strsz || !eostr || eostr == strsz || *eostr ||
  	   (unsigned long)fsize >= sz ||
 -	   data - buf >= sz - fsize
 +	   data - fname >= sz - fsize
  	) break;
  
- 	cli_dbgmsg("ishield: @%lx found file %s (%s) - version %s - size %lu\n", coff, fname, path, version, fsize);
+ 	cli_dbgmsg("ishield: @%lx found file %s (%s) - version %s - size %lu\n", (unsigned long int) coff, fname, path, version, (unsigned long int) fsize);
 -	sz -= (data - buf) + fsize;
 -	coff += (data - buf);
 +	sz -= (data - fname) + fsize;
 +
  	if(!strncasecmp(fname, "data", 4)) {
  	    long cabno;
  	    if(!strcasecmp(fname + 4, "1.hdr")) {
@@@ -407,21 -407,20 +407,21 @@@
  	    }
  	}
  
 -	ret = is_dump_and_scan(desc, ctx, coff, fsize);
 +	fmap_unneed_ptr(map, fname, data-fname);
 +	ret = is_dump_and_scan(ctx, coff, fsize);
  	coff += fsize;
      }
  
      if(ret == CL_CLEAN && (c.cabcnt || c.hdr != -1)) {
 -      if((ret = is_parse_hdr(desc, ctx, &c)) == CL_CLEAN) {
 +      if((ret = is_parse_hdr(ctx, &c)) == CL_CLEAN) {
  	    unsigned int i;
  	    if(c.hdr != -1) {
  		cli_dbgmsg("ishield: scanning data1.hdr\n");
 -		ret = is_dump_and_scan(desc, ctx, c.hdr, c.hdrsz);
 +		ret = is_dump_and_scan(ctx, c.hdr, c.hdrsz);
  	    }
  	    for(i=0; i<c.cabcnt && ret == CL_CLEAN; i++) {
  		cli_dbgmsg("ishield: scanning data%u.cab\n", c.cabs[i].cabno);
 -		ret = is_dump_and_scan(desc, ctx, c.cabs[i].off, c.cabs[i].sz);
 +		ret = is_dump_and_scan(ctx, c.cabs[i].off, c.cabs[i].sz);
  	    }
        } else if( ret == CL_BREAK ) ret = CL_CLEAN;
      }
@@@ -431,10 -430,9 +431,10 @@@
  
  
  /* Utility func to scan a fd @ a given offset and size */
 -static int is_dump_and_scan(int desc, cli_ctx *ctx, off_t off, size_t fsize) {
 -    char *fname, buf[BUFSIZ];
 +static int is_dump_and_scan(cli_ctx *ctx, off_t off, size_t fsize) {
 +    char *fname, *buf;
      int ofd, ret = CL_CLEAN;
 +    struct F_MAP *map = *ctx->fmap;
  
      if(!fsize) {
  	cli_dbgmsg("ishield: skipping empty file\n");
@@@ -449,18 -447,19 +449,18 @@@
  	return CL_ECREAT;
      }
      while(fsize) {
 -	size_t rd = fsize < sizeof(buf) ? fsize : sizeof(buf);
 -	int got = pread(desc, buf, rd, off);
 -	if(got <= 0) {
 +	size_t rd = MIN(fsize, map->pgsz);
 +	if(!(buf = fmap_need_off_once(map, off, rd))) {
  	    cli_dbgmsg("ishield: read error\n");
  	    ret = CL_EREAD;
  	    break;
  	}
 -	if(cli_writen(ofd, buf, got) <= 0) {
 +	if(cli_writen(ofd, buf, rd) <= 0) {
  	    ret = CL_EWRITE;
  	    break;
  	}
 -	fsize -= got;
 -	off += got;
 +	fsize -= rd;
 +	off += rd;
      }
      if(!fsize) {
  	cli_dbgmsg("ishield: extracted to %s\n", fname);
@@@ -474,13 -473,29 +474,13 @@@
      return ret;
  }
  
 -
 -struct IS_HDR {
 -    uint32_t magic; 
 -    uint32_t unk1; /* version ??? */
 -    uint32_t unk2; /* ??? */
 -    uint32_t data_off;
 -    uint32_t data_sz; /* ??? */
 -};
 -
 -
 -#define IS_FREE_HDR if(map) munmap(map, mp_hdrsz); else free(hdr);
 -#if HAVE_MMAP
 -#define IS_MAX_NOMAP_SZ 0x100000
 -#else
 -#define IS_MAX_NOMAP_SZ CLI_MAX_ALLOCATION
 -#endif
 -
  /* Process data1.hdr and extracts all the available files from dataX.cab */
 -static int is_parse_hdr(int desc, cli_ctx *ctx, struct IS_CABSTUFF *c) { 
 +static int is_parse_hdr(cli_ctx *ctx, struct IS_CABSTUFF *c) { 
      uint32_t h1_data_off, objs_files_cnt, objs_dirs_off;
      unsigned int off, i, scanned = 0;
      int ret = CL_BREAK;
 -    char hash[33], *hdr, *map = NULL;
 +    char hash[33], *hdr;
 +    struct F_MAP *map = *ctx->fmap;
      size_t mp_hdrsz;
  
      struct IS_HDR *h1;
@@@ -492,29 -507,52 +492,29 @@@
  	return CL_CLEAN;
      }
  
 -    if(c->hdrsz < IS_MAX_NOMAP_SZ) {
 -	if(!(hdr = (char *)cli_malloc(c->hdrsz)))
 -	    return CL_EMEM;
 -	if(pread(desc, hdr, c->hdrsz, c->hdr) < (ssize_t)c->hdrsz) {
 -	    cli_errmsg("is_parse_hdr: short read for header\n");
 -	    free(hdr);
 -	    return CL_EREAD; /* hdr must be within bounds, it's k to hard fail here */
 -	}
 -    } else {
 -#if defined(HAVE_MMAP) && defined(HAVE_CLI_GETPAGESIZE)
 -	int psz = cli_getpagesize();
 -	off_t mp_hdr = (c->hdr / psz) * psz;
 -	mp_hdrsz = c->hdrsz + c->hdr - mp_hdr;
 -	if((map = mmap(NULL, mp_hdrsz, PROT_READ, MAP_PRIVATE, desc, mp_hdr))==MAP_FAILED) {
 -	    cli_errmsg("is_parse_hdr: mmap failed\n");
 -	    return CL_EMEM;
 -	}
 -	hdr = map + c->hdr - mp_hdr;
 -#else
 -	cli_warnmsg("is_parse_hdr: hdr too big and mmap is not usable\n");
 -	return CL_CLEAN;
 -#endif
 -    }
 -
 -    h1 = (struct IS_HDR *)hdr;
 -    if(!CLI_ISCONTAINED(hdr, c->hdrsz, ((char *)h1), sizeof(*h1))) {
 +    if(!(h1 = fmap_need_off(map, c->hdr, c->hdrsz))) {
  	cli_dbgmsg("is_parse_hdr: not enough room for H1\n");
 -	IS_FREE_HDR;
  	return CL_CLEAN;
      }
 +    hdr = (char *)h1;
      h1_data_off = le32_to_host(h1->data_off);
 -    objs = (struct IS_OBJECTS *)(hdr + h1_data_off);
 -    if(!CLI_ISCONTAINED(hdr, c->hdrsz, ((char *)objs), sizeof(*objs))) {
 -	cli_dbgmsg("is_parse_hdr: not enough room for OBJECTS\n");
 -	IS_FREE_HDR;
 -	return CL_CLEAN;
 +    objs = (struct IS_OBJECTS *)fmap_need_ptr(map, hdr + h1_data_off, sizeof(*objs));
 +    if(!objs) {
 +        cli_dbgmsg("is_parse_hdr: not enough room for OBJECTS\n");
 +        fmunmap(map);
 +        return CL_CLEAN;
      }
  
      cli_dbgmsg("is_parse_hdr: magic %x, unk1 %x, unk2 %x, data_off %x, data_sz %x\n",
 -	       h1->magic, h1->unk1, h1->unk2, h1_data_off, h1->data_sz);
 +               h1->magic, h1->unk1, h1->unk2, h1_data_off, h1->data_sz);
      if(le32_to_host(h1->magic) != 0x28635349) {
 -	cli_dbgmsg("is_parse_hdr: bad magic. wrong version?\n");
 -	IS_FREE_HDR;
 -	return CL_CLEAN;
 +        cli_dbgmsg("is_parse_hdr: bad magic. wrong version?\n");
 +        fmunmap(map);
 +        return CL_CLEAN;
      }
  
 +    fmap_unneed_ptr(map, h1, sizeof(*h1));
 +
  /*     cli_errmsg("COMPONENTS\n"); */
  /*     off = le32_to_host(objs->comps_off) + h1_data_off; */
  /*     for(i=1;  ; i++) { */
@@@ -551,12 -589,11 +551,12 @@@
  
      objs_files_cnt = le32_to_host(objs->files_cnt);
      off = h1_data_off + objs_dirs_off + le32_to_host(objs->dir_sz2);
 +    fmap_unneed_ptr(map, objs, sizeof(*objs));
      for(i=0; i<objs_files_cnt ;i++) {
 -	struct IS_FILEITEM *file = (struct IS_FILEITEM *)(&hdr[off]);
 +	struct IS_FILEITEM *file = (struct IS_FILEITEM *)fmap_need_off(map, c->hdr + off, sizeof(*file));
  
 -	if(CLI_ISCONTAINED(hdr, c->hdrsz, ((char *)file), sizeof(*file))) {
 -	    const char *dir_name = "", *file_name = "";
 +	if(file) {
 +	    const char *emptyname = "", *dir_name = emptyname, *file_name = emptyname;
  	    uint32_t dir_rel = h1_data_off + objs_dirs_off + 4 * le32_to_host(file->dir_id); /* rel off of dir entry from array of rel ptrs */
  	    uint32_t file_rel = objs_dirs_off + h1_data_off + le32_to_host(file->str_name_off); /* rel off of fname */
  	    uint64_t file_stream_off, file_size, file_csize;
@@@ -564,12 -601,12 +564,12 @@@
  
  	    memcpy(hash, file->md5, 16);
  	    md5str((uint8_t *)hash);
 -	    if(CLI_ISCONTAINED(hdr, c->hdrsz, &hdr[dir_rel], 4)) {
 +	    if(fmap_need_ptr_once(map, &hdr[dir_rel], 4)) {
  		dir_rel = cli_readint32(&hdr[dir_rel]) + h1_data_off + objs_dirs_off;
 -		if(CLI_ISCONTAINED(hdr, c->hdrsz, &hdr[dir_rel], 1) && memchr(&hdr[dir_rel], 0, c->hdrsz - dir_rel))
 +		if(fmap_need_str(map, &hdr[dir_rel], c->hdrsz - dir_rel))
  		    dir_name = &hdr[dir_rel];
  	    }
 -	    if(CLI_ISCONTAINED(hdr, c->hdrsz, &hdr[file_rel], 1) && memchr(&hdr[file_rel], 0, c->hdrsz - file_rel))
 +	    if(fmap_need_str(map, &hdr[file_rel], c->hdrsz - file_rel))
  		file_name = &hdr[file_rel];
  		
  	    file_stream_off = le64_to_host(file->stream_off);
@@@ -599,7 -636,7 +599,7 @@@
  			int cabret = CL_CLEAN;
  
  			if(ctx->engine->maxfilesize && file_csize > ctx->engine->maxfilesize) {
- 			    cli_dbgmsg("is_parse_hdr: skipping file due to size limits (%lu vs %lu)\n", file_csize, ctx->engine->maxfilesize);
+ 			    cli_dbgmsg("is_parse_hdr: skipping file due to size limits (%lu vs %lu)\n", (unsigned long int) file_csize, (unsigned long int) ctx->engine->maxfilesize);
  			    break;
  			}
  
@@@ -609,13 -646,10 +609,13 @@@
  				scanned++;
  				if (ctx->engine->maxfiles && scanned >= ctx->engine->maxfiles) {
  				    cli_dbgmsg("is_parse_hdr: File limit reached (max: %u)\n", ctx->engine->maxfiles);
 -				    IS_FREE_HDR;
 +				    if(file_name != emptyname)
 +					fmap_unneed_ptr(map, (void *)file_name, strlen(file_name)+1);
 +				    if(dir_name != emptyname)
 +					fmap_unneed_ptr(map, (void *)dir_name, strlen(dir_name)+1);
  				    return CL_EMAXFILES;
  				}
 -				cabret = is_extract_cab(desc, ctx, file_stream_off + c->cabs[j].off, file_size, file_csize);
 +				cabret = is_extract_cab(ctx, file_stream_off + c->cabs[j].off, file_size, file_csize);
  			    } else {
  				ret = CL_CLEAN;
   				cli_dbgmsg("is_parse_hdr: stream out of file\n");
@@@ -629,10 -663,7 +629,10 @@@
  			    cabret = CL_CLEAN;
  			}
  			if(cabret != CL_CLEAN) {
 -			    IS_FREE_HDR;
 +			    if(file_name != emptyname)
 +				fmap_unneed_ptr(map, (void *)file_name, strlen(file_name)+1);
 +			    if(dir_name != emptyname)
 +				fmap_unneed_ptr(map, (void *)dir_name, strlen(dir_name)+1);
  			    return cabret;
  			}
  		    } else {
@@@ -643,17 -674,13 +643,17 @@@
  	    default:
  		cli_dbgmsg("is_parse_hdr: skipped unknown file entry %u\n", i);
  	    }
 +	    if(file_name != emptyname)
 +		fmap_unneed_ptr(map, (void *)file_name, strlen(file_name)+1);
 +	    if(dir_name != emptyname)
 +		fmap_unneed_ptr(map, (void *)dir_name, strlen(dir_name)+1);
 +	    fmap_unneed_ptr(map, file, sizeof(*file));
  	} else {
  	    ret = CL_CLEAN;
  	    cli_dbgmsg("is_parse_hdr: FILEITEM out of bounds\n");
  	}
  	off += sizeof(*file);
      }
 -    IS_FREE_HDR;
      return ret;
  }
  
@@@ -673,25 -700,55 +673,25 @@@ static void md5str(uint8_t *sum) 
  
  #define IS_CABBUFSZ 65536
  
 -static int is_extract_cab(int desc, cli_ctx *ctx, uint64_t off, uint64_t size, uint64_t csize) {
 +static int is_extract_cab(cli_ctx *ctx, uint64_t off, uint64_t size, uint64_t csize) {
      uint8_t *inbuf, *outbuf;
      char *tempfile;
 -    FILE *in;
      int ofd, ret = CL_CLEAN;
      z_stream z;
      uint64_t outsz = 0;
      int success = 0;
 +    struct F_MAP *map = *ctx->fmap;
  
 -    if((ofd=dup(desc)) < 0) {
 -	cli_errmsg("is_extract_cab: dup failed\n");
 -	return CL_EDUP;
 -    }
 -    if(!(in = fdopen(ofd, "rb"))) {
 -	cli_errmsg("is_extract_cab: fdopen failed\n");
 -	close(ofd);
 -	return CL_EOPEN;
 -    }
 -#if HAVE_FSEEKO
 -    if(fseeko(in, (off_t)off, SEEK_SET))
 -#else
 -    if(fseek(in, (long)off, SEEK_SET))
 -#endif
 -    {
 -	cli_dbgmsg("is_extract_cab: fseek failed\n");
 -	fclose(in);
 -	return CL_ESEEK;
 -    }
 -    if(!(inbuf = cli_malloc(IS_CABBUFSZ))) {
 -	fclose(in);
 -	return CL_EMEM;
 -    }
 -    if(!(outbuf = cli_malloc(IS_CABBUFSZ))) {
 -	free(inbuf);
 -	fclose(in);
 +    if(!(outbuf = cli_malloc(IS_CABBUFSZ)))
  	return CL_EMEM;
 -    }
 +
      if(!(tempfile = cli_gentemp(ctx->engine->tmpdir))) {
 -	free(inbuf);
  	free(outbuf);
 -	fclose(in);
 -	return CL_EMEM;
      }
      if((ofd = open(tempfile, O_RDWR|O_CREAT|O_TRUNC|O_BINARY, S_IRUSR|S_IWUSR)) < 0) {
  	cli_errmsg("is_extract_cab: failed to create file %s\n", tempfile);
  	free(tempfile);
 -	free(inbuf);
  	free(outbuf);
 -	fclose(in);
  	return CL_ECREAT;
      }
  
@@@ -703,12 -760,11 +703,12 @@@
  	    break;
  	}
  	csize -= 2;
 -	if(!fread(outbuf, 2, 1, in)) {
 +	if(!(inbuf = fmap_need_off_once(map, off, 2))) {
  	    cli_dbgmsg("is_extract_cab: short read for chunk size\n");
  	    break;
  	}
 -	chunksz = outbuf[0] | (outbuf[1] << 8);
 +	off += 2;
 +	chunksz = inbuf[0] | (inbuf[1] << 8);
  	if(!chunksz) {
  	    cli_dbgmsg("is_extract_cab: zero sized chunk\n");
  	    continue;
@@@ -718,11 -774,10 +718,11 @@@
  	    break;
  	}
  	csize -= chunksz;
 -	if(!fread(inbuf, chunksz, 1, in)) {
 +	if(!(inbuf = fmap_need_off_once(map, off, chunksz))) {
  	    cli_dbgmsg("is_extract_cab: short read for chunk\n");
  	    break;
  	}
 +	off += chunksz;
  	memset(&z, 0, sizeof(z));
  	inflateInit2(&z, -MAX_WBITS);
  	z.next_in = (uint8_t *)inbuf;
@@@ -742,7 -797,7 +742,7 @@@
  		    break;
  		}
  		if(ctx->engine->maxfilesize && z.total_out > ctx->engine->maxfilesize) {
- 		    cli_dbgmsg("ishield_extract_cab: trimming output file due to size limits (%lu vs %lu)\n", z.total_out, ctx->engine->maxfilesize);
+ 		    cli_dbgmsg("ishield_extract_cab: trimming output file due to size limits (%lu vs %lu)\n", z.total_out, (unsigned long int) ctx->engine->maxfilesize);
  		    success = 1;
  		    outsz = size;
  		    break;
@@@ -755,6 -810,8 +755,6 @@@
  	inflateEnd(&z);
  	if(!success) break;
      }
 -    fclose(in);
 -    free(inbuf);
      free(outbuf);
      if(success) {
  	if (outsz != size)
diff --combined libclamav/matcher-ac.c
index 2f3c144,00efb69..7646195
--- a/libclamav/matcher-ac.c
+++ b/libclamav/matcher-ac.c
@@@ -45,12 -45,18 +45,18 @@@
  
  #include "mpool.h"
  
+ #define AC_SPECIAL_ALT_CHAR	1
+ #define AC_SPECIAL_ALT_STR	2
+ #define AC_SPECIAL_LINE_START	3
+ #define AC_SPECIAL_LINE_END	4
+ #define AC_SPECIAL_BOUNDARY	5
+ 
  int cli_ac_addpatt(struct cli_matcher *root, struct cli_ac_patt *pattern)
  {
  	struct cli_ac_node *pt, *next;
  	struct cli_ac_patt *ph;
  	void *newtable;
- 	struct cli_ac_alt *a1, *a2;
+ 	struct cli_ac_special *a1, *a2;
  	uint8_t i, match;
  	uint16_t len = MIN(root->ac_maxdepth, pattern->length);
  
@@@ -131,27 -137,31 +137,31 @@@
      while(ph) {
  	if((ph->length == pattern->length) && (ph->prefix_length == pattern->prefix_length) && (ph->ch[0] == pattern->ch[0]) && (ph->ch[1] == pattern->ch[1])) {
  	    if(!memcmp(ph->pattern, pattern->pattern, ph->length * sizeof(uint16_t)) && !memcmp(ph->prefix, pattern->prefix, ph->prefix_length * sizeof(uint16_t))) {
- 		if(!ph->alt && !pattern->alt) {
+ 		if(!ph->special && !pattern->special) {
  		    match = 1;
- 		} else if(ph->alt == pattern->alt) {
+ 		} else if(ph->special == pattern->special) {
  		    match = 1;
- 		    for(i = 0; i < ph->alt; i++) {
- 			a1 = ph->alttable[i];
- 			a2 = pattern->alttable[i];
+ 		    for(i = 0; i < ph->special; i++) {
+ 			a1 = ph->special_table[i];
+ 			a2 = pattern->special_table[i];
  
  			if(a1->num != a2->num) {
  			    match = 0;
  			    break;
  			}
- 			if(a1->chmode != a2->chmode) {
+ 			if(a1->negative != a2->negative) {
+ 			    match = 0;
+ 			    break;
+ 			}
+ 			if(a1->type != a2->type) {
  			    match = 0;
  			    break;
- 			} else if(a1->chmode) {
+ 			} else if(a1->type == AC_SPECIAL_ALT_CHAR) {
  			    if(memcmp(a1->str, a2->str, a1->num)) {
  				match = 0;
  				break;
  			    }
- 			} else {
+ 			} else if(a1->type == AC_SPECIAL_ALT_STR) {
  			    while(a1 && a2) {
  				if((a1->len != a2->len) || memcmp(a1->str, a2->str, a1->len))
  				    break;
@@@ -350,22 -360,22 +360,22 @@@ int cli_ac_init(struct cli_matcher *roo
  }
  
  #ifdef USE_MPOOL
- #define mpool_ac_free_alt(a, b) ac_free_alt(a, b)
- static void ac_free_alt(mpool_t *mempool, struct cli_ac_patt *p)
+ #define mpool_ac_free_special(a, b) ac_free_special(a, b)
+ static void ac_free_special(mpool_t *mempool, struct cli_ac_patt *p)
  #else
- #define mpool_ac_free_alt(a, b) ac_free_alt(b)
- static void ac_free_alt(struct cli_ac_patt *p)
+ #define mpool_ac_free_special(a, b) ac_free_special(b)
+ static void ac_free_special(struct cli_ac_patt *p)
  #endif
  {
- 	uint16_t i;
- 	struct cli_ac_alt *a1, *a2;
+ 	unsigned int i;
+ 	struct cli_ac_special *a1, *a2;
  
  
-     if(!p->alt)
+     if(!p->special)
  	return;
  
-     for(i = 0; i < p->alt; i++) {
- 	a1 = p->alttable[i];
+     for(i = 0; i < p->special; i++) {
+ 	a1 = p->special_table[i];
  	while(a1) {
  	    a2 = a1;
  	    a1 = a1->next;
@@@ -374,7 -384,7 +384,7 @@@
  	    mpool_free(mempool, a2);
  	}
      }
-     mpool_free(mempool, p->alttable);
+     mpool_free(mempool, p->special_table);
  }
  
  void cli_ac_free(struct cli_matcher *root)
@@@ -387,8 -397,8 +397,8 @@@
  	patt = root->ac_pattable[i];
  	mpool_free(root->mempool, patt->prefix ? patt->prefix : patt->pattern);
  	mpool_free(root->mempool, patt->virname);
- 	if(patt->alt)
- 	    mpool_ac_free_alt(root->mempool, patt);
+ 	if(patt->special)
+ 	    mpool_ac_free_special(root->mempool, patt);
  	mpool_free(root->mempool, patt);
      }
      if(root->ac_pattable)
@@@ -650,61 -660,75 +660,75 @@@ int cli_ac_chklsig(const char *expr, co
   *        more than one of them can match at the current position.
   */
  
- #define AC_MATCH_CHAR(p,b)						\
-     switch(wc = p & CLI_MATCH_WILDCARD) {				\
- 	case CLI_MATCH_CHAR:						\
- 	    if((unsigned char) p != b)					\
- 		match = 0;						\
- 	    break;							\
- 									\
- 	case CLI_MATCH_IGNORE:						\
- 	    break;							\
- 									\
- 	case CLI_MATCH_ALTERNATIVE:					\
- 	    match = 0;							\
- 	    alt = pattern->alttable[altcnt];				\
- 	    if(alt->chmode) {						\
- 		for(j = 0; j < alt->num; j++) {				\
- 		    if(alt->str[j] == b) {				\
- 			match = 1;					\
- 			break;						\
- 		    }							\
- 		}							\
- 	    } else {							\
- 		while(alt) {						\
- 		    if(bp + alt->len <= length) {			\
- 			if(!memcmp(&buffer[bp], alt->str, alt->len)) {	\
- 			    match = 1;					\
- 			    bp += alt->len - 1;				\
- 			    break;					\
- 			}						\
- 		    }							\
- 		    alt = alt->next;					\
- 		}							\
- 	    }								\
- 	    altcnt++;							\
- 	    break;							\
- 									\
- 	case CLI_MATCH_NIBBLE_HIGH:					\
- 	    if((unsigned char) (p & 0x00f0) != (b & 0xf0))		\
- 		match = 0;						\
- 	    break;							\
- 									\
- 	case CLI_MATCH_NIBBLE_LOW:					\
- 	    if((unsigned char) (p & 0x000f) != (b & 0x0f))		\
- 		match = 0;						\
- 	    break;							\
- 									\
- 	default:							\
- 	    cli_errmsg("ac_findmatch: Unknown wildcard 0x%x\n", wc);	\
- 	    match = 0;							\
+ #define AC_MATCH_CHAR(p,b)								\
+     switch(wc = p & CLI_MATCH_WILDCARD) {						\
+ 	case CLI_MATCH_CHAR:								\
+ 	    if((unsigned char) p != b)							\
+ 		match = 0;								\
+ 	    break;									\
+ 											\
+ 	case CLI_MATCH_IGNORE:								\
+ 	    break;									\
+ 											\
+ 	case CLI_MATCH_SPECIAL:								\
+ 	    special = pattern->special_table[specialcnt];				\
+ 	    match = special->negative;							\
+ 	    switch(special->type) {							\
+ 		case AC_SPECIAL_ALT_CHAR:						\
+ 		    for(j = 0; j < special->num; j++) {					\
+ 			if(special->str[j] == b) {					\
+ 			    match = !special->negative;					\
+ 			    break;							\
+ 			} else if(special->str[j] > b)					\
+ 			    break;							\
+ 		    }									\
+ 		    break;								\
+ 											\
+ 		case AC_SPECIAL_ALT_STR:						\
+ 		    while(special) {							\
+ 			if(bp + special->len <= length) {				\
+ 			    if(!memcmp(&buffer[bp], special->str, special->len)) {	\
+ 				match = !special->negative;				\
+ 				bp += special->len - 1;					\
+ 				break;							\
+ 			    }								\
+ 			}								\
+ 			special = special->next;					\
+ 		    }									\
+ 		    break;								\
+ 											\
+ 		case AC_SPECIAL_BOUNDARY:						\
+ 		    if(memchr("\x22\x27\x20\x2f\x3d\x2d\x5f\x3e\x0a\x0d", b, 10))	\
+ 			match = !special->negative;					\
+ 		    break;								\
+ 											\
+ 		default:								\
+ 		    cli_errmsg("ac_findmatch: Unknown special\n");			\
+ 		    match = 0;								\
+ 	    }										\
+ 	    specialcnt++;								\
+ 	    break;									\
+ 											\
+ 	case CLI_MATCH_NIBBLE_HIGH:							\
+ 	    if((unsigned char) (p & 0x00f0) != (b & 0xf0))				\
+ 		match = 0;								\
+ 	    break;									\
+ 											\
+ 	case CLI_MATCH_NIBBLE_LOW:							\
+ 	    if((unsigned char) (p & 0x000f) != (b & 0x0f))				\
+ 		match = 0;								\
+ 	    break;									\
+ 											\
+ 	default:									\
+ 	    cli_errmsg("ac_findmatch: Unknown wildcard 0x%x\n", wc);			\
+ 	    match = 0;									\
      }
  
  inline static int ac_findmatch(const unsigned char *buffer, uint32_t offset, uint32_t length, const struct cli_ac_patt *pattern, uint32_t *end)
  {
  	uint32_t bp, match;
- 	uint16_t wc, i, j, altcnt = pattern->alt_pattern;
- 	struct cli_ac_alt *alt;
+ 	uint16_t wc, i, j, specialcnt = pattern->special_pattern;
+ 	struct cli_ac_special *special;
  
  
      if((offset + pattern->length > length) || (pattern->prefix_length > offset))
@@@ -737,7 -761,7 +761,7 @@@
      }
  
      if(pattern->prefix) {
- 	altcnt = 0;
+ 	specialcnt = 0;
  	bp = offset - pattern->prefix_length;
  	match = 1;
  	for(i = 0; i < pattern->prefix_length; i++) {
@@@ -830,23 -854,28 +854,23 @@@ int cli_ac_initdata(struct cli_ac_data 
      return CL_SUCCESS;
  }
  
 -int cli_ac_caloff(const struct cli_matcher *root, struct cli_ac_data *data, int fd)
 +int cli_ac_caloff(const struct cli_matcher *root, struct cli_ac_data *data, struct F_MAP *map)
  {
  	int ret;
  	unsigned int i;
  	struct cli_ac_patt *patt;
  	struct cli_target_info info;
 -	struct stat sb;
  
 -    if(fd != -1) {
 +    if(map) {
  	memset(&info, 0, sizeof(info));
 -	if(fstat(fd, &sb) == -1) {
 -	    cli_errmsg("cli_ac_caloff: fstat(%d) failed\n", fd);
 -	    return CL_ESTAT;
 -	}
 -	info.fsize = sb.st_size;
 +	info.fsize = map->len;
      }
  
      for(i = 0; i < root->ac_reloff_num; i++) {
  	patt = root->ac_reloff[i];
 -	if(fd == -1) {
 +	if(!map) {
  	    data->offset[patt->offset_min] = CLI_OFF_NONE;
 -	} else if((ret = cli_caloff(NULL, &info, fd, root->type, patt->offdata, &data->offset[patt->offset_min], &data->offset[patt->offset_max]))) {
 +	} else if((ret = cli_caloff(NULL, &info, map, root->type, patt->offdata, &data->offset[patt->offset_min], &data->offset[patt->offset_max]))) {
  	    cli_errmsg("cli_ac_caloff: Can't calculate relative offset in signature for %s\n", patt->virname);
  	    if(info.exeinfo.section)
  		free(info.exeinfo.section);
@@@ -855,7 -884,7 +879,7 @@@
  	    data->offset[patt->offset_min] = CLI_OFF_NONE;
  	}
      }
 -    if(fd != -1 && info.exeinfo.section)
 +    if(map && info.exeinfo.section)
  	free(info.exeinfo.section);
  
      return CL_SUCCESS;
@@@ -1151,6 -1180,11 +1175,11 @@@ int cli_ac_scanbuff(const unsigned cha
      return (mode & AC_SCAN_FT) ? type : CL_CLEAN;
  }
  
+ static int qcompare(const void *a, const void *b)
+ {
+     return *(unsigned char *)a - *(unsigned char *)b;
+ }
+ 
  /* FIXME: clean up the code */
  int cli_ac_addsig(struct cli_matcher *root, const char *virname, const char *hexsig, uint32_t sigid, uint16_t parts, uint16_t partno, uint16_t rtype, uint16_t type, uint32_t mindist, uint32_t maxdist, const char *offset, const uint32_t *lsigid, unsigned int options)
  {
@@@ -1158,7 -1192,7 +1187,7 @@@
  	char *pt, *pt2, *hex = NULL, *hexcpy = NULL;
  	uint16_t i, j, ppos = 0, pend, *dec, nzpos = 0;
  	uint8_t wprefix = 0, zprefix = 1, plen = 0, nzplen = 0;
- 	struct cli_ac_alt *newalt, *altpt, **newtable;
+ 	struct cli_ac_special *newspecial, *specialpt, **newtable;
  	int ret, error = CL_SUCCESS;
  
  
@@@ -1293,99 -1327,121 +1322,121 @@@
  		error = CL_EMALFDB;
  		break;
  	    }
- 
+ 	    newspecial = (struct cli_ac_special *) mpool_calloc(root->mempool, 1, sizeof(struct cli_ac_special));
+ 	    if(!newspecial) {
+ 		cli_errmsg("cli_ac_addsig: Can't allocate newspecial\n");
+ 		error = CL_EMEM;
+ 		break;
+ 	    }
+ 	    if(pt >= hexcpy + 2) {
+ 		if(pt[-2] == '!') {
+ 		    newspecial->negative = 1;
+ 		    pt[-2] = 0;
+ 		}
+ 	    }
  	    strcat(hexnew, start);
  	    strcat(hexnew, "()");
  
  	    if(!(start = strchr(pt, ')'))) {
+ 		mpool_free(root->mempool, newspecial);
  		error = CL_EMALFDB;
  		break;
  	    }
  	    *start++ = 0;
- 
- 	    newalt = (struct cli_ac_alt *) mpool_calloc(root->mempool, 1, sizeof(struct cli_ac_alt));
- 	    if(!newalt) {
- 		cli_errmsg("cli_ac_addsig: Can't allocate newalt\n");
- 		error = CL_EMEM;
+ 	    if(!strlen(pt)) {
+ 		cli_errmsg("cli_ac_addsig: Empty block\n");
+ 		error = CL_EMALFDB;
  		break;
  	    }
- 
- 	    new->alt++;
- 
- 	    newtable = (struct cli_ac_alt **) mpool_realloc(root->mempool, new->alttable, new->alt * sizeof(struct cli_ac_alt *));
+ 	    new->special++;
+ 	    newtable = (struct cli_ac_special **) mpool_realloc(root->mempool, new->special_table, new->special * sizeof(struct cli_ac_special *));
  	    if(!newtable) {
- 		new->alt--;
- 		mpool_free(root->mempool, newalt);
- 		cli_errmsg("cli_ac_addsig: Can't realloc new->alttable\n");
+ 		new->special--;
+ 		mpool_free(root->mempool, newspecial);
+ 		cli_errmsg("cli_ac_addsig: Can't realloc new->special_table\n");
  		error = CL_EMEM;
  		break;
  	    }
- 	    newtable[new->alt - 1] = newalt;
- 	    new->alttable = newtable;
- 
- 	    for(i = 0; i < strlen(pt); i++)
- 		if(pt[i] == '|')
- 		    newalt->num++;
- 
-             if(!newalt->num) {
-                 error = CL_EMALFDB;
-                 break;
-             } else
-                 newalt->num++;
- 
- 	    if(3 * newalt->num - 1 == (uint16_t) strlen(pt)) {
- 		newalt->chmode = 1;
- 		newalt->str = (unsigned char *) mpool_malloc(root->mempool, newalt->num);
- 		if(!newalt->str) {
- 		    cli_errmsg("cli_ac_addsig: Can't allocate newalt->str\n");
- 		    error = CL_EMEM;
- 		    break;
- 		}
- 	    }
+ 	    newtable[new->special - 1] = newspecial;
+ 	    new->special_table = newtable;
+ 
+ 	    if(!strcmp(pt, "B")) {
+ 		newspecial->type = AC_SPECIAL_BOUNDARY;
+ 	    /* TODO
+ 	    } else if(strcmp(pt, "S")) {
+ 		newspecial->type = AC_SPECIAL_LINE_START;
+ 	    } else if(strcmp(pt, "E")) {
+ 		newspecial->type = AC_SPECIAL_LINE_END;
+ 	    } else if(strcmp(pt, "W")) {
+ 		newspecial->type = AC_SPECIAL_WHITE;
+ 	    */
+ 	    } else {
+ 		for(i = 0; i < strlen(pt); i++)
+ 		    if(pt[i] == '|')
+ 			newspecial->num++;
  
- 	    for(i = 0; i < newalt->num; i++) {
- 		if(!(h = cli_strtok(pt, i, "|"))) {
+ 		if(!newspecial->num) {
  		    error = CL_EMALFDB;
  		    break;
+ 		} else
+ 		    newspecial->num++;
+ 
+ 		if(3 * newspecial->num - 1 == (uint16_t) strlen(pt)) {
+ 		    newspecial->type = AC_SPECIAL_ALT_CHAR;
+ 		    newspecial->str = (unsigned char *) mpool_malloc(root->mempool, newspecial->num);
+ 		    if(!newspecial->str) {
+ 			cli_errmsg("cli_ac_addsig: Can't allocate newspecial->str\n");
+ 			error = CL_EMEM;
+ 			break;
+ 		    }
+ 		} else {
+ 		    newspecial->type = AC_SPECIAL_ALT_STR;
  		}
  
- 		if(!(c = cli_mpool_hex2str(root->mempool, h))) {
- 		    free(h);
- 		    error = CL_EMALFDB;
- 		    break;
- 		}
+ 		for(i = 0; i < newspecial->num; i++) {
+ 		    if(!(h = cli_strtok(pt, i, "|"))) {
+ 			error = CL_EMALFDB;
+ 			break;
+ 		    }
  
- 		if(newalt->chmode) {
- 		    newalt->str[i] = *c;
- 		    mpool_free(root->mempool, c);
- 		} else {
- 		    if(i) {
- 			altpt = newalt;
- 			while(altpt->next)
- 			    altpt = altpt->next;
- 
- 			altpt->next = (struct cli_ac_alt *) mpool_calloc(root->mempool, 1, sizeof(struct cli_ac_alt));
- 			if(!altpt->next) {
- 			    cli_errmsg("cli_ac_addsig: Can't allocate altpt->next\n");
- 			    error = CL_EMEM;
- 			    free(c);
- 			    free(h);
- 			    break;
- 			}
+ 		    if(!(c = cli_mpool_hex2str(root->mempool, h))) {
+ 			free(h);
+ 			error = CL_EMALFDB;
+ 			break;
+ 		    }
  
- 			altpt->next->str = (unsigned char *) c;
- 			altpt->next->len = strlen(h) / 2;
+ 		    if(newspecial->type == AC_SPECIAL_ALT_CHAR) {
+ 			newspecial->str[i] = *c;
+ 			mpool_free(root->mempool, c);
  		    } else {
- 			newalt->str = (unsigned char *) c;
- 			newalt->len = strlen(h) / 2;
+ 			if(i) {
+ 			    specialpt = newspecial;
+ 			    while(specialpt->next)
+ 				specialpt = specialpt->next;
+ 
+ 			    specialpt->next = (struct cli_ac_special *) mpool_calloc(root->mempool, 1, sizeof(struct cli_ac_special));
+ 			    if(!specialpt->next) {
+ 				cli_errmsg("cli_ac_addsig: Can't allocate specialpt->next\n");
+ 				error = CL_EMEM;
+ 				free(c);
+ 				free(h);
+ 				break;
+ 			    }
+ 			    specialpt->next->str = (unsigned char *) c;
+ 			    specialpt->next->len = strlen(h) / 2;
+ 			} else {
+ 			    newspecial->str = (unsigned char *) c;
+ 			    newspecial->len = strlen(h) / 2;
+ 			}
  		    }
+ 		    free(h);
  		}
+ 		if(newspecial->type == AC_SPECIAL_ALT_CHAR)
+ 		    cli_qsort(newspecial->str, newspecial->num, sizeof(unsigned char), qcompare);
  
- 		free(h);
+ 		if(error)
+ 		    break;
  	    }
- 
- 	    if(error)
- 		break;
  	}
  
  	if(start)
@@@ -1395,9 -1451,9 +1446,9 @@@
  	free(hexcpy);
  
  	if(error) {
- 	    if(new->alt) {
+ 	    if(new->special) {
  		free(hex);
- 		mpool_ac_free_alt(root->mempool, new);
+ 		mpool_ac_free_special(root->mempool, new);
  	    }
  	    mpool_free(root->mempool, new);
  	    return error;
@@@ -1406,8 -1462,8 +1457,8 @@@
  
      new->pattern = cli_mpool_hex2ui(root->mempool, hex ? hex : hexsig);
      if(new->pattern == NULL) {
- 	if(new->alt)
- 	    mpool_ac_free_alt(root->mempool, new);
+ 	if(new->special)
+ 	    mpool_ac_free_special(root->mempool, new);
  	mpool_free(root->mempool, new);
  	free(hex);
  	return CL_EMALFDB;
@@@ -1456,7 -1512,7 +1507,7 @@@
  
  	if(plen < root->ac_mindepth) {
  	    cli_errmsg("cli_ac_addsig: Can't find a static subpattern of length %u\n", root->ac_mindepth);
- 	    mpool_ac_free_alt(root->mempool, new);
+ 	    mpool_ac_free_special(root->mempool, new);
  	    mpool_free(root->mempool, new->pattern);
  	    mpool_free(root->mempool, new);
  	    return CL_EMALFDB;
@@@ -1468,8 -1524,8 +1519,8 @@@
  	new->length -= ppos;
  
  	for(i = 0; i < new->prefix_length; i++)
- 	    if((new->prefix[i] & CLI_MATCH_WILDCARD) == CLI_MATCH_ALTERNATIVE)
- 		new->alt_pattern++;
+ 	    if((new->prefix[i] & CLI_MATCH_WILDCARD) == CLI_MATCH_SPECIAL)
+ 		new->special_pattern++;
      }
  
      if(new->length > root->maxpatlen)
@@@ -1478,7 -1534,7 +1529,7 @@@
      new->virname = cli_mpool_virname(root->mempool, (char *) virname, options & CL_DB_OFFICIAL);
      if(!new->virname) {
  	mpool_free(root->mempool, new->prefix ? new->prefix : new->pattern);
- 	mpool_ac_free_alt(root->mempool, new);
+ 	mpool_ac_free_special(root->mempool, new);
  	mpool_free(root->mempool, new);
  	return CL_EMEM;
      }
@@@ -1486,10 -1542,10 +1537,10 @@@
      if(new->lsigid[0])
  	root->ac_lsigtable[new->lsigid[1]]->virname = new->virname;
  
 -    ret = cli_caloff(offset, NULL, -1, root->type, new->offdata, &new->offset_min, &new->offset_max);
 +    ret = cli_caloff(offset, NULL, NULL, root->type, new->offdata, &new->offset_min, &new->offset_max);
      if(ret != CL_SUCCESS) {
  	mpool_free(root->mempool, new->prefix ? new->prefix : new->pattern);
- 	mpool_ac_free_alt(root->mempool, new);
+ 	mpool_ac_free_special(root->mempool, new);
  	mpool_free(root->mempool, new->virname);
  	mpool_free(root->mempool, new);
  	return ret;
@@@ -1498,7 -1554,7 +1549,7 @@@
      if((ret = cli_ac_addpatt(root, new))) {
  	mpool_free(root->mempool, new->prefix ? new->prefix : new->pattern);
  	mpool_free(root->mempool, new->virname);
- 	mpool_ac_free_alt(root->mempool, new);
+ 	mpool_ac_free_special(root->mempool, new);
  	mpool_free(root->mempool, new);
  	return ret;
      }
diff --combined libclamav/matcher-ac.h
index e8dc62f,ad0b9ab..90d9816
--- a/libclamav/matcher-ac.h
+++ b/libclamav/matcher-ac.h
@@@ -25,7 -25,6 +25,7 @@@
  
  #include "filetypes.h"
  #include "cltypes.h"
 +#include "fmap.h"
  
  #define AC_CH_MAXDIST 32
  
@@@ -39,11 -38,11 +39,11 @@@ struct cli_ac_data 
      uint32_t *offset;
  };
  
- struct cli_ac_alt {
+ struct cli_ac_special {
      unsigned char *str;
-     struct cli_ac_alt *next;
+     struct cli_ac_special *next;
      uint16_t len, num;
-     uint8_t chmode;
+     uint8_t type, negative;
  };
  
  struct cli_ac_patt {
@@@ -56,8 -55,8 +56,8 @@@
      void *customdata;
      uint16_t ch_mindist[2];
      uint16_t ch_maxdist[2];
-     uint16_t parts, partno, alt, alt_pattern;
-     struct cli_ac_alt **alttable;
+     uint16_t parts, partno, special, special_pattern;
+     struct cli_ac_special **special_table;
      struct cli_ac_patt *next, *next_same;
      uint8_t depth;
      uint16_t rtype, type;
@@@ -87,7 -86,7 +87,7 @@@ void cli_ac_freedata(struct cli_ac_dat
  int cli_ac_scanbuff(const unsigned char *buffer, uint32_t length, const char **virname, void **customdata, struct cli_ac_result **res, const struct cli_matcher *root, struct cli_ac_data *mdata, uint32_t offset, cli_file_t ftype, struct cli_matched_type **ftoffset, unsigned int mode, const cli_ctx *ctx);
  int cli_ac_buildtrie(struct cli_matcher *root);
  int cli_ac_init(struct cli_matcher *root, uint8_t mindepth, uint8_t maxdepth);
 -int cli_ac_caloff(const struct cli_matcher *root, struct cli_ac_data *data, int fd);
 +int cli_ac_caloff(const struct cli_matcher *root, struct cli_ac_data *data, struct F_MAP *map);
  void cli_ac_free(struct cli_matcher *root);
  int cli_ac_addsig(struct cli_matcher *root, const char *virname, const char *hexsig, uint32_t sigid, uint16_t parts, uint16_t partno, uint16_t rtype, uint16_t type, uint32_t mindist, uint32_t maxdist, const char *offset, const uint32_t *lsigid, unsigned int options);
  
diff --combined libclamav/matcher-bm.c
index 6b592eb,7c8eda6..caf4972
--- a/libclamav/matcher-bm.c
+++ b/libclamav/matcher-bm.c
@@@ -51,7 -51,7 +51,7 @@@ int cli_bm_addpatt(struct cli_matcher *
  	return CL_EMALFDB;
      }
  
 -    if((ret = cli_caloff(offset, NULL, -1, root->type, pattern->offdata, &pattern->offset_min, &pattern->offset_max))) {
 +    if((ret = cli_caloff(offset, NULL, NULL, root->type, pattern->offdata, &pattern->offset_min, &pattern->offset_max))) {
  	cli_errmsg("cli_bm_addpatt: Can't calculate offset for signature %s\n", pattern->virname);
  	return ret;
      }
@@@ -145,12 -145,13 +145,12 @@@ static int qcompare(const void *a, cons
      return *(const uint32_t *)a - *(const uint32_t *)b;
  }
  
 -int cli_bm_initoff(const struct cli_matcher *root, struct cli_bm_off *data, int fd)
 +int cli_bm_initoff(const struct cli_matcher *root, struct cli_bm_off *data, struct F_MAP *map)
  {
  	int ret;
  	unsigned int i;
  	struct cli_bm_patt *patt;
  	struct cli_target_info info;
 -	struct stat sb;
  
  
      if(!root->bm_patterns) {
@@@ -159,7 -160,11 +159,7 @@@
  	return CL_SUCCESS;
      }
      memset(&info, 0, sizeof(info));
 -    if(fstat(fd, &sb) == -1) {
 -	cli_errmsg("cli_bm_initoff: fstat(%d) failed\n", fd);
 -	return CL_ESTAT;
 -    }
 -    info.fsize = sb.st_size;
 +    info.fsize = map->len;
  
      data->cnt = data->pos = 0;
      data->offtab = (uint32_t *) cli_malloc(root->bm_patterns * sizeof(uint32_t));
@@@ -178,7 -183,7 +178,7 @@@
  	if(patt->offdata[0] == CLI_OFF_ABSOLUTE) {
  	    data->offtab[data->cnt] = patt->offset_min + patt->prefix_length;
  	    data->cnt++;
 -	} else if((ret = cli_caloff(NULL, &info, fd, root->type, patt->offdata, &data->offset[patt->offset_min], NULL))) {
 +	} else if((ret = cli_caloff(NULL, &info, map, root->type, patt->offdata, &data->offset[patt->offset_min], NULL))) {
  	    cli_errmsg("cli_bm_initoff: Can't calculate relative offset in signature for %s\n", patt->virname);
  	    if(info.exeinfo.section)
  		free(info.exeinfo.section);
@@@ -195,7 -200,7 +195,7 @@@
      if(info.exeinfo.section)
  	free(info.exeinfo.section);
  
-     qsort(data->offtab, data->cnt, sizeof(uint32_t), qcompare);
+     cli_qsort(data->offtab, data->cnt, sizeof(uint32_t), qcompare);
      return CL_SUCCESS;
  }
  
@@@ -238,7 -243,7 +238,7 @@@ void cli_bm_free(struct cli_matcher *ro
      }
  }
  
 -int cli_bm_scanbuff(const unsigned char *buffer, uint32_t length, const char **virname, const struct cli_matcher *root, uint32_t offset, int fd, struct cli_bm_off *offdata)
 +int cli_bm_scanbuff(const unsigned char *buffer, uint32_t length, const char **virname, const struct cli_matcher *root, uint32_t offset, struct F_MAP *map, struct cli_bm_off *offdata)
  {
  	uint32_t i, j, off, off_min, off_max;
  	uint8_t found, pchain, shift;
@@@ -272,7 -277,7 +272,7 @@@
  	    if(p && p->cnt == 1 && p->pattern0 != prefix) {
  		if(offdata) {
  		    off = offset + i - BM_MIN_LENGTH + BM_BLOCK_SIZE;
- 		    for(; off >= offdata->offtab[offdata->pos] && offdata->pos < offdata->cnt; offdata->pos++);
+ 		    for(; offdata->pos < offdata->cnt && off >= offdata->offtab[offdata->pos]; offdata->pos++);
  		    if(offdata->pos == offdata->cnt || off >= offdata->offtab[offdata->pos])
  			return CL_CLEAN;
  		    i += offdata->offtab[offdata->pos] - off;
@@@ -337,7 -342,7 +337,7 @@@
  		if(found && p->length + p->prefix_length == j) {
  		    if(!offdata && (p->offset_min != CLI_OFF_ANY)) {
  			if(p->offdata[0] != CLI_OFF_ABSOLUTE) {
 -			    ret = cli_caloff(NULL, &info, fd, root->type, p->offdata, &off_min, &off_max);
 +			    ret = cli_caloff(NULL, &info, map, root->type, p->offdata, &off_min, &off_max);
  			    if(ret != CL_SUCCESS) {
  				cli_errmsg("cli_bm_scanbuff: Can't calculate relative offset in signature for %s\n", p->virname);
  				if(info.exeinfo.section)
@@@ -367,7 -372,7 +367,7 @@@
  
  	if(offdata) {
  	    off = offset + i - BM_MIN_LENGTH + BM_BLOCK_SIZE;
- 	    for(; off >= offdata->offtab[offdata->pos] && offdata->pos < offdata->cnt; offdata->pos++);
+ 	    for(; offdata->pos < offdata->cnt && off >= offdata->offtab[offdata->pos]; offdata->pos++);
  	    if(offdata->pos == offdata->cnt || off >= offdata->offtab[offdata->pos])
  		return CL_CLEAN;
  	    i += offdata->offtab[offdata->pos] - off;
diff --combined libclamav/matcher.h
index 863a781,0385838..ff3d9e9
--- a/libclamav/matcher.h
+++ b/libclamav/matcher.h
@@@ -33,13 -33,13 +33,13 @@@
  #include "matcher-ac.h"
  #include "matcher-bm.h"
  #include "hashtab.h"
 -
 +#include "fmap.h"
  #include "mpool.h"
  
  #define CLI_MATCH_WILDCARD	0xff00
  #define CLI_MATCH_CHAR		0x0000
  #define CLI_MATCH_IGNORE	0x0100
- #define CLI_MATCH_ALTERNATIVE	0x0200
+ #define CLI_MATCH_SPECIAL	0x0200
  #define CLI_MATCH_NIBBLE_HIGH	0x0300
  #define CLI_MATCH_NIBBLE_LOW	0x0400
  
@@@ -140,9 -140,8 +140,9 @@@ struct cli_target_info 
  int cli_scanbuff(const unsigned char *buffer, uint32_t length, uint32_t offset, cli_ctx *ctx, cli_file_t ftype, struct cli_ac_data **acdata);
  
  int cli_scandesc(int desc, cli_ctx *ctx, cli_file_t ftype, uint8_t ftonly, struct cli_matched_type **ftoffset, unsigned int acmode);
 +int cli_fmap_scandesc(cli_ctx *ctx, cli_file_t ftype, uint8_t ftonly, struct cli_matched_type **ftoffset, unsigned int acmode);
  
 -int cli_caloff(const char *offstr, struct cli_target_info *info, int fd, unsigned int target, uint32_t *offdata, uint32_t *offset_min, uint32_t *offset_max);
 +int cli_caloff(const char *offstr, struct cli_target_info *info, struct F_MAP *map, unsigned int target, uint32_t *offdata, uint32_t *offset_min, uint32_t *offset_max);
  
  int cli_checkfp(int fd, cli_ctx *ctx);
  
diff --combined libclamav/others.h
index f4e9e3b,9f318dc..8ce23aa
--- a/libclamav/others.h
+++ b/libclamav/others.h
@@@ -38,7 -38,6 +38,7 @@@
  #include "clamav.h"
  #include "dconf.h"
  #include "filetypes.h"
 +#include "fmap.h"
  #include "libclamunrar_iface/unrar_iface.h"
  #include "regex/regex.h"
  
@@@ -110,7 -109,6 +110,7 @@@ typedef struct 
      unsigned int found_possibly_unwanted;
      cli_file_t container_type; /* FIXME: to be made into a stack or array - see bb#1579 & bb#1293 */
      struct cli_dconf *dconf;
 +    struct F_MAP **fmap;
  } cli_ctx;
  
  struct cl_engine {
@@@ -413,6 -411,7 +413,7 @@@ int cli_checklimits(const char *, cli_c
  int cli_updatelimits(cli_ctx *, unsigned long);
  unsigned long cli_getsizelimit(cli_ctx *, unsigned long);
  int cli_matchregex(const char *str, const char *regex);
+ void cli_qsort(void *basep, size_t nelems, size_t size, int (*comp)(const void *, const void *));
  
  /* symlink behaviour */
  #define CLI_FTW_FOLLOW_FILE_SYMLINK 0x01
diff --combined libclamav/scanners.c
index 463ad10,2eab5f6..22c1158
--- a/libclamav/scanners.c
+++ b/libclamav/scanners.c
@@@ -22,7 -22,6 +22,7 @@@
  #include "clamav-config.h"
  #endif
  
 +#include <sys/time.h>
  #include <stdio.h>
  #include <string.h>
  #include <stdlib.h>
@@@ -41,6 -40,14 +41,6 @@@
  #include <netinet/in.h>
  #endif
  
 -#if HAVE_MMAP
 -#if HAVE_SYS_MMAN_H
 -#include <sys/mman.h>
 -#else /* HAVE_SYS_MMAN_H */
 -#undef HAVE_MMAP
 -#endif
 -#endif
 -
  #ifndef	O_BINARY
  #define	O_BINARY	0
  #endif
@@@ -89,7 -96,6 +89,7 @@@
  #include "macho.h"
  #include "ishield.h"
  #include "7z.h"
 +#include "fmap.h"
  
  #ifdef HAVE_BZLIB_H
  #include <bzlib.h>
@@@ -432,67 -438,77 +432,67 @@@ static int cli_scanarj(int desc, cli_ct
      return ret;
  }
  
 -static int cli_scangzip(int desc, cli_ctx *ctx)
 +static int cli_scangzip(cli_ctx *ctx)
  {
 -	int fd, bytes, ret = CL_CLEAN;
 -	unsigned long int size = 0;
 -	char *buff;
 +	int fd, ret = CL_CLEAN;
 +	unsigned char buff[FILEBUFF];
  	char *tmpname;
 -	gzFile gd;
 -
 -
 +	z_stream z;
 +	size_t at = 0;
 +	struct F_MAP *map = *ctx->fmap;
 + 	
      cli_dbgmsg("in cli_scangzip()\n");
  
 -    if((gd = gzdopen(dup(desc), "rb")) == NULL) {
 -	cli_dbgmsg("GZip: Can't open descriptor %d\n", desc);
 -	return CL_EOPEN;
 +    memset(&z, 0, sizeof(z));
 +    if(inflateInit2(&z, MAX_WBITS + 16) != Z_OK) {
 +	cli_dbgmsg("GZip: InflateInit failed\n");
 +	return CL_CLEAN;
      }
  
 -    if((ret = cli_gentempfd(ctx->engine->tmpdir, &tmpname, &fd))) {
 +    if((ret = cli_gentempfd(ctx->engine->tmpdir, &tmpname, &fd)) != CL_SUCCESS) {
  	cli_dbgmsg("GZip: Can't generate temporary file.\n");
 -	gzclose(gd);
 +	inflateEnd(&z);
  	return ret;
      }
  
 -    if(!(buff = (char *) cli_malloc(FILEBUFF))) {
 -	cli_dbgmsg("GZip: Unable to malloc %u bytes.\n", FILEBUFF);
 -	gzclose(gd);
 -	close(fd);
 -	if(!ctx->engine->keeptmp) {
 -	    if(cli_unlink(tmpname)) {
 -	    	free(tmpname);
 +    while (at < map->len) {
 +	unsigned int bytes = MIN(map->len - at, map->pgsz);
 +	if(!(z.next_in = fmap_need_off_once(map, at, bytes))) {
 +	    cli_dbgmsg("GZip: Can't read %u bytes @ %lu.\n", bytes, (long unsigned)at);
 +	    inflateEnd(&z);
 +	    close(fd);
 +	    if (cli_unlink(tmpname)) {
 +		free(tmpname);
  		return CL_EUNLINK;
  	    }
 +	    free(tmpname);
 +	    return CL_EREAD;
  	}
 -	return CL_EMEM;
 -    }
 -
 -    while((bytes = gzread(gd, buff, FILEBUFF)) > 0) {
 -	size += bytes;
 -
 -	if(cli_checklimits("GZip", ctx, size + FILEBUFF, 0, 0)!=CL_CLEAN)
 -	    break;
 -
 -	if(cli_writen(fd, buff, bytes) != bytes) {
 -	    cli_dbgmsg("GZip: Can't write to file.\n");
 -	    close(fd);
 -	    if(!ctx->engine->keeptmp) {
 -	    	if (cli_unlink(tmpname)) {
 +	at += bytes;
 +	z.avail_in = bytes;
 +	do {
 +	    int inf;
 +	    z.avail_out = sizeof(buff);
 +            z.next_out = buff;
 +	    inf = inflate(&z, Z_NO_FLUSH);
 +	    if(inf != Z_OK && inf != Z_STREAM_END && inf != Z_BUF_ERROR) {
 +		cli_dbgmsg("GZip: Bad stream.\n");
 +		at = map->len;
 +		break;
 +	    }
 +	    if(cli_writen(fd, buff, sizeof(buff) - z.avail_out) < 0) {
 +		inflateEnd(&z);	    
 +		close(fd);
 +		if (cli_unlink(tmpname)) {
  		    free(tmpname);
  		    return CL_EUNLINK;
  		}
  	    }
 -	    free(tmpname);	
 -	    gzclose(gd);
 -	    free(buff);
 -	    return CL_EWRITE;
 -	}
 +	} while (z.avail_out == 0);
      }
  
 -    free(buff);
 -    gzclose(gd);
 -
 -    if(ret == CL_VIRUS) {
 -	close(fd);
 -	if(!ctx->engine->keeptmp)
 -	    if (cli_unlink(tmpname)) ret = CL_EUNLINK;
 -	free(tmpname);	
 -	return ret;
 -    }
 +    inflateEnd(&z);	    
  
 -    lseek(fd, 0, SEEK_SET);
      if((ret = cli_magic_scandesc(fd, ctx)) == CL_VIRUS ) {
  	cli_dbgmsg("GZip: Infected with %s\n", *ctx->virname);
  	close(fd);
@@@ -502,13 -518,14 +502,13 @@@
  		return CL_EUNLINK;
  	    }
  	}
 -	free(tmpname);	
 +	free(tmpname);
  	return CL_VIRUS;
      }
      close(fd);
      if(!ctx->engine->keeptmp)
  	if (cli_unlink(tmpname)) ret = CL_EUNLINK;
 -    free(tmpname);	
 -
 +    free(tmpname);
      return ret;
  }
  
@@@ -900,19 -917,24 +900,19 @@@ static int cli_vba_scandir(const char *
      return ret;
  }
  
 -static int cli_scanhtml(int desc, cli_ctx *ctx)
 +static int cli_scanhtml(cli_ctx *ctx)
  {
  	char *tempname, fullname[1024];
  	int ret=CL_CLEAN, fd;
 -	struct stat sb;
 +	struct F_MAP *map = *ctx->fmap;
  
      cli_dbgmsg("in cli_scanhtml()\n");
  
 -    if(fstat(desc, &sb) == -1) {
 -        cli_errmsg("cli_scanhtml: fstat() failed for descriptor %d\n", desc);
 -	return CL_ESTAT;
 -    }
 -
      /* Because HTML detection is FP-prone and html_normalise_fd() needs to
       * mmap the file don't normalise files larger than 10 MB.
       */
  
 -    if(sb.st_size > 10485760) {
 +    if(map->len > 10485760) {
  	cli_dbgmsg("cli_scanhtml: exiting (file larger than 10 MB)\n");
  	return CL_CLEAN;
      }
@@@ -928,7 -950,7 +928,7 @@@
  
      cli_dbgmsg("cli_scanhtml: using tempdir %s\n", tempname);
  
 -    html_normalise_fd(desc, tempname, NULL, ctx->dconf);
 +    html_normalise_map(map, tempname, NULL, ctx->dconf);
      snprintf(fullname, 1024, "%s/nocomment.html", tempname);
      fd = open(fullname, O_RDONLY|O_BINARY);
      if (fd >= 0) {
@@@ -936,7 -958,7 +936,7 @@@
  	    close(fd);
      }
  
 -    if(ret == CL_CLEAN && sb.st_size < 2097152) {
 +    if(ret == CL_CLEAN && map->len < 2097152) {
  	    /* limit to 2 MB, we're not interesting in scanning large files in notags form */
  	    /* TODO: don't even create notags if file is over 2 MB */
  	    snprintf(fullname, 1024, "%s/notags.html", tempname);
@@@ -972,25 -994,30 +972,25 @@@
      return ret;
  }
  
 -static int cli_scanscript(int desc, cli_ctx *ctx)
 +static int cli_scanscript(cli_ctx *ctx)
  {
 -	unsigned char buff[FILEBUFF];
 +	unsigned char *buff;
  	unsigned char* normalized;
  	struct text_norm_state state;
  	struct stat sb;
  	char *tmpname = NULL;
  	int ofd = -1, ret;
 -	ssize_t nread;
  	const struct cli_matcher *troot = ctx->engine->root[7];
  	uint32_t maxpatlen = troot ? troot->maxpatlen : 0, offset = 0;
  	struct cli_matcher *groot = ctx->engine->root[0];
  	struct cli_ac_data gmdata, tmdata;
  	struct cli_ac_data *mdata[2];
 +	struct F_MAP *map = *ctx->fmap;
 +	size_t at = 0;
  
  	cli_dbgmsg("in cli_scanscript()\n");
  
 -	if(fstat(desc, &sb) == -1) {
 -		cli_errmsg("cli_scanscript: fstat() failed for descriptor %d\n", desc);
 -		return CL_ESTAT;
 -	}
 -
 -	/* don't normalize files that are too large */
 -	if(sb.st_size > 5242880) {
 +	if(map->len > 5242880) {
  		cli_dbgmsg("cli_scanscript: exiting (file larger than 5 MB)\n");
  		return CL_CLEAN;
  	}
@@@ -1022,37 -1049,37 +1022,37 @@@
  	mdata[0] = &tmdata;
  	mdata[1] = &gmdata;
  
 -	do {
 -		nread = cli_readn(desc, buff, sizeof(buff));
 -		if(nread <= 0 || state.out_pos + nread > state.out_len) {
 -			/* flush if error/EOF, or too little buffer space left */
 -			if((ofd != -1) && (write(ofd, state.out, state.out_pos) == -1)) {
 -				cli_errmsg("cli_scanscript: can't write to file %s\n",tmpname);
 -				close(ofd);
 -				ofd = -1;
 -				/* we can continue to scan in memory */
 -			}
 -			/* when we flush the buffer also scan */
 -			if(cli_scanbuff(state.out, state.out_pos, offset, ctx, CL_TYPE_TEXT_ASCII, mdata) == CL_VIRUS) {
 -				ret = CL_VIRUS;
 -				break;
 -			}
 -			if(ctx->scanned)
 -			    *ctx->scanned += state.out_pos / CL_COUNT_PRECISION;
 -			offset += state.out_pos;
 -			/* carry over maxpatlen from previous buffer */
 -			if (state.out_pos > maxpatlen)
 -				memmove(state.out, state.out + state.out_pos - maxpatlen, maxpatlen); 
 -			text_normalize_reset(&state);
 -			state.out_pos = maxpatlen;
 +	while(1) {
 +	    size_t len = MIN(map->pgsz, map->len - at);
 +	    buff = fmap_need_off_once(map, at, len);
 +	    at += len;
 +	    if(!buff || !len || state.out_pos + len > state.out_len) {
 +		/* flush if error/EOF, or too little buffer space left */
 +		if((ofd != -1) && (write(ofd, state.out, state.out_pos) == -1)) {
 +		    cli_errmsg("cli_scanscript: can't write to file %s\n",tmpname);
 +		    close(ofd);
 +		    ofd = -1;
 +		    /* we can continue to scan in memory */
  		}
 -		if(nread > 0 && (text_normalize_buffer(&state, buff, nread) != nread)) {
 -			cli_dbgmsg("cli_scanscript: short read during normalizing\n");
 +		/* when we flush the buffer also scan */
 +		if(cli_scanbuff(state.out, state.out_pos, offset, ctx, CL_TYPE_TEXT_ASCII, mdata) == CL_VIRUS) {
 +		    ret = CL_VIRUS;
 +		    break;
  		}
 -		/* used a do {}while() here, since we need to flush our buffers at the end,
 -		 * and using while(){} loop would mean code duplication */
 -	} while (nread > 0);
 -
 +		if(ctx->scanned)
 +		    *ctx->scanned += state.out_pos / CL_COUNT_PRECISION;
 +		offset += state.out_pos;
 +		/* carry over maxpatlen from previous buffer */
 +		if (state.out_pos > maxpatlen)
 +		    memmove(state.out, state.out + state.out_pos - maxpatlen, maxpatlen); 
 +		text_normalize_reset(&state);
 +		state.out_pos = maxpatlen;
 +	    }
 +	    if(!len) break;
 +	    if(text_normalize_buffer(&state, buff, len) != len) {
 +		cli_dbgmsg("cli_scanscript: short read during normalizing\n");
 +	    }
 +	}
  	cli_ac_freedata(&tmdata);
  	cli_ac_freedata(&gmdata);
  	if(ctx->engine->keeptmp) {
@@@ -1064,12 -1091,11 +1064,12 @@@
  	return ret;
  }
  
 -static int cli_scanhtml_utf16(int desc, cli_ctx *ctx)
 +static int cli_scanhtml_utf16(cli_ctx *ctx)
  {
 -	char *tempname, buff[512], *decoded;
 +	char *tempname, *decoded, *buff;
  	int ret = CL_CLEAN, fd, bytes;
 -
 +	size_t at = 0;
 +	struct F_MAP *map = *ctx->fmap;
  
      cli_dbgmsg("in cli_scanhtml_utf16()\n");
  
@@@ -1084,15 -1110,7 +1084,15 @@@
  
      cli_dbgmsg("cli_scanhtml_utf16: using tempfile %s\n", tempname);
  
 -    while((bytes = read(desc, buff, sizeof(buff))) > 0) {
 +    while(at < map->len) {
 +	bytes = MIN(map->len - at, map->pgsz * 16);
 +	if(!(buff = fmap_need_off_once(map, at, bytes))) {
 +	    close(fd);
 +	    cli_unlink(tempname);
 +	    free(tempname);
 +	    return CL_EREAD;
 +	}
 +	at += bytes;
  	decoded = cli_utf16toascii(buff, bytes);
  	if(decoded) {
  	    if(write(fd, decoded, strlen(decoded)) == -1) {
@@@ -1107,14 -1125,8 +1107,14 @@@
  	}
      }
  
 -    lseek(fd, 0, SEEK_SET);
 -    ret = cli_scanhtml(fd, ctx);
 +    *ctx->fmap = fmap(fd, 0, 0);
 +    if(*ctx->fmap) {
 +	ret = cli_scanhtml(ctx);
 +	fmunmap(*ctx->fmap);
 +    } else
 +	cli_errmsg("cli_scanhtml_utf16: fmap of %s failed\n", tempname);
 +
 +    *ctx->fmap = map;
      close(fd);
  
      if(!ctx->engine->keeptmp) {
@@@ -1126,7 -1138,7 +1126,7 @@@
      return ret;
  }
  
 -static int cli_scanole2(int desc, cli_ctx *ctx)
 +static int cli_scanole2(cli_ctx *ctx)
  {
  	char *dir;
  	int ret = CL_CLEAN;
@@@ -1147,7 -1159,7 +1147,7 @@@
  	return CL_ETMPDIR;
      }
  
 -    ret = cli_ole2_extract(desc, dir, ctx, &vba);
 +    ret = cli_ole2_extract(dir, ctx, &vba);
      if(ret!=CL_CLEAN && ret!=CL_VIRUS) {
  	cli_dbgmsg("OLE2: %s\n", cl_strerror(ret));
  	if(!ctx->engine->keeptmp)
@@@ -1200,7 -1212,7 +1200,7 @@@ static int cli_scantar(int desc, cli_ct
      return ret;
  }
  
 -static int cli_scanbinhex(int desc, cli_ctx *ctx)
 +static int cli_scanbinhex(cli_ctx *ctx)
  {
  	char *dir;
  	int ret = CL_CLEAN;
@@@ -1218,7 -1230,7 +1218,7 @@@
  	return CL_ETMPDIR;
      }
  
 -    if((ret = cli_binhex(dir, desc)))
 +    if((ret = cli_binhex(dir, *ctx->fmap)))
  	cli_dbgmsg("Binhex: %s\n", cl_strerror(ret));
      else
  	ret = cli_scandir(dir, ctx);
@@@ -1258,11 -1270,11 +1258,11 @@@ static int cli_scanmschm(int desc, cli_
      }
  
     do {
 -	ret = cli_chm_prepare_file(desc, dir, &metadata);
 +	ret = cli_chm_prepare_file(&metadata);
  	if (ret != CL_SUCCESS) {
  	   break;
  	}
 -	ret = cli_chm_extract_file(desc, dir, &metadata, ctx);
 +	ret = cli_chm_extract_file(dir, &metadata, ctx);
  	if (ret == CL_SUCCESS) {
  	    lseek(metadata.ofd, 0, SEEK_SET);
  	    rc = cli_magic_scandesc(metadata.ofd, ctx);
@@@ -1429,7 -1441,7 +1429,7 @@@ static int cli_scancryptff(int desc, cl
      return ret;
  }
  
 -static int cli_scanpdf(int desc, cli_ctx *ctx, off_t offset)
 +static int cli_scanpdf(cli_ctx *ctx, off_t offset)
  {
  	int ret;
  	char *dir = cli_gentemp(ctx->engine->tmpdir);
@@@ -1443,7 -1455,7 +1443,7 @@@
  	return CL_ETMPDIR;
      }
  
 -    ret = cli_pdf(dir, desc, ctx, offset);
 +    ret = cli_pdf(dir, ctx, offset);
  
      if(!ctx->engine->keeptmp)
  	cli_rmdirs(dir);
@@@ -1478,7 -1490,7 +1478,7 @@@ static int cli_scantnef(int desc, cli_c
      return ret;
  }
  
 -static int cli_scanuuencoded(int desc, cli_ctx *ctx)
 +static int cli_scanuuencoded(cli_ctx *ctx)
  {
  	int ret;
  	char *dir = cli_gentemp(ctx->engine->tmpdir);
@@@ -1492,7 -1504,7 +1492,7 @@@
  	return CL_ETMPDIR;
      }
  
 -    ret = cli_uuencode(dir, desc);
 +    ret = cli_uuencode(dir, *ctx->fmap);
  
      if(ret == CL_CLEAN)
  	ret = cli_scandir(dir, ctx);
@@@ -1678,14 -1690,14 +1678,14 @@@ static int cli_scanembpe(int desc, cli_
      return CL_CLEAN;
  }
  
 -static int cli_scanraw(int desc, cli_ctx *ctx, cli_file_t type, uint8_t typercg, cli_file_t *dettype)
 +static int cli_scanraw(cli_ctx *ctx, cli_file_t type, uint8_t typercg, cli_file_t *dettype)
  {
  	int ret = CL_CLEAN, nret = CL_CLEAN;
  	struct cli_matched_type *ftoffset = NULL, *fpt;
  	uint32_t lastzip, lastrar;
  	struct cli_exe_info peinfo;
  	unsigned int acmode = AC_SCAN_VIR, break_loop = 0;
 -	struct stat sb;
 +	struct F_MAP *map = *ctx->fmap;
  
  
      if(ctx->engine->maxreclevel && ctx->recursion >= ctx->engine->maxreclevel)
@@@ -1694,7 -1706,12 +1694,7 @@@
      if(typercg)
  	acmode |= AC_SCAN_FT;
  
 -    if(lseek(desc, 0, SEEK_SET) < 0) {
 -	cli_errmsg("cli_scanraw: lseek() failed\n");
 -	return CL_ESEEK;
 -    }
 -
 -    ret = cli_scandesc(desc, ctx, type == CL_TYPE_TEXT_ASCII ? 0 : type, 0, &ftoffset, acmode);
 +    ret = cli_fmap_scandesc(ctx, type == CL_TYPE_TEXT_ASCII ? 0 : type, 0, &ftoffset, acmode);
  
      if(ret >= CL_TYPENO) {
  	ctx->recursion++;
@@@ -1718,72 -1735,73 +1718,72 @@@
  			    cli_dbgmsg("RAR/RAR-SFX signature found at %u\n", (unsigned int) fpt->offset);
  			if(type != CL_TYPE_RAR && have_rar && SCAN_ARCHIVE && fpt->offset < 102400 && (DCONF_ARCH & ARCH_CONF_RAR)) {
  			    cli_dbgmsg("RAR/RAR-SFX signature found at %u\n", (unsigned int) fpt->offset);
 -			    nret = cli_scanrar(desc, ctx, fpt->offset, &lastrar);
 +			    nret = cli_scanrar(map->fd, ctx, fpt->offset, &lastrar);
  			}
  			break;
  
  		    case CL_TYPE_ZIPSFX:
  			if(type != CL_TYPE_ZIP && SCAN_ARCHIVE && fpt->offset < 102400 && (DCONF_ARCH & ARCH_CONF_ZIP)) {
  			    cli_dbgmsg("ZIP/ZIP-SFX signature found at %u\n", (unsigned int) fpt->offset);
 -			    nret = cli_unzip_single(desc, ctx, fpt->offset);
 +			    nret = cli_unzip_single(ctx, fpt->offset);
  			}
  			break;
  
  		    case CL_TYPE_CABSFX:
  			if(type != CL_TYPE_MSCAB && SCAN_ARCHIVE && fpt->offset < 102400 && (DCONF_ARCH & ARCH_CONF_CAB)) {
  			    cli_dbgmsg("CAB/CAB-SFX signature found at %u\n", (unsigned int) fpt->offset);
 -			    nret = cli_scanmscab(desc, ctx, fpt->offset);
 +			    nret = cli_scanmscab(map->fd, ctx, fpt->offset);
  			}
  			break;
  		    case CL_TYPE_ARJSFX:
  			if(type != CL_TYPE_ARJ && SCAN_ARCHIVE && fpt->offset < 102400 && (DCONF_ARCH & ARCH_CONF_ARJ)) {
  			    cli_dbgmsg("ARJ-SFX signature found at %u\n", (unsigned int) fpt->offset);
 -			    nret = cli_scanarj(desc, ctx, fpt->offset, &lastrar);
 +			    nret = cli_scanarj(map->fd, ctx, fpt->offset, &lastrar);
  			}
  			break;
  
  		    case CL_TYPE_NULSFT:
  		        if(SCAN_ARCHIVE && type == CL_TYPE_MSEXE && (DCONF_ARCH & ARCH_CONF_NSIS) && fpt->offset > 4) {
  			    cli_dbgmsg("NSIS signature found at %u\n", (unsigned int) fpt->offset-4);
 -			    nret = cli_scannulsft(desc, ctx, fpt->offset - 4);
 +			    nret = cli_scannulsft(map->fd, ctx, fpt->offset - 4);
  			}
  			break;
  
  		    case CL_TYPE_AUTOIT:
  		        if(SCAN_ARCHIVE && type == CL_TYPE_MSEXE && (DCONF_ARCH & ARCH_CONF_AUTOIT)) {
  			    cli_dbgmsg("AUTOIT signature found at %u\n", (unsigned int) fpt->offset);
 -			    nret = cli_scanautoit(desc, ctx, fpt->offset + 23);
 +			    nret = cli_scanautoit(map->fd, ctx, fpt->offset + 23);
  			}
  			break;
  
  		    case CL_TYPE_ISHIELD_MSI:
  		        if(SCAN_ARCHIVE && type == CL_TYPE_MSEXE && (DCONF_ARCH & ARCH_CONF_ISHIELD)) {
  			    cli_dbgmsg("ISHIELD-MSI signature found at %u\n", (unsigned int) fpt->offset);
 -			    nret = cli_scanishield_msi(desc, ctx, fpt->offset + 14);
 +			    nret = cli_scanishield_msi(ctx, fpt->offset + 14);
  			}
  			break;
  
  		    case CL_TYPE_PDF:
  			if(type != CL_TYPE_PDF && SCAN_PDF && (DCONF_DOC & DOC_CONF_PDF)) {
  			    cli_dbgmsg("PDF signature found at %u\n", (unsigned int) fpt->offset);
 -			    nret = cli_scanpdf(desc, ctx, fpt->offset);
 +			    nret = cli_scanpdf(ctx, fpt->offset);
  			}
  			break;
  
  		    case CL_TYPE_MSEXE:
   			if(SCAN_PE && (type == CL_TYPE_MSEXE || type == CL_TYPE_ZIP || type == CL_TYPE_MSOLE2) && ctx->dconf->pe) {
 -			    fstat(desc, &sb);
 -			    if(sb.st_size > 10485760)
 +			    if(map->len > 10485760)
  				break;
  			    memset(&peinfo, 0, sizeof(struct cli_exe_info));
  			    peinfo.offset = fpt->offset;
 -			    lseek(desc, fpt->offset, SEEK_SET);
 -			    if(cli_peheader(desc, &peinfo) == 0) {
 +			    lseek(map->fd, fpt->offset, SEEK_SET);
 +			    if(cli_peheader(map, &peinfo) == 0) {
  				cli_dbgmsg("*** Detected embedded PE file at %u ***\n", (unsigned int) fpt->offset);
  				if(peinfo.section)
  				    free(peinfo.section);
  
 -				lseek(desc, fpt->offset, SEEK_SET);
 -				nret = cli_scanembpe(desc, ctx);
 +				lseek(map->fd, fpt->offset, SEEK_SET);
 +				nret = cli_scanembpe(map->fd, ctx);
  				break_loop = 1; /* we can stop here and other
  						 * embedded executables will
  						 * be found recursively
@@@ -1808,13 -1826,13 +1808,13 @@@
  	    case CL_TYPE_HTML:
  		if(SCAN_HTML && type == CL_TYPE_TEXT_ASCII && (DCONF_DOC & DOC_CONF_HTML)) {
  		    *dettype = CL_TYPE_HTML;
 -		    nret = cli_scanhtml(desc, ctx);
 +		    nret = cli_scanhtml(ctx);
  		}
  		break;
  
  	    case CL_TYPE_MAIL:
  		if(SCAN_MAIL && type == CL_TYPE_TEXT_ASCII && (DCONF_MAIL & MAIL_CONF_MBOX))
 -		    nret = cli_scanmail(desc, ctx);
 +		    nret = cli_scanmail(map->fd, ctx);
  		break;
  
  	    default:
@@@ -1831,7 -1849,7 +1831,7 @@@
      }
  
      if(ret == CL_VIRUS)
 -	cli_dbgmsg("%s found in descriptor %d\n", *ctx->virname, desc);
 +	cli_dbgmsg("%s found in descriptor %d\n", *ctx->virname, map->fd);
  
      return ret;
  }
@@@ -1872,40 -1890,28 +1872,40 @@@ int cli_magic_scandesc(int desc, cli_ct
      if(cli_updatelimits(ctx, sb.st_size)!=CL_CLEAN)
          return CL_CLEAN;
  
 +    ctx->fmap++;
 +    if(!(*ctx->fmap = fmap(desc, 0, sb.st_size))) {
 +	cli_errmsg("CRITICAL: fmap() failed\n");
 +	return CL_EMEM;
 +    }
 +
      if(!ctx->options || (ctx->recursion == ctx->engine->maxreclevel)) { /* raw mode (stdin, etc.) or last level of recursion */
  	if(ctx->recursion == ctx->engine->maxreclevel)
  	    cli_dbgmsg("cli_magic_scandesc: Hit recursion limit, only scanning raw file\n");
  	else
  	    cli_dbgmsg("Raw mode: No support for special files\n");
 -	if((ret = cli_scandesc(desc, ctx, 0, 0, NULL, AC_SCAN_VIR)) == CL_VIRUS)
 +	if((ret = cli_fmap_scandesc(ctx, 0, 0, NULL, AC_SCAN_VIR)) == CL_VIRUS)
  	    cli_dbgmsg("%s found in descriptor %d\n", *ctx->virname, desc);
 +	fmunmap(*ctx->fmap);
 +	ctx->fmap--; 
  	return ret;
      }
  
 -    lseek(desc, 0, SEEK_SET);
 -    type = cli_filetype2(desc, ctx->engine);
 +    type = cli_filetype2(*ctx->fmap, ctx->engine); /* FIXMEFMAP: port to fmap */
      if(type == CL_TYPE_ERROR) {
  	cli_dbgmsg("cli_magic_scandesc: cli_filetype2 returned CL_TYPE_ERROR\n");
 +	fmunmap(*ctx->fmap);
 +	ctx->fmap--; 
  	return CL_EREAD;
      }
 -    lseek(desc, 0, SEEK_SET);
 +    lseek(desc, 0, SEEK_SET); /* FIXMEFMAP: remove ? */
  
      if(type != CL_TYPE_IGNORED && ctx->engine->sdb) {
 -	if((ret = cli_scanraw(desc, ctx, type, 0, &dettype)) == CL_VIRUS)
 +	if((ret = cli_scanraw(ctx, type, 0, &dettype)) == CL_VIRUS) {
 +	    fmunmap(*ctx->fmap);
 +	    ctx->fmap--; 
  	    return CL_VIRUS;
 -	lseek(desc, 0, SEEK_SET);
 +	}
 +	lseek(desc, 0, SEEK_SET); /* FIXMEFMAP: remove ? */
      }
  
      ctx->container_type = 0;
@@@ -1921,12 -1927,12 +1921,12 @@@
  
  	case CL_TYPE_ZIP:
  	    if(SCAN_ARCHIVE && (DCONF_ARCH & ARCH_CONF_ZIP))
 -		ret = cli_unzip(desc, ctx);
 +		ret = cli_unzip(ctx);
  	    break;
  
  	case CL_TYPE_GZ:
  	    if(SCAN_ARCHIVE && (DCONF_ARCH & ARCH_CONF_GZ))
 -		ret = cli_scangzip(desc, ctx);
 +		ret = cli_scangzip(ctx);
  	    break;
  
  	case CL_TYPE_BZ:
@@@ -1960,17 -1966,17 +1960,17 @@@
  
  	case CL_TYPE_HTML:
  	    if(SCAN_HTML && (DCONF_DOC & DOC_CONF_HTML))
 -		ret = cli_scanhtml(desc, ctx);
 +		ret = cli_scanhtml(ctx);
  	    break;
  
  	case CL_TYPE_HTML_UTF16:
  	    if(SCAN_HTML && (DCONF_DOC & DOC_CONF_HTML))
 -		ret = cli_scanhtml_utf16(desc, ctx);
 +		ret = cli_scanhtml_utf16(ctx);
  	    break;
  
  	case CL_TYPE_SCRIPT:
  	    if((DCONF_DOC & DOC_CONF_SCRIPT) && dettype != CL_TYPE_HTML)
 -	        ret = cli_scanscript(desc, ctx);
 +	        ret = cli_scanscript(ctx);
  	    break;
  
  	case CL_TYPE_RTF:
@@@ -1990,7 -1996,7 +1990,7 @@@
  
  	case CL_TYPE_UUENCODED:
  	    if(DCONF_OTHER & OTHER_CONF_UUENC)
 -		ret = cli_scanuuencoded(desc, ctx);
 +		ret = cli_scanuuencoded(ctx);
  	    break;
  
  	case CL_TYPE_MSCHM:
@@@ -2000,7 -2006,7 +2000,7 @@@
  
  	case CL_TYPE_MSOLE2:
  	    if(SCAN_OLE2 && (DCONF_ARCH & ARCH_CONF_OLE2))
 -		ret = cli_scanole2(desc, ctx);
 +		ret = cli_scanole2(ctx);
  	    break;
  
  	case CL_TYPE_7Z:
@@@ -2040,7 -2046,7 +2040,7 @@@
  
  	case CL_TYPE_BINHEX:
  	    if(SCAN_ARCHIVE && (DCONF_ARCH & ARCH_CONF_BINHEX))
 -		ret = cli_scanbinhex(desc, ctx);
 +		ret = cli_scanbinhex(ctx);
  	    break;
  
  	case CL_TYPE_SCRENC:
@@@ -2060,7 -2066,7 +2060,7 @@@
  
          case CL_TYPE_PDF: /* FIXMELIMITS: pdf should be an archive! */
  	    if(SCAN_PDF && (DCONF_DOC & DOC_CONF_PDF))
 -		ret = cli_scanpdf(desc, ctx, 0);
 +		ret = cli_scanpdf(ctx, 0);
  	    break;
  
  	case CL_TYPE_CRYPTFF:
@@@ -2070,7 -2076,7 +2070,7 @@@
  
  	case CL_TYPE_ELF:
  	    if(SCAN_ELF && ctx->dconf->elf)
 -		ret = cli_scanelf(desc, ctx);
 +		ret = cli_scanelf(ctx);
  	    break;
  
  	case CL_TYPE_MACHO:
@@@ -2107,11 -2113,8 +2107,11 @@@
      ctx->recursion--;
      ctx->container_type = current_container;
  
 -    if(ret == CL_VIRUS)
 +    if(ret == CL_VIRUS) {
 +	fmunmap(*ctx->fmap);
 +	ctx->fmap--; 
  	return CL_VIRUS;
 +    }
  
      if(type == CL_TYPE_ZIP && SCAN_ARCHIVE && (DCONF_ARCH & ARCH_CONF_ZIP)) {
  	if(sb.st_size > 1048576) {
@@@ -2122,11 -2125,8 +2122,11 @@@
  
      /* CL_TYPE_HTML: raw HTML files are not scanned, unless safety measure activated via DCONF */
      if(type != CL_TYPE_IGNORED && (type != CL_TYPE_HTML || !(DCONF_DOC & DOC_CONF_HTML_SKIPRAW)) && !ctx->engine->sdb) {
 -	if(cli_scanraw(desc, ctx, type, typercg, &dettype) == CL_VIRUS)
 +	if(cli_scanraw(ctx, type, typercg, &dettype) == CL_VIRUS) {
 +	    fmunmap(*ctx->fmap);
 +	    ctx->fmap--; 
  	    return CL_VIRUS;
 +	}
      }
  
      ctx->recursion++;
@@@ -2137,24 -2137,24 +2137,26 @@@
  	case CL_TYPE_TEXT_UTF16LE:
  	case CL_TYPE_TEXT_UTF8:
  	    if((DCONF_DOC & DOC_CONF_SCRIPT) && dettype != CL_TYPE_HTML)
 -	        ret = cli_scanscript(desc, ctx);
 +	        ret = cli_scanscript(ctx);
- 	    if(ret != CL_VIRUS && ctx->container_type == CL_TYPE_MAIL)
+ 	    if(ret != CL_VIRUS && ctx->container_type == CL_TYPE_MAIL) {
+ 		lseek(desc, 0, SEEK_SET);
  		ret = cli_scandesc(desc, ctx, CL_TYPE_MAIL, 0, NULL, AC_SCAN_VIR);
+ 	    }
  	    break;
  	/* Due to performance reasons all executables were first scanned
  	 * in raw mode. Now we will try to unpack them
  	 */
  	case CL_TYPE_MSEXE:
  	    if(SCAN_PE && ctx->dconf->pe)
 -		ret = cli_scanpe(desc, ctx);
 +		ret = cli_scanpe(ctx);
  	    break;
  
  	default:
  	    break;
      }
      ctx->recursion--;
 +    fmunmap(*ctx->fmap);
 +    ctx->fmap--;
  
      switch(ret) {
  	case CL_EFORMAT:
@@@ -2181,15 -2181,8 +2183,15 @@@ int cl_scandesc(int desc, const char **
      ctx.found_possibly_unwanted = 0;
      ctx.container_type = 0;
      ctx.dconf = (struct cli_dconf *) engine->dconf;
 +    ctx.fmap = cli_calloc(sizeof(struct F_MAP *), ctx.engine->maxreclevel + 1);
 +    if(!ctx.fmap)
 +	return CL_EMEM;
 +    ctx.fmap--;
  
      rc = cli_magic_scandesc(desc, &ctx);
 +
 +    ctx.fmap++;
 +    free(ctx.fmap);
      if(rc == CL_CLEAN && ctx.found_possibly_unwanted)
      	rc = CL_VIRUS;
      return rc;
@@@ -2234,6 -2227,7 +2236,6 @@@ int cl_scanfile(const char *filename, c
  {
  	int fd, ret;
  
 -
      if((fd = open(filename, O_RDONLY|O_BINARY)) == -1)
  	return CL_EOPEN;
  
diff --combined libclamav/unzip.c
index c532f33,de17bf9..2f9697e
--- a/libclamav/unzip.c
+++ b/libclamav/unzip.c
@@@ -35,6 -35,12 +35,6 @@@
  #endif
  #include <stdlib.h>
  
 -#if HAVE_MMAP
 -#ifdef HAVE_SYS_MMAN_H
 -#include <sys/mman.h>
 -#endif
 -#endif /* HAVE_MMAP */
 -
  #include <stdio.h>
  
  #include <zlib.h>
@@@ -48,7 -54,6 +48,7 @@@
  #include "clamav.h"
  #include "scanners.h"
  #include "matcher.h"
 +#include "fmap.h"
  
  #define UNZIP_PRIVATE
  #include "unzip.h"
@@@ -62,6 -67,15 +62,6 @@@ static int wrap_inflateinit2(void *a, i
    return inflateInit2(a, b);
  }
  
 -static inline void destroy_map(void *map, size_t fsize) {
 -#if HAVE_MMAP
 -  munmap(map, fsize);
 -#else
 -  free(map);
 -#endif
 -}
 -
 -
  static int unz(uint8_t *src, uint32_t csize, uint32_t usize, uint16_t method, uint16_t flags, unsigned int *fu, cli_ctx *ctx, char *tmpd) {
    char name[1024], obuf[BUFSIZ];
    char *tempfile = name;
@@@ -92,7 -106,7 +92,7 @@@
      }
      if(res==1) {
        if(ctx->engine->maxfilesize && csize > ctx->engine->maxfilesize) {
- 	cli_dbgmsg("cli_unzip: trimming output size to maxfilesize (%lu)\n", ctx->engine->maxfilesize);
+ 	cli_dbgmsg("cli_unzip: trimming output size to maxfilesize (%lu)\n", (long unsigned int) ctx->engine->maxfilesize);
  	csize = ctx->engine->maxfilesize;
        }
        if(cli_writen(of, src, csize)!=(int)csize) ret = CL_EWRITE;
@@@ -153,7 -167,7 +153,7 @@@
        if(*avail_out!=sizeof(obuf)) {
  	written+=sizeof(obuf)-(*avail_out);
  	if(ctx->engine->maxfilesize && written > ctx->engine->maxfilesize) {
- 	  cli_dbgmsg("cli_unzip: trimming output size to maxfilesize (%lu)\n", ctx->engine->maxfilesize);
+ 	  cli_dbgmsg("cli_unzip: trimming output size to maxfilesize (%lu)\n", (long unsigned int) ctx->engine->maxfilesize);
  	  res = Z_STREAM_END;
  	  break;
  	}
@@@ -197,7 -211,7 +197,7 @@@
        if(strm.avail_out!=sizeof(obuf)) {
  	written+=sizeof(obuf)-strm.avail_out;
  	if(ctx->engine->maxfilesize && written > ctx->engine->maxfilesize) {
- 	  cli_dbgmsg("cli_unzip: trimming output size to maxfilesize (%lu)\n", ctx->engine->maxfilesize);
+ 	  cli_dbgmsg("cli_unzip: trimming output size to maxfilesize (%lu)\n", (unsigned long int) ctx->engine->maxfilesize);
  	  res = BZ_STREAM_END;
  	  break;
  	}
@@@ -234,7 -248,7 +234,7 @@@
        if(strm.avail_out!=sizeof(obuf)) {
  	written+=sizeof(obuf)-strm.avail_out;
  	if(ctx->engine->maxfilesize && written > ctx->engine->maxfilesize) {
- 	  cli_dbgmsg("cli_unzip: trimming output size to maxfilesize (%lu)\n", ctx->engine->maxfilesize);
+ 	  cli_dbgmsg("cli_unzip: trimming output size to maxfilesize (%lu)\n", (unsigned long int) ctx->engine->maxfilesize);
  	  res = 0;
  	  break;
  	}
@@@ -303,39 -317,33 +303,39 @@@
    return ret;
  }
  
 -static unsigned int lhdr(uint8_t *zip, uint32_t zsize, unsigned int *fu, unsigned int fc, uint8_t *ch, int *ret, cli_ctx *ctx, char *tmpd, int fd) {
 -  uint8_t *lh = zip;
 +static unsigned int lhdr(struct F_MAP *map, uint32_t loff,uint32_t zsize, unsigned int *fu, unsigned int fc, uint8_t *ch, int *ret, cli_ctx *ctx, char *tmpd) {
 +  uint8_t *lh, *zip;
    char name[256];
    uint32_t csize, usize;
    struct cli_meta_node *meta = ctx->engine->zip_mlist;
  
 -  if(zsize<=SIZEOF_LH) {
 -    cli_dbgmsg("cli_unzip: lh - out of file\n");
 -    return 0;
 +  if(!(lh = fmap_need_off(map, loff, SIZEOF_LH))) {
 +      cli_dbgmsg("cli_unzip: lh - out of file\n");
 +      return 0;
    }
    if(LH_magic != 0x04034b50) {
      if (!ch) cli_dbgmsg("cli_unzip: lh - wrkcomplete\n");
      else cli_dbgmsg("cli_unzip: lh - bad magic\n");
 +    fmap_unneed_off(map, loff, SIZEOF_LH);
      return 0;
    }
  
 -  zip+=SIZEOF_LH;
 +  zip = lh + SIZEOF_LH;
    zsize-=SIZEOF_LH;
  
    if(zsize<=LH_flen) {
      cli_dbgmsg("cli_unzip: lh - fname out of file\n");
 +    fmap_need_off(map, loff, SIZEOF_LH);
      return 0;
    }
    if(meta || cli_debug_flag) {
 -    uint32_t nsize = (LH_flen>=sizeof(name))?sizeof(name)-1:LH_flen;
 -    memcpy(name, zip, nsize);
 -    name[nsize]='\0';
 +      uint32_t nsize = (LH_flen>=sizeof(name))?sizeof(name)-1:LH_flen;
 +      char *src;
 +      if(nsize && (src = fmap_need_ptr_once(map, zip, nsize))) {
 +	  memcpy(name, zip, nsize);
 +	  name[nsize]='\0';
 +      } else
 +	  name[0] = '\0';
    }
    zip+=LH_flen;
    zsize-=LH_flen;
@@@ -356,20 -364,18 +356,20 @@@
  	 )
  	) meta = meta->next;
    if(meta) {
 -    if(!cli_checkfp(fd, ctx)) {
 +    if(!cli_checkfp(map->fd, ctx)) {
        *ctx->virname = meta->virname;
        *ret = CL_VIRUS;
      } else
        *ret = CL_CLEAN;
  
 +    fmap_need_off(map, loff, SIZEOF_LH);
      return 0;
    }
  
    if(LH_flags & F_MSKED) {
      cli_dbgmsg("cli_unzip: lh - header has got unusable masked data\n");
      /* FIXME: need to find/craft a sample */
 +    fmap_need_off(map, loff, SIZEOF_LH);
      return 0;
    }
  
@@@ -377,76 -383,63 +377,76 @@@
      cli_dbgmsg("cli_unzip: Encrypted files found in archive.\n");
      *ctx->virname = "Encrypted.Zip";
      *ret = CL_VIRUS;
 +    fmap_need_off(map, loff, SIZEOF_LH);
      return 0;
    }
   
    if(LH_flags & F_USEDD) {
      cli_dbgmsg("cli_unzip: lh - has data desc\n");
 -    if(!ch) return 0;
 +    if(!ch) {
 +	fmap_need_off(map, loff, SIZEOF_LH);
 +	return 0;
 +    }
      else { usize = CH_usize; csize = CH_csize; }
    } else { usize = LH_usize; csize = LH_csize; }
  
    if(zsize<=LH_elen) {
      cli_dbgmsg("cli_unzip: lh - extra out of file\n");
 +    fmap_need_off(map, loff, SIZEOF_LH);
      return 0;
    }
    zip+=LH_elen;
    zsize-=LH_elen;
  
    if (!csize) { /* FIXME: what's used for method0 files? csize or usize? Nothing in the specs, needs testing */
 -    cli_dbgmsg("cli_unzip: lh - skipping empty file\n");
 +      cli_dbgmsg("cli_unzip: lh - skipping empty file\n");
    } else {
 -    if(zsize<csize) {
 -      cli_dbgmsg("cli_unzip: lh - stream out of file\n");
 -      return 0;
 -    } 
 -    if(LH_flags & F_ENCR) {
 -      cli_dbgmsg("cli_unzip: lh - skipping encrypted file\n");
 -    } else *ret = unz(zip, csize, usize, LH_method, LH_flags, fu, ctx, tmpd);
 -    zip+=csize;
 -    zsize-=csize;
 +      if(zsize<csize) {
 +	  cli_dbgmsg("cli_unzip: lh - stream out of file\n");
 +	  fmap_need_off(map, loff, SIZEOF_LH);
 +	  return 0;
 +      }
 +      if(LH_flags & F_ENCR) {
 +	  cli_dbgmsg("cli_unzip: lh - skipping encrypted file\n");
 +      } else {
 +	  if(fmap_need_ptr_once(map, zip, csize))
 +	      *ret = unz(zip, csize, usize, LH_method, LH_flags, fu, ctx, tmpd);
 +      }
 +      zip+=csize;
 +      zsize-=csize;
    }
  
 +  fmap_need_off(map, loff, SIZEOF_LH); /* unneed now. block is guaranteed to exists till the next need */
    if(LH_flags & F_USEDD) {
 -    if(zsize<12) {
 -      cli_dbgmsg("cli_unzip: lh - data desc out of file\n");
 -      return 0;
 -    }
 -    zsize-=12;
 -    if(cli_readint32(zip)==0x08074b50) {
 -      if(zsize<4) {
 -	cli_dbgmsg("cli_unzip: lh - data desc out of file\n");
 -	return 0;
 +      if(zsize<12) {
 +	  cli_dbgmsg("cli_unzip: lh - data desc out of file\n");
 +	  return 0;
        }
 -      zip+=4;
 -    }
 -    zip+=12;
 +      zsize-=12;
 +      if(fmap_need_ptr_once(map, zip, 4)) {
 +	  if(cli_readint32(zip)==0x08074b50) {
 +	      if(zsize<4) {
 +		  cli_dbgmsg("cli_unzip: lh - data desc out of file\n");
 +		  return 0;
 +	      }
 +	      zip+=4;
 +	  }
 +      }
 +      zip+=12;
    }
    return zip-lh;
  }
  
  
 -static unsigned int chdr(uint8_t *zip, uint32_t coff, uint32_t zsize, unsigned int *fu, unsigned int fc, int *ret, cli_ctx *ctx, char *tmpd, int fd) {
 -  uint8_t *ch = &zip[coff];
 +static unsigned int chdr(struct F_MAP *map, uint32_t coff, uint32_t zsize, unsigned int *fu, unsigned int fc, int *ret, cli_ctx *ctx, char *tmpd) {
    char name[256];
    int last = 0;
 +  int8_t *ch;
  
 -  if(zsize-coff<=SIZEOF_CH || CH_magic != 0x02014b50) {
 -    cli_dbgmsg("cli_unzip: ch - wrkcomplete\n");
 -    return 0;
 +  if(!(ch = fmap_need_off(map, coff, SIZEOF_CH)) || CH_magic != 0x02014b50) {
 +      if(ch) fmap_unneed_ptr(map, ch, SIZEOF_CH);
 +      cli_dbgmsg("cli_unzip: ch - wrkcomplete\n");
 +      return 0;
    }
    coff+=SIZEOF_CH;
  
@@@ -457,13 -450,10 +457,13 @@@
      last=1;
    }
    if(cli_debug_flag && !last) {
 -    unsigned int size = (CH_flen>=sizeof(name))?sizeof(name)-1:CH_flen;
 -    memcpy(name, &zip[coff], size);
 -    name[size]='\0';
 -    cli_dbgmsg("cli_unzip: ch - fname: %s\n", name);
 +      unsigned int size = (CH_flen>=sizeof(name))?sizeof(name)-1:CH_flen;
 +      char *src = fmap_need_off_once(map, coff, size);
 +      if(src) {
 +	  memcpy(name, src, size);
 +	  name[size]='\0';
 +	  cli_dbgmsg("cli_unzip: ch - fname: %s\n", name);
 +      }
    }
    coff+=CH_flen;
  
@@@ -480,23 -470,27 +480,23 @@@
    coff+=CH_clen;
  
    if(CH_off<zsize-SIZEOF_LH) {
 -    lhdr(&zip[CH_off], zsize-CH_off, fu, fc, ch, ret, ctx, tmpd, fd);
 +      lhdr(map, CH_off, zsize-CH_off, fu, fc, ch, ret, ctx, tmpd);
    } else cli_dbgmsg("cli_unzip: ch - local hdr out of file\n");
 +  fmap_unneed_ptr(map, ch, SIZEOF_CH);
    return last?0:coff;
  }
  
  
 -int cli_unzip(int f, cli_ctx *ctx) {
 +int cli_unzip(cli_ctx *ctx) {
    unsigned int fc=0, fu=0;
    int ret=CL_CLEAN;
    uint32_t fsize, lhoff = 0, coff = 0;
 -  struct stat st;
 -  uint8_t *map;
 -  char *tmpd;
 +  struct F_MAP *map = *ctx->fmap;
 +  char *tmpd, *ptr;
  
    cli_dbgmsg("in cli_unzip\n");
 -  if (fstat(f, &st)==-1) {
 -    cli_warnmsg("cli_unzip: fstat() failed\n");
 -    return CL_ESTAT;
 -  }
 -  fsize = (uint32_t)st.st_size;
 -  if(sizeof(off_t)!=sizeof(uint32_t) && (off_t)fsize!=st.st_size) {
 +  fsize = (uint32_t)map->len;
 +  if(sizeof(off_t)!=sizeof(uint32_t) && (off_t)fsize!=map->len) {
      cli_dbgmsg("cli_unzip: file too big\n");
      return CL_CLEAN;
    }
@@@ -504,39 -498,59 +504,39 @@@
      cli_dbgmsg("cli_unzip: file too short\n");
      return CL_CLEAN;
    }
 -
 -#if HAVE_MMAP
 -  if ((map = mmap(NULL, fsize, PROT_READ, MAP_PRIVATE, f, 0))==MAP_FAILED) {
 -    cli_dbgmsg("cli_unzip: mmap failed\n");
 -    return CL_EMAP;
 -  }
 -#else
 -  if(fsize > CLI_MAX_ALLOCATION) {
 -    cli_warnmsg("cli_unzip: mmap not available and file is too big\n");
 -    return CL_CLEAN;
 -  }
 -  lseek(f, 0, SEEK_SET);
 -  if(!(map = cli_malloc(fsize)))
 -    return CL_EMEM;
 -  if(cli_readn(f, map, fsize)!=fsize) {
 -    free(map);
 -    return CL_EREAD;
 -  }
 -#endif
 -
    if (!(tmpd = cli_gentemp(ctx->engine->tmpdir))) {
 -    destroy_map(map, fsize);
      return CL_ETMPDIR;
    }
    if (mkdir(tmpd, 0700)) {
      cli_dbgmsg("cli_unzip: Can't create temporary directory %s\n", tmpd);
 -    destroy_map(map, fsize);
      free(tmpd);
      return CL_ETMPDIR;
    }
  
    for(coff=fsize-22 ; coff>0 ; coff--) { /* sizeof(EOC)==22 */
 -    if(cli_readint32(&map[coff])==0x06054b50) {
 -      uint32_t chptr = cli_readint32(&map[coff+16]);
 -      if(!CLI_ISCONTAINED(map, fsize, map+chptr, SIZEOF_CH)) continue;
 -      coff=chptr;
 -      break;
 -    }
 +      if(!(ptr = fmap_need_off_once(map, coff, 20)))
 +	  continue;
 +      if(cli_readint32(ptr)==0x06054b50) {
 +	  uint32_t chptr = cli_readint32(&ptr[16]);
 +	  if(!CLI_ISCONTAINED(0, fsize, chptr, SIZEOF_CH)) continue;
 +	  coff=chptr;
 +	  break;
 +      }
    }
  
    if(coff) {
 -    cli_dbgmsg("cli_unzip: central @%x\n", coff);
 -    while(ret==CL_CLEAN && (coff=chdr(map, coff, fsize, &fu, fc+1, &ret, ctx, tmpd, f))) {
 -      fc++;
 -      if (ctx->engine->maxfiles && fu>=ctx->engine->maxfiles) {
 -	cli_dbgmsg("cli_unzip: Files limit reached (max: %u)\n", ctx->engine->maxfiles);
 -	ret=CL_EMAXFILES;
 +      cli_dbgmsg("cli_unzip: central @%x\n", coff);
 +      while(ret==CL_CLEAN && (coff=chdr(map, coff, fsize, &fu, fc+1, &ret, ctx, tmpd))) {
 +	  fc++;
 +	  if (ctx->engine->maxfiles && fu>=ctx->engine->maxfiles) {
 +	      cli_dbgmsg("cli_unzip: Files limit reached (max: %u)\n", ctx->engine->maxfiles);
 +	      ret=CL_EMAXFILES;
 +	  }
        }
    } else cli_dbgmsg("cli_unzip: central not found, using localhdrs\n");
    if(fu<=(fc/4)) { /* FIXME: make up a sane ratio or remove the whole logic */
      fc = 0;
 -    while (ret==CL_CLEAN && lhoff<fsize && (coff=lhdr(&map[lhoff], fsize-lhoff, &fu, fc+1, NULL, &ret, ctx, tmpd, f))) {
 +    while (ret==CL_CLEAN && lhoff<fsize && (coff=lhdr(map, lhoff, fsize-lhoff, &fu, fc+1, NULL, &ret, ctx, tmpd))) {
        fc++;
        lhoff+=coff;
        if (ctx->engine->maxfiles && fu>=ctx->engine->maxfiles) {
@@@ -546,21 -560,27 +546,21 @@@
      }
    }
  
 -  destroy_map(map, fsize);
    if (!ctx->engine->keeptmp) cli_rmdirs(tmpd);
    free(tmpd);
  
    return ret;
  }
  
 -int cli_unzip_single(int f, cli_ctx *ctx, off_t lhoffl) {
 +int cli_unzip_single(cli_ctx *ctx, off_t lhoffl) {
    int ret=CL_CLEAN;
    unsigned int fu=0;
 -  struct stat st;
    uint32_t fsize;
 -  uint8_t *map;
 +  struct F_MAP *map = *ctx->fmap;
  
    cli_dbgmsg("in cli_unzip_single\n");
 -  if (fstat(f, &st)==-1) {
 -    cli_warnmsg("cli_unzip: fstat() failed\n");
 -    return CL_ESTAT;
 -  }
 -  fsize = (uint32_t)(st.st_size - lhoffl);
 -  if (lhoffl<0 || lhoffl>st.st_size || (sizeof(off_t)!=sizeof(uint32_t) && (off_t)fsize!=st.st_size - lhoffl)) {
 +  fsize = (uint32_t)(map->len - lhoffl);
 +  if (lhoffl<0 || lhoffl>map->len || (sizeof(off_t)!=sizeof(uint32_t) && (off_t)fsize!=map->len - lhoffl)) {
      cli_dbgmsg("cli_unzip: bad offset\n");
      return CL_CLEAN;
    }
@@@ -569,7 -589,26 +569,7 @@@
      return CL_CLEAN;
    }
  
 -#if HAVE_MMAP
 -  if ((map = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, f, 0))==MAP_FAILED) {
 -    cli_dbgmsg("cli_unzip: mmap() failed\n");
 -    return CL_EMAP;
 -  }
 -#else
 -  if(st.st_size > CLI_MAX_ALLOCATION) {
 -    cli_warnmsg("cli_unzip: mmap not available and file is too big\n");
 -    return CL_CLEAN;
 -  }
 -  lseek(f, 0, SEEK_SET);
 -  if(!(map = cli_malloc(st.st_size)))
 -    return CL_EMEM;
 -  if(cli_readn(f, map, st.st_size)!=st.st_size) {
 -    free(map);
 -    return CL_EREAD;
 -  }
 -#endif
 -  lhdr(&map[lhoffl], fsize, &fu, 0, NULL, &ret, ctx, NULL, f);
 +  lhdr(map, lhoffl, fsize, &fu, 0, NULL, &ret, ctx, NULL);
  
 -  destroy_map(map, st.st_size);
    return ret;
  }

-- 
Debian repository for ClamAV



More information about the Pkg-clamav-commits mailing list