]> git.seodisparate.com - SimpleArchiver/commitdiff
Impl. simple test/extract new file format (WIP)
authorStephen Seo <seo.disparate@gmail.com>
Fri, 27 Sep 2024 06:40:11 +0000 (15:40 +0900)
committerStephen Seo <seo.disparate@gmail.com>
Wed, 2 Oct 2024 06:11:23 +0000 (15:11 +0900)
TODO:
    Extract symlinks in new format (implemented but untested).
    Extract compressed files in new format.

file_format.md
file_format_1_example_0 [new file with mode: 0644]
src/archiver.c

index 6558f57118163f53f5358603e01162a512e32b4f..0ee7d633d156b505336be218db0436048112fd72 100644 (file)
@@ -120,14 +120,18 @@ Following the link-count bytes, the following bytes are added for each symlink:
            absolute links are preferred.
     2. The second byte.
         1. Currently unused.
-2. 2 bytes is 16-bit unsigned integer "link target absolute path" in
+2. 2 bytes 16-bit unsigned integer "link name" in big-endian. This does not
+   include the NULL at the end of the string. Must not be zero.
+3. X bytes of link-name (length defined by previous value). Is a NULL-terminated
+   string.
+4. 2 bytes is 16-bit unsigned integer "link target absolute path" in
    big-endian. This does not include the NULL at the end of the string.
-3. X bytes of link-target-absolute-path (length defined by previous value).
+5. X bytes of link-target-absolute-path (length defined by previous value).
    Is a NULL-terminated string. If the previous "size" value is 0, then
    this entry does not exist and should be skipped.
-4. 2 bytes is 16-bit unsigned integer "link target relative path" in
+6. 2 bytes is 16-bit unsigned integer "link target relative path" in
    big-endian. This does not include the NULL at the end of the string.
-5. X bytes of link-target-relative-path (length defined by previous value).
+7. X bytes of link-target-relative-path (length defined by previous value).
    Is a NULL-terminated string. If the previous "size" value is 0, then
    this entry does not exist and should be skipped.
 
diff --git a/file_format_1_example_0 b/file_format_1_example_0
new file mode 100644 (file)
index 0000000..f28c89e
Binary files /dev/null and b/file_format_1_example_0 differ
index 700ada8e7e5116f6fe7b67f9a912bf0e24787bc8..fe662ebe2b419a8ee69d1884644d45628bd004e8 100644 (file)
@@ -60,6 +60,14 @@ typedef struct SDArchiverInternalToWrite {
   uint64_t size;
 } SDArchiverInternalToWrite;
 
+typedef struct SDArchiverInternalFileInfo {
+  char *filename;
+  uint8_t bit_flags[4];
+  uint32_t uid;
+  uint32_t gid;
+  uint64_t file_size;
+} SDArchiverInternalFileInfo;
+
 void free_internal_to_write(void *data) {
   SDArchiverInternalToWrite *to_write = data;
   free(to_write->buf);
@@ -977,6 +985,155 @@ int filenames_to_abs_map_fn(void *data, void *ud) {
   return 0;
 }
 
+int read_buf_full_from_fd(FILE *fd, char *read_buf, const size_t read_buf_size,
+                          const size_t amount_total, char *dst_buf) {
+  size_t amount = amount_total;
+  while (amount != 0) {
+    if (amount >= read_buf_size) {
+      if (fread(read_buf, 1, read_buf_size, fd) != read_buf_size) {
+        return SDAS_INVALID_FILE;
+      }
+      if (dst_buf) {
+        memcpy(dst_buf + (amount_total - amount), read_buf, read_buf_size);
+      }
+      amount -= read_buf_size;
+    } else {
+      if (fread(read_buf, 1, amount, fd) != amount) {
+        return SDAS_INVALID_FILE;
+      }
+      if (dst_buf) {
+        memcpy(dst_buf + (amount_total - amount), read_buf, amount);
+      }
+      amount = 0;
+    }
+  }
+
+  return SDAS_SUCCESS;
+}
+
+int read_fd_to_out_fd(FILE *in_fd, FILE *out_fd, char *read_buf,
+                      const size_t read_buf_size, const size_t amount_total) {
+  size_t amount = amount_total;
+  while (amount != 0) {
+    if (amount >= read_buf_size) {
+      if (fread(read_buf, 1, read_buf_size, in_fd) != read_buf_size) {
+        return SDAS_INVALID_FILE;
+      } else if (fwrite(read_buf, 1, read_buf_size, out_fd) != read_buf_size) {
+        return SDAS_FAILED_TO_WRITE;
+      }
+      amount -= read_buf_size;
+    } else {
+      if (fread(read_buf, 1, amount, in_fd) != amount) {
+        return SDAS_INVALID_FILE;
+      } else if (fwrite(read_buf, 1, amount, out_fd) != amount) {
+        return SDAS_FAILED_TO_WRITE;
+      }
+      amount = 0;
+    }
+  }
+  return SDAS_SUCCESS;
+}
+
+void free_internal_file_info(void *data) {
+  SDArchiverInternalFileInfo *file_info = data;
+  if (file_info) {
+    if (file_info->filename) {
+      free(file_info->filename);
+    }
+    free(file_info);
+  }
+}
+
+void cleanup_internal_file_info(SDArchiverInternalFileInfo **file_info) {
+  if (file_info && *file_info) {
+    if ((*file_info)->filename) {
+      free((*file_info)->filename);
+    }
+    free(*file_info);
+    *file_info = NULL;
+  }
+}
+
+mode_t permissions_from_bits_version_1(const uint8_t flags[4],
+                                       uint_fast8_t print) {
+  mode_t permissions = 0;
+
+  if ((flags[0] & 1) != 0) {
+    permissions |= S_IRUSR;
+    if (print) {
+      fprintf(stderr, "r");
+    }
+  } else if (print) {
+    fprintf(stderr, "-");
+  }
+  if ((flags[0] & 2) != 0) {
+    permissions |= S_IWUSR;
+    if (print) {
+      fprintf(stderr, "w");
+    }
+  } else if (print) {
+    fprintf(stderr, "-");
+  }
+  if ((flags[0] & 4) != 0) {
+    permissions |= S_IXUSR;
+    if (print) {
+      fprintf(stderr, "x");
+    }
+  } else if (print) {
+    fprintf(stderr, "-");
+  }
+  if ((flags[0] & 8) != 0) {
+    permissions |= S_IRGRP;
+    if (print) {
+      fprintf(stderr, "r");
+    }
+  } else if (print) {
+    fprintf(stderr, "-");
+  }
+  if ((flags[0] & 0x10) != 0) {
+    permissions |= S_IWGRP;
+    if (print) {
+      fprintf(stderr, "w");
+    }
+  } else if (print) {
+    fprintf(stderr, "-");
+  }
+  if ((flags[0] & 0x20) != 0) {
+    permissions |= S_IXGRP;
+    if (print) {
+      fprintf(stderr, "x");
+    }
+  } else if (print) {
+    fprintf(stderr, "-");
+  }
+  if ((flags[0] & 0x40) != 0) {
+    permissions |= S_IROTH;
+    if (print) {
+      fprintf(stderr, "r");
+    }
+  } else if (print) {
+    fprintf(stderr, "-");
+  }
+  if ((flags[0] & 0x80) != 0) {
+    permissions |= S_IWOTH;
+    if (print) {
+      fprintf(stderr, "w");
+    }
+  } else if (print) {
+    fprintf(stderr, "-");
+  }
+  if ((flags[1] & 1) != 0) {
+    permissions |= S_IXOTH;
+    if (print) {
+      fprintf(stderr, "x");
+    }
+  } else if (print) {
+    fprintf(stderr, "-");
+  }
+
+  return permissions;
+}
+
 char *simple_archiver_error_to_string(enum SDArchiverStateReturns error) {
   switch (error) {
     case SDAS_SUCCESS:
@@ -2019,9 +2176,305 @@ int simple_archiver_parse_archive_version_0(FILE *in_f, int_fast8_t do_extract,
 
 int simple_archiver_parse_archive_version_1(FILE *in_f, int_fast8_t do_extract,
                                             const SDArchiverState *state) {
-  // TODO Implement this.
-  fprintf(stderr, "ERROR Handling archive version 1 is unimplemented!\n");
-  return SDAS_INTERNAL_ERROR;
+  uint8_t buf[1024];
+  memset(buf, 0, 1024);
+  uint16_t u16;
+  uint32_t u32;
+  uint64_t u64;
+
+  if (fread(buf, 1, 4, in_f) != 4) {
+    return SDAS_INVALID_FILE;
+  }
+
+  if (do_extract && state->parsed->user_cwd) {
+#if SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_COSMOPOLITAN || \
+    SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_MAC ||          \
+    SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_LINUX
+    if (chdir(state->parsed->user_cwd)) {
+      return SDAS_FAILED_TO_CHANGE_CWD;
+    }
+#endif
+  }
+
+  const int_fast8_t is_compressed = (buf[0] & 1) ? 1 : 0;
+
+  __attribute__((cleanup(
+      simple_archiver_helper_cleanup_c_string))) char *compressor_cmd = NULL;
+  __attribute__((cleanup(
+      simple_archiver_helper_cleanup_c_string))) char *decompressor_cmd = NULL;
+
+  if (is_compressed) {
+    if (fread(buf, 1, 2, in_f) != 2) {
+      return SDAS_INVALID_FILE;
+    }
+    memcpy(&u16, buf, 2);
+    simple_archiver_helper_16_bit_be(&u16);
+    compressor_cmd = malloc(u16 + 1);
+    int ret =
+        read_buf_full_from_fd(in_f, (char *)buf, 1024, u16 + 1, compressor_cmd);
+    if (ret != SDAS_SUCCESS) {
+      return ret;
+    }
+    compressor_cmd[u16] = 0;
+
+    if (fread(buf, 1, 2, in_f) != 2) {
+      return SDAS_INVALID_FILE;
+    }
+    memcpy(&u16, buf, 2);
+    simple_archiver_helper_16_bit_be(&u16);
+    decompressor_cmd = malloc(u16 + 1);
+    ret = read_buf_full_from_fd(in_f, (char *)buf, 1024, u16 + 1,
+                                decompressor_cmd);
+    if (ret != SDAS_SUCCESS) {
+      return ret;
+    }
+    decompressor_cmd[u16] = 0;
+  }
+
+  // Link count.
+  if (fread(buf, 1, 4, in_f) != 4) {
+    return SDAS_INVALID_FILE;
+  }
+  memcpy(&u32, buf, 4);
+  simple_archiver_helper_32_bit_be(&u32);
+
+  for (uint32_t idx = 0; idx < u32; ++idx) {
+    fprintf(stderr, "SYMLINK %3u of %3u\n", idx + 1, u32);
+    if (fread(buf, 1, 2, in_f) != 2) {
+      return SDAS_INVALID_FILE;
+    }
+    const uint_fast8_t absolute_preferred = (buf[0] & 1) ? 1 : 0;
+    uint_fast8_t link_extracted = 0;
+
+    if (fread(buf, 1, 2, in_f) != 2) {
+      return SDAS_INVALID_FILE;
+    }
+    memcpy(&u16, buf, 2);
+    simple_archiver_helper_16_bit_be(&u16);
+
+    __attribute__((
+        cleanup(simple_archiver_helper_cleanup_c_string))) char *link_name =
+        malloc(u16 + 1);
+
+    int ret =
+        read_buf_full_from_fd(in_f, (char *)buf, 1024, u16 + 1, link_name);
+    if (ret != SDAS_SUCCESS) {
+      return ret;
+    }
+
+    if (fread(buf, 1, 2, in_f) != 2) {
+      return SDAS_INVALID_FILE;
+    }
+    memcpy(&u16, buf, 2);
+    simple_archiver_helper_16_bit_be(&u16);
+    if (u16 != 0) {
+      __attribute__((
+          cleanup(simple_archiver_helper_cleanup_c_string))) char *path =
+          malloc(u16 + 1);
+      ret = read_buf_full_from_fd(in_f, (char *)buf, 1024, u16 + 1, path);
+      if (ret != SDAS_SUCCESS) {
+        return ret;
+      }
+      path[u16] = 0;
+      if (do_extract && absolute_preferred) {
+#if SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_COSMOPOLITAN || \
+    SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_MAC ||          \
+    SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_LINUX
+        simple_archiver_helper_make_dirs(link_name);
+        ret = symlink(path, link_name);
+        if (ret == -1) {
+          return SDAS_FAILED_TO_EXTRACT_SYMLINK;
+        }
+        link_extracted = 1;
+        fprintf(stderr, "  %s -> %s\n", link_name, path);
+#endif
+      }
+    }
+
+    if (fread(buf, 1, 2, in_f) != 2) {
+      return SDAS_INVALID_FILE;
+    }
+    memcpy(&u16, buf, 2);
+    simple_archiver_helper_16_bit_be(&u16);
+    if (u16 != 0) {
+      __attribute__((
+          cleanup(simple_archiver_helper_cleanup_c_string))) char *path =
+          malloc(u16 + 1);
+      ret = read_buf_full_from_fd(in_f, (char *)buf, 1024, u16 + 1, path);
+      if (ret != SDAS_SUCCESS) {
+        return ret;
+      }
+      path[u16] = 0;
+      if (do_extract && !absolute_preferred) {
+#if SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_COSMOPOLITAN || \
+    SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_MAC ||          \
+    SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_LINUX
+        simple_archiver_helper_make_dirs(link_name);
+        ret = symlink(path, link_name);
+        if (ret == -1) {
+          return SDAS_FAILED_TO_EXTRACT_SYMLINK;
+        }
+        link_extracted = 1;
+        fprintf(stderr, "  %s -> %s\n", link_name, path);
+#endif
+      }
+    }
+
+    if (!link_extracted) {
+      fprintf(stderr, "WARNING Symlink \"%s\" was not created!\n", link_name);
+    }
+  }
+
+  if (fread(buf, 1, 4, in_f) != 4) {
+    return SDAS_INVALID_FILE;
+  }
+  memcpy(&u32, buf, 4);
+  simple_archiver_helper_32_bit_be(&u32);
+
+  const uint32_t chunk_count = u32;
+  for (uint32_t chunk_idx = 0; chunk_idx < chunk_count; ++chunk_idx) {
+    fprintf(stderr, "CHUNK %3u of %3u\n", chunk_idx + 1, chunk_count);
+
+    if (fread(buf, 1, 2, in_f) != 2) {
+      return SDAS_INVALID_FILE;
+    }
+    memcpy(&u16, buf, 2);
+    simple_archiver_helper_16_bit_be(&u16);
+
+    const uint16_t file_count = u16;
+
+    __attribute__((cleanup(simple_archiver_list_free)))
+    SDArchiverLinkedList *file_info_list = simple_archiver_list_init();
+
+    __attribute__((cleanup(cleanup_internal_file_info)))
+    SDArchiverInternalFileInfo *file_info = NULL;
+
+    for (uint16_t file_idx = 0; file_idx < file_count; ++file_idx) {
+      file_info = malloc(sizeof(SDArchiverInternalFileInfo));
+      memset(file_info, 0, sizeof(SDArchiverInternalFileInfo));
+
+      if (fread(buf, 1, 2, in_f) != 2) {
+        return SDAS_INVALID_FILE;
+      }
+      memcpy(&u16, buf, 2);
+      simple_archiver_helper_16_bit_be(&u16);
+
+      file_info->filename = malloc(u16 + 1);
+      int ret = read_buf_full_from_fd(in_f, (char *)buf, 1024, u16 + 1,
+                                      file_info->filename);
+      if (ret != SDAS_SUCCESS) {
+        return ret;
+      }
+      file_info->filename[u16] = 0;
+
+      if (fread(file_info->bit_flags, 1, 4, in_f) != 4) {
+        return SDAS_INVALID_FILE;
+      }
+
+      if (fread(buf, 1, 4, in_f) != 4) {
+        return SDAS_INVALID_FILE;
+      }
+      memcpy(&u32, buf, 4);
+      simple_archiver_helper_32_bit_be(&u32);
+      file_info->uid = u32;
+
+      if (fread(buf, 1, 4, in_f) != 4) {
+        return SDAS_INVALID_FILE;
+      }
+      memcpy(&u32, buf, 4);
+      simple_archiver_helper_32_bit_be(&u32);
+      file_info->gid = u32;
+
+      if (fread(buf, 1, 8, in_f) != 8) {
+        return SDAS_INVALID_FILE;
+      }
+      memcpy(&u64, buf, 8);
+      simple_archiver_helper_64_bit_be(&u64);
+      file_info->file_size = u64;
+
+      simple_archiver_list_add(file_info_list, file_info,
+                               free_internal_file_info);
+      file_info = NULL;
+    }
+
+    if (fread(buf, 1, 8, in_f) != 8) {
+      return SDAS_INVALID_FILE;
+    }
+    memcpy(&u64, buf, 8);
+    simple_archiver_helper_64_bit_be(&u64);
+
+    const uint64_t chunk_size = u64;
+    uint64_t chunk_idx = 0;
+
+    SDArchiverLLNode *node = file_info_list->head;
+    uint16_t file_idx = 0;
+
+    if (is_compressed) {
+      fprintf(stderr, "ERROR Extracting compressed chunks is unimplemented!\n");
+      return SDAS_INTERNAL_ERROR;
+    } else {
+      while (node->next != file_info_list->tail) {
+        node = node->next;
+        const SDArchiverInternalFileInfo *file_info = node->data;
+        fprintf(stderr, "  FILE %3u of %3u\n", ++file_idx, file_count);
+        fprintf(stderr, "    Filename: %s\n", file_info->filename);
+        chunk_idx += file_info->file_size;
+        if (chunk_idx > chunk_size) {
+          fprintf(stderr, "ERROR Files in chunk is larger than chunk!\n");
+          return SDAS_INTERNAL_ERROR;
+        } else if (do_extract) {
+          mode_t permissions =
+              permissions_from_bits_version_1(file_info->bit_flags, 0);
+          if ((state->parsed->flags & 8) == 0) {
+            // Check if file already exists.
+            __attribute__((cleanup(simple_archiver_helper_cleanup_FILE)))
+            FILE *temp_fd = fopen(file_info->filename, "r");
+            if (temp_fd) {
+              fprintf(stderr,
+                      "  WARNING: File already exists and "
+                      "\"--overwrite-extract\" is not specified, skipping!\n");
+              int ret = read_buf_full_from_fd(in_f, (char *)buf, 1024,
+                                              file_info->file_size, NULL);
+              if (ret != SDAS_SUCCESS) {
+                return ret;
+              }
+              continue;
+            }
+          }
+          simple_archiver_helper_make_dirs(file_info->filename);
+          __attribute__((cleanup(simple_archiver_helper_cleanup_FILE)))
+          FILE *out_fd = fopen(file_info->filename, "wb");
+          int ret = read_fd_to_out_fd(in_f, out_fd, (char *)buf, 1024,
+                                      file_info->file_size);
+          if (ret != SDAS_SUCCESS) {
+            return ret;
+          }
+          simple_archiver_helper_cleanup_FILE(&out_fd);
+          if (chmod(file_info->filename, permissions) == -1) {
+            return SDAS_INTERNAL_ERROR;
+          }
+        } else {
+          fprintf(stderr, "    Permissions: ");
+          permissions_from_bits_version_1(file_info->bit_flags, 1);
+          fprintf(stderr, "\n    UID: %u\n    GID: %u\n", file_info->uid,
+                  file_info->gid);
+          if (is_compressed) {
+            fprintf(stderr, "    File size (compressed): %lu\n",
+                    file_info->file_size);
+          } else {
+            fprintf(stderr, "    File size: %lu\n", file_info->file_size);
+          }
+          int ret = read_buf_full_from_fd(in_f, (char *)buf, 1024,
+                                          file_info->file_size, NULL);
+          if (ret != SDAS_SUCCESS) {
+            return ret;
+          }
+        }
+      }
+    }
+  }
+
+  return SDAS_SUCCESS;
 }
 
 int simple_archiver_de_compress(int pipe_fd_in[2], int pipe_fd_out[2],