From edd51986c801e3c1ddc85c7633a45edf147a6c00 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 11 Feb 2026 18:25:34 +0000 Subject: [PATCH 1/7] Initial plan From 24840f08958339d37c9db2fd8b4f0ef475aa54b7 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 11 Feb 2026 18:33:45 +0000 Subject: [PATCH 2/7] Fix directory listing comparison to use exact match instead of prefix Co-authored-by: JohnAmadis <17320783+JohnAmadis@users.noreply.github.com> --- src/dmdevfs.c | 34 +++++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/src/dmdevfs.c b/src/dmdevfs.c index e87966b..503bb68 100644 --- a/src/dmdevfs.c +++ b/src/dmdevfs.c @@ -1180,23 +1180,35 @@ static int compare_driver_directory( const void* data, const void* user_data ) return 0; } - char directory_path[MAX_PATH_LENGTH] = {0}; - if(read_driver_parent_directory(node, directory_path, sizeof(directory_path)) != 0) + char parent_dir[MAX_PATH_LENGTH] = {0}; + if(read_driver_parent_directory(node, parent_dir, sizeof(parent_dir)) != 0) { return -1; } - char* driver_path = directory_path; - while(*path) + // Compare paths, handling optional trailing slashes + // Get lengths + size_t path_len = strlen(path); + size_t parent_len = strlen(parent_dir); + + // Remove trailing slashes from both paths for comparison + while (path_len > 1 && path[path_len - 1] == '/') { - if(*path != *driver_path) - { - return 1; - } - path++; - driver_path++; + path_len--; } - return 0; + while (parent_len > 1 && parent_dir[parent_len - 1] == '/') + { + parent_len--; + } + + // Lengths must match + if (path_len != parent_len) + { + return 1; + } + + // Content must match + return strncmp(path, parent_dir, path_len); } /** From 7c68a909ba0d352b3e9a6478def873dcd68b2eff Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 11 Feb 2026 18:34:57 +0000 Subject: [PATCH 3/7] Extract path comparison logic into reusable helper function Co-authored-by: JohnAmadis <17320783+JohnAmadis@users.noreply.github.com> --- src/dmdevfs.c | 60 ++++++++++++++++++++++++++++++++------------------- 1 file changed, 38 insertions(+), 22 deletions(-) diff --git a/src/dmdevfs.c b/src/dmdevfs.c index 503bb68..7068208 100644 --- a/src/dmdevfs.c +++ b/src/dmdevfs.c @@ -82,6 +82,7 @@ static Dmod_Context_t* prepare_driver_module(const char* driver_name, bool* was_ static void cleanup_driver_module(const char* driver_name, bool was_loaded, bool was_enabled); static int read_driver_parent_directory( const driver_node_t* node, char* path_buffer, size_t buffer_size ); static int read_driver_node_path( const driver_node_t* node, char* path_buffer, size_t buffer_size ); +static int compare_paths_ignore_trailing_slash( const char* path1, const char* path2 ); static int compare_driver_directory( const void* data, const void* user_data ); static int compare_driver_node_path( const void* data, const void* user_data ); static int compare_driver(const void* data, const void* user_data ); @@ -643,7 +644,7 @@ dmod_dmfsi_dif_api_declaration( 1.0, dmdevfs, int, _readdir, (dmfsi_context_t ct return DMFSI_ERR_GENERAL; } - bool file_should_be_listed = strcmp(dir_node->directory_path, parent_dir) == 0; + bool file_should_be_listed = compare_paths_ignore_trailing_slash(dir_node->directory_path, parent_dir) == 0; if(file_should_be_listed) { strncpy(entry->name, driver->path, sizeof(entry->name)); @@ -1169,46 +1170,61 @@ static int read_driver_node_path( const driver_node_t* node, char* path_buffer, } /** - * @brief Compare the path of a driver directory with a given path + * @brief Compare two paths, ignoring trailing slashes + * @return 0 if equal, non-zero if different */ -static int compare_driver_directory( const void* data, const void* user_data ) +static int compare_paths_ignore_trailing_slash( const char* path1, const char* path2 ) { - const driver_node_t* node = (const driver_node_t*)data; - const char* path = (const char*)user_data; - if (node == NULL || path == NULL) - { - return 0; - } - - char parent_dir[MAX_PATH_LENGTH] = {0}; - if(read_driver_parent_directory(node, parent_dir, sizeof(parent_dir)) != 0) + if (path1 == NULL || path2 == NULL) { - return -1; + return (path1 == path2) ? 0 : 1; } - // Compare paths, handling optional trailing slashes // Get lengths - size_t path_len = strlen(path); - size_t parent_len = strlen(parent_dir); + size_t len1 = strlen(path1); + size_t len2 = strlen(path2); // Remove trailing slashes from both paths for comparison - while (path_len > 1 && path[path_len - 1] == '/') + // Keep at least "/" if that's the entire path + while (len1 > 1 && path1[len1 - 1] == '/') { - path_len--; + len1--; } - while (parent_len > 1 && parent_dir[parent_len - 1] == '/') + while (len2 > 1 && path2[len2 - 1] == '/') { - parent_len--; + len2--; } // Lengths must match - if (path_len != parent_len) + if (len1 != len2) { return 1; } // Content must match - return strncmp(path, parent_dir, path_len); + return strncmp(path1, path2, len1); +} + +/** + * @brief Compare the path of a driver directory with a given path + */ +static int compare_driver_directory( const void* data, const void* user_data ) +{ + const driver_node_t* node = (const driver_node_t*)data; + const char* path = (const char*)user_data; + if (node == NULL || path == NULL) + { + return 0; + } + + char parent_dir[MAX_PATH_LENGTH] = {0}; + if(read_driver_parent_directory(node, parent_dir, sizeof(parent_dir)) != 0) + { + return -1; + } + + // Use helper function to compare paths, handling optional trailing slashes + return compare_paths_ignore_trailing_slash(path, parent_dir); } /** From 6c255758876a80a817bf74a2e901170e1c1cc3b4 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 11 Feb 2026 18:36:02 +0000 Subject: [PATCH 4/7] Address code review feedback: improve NULL handling and documentation Co-authored-by: JohnAmadis <17320783+JohnAmadis@users.noreply.github.com> --- src/dmdevfs.c | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/src/dmdevfs.c b/src/dmdevfs.c index 7068208..197aa2e 100644 --- a/src/dmdevfs.c +++ b/src/dmdevfs.c @@ -1171,13 +1171,23 @@ static int read_driver_node_path( const driver_node_t* node, char* path_buffer, /** * @brief Compare two paths, ignoring trailing slashes + * @param path1 First path to compare + * @param path2 Second path to compare * @return 0 if equal, non-zero if different + * + * Note: The root path "/" is treated specially and retains its slash. + * For example, "/" and "//" are considered equal, but "dir" and "dir/" are also equal. */ static int compare_paths_ignore_trailing_slash( const char* path1, const char* path2 ) { + // Handle NULL pointers - both NULL is equal, one NULL is different + if (path1 == NULL && path2 == NULL) + { + return 0; + } if (path1 == NULL || path2 == NULL) { - return (path1 == path2) ? 0 : 1; + return 1; } // Get lengths @@ -1185,7 +1195,7 @@ static int compare_paths_ignore_trailing_slash( const char* path1, const char* p size_t len2 = strlen(path2); // Remove trailing slashes from both paths for comparison - // Keep at least "/" if that's the entire path + // Keep at least "/" if that's the entire path (len > 1 ensures we keep root "/") while (len1 > 1 && path1[len1 - 1] == '/') { len1--; @@ -1207,6 +1217,18 @@ static int compare_paths_ignore_trailing_slash( const char* path1, const char* p /** * @brief Compare the path of a driver directory with a given path + * + * This function compares the parent directory of a driver node with a given path. + * It's used by opendir/readdir to find all driver nodes that belong to a specific directory. + * + * @param data Pointer to driver_node_t + * @param user_data Pointer to directory path string + * @return 0 if the node's parent matches the given path, non-zero otherwise + * + * Example: When listing directory "dmspiflash0", this function finds all nodes + * whose parent directory is "dmspiflash0" (e.g., nodes with path "dmspiflash0/1"). + * + * Note: Trailing slashes are ignored in comparison, so "dmspiflash0" matches "dmspiflash0/". */ static int compare_driver_directory( const void* data, const void* user_data ) { @@ -1224,6 +1246,8 @@ static int compare_driver_directory( const void* data, const void* user_data ) } // Use helper function to compare paths, handling optional trailing slashes + // This ensures exact path matching (not prefix matching) which is critical + // to prevent "/" from incorrectly matching subdirectories like "dmspiflash0/" return compare_paths_ignore_trailing_slash(path, parent_dir); } From da1510116ca0c6146c497a99c0aa5196a0585b7e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 11 Feb 2026 19:09:50 +0000 Subject: [PATCH 5/7] Fix readdir to return basename only, not full path Co-authored-by: JohnAmadis <17320783+JohnAmadis@users.noreply.github.com> --- src/dmdevfs.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/dmdevfs.c b/src/dmdevfs.c index 197aa2e..53342e3 100644 --- a/src/dmdevfs.c +++ b/src/dmdevfs.c @@ -647,7 +647,8 @@ dmod_dmfsi_dif_api_declaration( 1.0, dmdevfs, int, _readdir, (dmfsi_context_t ct bool file_should_be_listed = compare_paths_ignore_trailing_slash(dir_node->directory_path, parent_dir) == 0; if(file_should_be_listed) { - strncpy(entry->name, driver->path, sizeof(entry->name)); + // Extract basename from the full path for the directory entry + read_base_name(driver->path, entry->name, sizeof(entry->name)); dmdrvi_stat_t stat; int res = driver_stat(driver, driver->path, &stat); @@ -662,7 +663,8 @@ dmod_dmfsi_dif_api_declaration( 1.0, dmdevfs, int, _readdir, (dmfsi_context_t ct } else { - strncpy(entry->name, parent_dir, sizeof(entry->name)); + // Extract basename from parent directory for subdirectory entries + read_base_name(parent_dir, entry->name, sizeof(entry->name)); entry->size = 0; entry->attr = DMFSI_ATTR_DIRECTORY; } From 4734c2909650fc72cde7b97c4ed55ecd338f5178 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 11 Feb 2026 19:11:27 +0000 Subject: [PATCH 6/7] Fix subdirectory name extraction to handle trailing slashes correctly Co-authored-by: JohnAmadis <17320783+JohnAmadis@users.noreply.github.com> --- src/dmdevfs.c | 61 +++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 59 insertions(+), 2 deletions(-) diff --git a/src/dmdevfs.c b/src/dmdevfs.c index 53342e3..66c1873 100644 --- a/src/dmdevfs.c +++ b/src/dmdevfs.c @@ -77,6 +77,7 @@ static int unconfigure_drivers(dmfsi_context_t ctx); static bool is_file(const char* path); static bool is_driver( const char* name); static void read_base_name(const char* path, char* base_name, size_t name_size); +static void read_dir_name_from_path(const char* path, char* dir_name, size_t name_size); static dmini_context_t read_driver_for_config(const char* config_path, char* driver_name, size_t name_size, const char* default_driver); static Dmod_Context_t* prepare_driver_module(const char* driver_name, bool* was_loaded, bool* was_enabled); static void cleanup_driver_module(const char* driver_name, bool was_loaded, bool was_enabled); @@ -663,8 +664,9 @@ dmod_dmfsi_dif_api_declaration( 1.0, dmdevfs, int, _readdir, (dmfsi_context_t ct } else { - // Extract basename from parent directory for subdirectory entries - read_base_name(parent_dir, entry->name, sizeof(entry->name)); + // Extract directory name from parent path for subdirectory entries + // This handles paths like "dev/" -> "dev" + read_dir_name_from_path(parent_dir, entry->name, sizeof(entry->name)); entry->size = 0; entry->attr = DMFSI_ATTR_DIRECTORY; } @@ -1009,6 +1011,61 @@ static void read_base_name(const char* path, char* base_name, size_t name_size) base_name[name_size - 1] = '\0'; } +/** + * @brief Extract directory name from a path, handling trailing slashes + * @param path Path to extract directory name from (may have trailing slash) + * @param dir_name Output buffer for directory name + * @param name_size Size of output buffer + * + * Examples: + * - "dev/" -> "dev" + * - "/dev/" -> "dev" + * - "dmspiflash0/" -> "dmspiflash0" + * - "/" -> "" (root has no name) + */ +static void read_dir_name_from_path(const char* path, char* dir_name, size_t name_size) +{ + if (path == NULL || dir_name == NULL || name_size == 0) + { + if (dir_name && name_size > 0) + { + dir_name[0] = '\0'; + } + return; + } + + // Find the length without trailing slashes + size_t len = strlen(path); + while (len > 1 && path[len - 1] == '/') + { + len--; + } + + // Special case: if path is just "/", return empty + if (len == 1 && path[0] == '/') + { + dir_name[0] = '\0'; + return; + } + + // Find the last slash before the directory name + const char* last_slash = NULL; + for (size_t i = 0; i < len; i++) + { + if (path[i] == '/') + { + last_slash = &path[i]; + } + } + + // Extract the directory name + const char* name_start = (last_slash != NULL) ? last_slash + 1 : path; + size_t name_len = len - (name_start - path); + size_t copy_len = (name_len < name_size - 1) ? name_len : name_size - 1; + strncpy(dir_name, name_start, copy_len); + dir_name[copy_len] = '\0'; +} + /** * @brief Read driver name from configuration file */ From 4c1d66a1fc07b5bc1368b41e21f3579f2d9ee368 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 11 Feb 2026 19:12:07 +0000 Subject: [PATCH 7/7] Use Dmod_SnPrintf for safer string copying with guaranteed null-termination Co-authored-by: JohnAmadis <17320783+JohnAmadis@users.noreply.github.com> --- src/dmdevfs.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/dmdevfs.c b/src/dmdevfs.c index 66c1873..19d713b 100644 --- a/src/dmdevfs.c +++ b/src/dmdevfs.c @@ -1061,9 +1061,14 @@ static void read_dir_name_from_path(const char* path, char* dir_name, size_t nam // Extract the directory name const char* name_start = (last_slash != NULL) ? last_slash + 1 : path; size_t name_len = len - (name_start - path); - size_t copy_len = (name_len < name_size - 1) ? name_len : name_size - 1; - strncpy(dir_name, name_start, copy_len); - dir_name[copy_len] = '\0'; + + // Use snprintf for safe copying with guaranteed null-termination + int written = Dmod_SnPrintf(dir_name, name_size, "%.*s", (int)name_len, name_start); + if (written < 0 || (size_t)written >= name_size) + { + // Truncated, but snprintf ensures null-termination + dir_name[name_size - 1] = '\0'; + } } /**