1 /*
2 * Copyright (c) 2000-2010 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 /* Copyright (c) 1995 NeXT Computer, Inc. All Rights Reserved */
29 /*
30 * Copyright (c) 1989, 1993
31 * The Regents of the University of California. All rights reserved.
32 *
33 * This code is derived from software contributed
34 * to Berkeley by John Heidemann of the UCLA Ficus project.
35 *
36 * Source: * @(#)i405_init.c 2.10 92/04/27 UCLA Ficus project
37 *
38 * Redistribution and use in source and binary forms, with or without
39 * modification, are permitted provided that the following conditions
40 * are met:
41 * 1. Redistributions of source code must retain the above copyright
42 * notice, this list of conditions and the following disclaimer.
43 * 2. Redistributions in binary form must reproduce the above copyright
44 * notice, this list of conditions and the following disclaimer in the
45 * documentation and/or other materials provided with the distribution.
46 * 3. All advertising materials mentioning features or use of this software
47 * must display the following acknowledgement:
48 * This product includes software developed by the University of
49 * California, Berkeley and its contributors.
50 * 4. Neither the name of the University nor the names of its contributors
51 * may be used to endorse or promote products derived from this software
52 * without specific prior written permission.
53 *
54 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
55 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
56 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
57 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
58 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
59 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
60 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
61 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
62 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
63 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
64 * SUCH DAMAGE.
65 *
66 * @(#)vfs_init.c 8.5 (Berkeley) 5/11/95
67 */
68 /*
69 * NOTICE: This file was modified by SPARTA, Inc. in 2005 to introduce
70 * support for mandatory and extensible security protections. This notice
71 * is included in support of clause 2.2 (b) of the Apple Public License,
72 * Version 2.0.
73 */
74
75
76 #include <sys/param.h>
77 #include <sys/mount_internal.h>
78 #include <sys/time.h>
79 #include <sys/vm.h>
80 #include <sys/vnode_internal.h>
81 #include <sys/stat.h>
82 #include <sys/namei.h>
83 #include <sys/ucred.h>
84 #include <sys/errno.h>
85 #include <kern/kalloc.h>
86 #include <kern/smr.h>
87 #include <sys/decmpfs.h>
88
89 #if CONFIG_MACF
90 #include <security/mac_framework.h>
91 #include <sys/kauth.h>
92 #endif
93 #if QUOTA
94 #include <sys/quota.h>
95 #endif
96
97 /*
98 * Sigh, such primitive tools are these...
99 */
100 #if 0
101 #define DODEBUG(A) A
102 #else
103 #define DODEBUG(A)
104 #endif
105
106 KALLOC_TYPE_DEFINE(mount_zone, struct mount, KT_DEFAULT);
107
108 SMR_DEFINE(_vfs_smr);
109 const smr_t vfs_smr = &_vfs_smr;
110
111 __private_extern__ void vntblinit(void);
112
113 extern const struct vnodeopv_desc *vfs_opv_descs[];
114 /* a list of lists of vnodeops defns */
115 extern struct vnodeop_desc *vfs_op_descs[];
116 /* and the operations they perform */
117 /*
118 * This code doesn't work if the defn is **vnodop_defns with cc.
119 * The problem is because of the compiler sometimes putting in an
120 * extra level of indirection for arrays. It's an interesting
121 * "feature" of C.
122 */
123 int vfs_opv_numops;
124
125 typedef int (*PFIvp)(void *);
126
127 /*
128 * A miscellaneous routine.
129 * A generic "default" routine that just returns an error.
130 */
131 int
vn_default_error(void)132 vn_default_error(void)
133 {
134 return ENOTSUP;
135 }
136
137 /*
138 * vfs_init.c
139 *
140 * Allocate and fill in operations vectors.
141 *
142 * An undocumented feature of this approach to defining operations is that
143 * there can be multiple entries in vfs_opv_descs for the same operations
144 * vector. This allows third parties to extend the set of operations
145 * supported by another layer in a binary compatibile way. For example,
146 * assume that NFS needed to be modified to support Ficus. NFS has an entry
147 * (probably nfs_vnopdeop_decls) declaring all the operations NFS supports by
148 * default. Ficus could add another entry (ficus_nfs_vnodeop_decl_entensions)
149 * listing those new operations Ficus adds to NFS, all without modifying the
150 * NFS code. (Of couse, the OTW NFS protocol still needs to be munged, but
151 * that is a(whole)nother story.) This is a feature.
152 */
153 void
vfs_opv_init(void)154 vfs_opv_init(void)
155 {
156 int i, j, k;
157 int(***opv_desc_vector_p)(void *);
158 int(**opv_desc_vector)(void *);
159 const struct vnodeopv_entry_desc *opve_descp;
160
161 /*
162 * Allocate the dynamic vectors and fill them in.
163 */
164 for (i = 0; vfs_opv_descs[i]; i++) {
165 opv_desc_vector_p = vfs_opv_descs[i]->opv_desc_vector_p;
166 /*
167 * Allocate and init the vector, if it needs it.
168 * Also handle backwards compatibility.
169 */
170 if (*opv_desc_vector_p == NULL) {
171 *opv_desc_vector_p = zalloc_permanent(vfs_opv_numops * sizeof(PFIvp),
172 ZALIGN(PFIvp));
173 DODEBUG(printf("vector at %x allocated\n",
174 opv_desc_vector_p));
175 }
176 opv_desc_vector = *opv_desc_vector_p;
177 for (j = 0; vfs_opv_descs[i]->opv_desc_ops[j].opve_op; j++) {
178 opve_descp = &(vfs_opv_descs[i]->opv_desc_ops[j]);
179
180 /* Silently skip known-disabled operations */
181 if (opve_descp->opve_op->vdesc_flags & VDESC_DISABLED) {
182 printf("vfs_fsadd: Ignoring reference in %p to disabled operation %s.\n",
183 vfs_opv_descs[i], opve_descp->opve_op->vdesc_name);
184 continue;
185 }
186
187 /*
188 * Sanity check: is this operation listed
189 * in the list of operations? We check this
190 * by seeing if its offest is zero. Since
191 * the default routine should always be listed
192 * first, it should be the only one with a zero
193 * offset. Any other operation with a zero
194 * offset is probably not listed in
195 * vfs_op_descs, and so is probably an error.
196 *
197 * A panic here means the layer programmer
198 * has committed the all-too common bug
199 * of adding a new operation to the layer's
200 * list of vnode operations but
201 * not adding the operation to the system-wide
202 * list of supported operations.
203 */
204 if (opve_descp->opve_op->vdesc_offset == 0 &&
205 opve_descp->opve_op !=
206 VDESC(vnop_default)) {
207 printf("operation %s not listed in %s.\n",
208 opve_descp->opve_op->vdesc_name,
209 "vfs_op_descs");
210 panic("vfs_opv_init: bad operation");
211 }
212 /*
213 * Fill in this entry.
214 */
215 opv_desc_vector[opve_descp->opve_op->vdesc_offset] =
216 opve_descp->opve_impl;
217 }
218 }
219 /*
220 * Finally, go back and replace unfilled routines
221 * with their default. (Sigh, an O(n^3) algorithm. I
222 * could make it better, but that'd be work, and n is small.)
223 */
224 for (i = 0; vfs_opv_descs[i]; i++) {
225 opv_desc_vector = *(vfs_opv_descs[i]->opv_desc_vector_p);
226 /*
227 * Force every operations vector to have a default routine.
228 */
229 if (opv_desc_vector[VOFFSET(vnop_default)] == NULL) {
230 panic("vfs_opv_init: operation vector without default routine.");
231 }
232 for (k = 0; k < vfs_opv_numops; k++) {
233 if (opv_desc_vector[k] == NULL) {
234 opv_desc_vector[k] =
235 opv_desc_vector[VOFFSET(vnop_default)];
236 }
237 }
238 }
239 }
240
241 /*
242 * Initialize known vnode operations vectors.
243 */
244 void
vfs_op_init(void)245 vfs_op_init(void)
246 {
247 int i;
248
249 DODEBUG(printf("Vnode_interface_init.\n"));
250 /*
251 * Set all vnode vectors to a well known value.
252 */
253 for (i = 0; vfs_opv_descs[i]; i++) {
254 *(vfs_opv_descs[i]->opv_desc_vector_p) = NULL;
255 }
256 /*
257 * Figure out how many ops there are by counting the table,
258 * and assign each its offset.
259 */
260 for (vfs_opv_numops = 0, i = 0; vfs_op_descs[i]; i++) {
261 /* Silently skip known-disabled operations */
262 if (vfs_op_descs[i]->vdesc_flags & VDESC_DISABLED) {
263 continue;
264 }
265 vfs_op_descs[i]->vdesc_offset = vfs_opv_numops;
266 vfs_opv_numops++;
267 }
268 DODEBUG(printf("vfs_opv_numops=%d\n", vfs_opv_numops));
269 }
270
271 /*
272 * Routines having to do with the management of the vnode table.
273 */
274 extern struct vnodeops dead_vnodeops;
275 extern struct vnodeops spec_vnodeops;
276
277 /* vars for vnode list lock */
278 static LCK_GRP_DECLARE(vnode_list_lck_grp, "vnode list");
279 static LCK_ATTR_DECLARE(vnode_list_lck_attr, 0, 0);
280 static LCK_SPIN_DECLARE_ATTR(vnode_list_spin_lock,
281 &vnode_list_lck_grp, &vnode_list_lck_attr);
282 static LCK_MTX_DECLARE_ATTR(spechash_mtx_lock,
283 &vnode_list_lck_grp, &vnode_list_lck_attr);
284 LCK_MTX_DECLARE_ATTR(pkg_extensions_lck,
285 &vnode_list_lck_grp, &vnode_list_lck_attr);
286
287 /* vars for mount lock */
288 static LCK_GRP_DECLARE(mnt_lck_grp, "mount");
289 static LCK_ATTR_DECLARE(mnt_lck_attr, 0, 0);
290
291 /* vars for mount list lock */
292 static LCK_GRP_DECLARE(mnt_list_lck_grp, "mount list");
293 LCK_MTX_DECLARE(mnt_list_mtx_lock, &mnt_list_lck_grp);
294
295 /*
296 * We want dead_mountp to be a constant pointer, but vfsinit() runs
297 * pretty late, so we'll allocate the dead_mount statically and
298 * statically-initialized dead_mountp.
299 */
300 static struct mount dead_mount_store;
301 struct mount * const dead_mountp = &dead_mount_store;
302
303 /*
304 * Initialize the vnode structures and initialize each file system type.
305 */
306 void
vfsinit(void)307 vfsinit(void)
308 {
309 struct vfstable *vfsp;
310 int i, maxtypenum;
311 struct mount * mp;
312
313 /*
314 * Initialize the vnode table
315 */
316 vntblinit();
317 /*
318 * Initialize the filesystem event mechanism.
319 */
320 vfs_event_init();
321 /*
322 * Initialize the vnode name cache
323 */
324 nchinit();
325
326 /*
327 * Build vnode operation vectors.
328 */
329 vfs_op_init();
330 vfs_opv_init(); /* finish the job */
331 /*
332 * Initialize each file system type in the static list,
333 * until the first NULL ->vfs_vfsops is encountered.
334 */
335 maxtypenum = VT_NON;
336 for (vfsp = vfsconf, i = 0; i < maxvfsslots; i++, vfsp++) {
337 struct vfsconf vfsc;
338 if (vfsp->vfc_vfsops == (struct vfsops *)0) {
339 break;
340 }
341 if (i) {
342 vfsconf[i - 1].vfc_next = vfsp;
343 }
344 if (maxtypenum <= vfsp->vfc_typenum) {
345 maxtypenum = vfsp->vfc_typenum + 1;
346 }
347
348 bzero(&vfsc, sizeof(struct vfsconf));
349 vfsc.vfc_reserved1 = 0;
350 bcopy(vfsp->vfc_name, vfsc.vfc_name, sizeof(vfsc.vfc_name));
351 vfsc.vfc_typenum = vfsp->vfc_typenum;
352 vfsc.vfc_refcount = vfsp->vfc_refcount;
353 vfsc.vfc_flags = vfsp->vfc_flags;
354 vfsc.vfc_reserved2 = 0;
355 vfsc.vfc_reserved3 = 0;
356
357 if (vfsp->vfc_vfsops->vfs_sysctl) {
358 struct sysctl_oid *oidp = NULL;
359 struct sysctl_oid oid = SYSCTL_STRUCT_INIT(_vfs, vfsp->vfc_typenum, , CTLTYPE_NODE | CTLFLAG_KERN | CTLFLAG_RW | CTLFLAG_LOCKED, NULL, 0, vfs_sysctl_node, "-", "");
360
361 oidp = kalloc_type(struct sysctl_oid, Z_WAITOK);
362 *oidp = oid;
363
364 /* Memory for VFS oid held by vfsentry forever */
365 vfsp->vfc_sysctl = oidp;
366 oidp->oid_name = vfsp->vfc_name;
367 sysctl_register_oid(vfsp->vfc_sysctl);
368 }
369
370 (*vfsp->vfc_vfsops->vfs_init)(&vfsc);
371
372 numused_vfsslots++;
373 numregistered_fses++;
374 }
375 /* next vfc_typenum to be used */
376 maxvfstypenum = maxtypenum;
377
378 /*
379 * Initialize the vnop authorization scope.
380 */
381 vnode_authorize_init();
382
383 /*
384 * create a mount point for dead vnodes
385 */
386 mp = &dead_mount_store;
387 /* Initialize the default IO constraints */
388 mp->mnt_maxreadcnt = mp->mnt_maxwritecnt = MAXPHYS;
389 mp->mnt_segreadcnt = mp->mnt_segwritecnt = 32;
390 mp->mnt_maxsegreadsize = mp->mnt_maxreadcnt;
391 mp->mnt_maxsegwritesize = mp->mnt_maxwritecnt;
392 mp->mnt_devblocksize = DEV_BSIZE;
393 mp->mnt_alignmentmask = PAGE_MASK;
394 mp->mnt_ioqueue_depth = MNT_DEFAULT_IOQUEUE_DEPTH;
395 mp->mnt_ioscale = 1;
396 mp->mnt_ioflags = 0;
397 mp->mnt_realrootvp = NULLVP;
398 mp->mnt_authcache_ttl = CACHED_LOOKUP_RIGHT_TTL;
399
400 TAILQ_INIT(&mp->mnt_vnodelist);
401 TAILQ_INIT(&mp->mnt_workerqueue);
402 TAILQ_INIT(&mp->mnt_newvnodes);
403 mp->mnt_flag = MNT_LOCAL;
404 mp->mnt_lflag = MNT_LDEAD;
405 mount_lock_init(mp);
406
407 #if CONFIG_MACF
408 mac_mount_label_init(mp);
409 mac_mount_label_associate(vfs_context_kernel(), mp);
410 #endif
411 /*
412 * dead_mountp is a statically-initialized constant pointer
413 * to dead_mount_store.
414 */
415
416 #if FS_COMPRESSION
417 decmpfs_init();
418 #endif
419
420 nspace_resolver_init();
421 }
422
423 void
vnode_list_lock(void)424 vnode_list_lock(void)
425 {
426 lck_spin_lock_grp(&vnode_list_spin_lock, &vnode_list_lck_grp);
427 }
428
429 void
vnode_list_unlock(void)430 vnode_list_unlock(void)
431 {
432 lck_spin_unlock(&vnode_list_spin_lock);
433 }
434
435 void
mount_list_lock(void)436 mount_list_lock(void)
437 {
438 lck_mtx_lock(&mnt_list_mtx_lock);
439 }
440
441 void
mount_list_unlock(void)442 mount_list_unlock(void)
443 {
444 lck_mtx_unlock(&mnt_list_mtx_lock);
445 }
446
447 void
mount_lock_init(mount_t mp)448 mount_lock_init(mount_t mp)
449 {
450 lck_mtx_init(&mp->mnt_mlock, &mnt_lck_grp, &mnt_lck_attr);
451 lck_mtx_init(&mp->mnt_iter_lock, &mnt_lck_grp, &mnt_lck_attr);
452 lck_mtx_init(&mp->mnt_renamelock, &mnt_lck_grp, &mnt_lck_attr);
453 lck_rw_init(&mp->mnt_rwlock, &mnt_lck_grp, &mnt_lck_attr);
454 }
455
456 void
mount_lock_destroy(mount_t mp)457 mount_lock_destroy(mount_t mp)
458 {
459 lck_mtx_destroy(&mp->mnt_mlock, &mnt_lck_grp);
460 lck_mtx_destroy(&mp->mnt_iter_lock, &mnt_lck_grp);
461 lck_mtx_destroy(&mp->mnt_renamelock, &mnt_lck_grp);
462 lck_rw_destroy(&mp->mnt_rwlock, &mnt_lck_grp);
463 }
464
465
466 /*
467 * Name: vfstable_add
468 *
469 * Description: Add a filesystem to the vfsconf list at the first
470 * unused slot. If no slots are available, return an
471 * error.
472 *
473 * Parameter: nvfsp vfsconf for VFS to add
474 *
475 * Returns: 0 Success
476 * -1 Failure
477 *
478 * Notes: The vfsconf should be treated as a linked list by
479 * all external references, as the implementation is
480 * expected to change in the future. The linkage is
481 * through ->vfc_next, and the list is NULL terminated.
482 *
483 * Warning: This code assumes that vfsconf[0] is non-empty.
484 */
485 struct vfstable *
vfstable_add(struct vfstable * nvfsp)486 vfstable_add(struct vfstable *nvfsp)
487 {
488 int slot;
489 struct vfstable *slotp, *allocated = NULL;
490 struct sysctl_oid *oidp = NULL;
491
492
493 if (nvfsp->vfc_vfsops->vfs_sysctl) {
494 struct sysctl_oid oid = SYSCTL_STRUCT_INIT(_vfs, nvfsp->vfc_typenum, , CTLTYPE_NODE | CTLFLAG_KERN | CTLFLAG_RW | CTLFLAG_LOCKED, NULL, 0, vfs_sysctl_node, "-", "");
495
496 oidp = kalloc_type(struct sysctl_oid, Z_WAITOK);
497 *oidp = oid;
498 }
499
500 /*
501 * Find the next empty slot; we recognize an empty slot by a
502 * NULL-valued ->vfc_vfsops, so if we delete a VFS, we must
503 * ensure we set the entry back to NULL.
504 */
505 findslot:
506 mount_list_lock();
507 for (slot = 0; slot < maxvfsslots; slot++) {
508 if (vfsconf[slot].vfc_vfsops == NULL) {
509 break;
510 }
511 }
512 if (slot == maxvfsslots) {
513 if (allocated == NULL) {
514 mount_list_unlock();
515 /* out of static slots; allocate one instead */
516 allocated = kalloc_type(struct vfstable, Z_WAITOK);
517 goto findslot;
518 } else {
519 slotp = allocated;
520 }
521 } else {
522 slotp = &vfsconf[slot];
523 }
524
525 /*
526 * Replace the contents of the next empty slot with the contents
527 * of the provided nvfsp.
528 *
529 * Note; Takes advantage of the fact that 'slot' was left
530 * with the value of 'maxvfslots' in the allocation case.
531 */
532 bcopy(nvfsp, slotp, sizeof(struct vfstable));
533 if (slot != 0) {
534 slotp->vfc_next = vfsconf[slot - 1].vfc_next;
535 vfsconf[slot - 1].vfc_next = slotp;
536 } else {
537 slotp->vfc_next = NULL;
538 }
539
540 if (slotp != allocated) {
541 /* used a statically allocated slot */
542 numused_vfsslots++;
543 }
544 numregistered_fses++;
545
546 if (oidp) {
547 /* Memory freed in vfstable_del after unregistration */
548 slotp->vfc_sysctl = oidp;
549 oidp->oid_name = slotp->vfc_name;
550 sysctl_register_oid(slotp->vfc_sysctl);
551 }
552
553 mount_list_unlock();
554
555 if (allocated && allocated != slotp) {
556 /* did allocation, but ended up using static slot */
557 kfree_type(struct vfstable, allocated);
558 }
559
560 return slotp;
561 }
562
563 /*
564 * Name: vfstable_del
565 *
566 * Description: Remove a filesystem from the vfsconf list by name.
567 * If no such filesystem exists, return an error.
568 *
569 * Parameter: fs_name name of VFS to remove
570 *
571 * Returns: 0 Success
572 * -1 Failure
573 *
574 * Notes: Hopefully all filesystems have unique names.
575 */
576 int
vfstable_del(struct vfstable * vtbl)577 vfstable_del(struct vfstable * vtbl)
578 {
579 struct vfstable **vcpp;
580 struct vfstable *vcdelp;
581
582 #if DEBUG
583 lck_mtx_assert(&mnt_list_mtx_lock, LCK_MTX_ASSERT_OWNED);
584 #endif /* DEBUG */
585
586 /*
587 * Traverse the list looking for vtbl; if found, *vcpp
588 * will contain the address of the pointer to the entry to
589 * be removed.
590 */
591 for (vcpp = &vfsconf; *vcpp; vcpp = &(*vcpp)->vfc_next) {
592 if (*vcpp == vtbl) {
593 break;
594 }
595 }
596
597 if (*vcpp == NULL) {
598 return ESRCH; /* vtbl not on vfsconf list */
599 }
600 if ((*vcpp)->vfc_sysctl) {
601 sysctl_unregister_oid((*vcpp)->vfc_sysctl);
602 (*vcpp)->vfc_sysctl->oid_name = NULL;
603 kfree_type(struct sysctl_oid, (*vcpp)->vfc_sysctl);
604 }
605
606 /* Unlink entry */
607 vcdelp = *vcpp;
608 *vcpp = (*vcpp)->vfc_next;
609
610 /*
611 * Is this an entry from our static table? We find out by
612 * seeing if the pointer to the object to be deleted places
613 * the object in the address space containing the table (or not).
614 */
615 if (vcdelp >= vfsconf && vcdelp < (vfsconf + maxvfsslots)) { /* Y */
616 /* Mark as empty for vfscon_add() */
617 bzero(vcdelp, sizeof(struct vfstable));
618 numregistered_fses--;
619 numused_vfsslots--;
620 } else { /* N */
621 /*
622 * This entry was dynamically allocated; we must free it;
623 * we would prefer to have just linked the caller's
624 * vfsconf onto our list, but it may not be persistent
625 * because of the previous (copying) implementation.
626 */
627 numregistered_fses--;
628 mount_list_unlock();
629 kfree_type(struct vfstable, vcdelp);
630 mount_list_lock();
631 }
632
633 #if DEBUG
634 lck_mtx_assert(&mnt_list_mtx_lock, LCK_MTX_ASSERT_OWNED);
635 #endif /* DEBUG */
636
637 return 0;
638 }
639
640 void
SPECHASH_LOCK(void)641 SPECHASH_LOCK(void)
642 {
643 lck_mtx_lock(&spechash_mtx_lock);
644 }
645
646 void
SPECHASH_UNLOCK(void)647 SPECHASH_UNLOCK(void)
648 {
649 lck_mtx_unlock(&spechash_mtx_lock);
650 }
651