2023-01-12 20:04:52 +00:00
|
|
|
/*
|
|
|
|
* Copyright (c) 2023, apio.
|
|
|
|
*
|
|
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
|
|
*
|
|
|
|
* untar.c: Example utility which extracts files from a tar archive (POSIX only).
|
|
|
|
*/
|
2023-01-12 19:04:28 +00:00
|
|
|
|
2023-01-26 21:12:42 +00:00
|
|
|
#define _XOPEN_SOURCE 700
|
2023-01-12 19:04:28 +00:00
|
|
|
#include <errno.h>
|
|
|
|
#include <fcntl.h>
|
2023-02-25 19:28:23 +00:00
|
|
|
#include <libgen.h>
|
2023-01-12 19:04:28 +00:00
|
|
|
#include <minitar.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <sys/stat.h>
|
2023-01-29 21:07:54 +00:00
|
|
|
#include <sys/sysmacros.h>
|
2023-01-12 19:04:28 +00:00
|
|
|
#include <unistd.h>
|
|
|
|
|
2023-02-25 19:30:31 +00:00
|
|
|
// NOTE: This should be done for all entries, not just regular files
|
2023-02-25 19:28:23 +00:00
|
|
|
static int create_parent_recursively(const char* path)
|
|
|
|
{
|
|
|
|
char* path_copy = strdup(path);
|
|
|
|
if (!path_copy) return -1;
|
|
|
|
|
|
|
|
char* parent = dirname(path_copy);
|
|
|
|
|
|
|
|
create:
|
|
|
|
if (mkdir(parent, 0755) < 0)
|
|
|
|
{
|
|
|
|
if (errno == ENOENT)
|
|
|
|
{
|
|
|
|
create_parent_recursively(parent);
|
|
|
|
goto create;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (errno == EEXIST) goto success;
|
|
|
|
|
|
|
|
free(path_copy);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
success:
|
|
|
|
free(path_copy);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2023-01-12 19:04:28 +00:00
|
|
|
static int untar_file(const struct minitar_entry* entry, const void* buf)
|
|
|
|
{
|
2023-02-25 19:28:23 +00:00
|
|
|
if (create_parent_recursively(entry->metadata.path) < 0) return 1;
|
|
|
|
|
2023-01-12 19:04:28 +00:00
|
|
|
int fd = open(entry->metadata.path, O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC, 0644);
|
|
|
|
if (fd < 0) return 1;
|
|
|
|
|
|
|
|
if (write(fd, buf, entry->metadata.size) < 0) return 1;
|
|
|
|
|
|
|
|
if (fchmod(fd, entry->metadata.mode) < 0) return 1;
|
|
|
|
|
|
|
|
return close(fd);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int untar_directory(const struct minitar_entry* entry)
|
|
|
|
{
|
|
|
|
if (mkdir(entry->metadata.path, entry->metadata.mode) < 0) return 1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int main(int argc, char** argv)
|
|
|
|
{
|
|
|
|
if (argc == 1)
|
|
|
|
{
|
|
|
|
fprintf(stderr, "Usage: %s [file]\n", argv[0]);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
struct minitar mp;
|
|
|
|
if (minitar_open(argv[1], &mp) != 0)
|
|
|
|
{
|
|
|
|
perror(argv[1]);
|
|
|
|
return 1;
|
|
|
|
}
|
2023-01-29 21:07:54 +00:00
|
|
|
int exit_status = 0;
|
2023-01-12 19:04:28 +00:00
|
|
|
struct minitar_entry entry;
|
|
|
|
do {
|
|
|
|
if (minitar_read_entry(&mp, &entry) == 0)
|
|
|
|
{
|
|
|
|
if (entry.metadata.type == MTAR_DIRECTORY)
|
|
|
|
{
|
|
|
|
int status = untar_directory(&entry);
|
|
|
|
if (status != 0)
|
|
|
|
{
|
|
|
|
fprintf(stderr, "Failed to create directory %s: %s\n", entry.metadata.path, strerror(errno));
|
2023-01-29 21:07:54 +00:00
|
|
|
exit_status = 1;
|
2023-01-12 19:04:28 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
printf("mkdir %s\n", entry.metadata.path);
|
|
|
|
}
|
|
|
|
else if (entry.metadata.type == MTAR_REGULAR)
|
|
|
|
{
|
|
|
|
char* ptr = (char*)malloc(entry.metadata.size);
|
|
|
|
if (!ptr)
|
|
|
|
{
|
|
|
|
perror("malloc");
|
2023-01-29 21:07:54 +00:00
|
|
|
exit_status = 1;
|
2023-01-12 19:04:28 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
minitar_read_contents(&mp, &entry, ptr, entry.metadata.size);
|
|
|
|
|
|
|
|
int status = untar_file(&entry, ptr);
|
|
|
|
|
|
|
|
free(ptr);
|
|
|
|
|
|
|
|
if (status != 0)
|
|
|
|
{
|
|
|
|
fprintf(stderr, "Failed to extract file %s: %s\n", entry.metadata.path, strerror(errno));
|
2023-01-29 21:07:54 +00:00
|
|
|
exit_status = 1;
|
2023-01-12 19:04:28 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
printf("untar %s\n", entry.metadata.path);
|
|
|
|
}
|
2023-01-26 21:12:42 +00:00
|
|
|
else if (entry.metadata.type == MTAR_SYMLINK)
|
|
|
|
{
|
|
|
|
int status = symlink(entry.metadata.link, entry.metadata.path);
|
|
|
|
|
|
|
|
if (status != 0)
|
|
|
|
{
|
|
|
|
fprintf(stderr, "Failed to create symlink %s: %s\n", entry.metadata.path, strerror(errno));
|
2023-01-29 21:07:54 +00:00
|
|
|
exit_status = 1;
|
2023-01-26 21:12:42 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
printf("symlink %s -> %s\n", entry.metadata.path, entry.metadata.link);
|
|
|
|
}
|
2023-01-27 21:46:18 +00:00
|
|
|
else if (entry.metadata.type == MTAR_HARDLINK)
|
|
|
|
{
|
|
|
|
int status = link(entry.metadata.link, entry.metadata.path);
|
|
|
|
|
|
|
|
if (status != 0)
|
|
|
|
{
|
|
|
|
fprintf(stderr, "Failed to create hard link %s: %s\n", entry.metadata.path, strerror(errno));
|
2023-01-29 21:07:54 +00:00
|
|
|
exit_status = 1;
|
2023-01-27 21:46:18 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
printf("link %s -> %s\n", entry.metadata.path, entry.metadata.link);
|
|
|
|
}
|
2023-01-27 22:00:53 +00:00
|
|
|
else if (entry.metadata.type == MTAR_FIFO)
|
|
|
|
{
|
|
|
|
int status = mknod(entry.metadata.path, entry.metadata.mode | S_IFIFO, 0);
|
|
|
|
|
|
|
|
if (status != 0)
|
|
|
|
{
|
|
|
|
fprintf(stderr, "Failed to create FIFO %s: %s\n", entry.metadata.path, strerror(errno));
|
2023-01-29 21:07:54 +00:00
|
|
|
exit_status = 1;
|
2023-01-27 22:00:53 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
printf("fifo %s\n", entry.metadata.path);
|
|
|
|
}
|
2023-01-29 21:07:54 +00:00
|
|
|
else if (entry.metadata.type == MTAR_BLKDEV)
|
|
|
|
{
|
|
|
|
int status = mknod(entry.metadata.path, entry.metadata.mode | S_IFBLK,
|
|
|
|
makedev(entry.metadata.devmajor, entry.metadata.devminor));
|
|
|
|
|
|
|
|
if (status != 0)
|
|
|
|
{
|
|
|
|
fprintf(stderr, "Failed to create block device %s: %s\n", entry.metadata.path, strerror(errno));
|
|
|
|
exit_status = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
printf("blkdev %s (%u:%u)\n", entry.metadata.path, entry.metadata.devmajor, entry.metadata.devminor);
|
|
|
|
}
|
|
|
|
else if (entry.metadata.type == MTAR_CHRDEV)
|
|
|
|
{
|
|
|
|
int status = mknod(entry.metadata.path, entry.metadata.mode | S_IFCHR,
|
|
|
|
makedev(entry.metadata.devmajor, entry.metadata.devminor));
|
|
|
|
|
|
|
|
if (status != 0)
|
|
|
|
{
|
|
|
|
fprintf(stderr, "Failed to create character device %s: %s\n", entry.metadata.path, strerror(errno));
|
|
|
|
exit_status = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
printf("chrdev %s (%u:%u)\n", entry.metadata.path, entry.metadata.devmajor, entry.metadata.devminor);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
fprintf(stderr, "error: unknown entry type: %d", entry.metadata.type);
|
|
|
|
exit_status = 1;
|
|
|
|
break;
|
|
|
|
}
|
2023-01-12 19:04:28 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
break;
|
|
|
|
} while (1);
|
|
|
|
minitar_close(&mp);
|
2023-01-29 21:07:54 +00:00
|
|
|
return exit_status;
|
2023-01-12 19:04:28 +00:00
|
|
|
}
|