Compare commits

...

2 commits

Author SHA1 Message Date
da2a0f7b1c Implement extracting from archive
All checks were successful
Run Unit Tests / build-and-run-unit-tests (push) Successful in 5s
TODO:
    Storing file permissions to be extracted with same permissions.
    Archiving and extracting symbolic links.
2024-07-18 13:27:32 +09:00
0449ab389e Add helper to create dirs from filepath 2024-07-18 11:53:00 +09:00
7 changed files with 290 additions and 11 deletions

View file

@ -526,7 +526,8 @@ int simple_archiver_write_all(FILE *out_f, SDArchiverState *state,
return SDAS_SUCCESS; return SDAS_SUCCESS;
} }
int simple_archiver_print_archive_info(FILE *in_f) { int simple_archiver_parse_archive_info(FILE *in_f, int do_extract,
const SDArchiverState *state) {
unsigned char buf[1024]; unsigned char buf[1024];
memset(buf, 0, 1024); memset(buf, 0, 1024);
uint16_t u16; uint16_t u16;
@ -547,6 +548,8 @@ int simple_archiver_print_archive_info(FILE *in_f) {
return SDAS_INVALID_FILE; return SDAS_INVALID_FILE;
} }
__attribute__((cleanup(free_malloced_memory))) void *decompressor_cmd = NULL;
if ((buf[0] & 1) != 0) { if ((buf[0] & 1) != 0) {
fprintf(stderr, "De/compressor flag is set.\n"); fprintf(stderr, "De/compressor flag is set.\n");
is_compressed = 1; is_compressed = 1;
@ -586,6 +589,9 @@ int simple_archiver_print_archive_info(FILE *in_f) {
} }
buf[1023] = 0; buf[1023] = 0;
fprintf(stderr, "Decompressor cmd: %s\n", buf); fprintf(stderr, "Decompressor cmd: %s\n", buf);
decompressor_cmd = malloc(u16 + 1);
memcpy((char *)decompressor_cmd, buf, u16 + 1);
((char *)decompressor_cmd)[u16] = 0;
} else { } else {
__attribute__((cleanup(free_malloced_memory))) void *heap_buf = __attribute__((cleanup(free_malloced_memory))) void *heap_buf =
malloc(u16 + 1); malloc(u16 + 1);
@ -607,6 +613,7 @@ int simple_archiver_print_archive_info(FILE *in_f) {
fprintf(stderr, "File count is %u\n", u32); fprintf(stderr, "File count is %u\n", u32);
uint32_t size = u32; uint32_t size = u32;
int skip = 0;
for (uint32_t idx = 0; idx < size; ++idx) { for (uint32_t idx = 0; idx < size; ++idx) {
fprintf(stderr, "\nFile %10u of %10u.\n", idx + 1, size); fprintf(stderr, "\nFile %10u of %10u.\n", idx + 1, size);
if (feof(in_f) || ferror(in_f)) { if (feof(in_f) || ferror(in_f)) {
@ -615,12 +622,33 @@ int simple_archiver_print_archive_info(FILE *in_f) {
return SDAS_INVALID_FILE; return SDAS_INVALID_FILE;
} }
simple_archiver_helper_16_bit_be(&u16); simple_archiver_helper_16_bit_be(&u16);
__attribute__((cleanup(free_FILE_helper))) FILE *out_f = NULL;
if (u16 < 1024) { if (u16 < 1024) {
if (fread(buf, 1, u16 + 1, in_f) != (size_t)u16 + 1) { if (fread(buf, 1, u16 + 1, in_f) != (size_t)u16 + 1) {
return SDAS_INVALID_FILE; return SDAS_INVALID_FILE;
} }
buf[1023] = 0; buf[1023] = 0;
fprintf(stderr, " Filename: %s\n", buf); fprintf(stderr, " Filename: %s\n", buf);
if (do_extract) {
if ((state->parsed->flags & 0x8) == 0) {
__attribute__((cleanup(free_FILE_helper))) FILE *test_fd =
fopen((const char *)buf, "rb");
if (test_fd) {
skip = 1;
fprintf(stderr,
" WARNING: File already exists and "
"\"--overwrite-extract\" is not specified, skipping!\n");
} else {
skip = 0;
}
} else {
skip = 0;
}
if (!skip) {
simple_archiver_helper_make_dirs((const char *)buf);
out_f = fopen((const char *)buf, "wb");
}
}
} else { } else {
__attribute__((cleanup(free_malloced_memory))) void *heap_buf = __attribute__((cleanup(free_malloced_memory))) void *heap_buf =
malloc(u16 + 1); malloc(u16 + 1);
@ -630,6 +658,26 @@ int simple_archiver_print_archive_info(FILE *in_f) {
} }
uc_heap_buf[u16 - 1] = 0; uc_heap_buf[u16 - 1] = 0;
fprintf(stderr, " Filename: %s\n", uc_heap_buf); fprintf(stderr, " Filename: %s\n", uc_heap_buf);
if (do_extract) {
if ((state->parsed->flags & 0x8) == 0) {
__attribute__((cleanup(free_FILE_helper))) FILE *test_fd =
fopen((const char *)buf, "rb");
if (test_fd) {
skip = 1;
fprintf(stderr,
"WARNING: File already exists and \"--overwrite-extract\" "
"is not specified, skipping!\n");
} else {
skip = 0;
}
} else {
skip = 0;
}
if (!skip) {
simple_archiver_helper_make_dirs((const char *)uc_heap_buf);
out_f = fopen((const char *)buf, "wb");
}
}
} }
if (fread(buf, 1, 4, in_f) != 4) { if (fread(buf, 1, 4, in_f) != 4) {
@ -647,17 +695,159 @@ int simple_archiver_print_archive_info(FILE *in_f) {
} else { } else {
fprintf(stderr, " File size: %lu\n", u64); fprintf(stderr, " File size: %lu\n", u64);
} }
while (u64 > 0) {
if (u64 > 1024) { if (do_extract && !skip) {
if (fread(buf, 1, 1024, in_f) != 1024) { fprintf(stderr, " Extracting...\n");
return SDAS_INVALID_FILE; int pipe_into_cmd[2];
int pipe_outof_cmd[2];
pid_t decompressor_pid;
if (pipe(pipe_into_cmd) != 0) {
// Unable to create pipes.
break;
} else if (pipe(pipe_outof_cmd) != 0) {
// Unable to create second set of pipes.
close(pipe_into_cmd[0]);
close(pipe_into_cmd[1]);
return 1;
} else if (fcntl(pipe_into_cmd[1], F_SETFL, O_NONBLOCK) != 0) {
// Unable to set non-blocking on into-write-pipe.
close(pipe_into_cmd[0]);
close(pipe_into_cmd[1]);
close(pipe_outof_cmd[0]);
close(pipe_outof_cmd[1]);
return 1;
} else if (fcntl(pipe_outof_cmd[0], F_SETFL, O_NONBLOCK) != 0) {
// Unable to set non-blocking on outof-read-pipe.
close(pipe_into_cmd[0]);
close(pipe_into_cmd[1]);
close(pipe_outof_cmd[0]);
close(pipe_outof_cmd[1]);
return 1;
}
if (state && state->parsed && state->parsed->decompressor) {
if (simple_archiver_de_compress(pipe_into_cmd, pipe_outof_cmd,
state->parsed->decompressor,
&decompressor_pid) != 0) {
// Failed to spawn compressor.
close(pipe_into_cmd[1]);
close(pipe_outof_cmd[0]);
return 1;
} }
u64 -= 1024;
} else { } else {
if (fread(buf, 1, u64, in_f) != u64) { if (simple_archiver_de_compress(pipe_into_cmd, pipe_outof_cmd,
return SDAS_INVALID_FILE; decompressor_cmd,
&decompressor_pid) != 0) {
// Failed to spawn compressor.
close(pipe_into_cmd[1]);
close(pipe_outof_cmd[0]);
return 1;
}
}
// Close unnecessary pipe fds on this end of the transfer.
close(pipe_into_cmd[0]);
close(pipe_outof_cmd[1]);
uint64_t compressed_file_size = u64;
int write_again = 0;
int write_pipe_done = 0;
int read_pipe_done = 0;
ssize_t fread_ret;
char recv_buf[1024];
size_t amount_to_read;
while (!write_pipe_done || !read_pipe_done) {
// Read from file.
if (!write_pipe_done) {
if (!write_again && compressed_file_size != 0) {
if (compressed_file_size > 1024) {
amount_to_read = 1024;
} else {
amount_to_read = compressed_file_size;
}
fread_ret = fread(buf, 1, amount_to_read, in_f);
if (fread_ret > 0) {
compressed_file_size -= fread_ret;
}
}
// Send over pipe to decompressor.
if (fread_ret > 0) {
ssize_t write_ret = write(pipe_into_cmd[1], buf, fread_ret);
if (write_ret == fread_ret) {
// Successful write.
write_again = 0;
if (compressed_file_size == 0) {
close(pipe_into_cmd[1]);
write_pipe_done = 1;
}
} else if (write_ret == -1) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
write_again = 1;
} else {
// Error.
return 1;
}
} else {
// Should be unreachable, error.
return 1;
}
}
}
// Read output from decompressor and write to file.
if (!read_pipe_done) {
ssize_t read_ret = read(pipe_outof_cmd[0], recv_buf, 1024);
if (read_ret > 0) {
size_t fwrite_ret = fwrite(recv_buf, 1, read_ret, out_f);
if (fwrite_ret == (size_t)read_ret) {
// Success.
} else if (ferror(out_f)) {
// Error.
return 1;
} else {
// Invalid state, error.
return 1;
}
} else if (read_ret == -1) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
// No bytes to read yet.
} else {
// Error.
return 1;
}
} else if (read_ret == 0) {
// EOF.
read_pipe_done = 1;
close(pipe_outof_cmd[0]);
free_FILE_helper(&out_f);
} else {
// Invalid state (unreachable?), error.
return 1;
}
}
}
waitpid(decompressor_pid, NULL, 0);
fprintf(stderr, " Extracted.\n");
} else {
while (u64 != 0) {
if (u64 > 1024) {
ssize_t read_ret = fread(buf, 1, 1024, in_f);
if (read_ret > 0) {
u64 -= read_ret;
} else if (ferror(in_f)) {
return 1;
}
} else {
ssize_t read_ret = fread(buf, 1, u64, in_f);
if (read_ret > 0) {
u64 -= read_ret;
} else if (ferror(in_f)) {
return 1;
}
} }
u64 = 0;
} }
} }
} else { } else {

View file

@ -53,7 +53,8 @@ int simple_archiver_write_all(FILE *out_f, SDArchiverState *state,
const SDArchiverLinkedList *filenames); const SDArchiverLinkedList *filenames);
/// Returns zero on success. /// Returns zero on success.
int simple_archiver_print_archive_info(FILE *in_f); int simple_archiver_parse_archive_info(FILE *in_f, int do_extract,
const SDArchiverState *state);
/// Returns zero on success. /// Returns zero on success.
int simple_archiver_de_compress(int pipe_fd_in[2], int pipe_fd_out[2], int simple_archiver_de_compress(int pipe_fd_in[2], int pipe_fd_out[2],

View file

@ -22,6 +22,24 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include "platforms.h"
#if SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_COSMOPOLITAN || \
SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_MAC || \
SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_LINUX
#include <errno.h>
#include <fcntl.h>
#include <libgen.h>
#include <sys/stat.h>
#include <unistd.h>
#endif
void simple_archiver_internal_free_c_string(char **str) {
if (str && *str) {
free(*str);
*str = NULL;
}
}
int simple_archiver_helper_is_big_endian(void) { int simple_archiver_helper_is_big_endian(void) {
union { union {
uint32_t i; uint32_t i;
@ -128,3 +146,48 @@ void simple_archiver_helper_cmd_string_argv_free_ptr(char ***argv_strs) {
*argv_strs = NULL; *argv_strs = NULL;
} }
} }
int simple_archiver_helper_make_dirs(const char *file_path) {
#if SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_COSMOPOLITAN || \
SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_MAC || \
SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_LINUX
__attribute__((
cleanup(simple_archiver_internal_free_c_string))) char *path_dup =
strdup(file_path);
if (!path_dup) {
return 3;
}
const char *dir = dirname(path_dup);
if (strcmp(dir, "/") == 0 || strcmp(dir, ".") == 0) {
// At root.
return 0;
}
int dir_fd = open(dir, O_RDONLY | O_DIRECTORY);
if (dir_fd == -1) {
if (errno == ENOTDIR) {
// Error, somehow got non-dir in path.
return 1;
} else {
// Directory does not exist. Check parent dir first.
int ret = simple_archiver_helper_make_dirs(dir);
if (ret != 0) {
return ret;
}
// Now make dir.
ret = mkdir(dir, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
if (ret != 0) {
// Error.
return 2;
}
}
} else {
// Exists.
close(dir_fd);
}
return 0;
#else
return 1;
#endif
}

View file

@ -43,4 +43,7 @@ char **simple_archiver_helper_cmd_string_to_argv(const char *cmd);
void simple_archiver_helper_cmd_string_argv_free(char **argv_strs); void simple_archiver_helper_cmd_string_argv_free(char **argv_strs);
void simple_archiver_helper_cmd_string_argv_free_ptr(char ***argv_strs); void simple_archiver_helper_cmd_string_argv_free_ptr(char ***argv_strs);
/// Returns zero on success.
int simple_archiver_helper_make_dirs(const char *file_path);
#endif #endif

View file

@ -83,10 +83,25 @@ int main(int argc, const char **argv) {
return 3; return 3;
} }
if (simple_archiver_print_archive_info(file) != 0) { if (simple_archiver_parse_archive_info(file, 0, NULL) != 0) {
fprintf(stderr, "Error during archive checking/examining.\n"); fprintf(stderr, "Error during archive checking/examining.\n");
} }
fclose(file); fclose(file);
} else if ((parsed.flags & 3) == 1) {
FILE *file = fopen(parsed.filename, "rb");
if (!file) {
fprintf(stderr, "ERROR: Failed to open \"%s\" for reading!\n",
parsed.filename);
return 3;
}
__attribute__((cleanup(simple_archiver_free_state)))
SDArchiverState *state = simple_archiver_init_state(&parsed);
if (simple_archiver_parse_archive_info(file, 1, state) != 0) {
fprintf(stderr, "Error during archive extracting.\n");
}
fclose(file);
} }
return 0; return 0;

View file

@ -143,7 +143,11 @@ void simple_archiver_print_usage(void) {
"--compressor <full_compress_cmd> : requires --decompressor\n"); "--compressor <full_compress_cmd> : requires --decompressor\n");
fprintf(stderr, fprintf(stderr,
"--decompressor <full_decompress_cmd> : requires --compressor\n"); "--decompressor <full_decompress_cmd> : requires --compressor\n");
fprintf(stderr,
" Specifying \"--decompressor\" when extracting overrides archive "
"file's stored decompressor\n");
fprintf(stderr, "--overwrite-create : allows overwriting an archive file\n"); fprintf(stderr, "--overwrite-create : allows overwriting an archive file\n");
fprintf(stderr, "--overwrite-extract : allows overwriting when extracting\n");
fprintf(stderr, fprintf(stderr,
"-- : specifies remaining arguments are files to archive/extract\n"); "-- : specifies remaining arguments are files to archive/extract\n");
fprintf( fprintf(
@ -225,6 +229,8 @@ int simple_archiver_parse_args(int argc, const char **argv,
++argv; ++argv;
} else if (strcmp(argv[0], "--overwrite-create") == 0) { } else if (strcmp(argv[0], "--overwrite-create") == 0) {
out->flags |= 0x4; out->flags |= 0x4;
} else if (strcmp(argv[0], "--overwrite-extract") == 0) {
out->flags |= 0x8;
} else if (argv[0][0] == '-' && argv[0][1] == '-' && argv[0][2] == 0) { } else if (argv[0][0] == '-' && argv[0][1] == '-' && argv[0][2] == 0) {
is_remaining_args = 1; is_remaining_args = 1;
} else if (argv[0][0] != '-') { } else if (argv[0][0] != '-') {

View file

@ -28,6 +28,7 @@ typedef struct SDArchiverParsed {
/// 0b xxxx xx10 - is checking/examining. /// 0b xxxx xx10 - is checking/examining.
/// 0b xxxx x0xx - Do NOT allow create archive overwrite. /// 0b xxxx x0xx - Do NOT allow create archive overwrite.
/// 0b xxxx x1xx - Allow create archive overwrite. /// 0b xxxx x1xx - Allow create archive overwrite.
/// 0b xxxx 1xxx - Allow extract overwrite.
unsigned int flags; unsigned int flags;
/// Null-terminated string. /// Null-terminated string.
char *filename; char *filename;