]> git.seodisparate.com - SimpleArchiver/commitdiff
Fix/refactor created temp file when compressing
authorStephen Seo <seo.disparate@gmail.com>
Fri, 21 Feb 2025 07:26:32 +0000 (16:26 +0900)
committerStephen Seo <seo.disparate@gmail.com>
Fri, 21 Feb 2025 07:30:40 +0000 (16:30 +0900)
Before this commit, archived files using compression would use a
temporary file in the current-working-directory (usually the same
directory specified by "-C <dir>") to store compressed files. This is
required by design as the file-size of the compressed file/chunk is
required before storing the data. In other words, the size is stored
before the data without seeking so that output can be streamable (in
case of "-f -" or "-f /dev/stdout" when compressing).

This commit changes the behavior of creating temporary files for
compression by placing them in the same directory as the output file
(specified by "-f <output_file>"), or using "tmpfile()" if that should
fail. Note that the `--temp-files-dir <dir>` option exists, and
"tmpfile()" is used as a fallback in case writing to
`--temp-files-dir <dir>` fails.

src/archiver.c
src/helpers.c
src/helpers.h
src/parser.c
src/parser.h

index 66b622aa8d2dc8dcdb1c3ed3ef077e71cc11a06b..850ead96844c828f49d6984387b92da3dd437ee3 100644 (file)
@@ -43,7 +43,6 @@
 #include "helpers.h"
 #include "users.h"
 
-#define TEMP_FILENAME_CMP "%s%ssimple_archiver_compressed_%zu.tmp"
 #define FILE_COUNTS_OUTPUT_FORMAT_STR_0 \
   "\nFile %%%zu" PRIu32 " of %%%zu" PRIu32 ".\n"
 #define FILE_COUNTS_OUTPUT_FORMAT_STR_1 "[%%%zuzu/%%%zuzu]\n"
@@ -160,37 +159,19 @@ int write_files_fn_file_v0(void *data, void *ud) {
 #if SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_COSMOPOLITAN || \
     SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_MAC ||          \
     SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_LINUX
-      // Use temp file to store compressed data.
-      char temp_filename[512];
-      size_t idx = 0;
-      size_t temp_dir_end = strlen(state->parsed->temp_dir);
-      snprintf(temp_filename, 512, TEMP_FILENAME_CMP, state->parsed->temp_dir,
-               state->parsed->temp_dir[temp_dir_end - 1] == '/' ? "" : "/",
-               idx);
-      do {
-        FILE *test_fd = fopen(temp_filename, "rb");
-        if (test_fd) {
-          // File exists.
-          fclose(test_fd);
-          snprintf(temp_filename, 512, TEMP_FILENAME_CMP,
-                   state->parsed->temp_dir,
-                   state->parsed->temp_dir[temp_dir_end - 1] == '/' ? "" : "/",
-                   ++idx);
-        } else if (idx > 0xFFFF) {
-          // Sanity check.
-          return 1;
-        } else {
-          break;
-        }
-      } while (1);
       __attribute__((cleanup(simple_archiver_helper_cleanup_FILE)))
       FILE *file_fd = fopen(file_info->filename, "rb");
       if (!file_fd) {
         // Unable to open file for compressing and archiving.
         return 1;
       }
+      // Use temp file to store compressed data.
+      __attribute__((cleanup(simple_archiver_helper_cleanup_c_string)))
+      char *out_temp_filename = NULL;
       __attribute__((cleanup(simple_archiver_helper_cleanup_FILE)))
-      FILE *tmp_fd = fopen(temp_filename, "wb");
+      FILE *tmp_fd = simple_archiver_helper_temp_dir(state->parsed,
+                                                     &out_temp_filename);
+      fprintf(stderr, "DEBUG: out_temp_filename is %s\n");
       __attribute__((cleanup(cleanup_temp_filename_delete))) void **ptrs_array =
           malloc(sizeof(void *) * 2);
       ptrs_array[0] = NULL;
@@ -203,7 +184,7 @@ int write_files_fn_file_v0(void *data, void *ud) {
           return 1;
         }
       } else {
-        ptrs_array[0] = temp_filename;
+        ptrs_array[0] = out_temp_filename;
         ptrs_array[1] = &tmp_fd;
       }
 
@@ -364,7 +345,6 @@ int write_files_fn_file_v0(void *data, void *ud) {
             }
           } else if (ret == 0) {
             read_done = 1;
-            simple_archiver_helper_cleanup_FILE(&tmp_fd);
             close(pipe_outof_cmd[0]);
             pipe_outof_cmd[0] = -1;
             // fprintf(stderr, "read_done\n");
@@ -495,8 +475,7 @@ int write_files_fn_file_v0(void *data, void *ud) {
 
       // Get compressed file length.
       // Compressed file should be at "temp_filename".
-      tmp_fd = fopen(temp_filename, "rb");
-
+      clearerr(tmp_fd);
       long end;
       if (fseek(tmp_fd, 0, SEEK_END) != 0) {
         // Error seeking.
@@ -506,10 +485,8 @@ int write_files_fn_file_v0(void *data, void *ud) {
       if (end == -1L) {
         // Error getting end position.
         return 1;
-      } else if (fseek(tmp_fd, 0, SEEK_SET) != 0) {
-        // Error seeking.
-        return 1;
       }
+      rewind(tmp_fd);
 
       // Write file length.
       u64 = (uint64_t)end;
@@ -541,9 +518,6 @@ int write_files_fn_file_v0(void *data, void *ud) {
           break;
         }
       } while (1);
-
-      // Cleanup.
-      simple_archiver_helper_cleanup_FILE(&tmp_fd);
 #endif
     } else {
       uint16_t u16;
@@ -3107,10 +3081,8 @@ int simple_archiver_write_v1(FILE *out_f, SDArchiverState *state,
     if (state->parsed->compressor && state->parsed->decompressor) {
       // Is compressing.
 
-      size_t temp_filename_size = strlen(state->parsed->temp_dir) + 1 + 64;
-      __attribute__((cleanup(
-          simple_archiver_helper_cleanup_c_string))) char *temp_filename =
-          malloc(temp_filename_size);
+      __attribute__((cleanup(simple_archiver_helper_cleanup_c_string)))
+      char *temp_filename = NULL;
 
       __attribute__((cleanup(cleanup_temp_filename_delete))) void **ptrs_array =
           malloc(sizeof(void *) * 2);
@@ -3118,38 +3090,10 @@ int simple_archiver_write_v1(FILE *out_f, SDArchiverState *state,
       ptrs_array[1] = NULL;
 
       __attribute__((cleanup(simple_archiver_helper_cleanup_FILE)))
-      FILE *temp_fd = NULL;
-
-      if (state->parsed->temp_dir) {
-        size_t idx = 0;
-        size_t temp_dir_len = strlen(state->parsed->temp_dir);
-        snprintf(temp_filename, temp_filename_size, TEMP_FILENAME_CMP,
-                 state->parsed->temp_dir,
-                 state->parsed->temp_dir[temp_dir_len - 1] == '/' ? "" : "/",
-                 idx);
-        do {
-          FILE *test_fd = fopen(temp_filename, "rb");
-          if (test_fd) {
-            // File exists.
-            fclose(test_fd);
-            snprintf(
-                temp_filename, temp_filename_size, TEMP_FILENAME_CMP,
-                state->parsed->temp_dir,
-                state->parsed->temp_dir[temp_dir_len - 1] == '/' ? "" : "/",
-                ++idx);
-          } else if (idx > 0xFFFF) {
-            return SDAS_INTERNAL_ERROR;
-          } else {
-            break;
-          }
-        } while (1);
-        temp_fd = fopen(temp_filename, "w+b");
-        if (temp_fd) {
-          ptrs_array[0] = temp_filename;
-        }
-      } else {
-        temp_fd = tmpfile();
-      }
+      FILE *temp_fd = simple_archiver_helper_temp_dir(state->parsed,
+                                                      &temp_filename);
+
+      ptrs_array[0] = temp_filename;
 
       if (!temp_fd) {
         temp_fd = tmpfile();
@@ -3377,9 +3321,7 @@ int simple_archiver_write_v1(FILE *out_f, SDArchiverState *state,
         return SDAS_FAILED_TO_WRITE;
       }
 
-      if (fseek(temp_fd, 0, SEEK_SET) != 0) {
-        return SDAS_INTERNAL_ERROR;
-      }
+      rewind(temp_fd);
 
       size_t written_size = 0;
 
@@ -4124,11 +4066,8 @@ int simple_archiver_write_v2(FILE *out_f, SDArchiverState *state,
 
     if (state->parsed->compressor && state->parsed->decompressor) {
       // Is compressing.
-
-      size_t temp_filename_size = strlen(state->parsed->temp_dir) + 1 + 64;
-      __attribute__((cleanup(
-          simple_archiver_helper_cleanup_c_string))) char *temp_filename =
-          malloc(temp_filename_size);
+      __attribute__((cleanup(simple_archiver_helper_cleanup_c_string)))
+      char *temp_filename = NULL;
 
       __attribute__((cleanup(cleanup_temp_filename_delete))) void **ptrs_array =
           malloc(sizeof(void *) * 2);
@@ -4136,38 +4075,9 @@ int simple_archiver_write_v2(FILE *out_f, SDArchiverState *state,
       ptrs_array[1] = NULL;
 
       __attribute__((cleanup(simple_archiver_helper_cleanup_FILE)))
-      FILE *temp_fd = NULL;
-
-      if (state->parsed->temp_dir) {
-        size_t idx = 0;
-        size_t temp_dir_len = strlen(state->parsed->temp_dir);
-        snprintf(temp_filename, temp_filename_size, TEMP_FILENAME_CMP,
-                 state->parsed->temp_dir,
-                 state->parsed->temp_dir[temp_dir_len - 1] == '/' ? "" : "/",
-                 idx);
-        do {
-          FILE *test_fd = fopen(temp_filename, "rb");
-          if (test_fd) {
-            // File exists.
-            fclose(test_fd);
-            snprintf(
-                temp_filename, temp_filename_size, TEMP_FILENAME_CMP,
-                state->parsed->temp_dir,
-                state->parsed->temp_dir[temp_dir_len - 1] == '/' ? "" : "/",
-                ++idx);
-          } else if (idx > 0xFFFF) {
-            return SDAS_INTERNAL_ERROR;
-          } else {
-            break;
-          }
-        } while (1);
-        temp_fd = fopen(temp_filename, "w+b");
-        if (temp_fd) {
-          ptrs_array[0] = temp_filename;
-        }
-      } else {
-        temp_fd = tmpfile();
-      }
+      FILE *temp_fd = simple_archiver_helper_temp_dir(state->parsed,
+                                                      &temp_filename);
+      ptrs_array[0] = temp_filename;
 
       if (!temp_fd) {
         temp_fd = tmpfile();
@@ -4395,9 +4305,7 @@ int simple_archiver_write_v2(FILE *out_f, SDArchiverState *state,
         return SDAS_FAILED_TO_WRITE;
       }
 
-      if (fseek(temp_fd, 0, SEEK_SET) != 0) {
-        return SDAS_INTERNAL_ERROR;
-      }
+      rewind(temp_fd);
 
       size_t written_size = 0;
 
@@ -5401,11 +5309,8 @@ int simple_archiver_write_v3(FILE *out_f, SDArchiverState *state,
 
     if (state->parsed->compressor && state->parsed->decompressor) {
       // Is compressing.
-
-      size_t temp_filename_size = strlen(state->parsed->temp_dir) + 1 + 64;
-      __attribute__((cleanup(
-          simple_archiver_helper_cleanup_c_string))) char *temp_filename =
-          malloc(temp_filename_size);
+      __attribute__((cleanup(simple_archiver_helper_cleanup_c_string)))
+      char *temp_filename = NULL;
 
       __attribute__((cleanup(cleanup_temp_filename_delete))) void **ptrs_array =
           malloc(sizeof(void *) * 2);
@@ -5413,38 +5318,9 @@ int simple_archiver_write_v3(FILE *out_f, SDArchiverState *state,
       ptrs_array[1] = NULL;
 
       __attribute__((cleanup(simple_archiver_helper_cleanup_FILE)))
-      FILE *temp_fd = NULL;
-
-      if (state->parsed->temp_dir) {
-        size_t idx = 0;
-        size_t temp_dir_len = strlen(state->parsed->temp_dir);
-        snprintf(temp_filename, temp_filename_size, TEMP_FILENAME_CMP,
-                 state->parsed->temp_dir,
-                 state->parsed->temp_dir[temp_dir_len - 1] == '/' ? "" : "/",
-                 idx);
-        do {
-          FILE *test_fd = fopen(temp_filename, "rb");
-          if (test_fd) {
-            // File exists.
-            fclose(test_fd);
-            snprintf(
-                temp_filename, temp_filename_size, TEMP_FILENAME_CMP,
-                state->parsed->temp_dir,
-                state->parsed->temp_dir[temp_dir_len - 1] == '/' ? "" : "/",
-                ++idx);
-          } else if (idx > 0xFFFF) {
-            return SDAS_INTERNAL_ERROR;
-          } else {
-            break;
-          }
-        } while (1);
-        temp_fd = fopen(temp_filename, "w+b");
-        if (temp_fd) {
-          ptrs_array[0] = temp_filename;
-        }
-      } else {
-        temp_fd = tmpfile();
-      }
+      FILE *temp_fd = simple_archiver_helper_temp_dir(state->parsed,
+                                                      &temp_filename);
+      ptrs_array[0] = temp_filename;
 
       if (!temp_fd) {
         temp_fd = tmpfile();
@@ -5672,9 +5548,7 @@ int simple_archiver_write_v3(FILE *out_f, SDArchiverState *state,
         return SDAS_FAILED_TO_WRITE;
       }
 
-      if (fseek(temp_fd, 0, SEEK_SET) != 0) {
-        return SDAS_INTERNAL_ERROR;
-      }
+      rewind(temp_fd);
 
       size_t written_size = 0;
 
index f063467361d566475a9142f10ed32a4aae4e8319..46fdc86af08431a855e6a18fed6c61936a851599 100644 (file)
@@ -510,6 +510,10 @@ char *simple_archiver_helper_insert_prefix_in_link_path(const char *prefix,
 }
 
 char *simple_archiver_helper_real_path_to_name(const char *filename) {
+  if (!filename || filename[0] == 0) {
+    return NULL;
+  }
+
   __attribute__((cleanup(simple_archiver_helper_cleanup_c_string)))
   char *filename_copy = strdup(filename);
   char *filename_dir = dirname(filename_copy);
@@ -835,3 +839,73 @@ uint_fast8_t simple_archiver_helper_string_allowed_lists(
 
   return 1;
 }
+
+FILE *simple_archiver_helper_temp_dir(const SDArchiverParsed *parsed,
+                                      char **out_temp_filename) {
+  __attribute__((cleanup(simple_archiver_helper_cleanup_c_string)))
+  char *real_path_to_filename = parsed->temp_dir
+    ? strdup(parsed->temp_dir)
+    : strdup(parsed->filename_full_abs_path);
+
+  if (!real_path_to_filename) {
+    if (out_temp_filename) {
+      *out_temp_filename = NULL;
+    }
+    return tmpfile();
+  }
+
+  char *dir = parsed->temp_dir
+    ? real_path_to_filename
+    : dirname(real_path_to_filename);
+  if (!dir) {
+    if (out_temp_filename) {
+      *out_temp_filename = NULL;
+    }
+    return tmpfile();
+  }
+
+  char temp_filename[512];
+  size_t idx = 0;
+  size_t dir_len = strlen(dir);
+  snprintf(temp_filename,
+           512,
+           TEMP_FILENAME_CMP,
+           dir,
+           dir[dir_len - 1] == '/' ? "" : "/",
+           idx);
+  do {
+    FILE *test_fd = fopen(temp_filename, "rb");
+    if (test_fd) {
+      // File exists.
+      fclose(test_fd);
+        snprintf(temp_filename,
+                 512,
+                 TEMP_FILENAME_CMP,
+                 dir,
+                 dir[dir_len - 1] == '/' ? "" : "/",
+                 ++idx);
+    } else if (idx > 0xFFFF) {
+      // Sanity check.
+      if (out_temp_filename) {
+        *out_temp_filename = NULL;
+      }
+      return tmpfile();
+    } else {
+      break;
+    }
+  } while (1);
+
+  FILE *temp_file = fopen(temp_filename, "w+b");
+
+  if (temp_file) {
+    if (out_temp_filename) {
+      *out_temp_filename = strdup(temp_filename);
+    }
+    return temp_file;
+  } else {
+    if (out_temp_filename) {
+      *out_temp_filename = NULL;
+    }
+    return tmpfile();
+  }
+}
index 676e1a8336f5f70c266f610b61ce2f75d96ca316..a028c201658ae8276ad0a5eeb9483e8b89bb99af 100644 (file)
@@ -27,6 +27,8 @@
 #include "data_structures/linked_list.h"
 #include "parser.h"
 
+#define TEMP_FILENAME_CMP "%s%ssimple_archiver_compressed_%zu.tmp"
+
 static const uint32_t MAX_SYMBOLIC_LINK_SIZE = 512;
 
 /// Returns non-zero if this system is big-endian.
@@ -145,4 +147,9 @@ uint_fast8_t simple_archiver_helper_string_allowed_lists(
   uint_fast8_t case_i,
   const SDArchiverParsed *parsed);
 
+// Must be free'd with `fclose(...)`.
+// "out_temp_filename" must be free'd if non-NULL.
+FILE *simple_archiver_helper_temp_dir(const SDArchiverParsed *parsed,
+                                      char **out_temp_filename);
+
 #endif
index e9396f30e14d461a5bc3480c1c0b4cc10848f3c1..18e3257019597ffb5d5dbe0e030489ef60c8c041 100644 (file)
@@ -298,6 +298,7 @@ SDArchiverParsed simple_archiver_create_parsed(void) {
 
   parsed.flags = 0x40;
   parsed.filename = NULL;
+  parsed.filename_full_abs_path = NULL;
   parsed.compressor = NULL;
   parsed.decompressor = NULL;
   parsed.working_files = NULL;
@@ -338,6 +339,10 @@ int simple_archiver_parse_args(int argc, const char **argv,
     free(out->filename);
     out->filename = NULL;
   }
+  if (out->filename_full_abs_path) {
+    free(out->filename_full_abs_path);
+    out->filename_full_abs_path = NULL;
+  }
   if (out->compressor) {
     free(out->compressor);
     out->compressor = NULL;
@@ -383,12 +388,16 @@ int simple_archiver_parse_args(int argc, const char **argv,
           if (out->filename) {
             free(out->filename);
           }
+          if (out->filename_full_abs_path) {
+            free(out->filename_full_abs_path);
+          }
           out->filename = NULL;
+          out->filename_full_abs_path = NULL;
         } else {
           out->flags &= 0xFFFFFFEF;
-          size_t size = strlen(argv[1]) + 1;
-          out->filename = malloc(size);
-          strncpy(out->filename, argv[1], size);
+          out->filename = strdup(argv[1]);
+          out->filename_full_abs_path =
+            simple_archiver_helper_real_path_to_name(argv[1]);
         }
         --argc;
         ++argv;
@@ -467,7 +476,7 @@ int simple_archiver_parse_args(int argc, const char **argv,
           simple_archiver_print_usage();
           return 1;
         }
-        out->temp_dir = argv[1];
+        out->temp_dir = simple_archiver_helper_real_path_to_name(argv[1]);
         --argc;
         ++argv;
       } else if (strcmp(argv[0], "--write-version") == 0) {
@@ -925,10 +934,6 @@ int simple_archiver_parse_args(int argc, const char **argv,
     ++argv;
   }
 
-  if (!out->temp_dir) {
-    out->temp_dir = "./";
-  }
-
   return 0;
 }
 
@@ -938,6 +943,10 @@ void simple_archiver_free_parsed(SDArchiverParsed *parsed) {
     free(parsed->filename);
     parsed->filename = NULL;
   }
+  if (parsed->filename_full_abs_path) {
+    free(parsed->filename_full_abs_path);
+    parsed->filename_full_abs_path = NULL;
+  }
   if (parsed->compressor) {
     free(parsed->compressor);
     parsed->compressor = NULL;
@@ -957,6 +966,11 @@ void simple_archiver_free_parsed(SDArchiverParsed *parsed) {
     parsed->working_files = NULL;
   }
 
+  if (parsed->temp_dir) {
+    free(parsed->temp_dir);
+    parsed->temp_dir = NULL;
+  }
+
   simple_archiver_users_free_users_infos(&parsed->users_infos);
 
   if (parsed->mappings.UidToUname) {
index 7c6a9c5ba447961f045f6c1127e5c48321acf9f8..490c7010cc94cb83cfe2dfebba9877a7e90f4811 100644 (file)
@@ -65,6 +65,8 @@ typedef struct SDArchiverParsed {
   /// Null-terminated string.
   char *filename;
   /// Null-terminated string.
+  char *filename_full_abs_path;
+  /// Null-terminated string.
   char *compressor;
   /// Null-terminated string.
   char *decompressor;
@@ -73,8 +75,9 @@ typedef struct SDArchiverParsed {
   /// Determines a "white-list" of files to extract when extracting.
   char **working_files;
   /// Determines where to place temporary files. If NULL, temporary files are
-  /// created in the current working directory.
-  const char *temp_dir;
+  /// created in the target filename's directory.
+  /// No longer refers to string in argv.
+  char *temp_dir;
   /// Dir specified by "-C".
   const char *user_cwd;
   /// Currently only 0, 1, 2, and 3 is supported.