]> git.seodisparate.com - SimpleArchiver/commitdiff
Impl. "safe links" by default for v0
authorStephen Seo <seo.disparate@gmail.com>
Mon, 21 Oct 2024 07:55:39 +0000 (16:55 +0900)
committerStephen Seo <seo.disparate@gmail.com>
Mon, 21 Oct 2024 08:12:28 +0000 (17:12 +0900)
By default, links that point to outside of archive (or possibly doesn't
point to anything) will be ignored. Use "--no-safe-links" to preserve
such symlinks.

Note this has only been implemented for v0 of the file format in this
commit, and not yet v1.

file_format.md
src/archiver.c
src/parser.c
src/parser.h

index 4e395447a2e39abe1a977f6cd7a3a4da34adaf2b..1834af24bbfa4b0bfb62d3b7ef0a2122d58e74b2 100644 (file)
@@ -57,6 +57,11 @@ Following the file-count bytes, the following bytes are added for each file:
         2. The second bit is "other execute permission".
         3. The third bit is UNSET if relative links are preferred, and is SET
            if absolute links are preferred.
+        4. The fourth bit is set if this file/symlink-entry is invalid and must
+           be skipped. Ignore following bytes after these 4 bytes bit-flags in
+           this specification and skip to the next entry; if marked invalid,
+           the following specification bytes for this file/symlink entry must
+           not exist.
     3. The third byte.
         1. Currently unused.
     4. The fourth byte.
index b166d630401dc06404c4636da700f0d9070c8406..e093070ca4a75fee3d6a460e3446ecf0e1f170ef 100644 (file)
@@ -114,6 +114,20 @@ void cleanup_temp_filename_delete(void ***ptrs_array) {
 #endif
 }
 
+void cleanup_overwrite_filename_delete_simple(char **filename) {
+  if (filename && *filename) {
+    if ((*filename)[0] != 0) {
+#if SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_COSMOPOLITAN || \
+    SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_MAC ||          \
+    SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_LINUX
+      unlink(*filename);
+#endif
+    }
+    free(*filename);
+    *filename = NULL;
+  }
+}
+
 int write_files_fn(void *data, void *ud) {
   if (is_sig_int_occurred) {
     return 1;
@@ -773,16 +787,39 @@ int write_files_fn(void *data, void *ud) {
     if (abs_path && (state->parsed->flags & 0x20) == 0 &&
         !simple_archiver_hash_map_get(state->map, abs_path,
                                       strlen(abs_path) + 1)) {
-      // Is not a filename being archived, set preference to absolute path.
-      fprintf(stderr,
-              "NOTICE: abs_path exists, \"--no-abs-symlink\" not specified, "
-              "and link refers to file NOT in archive; preferring abs_path.\n");
-      ((uint8_t *)temp_to_write->buf)[1] |= 0x4;
+      // Is not a filename being archived.
+      if ((state->parsed->flags & 0x80) != 0) {
+        // No safe links, set preference to absolute path.
+        fprintf(
+            stderr,
+            "NOTICE: abs_path exists, \"--no-abs-symlink\" not specified, "
+            "and link refers to file NOT in archive; preferring abs_path.\n");
+        ((uint8_t *)temp_to_write->buf)[1] |= 0x4;
+      } else {
+        // Safe links, do not store symlink!
+        fprintf(stderr,
+                "WARNING: Symlink \"%s\" points to outside archive contents, "
+                "will not be stored! (Use \"--no-safe-links\" to disable this "
+                "behavior)\n",
+                file_info->filename);
+        ((uint8_t *)temp_to_write->buf)[1] |= 0x8;
+      }
     }
 
     // Store the 4 byte bit-flags for file.
     simple_archiver_list_add(to_write, temp_to_write, free_internal_to_write);
 
+    if ((((uint8_t *)temp_to_write->buf)[1] & 0x8) != 0) {
+      // Skipped symlink.
+      simple_archiver_list_get(to_write, write_list_datas_fn, state->out_f);
+      simple_archiver_list_free(&to_write);
+      char format_str[64];
+      snprintf(format_str, 64, FILE_COUNTS_OUTPUT_FORMAT_STR_1, state->digits,
+               state->digits);
+      fprintf(stderr, format_str, ++(state->count), state->max);
+      return 0;
+    }
+
     // Store the absolute and relative paths.
     if (!abs_path) {
       fprintf(stderr,
@@ -2802,6 +2839,9 @@ int simple_archiver_parse_archive_version_0(FILE *in_f, int_fast8_t do_extract,
         simple_archiver_helper_cleanup_malloced))) void *out_f_name = NULL;
     __attribute__((cleanup(simple_archiver_helper_cleanup_FILE))) FILE *out_f =
         NULL;
+    __attribute__((cleanup(
+        cleanup_overwrite_filename_delete_simple))) char *to_overwrite_dest =
+        NULL;
     if (u16 < SIMPLE_ARCHIVER_BUFFER_SIZE) {
       if (fread(buf, 1, u16 + 1, in_f) != (size_t)u16 + 1) {
         return SDAS_INVALID_FILE;
@@ -2831,12 +2871,14 @@ int simple_archiver_parse_archive_version_0(FILE *in_f, int_fast8_t do_extract,
           if (fd == -1) {
             if (errno == ELOOP) {
               // Is an existing symbolic file.
-              unlink((const char *)buf);
+              // Defer deletion to after "is invalid" check.
+              to_overwrite_dest = strdup((const char *)buf);
             }
           } else {
             close(fd);
             // Is an existing file.
-            unlink((const char *)buf);
+            // Defer deletion to after "is invalid" check.
+            to_overwrite_dest = strdup((const char *)buf);
           }
         }
         if (!skip) {
@@ -2878,12 +2920,14 @@ int simple_archiver_parse_archive_version_0(FILE *in_f, int_fast8_t do_extract,
           if (fd == -1) {
             if (errno == ELOOP) {
               // Is an existing symbolic file.
-              unlink((const char *)uc_heap_buf);
+              // Defer deletion to after "is invalid" check.
+              to_overwrite_dest = strdup((const char *)uc_heap_buf);
             }
           } else {
             close(fd);
             // Is an existing file.
-            unlink((const char *)uc_heap_buf);
+            // Defer deletion to after "is invalid" check.
+            to_overwrite_dest = strdup((const char *)uc_heap_buf);
           }
         }
         if (!skip) {
@@ -2898,6 +2942,17 @@ int simple_archiver_parse_archive_version_0(FILE *in_f, int_fast8_t do_extract,
       return SDAS_INVALID_FILE;
     }
 
+    // Check for "invalid entry" flag.
+    if ((buf[1] & 0x8) != 0) {
+      free(to_overwrite_dest);
+      to_overwrite_dest = NULL;
+      fprintf(stderr, "  This file entry was marked invalid, skipping...\n");
+      continue;
+    } else {
+      // Do deferred overwrite action: remove existing file/symlink.
+      cleanup_overwrite_filename_delete_simple(&to_overwrite_dest);
+    }
+
 #if SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_COSMOPOLITAN || \
     SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_MAC ||          \
     SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_LINUX
index 20e3b5e6b153a22abef9a57bbcb6d07154bddf38..8a66a5830f3d1a509476b117860711ad52b6552f 100644 (file)
@@ -168,6 +168,9 @@ void simple_archiver_print_usage(void) {
   fprintf(stderr, "--overwrite-extract : allows overwriting when extracting\n");
   fprintf(stderr,
           "--no-abs-symlink : do not store absolute paths for symlinks\n");
+  fprintf(stderr,
+          "--no-safe-links : keep symlinks that link to outside archive "
+          "contents\n");
   fprintf(stderr,
           "--temp-files-dir <dir> : where to store temporary files created "
           "when compressing (defaults to current working directory)\n");
@@ -303,6 +306,11 @@ int simple_archiver_parse_args(int argc, const char **argv,
         out->flags |= 0x8;
       } else if (strcmp(argv[0], "--no-abs-symlink") == 0) {
         out->flags |= 0x20;
+      } else if (strcmp(argv[0], "--no-safe-links") == 0) {
+        out->flags |= 0x80;
+        fprintf(stderr,
+                "NOTICE: Disabling safe-links, symlinks that point to outside "
+                "archived files will be preserved!\n");
       } else if (strcmp(argv[0], "--temp-files-dir") == 0) {
         if (argc < 2) {
           fprintf(stderr, "ERROR: --temp-files-dir is missing an argument!\n");
index 43d58f810bef02c5e6aecf21969347fa24fbe8a6..71462b2c5e9ef0a8d5f7f0e8872567c9d7a2ac4c 100644 (file)
@@ -36,6 +36,7 @@ typedef struct SDArchiverParsed {
   /// 0b xxx1 xxxx - Create archive to stdout or read archive from stdin.
   /// 0b xx1x xxxx - Do not save absolute paths for symlinks.
   /// 0b x1xx xxxx - Sort files by size before archiving.
+  /// 0b 1xxx xxxx - No safe links.
   uint32_t flags;
   /// Null-terminated string.
   char *filename;