]>
Commit | Line | Data |
---|---|---|
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 | */ | |
57 | int 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 | ||
98 | done: | |
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 | */ | |
109 | int 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); | |
155 | done: | |
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 | */ | |
165 | int 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 | ||
207 | done: | |
208 | return rvalue; | |
209 | } | |
210 | ||
211 | /* | |
212 | * mockfs_fsnode_orphan: | |
213 | * | |
214 | * Returns 0 on success, or an error. | |
215 | */ | |
216 | int 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 | ||
247 | done: | |
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 | */ | |
262 | int 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 | ||
280 | done: | |
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 | */ | |
291 | int 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 | ||
354 | done: | |
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 | */ | |
366 | int 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); | |
391 | done: | |
392 | return rvalue; | |
393 | } | |
394 |