Skip to content

Commit 53e3330

Browse files
authored
Merge pull request #33 from choco-technologies/copilot/fix-dmdevfs-directory-listing
Fix opendir/readdir returning no entries when drivers have major/minor device numbers
2 parents ff7ab03 + 437d802 commit 53e3330

1 file changed

Lines changed: 107 additions & 13 deletions

File tree

src/dmdevfs.c

Lines changed: 107 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ static bool is_file(const char* path);
8080
static bool is_driver( const char* name);
8181
static void read_base_name(const char* path, char* base_name, size_t name_size);
8282
static void read_dir_name_from_path(const char* path, char* dir_name, size_t name_size);
83+
static void read_next_subdir_name(const char* base_path, const char* full_path, char* dir_name, size_t name_size);
8384
static dmini_context_t read_driver_for_config(const char* config_path, char* driver_name, size_t name_size, const char* default_driver);
8485
static Dmod_Context_t* prepare_driver_module(const char* driver_name, bool* was_loaded, bool* was_enabled);
8586
static void cleanup_driver_module(const char* driver_name, bool was_loaded, bool was_enabled);
@@ -675,9 +676,9 @@ dmod_dmfsi_dif_api_declaration( 1.0, dmdevfs, int, _readdir, (dmfsi_context_t ct
675676
}
676677
else
677678
{
678-
// Extract directory name from parent path for subdirectory entries
679-
// This handles paths like "dev/" -> "dev"
680-
read_dir_name_from_path(parent_dir, entry->name, sizeof(entry->name));
679+
// Extract the immediate subdirectory name relative to the listing directory.
680+
// E.g. listing "/" with a driver whose parent is "dmgpio8/" yields "dmgpio8".
681+
read_next_subdir_name(dir_node->directory_path, parent_dir, entry->name, sizeof(entry->name));
681682
entry->size = 0;
682683
entry->attr = DMFSI_ATTR_DIRECTORY;
683684
}
@@ -1158,6 +1159,73 @@ static void read_dir_name_from_path(const char* path, char* dir_name, size_t nam
11581159
}
11591160
}
11601161

1162+
/**
1163+
* @brief Extract the first path component of full_path that comes after base_path
1164+
* @param base_path The directory currently being listed (e.g., "/" or "foo/")
1165+
* @param full_path The driver's parent directory path (e.g., "foo/bar/")
1166+
* @param dir_name Output buffer for the immediate subdirectory name
1167+
* @param name_size Size of the output buffer
1168+
*
1169+
* Examples:
1170+
* - base="/", full="dmgpio8/" -> "dmgpio8"
1171+
* - base="/", full="a/b/c/" -> "a"
1172+
* - base="a/", full="a/b/c/" -> "b"
1173+
* - base="a/b/", full="a/b/c/" -> "c"
1174+
*/
1175+
static void read_next_subdir_name(const char* base_path, const char* full_path, char* dir_name, size_t name_size)
1176+
{
1177+
if (base_path == NULL || full_path == NULL || dir_name == NULL || name_size == 0)
1178+
{
1179+
if (dir_name && name_size > 0)
1180+
{
1181+
dir_name[0] = '\0';
1182+
}
1183+
return;
1184+
}
1185+
1186+
// Compute effective length of base_path without trailing slashes
1187+
size_t base_len = strlen(base_path);
1188+
while (base_len > 1 && base_path[base_len - 1] == '/')
1189+
{
1190+
base_len--;
1191+
}
1192+
1193+
const char* start;
1194+
if (base_len == 1 && base_path[0] == '/')
1195+
{
1196+
// Base is the root directory; full_path has no leading slash
1197+
start = full_path;
1198+
}
1199+
else
1200+
{
1201+
// Skip past the base_path prefix and the separator '/'
1202+
if (strncmp(full_path, base_path, base_len) == 0 && full_path[base_len] == '/')
1203+
{
1204+
start = full_path + base_len + 1;
1205+
}
1206+
else
1207+
{
1208+
// Fallback: return the first component of full_path
1209+
start = full_path;
1210+
}
1211+
}
1212+
1213+
// Copy up to the next '/' (or end of string)
1214+
const char* end = start;
1215+
while (*end != '\0' && *end != '/')
1216+
{
1217+
end++;
1218+
}
1219+
1220+
size_t len = (size_t)(end - start);
1221+
if (len >= name_size)
1222+
{
1223+
len = name_size - 1;
1224+
}
1225+
strncpy(dir_name, start, len);
1226+
dir_name[len] = '\0';
1227+
}
1228+
11611229
/**
11621230
* @brief Read driver name from configuration file
11631231
*/
@@ -1369,17 +1437,20 @@ static int compare_paths_ignore_trailing_slash( const char* path1, const char* p
13691437
/**
13701438
* @brief Compare the path of a driver directory with a given path
13711439
*
1372-
* This function compares the parent directory of a driver node with a given path.
1373-
* It's used by opendir/readdir to find all driver nodes that belong to a specific directory.
1440+
* This function checks whether a driver node is reachable from the given path,
1441+
* either directly (its parent directory exactly matches path) or indirectly
1442+
* (its parent directory is a subdirectory of path).
13741443
*
13751444
* @param data Pointer to driver_node_t
13761445
* @param user_data Pointer to directory path string
1377-
* @return 0 if the node's parent matches the given path, non-zero otherwise
1446+
* @return 0 if the node is reachable from the given path, non-zero otherwise
13781447
*
1379-
* Example: When listing directory "dmspiflash0", this function finds all nodes
1380-
* whose parent directory is "dmspiflash0" (e.g., nodes with path "dmspiflash0/1").
1448+
* Example: When listing directory "/", this function returns 0 for any driver
1449+
* node, including those with parent "dmspiflash0/" or deeper paths.
1450+
* When listing "dmspiflash0", it returns 0 for nodes whose parent starts with
1451+
* "dmspiflash0/", enabling both direct files and nested subdirectories.
13811452
*
1382-
* Note: Trailing slashes are ignored in comparison, so "dmspiflash0" matches "dmspiflash0/".
1453+
* Note: Trailing slashes are ignored in comparison.
13831454
*/
13841455
static int compare_driver_directory( const void* data, const void* user_data )
13851456
{
@@ -1396,10 +1467,33 @@ static int compare_driver_directory( const void* data, const void* user_data )
13961467
return -1;
13971468
}
13981469

1399-
// Use helper function to compare paths, handling optional trailing slashes
1400-
// This ensures exact path matching (not prefix matching) which is critical
1401-
// to prevent "/" from incorrectly matching subdirectories like "dmspiflash0/"
1402-
return compare_paths_ignore_trailing_slash(path, parent_dir);
1470+
// Check for exact match (driver is directly in this directory)
1471+
if (compare_paths_ignore_trailing_slash(path, parent_dir) == 0)
1472+
{
1473+
return 0;
1474+
}
1475+
1476+
// Check if the driver is in a subdirectory of path.
1477+
// Determine the effective length of path without trailing slashes.
1478+
size_t path_len = strlen(path);
1479+
while (path_len > 1 && path[path_len - 1] == '/')
1480+
{
1481+
path_len--;
1482+
}
1483+
1484+
// Root directory "/" is an ancestor of every path
1485+
if (path_len == 1 && path[0] == '/')
1486+
{
1487+
return 0;
1488+
}
1489+
1490+
// parent_dir must start with path followed by '/' to be a subdirectory
1491+
if (strncmp(parent_dir, path, path_len) == 0 && parent_dir[path_len] == '/')
1492+
{
1493+
return 0;
1494+
}
1495+
1496+
return 1;
14031497
}
14041498

14051499
/**

0 commit comments

Comments
 (0)