From 58269d751c973365ee98c917ddcd4b165c75e623 Mon Sep 17 00:00:00 2001 From: Stephen Seo Date: Thu, 4 Jul 2024 14:48:56 +0900 Subject: [PATCH] Impl. handling symbolic links --- src/helpers.h | 2 + src/main.c | 9 +++- src/parser.c | 119 +++++++++++++++++++++++++++++++++++++++++++------- src/parser.h | 7 +++ 4 files changed, 120 insertions(+), 17 deletions(-) diff --git a/src/helpers.h b/src/helpers.h index 3089b33..f5915fd 100644 --- a/src/helpers.h +++ b/src/helpers.h @@ -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); diff --git a/src/main.c b/src/main.c index 1b12eee..b5f2c37 100644 --- a/src/main.c +++ b/src/main.c @@ -21,8 +21,13 @@ #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; } diff --git a/src/parser.c b/src/parser.c index eea47bb..c14f2aa 100644 --- a/src/parser.c +++ b/src/parser.c @@ -30,10 +30,12 @@ #include #include #include +#include #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; } } diff --git a/src/parser.h b/src/parser.h index 73cc7ca..19630d0 100644 --- a/src/parser.h +++ b/src/parser.h @@ -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);