]> git.seodisparate.com - SimpleArchiver/commitdiff
WIP --prefix: Impl --prefix for file v0 extracting
authorStephen Seo <seo.disparate@gmail.com>
Fri, 24 Jan 2025 09:07:18 +0000 (18:07 +0900)
committerStephen Seo <seo.disparate@gmail.com>
Fri, 24 Jan 2025 09:07:18 +0000 (18:07 +0900)
Minor fixes to helper code.

TODO:
    - file v1, v2, and v3 archiving and extracting with "--prefix"

src/archiver.c
src/helpers.c

index 8ef8dae6a9d07d902ce2961d8ed1998053028616..b0caad86e9536011aef54bc2693d643377dd5187 100644 (file)
@@ -5543,12 +5543,12 @@ int simple_archiver_parse_archive_version_0(FILE *in_f, int_fast8_t do_extract,
 
   __attribute__((cleanup(simple_archiver_list_free)))
   SDArchiverLinkedList *links_list =
-      state && state->parsed && state->parsed->flags & 0x80
+      state && state->parsed && (state->parsed->flags & 0x80)
           ? NULL
           : simple_archiver_list_init();
   __attribute__((cleanup(simple_archiver_hash_map_free)))
   SDArchiverHashMap *files_map =
-      state && state->parsed && state->parsed->flags & 0x80
+      state && state->parsed && (state->parsed->flags & 0x80)
           ? NULL
           : simple_archiver_hash_map_init();
 
@@ -5571,6 +5571,8 @@ int simple_archiver_parse_archive_version_0(FILE *in_f, int_fast8_t do_extract,
     __attribute__((cleanup(
         cleanup_overwrite_filename_delete_simple))) char *to_overwrite_dest =
         NULL;
+    __attribute__((cleanup(simple_archiver_helper_cleanup_c_string)))
+    char *filename_with_prefix = NULL;
     if (u16 < SIMPLE_ARCHIVER_BUFFER_SIZE) {
       if (fread(buf, 1, u16 + 1, in_f) != (size_t)u16 + 1) {
         return SDAS_INVALID_FILE;
@@ -5582,10 +5584,23 @@ int simple_archiver_parse_archive_version_0(FILE *in_f, int_fast8_t do_extract,
         skip = 1;
       }
 
+      if (state && state->parsed->prefix) {
+        const unsigned long buf_str_len = strlen((const char *)buf);
+        const unsigned long prefix_length = strlen(state->parsed->prefix);
+        filename_with_prefix = malloc(buf_str_len + prefix_length + 1);
+        memcpy(filename_with_prefix, state->parsed->prefix, prefix_length);
+        memcpy(filename_with_prefix + prefix_length, buf, buf_str_len);
+        filename_with_prefix[prefix_length + buf_str_len] = 0;
+      }
+
       if (do_extract && !skip) {
         if ((state->parsed->flags & 0x8) == 0) {
           __attribute__((cleanup(simple_archiver_helper_cleanup_FILE)))
-          FILE *test_fd = fopen((const char *)buf, "rb");
+          FILE *test_fd =
+            fopen(filename_with_prefix
+                    ? filename_with_prefix
+                    : (const char *)buf,
+                  "rb");
           if (test_fd) {
             skip = 1;
             fprintf(stderr,
@@ -5596,23 +5611,32 @@ int simple_archiver_parse_archive_version_0(FILE *in_f, int_fast8_t do_extract,
           }
         } else {
           skip = 0;
-          int fd = open((const char *)buf, O_RDONLY | O_NOFOLLOW);
+          int fd =
+            open(filename_with_prefix
+                   ? filename_with_prefix
+                   : (const char *)buf,
+                 O_RDONLY | O_NOFOLLOW);
           if (fd == -1) {
             if (errno == ELOOP) {
               // Is an existing symbolic file.
               // Defer deletion to after "is invalid" check.
-              to_overwrite_dest = strdup((const char *)buf);
+              to_overwrite_dest = strdup(filename_with_prefix
+                                         ? filename_with_prefix
+                                         : (const char *)buf);
             }
           } else {
             close(fd);
             // Is an existing file.
             // Defer deletion to after "is invalid" check.
-            to_overwrite_dest = strdup((const char *)buf);
+            to_overwrite_dest = strdup(filename_with_prefix
+                                       ? filename_with_prefix
+                                       : (const char *)buf);
           }
         }
         if (!skip) {
-          out_f_name = malloc(strlen((const char *)buf) + 1);
-          memcpy(out_f_name, buf, strlen((const char *)buf) + 1);
+          // Don't replace with "filename_with_prefix" here,
+          // original filename is needed later on.
+          out_f_name = strdup((const char *)buf);
         }
       }
     } else {
@@ -5631,10 +5655,22 @@ int simple_archiver_parse_archive_version_0(FILE *in_f, int_fast8_t do_extract,
         skip = 1;
       }
 
+      if (state && state->parsed->prefix) {
+        const unsigned long heap_buf_str_len = strlen((const char *)uc_heap_buf);
+        const unsigned long prefix_length = strlen(state->parsed->prefix);
+        filename_with_prefix = malloc(heap_buf_str_len + prefix_length + 1);
+        memcpy(filename_with_prefix, state->parsed->prefix, prefix_length);
+        memcpy(filename_with_prefix + prefix_length, uc_heap_buf, heap_buf_str_len);
+        filename_with_prefix[prefix_length + heap_buf_str_len] = 0;
+      }
+
       if (do_extract && !skip) {
         if ((state->parsed->flags & 0x8) == 0) {
           __attribute__((cleanup(simple_archiver_helper_cleanup_FILE)))
-          FILE *test_fd = fopen((const char *)uc_heap_buf, "rb");
+          FILE *test_fd = fopen(filename_with_prefix
+                                  ? filename_with_prefix
+                                  : (const char *)uc_heap_buf,
+                                "rb");
           if (test_fd) {
             skip = 1;
             fprintf(stderr,
@@ -5645,24 +5681,31 @@ int simple_archiver_parse_archive_version_0(FILE *in_f, int_fast8_t do_extract,
           }
         } else {
           skip = 0;
-          int fd = open((const char *)uc_heap_buf, O_RDONLY | O_NOFOLLOW);
+          int fd = open(filename_with_prefix
+                          ? filename_with_prefix
+                          : (const char *)uc_heap_buf,
+                        O_RDONLY | O_NOFOLLOW);
           if (fd == -1) {
             if (errno == ELOOP) {
               // Is an existing symbolic file.
               // Defer deletion to after "is invalid" check.
-              to_overwrite_dest = strdup((const char *)uc_heap_buf);
+              to_overwrite_dest = strdup(filename_with_prefix
+                                         ? filename_with_prefix
+                                         : (const char *)uc_heap_buf);
             }
           } else {
             close(fd);
             // Is an existing file.
             // Defer deletion to after "is invalid" check.
-            to_overwrite_dest = strdup((const char *)uc_heap_buf);
+            to_overwrite_dest = strdup(filename_with_prefix
+                                       ? filename_with_prefix
+                                       : (const char *)uc_heap_buf);
           }
         }
         if (!skip) {
-          out_f_name = malloc(strlen((const char *)uc_heap_buf) + 1);
-          memcpy(out_f_name, uc_heap_buf,
-                 strlen((const char *)uc_heap_buf) + 1);
+          // Don't replace with "filename_with_prefix" here,
+          // original filename is needed later on.
+          out_f_name = strdup((const char *)uc_heap_buf);
         }
       }
     }
@@ -5683,7 +5726,10 @@ int simple_archiver_parse_archive_version_0(FILE *in_f, int_fast8_t do_extract,
     }
 
     if (files_map && !skip && out_f_name) {
-      simple_archiver_internal_paths_to_files_map(files_map, out_f_name);
+      simple_archiver_internal_paths_to_files_map(files_map,
+                                                  filename_with_prefix
+                                                    ? filename_with_prefix
+                                                    : out_f_name);
     }
 
 #if SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_COSMOPOLITAN || \
@@ -5808,7 +5854,9 @@ int simple_archiver_parse_archive_version_0(FILE *in_f, int_fast8_t do_extract,
         fprintf(stderr, "  Extracting...\n");
 
         simple_archiver_helper_make_dirs_perms(
-          (const char *)out_f_name,
+          filename_with_prefix
+            ? filename_with_prefix
+            : (const char *)out_f_name,
           (state->parsed->flags & 0x2000)
             ? simple_archiver_internal_permissions_to_mode_t(
                 state->parsed->dir_permissions)
@@ -5816,17 +5864,24 @@ int simple_archiver_parse_archive_version_0(FILE *in_f, int_fast8_t do_extract,
           (state->parsed->flags & 0x400) ? state->parsed->uid : getuid(),
           (state->parsed->flags & 0x800) ? state->parsed->gid : getgid());
 
-        out_f = fopen(out_f_name, "wb");
+        out_f = fopen(filename_with_prefix
+                        ? filename_with_prefix
+                        : out_f_name,
+                      "wb");
         if (!out_f) {
           fprintf(stderr,
                   "WARNING: Failed to open \"%s\" for writing! (No write "
                   "permissions?)\n",
-                  (char *)out_f_name);
+                  filename_with_prefix
+                    ? filename_with_prefix
+                    : (char *)out_f_name);
         }
         __attribute__((
             cleanup(cleanup_temp_filename_delete))) void **ptrs_array =
             malloc(sizeof(void *) * 2);
-        ptrs_array[0] = out_f_name;
+        ptrs_array[0] = filename_with_prefix
+                          ? filename_with_prefix
+                          : out_f_name;
         ptrs_array[1] = &out_f;
 #if SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_COSMOPOLITAN || \
     SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_MAC ||          \
@@ -6080,7 +6135,10 @@ int simple_archiver_parse_archive_version_0(FILE *in_f, int_fast8_t do_extract,
           }
         }
 
-        if (chmod((const char *)out_f_name, permissions) == -1) {
+        if (chmod(filename_with_prefix
+                    ? filename_with_prefix
+                    : (const char *)out_f_name,
+                  permissions) == -1) {
           // Error.
           return SDAS_INTERNAL_ERROR;
         }
@@ -6169,7 +6227,9 @@ int simple_archiver_parse_archive_version_0(FILE *in_f, int_fast8_t do_extract,
 
       if (do_extract && !skip) {
         simple_archiver_helper_make_dirs_perms(
-          (const char *)out_f_name,
+          filename_with_prefix
+            ? filename_with_prefix
+            : (const char *)out_f_name,
           (state->parsed->flags & 0x2000)
             ? simple_archiver_internal_permissions_to_mode_t(
                 state->parsed->dir_permissions)
@@ -6183,8 +6243,21 @@ int simple_archiver_parse_archive_version_0(FILE *in_f, int_fast8_t do_extract,
     SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_LINUX
             int_fast8_t retry_symlink = 0;
             int ret;
+            __attribute__((cleanup(simple_archiver_helper_cleanup_c_string)))
+            char *abs_path_prefixed =
+              state->parsed->prefix
+              ? simple_archiver_helper_insert_prefix_in_link_path(
+                  state->parsed->prefix, out_f_name, abs_path)
+              : NULL;
+            if (filename_with_prefix && !abs_path_prefixed) {
+              fprintf(stderr, "  ERROR: Prefix specified but unable to resolve"
+                " abs link with prefix!\n");
+              return SDAS_FAILED_TO_EXTRACT_SYMLINK;
+            }
           V0_SYMLINK_CREATE_RETRY_0:
-            ret = symlink(abs_path, out_f_name);
+            ret = symlink(
+              abs_path_prefixed ? abs_path_prefixed : abs_path,
+              filename_with_prefix ? filename_with_prefix : out_f_name);
             if (ret == -1) {
               if (retry_symlink) {
                 fprintf(stderr,
@@ -6203,7 +6276,9 @@ int simple_archiver_parse_archive_version_0(FILE *in_f, int_fast8_t do_extract,
                           "  NOTICE: Symlink already exists and "
                           "\"--overwrite-extract\" specified, attempting to "
                           "overwrite...\n");
-                  unlink(out_f_name);
+                  unlink(filename_with_prefix
+                         ? filename_with_prefix
+                         : out_f_name);
                   retry_symlink = 1;
                   goto V0_SYMLINK_CREATE_RETRY_0;
                 }
@@ -6212,9 +6287,17 @@ int simple_archiver_parse_archive_version_0(FILE *in_f, int_fast8_t do_extract,
               }
             }
             if (links_list) {
-              simple_archiver_list_add(links_list, strdup(out_f_name), NULL);
+              simple_archiver_list_add(links_list,
+                                       filename_with_prefix
+                                         ? strdup(filename_with_prefix)
+                                         : strdup(out_f_name),
+                                       NULL);
             }
-            ret = fchmodat(AT_FDCWD, out_f_name, permissions,
+            ret = fchmodat(AT_FDCWD,
+                           filename_with_prefix
+                             ? filename_with_prefix
+                             : out_f_name,
+                           permissions,
                            AT_SYMLINK_NOFOLLOW);
             if (ret == -1) {
               if (errno == EOPNOTSUPP) {
@@ -6237,8 +6320,19 @@ int simple_archiver_parse_archive_version_0(FILE *in_f, int_fast8_t do_extract,
     SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_LINUX
             int_fast8_t retry_symlink = 0;
             int ret;
+            __attribute__((cleanup(simple_archiver_helper_cleanup_c_string)))
+            char *rel_path_prefixed =
+              simple_archiver_helper_insert_prefix_in_link_path(
+                state->parsed->prefix, out_f_name, rel_path);
+            if (filename_with_prefix && !rel_path_prefixed) {
+              fprintf(stderr, "  ERROR: Prefix specified but unable to resolve"
+                " relative link with prefix!\n");
+              return SDAS_FAILED_TO_EXTRACT_SYMLINK;
+            }
           V0_SYMLINK_CREATE_RETRY_1:
-            ret = symlink(rel_path, out_f_name);
+            ret = symlink(
+              rel_path_prefixed ? rel_path_prefixed : rel_path,
+              filename_with_prefix ? filename_with_prefix : out_f_name);
             if (ret == -1) {
               if (retry_symlink) {
                 fprintf(stderr,
@@ -6257,7 +6351,9 @@ int simple_archiver_parse_archive_version_0(FILE *in_f, int_fast8_t do_extract,
                           "  NOTICE: Symlink already exists and "
                           "\"--overwrite-extract\" specified, attempting to "
                           "overwrite...\n");
-                  unlink(out_f_name);
+                  unlink(filename_with_prefix
+                         ? filename_with_prefix
+                         : out_f_name);
                   retry_symlink = 1;
                   goto V0_SYMLINK_CREATE_RETRY_1;
                 }
@@ -6266,9 +6362,17 @@ int simple_archiver_parse_archive_version_0(FILE *in_f, int_fast8_t do_extract,
               }
             }
             if (links_list) {
-              simple_archiver_list_add(links_list, strdup(out_f_name), NULL);
+              simple_archiver_list_add(links_list,
+                                       filename_with_prefix
+                                         ? strdup(filename_with_prefix)
+                                         : strdup(out_f_name),
+                                       NULL);
             }
-            ret = fchmodat(AT_FDCWD, out_f_name, permissions,
+            ret = fchmodat(AT_FDCWD,
+                           filename_with_prefix
+                             ? filename_with_prefix
+                             : out_f_name,
+                           permissions,
                            AT_SYMLINK_NOFOLLOW);
             if (ret == -1) {
               if (errno == EOPNOTSUPP) {
@@ -6290,15 +6394,41 @@ int simple_archiver_parse_archive_version_0(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
-          int ret = symlink(abs_path, out_f_name);
+          __attribute__((cleanup(simple_archiver_helper_cleanup_c_string)))
+          char *abs_path_prefixed =
+            state->parsed->prefix
+            ? simple_archiver_helper_insert_prefix_in_link_path(
+                state->parsed->prefix, out_f_name, abs_path)
+            : NULL;
+          if (filename_with_prefix && !abs_path_prefixed) {
+            fprintf(stderr, "  ERROR: Prefix specified but unable to resolve"
+              " abs link with prefix!\n");
+            return SDAS_FAILED_TO_EXTRACT_SYMLINK;
+          }
+          int ret = symlink(
+            abs_path_prefixed
+              ? abs_path_prefixed
+              : abs_path,
+            filename_with_prefix
+              ? filename_with_prefix
+              : out_f_name);
           if (ret == -1) {
             return SDAS_FAILED_TO_EXTRACT_SYMLINK;
           }
           if (links_list) {
-            simple_archiver_list_add(links_list, strdup(out_f_name), NULL);
+            simple_archiver_list_add(
+              links_list,
+              filename_with_prefix
+                ? strdup(filename_with_prefix)
+                : strdup(out_f_name),
+              NULL);
           }
-          ret =
-              fchmodat(AT_FDCWD, out_f_name, permissions, AT_SYMLINK_NOFOLLOW);
+          ret = fchmodat(AT_FDCWD,
+                         filename_with_prefix
+                           ? filename_with_prefix
+                           : out_f_name,
+                         permissions,
+                         AT_SYMLINK_NOFOLLOW);
           if (ret == -1) {
             if (errno == EOPNOTSUPP) {
               fprintf(
@@ -6316,15 +6446,39 @@ int simple_archiver_parse_archive_version_0(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
-          int ret = symlink(rel_path, out_f_name);
+          __attribute__((cleanup(simple_archiver_helper_cleanup_c_string)))
+          char *rel_path_prefixed =
+            simple_archiver_helper_insert_prefix_in_link_path(
+              state->parsed->prefix, out_f_name, rel_path);
+          if (filename_with_prefix && !rel_path_prefixed) {
+            fprintf(stderr, "  ERROR: Prefix specified but unable to resolve"
+              " relative link with prefix!\n");
+            return SDAS_FAILED_TO_EXTRACT_SYMLINK;
+          }
+          int ret = symlink(
+            rel_path_prefixed
+              ? rel_path_prefixed
+              : rel_path,
+            filename_with_prefix
+              ? filename_with_prefix
+              : out_f_name);
           if (ret == -1) {
             return SDAS_FAILED_TO_EXTRACT_SYMLINK;
           }
           if (links_list) {
-            simple_archiver_list_add(links_list, strdup(out_f_name), NULL);
+            simple_archiver_list_add(
+              links_list,
+              filename_with_prefix
+                ? strdup(filename_with_prefix)
+                : strdup(out_f_name),
+              NULL);
           }
-          ret =
-              fchmodat(AT_FDCWD, out_f_name, permissions, AT_SYMLINK_NOFOLLOW);
+          ret = fchmodat(AT_FDCWD,
+                         filename_with_prefix
+                           ? filename_with_prefix
+                           : out_f_name,
+                         permissions,
+                         AT_SYMLINK_NOFOLLOW);
           if (ret == -1) {
             if (errno == EOPNOTSUPP) {
               fprintf(
index 3ad166b3d542e75c99ebfd2f6338eacfc47940b7..fa888d8130e77a562298015e4a5cf73cf8e90cd2 100644 (file)
@@ -385,6 +385,9 @@ uint16_t simple_archiver_helper_str_slash_count(const char *str) {
 char *simple_archiver_helper_insert_prefix_in_link_path(const char *prefix,
                                                         const char *link,
                                                         const char *path) {
+  if (!prefix) {
+    return NULL;
+  }
   uint16_t prefix_slash_count = simple_archiver_helper_str_slash_count(prefix);
   __attribute__((cleanup(simple_archiver_helper_cleanup_c_string)))
   char *cwd = getcwd(NULL, 0);
@@ -400,6 +403,7 @@ char *simple_archiver_helper_insert_prefix_in_link_path(const char *prefix,
     ++cwd_length;
   }
   const unsigned long prefix_length = strlen(prefix);
+  const unsigned long link_length = strlen(link);
   const unsigned long path_length = strlen(path);
   if (path[0] == '/') {
     // Dealing with an absolute path.
@@ -432,20 +436,20 @@ char *simple_archiver_helper_insert_prefix_in_link_path(const char *prefix,
     // Dealing with a relative path.
 
     // First check if "path" is in archive.
+    const unsigned long filename_full_length = cwd_length + link_length;
     __attribute__((cleanup(simple_archiver_helper_cleanup_c_string)))
-    char *filename_realpath = simple_archiver_helper_real_path_to_name(link);
-    if (!filename_realpath) {
-      return NULL;
-    }
-    const unsigned long filename_realpath_length = strlen(filename_realpath);
+    char *filename_full = malloc(filename_full_length + 1);
+    memcpy(filename_full, cwd, cwd_length);
+    memcpy(filename_full + cwd_length, link, link_length);
+    filename_full[cwd_length + link_length] = 0;
 
     size_t diff_idx = 0;
-    for (; filename_realpath[diff_idx] == cwd[diff_idx]
-           && diff_idx < filename_realpath_length
+    for (; filename_full[diff_idx] == cwd[diff_idx]
+           && diff_idx < filename_full_length
            && diff_idx < cwd_length;
          ++diff_idx);
     int32_t level = simple_archiver_helper_str_slash_count(
-      filename_realpath + diff_idx);
+      filename_full + diff_idx);
     const int32_t level_copy = level;
 
     size_t prev_start_idx = 0;