]>
Commit | Line | Data |
---|---|---|
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 | } |