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
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
|
#include <kernel/filesystem/mount_table.hpp>
#include <kernel/filesystem/dentry.hpp>
#include <kernel/filesystem/mount.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("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_dentry1 = kstd::make_shared<kernel::filesystem::dentry>(
nullptr, kstd::make_shared<kernel::tests::filesystem::inode>(), "/");
auto mount_dentry1 = kstd::make_shared<kernel::filesystem::dentry>(
nullptr, kstd::make_shared<kernel::tests::filesystem::inode>(), "/");
auto mount1 = kstd::make_shared<kernel::filesystem::mount>(mount_dentry1, root_dentry1, fs1, nullptr, nullptr);
auto fs2 = kstd::make_shared<kernel::tests::filesystem::filesystem>();
auto root_dentry2 = kstd::make_shared<kernel::filesystem::dentry>(
nullptr, kstd::make_shared<kernel::tests::filesystem::inode>(), "/");
auto mount_dentry2 = kstd::make_shared<kernel::filesystem::dentry>(
nullptr, kstd::make_shared<kernel::tests::filesystem::inode>(), "/mnt");
auto mount2 = kstd::make_shared<kernel::filesystem::mount>(mount_dentry2, root_dentry2, fs2, nullptr, nullptr);
table.add_mount(mount1);
table.add_mount(mount2);
THEN("dentry flags are set correctly for mounted dentries")
{
REQUIRE(mount_dentry1->has_flag(kernel::filesystem::dentry::dentry_flags::is_mount_point));
REQUIRE(mount_dentry2->has_flag(kernel::filesystem::dentry::dentry_flags::is_mount_point));
}
THEN("finding mounts by exact valid path returns the correct mount")
{
REQUIRE(table.find_mount("/") == mount1);
REQUIRE(table.find_mount("/mnt") == mount2);
}
THEN("finding mounts by exact invalid path returns null")
{
REQUIRE(table.find_mount("/nonexistent") == nullptr);
REQUIRE(table.find_mount("/mnt/file") == nullptr);
}
THEN("removing a mount that has no child mounts succeeds")
{
REQUIRE(table.remove_mount("/mnt") == kernel::filesystem::mount_table::operation_result::removed);
REQUIRE_FALSE(root_dentry2->has_flag(kernel::filesystem::dentry::dentry_flags::is_mount_point));
}
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_dentry1 = kstd::make_shared<kernel::filesystem::dentry>(
nullptr, kstd::make_shared<kernel::tests::filesystem::inode>(), "/");
auto mount_dentry1 = kstd::make_shared<kernel::filesystem::dentry>(
nullptr, kstd::make_shared<kernel::tests::filesystem::inode>(), "/");
auto mount1 = kstd::make_shared<kernel::filesystem::mount>(mount_dentry1, root_dentry1, fs1, nullptr, nullptr);
auto fs2 = kstd::make_shared<kernel::tests::filesystem::filesystem>();
auto root_dentry2 = kstd::make_shared<kernel::filesystem::dentry>(
nullptr, kstd::make_shared<kernel::tests::filesystem::inode>(), "/");
auto mount_dentry2 = kstd::make_shared<kernel::filesystem::dentry>(
nullptr, kstd::make_shared<kernel::tests::filesystem::inode>(), "/");
auto mount2 = kstd::make_shared<kernel::filesystem::mount>(mount_dentry2, root_dentry2, fs2, nullptr, nullptr);
table.add_mount(mount1);
table.add_mount(mount2);
THEN("finding mounts by exact valid path returns the correct mount")
{
REQUIRE(table.find_mount("/") == mount2);
}
THEN("removing the topmost mount with the same path succeeds")
{
REQUIRE(table.remove_mount("/") == kernel::filesystem::mount_table::operation_result::removed);
REQUIRE_FALSE(root_dentry2->has_flag(kernel::filesystem::dentry::dentry_flags::is_mount_point));
}
}
GIVEN("a mount with child mounts")
{
kernel::filesystem::mount_table table;
auto fs1 = kstd::make_shared<kernel::tests::filesystem::filesystem>();
auto root_dentry1 = kstd::make_shared<kernel::filesystem::dentry>(
nullptr, kstd::make_shared<kernel::tests::filesystem::inode>(), "/");
auto mount_dentry1 = kstd::make_shared<kernel::filesystem::dentry>(
nullptr, kstd::make_shared<kernel::tests::filesystem::inode>(), "/");
auto mount1 = kstd::make_shared<kernel::filesystem::mount>(mount_dentry1, root_dentry1, fs1, nullptr, nullptr);
auto fs2 = kstd::make_shared<kernel::tests::filesystem::filesystem>();
auto root_dentry2 = kstd::make_shared<kernel::filesystem::dentry>(
nullptr, kstd::make_shared<kernel::tests::filesystem::inode>(), "/");
auto mount_dentry2 = kstd::make_shared<kernel::filesystem::dentry>(
mount_dentry1, kstd::make_shared<kernel::tests::filesystem::inode>(), "mnt");
auto mount2 = kstd::make_shared<kernel::filesystem::mount>(mount_dentry2, root_dentry2, fs2, mount1, nullptr);
auto fs3 = kstd::make_shared<kernel::tests::filesystem::filesystem>();
auto root_dentry3 = kstd::make_shared<kernel::filesystem::dentry>(
nullptr, kstd::make_shared<kernel::tests::filesystem::inode>(), "/");
auto mount_dentry3 = kstd::make_shared<kernel::filesystem::dentry>(
mount_dentry2, kstd::make_shared<kernel::tests::filesystem::inode>(), "submnt");
auto mount3 = kstd::make_shared<kernel::filesystem::mount>(mount_dentry3, root_dentry3, fs3, mount2, nullptr);
table.add_mount(mount1);
table.add_mount(mount2);
table.add_mount(mount3);
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_FALSE(root_dentry3->has_flag(kernel::filesystem::dentry::dentry_flags::is_mount_point));
}
}
}
SCENARIO("Mount reference counting", "[filesystem][mount_table]")
{
kernel::filesystem::mount_table table;
GIVEN("a filesystem and a root dentry")
{
auto fs = kstd::make_shared<kernel::tests::filesystem::filesystem>();
auto root_inode = kstd::make_shared<kernel::tests::filesystem::inode>();
auto root_dentry = kstd::make_shared<kernel::filesystem::dentry>(nullptr, root_inode, "/");
auto source_mount = kstd::make_shared<kernel::filesystem::mount>(root_dentry, root_dentry, fs, nullptr, nullptr);
auto mount = kstd::make_shared<kernel::filesystem::mount>(root_dentry, root_dentry, fs, nullptr, source_mount);
THEN("reference count of source mount is incremented when a mount is added to the mount table and decremented when "
"the mount is removed")
{
REQUIRE(source_mount->ref_count() == 0);
table.add_mount(mount);
REQUIRE(source_mount->ref_count() == 1);
REQUIRE(table.remove_mount("/") == kernel::filesystem::mount_table::operation_result::removed);
REQUIRE(source_mount->ref_count() == 0);
}
}
}
|