]> git.seodisparate.com - SimpleArchiver/commitdiff
WIP work on `--map-user`, `--map-group`
authorStephen Seo <seo.disparate@gmail.com>
Mon, 6 Jan 2025 04:54:44 +0000 (13:54 +0900)
committerStephen Seo <seo.disparate@gmail.com>
Mon, 6 Jan 2025 05:08:07 +0000 (14:08 +0900)
The parser code sets up the mappings, but the archiving code doesn't
handle it yet.

src/helpers.c
src/helpers.h
src/parser.c
src/parser.h
src/test.c

index 4d8d88c7f6507561a79b58340fb332071293c5eb..a1dcae745bf7e285b22c9f9f084191c9417f68fb 100644 (file)
@@ -66,6 +66,13 @@ void simple_archiver_helper_cleanup_chdir_back(char **original) {
   }
 }
 
+void simple_archiver_helper_cleanup_uint32(uint32_t **uint) {
+  if (uint && *uint) {
+    free(*uint);
+    *uint = NULL;
+  }
+}
+
 void simple_archiver_helper_datastructure_cleanup_nop(
     __attribute__((unused)) void *unused) {}
 
index 040e92e8076921f141592b95cee3d7e37778632a..05c47fc76e1d54f6251bee7601e6a282cbdac9ac 100644 (file)
@@ -66,6 +66,7 @@ void simple_archiver_helper_cleanup_FILE(FILE **fd);
 void simple_archiver_helper_cleanup_malloced(void **data);
 void simple_archiver_helper_cleanup_c_string(char **str);
 void simple_archiver_helper_cleanup_chdir_back(char **original);
+void simple_archiver_helper_cleanup_uint32(uint32_t **uint);
 
 void simple_archiver_helper_datastructure_cleanup_nop(void *unused);
 
index c0d9419d7b4a0ae5e1d0d11b4ff8b9a5273d03c6..c922951d8e77aff0a182b5fe67f06b3c482e915d 100644 (file)
@@ -19,6 +19,7 @@
 #include "parser.h"
 
 #include <stdint.h>
+#include <inttypes.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -220,6 +221,12 @@ void simple_archiver_print_usage(void) {
           "--extract-prefer-gid : Prefer GID over Group when extracting\n");
   fprintf(stderr,
           "  Note that by default Group is preferred over UID\n");
+  fprintf(stderr,
+          "--map-user <UID/Uname>:<UID/Uname> : Maps a UID/Username to "
+          "UID/Username\n");
+  fprintf(stderr,
+          "--map-group <GID/Gname>:<GID/Gname> : Maps a GID/Group to "
+          "GID/Group\n");
   fprintf(stderr,
           "--force-file-permissions <3-octal-values> : Force set permissions "
           "for files on archive creation/extraction\n"
@@ -255,6 +262,14 @@ SDArchiverParsed simple_archiver_create_parsed(void) {
   parsed.file_permissions = 0;
   parsed.dir_permissions = 0;
   parsed.users_infos = simple_archiver_users_get_system_info();
+  parsed.mappings.UidToUname = simple_archiver_hash_map_init();
+  parsed.mappings.UnameToUid = simple_archiver_hash_map_init();
+  parsed.mappings.UidToUid = simple_archiver_hash_map_init();
+  parsed.mappings.UnameToUname = simple_archiver_hash_map_init();
+  parsed.mappings.GidToGname = simple_archiver_hash_map_init();
+  parsed.mappings.GnameToGid = simple_archiver_hash_map_init();
+  parsed.mappings.GidToGid = simple_archiver_hash_map_init();
+  parsed.mappings.GnameToGname = simple_archiver_hash_map_init();
 
   return parsed;
 }
@@ -493,6 +508,44 @@ int simple_archiver_parse_args(int argc, const char **argv,
         out->flags |= 0x4000;
       } else if (strcmp(argv[0], "--extract-prefer-gid") == 0) {
         out->flags |= 0x8000;
+      } else if (strcmp(argv[0], "--map-user") == 0) {
+        if (argc < 2) {
+          fprintf(stderr, "ERROR: --map-user requires an argument!\n"
+                  "  <UID/Username>:<UID/Username>, like \"1000:someuser\" or "
+                  "\"myuser:thisuser\" or \"thatuser:1011\" etc.\n");
+          simple_archiver_print_usage();
+          return 1;
+        }
+        if (simple_archiver_handle_map_user_or_group(
+            argv[1],
+            out->mappings.UidToUname,
+            out->mappings.UnameToUid,
+            out->mappings.UidToUid,
+            out->mappings.UnameToUname) != 0) {
+          simple_archiver_print_usage();
+          return 1;
+        }
+        --argc;
+        ++argv;
+      } else if (strcmp(argv[0], "--map-group") == 0) {
+        if (argc < 2) {
+          fprintf(stderr, "ERROR: --map-group requires an argument!\n"
+                  "  <GID/Group>:<GID/Group>, like \"1000:audio\" or "
+                  "\"cups:wheel\" or \"users:1011\" etc.\n");
+          simple_archiver_print_usage();
+          return 1;
+        }
+        if (simple_archiver_handle_map_user_or_group(
+            argv[1],
+            out->mappings.GidToGname,
+            out->mappings.GnameToGid,
+            out->mappings.GidToGid,
+            out->mappings.GnameToGname) != 0) {
+          simple_archiver_print_usage();
+          return 1;
+        }
+        --argc;
+        ++argv;
       } else if (strcmp(argv[0], "--force-file-permissions") == 0) {
         if (argc < 2
             || strlen(argv[1]) != 3
@@ -639,7 +692,33 @@ void simple_archiver_free_parsed(SDArchiverParsed *parsed) {
     free(parsed->working_files);
     parsed->working_files = NULL;
   }
+
   simple_archiver_users_free_users_infos(&parsed->users_infos);
+
+  if (parsed->mappings.UidToUname) {
+    simple_archiver_hash_map_free(&parsed->mappings.UidToUname);
+  }
+  if (parsed->mappings.UnameToUid) {
+    simple_archiver_hash_map_free(&parsed->mappings.UnameToUid);
+  }
+  if (parsed->mappings.UidToUid) {
+    simple_archiver_hash_map_free(&parsed->mappings.UidToUid);
+  }
+  if (parsed->mappings.UnameToUname) {
+    simple_archiver_hash_map_free(&parsed->mappings.UnameToUname);
+  }
+  if (parsed->mappings.GidToGname) {
+    simple_archiver_hash_map_free(&parsed->mappings.GidToGname);
+  }
+  if (parsed->mappings.GnameToGid) {
+    simple_archiver_hash_map_free(&parsed->mappings.GnameToGid);
+  }
+  if (parsed->mappings.GidToGid) {
+    simple_archiver_hash_map_free(&parsed->mappings.GidToGid);
+  }
+  if (parsed->mappings.GnameToGname) {
+    simple_archiver_hash_map_free(&parsed->mappings.GnameToGname);
+  }
 }
 
 SDArchiverLinkedList *simple_archiver_parsed_to_filenames(
@@ -911,3 +990,195 @@ SDArchiverLinkedList *simple_archiver_parsed_to_filenames(
   }
   return files_list;
 }
+
+int simple_archiver_handle_map_user_or_group(
+    const char *arg,
+    SDArchiverHashMap *IDToName,
+    SDArchiverHashMap *NameToID,
+    SDArchiverHashMap *IDToID,
+    SDArchiverHashMap *NameToName) {
+  const unsigned long arg_len = strlen(arg);
+
+  int32_t colon_idx = -1;
+  for (int32_t idx = 0; (unsigned long)idx < arg_len; ++idx) {
+    if (arg[idx] == ':') {
+      if (colon_idx == -1) {
+        colon_idx = idx;
+      } else {
+        fprintf(stderr,
+                "ERROR: Encountered multiple \":\" in --map-user arg!\n");
+        return 1;
+      }
+    }
+  }
+
+  if (colon_idx == -1) {
+    fprintf(stderr, "ERROR: No \":\" in --map-user arg!\n");
+    return 1;
+  } else if (colon_idx == 0) {
+    fprintf(stderr, "ERROR: Colon in arg before ID/Name!\n");
+    return 1;
+  } else if ((unsigned long)colon_idx + 1 == arg_len) {
+    fprintf(stderr, "ERROR: Colon in arg at end, no end-ID/Name!\n");
+    return 1;
+  }
+
+  uint_fast8_t first_is_numeric = 1;
+  uint_fast8_t last_is_numeric = 1;
+
+  for (uint32_t idx = 0; idx < (uint32_t)colon_idx; ++idx) {
+    if (arg[idx] < '0' || arg[idx] > '9') {
+      first_is_numeric = 0;
+      break;
+    }
+  }
+
+  for (uint32_t idx = (uint32_t)colon_idx + 1; idx < (uint32_t)arg_len; ++idx) {
+    if (arg[idx] < '0' || arg[idx] > '9') {
+      last_is_numeric = 0;
+      break;
+    }
+  }
+
+  __attribute__((cleanup(simple_archiver_helper_cleanup_c_string)))
+  char *first_buf = malloc((size_t)colon_idx + 1);
+  __attribute__((cleanup(simple_archiver_helper_cleanup_c_string)))
+  char *last_buf = malloc((size_t)arg_len - (size_t)colon_idx);
+
+  __attribute__((cleanup(simple_archiver_helper_cleanup_uint32)))
+  uint32_t *first_id = NULL;
+  __attribute__((cleanup(simple_archiver_helper_cleanup_uint32)))
+  uint32_t *last_id = NULL;
+
+  memcpy(first_buf, arg, (size_t)colon_idx);
+  first_buf[colon_idx] = 0;
+
+  memcpy(last_buf,
+         arg + colon_idx + 1,
+         (size_t)arg_len - (size_t)colon_idx - 1);
+  last_buf[(size_t)arg_len - (size_t)colon_idx - 1] = 0;
+
+  if (first_is_numeric) {
+    unsigned long integer = strtoul(first_buf, NULL, 10);
+    if (integer > 0xFFFFFFFF) {
+      fprintf(stderr, "ERROR: ID integer \"%s\" is too large!\n", first_buf);
+      return 1;
+    }
+    first_id = malloc(sizeof(uint32_t));
+    *first_id = (uint32_t)integer;
+  }
+
+  if (last_is_numeric) {
+    unsigned long integer = strtoul(last_buf, NULL, 10);
+    if (integer > 0xFFFFFFFF) {
+      fprintf(stderr, "ERROR: ID integer \"%s\" is too large!\n", last_buf);
+      return 1;
+    }
+    last_id = malloc(sizeof(uint32_t));
+    *last_id = (uint32_t)integer;
+  }
+
+  if (first_is_numeric && last_is_numeric) {
+    if (simple_archiver_hash_map_get(IDToID, first_id, sizeof(uint32_t))) {
+      fprintf(stderr,
+              "ERROR: Mapping with key \"%" PRIu32 "\" already exists!\n",
+              *first_id);
+      return 1;
+    }
+    if (simple_archiver_hash_map_insert(IDToID,
+                                        last_id,
+                                        first_id,
+                                        sizeof(uint32_t),
+                                        NULL,
+                                        NULL) != 0) {
+      // Values are free'd by insert fn on failure.
+      last_id = NULL;
+      first_id = NULL;
+      fprintf(stderr,
+              "ERROR: Internal error storing ID to ID mapping \"%s\"!",
+              arg);
+      return 1;
+    }
+    // Map takes ownership of values.
+    last_id = NULL;
+    first_id = NULL;
+  } else if (first_is_numeric) {
+    if (simple_archiver_hash_map_get(IDToName, first_id, sizeof(uint32_t))) {
+      fprintf(stderr,
+              "ERROR: Mapping with key \"%" PRIu32 "\" already exists!\n",
+              *first_id);
+      return 1;
+    }
+    if (simple_archiver_hash_map_insert(IDToName,
+                                        last_buf,
+                                        first_id,
+                                        sizeof(uint32_t),
+                                        NULL,
+                                        NULL) != 0) {
+      // Values are free'd by insert fn on failure.
+      last_buf = NULL;
+      first_id = NULL;
+      fprintf(stderr,
+              "ERROR: Internal error storing ID to Name mapping \"%s\"!",
+              arg);
+      return 1;
+    }
+    // Map takes ownership of values.
+    last_buf = NULL;
+    first_id = NULL;
+  } else if (last_is_numeric) {
+    if (simple_archiver_hash_map_get(NameToID,
+                                     first_buf,
+                                     strlen(first_buf) + 1)) {
+      fprintf(stderr,
+              "ERROR: Mapping with key \"%s\" already exists!\n",
+            first_buf);
+      return 1;
+    }
+    if (simple_archiver_hash_map_insert(NameToID,
+                                        last_id,
+                                        first_buf,
+                                        strlen(first_buf) + 1,
+                                        NULL,
+                                        NULL) != 0) {
+      // Values are free'd by insert fn on failure.
+      last_id = NULL;
+      first_buf = NULL;
+      fprintf(stderr,
+              "ERROR: Internal error storing Name to ID mapping \"%s\"!",
+              arg);
+      return 1;
+    }
+    // Map takes ownership of values.
+    last_id = NULL;
+    first_buf = NULL;
+  } else {
+    if (simple_archiver_hash_map_get(NameToName,
+                                     first_buf,
+                                     strlen(first_buf) + 1)) {
+      fprintf(stderr,
+              "ERROR: Mapping with key \"%s\" already exists!\n",
+            first_buf);
+      return 1;
+    }
+    if (simple_archiver_hash_map_insert(NameToName,
+                                        last_buf,
+                                        first_buf,
+                                        strlen(first_buf) + 1,
+                                        NULL,
+                                        NULL) != 0) {
+      // Values are free'd by insert fn on failure.
+      last_buf = NULL;
+      first_buf = NULL;
+      fprintf(stderr,
+              "ERROR: Internal error storing Name to Name mapping \"%s\"!",
+              arg);
+      return 1;
+    }
+    // Map takes ownership of values.
+    last_buf = NULL;
+    first_buf = NULL;
+  }
+
+  return 0;
+}
index 88b69f2aed50680e0bbe1415fcab6c332797cab3..8377148ef1dbd4dfb0e648f1675804fd47eea400 100644 (file)
 
 // Local includes.
 #include "data_structures/linked_list.h"
+#include "data_structures/hash_map.h"
 #include "users.h"
 
+typedef struct SDA_UGMapping {
+  SDArchiverHashMap *UidToUname;
+  SDArchiverHashMap *UnameToUid;
+  SDArchiverHashMap *UidToUid;
+  SDArchiverHashMap *UnameToUname;
+  SDArchiverHashMap *GidToGname;
+  SDArchiverHashMap *GnameToGid;
+  SDArchiverHashMap *GidToGid;
+  SDArchiverHashMap *GnameToGname;
+} SDA_UGMapping;
+
 typedef struct SDArchiverParsed {
   /// Each bit is a flag.
   /// 0b xxxx xx00 - is creating.
@@ -80,6 +92,7 @@ typedef struct SDArchiverParsed {
   uint_fast16_t file_permissions;
   uint_fast16_t dir_permissions;
   UsersInfos users_infos;
+  SDA_UGMapping mappings;
 } SDArchiverParsed;
 
 typedef struct SDArchiverFileInfo {
@@ -113,6 +126,13 @@ void simple_archiver_free_parsed(SDArchiverParsed *parsed);
 
 /// Each entry in the linked list is an SDArchiverFileInfo object.
 SDArchiverLinkedList *simple_archiver_parsed_to_filenames(
-    const SDArchiverParsed *parsed, SDArchiverParsedStatus *status_out);
+  const SDArchiverParsed *parsed, SDArchiverParsedStatus *status_out);
+
+int simple_archiver_handle_map_user_or_group(
+  const char *arg,
+  SDArchiverHashMap *IDToName,
+  SDArchiverHashMap *NameToID,
+  SDArchiverHashMap *IDToID,
+  SDArchiverHashMap *NameToName);
 
 #endif
index 716192122513bbd5c954346245b16e175c6be0b6..6c5c643c700c0fb571de53858e5c59292d8fb58d 100644 (file)
@@ -25,7 +25,9 @@
 
 // Local includes.
 #include "archiver.h"
+#include "data_structures/hash_map.h"
 #include "helpers.h"
+#include "parser.h"
 #include "parser_internal.h"
 
 static int32_t checks_checked = 0;
@@ -132,6 +134,110 @@ int main(void) {
     CHECK_TRUE(parsed.flags == 0x41);
 
     simple_archiver_free_parsed(&parsed);
+
+    // Test mappings.
+    parsed = simple_archiver_create_parsed();
+    CHECK_TRUE(simple_archiver_handle_map_user_or_group(
+      "1000:1001",
+      parsed.mappings.UidToUname,
+      parsed.mappings.UnameToUid,
+      parsed.mappings.UidToUid,
+      parsed.mappings.UnameToUname) == 0);
+    CHECK_TRUE(simple_archiver_handle_map_user_or_group(
+      "1002:user0",
+      parsed.mappings.UidToUname,
+      parsed.mappings.UnameToUid,
+      parsed.mappings.UidToUid,
+      parsed.mappings.UnameToUname) == 0);
+    CHECK_TRUE(simple_archiver_handle_map_user_or_group(
+      "user1:1003",
+      parsed.mappings.UidToUname,
+      parsed.mappings.UnameToUid,
+      parsed.mappings.UidToUid,
+      parsed.mappings.UnameToUname) == 0);
+    CHECK_TRUE(simple_archiver_handle_map_user_or_group(
+      "user2:user3",
+      parsed.mappings.UidToUname,
+      parsed.mappings.UnameToUid,
+      parsed.mappings.UidToUid,
+      parsed.mappings.UnameToUname) == 0);
+    fprintf(stderr, "Expecting ERROR output on next line:\n");
+    CHECK_TRUE(simple_archiver_handle_map_user_or_group(
+      "1000:1091",
+      parsed.mappings.UidToUname,
+      parsed.mappings.UnameToUid,
+      parsed.mappings.UidToUid,
+      parsed.mappings.UnameToUname) != 0);
+    fprintf(stderr, "Expecting ERROR output on next line:\n");
+    CHECK_TRUE(simple_archiver_handle_map_user_or_group(
+      "1002:user00",
+      parsed.mappings.UidToUname,
+      parsed.mappings.UnameToUid,
+      parsed.mappings.UidToUid,
+      parsed.mappings.UnameToUname) != 0);
+    fprintf(stderr, "Expecting ERROR output on next line:\n");
+    CHECK_TRUE(simple_archiver_handle_map_user_or_group(
+      "user1:1033",
+      parsed.mappings.UidToUname,
+      parsed.mappings.UnameToUid,
+      parsed.mappings.UidToUid,
+      parsed.mappings.UnameToUname) != 0);
+    fprintf(stderr, "Expecting ERROR output on next line:\n");
+    CHECK_TRUE(simple_archiver_handle_map_user_or_group(
+      "user2:us3r3",
+      parsed.mappings.UidToUname,
+      parsed.mappings.UnameToUid,
+      parsed.mappings.UidToUid,
+      parsed.mappings.UnameToUname) != 0);
+    uint32_t id_check = 1000;
+    uint32_t *id_get;
+
+    id_get = simple_archiver_hash_map_get(parsed.mappings.UidToUid,
+                                          &id_check,
+                                          sizeof(uint32_t));
+    CHECK_TRUE(id_get);
+    CHECK_TRUE(*id_get == 1001);
+    id_check = 1001;
+    id_get = simple_archiver_hash_map_get(parsed.mappings.UidToUid,
+                                          &id_check,
+                                          sizeof(uint32_t));
+    CHECK_FALSE(id_get);
+
+    id_check = 1002;
+    char *name_get;
+
+    name_get = simple_archiver_hash_map_get(parsed.mappings.UidToUname,
+                                            &id_check,
+                                            sizeof(uint32_t));
+    CHECK_TRUE(name_get);
+    CHECK_STREQ(name_get, "user0");
+    id_check = 1010;
+    name_get = simple_archiver_hash_map_get(parsed.mappings.UidToUname,
+                                            &id_check,
+                                            sizeof(uint32_t));
+    CHECK_FALSE(name_get);
+
+    id_get = simple_archiver_hash_map_get(parsed.mappings.UnameToUid,
+                                          "user1",
+                                          6);
+    CHECK_TRUE(id_get);
+    CHECK_TRUE(*id_get == 1003);
+    id_get = simple_archiver_hash_map_get(parsed.mappings.UnameToUid,
+                                          "user9",
+                                          6);
+    CHECK_FALSE(id_get);
+
+    name_get = simple_archiver_hash_map_get(parsed.mappings.UnameToUname,
+                                            "user2",
+                                            6);
+    CHECK_TRUE(name_get);
+    CHECK_STREQ(name_get, "user3");
+    name_get = simple_archiver_hash_map_get(parsed.mappings.UnameToUname,
+                                            "user3",
+                                            6);
+    CHECK_FALSE(name_get);
+
+    simple_archiver_free_parsed(&parsed);
   }
 
   // Test helpers.