《libmagic文件类型识别库的使用》介绍了libmagic库的使用,今天我们来对libmagic库的源码做一个简单的分析。
ligmagic的主要逻辑:启动时加载magic.mgc数据库的特征规则,然后读取要识别文件类型的文件内容,与magic.mgc的特征进行匹配,如果匹配上了,就表示确定了文件的类型,输出的是文件类型的一串描述信息。
magic.mgc的规则比较复杂,具体规则描述可以看https://linux.die.net/man/5/magic。
1、magic_open函数:申请内存,创建magic_set句柄
public struct magic_set *magic_open(int flags)
{
return file_ms_alloc(flags);
}
2、magic_load函数:magic.mgc规则加载到magic_set的mlist结构体指针中
magic_load
-->file_apprentice
-->magic_getpath(获取magic.mgc文件路径)
-->apprentice_1
-->apprentice_map(读取magic.mgc文件)
-->check_buffer(校验magic.mgc文件,MAGICNO、文件版本version等,相关信息都放入magic_map中)
-->add_mlist(将magic_map中的内容复制给magic_set的mlist结构体指针)
magic_map结构体:
struct magic_map {
void *p; //magic.mgc文件内容起始地址
size_t len; //magic.mgc文件大小
int type; //读取文件类型MAP_TYPE_MMAP
struct magic *magic[MAGIC_SETS];
uint32_t nmagic[MAGIC_SETS];
};
apprentice_map中支持以两种方式读取magic.mgc,一种是mmap映射,一种是常规的read方式:
map->len = (size_t)st.st_size;//文件大小
#ifdef QUICK //文件映射成内存
if ((map->p = mmap(0, (size_t)st.st_size, PROT_READ|PROT_WRITE,
MAP_PRIVATE|MAP_FILE, fd, (off_t)0)) == MAP_FAILED) {
file_error(ms, errno, "cannot map `%s'", dbname);
goto error;
}
map->type = MAP_TYPE_MMAP;
#else //读文件的方式
if ((map->p = CAST(void *, malloc(map->len))) == NULL) {
file_oomem(ms, map->len);
goto error;
}
if (read(fd, map->p, map->len) != (ssize_t)map->len) {
file_badread(ms);
goto error;
}
map->type = MAP_TYPE_MALLOC;
3、magic_file和magic_buffer函数:进行特征匹配,识别文件类型
magic_file和magic_bufffer的区别就是magic_file需要先调用file_or_fd读取一下文件的内容和长度,然后再就都是调用file_buffer函数。
magic_file
-->file_or_fd(读取文件内容)
-->file_buffer
magic_buffer
-->file_buffer
-->file_ascmagic
-->file_softmagic(遍历特征链表ms->mlist,与文件内容进行匹配)
-->match(将文件内容与magic.mgc中的特征进行匹配)
-->mget
-->mprint
-->file_printf
-->file_vprintf(如果匹配上了,最后在这里给ms->o.buf赋值描述信息)
-->file_getbuffer(返回文件描述信息ms->o.pbuf和ms->o.buf)
file_softmagic函数:
protected int
file_softmagic(struct magic_set *ms, const unsigned char *buf, size_t nbytes,
uint16_t indir_level, uint16_t *name_count, int mode, int text)
{
struct mlist *ml;
int rv, printed_something = 0, need_separator = 0;
uint16_t nc;
if (name_count == NULL) {
nc = 0;
name_count = &nc;
}
//真正进行文件类型比较的地方,遍历mlist链表
for (ml = ms->mlist[0]->next; ml != ms->mlist[0]; ml = ml->next)
if ((rv = match(ms, ml->magic, ml->nmagic, buf, nbytes, 0, mode,
text, 0, indir_level, name_count,
&printed_something, &need_separator, NULL)) != 0)
return rv;
return 0;
}
file_vprintf函数:
protected int
file_vprintf(struct magic_set *ms, const char *fmt, va_list ap)
{
int len;
char *buf, *newstr;
if (ms->event_flags & EVENT_HAD_ERR)
return 0;
len = vasprintf(&buf, fmt, ap);
if (len < 0)
goto out;
if (ms->o.buf != NULL) {
len = asprintf(&newstr, "%s%s", ms->o.buf, buf);
free(buf);
if (len < 0)
goto out;
free(ms->o.buf);
buf = newstr;
}
ms->o.buf = buf; //格式化之后的buf文件描述信息赋值给ms->o.buf
return 0;
out:
file_error(ms, errno, "vasprintf failed");
return -1;
}
libmagic规则的匹配过程在match函数中,match以及mget函数就是按这些特征描述,取文件的内容进行计算,与文件的魔数进行比较。如果匹配上了,就最后通过file_vprintf函数给ms->o.buf赋值描述信息,最后通过file_getbuffer函数,返回文件描述信息ms->o.pbuf和ms->o.buf。
好了,libmagic源码的简单分析就到这了。感兴趣的朋友,可以深究一下magic.mgc的格式以及match函数是如何匹配的,这个是libmagic源码中最核心的地方。
有问题的朋友可以进技术交流群获取(先加我微信,备注加群)。喜欢文章内容的朋友,记得关注公众号并加星标哦~~