2 * Copyright (c) 2012 Apple Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
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.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
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.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
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>
34 #include <sys/mount_internal.h>
35 #include <sys/ubc_internal.h>
36 #include <sys/vnode_internal.h>
37 #include <vm/vm_protos.h>
39 #include <libkern/libkern.h>
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
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.
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.
55 * Returns 0 on success, or an error.
58 mockfs_fsnode_create(mount_t mp
, uint8_t type
, mockfs_fsnode_t
* fsnpp
)
78 * For a regular file, size is meaningful, but it will always be equal to the
79 * size of the backing device.
81 new_size
= mp
->mnt_devvp
->v_specinfo
->si_devsize
;
88 MALLOC(*fsnpp
, typeof(*fsnpp
), sizeof(**fsnpp
), M_TEMP
, M_WAITOK
| M_ZERO
);
95 (*fsnpp
)->size
= new_size
;
96 (*fsnpp
)->type
= type
;
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).
108 * Returns 0 on success, or an error.
111 mockfs_fsnode_destroy(mockfs_fsnode_t fsnp
)
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.
121 if (!fsnp
|| (((mockfs_mount_t
)fsnp
->mnt
->mnt_data
)->mockfs_root
== fsnp
)) {
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.
131 panic("mockfs_fsnode_destroy called on node with live vnode; fsnp = %p (in case gdb is screwing with you)", fsnp
);
135 * If this node has children, we need to destroy them.
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
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
);
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
);
155 * We need to orphan this node before we destroy it.
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
);
169 * mockfs_fsnode_adopt:
170 * Given two nodes (parent, child), makes one node the child of the other node.
172 * Returns 0 on success, or an error.
175 mockfs_fsnode_adopt(mockfs_fsnode_t parent
, mockfs_fsnode_t child
)
182 * The child must be an orphan, and the parent cannot be the child.
184 if ((!parent
|| !child
|| child
->parent
) && (parent
!= child
)) {
190 * Nodes are actually tied to a specific mount, so assert that both nodes belong to the same mount.
192 if (parent
->mnt
!= child
->mnt
) {
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
203 * Can the parent support another child (food, shelter, unused pointers)?
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
;
220 * mockfs_fsnode_orphan:
222 * Returns 0 on success, or an error.
225 mockfs_fsnode_orphan(mockfs_fsnode_t fsnp
)
228 mockfs_fsnode_t parent
;
232 if (!fsnp
|| !fsnp
->parent
) {
238 * Disallow orphaning a node with a live vnode for now.
241 panic("mockfs_fsnode_orphan called on node with live vnode; fsnp = %p (in case gdb is screwing with you)", fsnp
);
244 parent
= fsnp
->parent
;
246 if (parent
->child_a
== fsnp
) {
247 parent
->child_a
= NULL
;
249 } else if (parent
->child_b
== fsnp
) {
250 parent
->child_b
= NULL
;
253 panic("mockfs_fsnode_orphan insanity, fsnp->parent != parent->child; fsnp = %p (in case gdb is screwing with you)", fsnp
);
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).
266 * This should be safe, as we are walking the read-only parts of the filesystem structure (not touching
269 * Returns 0 on success, or an error.
272 mockfs_fsnode_child_by_type(mockfs_fsnode_t parent
, uint8_t type
, mockfs_fsnode_t
* child
)
278 if (!parent
|| !child
) {
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
;
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.
300 * Returns 0 on success, or an error.
303 mockfs_fsnode_vnode(mockfs_fsnode_t fsnp
, vnode_t
* vpp
)
306 memory_object_control_t ubc_mem_object
;
307 mockfs_mount_t mockfs_mnt
;
308 struct vnode_fsparam vnfs_param
;
310 if ((!fsnp
) || (!vpp
)) {
315 mockfs_mnt
= ((mockfs_mount_t
) fsnp
->mnt
->mnt_data
);
316 lck_mtx_lock(&mockfs_mnt
->mockfs_mnt_mtx
);
320 * The vnode already exists; this should be easy.
322 rvalue
= vnode_get(fsnp
->vp
);
328 * We need to create the vnode; this will be unpleasant.
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
);
344 if ((!rvalue
) && (fsnp
->type
== MOCKFS_FILE
) && (mockfs_mnt
->mockfs_memory_backed
)) {
346 * We're memory backed; point the pager towards the backing store of the device.
348 ubc_mem_object
= ubc_getobject(fsnp
->vp
, 0);
350 if (!ubc_mem_object
) {
351 panic("mockfs_fsvnode failed to get ubc_mem_object for a new vnode");
354 rvalue
= pager_map_to_phys_contiguous(ubc_mem_object
, 0, (mockfs_mnt
->mockfs_memdev_base
<< PAGE_SHIFT
), fsnp
->size
);
357 panic("mockfs_fsnode_vnode failed to create fictitious pages for a memory-backed device; rvalue = %d", rvalue
);
366 lck_mtx_unlock(&mockfs_mnt
->mockfs_mnt_mtx
);
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.
378 * Returns 0 on success, or an error.
381 mockfs_fsnode_drop_vnode(mockfs_fsnode_t fsnp
)
384 mockfs_mount_t mockfs_mnt
;
394 mockfs_mnt
= ((mockfs_mount_t
) fsnp
->mnt
->mnt_data
);
395 lck_mtx_lock(&mockfs_mnt
->mockfs_mnt_mtx
);
398 panic("mock_fsnode_drop_vnode: target fsnode does not have an associated vnode");
403 vnode_clearfsnode(vp
);
405 lck_mtx_unlock(&mockfs_mnt
->mockfs_mnt_mtx
);