diff --git a/src/archiver.c b/src/archiver.c index 064232d..880412b 100644 --- a/src/archiver.c +++ b/src/archiver.c @@ -526,7 +526,8 @@ int simple_archiver_write_all(FILE *out_f, SDArchiverState *state, 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]; memset(buf, 0, 1024); uint16_t u16; @@ -547,6 +548,8 @@ int simple_archiver_print_archive_info(FILE *in_f) { return SDAS_INVALID_FILE; } + __attribute__((cleanup(free_malloced_memory))) void *decompressor_cmd = NULL; + if ((buf[0] & 1) != 0) { fprintf(stderr, "De/compressor flag is set.\n"); is_compressed = 1; @@ -586,6 +589,9 @@ int simple_archiver_print_archive_info(FILE *in_f) { } buf[1023] = 0; 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 { __attribute__((cleanup(free_malloced_memory))) void *heap_buf = malloc(u16 + 1); @@ -607,6 +613,7 @@ int simple_archiver_print_archive_info(FILE *in_f) { fprintf(stderr, "File count is %u\n", u32); uint32_t size = u32; + int skip = 0; for (uint32_t idx = 0; idx < size; ++idx) { fprintf(stderr, "\nFile %10u of %10u.\n", idx + 1, size); if (feof(in_f) || ferror(in_f)) { @@ -615,12 +622,33 @@ int simple_archiver_print_archive_info(FILE *in_f) { return SDAS_INVALID_FILE; } simple_archiver_helper_16_bit_be(&u16); + __attribute__((cleanup(free_FILE_helper))) FILE *out_f = NULL; if (u16 < 1024) { if (fread(buf, 1, u16 + 1, in_f) != (size_t)u16 + 1) { return SDAS_INVALID_FILE; } buf[1023] = 0; 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 { __attribute__((cleanup(free_malloced_memory))) void *heap_buf = malloc(u16 + 1); @@ -630,6 +658,26 @@ int simple_archiver_print_archive_info(FILE *in_f) { } uc_heap_buf[u16 - 1] = 0; 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) { @@ -647,17 +695,159 @@ int simple_archiver_print_archive_info(FILE *in_f) { } else { fprintf(stderr, " File size: %lu\n", u64); } - while (u64 > 0) { - if (u64 > 1024) { - if (fread(buf, 1, 1024, in_f) != 1024) { - return SDAS_INVALID_FILE; + + if (do_extract && !skip) { + fprintf(stderr, " Extracting...\n"); + 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 { - if (fread(buf, 1, u64, in_f) != u64) { - return SDAS_INVALID_FILE; + if (simple_archiver_de_compress(pipe_into_cmd, pipe_outof_cmd, + 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 { diff --git a/src/archiver.h b/src/archiver.h index da9499c..64f5ba8 100644 --- a/src/archiver.h +++ b/src/archiver.h @@ -53,7 +53,8 @@ int simple_archiver_write_all(FILE *out_f, SDArchiverState *state, const SDArchiverLinkedList *filenames); /// 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. int simple_archiver_de_compress(int pipe_fd_in[2], int pipe_fd_out[2], diff --git a/src/main.c b/src/main.c index a70970b..1200ce4 100644 --- a/src/main.c +++ b/src/main.c @@ -83,10 +83,25 @@ int main(int argc, const char **argv) { 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"); } 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; diff --git a/src/parser.c b/src/parser.c index 82eea63..db409d0 100644 --- a/src/parser.c +++ b/src/parser.c @@ -143,7 +143,11 @@ void simple_archiver_print_usage(void) { "--compressor : requires --decompressor\n"); fprintf(stderr, "--decompressor : 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-extract : allows overwriting when extracting\n"); fprintf(stderr, "-- : specifies remaining arguments are files to archive/extract\n"); fprintf( @@ -225,6 +229,8 @@ int simple_archiver_parse_args(int argc, const char **argv, ++argv; } else if (strcmp(argv[0], "--overwrite-create") == 0) { 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) { is_remaining_args = 1; } else if (argv[0][0] != '-') { diff --git a/src/parser.h b/src/parser.h index c9ebc34..40e8774 100644 --- a/src/parser.h +++ b/src/parser.h @@ -28,6 +28,7 @@ typedef struct SDArchiverParsed { /// 0b xxxx xx10 - is checking/examining. /// 0b xxxx x0xx - Do NOT allow create archive overwrite. /// 0b xxxx x1xx - Allow create archive overwrite. + /// 0b xxxx 1xxx - Allow extract overwrite. unsigned int flags; /// Null-terminated string. char *filename;