2022-11-06 10:22:27 +00:00
|
|
|
#define _IN_MINITAR
|
2022-11-06 10:02:26 +00:00
|
|
|
#include "minitar.h"
|
|
|
|
#include "tar.h"
|
2022-11-23 17:32:10 +00:00
|
|
|
#include <libgen.h>
|
2022-11-05 19:10:48 +00:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <stdnoreturn.h>
|
|
|
|
#include <string.h>
|
|
|
|
|
2022-11-23 19:32:05 +00:00
|
|
|
#ifdef _MSC_VER
|
|
|
|
#define strdup(p) _strdup(p)
|
|
|
|
#endif
|
|
|
|
|
2022-11-21 18:07:49 +00:00
|
|
|
// Default implementation for minitar_handle_panic(). Since it's declared weak, any other definition will silently
|
|
|
|
// override this one :)
|
2022-11-06 11:24:13 +00:00
|
|
|
__attribute__((weak)) noreturn void minitar_handle_panic(const char* message)
|
2022-11-05 19:10:48 +00:00
|
|
|
{
|
|
|
|
fprintf(stderr, "minitar: %s\n", message);
|
|
|
|
abort();
|
|
|
|
}
|
|
|
|
|
2022-11-06 11:24:13 +00:00
|
|
|
noreturn void minitar_panic(const char* message)
|
|
|
|
{
|
|
|
|
minitar_handle_panic(message);
|
|
|
|
}
|
|
|
|
|
2022-11-23 19:32:05 +00:00
|
|
|
// Safer BSD-style replacement for strcpy/strncpy. Copies at most size-1 bytes from src into dest, always
|
|
|
|
// null-terminating the result. Returns the full length of src, to make it easy to check for overflows. Non-standard, so
|
|
|
|
// we provide our own implementation.
|
|
|
|
// https://linux.die.net/man/3/strlcpy
|
2022-11-21 18:15:52 +00:00
|
|
|
size_t minitar_strlcpy(char* dest, const char* src, size_t size)
|
|
|
|
{
|
2022-11-23 19:32:05 +00:00
|
|
|
size_t len, full_len; // full_len is the total length of src, len is the length we're copying
|
2022-11-21 18:15:52 +00:00
|
|
|
len = full_len = strlen(src);
|
|
|
|
if (size == 0) return len;
|
|
|
|
if (len > (size - 1)) len = size - 1;
|
2022-11-23 19:32:05 +00:00
|
|
|
for (size_t i = 0; i < len; ++i) { *(dest + i) = *(src + i); }
|
|
|
|
dest[len] = 0; // null-terminate
|
2022-11-21 18:15:52 +00:00
|
|
|
return full_len;
|
|
|
|
}
|
|
|
|
|
2022-11-23 19:32:05 +00:00
|
|
|
// strdup() but copies at most max bytes of orig, always null-terminating the result. This function is non-standard and
|
|
|
|
// as such, we provide our own implementation, to be as portable as possible.
|
|
|
|
// https://linux.die.net/man/3/strndup
|
|
|
|
char* minitar_strndup(const char* orig, size_t max)
|
|
|
|
{
|
|
|
|
size_t len = strnlen(orig, max);
|
|
|
|
char* ptr =
|
|
|
|
calloc(len + 1, 1); // Use calloc so everything is automatically zeroed and we get a null-terminator for free :)
|
|
|
|
if (!ptr) return NULL;
|
|
|
|
for (size_t i = 0; i < len; ++i) { *(ptr + i) = *(orig + i); }
|
|
|
|
return ptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
// strcat, but for characters :)
|
2022-11-21 18:15:52 +00:00
|
|
|
void minitar_append_char(char* str, char c)
|
|
|
|
{
|
|
|
|
size_t len = strlen(str);
|
|
|
|
str[len] = c;
|
|
|
|
str[len + 1] = 0;
|
|
|
|
}
|
|
|
|
|
2022-11-23 19:39:34 +00:00
|
|
|
static size_t minitar_is_aligned_to_block_size(size_t size)
|
2022-11-21 19:20:37 +00:00
|
|
|
{
|
|
|
|
return (size % 512 == 0);
|
|
|
|
}
|
|
|
|
|
2022-11-23 19:39:34 +00:00
|
|
|
static size_t minitar_align_down_to_block_size(size_t size)
|
2022-11-21 19:20:37 +00:00
|
|
|
{
|
|
|
|
return size - (size % 512);
|
|
|
|
}
|
|
|
|
|
2022-11-23 19:39:34 +00:00
|
|
|
size_t minitar_align_up_to_block_size(size_t size)
|
2022-11-21 19:20:37 +00:00
|
|
|
{
|
2022-11-23 19:39:34 +00:00
|
|
|
return minitar_is_aligned_to_block_size(size) ? size : minitar_align_down_to_block_size(size) + 512;
|
2022-11-21 19:20:37 +00:00
|
|
|
}
|
|
|
|
|
2022-11-22 16:28:15 +00:00
|
|
|
void minitar_parse_metadata_from_tar_header(const struct tar_header* hdr, struct minitar_entry_metadata* metadata)
|
2022-11-05 19:10:48 +00:00
|
|
|
{
|
2022-11-23 19:32:05 +00:00
|
|
|
if (!strlen(hdr->prefix)) // If prefix is null, the full path is only the "name" field of the tar header.
|
2022-11-05 19:10:48 +00:00
|
|
|
{
|
2022-11-23 17:32:10 +00:00
|
|
|
size_t size = minitar_strlcpy(metadata->path, hdr->name, 100);
|
|
|
|
if (size >= 100) metadata->path[100] = '\0';
|
|
|
|
else
|
|
|
|
metadata->path[size] = '\0';
|
2022-11-06 10:02:26 +00:00
|
|
|
}
|
2022-11-23 19:32:05 +00:00
|
|
|
else // Construct the path by first taking the "prefix" field, then adding a slash, then concatenating the "name"
|
|
|
|
// field.
|
2022-11-06 10:02:26 +00:00
|
|
|
{
|
2022-11-23 17:32:10 +00:00
|
|
|
minitar_strlcpy(metadata->path, hdr->prefix, 155);
|
|
|
|
minitar_append_char(metadata->path, '/');
|
|
|
|
strncat(metadata->path, hdr->name, 100);
|
|
|
|
metadata->path[256] = '\0';
|
2022-11-05 19:10:48 +00:00
|
|
|
}
|
|
|
|
|
2022-11-23 19:32:05 +00:00
|
|
|
char* mut_path = strdup(metadata->path); // basename modifies the string passed to it, so we need to make a copy.
|
2022-11-23 17:32:10 +00:00
|
|
|
if (!mut_path) minitar_panic("Failed to allocate memory");
|
2022-11-23 19:32:05 +00:00
|
|
|
|
2022-11-23 17:32:10 +00:00
|
|
|
char* bname = basename(mut_path);
|
2022-11-23 19:32:05 +00:00
|
|
|
|
2022-11-23 17:32:10 +00:00
|
|
|
minitar_strlcpy(metadata->name, bname, sizeof(metadata->name));
|
|
|
|
free(mut_path);
|
|
|
|
|
2022-11-23 19:32:05 +00:00
|
|
|
// Numeric fields in tar archives are stored as octal-encoded ASCII strings. Weird decision (supposedly for
|
|
|
|
// portability), which means we have to parse these strings (the size and mtime fields aren't even null-terminated!)
|
|
|
|
// to get the far more user-friendlier integer values stored in our metadata structure.
|
|
|
|
|
2022-11-21 18:01:12 +00:00
|
|
|
metadata->mode = (mode_t)strtoul(hdr->mode, NULL, 8);
|
2022-11-05 19:10:48 +00:00
|
|
|
metadata->uid = (uid_t)strtoul(hdr->uid, NULL, 8);
|
|
|
|
metadata->gid = (gid_t)strtoul(hdr->gid, NULL, 8);
|
|
|
|
|
2022-11-23 19:32:05 +00:00
|
|
|
char* sizeptr = minitar_strndup(
|
2022-11-21 18:01:12 +00:00
|
|
|
hdr->size, 12); // The hdr->size field is not null-terminated, yet strndup returns a null-terminated string.
|
2022-11-23 17:32:10 +00:00
|
|
|
if (!sizeptr) minitar_panic("Failed to allocate memory");
|
2022-11-05 19:10:48 +00:00
|
|
|
metadata->size = (size_t)strtoull(sizeptr, NULL, 8);
|
|
|
|
free(sizeptr);
|
|
|
|
|
2022-11-23 19:32:05 +00:00
|
|
|
char* timeptr = minitar_strndup(
|
2022-11-21 18:01:12 +00:00
|
|
|
hdr->mtime, 12); // The hdr->mtime field is not null-terminated, yet strndup returns a null-terminated string.
|
2022-11-23 17:32:10 +00:00
|
|
|
if (!timeptr) minitar_panic("Failed to allocate memory");
|
2022-11-21 18:01:12 +00:00
|
|
|
metadata->mtime = (time_t)strtoull(timeptr, NULL, 8);
|
2022-11-05 19:10:48 +00:00
|
|
|
free(timeptr);
|
|
|
|
|
2022-11-23 19:32:05 +00:00
|
|
|
// The type is stored as a character instead of an integer.
|
|
|
|
|
2022-11-06 10:02:26 +00:00
|
|
|
switch (hdr->typeflag)
|
2022-11-05 19:10:48 +00:00
|
|
|
{
|
2022-11-06 10:02:26 +00:00
|
|
|
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");
|
2022-11-05 19:10:48 +00:00
|
|
|
}
|
|
|
|
|
2022-11-21 18:15:52 +00:00
|
|
|
minitar_strlcpy(metadata->uname, hdr->uname, 32);
|
|
|
|
minitar_strlcpy(metadata->gname, hdr->gname, 32);
|
2022-11-05 19:10:48 +00:00
|
|
|
}
|
|
|
|
|
2022-11-21 16:38:08 +00:00
|
|
|
int minitar_validate_header(const struct tar_header* hdr)
|
2022-11-05 19:10:48 +00:00
|
|
|
{
|
2022-11-06 13:14:06 +00:00
|
|
|
if (hdr->typeflag != '\0' && hdr->typeflag != '0' && hdr->typeflag != '1' && hdr->typeflag != '2' &&
|
|
|
|
hdr->typeflag != '3' && hdr->typeflag != '4' && hdr->typeflag != '5' && hdr->typeflag != '6')
|
|
|
|
return 0;
|
2022-11-05 19:10:48 +00:00
|
|
|
return !strncmp(hdr->magic, "ustar", 5);
|
|
|
|
}
|
|
|
|
|
|
|
|
int minitar_read_header(struct minitar* mp, struct tar_header* hdr)
|
|
|
|
{
|
2022-11-06 11:24:13 +00:00
|
|
|
size_t rc = fread(hdr, 1, sizeof *hdr, mp->stream);
|
2022-11-06 10:02:26 +00:00
|
|
|
if (rc == 0 && feof(mp->stream)) return 0;
|
2022-11-06 13:14:06 +00:00
|
|
|
if (rc == 0 && ferror(mp->stream)) minitar_panic("Error while reading file header from tar archive");
|
2022-11-06 11:24:13 +00:00
|
|
|
if (rc < sizeof *hdr) minitar_panic("Valid tar files should be split in 512-byte blocks");
|
2022-11-05 19:10:48 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2022-11-23 19:39:34 +00:00
|
|
|
// Create a heap-allocated copy of an entry on the stack.
|
2022-11-21 16:38:08 +00:00
|
|
|
struct minitar_entry* minitar_dup_entry(const struct minitar_entry* original)
|
2022-11-05 19:10:48 +00:00
|
|
|
{
|
|
|
|
struct minitar_entry* new = malloc(sizeof *original);
|
2022-11-06 10:02:26 +00:00
|
|
|
if (!new) return NULL;
|
2022-11-05 19:10:48 +00:00
|
|
|
memcpy(new, original, sizeof *new);
|
|
|
|
return new;
|
|
|
|
}
|