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 <miscfs/mockfs/mockfs.h>
30 #include <miscfs/mockfs/mockfs_fsnode.h>
31 #include <miscfs/mockfs/mockfs_vnops.h>
32 #include <miscfs/specfs/specdev.h>
33 #include <sys/disk.h>
34 #include <sys/mount_internal.h>
35 #include <sys/ubc_internal.h>
36 #include <sys/vnode_internal.h>
37 #include <vm/vm_protos.h>
38
39 #include <libkern/libkern.h>
40
41 /*
42 * For the moment, most operations that change the fsnode will be called only in the context of
43 * mockfs_mountroot, so they should not need to use a mutex. The exceptions are mockfs_fsnode_vnode,
44 * and mockfs_fsnode_drop_vnode, which will use a tree-wide mutex (that lives in the mockfs_mount_t
45 * for the mount).
46 *
47 * mockfs_fsnode_child_by_type doesn't require locking right now (we're only looking at the structure of
48 * the node tree, which should not change during VNOP operations.
49 */
50
51 /* mockfs_fsnode_create:
52 * Given a mount (mp) and mockfs node type (type), creates a new fsnode for that mountpoint (*fsnpp).
53 * For the moment (while type == fileid) we should have at most one node of any given type.
54 *
55 * Returns 0 on success, or an error.
56 */
57 int
mockfs_fsnode_create(mount_t mp,uint8_t type,mockfs_fsnode_t * fsnpp)58 mockfs_fsnode_create(mount_t mp, uint8_t type, mockfs_fsnode_t * fsnpp)
59 {
60 int rvalue;
61 uint64_t new_size;
62
63 rvalue = 0;
64 new_size = 0;
65
66 if (!fsnpp || !mp) {
67 rvalue = EINVAL;
68 goto done;
69 }
70
71 switch (type) {
72 case MOCKFS_ROOT:
73 break;
74 case MOCKFS_DEV:
75 break;
76 case MOCKFS_FILE:
77 /*
78 * For a regular file, size is meaningful, but it will always be equal to the
79 * size of the backing device.
80 */
81 new_size = mp->mnt_devvp->v_specinfo->si_devsize;
82 break;
83 default:
84 rvalue = EINVAL;
85 goto done;
86 }
87
88 *fsnpp = kalloc_type(mockfs_fsnode_t, Z_WAITOK | Z_ZERO | Z_NOFAIL);
89 (*fsnpp)->size = new_size;
90 (*fsnpp)->type = type;
91 (*fsnpp)->mnt = mp;
92
93 done:
94 return rvalue;
95 }
96
97 /*
98 * mockfs_fsnode_destroy:
99 * Given a node (fsnp), tears down and deallocates that node and the entire subtree that it is the
100 * root of (deallocates you, and your children, and your children's children! ...for three months).
101 *
102 * Returns 0 on success, or an error.
103 */
104 int
mockfs_fsnode_destroy(mockfs_fsnode_t fsnp)105 mockfs_fsnode_destroy(mockfs_fsnode_t fsnp)
106 {
107 int rvalue;
108
109 rvalue = 0;
110
111 /*
112 * We will not destroy a root node that is actively pointed to by the mount structure; the
113 * mount must drop the reference to the mockfs tree before we can deallocate it.
114 */
115 if (!fsnp || (((mockfs_mount_t)fsnp->mnt->mnt_data)->mockfs_root == fsnp)) {
116 rvalue = EINVAL;
117 goto done;
118 }
119
120 /*
121 * For now, panic in this case; I don't expect anyone to ask us to destroy a node with a live
122 * vfs reference, but this will tell me if that assumption is untrue.
123 */
124 if (fsnp->vp) {
125 panic("mockfs_fsnode_destroy called on node with live vnode; fsnp = %p (in case gdb is screwing with you)", fsnp);
126 }
127
128 /*
129 * If this node has children, we need to destroy them.
130 *
131 * At least for now, we aren't guaranteeing destroy will be clean; we may get partway through
132 * and encounter an error, in which case we will panic (we may still have a sane tree, but
133 * we've failed to destroy the subtree, which means someone called destroy when they should
134 * not have done so).
135 */
136 if (fsnp->child_a) {
137 if ((rvalue = mockfs_fsnode_destroy(fsnp->child_a))) {
138 panic("mockfs_fsnode_destroy failed on child_a; fsnp = %p (in case gdb is screwing with you), rvalue = %d", fsnp, rvalue);
139 }
140 }
141
142 if (fsnp->child_b) {
143 if ((rvalue = mockfs_fsnode_destroy(fsnp->child_b))) {
144 panic("mockfs_fsnode_destroy failed on child_b; fsnp = %p (in case gdb is screwing with you), rvalue = %d", fsnp, rvalue);
145 }
146 }
147
148 /*
149 * We need to orphan this node before we destroy it.
150 */
151 if (fsnp->parent) {
152 if ((rvalue = mockfs_fsnode_orphan(fsnp))) {
153 panic("mockfs_fsnode_orphan failed during destroy; fsnp = %p (in case gdb is screwing with you), rvalue = %d", fsnp, rvalue);
154 }
155 }
156
157 kfree_type(mockfs_fsnode_t, fsnp);
158 done:
159 return rvalue;
160 }
161
162 /*
163 * mockfs_fsnode_adopt:
164 * Given two nodes (parent, child), makes one node the child of the other node.
165 *
166 * Returns 0 on success, or an error.
167 */
168 int
mockfs_fsnode_adopt(mockfs_fsnode_t parent,mockfs_fsnode_t child)169 mockfs_fsnode_adopt(mockfs_fsnode_t parent, mockfs_fsnode_t child)
170 {
171 int rvalue;
172
173 rvalue = 0;
174
175 /*
176 * The child must be an orphan, and the parent cannot be the child.
177 */
178 if ((!parent || !child || child->parent) && (parent != child)) {
179 rvalue = EINVAL;
180 goto done;
181 }
182
183 /*
184 * Nodes are actually tied to a specific mount, so assert that both nodes belong to the same mount.
185 */
186 if (parent->mnt != child->mnt) {
187 rvalue = EINVAL;
188 goto done;
189 }
190
191 /*
192 * TODO: Get rid of this check if I ever get around to making the tree non-binary.
193 * TODO: Enforce that the parent cannot have two children of the same type (for the moment, this is
194 * implicit in the structure of the tree constructed by mockfs_mountroot, so we don't need to
195 * worry about it).
196 *
197 * Can the parent support another child (food, shelter, unused pointers)?
198 */
199 if (!parent->child_a) {
200 parent->child_a = child;
201 child->parent = parent;
202 } else if (!parent->child_b) {
203 parent->child_b = child;
204 child->parent = parent;
205 } else {
206 rvalue = ENOMEM;
207 }
208
209 done:
210 return rvalue;
211 }
212
213 /*
214 * mockfs_fsnode_orphan:
215 *
216 * Returns 0 on success, or an error.
217 */
218 int
mockfs_fsnode_orphan(mockfs_fsnode_t fsnp)219 mockfs_fsnode_orphan(mockfs_fsnode_t fsnp)
220 {
221 int rvalue;
222 mockfs_fsnode_t parent;
223
224 rvalue = 0;
225
226 if (!fsnp || !fsnp->parent) {
227 rvalue = EINVAL;
228 goto done;
229 }
230
231 /*
232 * Disallow orphaning a node with a live vnode for now.
233 */
234 if (fsnp->vp) {
235 panic("mockfs_fsnode_orphan called on node with live vnode; fsnp = %p (in case gdb is screwing with you)", fsnp);
236 }
237
238 parent = fsnp->parent;
239
240 if (parent->child_a == fsnp) {
241 parent->child_a = NULL;
242 fsnp->parent = NULL;
243 } else if (parent->child_b == fsnp) {
244 parent->child_b = NULL;
245 fsnp->parent = NULL;
246 } else {
247 panic("mockfs_fsnode_orphan insanity, fsnp->parent != parent->child; fsnp = %p (in case gdb is screwing with you)", fsnp);
248 }
249
250 done:
251 return rvalue;
252 }
253
254 /*
255 * mockfs_fsnode_child_by_type:
256 * Given a node (parent) and a type (type), returns the first child (*child) found corresponding to the
257 * requested type. This method exists to support lookup (which is responsible for mapping names, which
258 * we have no conception of currently, onto vnodes).
259 *
260 * This should be safe, as we are walking the read-only parts of the filesystem structure (not touching
261 * the vnode).
262 *
263 * Returns 0 on success, or an error.
264 */
265 int
mockfs_fsnode_child_by_type(mockfs_fsnode_t parent,uint8_t type,mockfs_fsnode_t * child)266 mockfs_fsnode_child_by_type(mockfs_fsnode_t parent, uint8_t type, mockfs_fsnode_t * child)
267 {
268 int rvalue;
269
270 rvalue = 0;
271
272 if (!parent || !child) {
273 rvalue = EINVAL;
274 goto done;
275 }
276
277 if ((parent->child_a) && (parent->child_a->type == type)) {
278 *child = parent->child_a;
279 } else if ((parent->child_b) && (parent->child_b->type == type)) {
280 *child = parent->child_b;
281 } else {
282 rvalue = ENOENT;
283 }
284
285 done:
286 return rvalue;
287 }
288
289 /*
290 * mockfs_fsnode_vnode:
291 * Given a mockfs node (fsnp), returns a vnode (*vpp) corresponding to the mockfs node; the vnode will
292 * have an iocount on it.
293 *
294 * Returns 0 on success, or an error.
295 */
296 int
mockfs_fsnode_vnode(mockfs_fsnode_t fsnp,vnode_t * vpp)297 mockfs_fsnode_vnode(mockfs_fsnode_t fsnp, vnode_t * vpp)
298 {
299 int rvalue;
300 memory_object_control_t ubc_mem_object;
301 mockfs_mount_t mockfs_mnt;
302 struct vnode_fsparam vnfs_param;
303
304 if ((!fsnp) || (!vpp)) {
305 rvalue = EINVAL;
306 goto done;
307 }
308
309 mockfs_mnt = ((mockfs_mount_t) fsnp->mnt->mnt_data);
310 lck_mtx_lock(&mockfs_mnt->mockfs_mnt_mtx);
311
312 if (fsnp->vp) {
313 /*
314 * The vnode already exists; this should be easy.
315 */
316 rvalue = vnode_get(fsnp->vp);
317 if (!rvalue) {
318 *vpp = fsnp->vp;
319 }
320 } else {
321 /*
322 * We need to create the vnode; this will be unpleasant.
323 */
324 vnfs_param.vnfs_mp = fsnp->mnt;
325 vnfs_param.vnfs_vtype = (fsnp->type == MOCKFS_FILE) ? VREG : VDIR;
326 vnfs_param.vnfs_str = "mockfs";
327 vnfs_param.vnfs_dvp = (fsnp->type == MOCKFS_ROOT) ? NULL : fsnp->parent->vp;
328 vnfs_param.vnfs_fsnode = fsnp;
329 vnfs_param.vnfs_vops = mockfs_vnodeop_p;
330 vnfs_param.vnfs_markroot = (fsnp->type == MOCKFS_ROOT);
331 vnfs_param.vnfs_marksystem = 0;
332 vnfs_param.vnfs_rdev = 0;
333 vnfs_param.vnfs_filesize = fsnp->size;
334 vnfs_param.vnfs_cnp = NULL;
335 vnfs_param.vnfs_flags = VNFS_CANTCACHE | VNFS_NOCACHE;
336 rvalue = vnode_create(VNCREATE_FLAVOR, VCREATESIZE, &vnfs_param, &fsnp->vp);
337
338 if ((!rvalue) && (fsnp->type == MOCKFS_FILE) && (mockfs_mnt->mockfs_memory_backed)) {
339 /*
340 * We're memory backed; point the pager towards the backing store of the device.
341 */
342 ubc_mem_object = ubc_getobject(fsnp->vp, 0);
343
344 if (!ubc_mem_object) {
345 panic("mockfs_fsvnode failed to get ubc_mem_object for a new vnode");
346 }
347
348 rvalue = pager_map_to_phys_contiguous(ubc_mem_object, 0, (mockfs_mnt->mockfs_memdev_base << PAGE_SHIFT), fsnp->size);
349
350 if (rvalue) {
351 panic("mockfs_fsnode_vnode failed to create fictitious pages for a memory-backed device; rvalue = %d", rvalue);
352 }
353 }
354
355 if (!rvalue) {
356 *vpp = fsnp->vp;
357 }
358 }
359
360 lck_mtx_unlock(&mockfs_mnt->mockfs_mnt_mtx);
361
362 done:
363 return rvalue;
364 }
365
366 /*
367 * mockfs_fsnode_vnode:
368 * Given a mockfs node (fsnp) that has a vnode associated with it, causes them to drop their
369 * references to each other. This exists to support mockfs_reclaim. This method will grab the tree
370 * mutex, as this will mutate the tree.
371 *
372 * Returns 0 on success, or an error.
373 */
374 int
mockfs_fsnode_drop_vnode(mockfs_fsnode_t fsnp)375 mockfs_fsnode_drop_vnode(mockfs_fsnode_t fsnp)
376 {
377 int rvalue;
378 mockfs_mount_t mockfs_mnt;
379 vnode_t vp;
380
381 rvalue = 0;
382
383 if (!fsnp) {
384 rvalue = EINVAL;
385 goto done;
386 }
387
388 mockfs_mnt = ((mockfs_mount_t) fsnp->mnt->mnt_data);
389 lck_mtx_lock(&mockfs_mnt->mockfs_mnt_mtx);
390
391 if (!(fsnp->vp)) {
392 panic("mock_fsnode_drop_vnode: target fsnode does not have an associated vnode");
393 }
394
395 vp = fsnp->vp;
396 fsnp->vp = NULL;
397 vnode_clearfsnode(vp);
398
399 lck_mtx_unlock(&mockfs_mnt->mockfs_mnt_mtx);
400 done:
401 return rvalue;
402 }
403