先明确需求:用C语言实现判断文件是文本文件还是二进制文件,或者其他压缩格式文件。
文件的类型
Linux系统下,万物皆文件。
为了将所有的东西都能当成文件来管理,Linux系统将文件分成了七种类型,分别如下:
上表中第三、第四列是Linux下使用stat函数判断文件类型提供的一些宏定义。如判断一个文件是否属于普通文件,可以使用下面的代码:
stat(pathname, &sb);
if ((sb.st_mode & S_IFMT) == S_IFREG) {
/* Handle regular file */
}
或者直接使用:
stat(pathname, &sb);
if (S_ISREG(sb.st_mode)) {
/* Handle regular file */
}
但是我们的需求是判断文件是否属于文本文件还是二进制文件。而这两种都属于S_IFREG普通文件,因此无法使用上面的方法进行判断。
万能的file命令
file命令是Linux下用来检测文件类型的一个内置的命令。
大概原理就是读取一个文件的前面1024个字节,然后根据magic(/etc/magic 或者 /usr/share/misc/magic) 里对应的规则分析出文件头,并打印到屏幕上。
使用也很简单,直接file后面跟上文件名即可:
[root@ck08 ~]# file anaconda-ks.cfg
anaconda-ks.cfg: ASCII text
[root@ck08 ~]# file tls.pcap
tls.pcap: tcpdump capture file (little-endian) - version 2.4 (Ethernet, capture length 262144)
[root@ck08 ~]# file zlib-1.2.11.tar.gz
zlib-1.2.11.tar.gz: gzip compressed data, was "zlib-1.2.11.tar", from Unix, last modified: Mon Jan 16 01:36:58 2017, max compression
[root@ck08 ~]# file /usr/bin/grep
/usr/bin/grep: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.32, BuildID[sha1]=bb5d89868c5a04ae48f76250559cb01fae1cd762, stripped
从上面的示例中,可以看出file命令很强大,几乎可以识别出文件的详细类型,甚至是编码,压缩格式,大小端等具体的信息。
因此,这个命名是符合我们的需求的。
但是,我们需要的是C语言实现,因此,不得不研究magic的文件头规则。
magic文件规则
文件中的每行都指定了一个规则测试去检验文件类型,这个规则由4个域指定。分别为offset、type、test、message。
offset
指定由文件起始的第几个byte开始检验。
type
要进行检验的数据类型,即由offset那个byte开始的那个数据类型是什么。具体有哪些数据类型,可以参考magic(5)。常用的数据类型有
byte:一个byte的值
short:两个byte的值
long:四个byte的值
string:字符串
test
检验值。用于检验offset下的type是否是这个test值。使用C语言的数值或字符表示形式。
message
用于显示检验结果的信息显示。
如果type为数值类型,那么其后面可添加&value,表示先与后面的test值进行’与’操作,再进行比较。如果type为字符串类型,则其后可跟/[Bbc]*,/b表示忽略空格,/c表示忽略字母大小写。
如果test的值为数值类型,可以数值前添加=,<,>,&,^,~,分别表示相等、小于、大于、与操作、异或操作、取反操作。
如果test的值为字符串类型,可以在其前添加=、<、>。
例如,ELF文件的magic表示为:
# ELF
#0string ELF ELF
0 string \177ELF ELF
>4 byte 1 32-bit