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