aboutsummaryrefslogtreecommitdiff
path: root/kernel/src/filesystem/mount_table.tests.cpp
blob: 9f390c67a8cbca76959ae414dae2b3e098602b5a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
#include "kernel/filesystem/mount_table.hpp"

#include "kernel/filesystem/dentry.hpp"
#include "kernel/filesystem/mount.hpp"
#include "kernel/test_support/cpu.hpp"
#include "kernel/test_support/filesystem/filesystem.hpp"
#include "kernel/test_support/filesystem/inode.hpp"

#include <kstd/memory>
#include <kstd/print>
#include <kstd/vector>

#include <catch2/catch_test_macros.hpp>

#include <string_view>

SCENARIO("Mount table construction", "[filesystem][mount_table]")
{
  GIVEN("an empty mount table")
  {
    kernel::filesystem::mount_table table;

    THEN("finding any mount returns null")
    {
      REQUIRE(table.find_longest_prefix_mount("/") == nullptr);
      REQUIRE(table.find_longest_prefix_mount("/any/path") == nullptr);
    }

    THEN("removing any mount returns mount_not_found")
    {
      REQUIRE(table.remove_mount("/") == kernel::filesystem::mount_table::operation_result::mount_not_found);
      REQUIRE(table.remove_mount("/any/path") == kernel::filesystem::mount_table::operation_result::mount_not_found);
    }
  }
}

SCENARIO("Adding, finding and removing mounts in the mount table", "[filesystem][mount_table]")
{
  GIVEN("a mount table and some mounts")
  {
    kernel::filesystem::mount_table table;

    auto fs1 = kstd::make_shared<kernel::tests::filesystem::filesystem>();
    auto root_inode1 = kstd::make_shared<kernel::tests::filesystem::inode>();
    auto root_dentry1 = kstd::make_shared<kernel::filesystem::dentry>(nullptr, root_inode1, "/");
    auto mount1 = kstd::make_shared<kernel::filesystem::mount>(root_dentry1, root_dentry1, fs1, "/", nullptr);

    auto fs2 = kstd::make_shared<kernel::tests::filesystem::filesystem>();
    auto root_inode2 = kstd::make_shared<kernel::tests::filesystem::inode>();
    auto root_dentry2 = kstd::make_shared<kernel::filesystem::dentry>(nullptr, root_inode2, "/");
    auto mount2 = kstd::make_shared<kernel::filesystem::mount>(root_dentry1, root_dentry2, fs2, "/mnt", nullptr);

    table.add_mount(mount1);
    table.add_mount(mount2);

    THEN("dentry flags are set correctly for mounted dentries")
    {
      REQUIRE(root_dentry1->has_flag(kernel::filesystem::dentry::dentry_flags::dcache_mounted));
      REQUIRE(root_dentry2->has_flag(kernel::filesystem::dentry::dentry_flags::dcache_mounted));
    }

    THEN("finding mounts by path returns the correct mount")
    {
      REQUIRE(table.find_longest_prefix_mount("/") == mount1);
      REQUIRE(table.find_longest_prefix_mount("/file") == mount1);
      REQUIRE(table.find_longest_prefix_mount("/mnt") == mount2);
      REQUIRE(table.find_longest_prefix_mount("/mnt/file") == mount2);
      REQUIRE(table.find_longest_prefix_mount("/other") == mount1);
    }

    THEN("removing a mount that has no child mounts succeeds")
    {
      REQUIRE(table.remove_mount("/mnt") == kernel::filesystem::mount_table::operation_result::removed);
      REQUIRE(!root_dentry2->has_flag(kernel::filesystem::dentry::dentry_flags::dcache_mounted));
      REQUIRE(table.find_longest_prefix_mount("/mnt") == nullptr);
    }

    THEN("removing a mount that does not exist returns mount_not_found")
    {
      REQUIRE(table.remove_mount("/nonexistent") == kernel::filesystem::mount_table::operation_result::mount_not_found);
    }
  }

  GIVEN("multiple mounts with the same path")
  {
    kernel::filesystem::mount_table table;

    auto fs1 = kstd::make_shared<kernel::tests::filesystem::filesystem>();
    auto root_inode1 = kstd::make_shared<kernel::tests::filesystem::inode>();
    auto root_dentry1 = kstd::make_shared<kernel::filesystem::dentry>(nullptr, root_inode1, "/");
    auto mount1 = kstd::make_shared<kernel::filesystem::mount>(root_dentry1, root_dentry1, fs1, "/", nullptr);

    auto fs2 = kstd::make_shared<kernel::tests::filesystem::filesystem>();
    auto root_inode2 = kstd::make_shared<kernel::tests::filesystem::inode>();
    auto root_dentry2 = kstd::make_shared<kernel::filesystem::dentry>(nullptr, root_inode2, "/");
    auto mount2 = kstd::make_shared<kernel::filesystem::mount>(root_dentry1, root_dentry2, fs2, "/", mount1);

    table.add_mount(mount1);
    table.add_mount(mount2);

    THEN("finding mounts by path returns the correct mount based on longest prefix")
    {
      REQUIRE(table.find_longest_prefix_mount("/") == mount2);
      REQUIRE(table.find_longest_prefix_mount("/file") == mount2);
      REQUIRE(table.find_longest_prefix_mount("/mnt") == mount2);
      REQUIRE(table.find_longest_prefix_mount("/mnt/file") == mount2);
      REQUIRE(table.find_longest_prefix_mount("/other") == mount2);
    }

    THEN("removing the topmost mount with the same path succeeds")
    {
      REQUIRE(table.remove_mount("/") == kernel::filesystem::mount_table::operation_result::removed);
      REQUIRE(!root_dentry2->has_flag(kernel::filesystem::dentry::dentry_flags::dcache_mounted));
      REQUIRE(table.find_longest_prefix_mount("/") == mount1);
    }
  }

  GIVEN("a mount with child mounts")
  {
    kernel::filesystem::mount_table table;

    auto fs1 = kstd::make_shared<kernel::tests::filesystem::filesystem>();
    auto root_inode1 = kstd::make_shared<kernel::tests::filesystem::inode>();
    auto root_dentry1 = kstd::make_shared<kernel::filesystem::dentry>(nullptr, root_inode1, "/");
    auto mount1 = kstd::make_shared<kernel::filesystem::mount>(root_dentry1, root_dentry1, fs1, "/", nullptr);

    auto fs2 = kstd::make_shared<kernel::tests::filesystem::filesystem>();
    auto root_inode2 = kstd::make_shared<kernel::tests::filesystem::inode>();
    auto root_dentry2 = kstd::make_shared<kernel::filesystem::dentry>(nullptr, root_inode2, "/");
    auto mount2 = kstd::make_shared<kernel::filesystem::mount>(root_dentry1, root_dentry2, fs2, "/mnt", mount1);

    auto fs3 = kstd::make_shared<kernel::tests::filesystem::filesystem>();
    auto root_inode3 = kstd::make_shared<kernel::tests::filesystem::inode>();
    auto root_dentry3 = kstd::make_shared<kernel::filesystem::dentry>(nullptr, root_inode3, "/");
    auto mount3 = kstd::make_shared<kernel::filesystem::mount>(root_dentry2, root_dentry3, fs3, "/mnt/submnt", mount2);

    table.add_mount(mount1);
    table.add_mount(mount2);
    table.add_mount(mount3);

    THEN("finding mounts by path returns the correct mount based on longest prefix")
    {
      REQUIRE(table.find_longest_prefix_mount("/") == mount1);
      REQUIRE(table.find_longest_prefix_mount("/file") == mount1);
      REQUIRE(table.find_longest_prefix_mount("/mnt") == mount2);
      REQUIRE(table.find_longest_prefix_mount("/mnt/file") == mount2);
      REQUIRE(table.find_longest_prefix_mount("/mnt/submnt") == mount3);
      REQUIRE(table.find_longest_prefix_mount("/other") == mount1);
    }

    THEN("removing a mount with child mounts returns has_child_mounts")
    {
      REQUIRE(table.remove_mount("/") == kernel::filesystem::mount_table::operation_result::has_child_mounts);
      REQUIRE(table.remove_mount("/mnt") == kernel::filesystem::mount_table::operation_result::has_child_mounts);
    }

    THEN("removing a leaf mount succeeds")
    {
      REQUIRE(table.remove_mount("/mnt/submnt") == kernel::filesystem::mount_table::operation_result::removed);
      REQUIRE(!root_dentry3->has_flag(kernel::filesystem::dentry::dentry_flags::dcache_mounted));
      REQUIRE(table.find_longest_prefix_mount("/mnt/submnt") == nullptr);
    }
  }
}