microtar —— 轻量级读写 tar 文件的 C 库

本文介绍一个开源的轻量级读写 tar 文件的 C 库 —— microtar,作者是 rxi,该开源代码是在 MIT License 下发布的。

该库只包含一个头文件 microtar.h 和一个源文件 microtar.c,要使用该库只需把这两个文件直接放进项目里面。

tar 文件结构

tar 文件的结构如下:

tar 文件结构
file_1 metadata header (512 bytes)
file_1 data
padding
file_2 metadata header (512 bytes)
file_2 data
padding
file_n metadata header (512 bytes)
file_n data
padding
tar final padding

这里的文件 file 不仅指普通文件,还包括目录、链接等各种文件。

tar 文件会依顺序写入每个文件的 metadata header 以及该文件原原本本的二进制数据 data,每个 metadata header 的大小是 512 个字节,如果该文件的二进制数据字节数不是 512 的整数倍,会在该文件 data 后填充内容为 0 的字节,使得 datapadding 的总字节数恰好是 512 的整数倍。如果 data 大小为 0 字节(比如说目录),那么有的 tar 打包软件会填充 512 字节的 padding,但本文介绍的 microtar 不会进行填充,也就是说 datapadding 的总字节数会是 0

当所有需要打包的文件已被依次写入,tar 文件结尾会填充数量不等的内容全为 0 的大小为 512 字节的数据块,比如本文介绍的 microtar 会填充两个这样的数据块,其他 tar 软件实现可能会选择不同的填充数量。

metadata header 结构

  1. name (100 bytes)
    文件路径字符串,比如 “path/file_name”,如果路径长度超过 100 个字符,则字段 prefix (155 bytes) 存储文件路径前一部分,name 字段存储文件路径后一部分,总共 255 个字符。
  2. mode (8 bytes)
    文件权限,比如读、写、执行等。假设权限是 rw-rw-r–,那么 mode 字段最终存储的是权限的八进制形式字符串 “664”,至于该八进制形式字符串是左对齐(“664\0\0\0\0\0”)还是左边补数字 0“0000664\0”),抑或是其它格式,则可能不同软件有不同实现。本文介绍的 microtar 采取的是左对齐。
  3. owner (8 bytes)
    用户 ID,可选。
  4. group (8 bytes)
    组 ID,可选。
  5. size (12 bytes)
    文件字节大小。同 mode 字段一样,最终存储的是八进制形式字符串。
  6. mtime (12 bytes)
    文件最后修改时间。同 mode 字段一样,最终存储的是八进制形式字符串。
  7. checksum (8 bytes)
    metadata header 的校验值,该值是 metadata header 除了 checksum 字段以外其余 504 个字节的值的总和。同 mode 字段一样,最终存储的是八进制形式字符串。本文介绍的 microtar 采取的是 %06o 形式,即字符串长度为 6,不足的左边补上数字 0,因为 checksum 最大值的八进制形式是 373010
  8. type (1 bytes)
    文件类型。0 = 普通文件;1 = 硬链接;2 = 软链接;3 = 字符设备;4 = 块设备;5 = 目录;6 = FIFO。
  9. linkname (100 bytes)
    链接名,可选。
  10. other (255 bytes)
    其他字段比如 prefix (155 bytes),在本文介绍的 microtar 中并没有实现,而是全部放一起当作一个 255 字节的 padding,因此本文也不进行介绍。

API

metadata header

typedef struct {
  unsigned mode;
  unsigned owner;
  unsigned size;
  unsigned mtime;
  unsigned type;
  char name[100];
  char linkname[100];
} mtar_header_t;

struct mtar_t

struct mtar_t {
  int (*read)(mtar_t *tar, void *data, unsigned size);
  int (*write)(mtar_t *tar, const void *data, unsigned size);
  int (*seek)(mtar_t *tar, unsigned pos);
  int (*close)(mtar_t *tar);
  void *stream;
  unsigned pos;
  unsigned remaining_data;
  unsigned last_header;
};

读写函数

const char* mtar_strerror(int err);

int mtar_open(mtar_t *tar, const char *filename, const char *mode);
int mtar_close(mtar_t *tar);

int mtar_seek(mtar_t *tar, unsigned pos);
int mtar_rewind(mtar_t *tar);
int mtar_next(mtar_t *tar);
int mtar_find(mtar_t *tar, const char *name, mtar_header_t *h);
int mtar_read_header(mtar_t *tar, mtar_header_t *h);
int mtar_read_data(mtar_t *tar, void *ptr, unsigned size);

int mtar_write_header(mtar_t *tar, const mtar_header_t *h);
int mtar_write_file_header(mtar_t *tar, const char *name, unsigned size);
int mtar_write_dir_header(mtar_t *tar, const char *name);
int mtar_write_data(mtar_t *tar, const void *data, unsigned size);
int mtar_finalize(mtar_t *tar);

例子

writing.c

// gcc microtar.c writing.c -o writing
#include <stdio.h>
#include <stdlib.h>

#include "microtar.h"

int main() {

    mtar_t tar;
    const char *str1 = "Hello world";
    const char *str2 = "Goodbye world";

    /* Open archive for writing */
    mtar_open(&tar, "test.tar", "w");

    /* Write strings to files `test1.txt` and `test2.txt` */
    mtar_write_file_header(&tar, "test1.txt", strlen(str1));
    mtar_write_data(&tar, str1, strlen(str1));
    mtar_write_file_header(&tar, "test2.txt", strlen(str2));
    mtar_write_data(&tar, str2, strlen(str2));

    /* Finalize -- this needs to be the last thing done before closing */
    mtar_finalize(&tar);

    /* Close archive */
    mtar_close(&tar);

    return 0;
}

reading.c

// gcc microtar.c reading.c -o reading
#include <stdio.h>
#include <stdlib.h>

#include "microtar.h"

int main() {

    mtar_t tar;
    mtar_header_t h;
    char *p;

    /* Open archive for reading */
    mtar_open(&tar, "test.tar", "r");

    /* Print all file names and sizes */
    while ( (mtar_read_header(&tar, &h)) != MTAR_ENULLRECORD ) {
      printf("%s (%d bytes)\n", h.name, h.size);
      mtar_next(&tar);
    }

    /* Load and print contents of file "test1.txt" */
    mtar_find(&tar, "test1.txt", &h);
    p = calloc(1, h.size + 1);
    mtar_read_data(&tar, p, h.size);
    printf("%s\n", p);
    free(p);

    /* Close archive */
    mtar_close(&tar);

    return 0;
}    

分别编译 writing.creading.c,先运行 writing,再运行 reading,最终结果如下:

test1.txt (11 bytes)
test2.txt (13 bytes)
Hello world
  • 36
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值