1 // Copyright 2018 The Fuchsia Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file
4
5 #include <errno.h>
6 #include <fcntl.h>
7 #include <limits.h>
8 #include <string.h>
9 #include <sys/stat.h>
10 #include <sys/types.h>
11
12 #include <fbl/string_buffer.h>
13 #include <fbl/string_printf.h>
14 #include <fbl/unique_fd.h>
15 #include <fs-management/fvm.h>
16 #include <fs-management/ramdisk.h>
17 #include <fs-test-utils/fixture.h>
18 #include <lib/async-loop/cpp/loop.h>
19 #include <lib/memfs/memfs.h>
20 #include <lib/sync/completion.h>
21 #include <zircon/assert.h>
22 #include <zircon/device/device.h>
23 #include <zircon/device/vfs.h>
24 #include <zircon/errors.h>
25
26 namespace fs_test_utils {
27
28 namespace {
29
30 // Mount point for local MemFs to be mounted.
31 constexpr char kMemFsPath[] = "/memfs";
32
33 // Name for MemFs serving thread.
34 constexpr char kMemFsThreadName[] = "TestServingMemFsName";
35
36 // Path where to mount the filesystem.
37 constexpr char kFsPath[] = "%s/fs-root";
38
39 // Partition name where the filesystem will be mounted when using fvm.
40 constexpr char kFsPartitionName[] = "fs-test-partition";
41
42 // FVM Driver lib
43 constexpr char kFvmDriverLibPath[] = "/boot/driver/fvm.so";
44
45 constexpr uint8_t kTestUniqueGUID[] = {
46 0xFF, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
47 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f};
48
49 constexpr uint8_t kTestPartGUID[] = {
50 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
51 0xFF, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07};
52
ToStatus(ssize_t result)53 zx_status_t ToStatus(ssize_t result) {
54 return result < 0 ? static_cast<zx_status_t>(result) : ZX_OK;
55 }
56
MountMemFs(async::Loop * loop)57 zx_status_t MountMemFs(async::Loop* loop) {
58 zx_status_t result = ZX_OK;
59 result = loop->StartThread(kMemFsThreadName);
60 if (result != ZX_OK) {
61 LOG_ERROR(result, "Failed to start serving thread for MemFs.\n");
62 return result;
63 }
64
65 result = memfs_install_at(loop->dispatcher(), kMemFsPath);
66
67 return result;
68 }
69
MakeRamdisk(const FixtureOptions & options,fbl::String * block_device_path)70 zx_status_t MakeRamdisk(const FixtureOptions& options, fbl::String* block_device_path) {
71 ZX_DEBUG_ASSERT(options.use_ramdisk);
72 if (options.use_ramdisk) {
73 char buffer[kPathSize];
74 zx_status_t result = create_ramdisk(options.ramdisk_block_size,
75 options.ramdisk_block_count, buffer);
76 if (result != ZX_OK) {
77 LOG_ERROR(result,
78 "Failed to create ramdisk(block_size=%lu, ramdisk_block_count=%lu)\n",
79 options.ramdisk_block_size, options.ramdisk_block_count);
80 return result;
81 }
82 *block_device_path = buffer;
83 }
84
85 return ZX_OK;
86 }
87
RemoveRamdisk(const FixtureOptions & options,const fbl::String & block_device_path)88 zx_status_t RemoveRamdisk(const FixtureOptions& options, const fbl::String& block_device_path) {
89 ZX_DEBUG_ASSERT(options.use_ramdisk);
90 if (options.use_ramdisk && !block_device_path.empty()) {
91 zx_status_t result = destroy_ramdisk(block_device_path.c_str());
92 if (result != ZX_OK) {
93 LOG_ERROR(result, "Failed to destroy ramdisk.\nblock_device_path:%s\n",
94 block_device_path.c_str());
95 }
96 }
97 return ZX_OK;
98 }
99
FormatDevice(const FixtureOptions & options,const fbl::String & block_device_path)100 zx_status_t FormatDevice(const FixtureOptions& options, const fbl::String& block_device_path) {
101 // Format device.
102 mkfs_options_t mkfs_options = default_mkfs_options;
103 zx_status_t result = mkfs(block_device_path.c_str(), options.fs_type,
104 launch_stdio_sync, &mkfs_options);
105 if (result != ZX_OK) {
106 LOG_ERROR(result, "Failed to format block device.\nblock_device_path:%s\n",
107 block_device_path.c_str());
108 return result;
109 }
110
111 // Verify format.
112 fsck_options_t fsck_options = default_fsck_options;
113 result = fsck(block_device_path.c_str(), options.fs_type, &fsck_options, launch_stdio_sync);
114 if (result != ZX_OK) {
115 LOG_ERROR(result, "Block device format has errors.\nblock_device_path:%s\n",
116 block_device_path.c_str());
117 return result;
118 }
119
120 return ZX_OK;
121 }
122
MakeFvm(const fbl::String & block_device_path,uint64_t fvm_slice_size,fbl::String * partition_path,bool * fvm_mounted)123 zx_status_t MakeFvm(const fbl::String& block_device_path, uint64_t fvm_slice_size,
124 fbl::String* partition_path, bool* fvm_mounted) {
125 zx_status_t result = ZX_OK;
126 fbl::unique_fd fd(open(block_device_path.c_str(), O_RDWR));
127 if (!fd) {
128 LOG_ERROR(ZX_ERR_IO, "%s.\nblock_device_path: %s\n",
129 strerror(errno), block_device_path.c_str());
130 return ZX_ERR_IO;
131 }
132 result = fvm_init(fd.get(), fvm_slice_size);
133 if (result != ZX_OK) {
134 LOG_ERROR(result, "Failed to format device with FVM.\nblock_device_path: %s\n",
135 block_device_path.c_str());
136 return result;
137 }
138 *fvm_mounted = true;
139 fbl::String fvm_device_path = fbl::StringPrintf("%s/fvm", block_device_path.c_str());
140 // Bind FVM Driver.
141 result = ToStatus(ioctl_device_bind(fd.get(), kFvmDriverLibPath, strlen(kFvmDriverLibPath)));
142 if (result != ZX_OK) {
143 LOG_ERROR(result, "Failed to bind fvm driver to block device.\nblock_device:%s\n",
144 block_device_path.c_str());
145 return result;
146 }
147
148 result = wait_for_device(fvm_device_path.c_str(), zx::sec(3).get());
149 if (result != ZX_OK) {
150 LOG_ERROR(result, "FVM driver failed to start.\nfvm_device_path:%s\n",
151 fvm_device_path.c_str());
152 return result;
153 }
154
155 fbl::unique_fd fvm_fd(open(fvm_device_path.c_str(), O_RDWR));
156 if (!fvm_fd) {
157 LOG_ERROR(ZX_ERR_IO, "%s.\nfvm_device_path:%s\n",
158 strerror(errno), fvm_device_path.c_str());
159 return ZX_ERR_IO;
160 }
161
162 alloc_req_t request;
163 memset(&request, 0, sizeof(request));
164 request.slice_count = 1;
165 strcpy(request.name, kFsPartitionName);
166 memcpy(request.type, kTestPartGUID, sizeof(request.type));
167 memcpy(request.guid, kTestUniqueGUID, sizeof(request.guid));
168
169 fbl::unique_fd partition_fd(fvm_allocate_partition(fvm_fd.get(), &request));
170 if (!partition_fd) {
171 LOG_ERROR(ZX_ERR_IO, "Failed to allocate FVM partition\n");
172 return ZX_ERR_IO;
173 }
174
175 char buffer[kPathSize];
176 partition_fd.reset(open_partition(kTestUniqueGUID, kTestPartGUID, 0, buffer));
177 *partition_path = buffer;
178 if (!partition_fd) {
179 LOG_ERROR(ZX_ERR_IO, "Could not locate FVM partition. %s\n", strerror(errno));
180 return ZX_ERR_IO;
181 }
182
183 return ZX_OK;
184 }
185
186 } // namespace
187
IsValid(fbl::String * err_description) const188 bool FixtureOptions::IsValid(fbl::String* err_description) const {
189 ZX_DEBUG_ASSERT(err_description != nullptr);
190 fbl::StringBuffer<400> buffer;
191 err_description->clear();
192
193 if (use_ramdisk) {
194 if (!block_device_path.empty()) {
195 buffer.Append("use_ramdisk and block_device_path are mutually exclusive.\n");
196 }
197 size_t max_size = zx_system_get_physmem();
198 size_t requested_size = ramdisk_block_count * ramdisk_block_size;
199 if (max_size < requested_size) {
200 buffer.AppendPrintf(
201 "ramdisk size(%lu) cannot exceed available memory(%lu).\n",
202 requested_size, requested_size);
203 }
204 if (ramdisk_block_count == 0) {
205 buffer.Append("ramdisk_block_count must be greater than 0.\n");
206 }
207 if (ramdisk_block_size == 0) {
208 buffer.Append("ramdisk_block_size must be greater than 0.\n");
209 }
210 } else if (block_device_path.empty()) {
211 buffer.Append("block_device_path or use_ramdisk must be set\n.");
212 }
213
214 if (use_fvm) {
215 if (fvm_slice_size == 0 || fvm_slice_size % kFvmBlockSize != 0) {
216 buffer.AppendPrintf("fvm_slize_size must be a multiple of %lu.\n", kFvmBlockSize);
217 }
218 }
219
220 *err_description = buffer.ToString();
221
222 return err_description->empty();
223 }
224
Fixture(const FixtureOptions & options)225 Fixture::Fixture(const FixtureOptions& options)
226 : options_(options) {}
227
~Fixture()228 Fixture::~Fixture() {
229 // Sanity check, teardown any resources if these two were not called yet.
230 TearDown();
231 TearDownTestCase();
232 };
233
Mount()234 zx_status_t Fixture::Mount() {
235 fbl::unique_fd fd(open(GetFsBlockDevice().c_str(), O_RDWR));
236 if (!fd) {
237 LOG_ERROR(ZX_ERR_IO, "%s.\nblock_device_path:%s\n",
238 strerror(errno), GetFsBlockDevice().c_str());
239 return ZX_ERR_IO;
240 }
241
242 // Already mounted.
243 if (fs_state_ == ResourceState::kAllocated) {
244 return ZX_OK;
245 }
246
247 mount_options_t mount_options = default_mount_options;
248 mount_options.create_mountpoint = true;
249 mount_options.wait_until_ready = true;
250
251 disk_format_t format = detect_disk_format(fd.get());
252 zx_status_t result = mount(fd.release(), fs_path_.c_str(), format,
253 &mount_options, launch_stdio_async);
254 if (result != ZX_OK) {
255 LOG_ERROR(result, "Failed to mount device at %s.\nblock_device_path:%s\n",
256 fs_path_.c_str(), GetFsBlockDevice().c_str());
257 return result;
258 }
259 fs_state_ = ResourceState::kAllocated;
260 return ZX_OK;
261 }
262
Umount()263 zx_status_t Fixture::Umount() {
264 if (fs_state_ != ResourceState::kAllocated) {
265 return ZX_OK;
266 }
267 if (!fs_path_.empty()) {
268 zx_status_t result = umount(fs_path_.c_str());
269 if (result != ZX_OK) {
270 LOG_ERROR(result,
271 "Failed to umount device from MemFs.\nblock_device_path:%s\nmount_path:%s\n",
272 GetFsBlockDevice().c_str(), fs_path_.c_str());
273 return result;
274 }
275 fs_state_ = ResourceState::kFreed;
276 }
277 return ZX_OK;
278 }
279
SetUpTestCase()280 zx_status_t Fixture::SetUpTestCase() {
281 LOG_INFO("Using random seed: %u\n", options_.seed);
282 seed_ = options_.seed;
283 if (options_.use_ramdisk) {
284 zx_status_t result = MakeRamdisk(options_, &block_device_path_);
285 if (result != ZX_OK) {
286 return result;
287 }
288 ramdisk_state_ = ResourceState::kAllocated;
289 }
290
291 if (!options_.block_device_path.empty()) {
292 block_device_path_ = options_.block_device_path;
293 }
294
295 return ZX_OK;
296 }
297
SetUp()298 zx_status_t Fixture::SetUp() {
299 fvm_state_ = ResourceState::kUnallocated;
300 fs_state_ = ResourceState::kUnallocated;
301 if (options_.use_fvm) {
302 bool allocated = false;
303 zx_status_t result = MakeFvm(block_device_path_, options_.fvm_slice_size,
304 &partition_path_, &allocated);
305 if (allocated) {
306 fvm_state_ = ResourceState::kAllocated;
307 }
308
309 if (result != ZX_OK) {
310 return result;
311 }
312 }
313
314 fs_path_ = fbl::StringPrintf(kFsPath, kMemFsPath);
315 zx_status_t result;
316 if (options_.fs_format) {
317 result = FormatDevice(options_, GetFsBlockDevice());
318 if (result != ZX_OK) {
319 return result;
320 }
321 }
322
323 if (options_.fs_mount) {
324 result = Mount();
325 if (result != ZX_OK) {
326 return result;
327 }
328 fs_state_ = ResourceState::kAllocated;
329 }
330
331 return ZX_OK;
332 }
333
TearDown()334 zx_status_t Fixture::TearDown() {
335 zx_status_t result;
336 // Umount Fs from MemFs.
337 if (fs_state_ == ResourceState::kAllocated) {
338 result = Umount();
339 if (result != ZX_OK) {
340 return result;
341 }
342 }
343
344 // If real device not in FVM, clean it.
345 if (!block_device_path_.empty() && !options_.use_fvm &&
346 fs_state_ == ResourceState::kAllocated) {
347 result = FormatDevice(options_, block_device_path_);
348 if (result != ZX_OK) {
349 return result;
350 }
351 fs_state_ = ResourceState::kFreed;
352 }
353
354 // If using FVM on top of device, just destroy the fvm, this only applies if
355 // the fvm was created within this process.
356 if (options_.use_fvm && fvm_state_ == ResourceState::kAllocated) {
357 result = fvm_destroy(block_device_path_.c_str());
358 if (result != ZX_OK) {
359 LOG_ERROR(result, "Failed to destroy fvm in block_device.\nblock_device: %s\n",
360 block_device_path_.cend());
361 return result;
362 }
363 fs_state_ = ResourceState::kFreed;
364 fvm_state_ = ResourceState::kFreed;
365 }
366 return ZX_OK;
367 }
368
TearDownTestCase()369 zx_status_t Fixture::TearDownTestCase() {
370 if (ramdisk_state_ == ResourceState::kAllocated) {
371 zx_status_t ramdisk_result = RemoveRamdisk(options_, block_device_path_);
372 if (ramdisk_result != ZX_OK) {
373 return ramdisk_result;
374 }
375 }
376 ramdisk_state_ = ResourceState::kFreed;
377
378 return ZX_OK;
379 }
380
RunWithMemFs(const fbl::Function<int ()> & main_fn)381 int RunWithMemFs(const fbl::Function<int()>& main_fn) {
382 async::Loop loop(&kAsyncLoopConfigNoAttachToThread);
383 if (MountMemFs(&loop) != ZX_OK) {
384 return -1;
385 }
386 int result = main_fn();
387 return result;
388 }
389
390 } // namespace fs_test_utils
391