/* * Copyright (c) 2012 Apple Inc. All rights reserved. * * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. The rights granted to you under the License * may not be used to create, or enable the creation or redistribution of, * unlawful or unlicensed copies of an Apple operating system, or to * circumvent, violate, or enable the circumvention or violation of, any * terms of an Apple operating system software license agreement. * * Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ */ #include #include #include #include #include #include #include #include #include #include #include #include static LCK_GRP_DECLARE(mockfs_mtx_grp, "mockfs-mutex"); int mockfs_mountroot(mount_t mp, vnode_t rvp, __unused vfs_context_t ctx); /* * Functions that are part of the mockfs_vfsops structure. */ int mockfs_unmount(__unused struct mount *mp, __unused int mntflags, __unused vfs_context_t ctx); int mockfs_root(mount_t mp, vnode_t * vpp, __unused vfs_context_t ctx); int mockfs_sync(__unused struct mount *mp, __unused int waitfor, __unused vfs_context_t ctx); int mockfs_init(__unused struct vfsconf * vfsc); /* * mockfs_mountroot: * Given a mount (mp) and a vnode for the root device (rvp), builds a fake filesystem for rvp. This consists * of three nodes; a directory node (to serve as a mountpoint for devfs), a file node meant to serve as an * executable frontend for rootvp (we will assume that rootvp is an executable, that the kernel can subsequently * run), and the root node for the mockfs filesystem. The structure of mockfs is memory-backed; only the * contents of the file node refer to the backing device. * * Returns 0 on success, or an error. */ int mockfs_mountroot(mount_t mp, vnode_t rvp, __unused vfs_context_t ctx) { int rvalue = 0; mockfs_fsnode_t root_fsnode = NULL; mockfs_fsnode_t dev_fsnode = NULL; mockfs_fsnode_t file_fsnode = NULL; mockfs_mount_t mockfs_mount_data = NULL; dk_memdev_info_t memdev_info; /* * TODO: Validate that the device at least LOOKS like a mach-o (has a sane header); this would prevent us * from causing EBADMACHO panics further along the boot path. */ /* * There are no M_MOCKFS* definitions at the moment, just use M_TEMP. */ mockfs_mount_data = kalloc_type(struct mockfs_mount, Z_WAITOK | Z_ZERO); mockfs_fsnode_create(mp, MOCKFS_ROOT, &root_fsnode); mockfs_fsnode_create(mp, MOCKFS_DEV, &dev_fsnode); mockfs_fsnode_create(mp, MOCKFS_FILE, &file_fsnode); if (!root_fsnode || !dev_fsnode || !file_fsnode) { rvalue = ENOMEM; goto done; } /* * If rvp is a memory device (with a few caveats), we can point to the same physical memory as the device * and avoid pointless paging/copying; query the device node for the information we need to determine * if we can do this. */ bzero(&memdev_info, sizeof(memdev_info)); if (!VNOP_IOCTL(rvp, DKIOCGETMEMDEVINFO, (caddr_t)&memdev_info, 0, NULL)) { /* * For the moment, we won't try to optimize when mi_phys is true. */ if (!mockfs_mount_data->mockfs_physical_memory) { mockfs_mount_data->mockfs_memory_backed = memdev_info.mi_mdev; mockfs_mount_data->mockfs_physical_memory = memdev_info.mi_phys; mockfs_mount_data->mockfs_memdev_base = memdev_info.mi_base; mockfs_mount_data->mockfs_memdev_size = memdev_info.mi_size; } } lck_mtx_init(&mockfs_mount_data->mockfs_mnt_mtx, &mockfs_mtx_grp, LCK_ATTR_NULL); /* * All of the needed nodes/structures have been set up; now we just need to establish the relationships * between the various mockfs nodes. */ if ((rvalue = mockfs_fsnode_adopt(root_fsnode, dev_fsnode))) { goto done; } if ((rvalue = mockfs_fsnode_adopt(root_fsnode, file_fsnode))) { goto done; } mockfs_mount_data->mockfs_root = root_fsnode; mp->mnt_data = (typeof(mp->mnt_data))mockfs_mount_data; done: if (rvalue) { if (file_fsnode) { mockfs_fsnode_destroy(file_fsnode); } if (dev_fsnode) { mockfs_fsnode_destroy(dev_fsnode); } if (root_fsnode) { mockfs_fsnode_destroy(root_fsnode); } if (mockfs_mount_data) { lck_mtx_destroy(&mockfs_mount_data->mockfs_mnt_mtx, &mockfs_mtx_grp); kfree_type(struct mockfs_mount, mockfs_mount_data); } } return rvalue; } /* * mockfs_unmount: * Given a mount (mp), and associated flags (mntflags), performs the necessary teardown to destroy the mount. * * Returns 0 on success, or an error. */ int mockfs_unmount(struct mount *mp, int mntflags, __unused vfs_context_t ctx) { int rvalue; int vflush_flags; mockfs_fsnode_t root_fsnode; mockfs_mount_t mockfs_mnt; vflush_flags = 0; mockfs_mnt = (mockfs_mount_t) mp->mnt_data; /* * Reclaim the vnodes for the mount (forcibly, if requested; given that mockfs only support mountroot * at the moment, this should ALWAYS be forced), */ if (mntflags & MNT_FORCE) { vflush_flags |= FORCECLOSE; } rvalue = vflush(mp, NULL, vflush_flags); if (rvalue) { return rvalue; } /* * Past this point, errors are likely to be unrecoverable, so panic if we're given any excuse; we * need to teardown the mockfs_mnt data now, so that VFS can cleanup the mount structure. Note * that clearing mockfs_root before destroying the fsnode tree is related to an implementation * detail of mockfs_fsnode_destroy (which will refuse to destroy the root node). */ root_fsnode = mockfs_mnt->mockfs_root; mockfs_mnt->mockfs_root = NULL; rvalue = mockfs_fsnode_destroy(root_fsnode); if (rvalue) { panic("mockfs_unmount: Failed to destroy the fsnode tree"); } lck_mtx_destroy(&mockfs_mnt->mockfs_mnt_mtx, &mockfs_mtx_grp); kfree_type(struct mockfs_mount, mockfs_mnt); mp->mnt_data = NULL; return rvalue; } /* * mockfs_root: * Given a mount (mp), returns the root vnode (*vpp) for that mount with an iocount. * * Returns 0 on success, or an error. */ int mockfs_root(mount_t mp, vnode_t * vpp, __unused vfs_context_t ctx) { int rvalue; rvalue = mockfs_fsnode_vnode(((mockfs_mount_t) mp->mnt_data)->mockfs_root, vpp); return rvalue; } /* * mockfs_sync: * Returns success because we're a read-only filesystem. * * Returns 0. */ int mockfs_sync(__unused struct mount *mp, __unused int waitfor, __unused vfs_context_t ctx) { return 0; } int mockfs_init(__unused struct vfsconf * vfsc) { return 0; } struct vfsops mockfs_vfsops = { .vfs_unmount = mockfs_unmount, .vfs_root = mockfs_root, .vfs_sync = mockfs_sync, .vfs_init = mockfs_init, };