ClamAV学习【9】——cvd文件解析及cli_untgz函数浏览

这个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()
");

	//dup复制文件描述符
    if((fdd = dup(fd)) == -1) {
	cli_errmsg("cli_untgz: Can't duplicate descriptor %d
", fd);
	return -1;
    }

	//打开文件
    if((infile = gzdopen(fdd, "rb")) == NULL) {
	cli_errmsg("cli_untgz: Can't gzdopen() descriptor %d, errno = %d
", 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
");
	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
");
	    free(path);
	    gzclose(infile);
	    return -1;
	}

	//上一种病毒库已经读完
	//进行下一种病毒库文件头信息处理
	//既是文件名、大小等
	if(!in_block) {
		//解压完病毒库
	    if (block[0] == '')  /* We're done */
		break;

		//前99bytes中是文件名属性
	    strncpy(name, block, 100);
	    name[100] = '';

		//该斜号分割不允许
		//在錡indows下应该也需要更改,不过不影响
		//name只能是文件名
	    if(strchr(name, '/')) {
		cli_errmsg("cli_untgz: Slash separators are not allowed in CVD
");
		free(path);
	        gzclose(infile);
		return -1;
	    }

		//给路径变量赋值
		//设置为$tempdir$/newvirusfilename
	    snprintf(path, pathlen, "%s/%s", destdir, name);
	    cli_dbgmsg("cli_untgz: Unpacking %s
", path);

		//156位置文件标志
	    type = block[156];

		//判断类型
	    switch(type) {
		case '0':
		case '':
		    break;
		case '5':
		    cli_errmsg("cli_untgz: Directories are not supported in CVD
");
		    free(path);
	            gzclose(infile);
		    return -1;
		default:
		    cli_errmsg("cli_untgz: Unknown type flag '%c'
", 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
", path);
		    free(path);
	            gzclose(infile);
		    return -1;
		}
		outfile = NULL;
	    }

		//输出文件指针指向当前病毒库文件
	    if(!(outfile = fopen(path, "wb"))) {
		cli_errmsg("cli_untgz: Cannot create file %s
", path);
		free(path);
	        gzclose(infile);
		return -1;
	    }

		//124后的为病毒签名数量
	    strncpy(osize, block + 124, 12);
	    osize[12] = '';

		//读取数量,用于写是否结束判断标志
	    if((sscanf(osize, "%o", &size)) == 0) {
		cli_errmsg("cli_untgz: Invalid size in header
");
		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)
", 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;
}

原文:http://blog.csdn.net/betabin/article/details/7456873

原文地址:https://www.cnblogs.com/sunylat/p/6393462.html