From c5dd04019c0ac298a35e4f69ab3a46d1dd81261d Mon Sep 17 00:00:00 2001 From: andy5995 Date: Mon, 30 Dec 2024 23:30:58 -0600 Subject: [PATCH 1/3] Use pathconf(), also check NAME_MAX --- src/config_rmw.c | 9 +++++ src/trashinfo.h | 2 + src/utils.c | 103 +++++++++++++++++++++++++++++++++++++++++++++++ src/utils.h | 6 +++ 4 files changed, 120 insertions(+) diff --git a/src/config_rmw.c b/src/config_rmw.c index 06798f7a..e6fdcfcb 100644 --- a/src/config_rmw.c +++ b/src/config_rmw.c @@ -275,6 +275,15 @@ parse_line_waste(st_waste *waste_curr, struct Canfigger *node, else if (p_state == -1) exit(p_state); + waste_curr->path_max = + (waste_curr->removable == false) ? get_path_max(waste_curr->parent) : 0; + waste_curr->name_max = + (waste_curr->removable == false) ? get_name_max(waste_curr->parent) : 0; + + //if (verbose) + //printf("path_max: %ld; name_max %ld", waste_curr->path_max, + //waste_curr->name_max); + waste_curr->is_btrfs = is_btrfs(waste_curr->parent); // get device number to use later for rename diff --git a/src/trashinfo.h b/src/trashinfo.h index 342c8c8a..251dd617 100644 --- a/src/trashinfo.h +++ b/src/trashinfo.h @@ -31,6 +31,8 @@ along with this program. If not, see . typedef struct st_waste st_waste; struct st_waste { + long path_max; + long name_max; /** The parent directory, e.g. $HOME/.local/share/Trash */ char *parent; diff --git a/src/utils.c b/src/utils.c index 03a342e8..06e14d7e 100644 --- a/src/utils.c +++ b/src/utils.c @@ -495,6 +495,98 @@ count_chars(const char c, const char *str) } +static long +get_pathconf_limit(const char *path, int name, long fallback, + const char *limit_name) +{ + if (!path) + { + fprintf(stderr, "Error: Path is NULL.\n"); + return -1; + } + + const char *actual_path = +#ifdef TEST_LIB + "/" +#else + path +#endif + ; + + long limit = pathconf(actual_path, name); + if (limit == -1) + { + if (errno == 0) + limit = fallback; // Use fallback if no limit is defined + else + { + fprintf(stderr, "Error querying %s: ", limit_name); + perror("pathconf"); + return -1; + } + } + + return limit; +} + + +long +get_name_max(const char *path) +{ + return get_pathconf_limit(path, _PC_NAME_MAX, NAME_MAX, "NAME_MAX"); +} + + +long +get_path_max(const char *path) +{ + return get_pathconf_limit(path, _PC_PATH_MAX, PATH_MAX, "PATH_MAX"); +} + +int +validate_path(const char *path) +{ + if (!path) + { + fprintf(stderr, "Error: Path is NULL.\n"); + return -1; // Invalid input + } + + long path_max = get_path_max(path); + long name_max = get_name_max(path); + size_t path_len = strlen(path); + if (path_len > (size_t) path_max) + { + fprintf(stderr, "Error: Path length (%zu) exceeds PATH_MAX (%ld).\n", + path_len, path_max); + return -1; + } + + // Check individual component lengths + const char *start = path; + while (*start) + { + const char *end = strchr(start, '/'); + size_t component_len = end ? (size_t) (end - start) : strlen(start); + + if (component_len > (size_t) name_max) + { + fprintf(stderr, + "Error: Path component '%.*s' exceeds NAME_MAX (%ld).\n", + (int) component_len, start, name_max); + return -1; + } + + if (!end) + break; + + start = end + 1; + } + + return 0; +} + + /////////////////////////////////////////////////////////////////////// #ifdef TEST_LIB @@ -723,6 +815,15 @@ test_count_chars(void) return; } +void +test_validate_path(void) +{ + assert(validate_path + ("/dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd/foo") + != 0); + return; +} + int main() @@ -756,6 +857,8 @@ main() free(escaped_path); test_count_chars(); + + test_validate_path(); return 0; } #endif diff --git a/src/utils.h b/src/utils.h index 947a725a..781da06b 100644 --- a/src/utils.h +++ b/src/utils.h @@ -64,4 +64,10 @@ bool is_dir_f(const char *pathname); int count_chars(const char c, const char *str); +long get_path_max(const char *path); + +long get_name_max(const char *path); + +int validate_path(const char *path); + #endif From 95cf2aad672697be5cc51742f02622d20a5b02d6 Mon Sep 17 00:00:00 2001 From: andy5995 Date: Wed, 1 Jan 2025 15:51:32 -0600 Subject: [PATCH 2/3] Use structure instead --- src/config_rmw.c | 137 ++++++++++++++++++++++++++++++++++++++++++++--- src/config_rmw.h | 2 + src/trashinfo.h | 10 +++- src/utils.c | 102 ----------------------------------- src/utils.h | 6 --- test/meson.build | 2 +- 6 files changed, 142 insertions(+), 117 deletions(-) diff --git a/src/config_rmw.c b/src/config_rmw.c index e6fdcfcb..f24894d5 100644 --- a/src/config_rmw.c +++ b/src/config_rmw.c @@ -160,6 +160,58 @@ realize_str(char *str, const char *homedir, const char *uid) return 0; } +static long +get_single_pathconf_limit(const char *path, int name, long fallback, + const char *limit_name) +{ + if (!path) + { + fprintf(stderr, "Error: Path is NULL.\n"); + return -1; + } + + long limit = pathconf(path, name); + if (limit == -1) + { + if (errno == 0) + limit = fallback; // Use fallback if no limit is defined + else + { + fprintf(stderr, "Error querying %s: ", limit_name); + perror("pathconf"); + return -1; + } + } + + return limit; +} + + +static long +get_name_max(const char *path) +{ + return get_single_pathconf_limit(path, _PC_NAME_MAX, NAME_MAX, "NAME_MAX"); +} + + +static long +get_path_max(const char *path) +{ + return get_single_pathconf_limit(path, _PC_PATH_MAX, PATH_MAX, "PATH_MAX"); +} + + +static int +get_pathconf_limits(const char *path, struct pathconf_limits *pathconf_limits) +{ + if ((pathconf_limits->path_max = get_path_max(path)) == -1) + return -1; + if ((pathconf_limits->name_max = get_name_max(path)) == -1) + return -1; + + return 0; +} + /*! * This function is called when the "WASTE" option is encountered in the @@ -275,14 +327,21 @@ parse_line_waste(st_waste *waste_curr, struct Canfigger *node, else if (p_state == -1) exit(p_state); - waste_curr->path_max = - (waste_curr->removable == false) ? get_path_max(waste_curr->parent) : 0; - waste_curr->name_max = - (waste_curr->removable == false) ? get_name_max(waste_curr->parent) : 0; + if (!waste_curr->removable) + { + if (get_pathconf_limits(waste_curr->parent, &waste_curr->pathconf_limits) + == -1) + exit(EXIT_FAILURE); + } + else + { + waste_curr->pathconf_limits.path_max = 0; + waste_curr->pathconf_limits.name_max = 0; + } //if (verbose) - //printf("path_max: %ld; name_max %ld", waste_curr->path_max, - //waste_curr->name_max); + //printf("path_max: %ld; name_max %ld", waste_curr->pathconf_limits.path_max, + //waste_curr->pathconf_limits.name_max); waste_curr->is_btrfs = is_btrfs(waste_curr->parent); @@ -468,3 +527,69 @@ show_folder_line(const char *folder, const bool is_r, const bool is_attached) putchar('\n'); } + + +int +validate_path(const char *path, struct pathconf_limits *pathconf_limits) +{ + if (!path) + { + fprintf(stderr, "Error: Path is NULL.\n"); + return -1; // Invalid input + } + + size_t path_len = strlen(path); + if (path_len > (size_t) pathconf_limits->path_max) + { + fprintf(stderr, "Error: Path length (%zu) exceeds PATH_MAX (%ld).\n", + path_len, pathconf_limits->path_max); + return -1; + } + + // Check individual component lengths + const char *start = path; + while (*start) + { + const char *end = strchr(start, '/'); + size_t component_len = end ? (size_t) (end - start) : strlen(start); + + if (component_len > (size_t) pathconf_limits->name_max) + { + fprintf(stderr, + "Error: Path component '%.*s' exceeds NAME_MAX (%ld).\n", + (int) component_len, start, pathconf_limits->name_max); + return -1; + } + + if (!end) + break; + + start = end + 1; + } + + return 0; +} + +/////////////////////////////////////////////////////////////////////// +#ifdef TEST_LIB + +#include "test.h" + +void +test_validate_path(void) +{ + struct pathconf_limits pathconf_limits; + get_pathconf_limits("/", &pathconf_limits); + assert(validate_path + ("/dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd/foo", + &pathconf_limits) != 0); + return; +} + +int +main(void) +{ + test_validate_path(); +} + +#endif diff --git a/src/config_rmw.h b/src/config_rmw.h index 63ceb775..e8d805b4 100644 --- a/src/config_rmw.h +++ b/src/config_rmw.h @@ -51,3 +51,5 @@ void init_config_data(st_config * x); void show_folder_line(const char *folder, const bool is_r, const bool is_attached); + +int validate_path(const char *path, struct pathconf_limits *pathconf_limits); diff --git a/src/trashinfo.h b/src/trashinfo.h index 251dd617..4af194e3 100644 --- a/src/trashinfo.h +++ b/src/trashinfo.h @@ -25,14 +25,20 @@ along with this program. If not, see . #include "time_rmw.h" +struct pathconf_limits +{ + long path_max; + long name_max; +}; + /** Each waste directory is added to a linked list and has the data * from this structure associated with it. */ typedef struct st_waste st_waste; struct st_waste { - long path_max; - long name_max; + struct pathconf_limits pathconf_limits; + /** The parent directory, e.g. $HOME/.local/share/Trash */ char *parent; diff --git a/src/utils.c b/src/utils.c index 06e14d7e..289eae32 100644 --- a/src/utils.c +++ b/src/utils.c @@ -495,98 +495,6 @@ count_chars(const char c, const char *str) } -static long -get_pathconf_limit(const char *path, int name, long fallback, - const char *limit_name) -{ - if (!path) - { - fprintf(stderr, "Error: Path is NULL.\n"); - return -1; - } - - const char *actual_path = -#ifdef TEST_LIB - "/" -#else - path -#endif - ; - - long limit = pathconf(actual_path, name); - if (limit == -1) - { - if (errno == 0) - limit = fallback; // Use fallback if no limit is defined - else - { - fprintf(stderr, "Error querying %s: ", limit_name); - perror("pathconf"); - return -1; - } - } - - return limit; -} - - -long -get_name_max(const char *path) -{ - return get_pathconf_limit(path, _PC_NAME_MAX, NAME_MAX, "NAME_MAX"); -} - - -long -get_path_max(const char *path) -{ - return get_pathconf_limit(path, _PC_PATH_MAX, PATH_MAX, "PATH_MAX"); -} - -int -validate_path(const char *path) -{ - if (!path) - { - fprintf(stderr, "Error: Path is NULL.\n"); - return -1; // Invalid input - } - - long path_max = get_path_max(path); - long name_max = get_name_max(path); - size_t path_len = strlen(path); - if (path_len > (size_t) path_max) - { - fprintf(stderr, "Error: Path length (%zu) exceeds PATH_MAX (%ld).\n", - path_len, path_max); - return -1; - } - - // Check individual component lengths - const char *start = path; - while (*start) - { - const char *end = strchr(start, '/'); - size_t component_len = end ? (size_t) (end - start) : strlen(start); - - if (component_len > (size_t) name_max) - { - fprintf(stderr, - "Error: Path component '%.*s' exceeds NAME_MAX (%ld).\n", - (int) component_len, start, name_max); - return -1; - } - - if (!end) - break; - - start = end + 1; - } - - return 0; -} - - /////////////////////////////////////////////////////////////////////// #ifdef TEST_LIB @@ -815,15 +723,6 @@ test_count_chars(void) return; } -void -test_validate_path(void) -{ - assert(validate_path - ("/dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd/foo") - != 0); - return; -} - int main() @@ -858,7 +757,6 @@ main() test_count_chars(); - test_validate_path(); return 0; } #endif diff --git a/src/utils.h b/src/utils.h index 781da06b..947a725a 100644 --- a/src/utils.h +++ b/src/utils.h @@ -64,10 +64,4 @@ bool is_dir_f(const char *pathname); int count_chars(const char c, const char *str); -long get_path_max(const char *path); - -long get_name_max(const char *path); - -int validate_path(const char *path); - #endif diff --git a/test/meson.build b/test/meson.build index 670952e4..d62cfd53 100644 --- a/test/meson.build +++ b/test/meson.build @@ -9,7 +9,7 @@ if faketime.found() ) endif -test_cases = ['strings_rmw', 'utils', 'restore'] +test_cases = ['config_rmw', 'strings_rmw', 'utils', 'restore'] scripts = [ 'test_basic.sh', From c56e5cd5c122fd3b2274d8ba6bfd13f652f76f7c Mon Sep 17 00:00:00 2001 From: andy5995 Date: Wed, 1 Jan 2025 17:00:53 -0600 Subject: [PATCH 3/3] Improve test --- src/config_rmw.c | 77 +++++++++++++++++++++++++++++++++++++----------- 1 file changed, 60 insertions(+), 17 deletions(-) diff --git a/src/config_rmw.c b/src/config_rmw.c index f24894d5..3fe9c62d 100644 --- a/src/config_rmw.c +++ b/src/config_rmw.c @@ -327,17 +327,11 @@ parse_line_waste(st_waste *waste_curr, struct Canfigger *node, else if (p_state == -1) exit(p_state); - if (!waste_curr->removable) - { - if (get_pathconf_limits(waste_curr->parent, &waste_curr->pathconf_limits) - == -1) - exit(EXIT_FAILURE); - } - else - { - waste_curr->pathconf_limits.path_max = 0; - waste_curr->pathconf_limits.name_max = 0; - } + struct pathconf_limits temp = { 0, 0 }; + waste_curr->pathconf_limits = temp; + if (get_pathconf_limits(waste_curr->parent, &waste_curr->pathconf_limits) + == -1) + exit(EXIT_FAILURE); //if (verbose) //printf("path_max: %ld; name_max %ld", waste_curr->pathconf_limits.path_max, @@ -539,10 +533,10 @@ validate_path(const char *path, struct pathconf_limits *pathconf_limits) } size_t path_len = strlen(path); - if (path_len > (size_t) pathconf_limits->path_max) + if (path_len > (size_t) pathconf_limits->path_max - 1) { - fprintf(stderr, "Error: Path length (%zu) exceeds PATH_MAX (%ld).\n", - path_len, pathconf_limits->path_max); + fprintf(stderr, "Error: Path length (%zu) exceeds PATH_MAX-1 (%ld).\n", + path_len, pathconf_limits->path_max - 1); return -1; } @@ -556,8 +550,8 @@ validate_path(const char *path, struct pathconf_limits *pathconf_limits) if (component_len > (size_t) pathconf_limits->name_max) { fprintf(stderr, - "Error: Path component '%.*s' exceeds NAME_MAX (%ld).\n", - (int) component_len, start, pathconf_limits->name_max); + "Error: Path component '%.*s' exceeds NAME_MAX-1 (%ld).\n", + (int) component_len, start, pathconf_limits->name_max - 1); return -1; } @@ -579,10 +573,59 @@ void test_validate_path(void) { struct pathconf_limits pathconf_limits; - get_pathconf_limits("/", &pathconf_limits); + long ret = get_pathconf_limits("/", &pathconf_limits); + assert(ret != -1); + +// This should be true on most systems, and if it's ever false, the test +// needs to be changed. + assert(pathconf_limits.path_max >= 128 && pathconf_limits.name_max >= 128); + assert(pathconf_limits.path_max <= 4096 + && pathconf_limits.name_max <= 4096); assert(validate_path ("/dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd/foo", &pathconf_limits) != 0); + + long i, k; + long separators = pathconf_limits.path_max / pathconf_limits.name_max; + char tmp[pathconf_limits.path_max + separators + 1]; + fprintf(stderr, "sizeof tmp: %ld\n", sizeof(tmp)); + + memset(tmp, 0, sizeof(tmp)); + char *ptr = tmp; + for (k = 0; k < separators; k++) + { + *ptr++ = '/'; + for (i = 0; i < pathconf_limits.name_max; i++) + *ptr++ = 'a'; + } + ptr--; + *ptr = '\0'; + fprintf(stderr, "tmp: %ld\n", strlen(tmp)); + assert(validate_path(tmp, &pathconf_limits) == 0); + + // Exceed path_max + *ptr++ = 'a'; + *ptr = '\0'; + fprintf(stderr, "tmp: %ld\n", strlen(tmp)); + assert(validate_path(tmp, &pathconf_limits) == -1); + + // Exceed name_max + memset(tmp, 0, sizeof(tmp)); + ptr = tmp; + for (k = 0; k < separators; k++) + { + *ptr++ = '/'; + for (i = 0; i < pathconf_limits.name_max - 2; i++) + *ptr++ = 'a'; + } + + for (k = 0; k < 5; k++) + *ptr++ = 'a'; + *ptr = '\0'; + + fprintf(stderr, "tmp: %ld\n", strlen(tmp)); + assert(validate_path(tmp, &pathconf_limits) != 0); + return; }