tar: rewrite to support reading from stdin

This commit is contained in:
K. Lange 2020-05-05 00:07:29 +09:00
parent 0e38268863
commit c43fc42b8a

View File

@ -20,6 +20,7 @@
#include <sys/types.h>
#include <toaru/hashmap.h>
#include <toaru/inflate.h>
struct ustar {
char filename[100];
@ -44,11 +45,11 @@ struct ustar {
char dev_minor[8];
char prefix[155];
char padding[12];
};
static struct ustar * file_from_offset(FILE * f, size_t offset) {
static struct ustar * extract_file(FILE * f) {
static struct ustar _ustar;
fseek(f, offset, SEEK_SET);
if (fread(&_ustar, 1, sizeof(struct ustar), f) != sizeof(struct ustar)) {
fprintf(stderr, "failed to read file\n");
return NULL;
@ -64,13 +65,6 @@ static struct ustar * file_from_offset(FILE * f, size_t offset) {
return &_ustar;
}
static unsigned int round_to_512(unsigned int i) {
unsigned int t = i % 512;
if (!t) return i;
return i + (512 - t);
}
static unsigned int interpret_mode(struct ustar * file) {
return
((file->mode[0] - '0') << 18) |
@ -137,9 +131,8 @@ static void dump_file(struct ustar * file) {
#endif
#define CHUNK_SIZE 4096
static void write_file(struct ustar * file, FILE * f, FILE * mf, size_t off, char * name) {
static void write_file(struct ustar * file, FILE * f, FILE * mf, char * name) {
size_t length = interpret_size(file);
fseek(f, off + 512, SEEK_SET);
char buf[CHUNK_SIZE];
while (length > CHUNK_SIZE) {
fread( buf, 1, CHUNK_SIZE, f);
@ -151,11 +144,26 @@ static void write_file(struct ustar * file, FILE * f, FILE * mf, size_t off, cha
fwrite(buf, 1, length, mf);
}
fclose(mf);
fseek(f, off, SEEK_SET);
/* TODO: fchmod? */
chmod(name, interpret_mode(file));
}
static void _seek_forward(FILE * f, size_t amount) {
for (size_t i = 0; i < amount; ++i) {
fgetc(f);
}
}
static void usage(char * argv[]) {
fprintf(stderr,
"tar - extract ustar archives\n"
"\n"
"usage: %s [-ctxvaf] [name]\n"
"\n"
" -f \033[3mfile archive to open\033[0m\n"
" -x \033[3mextract\033[0m\n"
"\n", argv[0]);
}
int main(int argc, char * argv[]) {
int opt;
@ -166,7 +174,7 @@ int main(int argc, char * argv[]) {
#define TAR_ACTION_CREATE 2
#define TAR_ACTION_LIST 3
while ((opt = getopt(argc, argv, "ctxvaf:")) != -1) {
while ((opt = getopt(argc, argv, "?ctxvaf:")) != -1) {
switch (opt) {
case 'c':
if (action) {
@ -195,6 +203,9 @@ int main(int argc, char * argv[]) {
case 'v':
verbose = 1;
break;
case '?':
usage(argv);
return 1;
default:
fprintf(stderr, "%s: unsupported option '%c'\n", argv[0], opt);
return 1;
@ -202,40 +213,45 @@ int main(int argc, char * argv[]) {
}
if (!fname) {
fprintf(stderr, "%s: todo: stdin/stdout\n", argv[0]);
return 1;
fname = "-";
}
if (action == TAR_ACTION_EXTRACT || action == TAR_ACTION_LIST) {
hashmap_t * files = hashmap_create(10);
FILE * f = fopen(fname,"r");
FILE * f;
if (!strcmp(fname,"-")) {
f = stdin;
} else {
f = fopen(fname,"r");
}
if (!f) {
fprintf(stderr, "%s: %s: %s\n", argv[0], fname, strerror(errno));
return 1;
}
fseek(f, 0, SEEK_END);
size_t length = ftell(f);
fseek(f, 0, SEEK_SET);
char tmpname[1024] = {0};
int last_was_long = 0;
size_t off = 0;
while (!feof(f)) {
struct ustar * file = file_from_offset(f, off);
struct ustar * file = extract_file(f);
if (!file) {
break;
}
if (action == TAR_ACTION_LIST || verbose) {
fprintf(stdout, "%.155s%.100s\n", file->prefix, file->filename);
}
if (action == TAR_ACTION_EXTRACT) {
if (action == TAR_ACTION_LIST) {
if (verbose) {
fprintf(stdout, "%10d %.155s%.100s\n", interpret_size(file), file->prefix, file->filename);
} else {
fprintf(stdout, "%.155s%.100s\n", file->prefix, file->filename);
}
_seek_forward(f, interpret_size(file));
} else if (action == TAR_ACTION_EXTRACT) {
if (verbose) {
fprintf(stdout, "%.155s%.100s\n", file->prefix, file->filename);
}
char name[1024] = {0};
if (last_was_long) {
strncat(name, tmpname, 1023);
@ -249,8 +265,9 @@ int main(int argc, char * argv[]) {
FILE * mf = fopen(name,"w");
if (!mf) {
fprintf(stderr, "%s: %s: %s: %s\n", argv[0], fname, name, strerror(errno));
_seek_forward(f, interpret_size(file));
} else {
write_file(file,f,mf,off,name);
write_file(file,f,mf,name);
}
struct ustar * tmp = malloc(sizeof(struct ustar));
memcpy(tmp, file, sizeof(struct ustar));
@ -276,31 +293,33 @@ int main(int argc, char * argv[]) {
if (!mf) {
fprintf(stderr, "%s: %s: %s: %s\n", argv[0], fname, name, strerror(errno));
} else {
write_file(hashmap_get(files,tmp),f,mf,off,name);
write_file(hashmap_get(files,tmp),f,mf,name);
}
}
_seek_forward(f, interpret_size(file));
} else if (file->type[0] == '2') {
char tmp[101] = {0};
strncat(tmp, file->link, 100);
if (symlink(tmp, name) < 0) {
fprintf(stderr, "%s: %s: %s: %s: %s\n", argv[0], fname, name, tmp, strerror(errno));
}
_seek_forward(f, interpret_size(file));
} else if (file->type[0] == 'L') {
/* This is a GNU Long Name block; store its contents as a file name */
size_t s = interpret_size(file);
fseek(f, off + 512, SEEK_SET);
fread(tmpname, 1, s, f);
tmpname[s] = '\0';
fseek(f, off, SEEK_SET);
last_was_long = 1;
} else {
fprintf(stderr, "%s: %s: %s: %s\n", argv[0], fname, name, type_to_string(file->type[0]));
_seek_forward(f, interpret_size(file));
}
}
off += 512;
off += round_to_512(interpret_size(file));
if (off >= length) break;
size_t file_size = interpret_size(file);
if (file_size % 512) {
_seek_forward(f, 512 - (file_size % 512));
}
}
} else {
fprintf(stderr, "%s: unsupported action\n", argv[0]);