环境: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;
}