diff --git a/src/config_rmw.c b/src/config_rmw.c index 06798f7a..3fe9c62d 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,6 +327,16 @@ parse_line_waste(st_waste *waste_curr, struct Canfigger *node, else if (p_state == -1) exit(p_state); + 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, + //waste_curr->pathconf_limits.name_max); + waste_curr->is_btrfs = is_btrfs(waste_curr->parent); // get device number to use later for rename @@ -459,3 +521,118 @@ 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 - 1) + { + fprintf(stderr, "Error: Path length (%zu) exceeds PATH_MAX-1 (%ld).\n", + path_len, pathconf_limits->path_max - 1); + 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-1 (%ld).\n", + (int) component_len, start, pathconf_limits->name_max - 1); + 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; + 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; +} + +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 342c8c8a..4af194e3 100644 --- a/src/trashinfo.h +++ b/src/trashinfo.h @@ -25,12 +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 { + 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 03a342e8..289eae32 100644 --- a/src/utils.c +++ b/src/utils.c @@ -756,6 +756,7 @@ main() free(escaped_path); test_count_chars(); + return 0; } #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',