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
185
186
187
188
189
190
191
192
193
194
195
196
197
198
|
#include <kernel/filesystem/open_file_descriptor.hpp>
#include <kernel/devices/storage.hpp>
#include <kernel/filesystem/dentry.hpp>
#include <kernel/filesystem/device_inode.hpp>
#include <kernel/filesystem/inode.hpp>
#include <kernel/filesystem/vfs.hpp>
#include <kernel/test_support/filesystem/inode.hpp>
#include <kernel/test_support/filesystem/storage_boot_module_fixture.hpp>
#include <kernel/test_support/filesystem/storage_boot_module_vfs_fixture.hpp>
#include <kapi/devices.hpp>
#include <kstd/memory.hpp>
#include <kstd/print.hpp>
#include <kstd/system_error.hpp>
#include <kstd/units.hpp>
#include <kstd/vector.hpp>
#include <catch2/catch_test_macros.hpp>
#include <cstddef>
#include <filesystem>
#include <string_view>
using namespace kstd::units_literals;
// NOLINTBEGIN(readability-magic-numbers)
SCENARIO("Open file descriptor construction", "[filesystem][open_file_descriptor]")
{
GIVEN("a dentry and an open file descriptor for that dentry")
{
auto inode = kstd::make_shared<kernel::tests::filesystem::inode>();
auto dentry = kstd::make_shared<kernel::filesystem::dentry>(nullptr, inode, "test_dentry");
auto file_descriptor = kernel::filesystem::open_file_descriptor{dentry};
THEN("the initial offset is zero")
{
REQUIRE(file_descriptor.offset() == 0_B);
}
}
}
SCENARIO("Open file descriptor read/write offset management", "[filesystem][open_file_descriptor]")
{
GIVEN("a dentry that tracks read/write calls and an open file descriptor for that dentry")
{
auto inode = kstd::make_shared<kernel::tests::filesystem::inode>();
auto dentry = kstd::make_shared<kernel::filesystem::dentry>(nullptr, inode, "test_dentry");
auto file_descriptor = kernel::filesystem::open_file_descriptor{dentry};
THEN("the offset is updated correctly after reads")
{
REQUIRE(file_descriptor.read(nullptr, 100_B) == 100_B);
REQUIRE(file_descriptor.offset() == 100_B);
REQUIRE(file_descriptor.read(nullptr, 50_B) == 50_B);
REQUIRE(file_descriptor.offset() == 150_B);
}
THEN("the offset is updated correctly after writes")
{
REQUIRE(file_descriptor.write(nullptr, 200_B) == 200_B);
REQUIRE(file_descriptor.offset() == 200_B);
REQUIRE(file_descriptor.write(nullptr, 25_B) == 25_B);
REQUIRE(file_descriptor.offset() == 225_B);
}
THEN("reads and writes both update the same offset")
{
REQUIRE(file_descriptor.read(nullptr, 10_B) == 10_B);
REQUIRE(file_descriptor.offset() == 10_B);
REQUIRE(file_descriptor.write(nullptr, 20_B) == 20_B);
REQUIRE(file_descriptor.offset() == 30_B);
REQUIRE(file_descriptor.read(nullptr, 5_B) == 5_B);
REQUIRE(file_descriptor.offset() == 35_B);
REQUIRE(file_descriptor.write(nullptr, 15_B) == 15_B);
REQUIRE(file_descriptor.offset() == 50_B);
}
}
}
SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "Open file descriptor read with real image",
"[filesystem][open_file_descriptor][img]")
{
auto const image_path = std::filesystem::path{KERNEL_TEST_ASSETS_DIR} / "ext2_1KB_fs.img";
GIVEN("an open file descriptor for a file in a real image")
{
REQUIRE(std::filesystem::exists(image_path));
REQUIRE_NOTHROW(setup_modules_from_img_and_init_vfs({"test_img_module"}, {image_path}));
auto & vfs = kernel::filesystem::vfs::get();
auto dentry = vfs.open("/information/info_1.txt");
REQUIRE(dentry);
auto ofd = kstd::make_shared<kernel::filesystem::open_file_descriptor>(dentry.value());
THEN("the file can be read and the offset is updated")
{
kstd::vector<std::byte> buffer(32);
auto bytes_read = ofd->read(buffer.data(), kstd::units::bytes{buffer.size()});
REQUIRE(bytes_read == 7_B);
REQUIRE(ofd->offset() == 7_B);
std::string_view buffer_as_str{reinterpret_cast<char *>(buffer.data()), bytes_read->value};
REQUIRE(buffer_as_str == "info_1\n");
}
THEN("the file can be read multiple times and the offset is updated")
{
kstd::vector<std::byte> buffer(4);
auto bytes_read_1 = ofd->read(buffer.data(), kstd::units::bytes{buffer.size() / 2});
REQUIRE(bytes_read_1 == kstd::units::bytes{buffer.size() / 2});
REQUIRE(ofd->offset() == kstd::units::bytes{buffer.size() / 2});
auto bytes_read_2 = ofd->read(buffer.data() + buffer.size() / 2, kstd::units::bytes{buffer.size() / 2});
REQUIRE(bytes_read_2 == kstd::units::bytes{buffer.size() / 2});
REQUIRE(ofd->offset() == kstd::units::bytes{buffer.size()});
std::string_view buffer_as_str{reinterpret_cast<char *>(buffer.data()),
bytes_read_1->value + bytes_read_2->value};
REQUIRE(buffer_as_str == "info");
}
THEN("the file can be written to and the offset is updated")
{
auto write_buffer = kstd::vector<std::byte>(12, std::byte{0xAA});
auto const bytes_written = ofd->write(write_buffer.data(), kstd::units::bytes{write_buffer.size()});
REQUIRE(bytes_written == 12_B);
REQUIRE(ofd->offset() == 12_B);
}
THEN("the file can be written to multiple times and the offset is updated")
{
auto write_buffer = kstd::vector<std::byte>(8, std::byte{0xAA});
auto const bytes_written_1 = ofd->write(write_buffer.data(), kstd::units::bytes{write_buffer.size() / 2});
REQUIRE(bytes_written_1 == kstd::units::bytes{write_buffer.size() / 2});
REQUIRE(ofd->offset() == kstd::units::bytes{write_buffer.size() / 2});
auto const bytes_written_2 =
ofd->write(write_buffer.data() + write_buffer.size() / 2, kstd::units::bytes{write_buffer.size() / 2});
REQUIRE(bytes_written_2 == kstd::units::bytes{write_buffer.size() / 2});
REQUIRE(ofd->offset() == kstd::units::bytes{write_buffer.size()});
}
}
}
SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_fixture, "Open file descriptor handles device removal",
"[filesystem][open_file_descriptor]")
{
GIVEN("A file descriptor open on a bound, published devices")
{
setup_modules(1);
auto device = kernel::devices::storage::determine_boot_device();
REQUIRE(device);
auto weak_reference = kstd::weak_ptr{device};
auto device_inode = kstd::make_shared<kernel::filesystem::device_inode>(device);
auto dentry = kstd::make_shared<kernel::filesystem::dentry>(nullptr, device_inode, "ram0");
auto fd = kstd::make_shared<kernel::filesystem::open_file_descriptor>(dentry);
device.reset();
REQUIRE_FALSE(weak_reference.expired());
WHEN("the device is removed while the descriptor is still open")
{
auto locked = weak_reference.lock();
REQUIRE(locked);
REQUIRE(kapi::devices::remove_device(*locked));
locked.reset();
THEN("the device is kept alive through the file descriptor")
{
REQUIRE_FALSE(weak_reference.expired());
}
THEN("reading through the file descriptor fails with 'no such device'")
{
auto buffer = kstd::vector<std::byte>(10);
auto bytes_read = fd->read(buffer.data(), kstd::units::bytes{buffer.size()});
REQUIRE(bytes_read.error() == kstd::errc::no_such_device);
}
THEN("writing through the file descriptor fails with 'no such device'")
{
auto buffer = kstd::vector<std::byte>(10);
auto bytes_written = fd->write(buffer.data(), kstd::units::bytes{buffer.size()});
REQUIRE(bytes_written.error() == kstd::errc::no_such_device);
}
}
}
}
// NOLINTEND(readability-magic-numbers)
|