From 0952af15b4aba37e64f7495533e7ef9bc889538b Mon Sep 17 00:00:00 2001 From: apio Date: Sat, 5 Nov 2022 20:10:48 +0100 Subject: [PATCH] Basic file listing :) --- Makefile | 3 +- README.md | 35 ++++++++++++++++++++- minitar.h | 30 ++++++++++++++++++ src/tar.c | 41 +++++++++++++++++++++++++ src/tar.h | 24 +++++++++++++++ src/util.c | 90 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 221 insertions(+), 2 deletions(-) create mode 100644 src/tar.h create mode 100644 src/util.c diff --git a/Makefile b/Makefile index 7214d6b..584c692 100644 --- a/Makefile +++ b/Makefile @@ -9,7 +9,8 @@ CFLAGS ?= -O2 -Wall -Wextra CFLAGS := ${CFLAGS} -I. DESTDIR ?= /usr/local -OBJS := $(OBJ)/tar.o +OBJS := $(OBJ)/tar.o \ + $(OBJ)/util.o build: $(OBJS) @echo -- Creating $(LIBNAME).a diff --git a/README.md b/README.md index 0985885..735fc83 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,36 @@ # minitar -Tiny C library to interact with tar archives \ No newline at end of file +Tiny C library to interact with tar archives + +## Example + +``` +#include +#include + +int main(int argc, char** argv) +{ + if(argc == 1) + { + fprintf(stderr, "Usage: %s [file]\n", argv[0]); + return 1; + } + struct minitar* mp = minitar_open(argv[1]); + if(!mp) + { + perror(argv[1]); + return 1; + } + struct minitar_entry* entry; + do { + entry = minitar_read_entry(mp); + if(entry) { + printf("Found file %s\n", entry->metadata.name); + minitar_free_entry(entry); + } + } while(entry); + minitar_close(mp); +} +``` + +This program will list out the files in a tar archive :) \ No newline at end of file diff --git a/minitar.h b/minitar.h index 9a09e69..be63f29 100644 --- a/minitar.h +++ b/minitar.h @@ -1,13 +1,43 @@ #ifndef MINITAR_H #define MINITAR_H #include +#include struct minitar { FILE* stream; }; +enum minitar_file_type +{ + MTAR_REGULAR, + MTAR_CHRDEV, + MTAR_BLKDEV, + MTAR_DIRECTORY +}; + +struct minitar_entry_metadata +{ + char name[257]; + mode_t mode; + uid_t uid; + gid_t gid; + size_t size; + time_t mtime; + enum minitar_file_type type; + char uname[32]; + char gname[32]; +}; + +struct minitar_entry +{ + struct minitar_entry_metadata metadata; + char* ptr; +}; + struct minitar* minitar_open(const char* pathname); +struct minitar_entry* minitar_read_entry(struct minitar* mp); +void minitar_free_entry(struct minitar_entry* entry); int minitar_close(struct minitar* mp); #endif \ No newline at end of file diff --git a/src/tar.c b/src/tar.c index 6e91662..e8e74a7 100644 --- a/src/tar.c +++ b/src/tar.c @@ -1,5 +1,11 @@ #include #include "minitar.h" +#include "tar.h" + +int minitar_read_header(struct minitar* mp, struct tar_header* hdr); +int minitar_validate_header(struct tar_header* hdr); +void minitar_parse_tar_header(struct tar_header* hdr, struct minitar_entry_metadata* metadata); +struct minitar_entry* minitar_dup_entry(struct minitar_entry* original); struct minitar* minitar_open(const char* path) { @@ -17,4 +23,39 @@ int minitar_close(struct minitar* mp) free(mp); if(rc) return rc; return 0; +} + +static struct minitar_entry* minitar_attempt_read_entry(struct minitar* mp, int* valid) +{ + struct minitar_entry entry; + struct tar_header hdr; + if(!minitar_read_header(mp, &hdr)) + { + *valid = 1; // we are at end-of-file + return NULL; + } + if(!minitar_validate_header(&hdr)) + { + *valid = 0; + return NULL; + } + *valid = 1; + minitar_parse_tar_header(&hdr, &entry.metadata); + // FIXME: Actually read the file and place it in buf. + return minitar_dup_entry(&entry); +} + +struct minitar_entry* minitar_read_entry(struct minitar* mp) +{ + int valid; + struct minitar_entry* result; + do { + result = minitar_attempt_read_entry(mp, &valid); + } while(!valid); + return result; +} + +void minitar_free_entry(struct minitar_entry* entry) +{ + free(entry); // FIXME: Also free the file's content, when it's placed in buf. } \ No newline at end of file diff --git a/src/tar.h b/src/tar.h new file mode 100644 index 0000000..a5ff046 --- /dev/null +++ b/src/tar.h @@ -0,0 +1,24 @@ +#ifndef MINITAR_TAR_H +#define MINITAR_TAR_H + +struct tar_header { + char name[100]; + char mode[8]; + char uid[8]; + char gid[8]; + char size[12]; + char mtime[12]; + char chksum[8]; + char typeflag; + char linkname[100]; + char magic[6]; + char version[2]; + char uname[32]; + char gname[32]; + char devmajor[8]; + char devminor[8]; + char prefix[155]; + char padding[12]; +} __attribute__((packed)); + +#endif \ No newline at end of file diff --git a/src/util.c b/src/util.c new file mode 100644 index 0000000..627603d --- /dev/null +++ b/src/util.c @@ -0,0 +1,90 @@ +#define _POSIX_C_SOURCE 200809L +#include +#include +#include +#include +#include +#include "minitar.h" +#include "tar.h" + +noreturn void minitar_panic(const char* message) +{ + fprintf(stderr, "minitar: %s\n", message); + abort(); +} + +void minitar_parse_tar_header(struct tar_header* hdr, struct minitar_entry_metadata* metadata) +{ + if(!strlen(hdr->prefix)) + { + strncpy(metadata->name, hdr->name, 100); + metadata->name[100] = '\0'; + } else { + strcpy(metadata->name, hdr->prefix); + strcat(metadata->name, "/"); + strncat(metadata->name, hdr->name, 100); + metadata->name[256] = '\0'; + } + + metadata->mode = (mode_t)strtol(hdr->mode, NULL, 8); + metadata->uid = (uid_t)strtoul(hdr->uid, NULL, 8); + metadata->gid = (gid_t)strtoul(hdr->gid, NULL, 8); + + char* sizeptr = strndup(hdr->size, 12); + if(!sizeptr) minitar_panic("Failed to allocate memory to duplicate a tar header's size field"); + metadata->size = (size_t)strtoull(sizeptr, NULL, 8); + free(sizeptr); + + char* timeptr = strndup(hdr->mtime, 12); + if(!timeptr) minitar_panic("Failed to allocate memory to duplicate a tar header's mtime field"); + metadata->mtime = (time_t)strtol(timeptr, NULL, 8); + free(timeptr); + + switch(hdr->typeflag) + { + case '\0': + case '0': + metadata->type = MTAR_REGULAR; + break; + case '1': + minitar_panic("Links to other files within a tar archive are unsupported"); + case '2': + minitar_panic("Symbolic links are unsupported"); + case '3': + metadata->type = MTAR_CHRDEV; + break; + case '4': + metadata->type = MTAR_BLKDEV; + break; + case '5': + metadata->type = MTAR_DIRECTORY; + break; + case '6': + minitar_panic("FIFOs are unsupported"); + default: + minitar_panic("Unknown entry type in tar header"); + } + + strcpy(metadata->uname, hdr->uname); + strcpy(metadata->gname, hdr->gname); +} + +int minitar_validate_header(struct tar_header* hdr) +{ + return !strncmp(hdr->magic, "ustar", 5); +} + +int minitar_read_header(struct minitar* mp, struct tar_header* hdr) +{ + size_t rc = fread(hdr, sizeof *hdr, 1, mp->stream); + if(rc == 0 && feof(mp->stream)) return 0; + return 1; +} + +struct minitar_entry* minitar_dup_entry(struct minitar_entry* original) +{ + struct minitar_entry* new = malloc(sizeof *original); + if(!new) return NULL; + memcpy(new, original, sizeof *new); + return new; +} \ No newline at end of file