Tar是Unix平台下非常流行的打包格式,该格式不负责压缩,只负责打包,简单的将多个文件或文件夹组合。
除了文件本身的数据,Tar还会使用“标头”来记录文件名、文件属性、大小等信息。拥有了这些信息,再加上文件本身的数据,便可以将打包前的文件及其目录结构还原出来。
/* typeflag:字段定义文件类型 */
#define LF_OLDNORMAL '\0' /* 普通磁盘文件,Unix兼容 */
#define LF_NORMAL '0' /* 普通磁盘文件 */
#define LF_LINK '1' /* 硬链接 */
#define LF_SYMLINK '2' /* 符号链接(软链接) */
#define LF_CHR '3' /* 字符设备文件 */
#define LF_BLK '4' /* 块设备文件 */
#define LF_DIR '5' /* 目录 */
#define LF_FIFO '6' /* FIFO管道文件 */
#define LF_CONTIG '7' /* 连续文件 */
不必了解其中的每一项代表什么含义,只需要关注以下几项:
name:文件名(路径)。
size:文件大小(文件数据部分的尺寸)。
typeflag:类型标志,标志该节点是一个普通文件,还是软连接抑或是目录等。
下面是我一个解析样例,可提取多级文件夹里的指定文件:
#include <stdio.h>
#include <io.h>
#include <string.h>
// #include <.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <fcntl.h>
#define min(a,b) (((a) < (b)) ? a : b)
struct posix_tar_header
{
char name[100];
char mode[8];
char uid[8];
char gid[8];
char size[12];
char mtime[12];
char chksum[8];
char typeflag;
char linkname[100];
char magic[6];
char version[2];
char uname[32];
char gname[32];
char devmajor[8];
char devminor[8];
char prefix[155];
};
int main(int argc,char *argv[])
{
if (argc != 3)
{
printf("请输入文件: argc[%d] \n",argc);
exit(1);
}
printf("[解压文件: %s\n",argv[1]);
int fd = open(argv[1],O_RDWR);
char buf[1024*4];
int chunk = sizeof(buf);
while(1)
{
read(fd,buf,512);//读取tar header
if (buf[0] == 0)
break;
struct posix_tar_header *phdr = (struct posix_tar_header*)buf;
//从tar header头得到size
char *p = phdr->size;
int f_len = 0;
int ret;
while(*p)//8进制->10进制
f_len = (f_len * 8) + (*p++ - '0');
//printf("typeflag=%d,f_len=%d\n",phdr->typeflag,f_len);
int bytes_left = f_len;//此文件大小
if(phdr->typeflag =='5') //文件夹
{
printf(" %s (%d bytes)\n",phdr->name,f_len);
#if 0
ret= mkdir(phdr->name);
if(ret!=0)
{
printf(" mkdir %s failed\n",phdr->name);
return 0;
}
#endif
}
else
{
//比较文件名和指定文件名是否相同
char *filename= strrchr(phdr->name, '/')+1;
//printf("filename[%s],argv[%s]\n",filename,argv[2]);
if((strcmp(filename,argv[2]))!=0)
{
while(bytes_left)
{
int iobytes = min(chunk,bytes_left);
read(fd,buf,((iobytes - 1) / 512 + 1) * 512);
bytes_left -= iobytes;
}
}
else
{
int fdout = open(filename,O_CREAT|O_TRUNC|O_RDWR,0777);
if (fdout == -1)
{
printf(" failed to extract file: %s\n",phdr->name);
printf(" fdout open failed\n");
return 0;
}
while(bytes_left)
{
int iobytes = min(chunk,bytes_left);
//read(fd,buf,iobytes);
read(fd,buf,((iobytes - 1) / 512 + 1) * 512);
write(fdout,buf,iobytes);
bytes_left -= iobytes;
}
close(fdout);
}
}
}
close(fd);
printf("done]\n");
return 0;
}