当前位置: 首页 > 科技观察

带你在Linux下写一个打包软件Tar

时间:2023-03-21 23:02:15 科技观察

相信大家对Linux的.tar.gz已经不陌生了。这是先tar这个包(.tar后缀),然后用gzip压缩这个tar文件(.tar.gz)后缀名。值得注意的是,tar不是压缩软件,它只是将一堆文件/文件夹打包成一个文件(tar文件),文件关联、文件权限、相对路径等都会为您保存。一开始的设计是tar和gzip只做一件事,各司其职。后来他们觉得太麻烦了,就把压缩功能集成到tar里面了。-Createagzippedarchive:tarczftarget.tar.gzfile1file2file3最近在学习OS的时候写了一个类似tar的项目,所以今天简单说一下如何写一个打包软件。本软件会通过md5比较重复的文件内容,重用旧内容。.基本单位blockblock可以理解为文件系统的最小单位,有以下几种类型:目录块,文件夹块,存放文件夹元信息;文件块,文件块,存储文件元信息;数据块,只用来存放文件内容;目录块,注意entry中必须有一个fileindex,用来存放重复文件名的下标。另外,给项目一个根目录。typedefstruct{charname[SIFS_MAX_NAME_LENGTH];//nameofthedirectorytime_tmodtime;//timelastmodified<-time()uint32_tnentries;//文件夹内的文件/文件夹个数struct{SIFS_BLOCKIDblockID;//blockIDuint32_tfileindexofsubdirectoryorfile;//DifferentduplicatefilesName}entries[SIFS_MAX_ENTRIES];}SIFS_DIRBLOCK;FileBlock,length是文件内容有多少字节,然后用来统计有多少个数据块,firstblockID记录第一个数据块的id,nfiles记录有多少个内容重复的文件number,filenames是重复这个文件块的文件内容的文件名。typedefstruct{time_tmodtime;//timefirstfileadded<-time()size_tlength;//lengthofffiles'contentsinbytesunsignedcharmd5[MD5_BYTELEN];//theMD5cryptographicdigest(asumary)ofthefiles'contentsSIFS_BLOCKIDfirstblockID;//theblocknumber(blockID)ofthefiles'firstdata-blockuint32_tnfiles;//nfileswithidenticalENT_RFScharmd5[icalcontentsIES];][SIFS_MAX_NAME_LENGTH];//每个相同文件的名称及其修改时间的数组。}SIFS_FILEBLOCK;位图数组,记录每个块的类型,有文件、文件夹和数据块三种类型。通用功能,让大家看一下关键功能:读取tar后文件的元头记录了块的大小(blocksize)和多少块。voidread_vol_header(FILE*vol,SIFS_VOLUME_HEADER*header){fread(header,sizeof(SIFS_VOLUME_HEADER),1,vol);printf("header->blocksize%zu,header->nblocks%u\n",header->blocksize,header->nblocks);}bitmap,每次操作tar文件都要读取。voidread_bitmap(FILE*vol,SIFS_BIT*bitmap,intnblocks){intsize=nblocks*sizeof(SIFS_BIT);fread(bitmap,size,1,vol);}root_block同理,从根块开始读写,根目录离开。voidread_root_block(FILE*vol,SIFS_DIRBLOCK*dirblock){fread(dirblock,sizeof(SIFS_DIRBLOCK),1,vol);printf("read_root_blockfinish,dirblock.name:%s,dirblock.entrieds:%d,dirblock.modtime%ld\n",dirblock->name,dirblock->nentries,dirblock->modtime);}路径,你懂的,./sifs_putvolume~/res.txt/dirB/subdirB/subsubdir/newfileB,要读取的内容即可是靠读取函数解决的,但是写入tar文件必须手动解析递归搜索路径。voidread_route_names(char*pathname,char**route_names,int*route_cnt){char*dir;char*pathname_to_split=copyStr(pathname);strcpy(pathname_to_split,pathname);while((dir=strsep(&pathname_to_split,"/"))!=NULL){route_names[*route_cnt]=copyStr(dir);(*route_cnt)++;}}以上操作几乎都是mkdir,rmdir,writefile,readfile,putfile等完成的。那么,应该举一个readfile的例子作为代表。intrecursive_dirinfo(SIFS_DIRBLOCK*cur_dir_block,char**route_names,introute_name_p,introute_cnt);实现:intrecursive_dirinfo(SIFS_DIRBLOCK*cur_dir_block,char**route_names,introute_name_p,introute_cnt){for(inti=0;inirieent_block;s{intblockid=cur_dir_block->entries[i].blockID;if(位图[blockid]==SIFS_DIR){SIFS_DIRBLOCKdirblock;intstart=sizeof(SIFS_VOLUME_HEADER)+header.nblocks*sizeof(SIFS_BIT);read_dir_block(vol,&dirblock,blockid*blocksize,start);if(strcmp(dirblock.name,route_names[route_name_p])==0){if(route_name_p+2==route_cnt){returndo_read_file(cur_dir_block,route_names[route_name_p+1],blockid);}returnrecursive_dirinfo(&dirblock,route_names,route_name_p+1,route_cnt);}}}return1;}以``./sifs_putvolume~/res.txt/dirB/subdirB/subsubdir/newfileB为例,如果递归找到subsubdir`这个文件夹块,执行对应操作:写文件时,去位图中找一个未使用的block,如果够写文件就写入,并更新文件夹信息。读取文件就是根据文件夹块在其中找到新文件。nblocks;i++){SIFS_FILEBLOCKfileblock;if(位图[i]==SIFS_FILE){intstart=sizeof(SIFS_VOLUME_HEADER)+header.nblocks*sizeof(SIFS_BIT);read_file_block(vol,&fileblock,i*blocksize,start);*nbytes=fileblock.length;intneed_data_blocks=*nbytes/header.blocksize;if(strcmp(fileblock.filenames[0],filename)==0){for(intd_block_id=fileblock.firstblockID;d_block_id-i-1