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