]> git.seodisparate.com - SimpleArchiver/commitdiff
Impl. handling symbolic links
authorStephen Seo <seo.disparate@gmail.com>
Thu, 4 Jul 2024 05:48:56 +0000 (14:48 +0900)
committerStephen Seo <seo.disparate@gmail.com>
Thu, 4 Jul 2024 05:48:56 +0000 (14:48 +0900)
src/helpers.h
src/main.c
src/parser.c
src/parser.h

index 3089b3338e92ebb6384ed93a2d2e9f7314125e42..f5915fd3b3b2ee86bf05dad036cc6dc8dff902ba 100644 (file)
@@ -19,6 +19,8 @@
 #ifndef SEODISPARATE_COM_SIMPLE_ARCHIVER_HELPERS_H_
 #define SEODISPARATE_COM_SIMPLE_ARCHIVER_HELPERS_H_
 
+static const unsigned int MAX_SYMBOLIC_LINK_SIZE = 512;
+
 /// Returns non-zero if this system is big-endian.
 int simple_archiver_helper_is_big_endian(void);
 
index 1b12eee3f6445494209d367907f4fa4545fad41a..b5f2c372c1443406c95d1c7294145b602031cf90 100644 (file)
 #include "parser.h"
 
 int print_list_fn(void *data, __attribute__((unused)) void *ud) {
-  const char *cstr = data;
-  printf("  %s\n", cstr);
+  const SDArchiverFileInfo *file_info = data;
+  if (file_info->link_dest == NULL) {
+    printf("  REGULAR FILE:  %s\n", file_info->filename);
+  } else {
+    printf("  SYMBOLIC LINK: %s -> %s\n", file_info->filename,
+           file_info->link_dest);
+  }
   return 0;
 }
 
index eea47bba33b97011e3fc614e8a6f784c171ad49e..c14f2aa3854994314cf0eec7045e8dd8dbd6368e 100644 (file)
 #include <fcntl.h>
 #include <sys/stat.h>
 #include <sys/types.h>
+#include <unistd.h>
 #endif
 
 #include "data_structures/hash_map.h"
 #include "data_structures/linked_list.h"
+#include "helpers.h"
 #include "parser_internal.h"
 
 /// Gets the first non "./"-like character in the filename.
@@ -87,6 +89,34 @@ unsigned int simple_archiver_parser_internal_filename_idx(
   return idx;
 }
 
+void simple_archiver_parser_internal_remove_end_slash(char *filename) {
+  int len = strlen(filename);
+  int idx;
+  for (idx = len; idx-- > 0;) {
+    if (filename[idx] != '/') {
+      ++idx;
+      break;
+    }
+  }
+  if (idx < len && idx > 0) {
+    filename[idx] = 0;
+  }
+}
+
+void simple_archiver_internal_free_file_info_fn(void *data) {
+  SDArchiverFileInfo *file_info = data;
+  if (file_info) {
+    if (file_info->filename) {
+      free(file_info->filename);
+    }
+    if (file_info->link_dest) {
+      free(file_info->link_dest);
+    }
+  }
+
+  free(data);
+}
+
 int list_get_last_fn(void *data, void *ud) {
   char **last = ud;
   *last = data;
@@ -188,6 +218,7 @@ int simple_archiver_parse_args(int argc, const char **argv,
         int arg_length = strlen(argv[0] + arg_idx) + 1;
         out->working_files[0] = malloc(arg_length);
         strncpy(out->working_files[0], argv[0] + arg_idx, arg_length);
+        simple_archiver_parser_internal_remove_end_slash(out->working_files[0]);
         out->working_files[1] = NULL;
       } else {
         int working_size = 1;
@@ -209,6 +240,8 @@ int simple_archiver_parse_args(int argc, const char **argv,
         // Set last element to the arg.
         out->working_files[working_size - 1] = malloc(size);
         strncpy(out->working_files[working_size - 1], argv[0] + arg_idx, size);
+        simple_archiver_parser_internal_remove_end_slash(
+            out->working_files[working_size - 1]);
       }
     }
 
@@ -256,16 +289,36 @@ SDArchiverLinkedList *simple_archiver_parsed_to_filenames(
     SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_LINUX
   for (char **iter = parsed->working_files; iter && *iter; ++iter) {
     struct stat st;
+    memset(&st, 0, sizeof(struct stat));
     fstatat(AT_FDCWD, *iter, &st, AT_SYMLINK_NOFOLLOW);
-    if ((st.st_mode & S_IFMT) == S_IFLNK) {
-      // Is a symbolic link. TODO handle this.
-    } else if ((st.st_mode & S_IFMT) == S_IFREG) {
-      // Is a regular file.
+    if ((st.st_mode & S_IFMT) == S_IFREG || (st.st_mode & S_IFMT) == S_IFLNK) {
+      // Is a regular file or a symbolic link.
       int len = strlen(*iter) + 1;
       char *filename = malloc(len);
       strncpy(filename, *iter, len);
       if (simple_archiver_hash_map_get(hash_map, filename, len - 1) == NULL) {
-        simple_archiver_list_add(files_list, filename, NULL);
+        SDArchiverFileInfo *file_info = malloc(sizeof(SDArchiverFileInfo));
+        file_info->filename = filename;
+        if ((st.st_mode & S_IFMT) == S_IFLNK) {
+          file_info->link_dest = malloc(MAX_SYMBOLIC_LINK_SIZE);
+          ssize_t count = readlinkat(AT_FDCWD, filename, file_info->link_dest,
+                                     MAX_SYMBOLIC_LINK_SIZE - 1);
+          if (count >= MAX_SYMBOLIC_LINK_SIZE - 1) {
+            file_info->link_dest[MAX_SYMBOLIC_LINK_SIZE - 1] = 0;
+          } else if (count > 0) {
+            file_info->link_dest[count] = 0;
+          } else {
+            // Failure.
+            free(file_info->link_dest);
+            free(file_info);
+            free(filename);
+            continue;
+          }
+        } else {
+          file_info->link_dest = NULL;
+        }
+        simple_archiver_list_add(files_list, file_info,
+                                 simple_archiver_internal_free_file_info_fn);
         simple_archiver_hash_map_insert(&hash_map, &hash_map_sentinel, filename,
                                         len - 1, container_no_free_fn,
                                         container_no_free_fn);
@@ -297,14 +350,48 @@ SDArchiverLinkedList *simple_archiver_parsed_to_filenames(
             char *combined_path = malloc(combined_size);
             snprintf(combined_path, combined_size, "%s/%s", next,
                      dir_entry->d_name);
+            unsigned int valid_idx =
+                simple_archiver_parser_internal_filename_idx(combined_path);
+            if (valid_idx > 0) {
+              char *new_path = malloc(combined_size - valid_idx);
+              strncpy(new_path, combined_path + valid_idx,
+                      combined_size - valid_idx);
+              free(combined_path);
+              combined_path = new_path;
+              combined_size -= valid_idx;
+            }
+            memset(&st, 0, sizeof(struct stat));
             fstatat(AT_FDCWD, combined_path, &st, AT_SYMLINK_NOFOLLOW);
-            if ((st.st_mode & S_IFMT) == S_IFLNK) {
-              // Is a symbolic link. TODO handle this.
-            } else if ((st.st_mode & S_IFMT) == S_IFREG) {
-              // Is a file.
+            if ((st.st_mode & S_IFMT) == S_IFREG ||
+                (st.st_mode & S_IFMT) == S_IFLNK) {
+              // Is a file or a symbolic link.
               if (simple_archiver_hash_map_get(hash_map, combined_path,
                                                combined_size - 1) == NULL) {
-                simple_archiver_list_add(files_list, combined_path, NULL);
+                SDArchiverFileInfo *file_info =
+                    malloc(sizeof(SDArchiverFileInfo));
+                file_info->filename = combined_path;
+                if ((st.st_mode & S_IFMT) == S_IFLNK) {
+                  file_info->link_dest = malloc(MAX_SYMBOLIC_LINK_SIZE);
+                  ssize_t count =
+                      readlinkat(AT_FDCWD, combined_path, file_info->link_dest,
+                                 MAX_SYMBOLIC_LINK_SIZE - 1);
+                  if (count >= MAX_SYMBOLIC_LINK_SIZE - 1) {
+                    file_info->link_dest[MAX_SYMBOLIC_LINK_SIZE - 1] = 0;
+                  } else if (count > 0) {
+                    file_info->link_dest[count] = 0;
+                  } else {
+                    // Failure.
+                    free(file_info->link_dest);
+                    free(file_info);
+                    free(combined_path);
+                    continue;
+                  }
+                } else {
+                  file_info->link_dest = NULL;
+                }
+                simple_archiver_list_add(
+                    files_list, file_info,
+                    simple_archiver_internal_free_file_info_fn);
                 simple_archiver_hash_map_insert(
                     &hash_map, &hash_map_sentinel, combined_path,
                     combined_size - 1, container_no_free_fn,
@@ -336,13 +423,15 @@ SDArchiverLinkedList *simple_archiver_parsed_to_filenames(
   // Remove leading "./" entries from files_list.
   for (SDArchiverLLNode *iter = files_list->head->next;
        iter != files_list->tail; iter = iter->next) {
-    unsigned int idx = simple_archiver_parser_internal_filename_idx(iter->data);
+    SDArchiverFileInfo *file_info = iter->data;
+    unsigned int idx =
+        simple_archiver_parser_internal_filename_idx(file_info->filename);
     if (idx > 0) {
-      int len = strlen((char *)iter->data) + 1 - idx;
+      int len = strlen(file_info->filename) + 1 - idx;
       char *substr = malloc(len);
-      strncpy(substr, (char *)iter->data + idx, len);
-      free(iter->data);
-      iter->data = substr;
+      strncpy(substr, file_info->filename + idx, len);
+      free(file_info->filename);
+      file_info->filename = substr;
     }
   }
 
index 73cc7caada6d820b2467c3e11a4c7e42e5d51b4a..19630d0f4447ed21a258357e8659b6d9fff44b7e 100644 (file)
@@ -38,6 +38,12 @@ typedef struct SDArchiverParsed {
   char **working_files;
 } SDArchiverParsed;
 
+typedef struct SDArchiverFileInfo {
+  char *filename;
+  /// Is NULL if not a symbolic link.
+  char *link_dest;
+} SDArchiverFileInfo;
+
 void simple_archiver_print_usage(void);
 
 SDArchiverParsed simple_archiver_create_parsed(void);
@@ -50,6 +56,7 @@ int simple_archiver_parse_args(int argc, const char **argv,
 
 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);