From d52cc73754904c1ab2b3396dacd8eaced358d1bb Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 8 Feb 2026 10:09:47 +0000 Subject: [PATCH 1/3] Initial plan From 3e222f0853c27f36bbf4a742016ab27334fef99b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 8 Feb 2026 10:13:26 +0000 Subject: [PATCH 2/3] Fix get_mount_point_for_path to select longest matching mount point Co-authored-by: JohnAmadis <17320783+JohnAmadis@users.noreply.github.com> --- src/dmvfs.c | 22 ++- tests/CMakeLists.txt | 8 +- tests/test_mount_point_selection.c | 239 +++++++++++++++++++++++++++++ 3 files changed, 263 insertions(+), 6 deletions(-) create mode 100644 tests/test_mount_point_selection.c diff --git a/src/dmvfs.c b/src/dmvfs.c index a08a55f..1b67589 100644 --- a/src/dmvfs.c +++ b/src/dmvfs.c @@ -336,17 +336,29 @@ static mount_point_t* get_mount_point_for_path(const char* path) return NULL; } + mount_point_t* best_match = NULL; + size_t best_match_length = 0; + for(int i = 0; i < g_max_mount_points; i++) { - if(g_mount_points[i].mount_point != NULL && - strncmp(path, g_mount_points[i].mount_point, strlen(g_mount_points[i].mount_point)) == 0) + if(g_mount_points[i].mount_point != NULL) { - return &g_mount_points[i]; + size_t mount_point_length = strlen(g_mount_points[i].mount_point); + if(strncmp(path, g_mount_points[i].mount_point, mount_point_length) == 0 && + mount_point_length > best_match_length) + { + best_match = &g_mount_points[i]; + best_match_length = mount_point_length; + } } } - DMOD_LOG_WARN("No mount point found for path '%s'\n", path); - return NULL; + if(best_match == NULL) + { + DMOD_LOG_WARN("No mount point found for path '%s'\n", path); + } + + return best_match; } /** diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 87e8ed1..fd62a79 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -36,4 +36,10 @@ include_directories(${PROJECT_SOURCE_DIR}) target_link_libraries(${PROJECT_NAME} dmod dmvfs dmlist) target_link_options(${PROJECT_NAME} PRIVATE -L ${DMOD_DIR}/scripts) -target_link_options(${PROJECT_NAME} PRIVATE -T ${CMAKE_CURRENT_SOURCE_DIR}/main.ld) \ No newline at end of file +target_link_options(${PROJECT_NAME} PRIVATE -T ${CMAKE_CURRENT_SOURCE_DIR}/main.ld) + +# Add mount point selection test +add_executable(mount_point_test test_mount_point_selection.c) +target_link_libraries(mount_point_test dmod dmvfs) +target_link_options(mount_point_test PRIVATE -L ${DMOD_DIR}/scripts) +target_link_options(mount_point_test PRIVATE -T ${CMAKE_CURRENT_SOURCE_DIR}/main.ld) \ No newline at end of file diff --git a/tests/test_mount_point_selection.c b/tests/test_mount_point_selection.c new file mode 100644 index 0000000..1fe0103 --- /dev/null +++ b/tests/test_mount_point_selection.c @@ -0,0 +1,239 @@ +/* + * Test program to verify correct mount point selection + * Tests the fix for: get_mount_point_for_path should return the longest matching mount point + */ + +#define DMOD_ENABLE_REGISTRATION +#include +#include +#include +#include "dmod.h" +#include "dmvfs.h" + +static int test_passed = 0; +static int test_failed = 0; + +#define TEST_PASS(msg) do { \ + printf("✓ PASS: %s\n", msg); \ + test_passed++; \ +} while(0) + +#define TEST_FAIL(msg) do { \ + printf("✗ FAIL: %s\n", msg); \ + test_failed++; \ +} while(0) + +int main(int argc, char *argv[]) +{ + if (argc < 3) { + printf("Usage: %s \n", argv[0]); + printf("This test requires two filesystem modules to test mount point selection.\n"); + return 1; + } + + const char* fs1_path = argv[1]; + const char* fs2_path = argv[2]; + + printf("========================================\n"); + printf(" Mount Point Selection Test\n"); + printf("========================================\n\n"); + + // Load first filesystem module + Dmod_Context_t* context1 = Dmod_LoadFile(fs1_path); + if (context1 == NULL) { + printf("✗ Cannot load module: %s\n", fs1_path); + return -1; + } + + if (!Dmod_Enable(context1, false, NULL)) { + printf("✗ Cannot enable module: %s\n", fs1_path); + Dmod_Unload(context1, false); + return -1; + } + + // Load second filesystem module + Dmod_Context_t* context2 = Dmod_LoadFile(fs2_path); + if (context2 == NULL) { + printf("✗ Cannot load module: %s\n", fs2_path); + Dmod_Unload(context1, false); + return -1; + } + + if (!Dmod_Enable(context2, false, NULL)) { + printf("✗ Cannot enable module: %s\n", fs2_path); + Dmod_Unload(context2, false); + Dmod_Unload(context1, false); + return -1; + } + + const char* fs1_name = Dmod_GetName(context1); + const char* fs2_name = Dmod_GetName(context2); + + printf("Loaded filesystems:\n"); + printf(" FS1: %s\n", fs1_name); + printf(" FS2: %s\n", fs2_name); + printf("\n"); + + // Initialize DMVFS + if (!dmvfs_init(16, 32)) { + printf("✗ Cannot initialize DMVFS\n"); + return -1; + } + + printf("Test scenario: Mount FS1 at '/' and FS2 at '/configs'\n"); + printf("Expected behavior: Paths under '/configs' should use FS2, not FS1\n\n"); + + // Mount first filesystem at root + if (!dmvfs_mount_fs(fs1_name, "/", NULL)) { + printf("✗ Cannot mount %s at /\n", fs1_name); + dmvfs_deinit(); + return -1; + } + printf("✓ Mounted %s at /\n", fs1_name); + + // Mount second filesystem at /configs + if (!dmvfs_mount_fs(fs2_name, "/configs", NULL)) { + printf("✗ Cannot mount %s at /configs\n", fs2_name); + dmvfs_unmount_fs("/"); + dmvfs_deinit(); + return -1; + } + printf("✓ Mounted %s at /configs\n\n", fs2_name); + + printf("Running tests...\n\n"); + + // Test 1: Create file at /test.txt (should use FS1) + printf("Test 1: Create /test.txt (should use %s at /)\n", fs1_name); + void* fp1 = NULL; + int ret = dmvfs_fopen(&fp1, "/test.txt", DMFSI_O_CREAT | DMFSI_O_WRONLY, 0, 0); + if (ret == DMFSI_OK && fp1 != NULL) { + const char* data = "Root filesystem"; + size_t written = 0; + dmvfs_fwrite(fp1, data, strlen(data), &written); + dmvfs_fclose(fp1); + TEST_PASS("Created file at root mount point"); + } else { + TEST_FAIL("Cannot create file at root mount point"); + } + + // Test 2: Create file at /configs/app.conf (should use FS2, not FS1) + printf("Test 2: Create /configs/app.conf (should use %s at /configs)\n", fs2_name); + void* fp2 = NULL; + ret = dmvfs_fopen(&fp2, "/configs/app.conf", DMFSI_O_CREAT | DMFSI_O_WRONLY, 0, 0); + if (ret == DMFSI_OK && fp2 != NULL) { + const char* data = "Config filesystem"; + size_t written = 0; + dmvfs_fwrite(fp2, data, strlen(data), &written); + dmvfs_fclose(fp2); + TEST_PASS("Created file at /configs mount point"); + } else { + TEST_FAIL("Cannot create file at /configs mount point"); + } + + // Test 3: Read back the files to verify they're in different filesystems + printf("Test 3: Verify files can be read back\n"); + + // Read /test.txt + ret = dmvfs_fopen(&fp1, "/test.txt", DMFSI_O_RDONLY, 0, 0); + if (ret == DMFSI_OK && fp1 != NULL) { + char buffer[64] = {0}; + size_t read_bytes = 0; + dmvfs_fread(fp1, buffer, sizeof(buffer), &read_bytes); + dmvfs_fclose(fp1); + if (strcmp(buffer, "Root filesystem") == 0) { + TEST_PASS("Read /test.txt successfully"); + } else { + TEST_FAIL("Content mismatch in /test.txt"); + } + } else { + TEST_FAIL("Cannot read /test.txt"); + } + + // Read /configs/app.conf + ret = dmvfs_fopen(&fp2, "/configs/app.conf", DMFSI_O_RDONLY, 0, 0); + if (ret == DMFSI_OK && fp2 != NULL) { + char buffer[64] = {0}; + size_t read_bytes = 0; + dmvfs_fread(fp2, buffer, sizeof(buffer), &read_bytes); + dmvfs_fclose(fp2); + if (strcmp(buffer, "Config filesystem") == 0) { + TEST_PASS("Read /configs/app.conf successfully"); + } else { + TEST_FAIL("Content mismatch in /configs/app.conf"); + } + } else { + TEST_FAIL("Cannot read /configs/app.conf"); + } + + // Test 4: List files in both mount points + printf("Test 4: Directory listing verification\n"); + + // List root + void* dp = NULL; + ret = dmvfs_opendir(&dp, "/"); + if (ret == DMFSI_OK && dp != NULL) { + printf(" Files in /:\n"); + dmfsi_dir_entry_t entry; + bool found_test_txt = false; + while (dmvfs_readdir(dp, &entry) == DMFSI_OK) { + printf(" - %s\n", entry.name); + if (strcmp(entry.name, "test.txt") == 0) { + found_test_txt = true; + } + } + dmvfs_closedir(dp); + if (found_test_txt) { + TEST_PASS("Found test.txt in root directory"); + } else { + TEST_FAIL("test.txt not found in root directory"); + } + } else { + TEST_FAIL("Cannot list root directory"); + } + + // List /configs + ret = dmvfs_opendir(&dp, "/configs"); + if (ret == DMFSI_OK && dp != NULL) { + printf(" Files in /configs:\n"); + dmfsi_dir_entry_t entry; + bool found_app_conf = false; + while (dmvfs_readdir(dp, &entry) == DMFSI_OK) { + printf(" - %s\n", entry.name); + if (strcmp(entry.name, "app.conf") == 0) { + found_app_conf = true; + } + } + dmvfs_closedir(dp); + if (found_app_conf) { + TEST_PASS("Found app.conf in /configs directory"); + } else { + TEST_FAIL("app.conf not found in /configs directory"); + } + } else { + TEST_FAIL("Cannot list /configs directory"); + } + + // Cleanup + printf("\nCleaning up...\n"); + dmvfs_unmount_fs("/configs"); + dmvfs_unmount_fs("/"); + dmvfs_deinit(); + + // Print summary + printf("\n========================================\n"); + printf(" Test Summary\n"); + printf("========================================\n"); + printf("Total tests: %d\n", test_passed + test_failed); + printf("Passed: %d\n", test_passed); + printf("Failed: %d\n", test_failed); + printf("========================================\n"); + + if (test_failed == 0) { + printf("\n✓ ALL TESTS PASSED\n"); + printf("The fix correctly selects the longest matching mount point!\n\n"); + return 0; + } else { + printf("\n✗ SOME TESTS FAILED\n\n"); + return 1; + } +} From 37ef31e48d4e0dc363a02e5b14af7d6c5119fef1 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 8 Feb 2026 10:17:12 +0000 Subject: [PATCH 3/3] Add boundary checking to prevent incorrect prefix matches Co-authored-by: JohnAmadis <17320783+JohnAmadis@users.noreply.github.com> --- src/dmvfs.c | 9 +++++++++ tests/test_mount_point_selection.c | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/dmvfs.c b/src/dmvfs.c index 1b67589..74dcad9 100644 --- a/src/dmvfs.c +++ b/src/dmvfs.c @@ -344,7 +344,16 @@ static mount_point_t* get_mount_point_for_path(const char* path) if(g_mount_points[i].mount_point != NULL) { size_t mount_point_length = strlen(g_mount_points[i].mount_point); + // Check if path starts with mount point and either: + // - The mount point is the root "/" (special case - matches all paths) + // - The mount point is exactly the path (path[mount_point_length] == '\0') + // - The next character is a path separator (path[mount_point_length] == '/') + // This ensures we match at path boundaries only + bool is_root_mount = (mount_point_length == 1 && g_mount_points[i].mount_point[0] == '/'); + bool is_boundary_match = (path[mount_point_length] == '/' || path[mount_point_length] == '\0'); + if(strncmp(path, g_mount_points[i].mount_point, mount_point_length) == 0 && + (is_root_mount || is_boundary_match) && mount_point_length > best_match_length) { best_match = &g_mount_points[i]; diff --git a/tests/test_mount_point_selection.c b/tests/test_mount_point_selection.c index 1fe0103..1413f21 100644 --- a/tests/test_mount_point_selection.c +++ b/tests/test_mount_point_selection.c @@ -28,7 +28,7 @@ int main(int argc, char *argv[]) if (argc < 3) { printf("Usage: %s \n", argv[0]); printf("This test requires two filesystem modules to test mount point selection.\n"); - return 1; + return -1; } const char* fs1_path = argv[1];