1 // Copyright 2017 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 <stdalign.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <sys/stat.h>
13 #include <sys/statfs.h>
14 #include <sys/types.h>
15 #include <time.h>
16 #include <unistd.h>
17 
18 #include <fbl/string.h>
19 #include <fbl/string_buffer.h>
20 #include <fbl/unique_fd.h>
21 #include <fs-test-utils/fixture.h>
22 #include <fs-test-utils/unittest.h>
23 #include <fuchsia/io/c/fidl.h>
24 #include <lib/fzl/fdio.h>
25 #include <unittest/unittest.h>
26 #include <zircon/device/block.h>
27 #include <zircon/device/ramdisk.h>
28 #include <zircon/device/vfs.h>
29 #include <zircon/syscalls.h>
30 
31 #include <fs-management/mount.h>
32 #include <fs-management/ramdisk.h>
33 
34 #include <utility>
35 
36 namespace {
37 
PartitionOverFvmWithRamdisk()38 fs_test_utils::FixtureOptions PartitionOverFvmWithRamdisk() {
39     fs_test_utils::FixtureOptions options =
40         fs_test_utils::FixtureOptions::Default(DISK_FORMAT_MINFS);
41     options.use_fvm = true;
42     options.fs_format = false;
43     options.fs_mount = false;
44     return options;
45 }
46 
MinfsRamdiskOptions()47 fs_test_utils::FixtureOptions MinfsRamdiskOptions() {
48     fs_test_utils::FixtureOptions options =
49         fs_test_utils::FixtureOptions::Default(DISK_FORMAT_MINFS);
50     options.use_fvm = false;
51     options.fs_format = true;
52     options.fs_mount = true;
53     return options;
54 }
55 
CheckMountedFs(const char * path,const char * fs_name,size_t len)56 bool CheckMountedFs(const char* path, const char* fs_name, size_t len) {
57     BEGIN_HELPER;
58     fbl::unique_fd fd(open(path, O_RDONLY | O_DIRECTORY));
59     ASSERT_TRUE(fd);
60 
61     fuchsia_io_FilesystemInfo info;
62     zx_status_t status;
63     fzl::FdioCaller caller(std::move(fd));
64     ASSERT_EQ(fuchsia_io_DirectoryAdminQueryFilesystem(caller.borrow_channel(), &status, &info),
65               ZX_OK);
66     ASSERT_EQ(status, ZX_OK);
67     ASSERT_EQ(strncmp(fs_name, reinterpret_cast<char*>(info.name), strlen(fs_name)), 0);
68     ASSERT_LE(info.used_nodes, info.total_nodes, "Used nodes greater than free nodes");
69     ASSERT_LE(info.used_bytes, info.total_bytes, "Used bytes greater than free bytes");
70     // TODO(planders): eventually check that total/used counts are > 0
71     END_HELPER;
72 }
73 
MountUnmountShared(size_t block_size)74 bool MountUnmountShared(size_t block_size) {
75     BEGIN_HELPER;
76     char ramdisk_path[PATH_MAX];
77     const char* mount_path = "/tmp/mount_unmount";
78 
79     ASSERT_EQ(create_ramdisk(block_size, 1 << 16, ramdisk_path), ZX_OK);
80     ASSERT_EQ(mkfs(ramdisk_path, DISK_FORMAT_MINFS, launch_stdio_sync, &default_mkfs_options),
81               ZX_OK);
82     ASSERT_EQ(mkdir(mount_path, 0666), 0);
83     ASSERT_TRUE(CheckMountedFs(mount_path, "memfs", strlen("memfs")));
84     int fd = open(ramdisk_path, O_RDWR);
85     ASSERT_GT(fd, 0);
86     ASSERT_EQ(mount(fd, mount_path, DISK_FORMAT_MINFS, &default_mount_options, launch_stdio_async),
87               ZX_OK);
88     ASSERT_TRUE(CheckMountedFs(mount_path, "minfs", strlen("minfs")));
89     ASSERT_EQ(umount(mount_path), ZX_OK);
90     ASSERT_TRUE(CheckMountedFs(mount_path, "memfs", strlen("memfs")));
91     ASSERT_EQ(destroy_ramdisk(ramdisk_path), 0);
92     ASSERT_EQ(unlink(mount_path), 0);
93     END_HELPER;
94 }
95 
MountUnmount()96 bool MountUnmount() {
97     BEGIN_TEST;
98     ASSERT_TRUE(MountUnmountShared(512));
99     END_TEST;
100 }
101 
MountUnmountLargeBlock()102 bool MountUnmountLargeBlock() {
103     BEGIN_TEST;
104     ASSERT_TRUE(MountUnmountShared(8192));
105     END_TEST;
106 }
107 
MountMkdirUnmount()108 bool MountMkdirUnmount() {
109     char ramdisk_path[PATH_MAX];
110     const char* mount_path = "/tmp/mount_mkdir_unmount";
111 
112     BEGIN_TEST;
113     ASSERT_EQ(create_ramdisk(512, 1 << 16, ramdisk_path), ZX_OK);
114     ASSERT_EQ(mkfs(ramdisk_path, DISK_FORMAT_MINFS, launch_stdio_sync, &default_mkfs_options),
115               ZX_OK);
116     int fd = open(ramdisk_path, O_RDWR);
117     ASSERT_GT(fd, 0);
118     mount_options_t options = default_mount_options;
119     options.create_mountpoint = true;
120     ASSERT_EQ(mount(fd, mount_path, DISK_FORMAT_MINFS, &options, launch_stdio_async), ZX_OK);
121     ASSERT_TRUE(CheckMountedFs(mount_path, "minfs", strlen("minfs")));
122     ASSERT_EQ(umount(mount_path), ZX_OK);
123     ASSERT_TRUE(CheckMountedFs(mount_path, "memfs", strlen("memfs")));
124     ASSERT_EQ(destroy_ramdisk(ramdisk_path), 0);
125     ASSERT_EQ(unlink(mount_path), 0);
126     END_TEST;
127 }
128 
FmountFunmount()129 bool FmountFunmount() {
130     char ramdisk_path[PATH_MAX];
131     const char* mount_path = "/tmp/mount_unmount";
132 
133     BEGIN_TEST;
134     ASSERT_EQ(create_ramdisk(512, 1 << 16, ramdisk_path), ZX_OK);
135     ASSERT_EQ(mkfs(ramdisk_path, DISK_FORMAT_MINFS, launch_stdio_sync, &default_mkfs_options),
136               ZX_OK);
137     ASSERT_EQ(mkdir(mount_path, 0666), 0);
138     ASSERT_TRUE(CheckMountedFs(mount_path, "memfs", strlen("memfs")));
139     int fd = open(ramdisk_path, O_RDWR);
140     ASSERT_GT(fd, 0);
141 
142     int mountfd = open(mount_path, O_RDONLY | O_DIRECTORY | O_ADMIN);
143     ASSERT_GT(mountfd, 0, "Couldn't open mount point");
144     ASSERT_EQ(fmount(fd, mountfd, DISK_FORMAT_MINFS, &default_mount_options, launch_stdio_async),
145               ZX_OK);
146     ASSERT_TRUE(CheckMountedFs(mount_path, "minfs", strlen("minfs")));
147     ASSERT_EQ(fumount(mountfd), ZX_OK);
148     ASSERT_TRUE(CheckMountedFs(mount_path, "memfs", strlen("memfs")));
149     ASSERT_EQ(destroy_ramdisk(ramdisk_path), 0);
150     ASSERT_EQ(close(mountfd), 0, "Couldn't close ex-mount point");
151     ASSERT_EQ(unlink(mount_path), 0);
152     END_TEST;
153 }
154 
155 // All "parent" filesystems attempt to mount a MinFS ramdisk under malicious
156 // conditions.
157 //
158 // Note: For cases where "fmount" fails, we briefly sleep to allow the
159 // filesystem to unmount itself and relinquish control of the block device.
DoMountEvil(const char * parentfs_name,const char * mount_path)160 bool DoMountEvil(const char* parentfs_name, const char* mount_path) {
161     BEGIN_HELPER;
162     char ramdisk_path[PATH_MAX];
163     ASSERT_EQ(create_ramdisk(512, 1 << 16, ramdisk_path), ZX_OK);
164     ASSERT_EQ(mkfs(ramdisk_path, DISK_FORMAT_MINFS, launch_stdio_sync, &default_mkfs_options),
165               ZX_OK);
166     ASSERT_EQ(mkdir(mount_path, 0666), 0);
167 
168     int fd = open(ramdisk_path, O_RDWR);
169     ASSERT_GT(fd, 0);
170 
171     int mountfd = open(mount_path, O_RDONLY | O_DIRECTORY | O_ADMIN);
172     ASSERT_GT(mountfd, 0, "Couldn't open mount point");
173 
174     // Everything *would* be perfect to call fmount, when suddenly...
175     ASSERT_EQ(rmdir(mount_path), 0);
176     // The directory was unlinked! We can't mount now!
177     ASSERT_EQ(fmount(fd, mountfd, DISK_FORMAT_MINFS, &default_mount_options, launch_stdio_async),
178               ZX_ERR_NOT_DIR);
179     usleep(10000);
180     ASSERT_NE(fumount(mountfd), ZX_OK);
181     ASSERT_EQ(close(mountfd), 0, "Couldn't close unlinked not-mount point");
182 
183     // Re-acquire the ramdisk mount point; it's always consumed...
184     fd = open(ramdisk_path, O_RDWR);
185     ASSERT_GT(fd, 0);
186 
187     // Okay, okay, let's get a new mount path...
188     mountfd = open(mount_path, O_CREAT | O_RDWR);
189     ASSERT_GT(mountfd, 0);
190     // Wait a sec, that was a file, not a directory! We can't mount that!
191     ASSERT_EQ(fmount(fd, mountfd, DISK_FORMAT_MINFS, &default_mount_options, launch_stdio_async),
192               ZX_ERR_ACCESS_DENIED);
193     usleep(10000);
194     ASSERT_NE(fumount(mountfd), ZX_OK);
195     ASSERT_EQ(close(mountfd), 0, "Couldn't close file not-mount point");
196     ASSERT_EQ(unlink(mount_path), 0);
197 
198     // Re-acquire the ramdisk mount point again...
199     fd = open(ramdisk_path, O_RDWR);
200     ASSERT_GT(fd, 0);
201     ASSERT_EQ(mkdir(mount_path, 0666), 0);
202     // Try mounting without O_ADMIN (which is disallowed)
203     mountfd = open(mount_path, O_RDONLY | O_DIRECTORY);
204     ASSERT_GT(mountfd, 0, "Couldn't open mount point");
205     ASSERT_EQ(fmount(fd, mountfd, DISK_FORMAT_MINFS, &default_mount_options, launch_stdio_async),
206               ZX_ERR_ACCESS_DENIED);
207     usleep(10000);
208     ASSERT_EQ(close(mountfd), 0, "Couldn't close the unpriviledged mount point");
209 
210     // Okay, fine, let's mount successfully...
211     fd = open(ramdisk_path, O_RDWR);
212     ASSERT_GT(fd, 0);
213     mountfd = open(mount_path, O_RDONLY | O_DIRECTORY | O_ADMIN);
214     ASSERT_GT(mountfd, 0, "Couldn't open mount point");
215     ASSERT_EQ(fmount(fd, mountfd, DISK_FORMAT_MINFS, &default_mount_options, launch_stdio_async),
216               ZX_OK);
217     // Awesome, that worked. But we shouldn't be able to mount again!
218     fd = open(ramdisk_path, O_RDWR);
219     ASSERT_GT(fd, 0);
220     ASSERT_EQ(fmount(fd, mountfd, DISK_FORMAT_MINFS, &default_mount_options, launch_stdio_async),
221               ZX_ERR_BAD_STATE);
222     usleep(10000);
223     ASSERT_TRUE(CheckMountedFs(mount_path, "minfs", strlen("minfs")));
224 
225     // Let's try removing the mount point (we shouldn't be allowed to do so)
226     ASSERT_EQ(rmdir(mount_path), -1);
227     ASSERT_EQ(errno, EBUSY);
228 
229     // Let's try telling the target filesystem to shut down
230     // WITHOUT O_ADMIN
231     fbl::unique_fd badfd(open(mount_path, O_RDONLY | O_DIRECTORY));
232     ASSERT_TRUE(badfd);
233     zx_status_t status;
234     fzl::FdioCaller caller(std::move(badfd));
235     ASSERT_EQ(fuchsia_io_DirectoryAdminUnmount(caller.borrow_channel(), &status), ZX_OK);
236     ASSERT_EQ(status, ZX_ERR_ACCESS_DENIED);
237     ASSERT_EQ(close(caller.release().release()), 0);
238 
239     // Let's try unmounting the filesystem WITHOUT O_ADMIN
240     // (unpinning the remote handle from the parent FS).
241     badfd.reset(open(mount_path, O_RDONLY | O_DIRECTORY));
242     ASSERT_TRUE(badfd);
243     zx_handle_t h;
244     caller.reset(std::move(badfd));
245     ASSERT_EQ(fuchsia_io_DirectoryAdminUnmountNode(caller.borrow_channel(), &status, &h), ZX_OK);
246     ASSERT_EQ(h, ZX_HANDLE_INVALID);
247     ASSERT_EQ(status, ZX_ERR_ACCESS_DENIED);
248     ASSERT_EQ(close(caller.release().release()), 0);
249 
250     // When we unmount with an O_ADMIN handle, it should successfully detach.
251     ASSERT_EQ(fumount(mountfd), ZX_OK);
252     ASSERT_TRUE(CheckMountedFs(mount_path, parentfs_name, strlen(parentfs_name)));
253     ASSERT_EQ(close(mountfd), 0);
254     ASSERT_EQ(rmdir(mount_path), 0);
255     ASSERT_EQ(destroy_ramdisk(ramdisk_path), 0);
256     END_HELPER;
257 }
258 
MountEvilMemfs()259 bool MountEvilMemfs() {
260     BEGIN_TEST;
261     const char* mount_path = "/tmp/mount_evil";
262     ASSERT_TRUE(DoMountEvil("memfs", mount_path));
263     END_TEST;
264 }
265 
MountEvilMinfs()266 bool MountEvilMinfs() {
267     char ramdisk_path[PATH_MAX];
268 
269     BEGIN_TEST;
270     ASSERT_EQ(create_ramdisk(512, 1 << 16, ramdisk_path), ZX_OK);
271     ASSERT_EQ(mkfs(ramdisk_path, DISK_FORMAT_MINFS, launch_stdio_sync, &default_mkfs_options),
272               ZX_OK);
273     const char* parent_path = "/tmp/parent";
274     ASSERT_EQ(mkdir(parent_path, 0666), 0);
275     int mountfd = open(parent_path, O_RDONLY | O_DIRECTORY | O_ADMIN);
276     ASSERT_GT(mountfd, 0, "Couldn't open mount point");
277     int ramdiskfd = open(ramdisk_path, O_RDWR);
278     ASSERT_GT(ramdiskfd, 0);
279     ASSERT_EQ(
280         fmount(ramdiskfd, mountfd, DISK_FORMAT_MINFS, &default_mount_options, launch_stdio_async),
281         ZX_OK);
282     ASSERT_EQ(close(mountfd), 0);
283 
284     const char* mount_path = "/tmp/parent/mount_evil";
285     ASSERT_TRUE(DoMountEvil("minfs", mount_path));
286 
287     ASSERT_EQ(umount(parent_path), 0);
288     ASSERT_EQ(rmdir(parent_path), 0);
289     ASSERT_EQ(destroy_ramdisk(ramdisk_path), 0);
290     END_TEST;
291 }
292 
UmountTestEvil()293 bool UmountTestEvil() {
294     char ramdisk_path[PATH_MAX];
295     const char* mount_path = "/tmp/umount_test_evil";
296 
297     BEGIN_TEST;
298 
299     // Create a ramdisk, mount minfs
300     ASSERT_EQ(create_ramdisk(512, 1 << 16, ramdisk_path), ZX_OK);
301     ASSERT_EQ(mkfs(ramdisk_path, DISK_FORMAT_MINFS, launch_stdio_sync, &default_mkfs_options),
302               ZX_OK);
303     ASSERT_EQ(mkdir(mount_path, 0666), 0);
304     ASSERT_TRUE(CheckMountedFs(mount_path, "memfs", strlen("memfs")));
305     int fd = open(ramdisk_path, O_RDWR);
306     ASSERT_GT(fd, 0);
307     ASSERT_EQ(mount(fd, mount_path, DISK_FORMAT_MINFS, &default_mount_options, launch_stdio_async),
308               ZX_OK);
309     ASSERT_TRUE(CheckMountedFs(mount_path, "minfs", strlen("minfs")));
310 
311     // Try re-opening the root without O_ADMIN. We shouldn't be able to umount.
312     fbl::unique_fd weak_root_fd(open(mount_path, O_RDONLY | O_DIRECTORY));
313     ASSERT_TRUE(weak_root_fd);
314     zx_status_t status;
315     fzl::FdioCaller caller(std::move(weak_root_fd));
316     ASSERT_EQ(fuchsia_io_DirectoryAdminUnmount(caller.borrow_channel(), &status), ZX_OK);
317     ASSERT_EQ(status, ZX_ERR_ACCESS_DENIED);
318     weak_root_fd.reset(caller.release().release());
319 
320     // Try opening a non-root directory without O_ADMIN. We shouldn't be able
321     // to umount.
322     ASSERT_EQ(mkdirat(weak_root_fd.get(), "subdir", 0666), 0);
323     fbl::unique_fd weak_subdir_fd(openat(weak_root_fd.get(), "subdir", O_RDONLY | O_DIRECTORY));
324     ASSERT_TRUE(weak_subdir_fd);
325     caller.reset(std::move(weak_subdir_fd));
326     ASSERT_EQ(fuchsia_io_DirectoryAdminUnmount(caller.borrow_channel(), &status), ZX_OK);
327     ASSERT_EQ(status, ZX_ERR_ACCESS_DENIED);
328 
329     // Try opening a new directory with O_ADMIN. It shouldn't open.
330     weak_subdir_fd.reset(openat(weak_root_fd.get(), "subdir", O_RDONLY | O_DIRECTORY | O_ADMIN));
331     ASSERT_FALSE(weak_subdir_fd);
332 
333     // Finally, umount using O_NOREMOTE and acquiring the connection
334     // that has "O_ADMIN" set.
335     ASSERT_EQ(umount(mount_path), ZX_OK);
336     ASSERT_TRUE(CheckMountedFs(mount_path, "memfs", strlen("memfs")));
337     ASSERT_EQ(destroy_ramdisk(ramdisk_path), 0);
338     ASSERT_EQ(unlink(mount_path), 0);
339     END_TEST;
340 }
341 
DoubleMountRoot()342 bool DoubleMountRoot() {
343     char ramdisk_path[PATH_MAX];
344     const char* mount_path = "/tmp/double_mount_root";
345 
346     BEGIN_TEST;
347 
348     // Create a ramdisk, mount minfs
349     ASSERT_EQ(create_ramdisk(512, 1 << 16, ramdisk_path), ZX_OK);
350     ASSERT_EQ(mkfs(ramdisk_path, DISK_FORMAT_MINFS, launch_stdio_sync, &default_mkfs_options),
351               ZX_OK);
352     ASSERT_EQ(mkdir(mount_path, 0666), 0);
353     ASSERT_TRUE(CheckMountedFs(mount_path, "memfs", strlen("memfs")));
354     int fd = open(ramdisk_path, O_RDWR);
355     ASSERT_GE(fd, 0);
356     ASSERT_EQ(mount(fd, mount_path, DISK_FORMAT_MINFS, &default_mount_options, launch_stdio_async),
357               ZX_OK);
358     ASSERT_TRUE(CheckMountedFs(mount_path, "minfs", strlen("minfs")));
359 
360     // Create ANOTHER ramdisk, ready to be mounted...
361     // Try mounting again on top Minfs' remote root.
362     char ramdisk_path2[PATH_MAX];
363     ASSERT_EQ(create_ramdisk(512, 1 << 16, ramdisk_path2), ZX_OK);
364     ASSERT_EQ(mkfs(ramdisk_path2, DISK_FORMAT_MINFS, launch_stdio_sync, &default_mkfs_options),
365               ZX_OK);
366 
367     // Try mounting on the mount point (locally; should fail because something is already mounted)
368     int mount_fd = open(mount_path, O_RDONLY | O_NOREMOTE | O_ADMIN);
369     ASSERT_GE(mount_fd, 0);
370     fd = open(ramdisk_path2, O_RDWR);
371     ASSERT_GE(fd, 0);
372     ASSERT_NE(fmount(fd, mount_fd, DISK_FORMAT_MINFS, &default_mount_options, launch_stdio_async),
373               ZX_OK);
374     ASSERT_EQ(close(mount_fd), 0);
375 
376     // Try mounting on the mount root (remote; should fail because MinFS doesn't allow mounting
377     // on top of the root directory).
378     mount_fd = open(mount_path, O_RDONLY | O_ADMIN);
379     ASSERT_GE(mount_fd, 0);
380     fd = open(ramdisk_path2, O_RDWR);
381     ASSERT_GE(fd, 0);
382     ASSERT_NE(fmount(fd, mount_fd, DISK_FORMAT_MINFS, &default_mount_options, launch_stdio_async),
383               ZX_OK);
384     ASSERT_EQ(close(mount_fd), 0);
385 
386     ASSERT_EQ(umount(mount_path), ZX_OK);
387     ASSERT_TRUE(CheckMountedFs(mount_path, "memfs", strlen("memfs")));
388     ASSERT_EQ(destroy_ramdisk(ramdisk_path), 0);
389     ASSERT_EQ(destroy_ramdisk(ramdisk_path2), 0);
390     ASSERT_EQ(rmdir(mount_path), 0);
391     END_TEST;
392 }
393 
MountRemount()394 bool MountRemount() {
395     char ramdisk_path[PATH_MAX];
396     const char* mount_path = "/tmp/mount_remount";
397 
398     BEGIN_TEST;
399     ASSERT_EQ(create_ramdisk(512, 1 << 16, ramdisk_path), ZX_OK);
400     ASSERT_EQ(mkfs(ramdisk_path, DISK_FORMAT_MINFS, launch_stdio_sync, &default_mkfs_options),
401               ZX_OK);
402     ASSERT_EQ(mkdir(mount_path, 0666), 0);
403 
404     // We should still be able to mount and unmount the filesystem multiple times
405     for (size_t i = 0; i < 10; i++) {
406         int fd = open(ramdisk_path, O_RDWR);
407         ASSERT_GE(fd, 0);
408         ASSERT_EQ(
409             mount(fd, mount_path, DISK_FORMAT_MINFS, &default_mount_options, launch_stdio_async),
410             ZX_OK);
411         ASSERT_EQ(umount(mount_path), ZX_OK);
412     }
413     ASSERT_EQ(destroy_ramdisk(ramdisk_path), 0);
414     ASSERT_EQ(unlink(mount_path), 0);
415     END_TEST;
416 }
417 
MountFsck()418 bool MountFsck() {
419     char ramdisk_path[PATH_MAX];
420     const char* mount_path = "/tmp/mount_fsck";
421 
422     BEGIN_TEST;
423     ASSERT_EQ(create_ramdisk(512, 1 << 16, ramdisk_path), ZX_OK);
424     ASSERT_EQ(mkfs(ramdisk_path, DISK_FORMAT_MINFS, launch_stdio_sync, &default_mkfs_options),
425               ZX_OK);
426     ASSERT_EQ(mkdir(mount_path, 0666), 0);
427     int fd = open(ramdisk_path, O_RDWR);
428     ASSERT_GE(fd, 0, "Could not open ramdisk device");
429     ASSERT_EQ(mount(fd, mount_path, DISK_FORMAT_MINFS, &default_mount_options, launch_stdio_async),
430               ZX_OK);
431     ASSERT_EQ(umount(mount_path), ZX_OK);
432     // fsck shouldn't require any user input for a newly mkfs'd filesystem
433     ASSERT_EQ(fsck(ramdisk_path, DISK_FORMAT_MINFS, &default_fsck_options, launch_stdio_sync),
434               ZX_OK);
435     ASSERT_EQ(destroy_ramdisk(ramdisk_path), 0);
436     ASSERT_EQ(unlink(mount_path), 0);
437     END_TEST;
438 }
439 
MountGetDevice()440 bool MountGetDevice() {
441     char ramdisk_path[PATH_MAX];
442     const char* mount_path = "/tmp/mount_get_device";
443 
444     BEGIN_TEST;
445     ASSERT_EQ(create_ramdisk(512, 1 << 16, ramdisk_path), ZX_OK);
446     ASSERT_EQ(mkfs(ramdisk_path, DISK_FORMAT_MINFS, launch_stdio_sync, &default_mkfs_options),
447               ZX_OK);
448     ASSERT_EQ(mkdir(mount_path, 0666), 0);
449     ASSERT_TRUE(CheckMountedFs(mount_path, "memfs", strlen("memfs")));
450 
451     fbl::unique_fd mountfd(open(mount_path, O_RDONLY | O_ADMIN));
452     ASSERT_TRUE(mountfd);
453     char device_buffer[1024];
454     char* device_path = static_cast<char*>(device_buffer);
455     zx_status_t status;
456     size_t path_len;
457     fzl::FdioCaller caller(std::move(mountfd));
458     ASSERT_EQ(fuchsia_io_DirectoryAdminGetDevicePath(caller.borrow_channel(), &status,
459                                                      device_path, sizeof(device_buffer),
460                                                      &path_len),
461               ZX_OK);
462     ASSERT_EQ(status, ZX_ERR_NOT_SUPPORTED);
463 
464     int fd = open(ramdisk_path, O_RDWR);
465     ASSERT_GT(fd, 0);
466     ASSERT_EQ(mount(fd, mount_path, DISK_FORMAT_MINFS, &default_mount_options, launch_stdio_async),
467               ZX_OK);
468     ASSERT_TRUE(CheckMountedFs(mount_path, "minfs", strlen("minfs")));
469 
470     mountfd.reset(open(mount_path, O_RDONLY | O_ADMIN));
471     ASSERT_TRUE(mountfd);
472     caller.reset(std::move(mountfd));
473     ASSERT_EQ(fuchsia_io_DirectoryAdminGetDevicePath(caller.borrow_channel(), &status,
474                                                      device_path, sizeof(device_buffer),
475                                                      &path_len),
476               ZX_OK);
477     ASSERT_EQ(status, ZX_OK);
478     ASSERT_GT(path_len, 0, "Device path not found");
479     ASSERT_EQ(strncmp(ramdisk_path, device_path, path_len), 0, "Unexpected device path");
480 
481     mountfd.reset(open(mount_path, O_RDONLY));
482     ASSERT_TRUE(mountfd);
483     caller.reset(std::move(mountfd));
484     ASSERT_EQ(fuchsia_io_DirectoryAdminGetDevicePath(caller.borrow_channel(), &status,
485                                                      device_path, sizeof(device_buffer),
486                                                      &path_len),
487               ZX_OK);
488     ASSERT_EQ(status, ZX_ERR_ACCESS_DENIED);
489 
490     ASSERT_EQ(umount(mount_path), ZX_OK);
491     ASSERT_TRUE(CheckMountedFs(mount_path, "memfs", strlen("memfs")));
492 
493     mountfd.reset(open(mount_path, O_RDONLY | O_ADMIN));
494     ASSERT_TRUE(mountfd);
495     caller.reset(std::move(mountfd));
496     ASSERT_EQ(fuchsia_io_DirectoryAdminGetDevicePath(caller.borrow_channel(), &status,
497                                                      device_path, sizeof(device_buffer),
498                                                      &path_len),
499               ZX_OK);
500     ASSERT_EQ(status, ZX_ERR_NOT_SUPPORTED);
501 
502     ASSERT_EQ(destroy_ramdisk(ramdisk_path), 0);
503     ASSERT_EQ(unlink(mount_path), 0);
504     END_TEST;
505 }
506 
507 // Mounts a minfs formatted partition to the desired point.
MountMinfs(int block_fd,bool read_only,const char * mount_path)508 bool MountMinfs(int block_fd, bool read_only, const char* mount_path) {
509     BEGIN_HELPER;
510     mount_options_t options;
511     memcpy(&options, &default_mount_options, sizeof(mount_options_t));
512     options.readonly = read_only;
513 
514     ASSERT_EQ(mount(block_fd, mount_path, DISK_FORMAT_MINFS, &options, launch_stdio_async), ZX_OK);
515     ASSERT_TRUE(CheckMountedFs(mount_path, "minfs", strlen("minfs")));
516     END_HELPER;
517 }
518 
519 // Formats the ramdisk with minfs, and writes a small file to it.
CreateTestFile(const char * ramdisk_path,const char * mount_path,const char * file_name)520 bool CreateTestFile(const char* ramdisk_path, const char* mount_path, const char* file_name) {
521     BEGIN_HELPER;
522     ASSERT_EQ(mkfs(ramdisk_path, DISK_FORMAT_MINFS, launch_stdio_sync, &default_mkfs_options),
523               ZX_OK);
524     ASSERT_EQ(mkdir(mount_path, 0666), 0);
525 
526     int fd = open(ramdisk_path, O_RDWR);
527     ASSERT_GT(fd, 0);
528     ASSERT_TRUE(MountMinfs(fd, false, mount_path));
529 
530     int root_fd = open(mount_path, O_RDONLY | O_DIRECTORY);
531     ASSERT_GE(root_fd, 0);
532     fd = openat(root_fd, file_name, O_CREAT | O_RDWR);
533     ASSERT_GE(fd, 0);
534     ASSERT_EQ(write(fd, "hello", 6), 6);
535 
536     ASSERT_EQ(close(fd), 0);
537     ASSERT_EQ(close(root_fd), 0);
538     ASSERT_EQ(umount(mount_path), ZX_OK);
539     END_HELPER;
540 }
541 
542 // Tests that setting read-only on the mount options works as expected.
MountReadonly()543 bool MountReadonly() {
544     char ramdisk_path[PATH_MAX];
545     const char* mount_path = "/tmp/mount_readonly";
546     const char file_name[] = "some_file";
547 
548     BEGIN_TEST;
549     ASSERT_EQ(create_ramdisk(512, 1 << 16, ramdisk_path), ZX_OK);
550     ASSERT_TRUE(CreateTestFile(ramdisk_path, mount_path, file_name));
551 
552     int fd = open(ramdisk_path, O_RDWR);
553     ASSERT_GT(fd, 0);
554 
555     bool read_only = true;
556     ASSERT_TRUE(MountMinfs(fd, read_only, mount_path));
557 
558     int root_fd = open(mount_path, O_RDONLY | O_DIRECTORY);
559     ASSERT_GE(root_fd, 0);
560     fd = openat(root_fd, file_name, O_CREAT | O_RDWR);
561 
562     // We can no longer open the file as writable
563     ASSERT_LT(fd, 0);
564 
565     // We CAN open it as readable though
566     fd = openat(root_fd, file_name, O_RDONLY);
567     ASSERT_GT(fd, 0);
568     ASSERT_LT(write(fd, "hello", 6), 0);
569     char buf[6];
570     ASSERT_EQ(read(fd, buf, 6), 6);
571     ASSERT_EQ(memcmp(buf, "hello", 6), 0);
572 
573     ASSERT_LT(renameat(root_fd, file_name, root_fd, "new_file"), 0);
574     ASSERT_LT(unlinkat(root_fd, file_name, 0), 0);
575 
576     ASSERT_EQ(close(fd), 0);
577     ASSERT_EQ(close(root_fd), 0);
578     ASSERT_EQ(umount(mount_path), ZX_OK);
579 
580     ASSERT_EQ(destroy_ramdisk(ramdisk_path), 0);
581     ASSERT_EQ(unlink(mount_path), 0);
582 
583     END_TEST;
584 }
585 
586 // Test that when a block device claims to be read-only, the filesystem is mounted as read-only.
MountBlockReadonly()587 bool MountBlockReadonly() {
588     char ramdisk_path[PATH_MAX];
589     const char* mount_path = "/tmp/mount_readonly";
590     const char file_name[] = "some_file";
591 
592     BEGIN_TEST;
593 
594     ASSERT_EQ(create_ramdisk(512, 1 << 16, ramdisk_path), ZX_OK);
595     ASSERT_TRUE(CreateTestFile(ramdisk_path, mount_path, file_name));
596 
597     int fd = open(ramdisk_path, O_RDWR);
598     ASSERT_GT(fd, 0);
599 
600     uint32_t flags = BLOCK_FLAG_READONLY;
601     ASSERT_EQ(0, ioctl_ramdisk_set_flags(fd, &flags));
602 
603     bool read_only = false;
604     ASSERT_TRUE(MountMinfs(fd, read_only, mount_path));
605 
606     // We can't modify the file.
607     int root_fd = open(mount_path, O_RDONLY | O_DIRECTORY);
608     ASSERT_GE(root_fd, 0);
609     fd = openat(root_fd, file_name, O_CREAT | O_RDWR);
610     ASSERT_LT(fd, 0);
611 
612     // We can open it as read-only.
613     fd = openat(root_fd, file_name, O_RDONLY);
614     ASSERT_GT(fd, 0);
615     ASSERT_EQ(close(fd), 0);
616     ASSERT_EQ(close(root_fd), 0);
617     ASSERT_EQ(umount(mount_path), ZX_OK);
618 
619     ASSERT_EQ(destroy_ramdisk(ramdisk_path), 0);
620     ASSERT_EQ(unlink(mount_path), 0);
621 
622     END_TEST;
623 }
624 
StatfsTest()625 bool StatfsTest() {
626     char ramdisk_path[PATH_MAX];
627     const char* mount_path = "/tmp/mount_unmount";
628 
629     BEGIN_TEST;
630     ASSERT_EQ(create_ramdisk(512, 1 << 16, ramdisk_path), ZX_OK);
631     ASSERT_EQ(mkfs(ramdisk_path, DISK_FORMAT_MINFS, launch_stdio_sync, &default_mkfs_options),
632               ZX_OK);
633     ASSERT_EQ(mkdir(mount_path, 0666), 0);
634     int fd = open(ramdisk_path, O_RDWR);
635     ASSERT_GT(fd, 0);
636     ASSERT_EQ(mount(fd, mount_path, DISK_FORMAT_MINFS, &default_mount_options, launch_stdio_async),
637               ZX_OK);
638 
639     struct statfs stats;
640     int rc = statfs("", &stats);
641     int err = errno;
642     ASSERT_EQ(rc, -1);
643     ASSERT_EQ(err, ENOENT);
644 
645     rc = statfs(mount_path, &stats);
646     ASSERT_EQ(rc, 0);
647 
648     // Verify that at least some values make sense, without making the test too brittle.
649     ASSERT_EQ(stats.f_type, VFS_TYPE_MINFS);
650     ASSERT_NE(stats.f_fsid.__val[0] | stats.f_fsid.__val[1], 0);
651     ASSERT_EQ(stats.f_bsize, 8192u);
652     ASSERT_EQ(stats.f_namelen, 255u);
653     ASSERT_GT(stats.f_bavail, 0u);
654     ASSERT_GT(stats.f_ffree, 0u);
655 
656     ASSERT_EQ(umount(mount_path), ZX_OK);
657     ASSERT_EQ(destroy_ramdisk(ramdisk_path), 0);
658     ASSERT_EQ(unlink(mount_path), 0);
659     END_TEST;
660 }
661 
662 // Verifies that the values in stats match the other parameters
CheckStats(block_stats_t stats,size_t total_ops,size_t total_blocks,size_t total_reads,size_t total_blocks_read,size_t total_writes,size_t total_blocks_written)663 bool CheckStats(block_stats_t stats, size_t total_ops, size_t total_blocks, size_t total_reads,
664                 size_t total_blocks_read, size_t total_writes, size_t total_blocks_written) {
665     BEGIN_HELPER;
666     ASSERT_EQ(stats.total_ops, total_ops);
667     ASSERT_EQ(stats.total_blocks, total_blocks);
668     ASSERT_EQ(stats.total_reads, total_reads);
669     ASSERT_EQ(stats.total_blocks_read, total_blocks_read);
670     ASSERT_EQ(stats.total_writes, total_writes);
671     ASSERT_EQ(stats.total_blocks_written, total_blocks_written);
672     END_HELPER;
673 }
674 
675 // Tests functionality of IOCTL_BLOCK_GET_STATS
GetStatsTest(fs_test_utils::Fixture * fixture)676 bool GetStatsTest(fs_test_utils::Fixture* fixture) {
677     fbl::StringBuffer<512> test_data;
678     test_data.Resize(512, 'c');
679     char test_read[512];
680 
681     BEGIN_TEST;
682     fbl::unique_fd ram_fd(open(fixture->block_device_path().c_str(), O_RDONLY));
683     ASSERT_TRUE(ram_fd);
684     block_stats_t block_stats;
685     bool clear = true;
686     // Clear stats from creating ramdisk
687     ASSERT_GE(ioctl_block_get_stats(ram_fd.get(), &clear, &block_stats), ZX_OK);
688     // Retrieve cleared stats
689     ASSERT_GE(ioctl_block_get_stats(ram_fd.get(), &clear, &block_stats), ZX_OK);
690     ASSERT_TRUE(CheckStats(block_stats, 0, 0, 0, 0, 0, 0));
691 
692     fbl::StringBuffer<fs_test_utils::kPathSize> myfile;
693     myfile.AppendPrintf("%s/my_file.txt", fixture->fs_path().c_str());
694     fbl::unique_fd file_to_create(open(myfile.c_str(), O_RDWR | O_CREAT));
695     fsync(file_to_create.get());
696 
697     // Clear stats from creating file
698     ASSERT_GE(ioctl_block_get_stats(ram_fd.get(), &clear, &block_stats), ZX_OK);
699     ASSERT_EQ(write(file_to_create.get(), test_data.data(), 512), 512);
700     fsync(file_to_create.get());
701     ASSERT_GE(ioctl_block_get_stats(ram_fd.get(), &clear, &block_stats), ZX_OK);
702     // 5 ops total, 4 for the write and 1 for the sync, 64 blocks written by 4 16 block writes
703     ASSERT_TRUE(CheckStats(block_stats, 5, 64, 0, 0, 4, 64));
704     ASSERT_EQ(lseek(file_to_create.get(), 0, SEEK_SET), 0);
705     // Reset file to clear it from the cache
706     file_to_create.reset();
707     fixture->Remount();
708     file_to_create.reset(open(myfile.c_str(), O_RDONLY));
709     // Clear the stats from reseting the file
710     ASSERT_GE(ioctl_block_get_stats(ram_fd.get(), &clear, &block_stats), ZX_OK);
711     ASSERT_EQ(read(file_to_create.get(), test_read, 512), 512);
712     ASSERT_GE(ioctl_block_get_stats(ram_fd.get(), &clear, &block_stats), ZX_OK);
713     // 1 op for reading 16 blocks, no writes
714     ASSERT_TRUE(CheckStats(block_stats, 1, 16, 1, 16, 0, 0));
715     END_TEST;
716 }
717 
GetPartitionSliceCount(int partition_fd,size_t * out_count)718 bool GetPartitionSliceCount(int partition_fd, size_t* out_count) {
719     BEGIN_HELPER;
720 
721     fvm_info_t fvm_info;
722     ASSERT_GE(ioctl_block_fvm_query(partition_fd, &fvm_info), ZX_OK);
723 
724     query_request_t request;
725     query_response_t response;
726     memset(&request, 0, sizeof(request));
727     memset(&response, 0, sizeof(response));
728     request.count = 1;
729 
730     size_t allocated_slices = 0;
731     size_t end = 0;
732     for (size_t curr_slice = 0; curr_slice < fvm_info.vslice_count; curr_slice = end) {
733         request.vslice_start[0] = curr_slice;
734         ASSERT_GE(ioctl_block_fvm_vslice_query(partition_fd, &request, &response), ZX_OK);
735         end = curr_slice + response.vslice_range[0].count;
736         if (response.vslice_range[0].allocated) {
737             allocated_slices += response.vslice_range[0].count;
738         }
739     }
740     *out_count = allocated_slices;
741     END_HELPER;
742 }
743 
744 // Reformat the partition using a number of slices and verify that there are as many slices as
745 // originally pre-allocated.
MkfsMinfsWithMinFvmSlices(fs_test_utils::Fixture * fixture)746 bool MkfsMinfsWithMinFvmSlices(fs_test_utils::Fixture* fixture) {
747     BEGIN_TEST;
748     mkfs_options_t options = default_mkfs_options;
749     size_t base_slices = 0;
750     ASSERT_OK(
751         mkfs(fixture->partition_path().c_str(), DISK_FORMAT_MINFS, launch_stdio_sync,
752              &default_mkfs_options));
753     fbl::unique_fd partition_fd(open(fixture->partition_path().c_str(), O_RDONLY));
754     ASSERT_TRUE(partition_fd);
755     ASSERT_TRUE(GetPartitionSliceCount(partition_fd.get(), &base_slices));
756     options.fvm_data_slices += 10;
757 
758     ASSERT_TRUE(partition_fd);
759 
760     ASSERT_OK(
761         mkfs(fixture->partition_path().c_str(), DISK_FORMAT_MINFS, launch_stdio_sync, &options));
762     size_t allocated_slices = 0;
763     ASSERT_TRUE(GetPartitionSliceCount(partition_fd.get(), &allocated_slices));
764     EXPECT_GE(allocated_slices, base_slices + 10);
765 
766     disk_format_t actual_format = detect_disk_format(partition_fd.get());
767     ASSERT_EQ(actual_format, DISK_FORMAT_MINFS);
768     END_TEST;
769 }
770 
771 } // namespace
772 
773 BEGIN_TEST_CASE(fs_management_tests)
RUN_TEST_MEDIUM(MountUnmount)774 RUN_TEST_MEDIUM(MountUnmount)
775 RUN_TEST_MEDIUM(MountUnmountLargeBlock)
776 RUN_TEST_MEDIUM(MountMkdirUnmount)
777 RUN_TEST_MEDIUM(FmountFunmount)
778 RUN_TEST_MEDIUM(MountEvilMemfs)
779 RUN_TEST_MEDIUM(MountEvilMinfs)
780 RUN_TEST_MEDIUM(UmountTestEvil)
781 RUN_TEST_MEDIUM(DoubleMountRoot)
782 RUN_TEST_MEDIUM(MountRemount)
783 RUN_TEST_MEDIUM(MountFsck)
784 RUN_TEST_MEDIUM(MountGetDevice)
785 RUN_TEST_MEDIUM(MountReadonly)
786 RUN_TEST_MEDIUM(MountBlockReadonly)
787 RUN_TEST_MEDIUM(StatfsTest)
788 END_TEST_CASE(fs_management_tests)
789 
790 BEGIN_FS_TEST_CASE(fs_management_get_stats, MinfsRamdiskOptions)
791 RUN_FS_TEST_F(GetStatsTest)
792 END_FS_TEST_CASE(fs_management_get_stats, MinfsRamdiskOptions)
793 
794 BEGIN_FS_TEST_CASE(fs_management_mkfs_tests, PartitionOverFvmWithRamdisk)
795 RUN_FS_TEST_F(MkfsMinfsWithMinFvmSlices)
796 END_FS_TEST_CASE(fs_management_mkfs_tests, PartitionOverFvmWithRamdisk)
797 
798 int main(int argc, char** argv) {
799     return fs_test_utils::RunWithMemFs(
800         [argc, argv]() { return unittest_run_all_tests(argc, argv) ? 0 : -1; });
801 }
802