]> git.seodisparate.com - SimpleArchiver/commitdiff
Implement extracting from archive
authorStephen Seo <seo.disparate@gmail.com>
Thu, 18 Jul 2024 04:16:24 +0000 (13:16 +0900)
committerStephen Seo <seo.disparate@gmail.com>
Thu, 18 Jul 2024 04:27:32 +0000 (13:27 +0900)
TODO:
    Storing file permissions to be extracted with same permissions.
    Archiving and extracting symbolic links.

src/archiver.c
src/archiver.h
src/main.c
src/parser.c
src/parser.h

index 064232d05d0bbceeb006f4f1156e5eb80a44f5d2..880412b91bb883b20fb626b352dbf4e446214bb2 100644 (file)
@@ -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 {
index da9499c23b1418e67e2358894c30f05c59624cad..64f5ba86df6c5c4c3814f42cc9b5a79bedf10374 100644 (file)
@@ -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],
index a70970bc0e64e4b0d3f6698b1607d4d47c71e830..1200ce49aa5c705a551a3c2518220a5e79b7d34e 100644 (file)
@@ -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;
index 82eea638f95455bf56154a9cf9e5b220a2d476d7..db409d0659aa01fa80459c88cd4c6e39d6a668c2 100644 (file)
@@ -143,7 +143,11 @@ void simple_archiver_print_usage(void) {
           "--compressor <full_compress_cmd> : requires --decompressor\n");
   fprintf(stderr,
           "--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-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] != '-') {
index c9ebc34bc10c96b768696aac2634e747b09a9775..40e8774e731b421a863e9eeaf7295ee33b46ca91 100644 (file)
@@ -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;