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