xref: /xnu-8796.101.5/bsd/miscfs/mockfs/mockfs_fsnode.c (revision aca3beaa3dfbd42498b42c5e5ce20a938e6554e5)
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