这个cli_untgz函数,是用来解压CVD文件的。
那么,就刚先搞清楚CVD文件的功能作用。下了源码,我们会发现,没有前面提到的*.mdb或者*.hbd等病毒签名文件。原因就是,那些文件都是由CVD文件解压生成的,是的,CVD是个病毒签名压缩文件。(下面是daily.cvd解压后的)
CVD文件,前512个bytes是一个特殊的头文件,在前面也提到过了(http://blog.csdn.net/betabin/article/details/7448447)。记录引擎病毒库的简单信息。然后后面的内容,曾经多次用UE打开,发现是乱码。所以,是个压缩文件。压缩类型,根据cli_untgz函数里面使用的gzread读取函数,可以猜到是使用zlib压缩库压缩的。然后根据函数内容,接着可以判断出,原信息是一个接着一个病毒库文件存储的。既是每个新的病毒库开始的前512bytes中,存储着名字及病毒签名数量。接着就是病毒签名信息。通过签名的病毒签名数量,可以判断正在读取的病毒签名是否读取完。
还是贴代码注释比较好理解:
//解压CVD文件到临时目录中 int cli_untgz(int fd, const char *destdir) { char *path, osize[13], name[101], type; char block[TAR_BLOCKSIZE]; int nbytes, nread, nwritten, in_block = 0, fdd; unsigned int size, pathlen = strlen(destdir) + 100 + 5; FILE *outfile = NULL; struct stat foo; gzFile *infile; //提示 cli_dbgmsg("in cli_untgz()\n"); //dup复制文件描述符 if((fdd = dup(fd)) == -1) { cli_errmsg("cli_untgz: Can't duplicate descriptor %d\n", fd); return -1; } //打开文件 if((infile = gzdopen(fdd, "rb")) == NULL) { cli_errmsg("cli_untgz: Can't gzdopen() descriptor %d, errno = %d\n", fdd, errno); if(fstat(fdd, &foo) == 0) close(fdd); return -1; } //路径变量分配内存 path = (char *) cli_calloc(sizeof(char), pathlen); if(!path) { cli_errmsg("cli_untgz: Can't allocate memory for path\n"); gzclose(infile); return -1; } //开始循环读取cvd文件内容 while(1) { //每次读取512个bytes部分 nread = gzread(infile, block, TAR_BLOCKSIZE); //上一种病毒库已经读完 //且读不到下一种病毒库头信息时 //结束 if(!in_block && !nread) break; if(nread != TAR_BLOCKSIZE) { cli_errmsg("cli_untgz: Incomplete block read\n"); free(path); gzclose(infile); return -1; } //上一种病毒库已经读完 //进行下一种病毒库文件头信息处理 //既是文件名、大小等 if(!in_block) { //解压完病毒库 if (block[0] == '\0') /* We're done */ break; //前99bytes中是文件名属性 strncpy(name, block, 100); name[100] = '\0'; //该斜号分割不允许 //在錡indows下应该也需要更改,不过不影响 //name只能是文件名 if(strchr(name, '/')) { cli_errmsg("cli_untgz: Slash separators are not allowed in CVD\n"); free(path); gzclose(infile); return -1; } //给路径变量赋值 //设置为$tempdir$/newvirusfilename snprintf(path, pathlen, "%s/%s", destdir, name); cli_dbgmsg("cli_untgz: Unpacking %s\n", path); //156位置文件标志 type = block[156]; //判断类型 switch(type) { case '0': case '\0': break; case '5': cli_errmsg("cli_untgz: Directories are not supported in CVD\n"); free(path); gzclose(infile); return -1; default: cli_errmsg("cli_untgz: Unknown type flag '%c'\n", type); free(path); gzclose(infile); return -1; } //设置in_block参数 //表示接下来开始写内容 in_block = 1; //关闭上一个病毒库文件指针 if(outfile) { if(fclose(outfile)) { cli_errmsg("cli_untgz: Cannot close file %s\n", path); free(path); gzclose(infile); return -1; } outfile = NULL; } //输出文件指针指向当前病毒库文件 if(!(outfile = fopen(path, "wb"))) { cli_errmsg("cli_untgz: Cannot create file %s\n", path); free(path); gzclose(infile); return -1; } //124后的为病毒签名数量 strncpy(osize, block + 124, 12); osize[12] = '\0'; //读取数量,用于写是否结束判断标志 if((sscanf(osize, "%o", &size)) == 0) { cli_errmsg("cli_untgz: Invalid size in header\n"); free(path); gzclose(infile); fclose(outfile); return -1; } } else { /* write or continue writing file contents */ //写入path病毒文件 nbytes = size > TAR_BLOCKSIZE ? TAR_BLOCKSIZE : size; nwritten = fwrite(block, 1, nbytes, outfile); if(nwritten != nbytes) { cli_errmsg("cli_untgz: Wrote %d instead of %d (%s)\n", nwritten, nbytes, path); free(path); gzclose(infile); return -1; } //减去已经写了的病毒签名数 //判断是否结束 size -= nbytes; if(size == 0) in_block = 0; } } if(outfile) fclose(outfile); gzclose(infile); free(path); return 0; }