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()\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;
}
浙公网安备 33010602011771号