Created
July 20, 2022 16:32
-
-
Save mortie/ad846cd63dafcbdb02574ac13ef704db to your computer and use it in GitHub Desktop.
Fast tar archive extractor
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <sys/stat.h> | |
#include <sys/types.h> | |
#include <fcntl.h> | |
#include <unistd.h> | |
#include <errno.h> | |
char zeroblock[512]; | |
struct __attribute__((packed)) block_header { | |
char name[100]; | |
char mode[8]; | |
char uid[8]; | |
char gid[8]; | |
char size[12]; | |
char mtime[12]; | |
char chksum[8]; | |
char type; | |
char linkname[100]; | |
char magic[6]; | |
char version[2]; | |
char uname[32]; | |
char gname[32]; | |
char devmajor[8]; | |
char devminor[8]; | |
char prefix[155]; | |
}; | |
void dump_header(struct block_header *h) { | |
fprintf(stderr, "Name: %.100s\n", h->name); | |
fprintf(stderr, "Mode: %.8s\n", h->mode); | |
fprintf(stderr, "UID: %.8s\n", h->uid); | |
fprintf(stderr, "GID: %.8s\n", h->gid); | |
fprintf(stderr, "Size: %.12s\n", h->size); | |
fprintf(stderr, "MTime: %.12s\n", h->mtime); | |
fprintf(stderr, "Checksum: %.8s\n", h->chksum); | |
fprintf(stderr, "Type: %c\n", h->type); | |
fprintf(stderr, "Linkname: %.100s\n", h->linkname); | |
fprintf(stderr, "Magic: %.6s\n", h->magic); | |
fprintf(stderr, "Version: %.2s\n", h->version); | |
fprintf(stderr, "Uname: %.32s\n", h->uname); | |
fprintf(stderr, "Gname: %.32s\n", h->gname); | |
fprintf(stderr, "Devmajor: %.8s\n", h->devmajor); | |
fprintf(stderr, "Devminor: %.8s\n", h->devminor); | |
fprintf(stderr, "Prefix: %.155s\n", h->prefix); | |
} | |
size_t parse_octal(char *oct, size_t len) { | |
size_t num = 0; | |
for (size_t i = 0; i < len; ++i) { | |
if (oct[i] == '\0') break; | |
num *= 8; | |
num += oct[i] - '0'; | |
} | |
return num; | |
} | |
void xfread(void *ptr, size_t size, FILE *stream) { | |
if (fread(ptr, 1, size, stream) < size) { | |
fprintf(stderr, "Short read\n"); | |
abort(); | |
} | |
} | |
int dump = 0; | |
int read_block(FILE *f) { | |
char name[4096]; | |
int hasname = 0; | |
char linkname[4096]; | |
int haslinkname = 0; | |
unsigned char hblock[512]; | |
xfread(hblock, sizeof(hblock), f); | |
struct block_header *header = (struct block_header *)hblock; | |
if (memcmp(hblock, zeroblock, sizeof(hblock)) == 0) { | |
fprintf(stderr, "Done.\n"); | |
return 0; | |
} | |
while (header->type == 'L' || header->type == 'K') { | |
size_t namesize = parse_octal(header->size, sizeof(header->size)); | |
if (namesize >= 4096) { | |
fprintf(stderr, "File name %zi too long. Max: 4096\n", namesize); | |
abort(); | |
} | |
if (dump) { | |
dump_header(header); | |
} | |
size_t nameread = 0; | |
size_t n = namesize; | |
while (n > 512) { | |
nameread += 512; | |
n -= 512; | |
} | |
if (n) nameread += 512; | |
char *buf; | |
if (header->type == 'L') { | |
buf = name; | |
hasname = 1; | |
} else { | |
buf = linkname; | |
haslinkname = 1; | |
} | |
xfread(buf, nameread, f); | |
buf[namesize] = '\0'; | |
xfread(hblock, sizeof(hblock), f); | |
} | |
if (!hasname) { | |
memcpy(name, header->name, 100); | |
name[100] = '\0'; | |
} | |
if (!haslinkname) { | |
memcpy(linkname, header->linkname, 100); | |
linkname[100] = '\0'; | |
} | |
mode_t mode = (mode_t)parse_octal(header->mode, 8); | |
uid_t uid = (uid_t)parse_octal(header->uid, 8); | |
gid_t gid = (gid_t)parse_octal(header->gid, 8); | |
int outfd = -1; | |
if (header->type == '0' || header->type == '\0') { | |
// Regular file | |
//fprintf(stderr, "FILE %s\n", name); | |
outfd = open(name, O_CREAT | O_WRONLY, mode); | |
if (outfd < 0) { | |
fprintf(stderr, "open %s: %s\n", name, strerror(errno)); | |
abort(); | |
} | |
if (fchown(outfd, uid, gid) < 0) { | |
perror("chown"); | |
abort(); | |
} | |
} else if (header->type == '1') { | |
// Hard link | |
fprintf(stderr, "Hard links aren't supported!\n"); | |
abort(); | |
} else if (header->type == '2') { | |
// Symlink | |
//fprintf(stderr, "SYM %s -> %s\n", name, linkname); | |
if (symlink(linkname, name) < 0) { | |
fprintf(stderr, "symlink %s -> %s: %s\n", name, linkname, strerror(errno)); | |
abort(); | |
} | |
if (lchown(name, uid, gid) < 0) { | |
fprintf(stderr, "chown %s: %s\n", name, strerror(errno)); | |
perror("chown"); | |
abort(); | |
} | |
} else if (header->type == '5') { | |
// Directory | |
//fprintf(stderr, "DIR %s\n", name); | |
if (mkdir(name, mode) < 0) { | |
if (errno != EEXIST || strcmp(name, "./") != 0) { | |
fprintf(stderr, "mkdir %s: %s\n", name, strerror(errno)); | |
abort(); | |
} | |
} | |
if (chown(name, uid, gid) < 0) { | |
fprintf(stderr, "chown %s: %s\n", name, strerror(errno)); | |
abort(); | |
} | |
} else { | |
fprintf(stderr, "Unknown type: %c\n", header->type); | |
dump_header(header); | |
} | |
size_t fsize = parse_octal(header->size, sizeof(header->size)); | |
char block[4096 * 1024]; | |
while (fsize >= sizeof(block)) { | |
xfread(block, sizeof(block), f); | |
if (outfd >= 0) { | |
if (write(outfd, block, sizeof(block)) < sizeof(block)) { | |
fprintf(stderr, "Short write.\n"); | |
abort(); | |
} | |
} | |
fsize -= sizeof(block); | |
} | |
size_t blockread = 0; | |
size_t n = fsize; | |
while (n > 512) { | |
blockread += 512; | |
n -= 512; | |
} | |
if (n > 0) blockread += 512; | |
if (blockread > 0) { | |
xfread(block, blockread, f); | |
if (outfd >= 0) { | |
if (write(outfd, block, fsize) < fsize) { | |
fprintf(stderr, "Short write.\n"); | |
abort(); | |
} | |
} | |
} | |
if (outfd >= 0) { | |
close(outfd); | |
} | |
return 1; | |
} | |
int main(int argc, char **argv) { | |
while (read_block(stdin)); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment