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