]> git.saurik.com Git - apple/xnu.git/blob - bsd/miscfs/mockfs/mockfs_vfsops.c
588fa7aadfd70fcd2359f49bdb9d7272c3a5daf2
[apple/xnu.git] / bsd / miscfs / mockfs / mockfs_vfsops.c
1 /*
2 * Copyright (c) 2012 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28
29 #include <kern/assert.h>
30 #include <kern/debug.h>
31 #include <libkern/libkern.h>
32 #include <miscfs/mockfs/mockfs.h>
33 #include <miscfs/mockfs/mockfs_fsnode.h>
34 #include <miscfs/mockfs/mockfs_vnops.h>
35 #include <miscfs/specfs/specdev.h>
36 #include <sys/disk.h>
37 #include <sys/errno.h>
38 #include <sys/malloc.h>
39 #include <sys/mount_internal.h>
40 #include <sys/vnode_internal.h>
41
42 static LCK_GRP_DECLARE(mockfs_mtx_grp, "mockfs-mutex");
43
44 int mockfs_mountroot(mount_t mp, vnode_t rvp, __unused vfs_context_t ctx);
45
46 /*
47 * Functions that are part of the mockfs_vfsops structure.
48 */
49 int mockfs_unmount(__unused struct mount *mp, __unused int mntflags, __unused vfs_context_t ctx);
50 int mockfs_root(mount_t mp, vnode_t * vpp, __unused vfs_context_t ctx);
51 int mockfs_sync(__unused struct mount *mp, __unused int waitfor, __unused vfs_context_t ctx);
52 int mockfs_init(__unused struct vfsconf * vfsc);
53
54 /*
55 * mockfs_mountroot:
56 * Given a mount (mp) and a vnode for the root device (rvp), builds a fake filesystem for rvp. This consists
57 * of three nodes; a directory node (to serve as a mountpoint for devfs), a file node meant to serve as an
58 * executable frontend for rootvp (we will assume that rootvp is an executable, that the kernel can subsequently
59 * run), and the root node for the mockfs filesystem. The structure of mockfs is memory-backed; only the
60 * contents of the file node refer to the backing device.
61 *
62 * Returns 0 on success, or an error.
63 */
64 int
65 mockfs_mountroot(mount_t mp, vnode_t rvp, __unused vfs_context_t ctx)
66 {
67 int rvalue = 0;
68 mockfs_fsnode_t root_fsnode = NULL;
69 mockfs_fsnode_t dev_fsnode = NULL;
70 mockfs_fsnode_t file_fsnode = NULL;
71 mockfs_mount_t mockfs_mount_data = NULL;
72 dk_memdev_info_t memdev_info;
73
74 /*
75 * TODO: Validate that the device at least LOOKS like a mach-o (has a sane header); this would prevent us
76 * from causing EBADMACHO panics further along the boot path.
77 */
78
79 /*
80 * There are no M_MOCKFS* definitions at the moment, just use M_TEMP.
81 */
82
83 MALLOC(mockfs_mount_data, mockfs_mount_t, sizeof(*mockfs_mount_data), M_TEMP, M_WAITOK | M_ZERO);
84 mockfs_fsnode_create(mp, MOCKFS_ROOT, &root_fsnode);
85 mockfs_fsnode_create(mp, MOCKFS_DEV, &dev_fsnode);
86 mockfs_fsnode_create(mp, MOCKFS_FILE, &file_fsnode);
87
88 if (!mockfs_mount_data || !root_fsnode || !dev_fsnode || !file_fsnode) {
89 rvalue = ENOMEM;
90 goto done;
91 }
92
93 /*
94 * If rvp is a memory device (with a few caveats), we can point to the same physical memory as the device
95 * and avoid pointless paging/copying; query the device node for the information we need to determine
96 * if we can do this.
97 */
98 bzero(&memdev_info, sizeof(memdev_info));
99
100 if (!VNOP_IOCTL(rvp, DKIOCGETMEMDEVINFO, (caddr_t)&memdev_info, 0, NULL)) {
101 /*
102 * For the moment, we won't try to optimize when mi_phys is true.
103 */
104 if (!mockfs_mount_data->mockfs_physical_memory) {
105 mockfs_mount_data->mockfs_memory_backed = memdev_info.mi_mdev;
106 mockfs_mount_data->mockfs_physical_memory = memdev_info.mi_phys;
107 mockfs_mount_data->mockfs_memdev_base = memdev_info.mi_base;
108 mockfs_mount_data->mockfs_memdev_size = memdev_info.mi_size;
109 }
110 }
111
112 lck_mtx_init(&mockfs_mount_data->mockfs_mnt_mtx, &mockfs_mtx_grp, LCK_ATTR_NULL);
113
114 /*
115 * All of the needed nodes/structures have been set up; now we just need to establish the relationships
116 * between the various mockfs nodes.
117 */
118 if ((rvalue = mockfs_fsnode_adopt(root_fsnode, dev_fsnode))) {
119 goto done;
120 }
121
122 if ((rvalue = mockfs_fsnode_adopt(root_fsnode, file_fsnode))) {
123 goto done;
124 }
125
126 mockfs_mount_data->mockfs_root = root_fsnode;
127 mp->mnt_data = (typeof(mp->mnt_data))mockfs_mount_data;
128
129 done:
130 if (rvalue) {
131 if (file_fsnode) {
132 mockfs_fsnode_destroy(file_fsnode);
133 }
134 if (dev_fsnode) {
135 mockfs_fsnode_destroy(dev_fsnode);
136 }
137 if (root_fsnode) {
138 mockfs_fsnode_destroy(root_fsnode);
139 }
140 if (mockfs_mount_data) {
141 lck_mtx_destroy(&mockfs_mount_data->mockfs_mnt_mtx, &mockfs_mtx_grp);
142 FREE(mockfs_mount_data, M_TEMP);
143 }
144 }
145
146 return rvalue;
147 }
148
149 /*
150 * mockfs_unmount:
151 * Given a mount (mp), and associated flags (mntflags), performs the necessary teardown to destroy the mount.
152 *
153 * Returns 0 on success, or an error.
154 */
155 int
156 mockfs_unmount(struct mount *mp, int mntflags, __unused vfs_context_t ctx)
157 {
158 int rvalue;
159 int vflush_flags;
160 mockfs_fsnode_t root_fsnode;
161 mockfs_mount_t mockfs_mnt;
162
163 vflush_flags = 0;
164 mockfs_mnt = (mockfs_mount_t) mp->mnt_data;
165
166 /*
167 * Reclaim the vnodes for the mount (forcibly, if requested; given that mockfs only support mountroot
168 * at the moment, this should ALWAYS be forced),
169 */
170 if (mntflags & MNT_FORCE) {
171 vflush_flags |= FORCECLOSE;
172 }
173
174 rvalue = vflush(mp, NULL, vflush_flags);
175
176 if (rvalue) {
177 return rvalue;
178 }
179
180 /*
181 * Past this point, errors are likely to be unrecoverable, so panic if we're given any excuse; we
182 * need to teardown the mockfs_mnt data now, so that VFS can cleanup the mount structure. Note
183 * that clearing mockfs_root before destroying the fsnode tree is related to an implementation
184 * detail of mockfs_fsnode_destroy (which will refuse to destroy the root node).
185 */
186 root_fsnode = mockfs_mnt->mockfs_root;
187 mockfs_mnt->mockfs_root = NULL;
188 rvalue = mockfs_fsnode_destroy(root_fsnode);
189
190 if (rvalue) {
191 panic("mockfs_unmount: Failed to destroy the fsnode tree");
192 }
193
194 lck_mtx_destroy(&mockfs_mnt->mockfs_mnt_mtx, &mockfs_mtx_grp);
195 FREE(mockfs_mnt, M_TEMP);
196 mp->mnt_data = NULL;
197
198 return rvalue;
199 }
200
201 /*
202 * mockfs_root:
203 * Given a mount (mp), returns the root vnode (*vpp) for that mount with an iocount.
204 *
205 * Returns 0 on success, or an error.
206 */
207 int
208 mockfs_root(mount_t mp, vnode_t * vpp, __unused vfs_context_t ctx)
209 {
210 int rvalue;
211
212 rvalue = mockfs_fsnode_vnode(((mockfs_mount_t) mp->mnt_data)->mockfs_root, vpp);
213 return rvalue;
214 }
215
216 /*
217 * mockfs_sync:
218 * Returns success because we're a read-only filesystem.
219 *
220 * Returns 0.
221 */
222 int
223 mockfs_sync(__unused struct mount *mp, __unused int waitfor, __unused vfs_context_t ctx)
224 {
225 return 0;
226 }
227
228 int
229 mockfs_init(__unused struct vfsconf * vfsc)
230 {
231 return 0;
232 }
233
234 struct vfsops mockfs_vfsops = {
235 .vfs_unmount = mockfs_unmount,
236 .vfs_root = mockfs_root,
237 .vfs_sync = mockfs_sync,
238 .vfs_init = mockfs_init,
239 };