microtar
| Language | C (ANSI C, usable from C++) |
|---|---|
| License | MIT |
| Repository | github.com/rxi/microtar |
| Documentation | README and microtar.h |
| Depends on | nothing (C standard library) |
| Read / write | yes / yes; basic v7 headers only: names under 100 bytes, files under 4 GB, no pax or GNU long names, no compression |
microtar is 2 files, microtar.c and microtar.h, that read and write uncompressed TAR archives. It can find an entry by name, read its data, and write file and directory entries. I/O goes through callbacks, so it can work on memory buffers or a flash file system as well as FILE*.
It is small because it skips most of the format: it ignores the ustar prefix field, pax x headers and GNU L entries (they show up as entries with odd names), stores sizes as 32-bit unsigned, and doesn’t check paths when you extract. The last change in the repository is from 2017. Use it for archives your own program writes; use libarchive for archives from other tools.
Installation#
Copy src/microtar.c and src/microtar.h from the repository into your project and compile microtar.c with the rest of your code. For .tar.gz decompress first with zlib (gzread), then pass the data to microtar through the stream callbacks.
Basic usage: list and read a tar file#
#include "microtar.h"
#include <stdio.h>
#include <stdlib.h>
int main(void) {
mtar_t tar;
mtar_header_t h;
int err = mtar_open(&tar, "test.tar", "r");
if (err != MTAR_ESUCCESS) {
fprintf(stderr, "open: %s\n", mtar_strerror(err));
return 1;
}
// list all entries
while ((err = mtar_read_header(&tar, &h)) == MTAR_ESUCCESS) {
printf("%c %8u %s\n", h.type == MTAR_TDIR ? 'd' : '-', h.size, h.name);
mtar_next(&tar);
}
if (err != MTAR_ENULLRECORD) // MTAR_ENULLRECORD means the end of the archive
fprintf(stderr, "read: %s\n", mtar_strerror(err));
// load one file
if (mtar_find(&tar, "test.txt", &h) == MTAR_ESUCCESS) {
char *p = calloc(1, h.size + 1);
if (p && mtar_read_data(&tar, p, h.size) == MTAR_ESUCCESS)
printf("%s", p);
free(p);
}
mtar_close(&tar);
return 0;
}
How to write a tar file#
#include "microtar.h"
#include <string.h>
int write_tar(void) {
mtar_t tar;
const char *s1 = "Hello world\n";
const char *s2 = "Goodbye world\n";
if (mtar_open(&tar, "out.tar", "w") != MTAR_ESUCCESS) return 1;
mtar_write_dir_header(&tar, "docs");
mtar_write_file_header(&tar, "docs/hello.txt", (unsigned)strlen(s1));
mtar_write_data(&tar, s1, (unsigned)strlen(s1));
mtar_write_file_header(&tar, "docs/bye.txt", (unsigned)strlen(s2));
mtar_write_data(&tar, s2, (unsigned)strlen(s2));
mtar_finalize(&tar); // writes the 2 zero blocks; must come before close
mtar_close(&tar);
return 0;
}
mtar_write_file_header sets mode 0664 and leaves the modification time at 0 (1970). To set mode, owner and time, fill an mtar_header_t and call mtar_write_header.
How to read a tar from memory#
Set the callbacks yourself instead of calling mtar_open. Reading needs read, seek and close:
#include "microtar.h"
#include <string.h>
typedef struct { const unsigned char *data; unsigned size; unsigned pos; } membuf_t;
static int mem_read(mtar_t *tar, void *data, unsigned size) {
membuf_t *m = tar->stream;
if (m->pos + size > m->size) return MTAR_EREADFAIL;
memcpy(data, m->data + m->pos, size);
m->pos += size;
return MTAR_ESUCCESS;
}
static int mem_seek(mtar_t *tar, unsigned pos) {
membuf_t *m = tar->stream;
if (pos > m->size) return MTAR_ESEEKFAIL;
m->pos = pos;
return MTAR_ESUCCESS;
}
static int mem_close(mtar_t *tar) { (void)tar; return MTAR_ESUCCESS; }
void open_memory_tar(mtar_t *tar, membuf_t *m) {
memset(tar, 0, sizeof(*tar)); // unused fields must be zero
tar->stream = m;
tar->read = mem_read;
tar->seek = mem_seek;
tar->close = mem_close;
// now use mtar_read_header, mtar_find, mtar_read_data as with a file
}
Tips#
- Check
h.namebefore writing files to disk: reject names with..or a leading/. - Keep names under 100 bytes: the code copies names with
strcpy, so a 100-byte name (no terminating NUL in the header) overruns the buffer. - Archives created by GNU tar, bsdtar or Python with long names contain extra
././@LongLinkorPaxHeaderentries; microtar returns them as regular entries.