]> git.seodisparate.com - SimpleArchiver/commitdiff
Impl --prefix extract for file fmt version 3
authorStephen Seo <seo.disparate@gmail.com>
Mon, 27 Jan 2025 10:20:58 +0000 (19:20 +0900)
committerStephen Seo <seo.disparate@gmail.com>
Mon, 27 Jan 2025 10:20:58 +0000 (19:20 +0900)
TODO:
    Testing, cleanup, regression fixes.

src/archiver.c

index 9e729bde6dc582dbcbd1d9b50bdeefa21fb22e42..ff5da8628d6a623f4be6270f88ebcf2cbcebb513 100644 (file)
@@ -8246,6 +8246,10 @@ int simple_archiver_parse_archive_version_3(FILE *in_f, int_fast8_t do_extract,
     return SDAS_SIGINT;
   }
 
+  const size_t prefix_length = state && state->parsed->prefix
+                               ? strlen(state->parsed->prefix)
+                               : 0;
+
   // Link count.
   if (fread(buf, 1, 4, in_f) != 4) {
     return SDAS_INVALID_FILE;
@@ -8279,21 +8283,25 @@ int simple_archiver_parse_archive_version_3(FILE *in_f, int_fast8_t do_extract,
     uint_fast8_t skip_due_to_map = 0;
     uint_fast8_t skip_due_to_invalid = is_invalid ? 1 : 0;
 
-    if (fread(buf, 1, 2, in_f) != 2) {
+    if (fread(&u16, 2, 1, in_f) != 1) {
       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);
+    const size_t link_name_length = u16;
+
+    __attribute__((cleanup(simple_archiver_helper_cleanup_c_string)))
+    char *link_name = malloc(link_name_length + 1);
 
-    int ret = read_buf_full_from_fd(
-        in_f, (char *)buf, SIMPLE_ARCHIVER_BUFFER_SIZE, u16 + 1, link_name);
+    int ret = read_buf_full_from_fd(in_f,
+                                    (char *)buf,
+                                    SIMPLE_ARCHIVER_BUFFER_SIZE,
+                                    link_name_length + 1,
+                                    link_name);
     if (ret != SDAS_SUCCESS) {
       return ret;
     }
+    link_name[link_name_length] = 0;
 
     if (!do_extract) {
       fprintf(stderr, "  Link name: %s\n", link_name);
@@ -8311,6 +8319,15 @@ int simple_archiver_parse_archive_version_3(FILE *in_f, int_fast8_t do_extract,
 #endif
     }
 
+    __attribute__((cleanup(simple_archiver_helper_cleanup_c_string)))
+    char *link_name_prefixed = NULL;
+    if (state && state->parsed->prefix) {
+      link_name_prefixed = malloc(prefix_length + link_name_length + 1);
+      memcpy(link_name_prefixed, state->parsed->prefix, prefix_length);
+      memcpy(link_name_prefixed + prefix_length, link_name, link_name_length + 1);
+      link_name_prefixed[prefix_length + link_name_length] = 0;
+    }
+
     if (simple_archiver_validate_file_path(link_name)) {
       fprintf(stderr, "  WARNING: Invalid link name \"%s\"!\n", link_name);
       skip_due_to_invalid = 1;
@@ -8323,28 +8340,42 @@ int simple_archiver_parse_archive_version_3(FILE *in_f, int_fast8_t do_extract,
       fprintf(stderr, "  Skipping not specified in args...\n");
     }
 
-    if (fread(buf, 1, 2, in_f) != 2) {
+    if (fread(&u16, 2, 1, in_f) != 1) {
       return SDAS_INVALID_FILE;
     }
-    memcpy(&u16, buf, 2);
     simple_archiver_helper_16_bit_be(&u16);
 
+    __attribute__((cleanup(simple_archiver_helper_cleanup_c_string)))
+    char *abs_path_prefixed = NULL;
+
     __attribute__((cleanup(simple_archiver_helper_cleanup_c_string)))
     char *parsed_abs_path = NULL;
     if (u16 != 0) {
-      parsed_abs_path = malloc(u16 + 1);
+      const size_t path_length = u16;
+      parsed_abs_path = malloc(path_length + 1);
       ret = read_buf_full_from_fd(in_f,
                                   (char *)buf,
                                   SIMPLE_ARCHIVER_BUFFER_SIZE,
-                                  u16 + 1,
+                                  path_length + 1,
                                   parsed_abs_path);
       if (ret != SDAS_SUCCESS) {
         return ret;
       }
-      parsed_abs_path[u16] = 0;
+      parsed_abs_path[path_length] = 0;
       if (!do_extract) {
         fprintf(stderr, "  Abs path: %s\n", parsed_abs_path);
       }
+
+      if (state && state->parsed->prefix) {
+        abs_path_prefixed =
+          simple_archiver_helper_insert_prefix_in_link_path(
+            state->parsed->prefix, link_name, parsed_abs_path);
+        if (!abs_path_prefixed) {
+          fprintf(stderr,
+                  "ERROR: Failed to insert prefix to absolute path!\n");
+          return SDAS_INTERNAL_ERROR;
+        }
+      }
     } else if (!do_extract) {
       fprintf(stderr, "  No Absolute path.\n");
     }
@@ -8354,22 +8385,38 @@ int simple_archiver_parse_archive_version_3(FILE *in_f, int_fast8_t do_extract,
     }
     memcpy(&u16, buf, 2);
     simple_archiver_helper_16_bit_be(&u16);
+
+    __attribute__((cleanup(simple_archiver_helper_cleanup_c_string)))
+    char *rel_path_prefixed = NULL;
+
     __attribute__((cleanup(simple_archiver_helper_cleanup_c_string)))
     char *parsed_rel_path = NULL;
     if (u16 != 0) {
-      parsed_rel_path = malloc(u16 + 1);
+      const size_t path_length = u16;
+      parsed_rel_path = malloc(path_length + 1);
       ret = read_buf_full_from_fd(in_f,
                                   (char *)buf,
                                   SIMPLE_ARCHIVER_BUFFER_SIZE,
-                                  u16 + 1,
+                                  path_length + 1,
                                   parsed_rel_path);
       if (ret != SDAS_SUCCESS) {
         return ret;
       }
-      parsed_rel_path[u16] = 0;
+      parsed_rel_path[path_length] = 0;
       if (!do_extract) {
         fprintf(stderr, "  Rel path: %s\n", parsed_rel_path);
       }
+
+      if (state && state->parsed->prefix) {
+        rel_path_prefixed =
+          simple_archiver_helper_insert_prefix_in_link_path(
+            state->parsed->prefix, link_name, parsed_rel_path);
+        if (!rel_path_prefixed) {
+          fprintf(stderr,
+                  "ERROR: Failed to insert prefix to relative path!\n");
+          return SDAS_INTERNAL_ERROR;
+        }
+      }
     } else if (!do_extract) {
       fprintf(stderr, "  No Relative path.\n");
     }
@@ -8543,7 +8590,7 @@ int simple_archiver_parse_archive_version_3(FILE *in_f, int_fast8_t do_extract,
   SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_MAC ||          \
   SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_LINUX
       simple_archiver_helper_make_dirs_perms(
-        link_name,
+        link_name_prefixed ? link_name_prefixed : link_name,
         (state->parsed->flags & 0x2000)
           ? simple_archiver_internal_permissions_to_mode_t(
               state->parsed->dir_permissions)
@@ -8552,7 +8599,9 @@ int simple_archiver_parse_archive_version_3(FILE *in_f, int_fast8_t do_extract,
         (state->parsed->flags & 0x800) ? state->parsed->gid : current_gid);
       int_fast8_t link_create_retry = 0;
     V3_SYMLINK_CREATE_RETRY_0:
-      ret = symlink(parsed_abs_path, link_name);
+      ret = symlink(
+        abs_path_prefixed ? abs_path_prefixed : parsed_abs_path,
+        link_name_prefixed ? link_name_prefixed : link_name);
       if (ret == -1) {
         if (link_create_retry) {
           fprintf(
@@ -8571,14 +8620,17 @@ int simple_archiver_parse_archive_version_3(FILE *in_f, int_fast8_t do_extract,
                     "  NOTICE: Symlink already exists and "
                     "\"--overwrite-extract\" specified, attempting to "
                     "overwrite...\n");
-            unlink(link_name);
+            unlink(link_name_prefixed ? link_name_prefixed : link_name);
             link_create_retry = 1;
             goto V3_SYMLINK_CREATE_RETRY_0;
           }
         }
         return SDAS_FAILED_TO_EXTRACT_SYMLINK;
       }
-      ret = fchmodat(AT_FDCWD, link_name, permissions, AT_SYMLINK_NOFOLLOW);
+      ret = fchmodat(AT_FDCWD,
+                     link_name_prefixed ? link_name_prefixed : link_name,
+                     permissions,
+                     AT_SYMLINK_NOFOLLOW);
       if (ret == -1) {
         if (errno == EOPNOTSUPP) {
           fprintf(stderr,
@@ -8591,7 +8643,10 @@ int simple_archiver_parse_archive_version_3(FILE *in_f, int_fast8_t do_extract,
         }
       }
       link_extracted = 1;
-      fprintf(stderr, "  %s -> %s\n", link_name, parsed_abs_path);
+      fprintf(stderr,
+              "  %s -> %s\n",
+              link_name_prefixed ? link_name_prefixed : link_name,
+              abs_path_prefixed ? abs_path_prefixed : parsed_abs_path);
     V3_SYMLINK_CREATE_AFTER_0:
       link_create_retry = 1;
 #endif
@@ -8601,7 +8656,7 @@ int simple_archiver_parse_archive_version_3(FILE *in_f, int_fast8_t do_extract,
   SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_MAC ||          \
   SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_LINUX
       simple_archiver_helper_make_dirs_perms(
-        link_name,
+        link_name_prefixed ? link_name_prefixed : link_name,
         (state->parsed->flags & 0x2000)
           ? simple_archiver_internal_permissions_to_mode_t(
               state->parsed->dir_permissions)
@@ -8610,7 +8665,9 @@ int simple_archiver_parse_archive_version_3(FILE *in_f, int_fast8_t do_extract,
         (state->parsed->flags & 0x800) ? state->parsed->gid : current_gid);
       int_fast8_t link_create_retry = 0;
     V3_SYMLINK_CREATE_RETRY_1:
-      ret = symlink(parsed_rel_path, link_name);
+      ret = symlink(
+        rel_path_prefixed ? rel_path_prefixed : parsed_rel_path,
+        link_name_prefixed ? link_name_prefixed : link_name);
       if (ret == -1) {
         if (link_create_retry) {
           fprintf(
@@ -8629,14 +8686,17 @@ int simple_archiver_parse_archive_version_3(FILE *in_f, int_fast8_t do_extract,
                     "  NOTICE: Symlink already exists and "
                     "\"--overwrite-extract\" specified, attempting to "
                     "overwrite...\n");
-            unlink(link_name);
+            unlink(link_name_prefixed ? link_name_prefixed : link_name);
             link_create_retry = 1;
             goto V3_SYMLINK_CREATE_RETRY_1;
           }
         }
         return SDAS_FAILED_TO_EXTRACT_SYMLINK;
       }
-      ret = fchmodat(AT_FDCWD, link_name, permissions, AT_SYMLINK_NOFOLLOW);
+      ret = fchmodat(AT_FDCWD,
+                     link_name_prefixed ? link_name_prefixed : link_name,
+                     permissions,
+                     AT_SYMLINK_NOFOLLOW);
       if (ret == -1) {
         if (errno == EOPNOTSUPP) {
           fprintf(stderr,
@@ -8650,7 +8710,10 @@ int simple_archiver_parse_archive_version_3(FILE *in_f, int_fast8_t do_extract,
       }
 
       link_extracted = 1;
-      fprintf(stderr, "  %s -> %s\n", link_name, parsed_rel_path);
+      fprintf(stderr,
+              "  %s -> %s\n",
+              link_name_prefixed ? link_name_prefixed : link_name,
+              rel_path_prefixed ? rel_path_prefixed : parsed_rel_path);
     V3_SYMLINK_CREATE_AFTER_1:
       link_create_retry = 1;
 #endif
@@ -8727,7 +8790,7 @@ int simple_archiver_parse_archive_version_3(FILE *in_f, int_fast8_t do_extract,
       }
       ret = fchownat(
           AT_FDCWD,
-          link_name,
+          link_name_prefixed ? link_name_prefixed : link_name,
           state->parsed->flags & 0x400 ? state->parsed->uid : picked_uid,
           state->parsed->flags & 0x800 ? state->parsed->gid : picked_gid,
           AT_SYMLINK_NOFOLLOW);
@@ -9146,10 +9209,23 @@ int simple_archiver_parse_archive_version_3(FILE *in_f, int_fast8_t do_extract,
                 file_count,
                 file_info->filename);
 
+        const size_t filename_length = strlen(file_info->filename);
+
+        __attribute__((cleanup(simple_archiver_helper_cleanup_c_string)))
+        char *filename_prefixed = NULL;
+        if (state && state->parsed->prefix) {
+          filename_prefixed = malloc(prefix_length + filename_length + 1);
+          memcpy(filename_prefixed, state->parsed->prefix, prefix_length);
+          memcpy(filename_prefixed + prefix_length,
+                 file_info->filename,
+                 filename_length + 1);
+          filename_prefixed[prefix_length + filename_length] = 0;
+        }
+
         uint_fast8_t skip_due_to_map = 0;
         if (working_files_map && simple_archiver_hash_map_get(
                                      working_files_map, file_info->filename,
-                                     strlen(file_info->filename) + 1) == NULL) {
+                                     filename_length + 1) == NULL) {
           skip_due_to_map = 1;
           fprintf(stderr, "    Skipping not specified in args...\n");
         } else if ((file_info->other_flags & 1) != 0) {
@@ -9175,7 +9251,9 @@ int simple_archiver_parse_archive_version_3(FILE *in_f, int_fast8_t do_extract,
           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");
+            FILE *temp_fd = fopen(
+              filename_prefixed ? filename_prefixed : file_info->filename,
+              "r");
             if (temp_fd) {
               fprintf(stderr,
                       "  WARNING: File already exists and "
@@ -9193,7 +9271,7 @@ int simple_archiver_parse_archive_version_3(FILE *in_f, int_fast8_t do_extract,
           }
 
           simple_archiver_helper_make_dirs_perms(
-            file_info->filename,
+            filename_prefixed ? filename_prefixed : file_info->filename,
             (state->parsed->flags & 0x2000)
               ? simple_archiver_internal_permissions_to_mode_t(
                   state->parsed->dir_permissions)
@@ -9201,24 +9279,37 @@ int simple_archiver_parse_archive_version_3(FILE *in_f, int_fast8_t do_extract,
             (state->parsed->flags & 0x400) ? state->parsed->uid : file_info->uid,
             (state->parsed->flags & 0x800) ? state->parsed->gid : file_info->gid);
           int ret = read_decomp_to_out_file(
-              file_info->filename, pipe_outof_read, (char *)buf,
-              SIMPLE_ARCHIVER_BUFFER_SIZE, file_info->file_size,
-              &pipe_into_write, &chunk_remaining, in_f, hold_buf, &has_hold);
+              filename_prefixed ? filename_prefixed : file_info->filename,
+              pipe_outof_read,
+              (char *)buf,
+              SIMPLE_ARCHIVER_BUFFER_SIZE,
+              file_info->file_size,
+              &pipe_into_write,
+              &chunk_remaining,
+              in_f,
+              hold_buf,
+              &has_hold);
           if (ret != SDAS_SUCCESS) {
             return ret;
           }
 #if SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_COSMOPOLITAN || \
     SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_MAC ||          \
     SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_LINUX
-          if (chmod(file_info->filename, permissions) == -1) {
+          if (chmod(filename_prefixed ? filename_prefixed : file_info->filename,
+                    permissions)
+                == -1) {
             return SDAS_INTERNAL_ERROR;
           } else if (geteuid() == 0 &&
-                     chown(file_info->filename,
+                     chown(filename_prefixed
+                             ? filename_prefixed
+                             : file_info->filename,
                            file_info->uid,
                            file_info->gid) != 0) {
             fprintf(stderr,
                     "    ERROR Failed to set UID/GID of file \"%s\"!\n",
-                    file_info->filename);
+                    filename_prefixed
+                      ? filename_prefixed
+                      : file_info->filename);
             return SDAS_INTERNAL_ERROR;
           }
 #endif
@@ -9294,10 +9385,23 @@ int simple_archiver_parse_archive_version_3(FILE *in_f, int_fast8_t do_extract,
           return SDAS_INTERNAL_ERROR;
         }
 
+        const size_t filename_length = strlen(file_info->filename);
+
+        __attribute__((cleanup(simple_archiver_helper_cleanup_c_string)))
+        char *filename_prefixed = NULL;
+        if (state && state->parsed->prefix) {
+          filename_prefixed = malloc(prefix_length + filename_length + 1);
+          memcpy(filename_prefixed, state->parsed->prefix, prefix_length);
+          memcpy(filename_prefixed + prefix_length,
+                 file_info->filename,
+                 filename_length + 1);
+          filename_prefixed[prefix_length + filename_length] = 0;
+        }
+
         uint_fast8_t skip_due_to_map = 0;
         if (working_files_map && simple_archiver_hash_map_get(
                                      working_files_map, file_info->filename,
-                                     strlen(file_info->filename) + 1) == NULL) {
+                                     filename_length + 1) == NULL) {
           skip_due_to_map = 1;
           fprintf(stderr, "    Skipping not specified in args...\n");
         } else if (file_info->other_flags & 1) {
@@ -9325,7 +9429,10 @@ int simple_archiver_parse_archive_version_3(FILE *in_f, int_fast8_t do_extract,
           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");
+            FILE *temp_fd = fopen(filename_prefixed
+                                    ? filename_prefixed
+                                    : file_info->filename,
+                                  "r");
             if (temp_fd) {
               fprintf(stderr,
                       "  WARNING: File already exists and "
@@ -9340,7 +9447,7 @@ int simple_archiver_parse_archive_version_3(FILE *in_f, int_fast8_t do_extract,
             }
           }
           simple_archiver_helper_make_dirs_perms(
-            file_info->filename,
+            filename_prefixed ? filename_prefixed : file_info->filename,
             (state->parsed->flags & 0x2000)
               ? simple_archiver_internal_permissions_to_mode_t(
                   state->parsed->dir_permissions)
@@ -9348,7 +9455,10 @@ int simple_archiver_parse_archive_version_3(FILE *in_f, int_fast8_t do_extract,
             (state->parsed->flags & 0x400) ? state->parsed->uid : file_info->uid,
             (state->parsed->flags & 0x800) ? state->parsed->gid : file_info->gid);
           __attribute__((cleanup(simple_archiver_helper_cleanup_FILE)))
-          FILE *out_fd = fopen(file_info->filename, "wb");
+          FILE *out_fd = fopen(filename_prefixed
+                                 ? filename_prefixed
+                                 : file_info->filename,
+                               "wb");
           int ret = read_fd_to_out_fd(in_f, out_fd, (char *)buf,
                                       SIMPLE_ARCHIVER_BUFFER_SIZE,
                                       file_info->file_size);
@@ -9359,18 +9469,24 @@ int simple_archiver_parse_archive_version_3(FILE *in_f, int_fast8_t do_extract,
 #if SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_COSMOPOLITAN || \
     SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_MAC ||          \
     SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_LINUX
-          if (chmod(file_info->filename, permissions) == -1) {
-            fprintf(stderr,
-                    "ERROR Failed to set permissions of file \"%s\"!\n",
-                    file_info->filename);
+          if (chmod(filename_prefixed ? filename_prefixed : file_info->filename,
+                    permissions)
+                == -1) {
+            fprintf(
+              stderr,
+              "ERROR Failed to set permissions of file \"%s\"!\n",
+              filename_prefixed ? filename_prefixed : file_info->filename);
             return SDAS_INTERNAL_ERROR;
           } else if (geteuid() == 0 &&
-                     chown(file_info->filename,
+                     chown(filename_prefixed
+                             ? filename_prefixed
+                             : file_info->filename,
                            file_info->uid,
                            file_info->gid) != 0) {
-            fprintf(stderr,
-                    "    ERROR Failed to set UID/GID of file \"%s\"!\n",
-                    file_info->filename);
+            fprintf(
+              stderr,
+              "    ERROR Failed to set UID/GID of file \"%s\"!\n",
+              filename_prefixed ? filename_prefixed : file_info->filename);
             return SDAS_INTERNAL_ERROR;
           }
 #endif
@@ -9663,6 +9779,11 @@ int simple_archiver_parse_archive_version_3(FILE *in_f, int_fast8_t do_extract,
         "/",
         simple_archiver_helper_datastructure_cleanup_nop);
     }
+    if (state && state->parsed->prefix) {
+      simple_archiver_list_add(string_parts,
+                               strdup(state->parsed->prefix),
+                               NULL);
+    }
     simple_archiver_list_add(string_parts, strdup((char *)buf), NULL);
     simple_archiver_list_add(
       string_parts,