c++实现tar打包和解包

环境:win10,vs2019 debug x86

最近在做一个测试工具,在里面加入lz4进行解压缩,需要对文件夹进行压缩,最简单就是将文件路径和文件内容写到同一个文件中再进行压缩,后面突然想到可以用tar来进行打包,再进行压缩。所以就去网上找别人的代码,要么要积分购买,要么只有解包,所以就自己根据解包的代码和打包后的二进制文件,自己实现了一个tar打包和解包的代码

参考的代码忘记来自哪里了,具体的tar格式我不详细介绍,详细可以去看

https://www.gnu.org/software/tar/manual/html_node/Standard.html

1、tar所有的数据都是512的倍数,文件不足用0x00补齐

2、tar尾部会以1024个0x00结束

3、tar中校验码为头部512个字节累加,计算到校验码8位时以0x20取代

4、tar中头部的信息以8进制格式显示,比如说mtime、size等

其余的就去看代码,代码不长,逻辑也很好理解,虽然没什么注释,毕竟懒得重构,需要的自行拿去修改。

irg3_tar.h

#pragma once
#include <vector>
#include <string>
class Irg3_Tar
{
private:
	Irg3_Tar() {}
public:
	virtual ~Irg3_Tar(){}
	static Irg3_Tar* getInstance() {
		static Irg3_Tar tar_;
		return &tar_;
	}
	struct posix_header{                                     /* byte offset */
		char name[100];               /*   0 */
		char mode[8];                 /* 100 */
		char uid[8];                  /* 108 */
		char gid[8];                  /* 116 */
		char size[12];                /* 124 */
		char mtime[12];               /* 136 */
		char chksum[8];               /* 148 */
		char typeflag;                /* 156 */
		char linkname[100];           /* 157 */
		char magic[6];                /* 257 */
		char version[2];              /* 263 */
		char uname[32];               /* 265 */
		char gname[32];               /* 297 */
		char devmajor[8];             /* 329 */
		char devminor[8];             /* 337 */
		char prefix[155];             /* 345 */
										  /* 500 */
	};
public:
	
	bool detar(const char* path_, const char* dst_);
	bool tar(const char* src_, const char* dst_);
private:
	bool tar_handle(std::ofstream &out,std::string& path, std::string& prefix);
	int verifyChecksum(const unsigned char* p);
	int parseoct(const unsigned char* p, std::size_t n);
	void setCheckSum(const unsigned char* p, char* buf_);
	bool mkdir(std::string& path_);
	bool set_posix_header(std::string path_, std::string prefix, unsigned char* buf);
	bool copy(std::ofstream& out, const char* msg_);
};

irg3_tar.cpp

#include "irg3_tar.h"
#include <fstream>
#include <direct.h>
#include <algorithm>
#include <io.h>  


#define TMAGIC   "ustar"        /* ustar and a null */
#define TMAGLEN  6
#define TVERSION "00"           /* 00 and no null */
#define TVERSLEN 2

/* Values used in typeflag field.  */
#define REGTYPE  '0'            /* regular file */
#define AREGTYPE '\0'           /* regular file */
#define LNKTYPE  '1'            /* link */
#define SYMTYPE  '2'            /* reserved */
#define CHRTYPE  '3'            /* character special */
#define BLKTYPE  '4'            /* block special */
#define DIRTYPE  '5'            /* directory */
#define FIFOTYPE '6'            /* FIFO special */
#define CONTTYPE '7'            /* reserved */

static bool dir_exit(const char* path_) {
	struct _stat st;
	if (_stat(path_, &st) == 0 && st.st_mode & S_IFDIR) {
		return true;
	}
	return false;
}


bool Irg3_Tar::detar(const char*path_,const char*dst_)
{
	if (path_ == nullptr) {
		return false;
	}

	struct _stat st;
	FILE* file = nullptr;
	if (_stat(path_, &st) != 0||(file = fopen(path_, "rb"))==nullptr) {
		return false;
	}
	const int block_size{ 512 };
	unsigned char buf[block_size];
	Irg3_Tar::posix_header* header = (Irg3_Tar::posix_header*)buf;
	if (st.st_size % block_size != 0) {
		//tar file size should be a multiple of 512 bytes
		return false;
	}
	bool flag_ = true;

	//最小
	auto min_met = [](long long a, long long b)->long long {
		return a < b ? a : b;
	};


	while (1) {
		if (fread(buf, block_size, 1, file) != 1) {
			//结束
			break;
		}
		if (!verifyChecksum(buf)) {
			//校验失败
			flag_ = false;
			break;
		}
		if (strncmp(header->magic, TMAGIC, 5)) {
			flag_ = false;
			break;
		}

		long long file_size{ 0 };
		sscanf(header->size, "%lo", &file_size);

		std::string str_path_ = dst_;
		std::replace(str_path_.begin(), str_path_.end(), '\\', '/');
		if (str_path_[str_path_.size() - 1] != '/') {
			str_path_.append("/");
		}
		str_path_.append(header->name);
		if (header->typeflag == REGTYPE || header->typeflag == AREGTYPE) {
			std::ofstream out;
			out.open(str_path_.c_str(), std::ios::binary);
			while(file_size>0){
				fread(buf, block_size, 1, file);
				out.write((char*)buf, min_met(file_size,block_size));
				file_size -= block_size;
			}
			out.close();
		}
		else if (header->typeflag == '5') {
			mkdir(str_path_);
		}
		else {

		}
	}
	fclose(file);
	return flag_;
}

bool Irg3_Tar::set_posix_header(std::string path_, std::string prefix, unsigned char* buf) {


	Irg3_Tar::posix_header* header = (Irg3_Tar::posix_header*)buf;
	struct _stat st;
	if (_stat(path_.append(prefix).c_str(), &st) != 0) {
		return false;
	}

	if ((st.st_mode & S_IFDIR)) {
		header->typeflag = DIRTYPE;
		prefix = prefix.substr(1).append("/");
	}
	else if (st.st_mode & S_IFREG){
		header->typeflag = REGTYPE;
		prefix = prefix.substr(1);
	}
	else {
		return false;
	}


	//tar_posix_header head;
	//name
	memcpy(header->name, prefix.c_str(), prefix.size());
	header->name[prefix.size()] = '\0';


	sprintf(header->mode, "%07d", 777);
	sprintf(header->uid, "%07o", st.st_uid);
	sprintf(header->gid, "%07o", st.st_gid);
	sprintf(header->size, "%011lo", st.st_size);
	sprintf(header->mtime, "%011llo", st.st_mtime);
	memcpy(header->chksum, "        ", 8);
	memcpy(header->magic, "ustar ", 6);
	sprintf(header->version, " ");
	setCheckSum(buf, header->chksum);
	return true;
}


bool Irg3_Tar::tar(const char* src_, const char* dst_) {

	
	if (src_ == nullptr|| !dir_exit(src_)) {
		return false;
	}

	std::string path_ = src_;

	std::replace(path_.begin(), path_.end(), '\\', '/');
	if (path_[path_.size() - 1] == '/') {
		path_ = path_.substr(0, path_.size() - 1);
	}
	std::string prefix = path_.substr(path_.rfind('/'));
	path_ = path_.substr(0 , path_.rfind('/'));


	const int block_size{ 512 };
	unsigned char buf[block_size];
	
	memset(buf, 0, block_size);
	set_posix_header(path_, prefix, buf);
	std::ofstream out;
	out.open(dst_, std::ios::binary);
	out.write((char*)buf, 512);

	tar_handle(out, path_, prefix);

	for (int i = 0; i < 1024; i++) {
		out.put('\0');
	}
	out.close();
	return true;
}

bool Irg3_Tar::tar_handle(std::ofstream &out,std::string& path, std::string& prefix)
{
	//文件句柄,win10用long long,win7用long就可以了
	long hFile = 0;
	//文件信息
	struct _finddata_t fileinfo;
	

	std::string p;
	if ((hFile = _findfirst(p.assign(path).append(prefix).append("/*").c_str(), &fileinfo)) != -1) {
		do {
			if (strcmp(fileinfo.name, ".") != 0 && strcmp(fileinfo.name, "..") != 0) {
				const int block_size{ 512 };
				unsigned char buf[block_size];

				memset(buf, 0, block_size);
				
				set_posix_header(path, p.assign(prefix).append("/").append(fileinfo.name), buf);
				out.write((char*)buf, 512);
			}
			//如果是目录,迭代之 //如果不是,加入列表
			if ((fileinfo.attrib & _A_SUBDIR)) {
				if (strcmp(fileinfo.name, ".") != 0 && strcmp(fileinfo.name, "..") != 0) {
					tar_handle(out,path, p.assign(prefix).append("/").append(fileinfo.name));
				}
			}
			else {
				copy(out, p.assign(path).append(prefix).append("/").append(fileinfo.name).c_str());
				int k = fileinfo.size % 512;
				for (int i = k; i < 512; i++) {
					out.put('\0');
				}
			}
		} while (_findnext(hFile, &fileinfo) == 0);
		_findclose(hFile);
	}
	else {
		return false;
	}
	return true;
}



/* Parse an octal number, ignoring leading and trailing nonsense. */
int Irg3_Tar::parseoct(const unsigned char* p, std::size_t n) {
	int i = 0;

	while ((*p < '0' || *p > '7') && n > 0) {
		++p;
		--n;
	}
	while (*p >= '0' && *p <= '7' && n > 0) {
		i *= 8;
		i += *p - '0';
		++p;
		--n;
	}
	return (i);
}

/* Verify the tar checksum. */
int Irg3_Tar::verifyChecksum(const unsigned char* p) {
	int n, u = 0;
	for (n = 0; n < 512; ++n) {
		if (n < 148 || n > 155)
			/* Standard tar checksum adds unsigned bytes. */
			u += ((unsigned char*)p)[n];
		else
			u += 0x20;
	}
	return (u == parseoct(p + 148, 8));
}

void Irg3_Tar::setCheckSum(const unsigned char* p, char* buf_) {
	int n, u = 0;
	for (n = 0; n < 512; ++n) {
		if (n < 148 || n > 155)
			/* Standard tar checksum adds unsigned bytes. */
			u += ((unsigned char*)p)[n];
		else
			u += 0x20;
	}
	sprintf(buf_, "%06o", u);
}



bool Irg3_Tar::mkdir(std::string& path_) {


	if(dir_exit(path_.c_str())) {
		return true;
	}
	//替换
	std::replace(path_.begin(), path_.end(), '/', '\\');

	std::string::size_type pos = path_.find('\\', 0) + 1;
	while ((pos = path_.find('\\', pos)) != std::string::npos){
		std::string str_ = path_.substr(0, pos);
		if (!dir_exit(str_.c_str())) {
			// 不存在该文件夹
			if (_mkdir(str_.c_str()) < 0) {
				return false;
			}
		}
		++pos;
	}
	if (_mkdir(path_.c_str()) < 0){
		return false;
	}
	return true;
}

bool Irg3_Tar::copy(std::ofstream& out, const char* msg_) {


	std::ifstream in;
	in.open(msg_, std::ios::binary);
	if (!in) {
		return false;
	}
	out << in.rdbuf();
	in.close();
	return true;
}

main.cpp


#include "irg3_tar.h"

// 测试
int main(int argc, char* argv[]) {
    //Irg3_Tar::getInstance()->detar("D:\\test_2\\1\\zip_utils_src.tar", "D:\\test_2\\1");
    Irg3_Tar::getInstance()->tar("D:\\test_2\\zip_utils_src", "D:\\test_2\\1.tar");
    return 0;
}

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值