Merge commit 'ea01a15a654b9e1c7b37d958f4d1911882ed7781'
[unleashed.git] / kernel / fs / namefs / namevfs.c
blob4ca7f529ba7915f87d290300a9d79b01c581ed24
1 /*
2 * CDDL HEADER START
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
19 * CDDL HEADER END
22 * Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
23 * Copyright (c) 2017 by Delphix. All rights reserved.
26 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
27 /* All Rights Reserved */
31 * This file supports the vfs operations for the NAMEFS file system.
34 #include <sys/types.h>
35 #include <sys/param.h>
36 #include <sys/systm.h>
37 #include <sys/debug.h>
38 #include <sys/errno.h>
39 #include <sys/kmem.h>
40 #include <sys/inline.h>
41 #include <sys/file.h>
42 #include <sys/proc.h>
43 #include <sys/stat.h>
44 #include <sys/statvfs.h>
45 #include <sys/mount.h>
46 #include <sys/sysmacros.h>
47 #include <sys/var.h>
48 #include <sys/vfs.h>
49 #include <sys/vnode.h>
50 #include <sys/mode.h>
51 #include <sys/pcb.h>
52 #include <sys/signal.h>
53 #include <sys/user.h>
54 #include <sys/uio.h>
55 #include <sys/cred.h>
56 #include <sys/fs/namenode.h>
57 #include <sys/stream.h>
58 #include <sys/strsubr.h>
59 #include <sys/cmn_err.h>
60 #include <sys/modctl.h>
61 #include <sys/fs_subr.h>
62 #include <sys/policy.h>
63 #include <sys/vmem.h>
64 #include <sys/fs/sdev_impl.h>
66 #define NM_INOQUANT (64 * 1024)
69 * Define global data structures.
71 dev_t namedev;
72 int namefstype;
73 struct namenode *nm_filevp_hash[NM_FILEVP_HASH_SIZE];
74 struct vfs namevfs;
75 kmutex_t ntable_lock;
77 static vmem_t *nm_inoarena; /* vmem arena to allocate inode no's from */
78 static kmutex_t nm_inolock;
80 static const struct vfsops namefs_vfsops;
83 * Functions to allocate node id's starting from 1. Based on vmem routines.
84 * The vmem arena is extended in NM_INOQUANT chunks.
86 uint64_t
87 namenodeno_alloc(void)
89 uint64_t nno;
91 mutex_enter(&nm_inolock);
92 nno = (uint64_t)(uintptr_t)
93 vmem_alloc(nm_inoarena, 1, VM_NOSLEEP + VM_FIRSTFIT);
94 if (nno == 0) {
95 (void) vmem_add(nm_inoarena, (void *)(vmem_size(nm_inoarena,
96 VMEM_ALLOC | VMEM_FREE) + 1), NM_INOQUANT, VM_SLEEP);
97 nno = (uint64_t)(uintptr_t)
98 vmem_alloc(nm_inoarena, 1, VM_SLEEP + VM_FIRSTFIT);
99 ASSERT(nno != 0);
101 mutex_exit(&nm_inolock);
102 ASSERT32(nno <= ULONG_MAX);
103 return (nno);
106 static void
107 namenodeno_init(void)
109 nm_inoarena = vmem_create("namefs_inodes", (void *)1, NM_INOQUANT, 1,
110 NULL, NULL, NULL, 1, VM_SLEEP);
111 mutex_init(&nm_inolock, NULL, MUTEX_DEFAULT, NULL);
114 void
115 namenodeno_free(uint64_t nn)
117 void *vaddr = (void *)(uintptr_t)nn;
119 ASSERT32((uint64_t)(uintptr_t)vaddr == nn);
121 mutex_enter(&nm_inolock);
122 vmem_free(nm_inoarena, vaddr, 1);
123 mutex_exit(&nm_inolock);
127 * Insert a namenode into the nm_filevp_hash table.
129 * Each link has a unique namenode with a unique nm_mountvp field.
130 * The nm_filevp field of the namenode need not be unique, since a
131 * file descriptor may be mounted to multiple nodes at the same time.
132 * We hash on nm_filevp since that's what discriminates the searches
133 * in namefind() and nm_unmountall().
135 void
136 nameinsert(struct namenode *nodep)
138 struct namenode **bucket;
140 ASSERT(MUTEX_HELD(&ntable_lock));
142 bucket = NM_FILEVP_HASH(nodep->nm_filevp);
143 nodep->nm_nextp = *bucket;
144 *bucket = nodep;
148 * Remove a namenode from the hash table, if present.
150 void
151 nameremove(struct namenode *nodep)
153 struct namenode *np, **npp;
155 ASSERT(MUTEX_HELD(&ntable_lock));
157 for (npp = NM_FILEVP_HASH(nodep->nm_filevp); (np = *npp) != NULL;
158 npp = &np->nm_nextp) {
159 if (np == nodep) {
160 *npp = np->nm_nextp;
161 return;
167 * Search for a namenode that has a nm_filevp == vp and nm_mountpt == mnt.
168 * If mnt is NULL, return the first link with nm_filevp of vp.
169 * Returns namenode pointer on success, NULL on failure.
171 struct namenode *
172 namefind(vnode_t *vp, vnode_t *mnt)
174 struct namenode *np;
176 ASSERT(MUTEX_HELD(&ntable_lock));
177 for (np = *NM_FILEVP_HASH(vp); np != NULL; np = np->nm_nextp)
178 if (np->nm_filevp == vp &&
179 (mnt == NULL || np->nm_mountpt == mnt))
180 break;
181 return (np);
185 * Force the unmouting of a file descriptor from ALL of the nodes
186 * that it was mounted to.
187 * At the present time, the only usage for this routine is in the
188 * event one end of a pipe was mounted. At the time the unmounted
189 * end gets closed down, the mounted end is forced to be unmounted.
191 * This routine searches the namenode hash list for all namenodes
192 * that have a nm_filevp field equal to vp. Each time one is found,
193 * the dounmount() routine is called. This causes the nm_unmount()
194 * routine to be called and thus, the file descriptor is unmounted
195 * from the node.
197 * At the start of this routine, the reference count for vp is
198 * incremented to protect the vnode from being released in the
199 * event the mount was the only thing keeping the vnode active.
200 * If that is the case, the fop_close operation is applied to
201 * the vnode, prior to it being released.
203 static int
204 nm_umountall(vnode_t *vp, cred_t *crp)
206 vfs_t *vfsp;
207 struct namenode *nodep;
208 int error = 0;
209 int realerr = 0;
212 * For each namenode that is associated with the file:
213 * If the v_vfsp field is not namevfs, dounmount it. Otherwise,
214 * it was created in nm_open() and will be released in time.
215 * The following loop replicates some code from nm_find. That
216 * routine can't be used as is since the list isn't strictly
217 * consumed as it is traversed.
219 mutex_enter(&ntable_lock);
220 nodep = *NM_FILEVP_HASH(vp);
221 while (nodep) {
222 if (nodep->nm_filevp == vp &&
223 (vfsp = NMTOV(nodep)->v_vfsp) != NULL &&
224 vfsp != &namevfs && (NMTOV(nodep)->v_flag & VROOT)) {
227 * If the vn_vfswlock fails, skip the vfs since
228 * somebody else may be unmounting it.
230 if (vn_vfswlock(vfsp->vfs_vnodecovered)) {
231 realerr = EBUSY;
232 nodep = nodep->nm_nextp;
233 continue;
237 * Can't hold ntable_lock across call to do_unmount
238 * because nm_unmount tries to acquire it. This means
239 * there is a window where another mount of vp can
240 * happen so it is possible that after nm_unmountall
241 * there are still some mounts. This situation existed
242 * without MT locking because dounmount can sleep
243 * so another mount could happen during that time.
244 * This situation is unlikely and doesn't really cause
245 * any problems.
247 mutex_exit(&ntable_lock);
248 if ((error = dounmount(vfsp, 0, crp)) != 0)
249 realerr = error;
250 mutex_enter(&ntable_lock);
252 * Since we dropped the ntable_lock, we
253 * have to start over from the beginning.
254 * If for some reasons dounmount() fails,
255 * start from beginning means that we will keep on
256 * trying unless another thread unmounts it for us.
258 nodep = *NM_FILEVP_HASH(vp);
259 } else
260 nodep = nodep->nm_nextp;
262 mutex_exit(&ntable_lock);
263 return (realerr);
267 * Force the unmouting of a file descriptor from ALL of the nodes
268 * that it was mounted to. XXX: fifo_close() calls this routine.
270 * nm_umountall() may return EBUSY.
271 * nm_unmountall() will keep on trying until it succeeds.
274 nm_unmountall(vnode_t *vp, cred_t *crp)
276 int error;
279 * Nm_umuontall() returns only if it succeeds or
280 * return with error EBUSY. If EBUSY, that means
281 * it cannot acquire the lock on the covered vnode,
282 * and we will keep on trying.
284 for (;;) {
285 error = nm_umountall(vp, crp);
286 if (error != EBUSY)
287 break;
288 delay(1); /* yield cpu briefly, then try again */
290 return (error);
294 * Mount a file descriptor onto the node in the file system.
295 * Create a new vnode, update the attributes with info from the
296 * file descriptor and the mount point. The mask, mode, uid, gid,
297 * atime, mtime and ctime are taken from the mountpt. Link count is
298 * set to one, the file system id is namedev and nodeid is unique
299 * for each mounted object. Other attributes are taken from mount point.
300 * Make sure user is owner (or root) with write permissions on mount point.
301 * Hash the new vnode and return 0.
302 * Upon entry to this routine, the file descriptor is in the
303 * fd field of a struct namefd. Copy that structure from user
304 * space and retrieve the file descriptor.
306 static int
307 nm_mount(vfs_t *vfsp, vnode_t *mvp, struct mounta *uap, cred_t *crp)
309 struct namefd namefdp;
310 struct vnode *filevp; /* file descriptor vnode */
311 struct file *fp;
312 struct vnode *newvp; /* vnode representing this mount */
313 struct vnode *rvp; /* realvp (if any) for the mountpt */
314 struct namenode *nodep; /* namenode for this mount */
315 struct vattr filevattr; /* attributes of file dec. */
316 struct vattr *vattrp; /* attributes of this mount */
317 char *resource_name;
318 char *resource_nodetype;
319 statvfs64_t *svfsp;
320 int error = 0;
323 * Get the file descriptor from user space.
324 * Make sure the file descriptor is valid and has an
325 * associated file pointer.
326 * If so, extract the vnode from the file pointer.
328 if (uap->datalen != sizeof (struct namefd))
329 return (EINVAL);
331 if (copyin(uap->dataptr, &namefdp, uap->datalen))
332 return (EFAULT);
334 if ((fp = getf(namefdp.fd)) == NULL)
335 return (EBADF);
338 * If the mount point already has something mounted
339 * on it, disallow this mount. (This restriction may
340 * be removed in a later release).
341 * Or unmount has completed but the namefs ROOT vnode
342 * count has not decremented to zero, disallow this mount.
345 mutex_enter(&mvp->v_lock);
346 if ((mvp->v_flag & VROOT) ||
347 vfs_matchops(mvp->v_vfsp, &namefs_vfsops)) {
348 mutex_exit(&mvp->v_lock);
349 releasef(namefdp.fd);
350 return (EBUSY);
352 mutex_exit(&mvp->v_lock);
355 * Cannot allow users to fattach() in /dev/pts.
356 * First, there is no need for doing so and secondly
357 * we cannot allow arbitrary users to park on a node in
358 * /dev/pts or /dev/vt.
360 rvp = NULLVP;
361 if (vn_matchops(mvp, spec_getvnodeops()) &&
362 fop_realvp(mvp, &rvp, NULL) == 0 && rvp &&
363 (vn_matchops(rvp, devpts_getvnodeops()) ||
364 vn_matchops(rvp, devvt_getvnodeops()))) {
365 releasef(namefdp.fd);
366 return (ENOTSUP);
369 filevp = fp->f_vnode;
370 if (filevp->v_type == VDIR || filevp->v_type == VPORT) {
371 releasef(namefdp.fd);
372 return (EINVAL);
376 * If the fd being mounted refers to neither a door nor a stream,
377 * make sure the caller is privileged.
379 if (filevp->v_type != VDOOR && filevp->v_stream == NULL) {
380 if (secpolicy_fs_mount(crp, filevp, vfsp) != 0) {
381 /* fd is neither a stream nor a door */
382 releasef(namefdp.fd);
383 return (EINVAL);
388 * Make sure the file descriptor is not the root of some
389 * file system.
390 * If it's not, create a reference and allocate a namenode
391 * to represent this mount request.
393 if (filevp->v_flag & VROOT) {
394 releasef(namefdp.fd);
395 return (EBUSY);
398 nodep = kmem_zalloc(sizeof (struct namenode), KM_SLEEP);
400 mutex_init(&nodep->nm_lock, NULL, MUTEX_DEFAULT, NULL);
401 vattrp = &nodep->nm_vattr;
402 vattrp->va_mask = AT_ALL;
403 if (error = fop_getattr(mvp, vattrp, 0, crp, NULL))
404 goto out;
406 filevattr.va_mask = AT_ALL;
407 if (error = fop_getattr(filevp, &filevattr, 0, crp, NULL))
408 goto out;
410 * Make sure the user is the owner of the mount point
411 * or has sufficient privileges.
413 if (error = secpolicy_vnode_owner(crp, vattrp->va_uid))
414 goto out;
417 * Make sure the user has write permissions on the
418 * mount point (or has sufficient privileges).
420 if (secpolicy_vnode_access2(crp, mvp, vattrp->va_uid, vattrp->va_mode,
421 VWRITE) != 0) {
422 error = EACCES;
423 goto out;
427 * If the file descriptor has file/record locking, don't
428 * allow the mount to succeed.
430 if (vn_has_flocks(filevp)) {
431 error = EACCES;
432 goto out;
436 * Initialize the namenode.
438 if (filevp->v_stream) {
439 struct stdata *stp = filevp->v_stream;
440 mutex_enter(&stp->sd_lock);
441 stp->sd_flag |= STRMOUNT;
442 mutex_exit(&stp->sd_lock);
444 nodep->nm_filevp = filevp;
445 mutex_enter(&fp->f_tlock);
446 fp->f_count++;
447 mutex_exit(&fp->f_tlock);
449 releasef(namefdp.fd);
450 nodep->nm_filep = fp;
451 nodep->nm_mountpt = mvp;
454 * The attributes for the mounted file descriptor were initialized
455 * above by applying fop_getattr to the mount point. Some of
456 * the fields of the attributes structure will be overwritten
457 * by the attributes from the file descriptor.
459 vattrp->va_type = filevattr.va_type;
460 vattrp->va_fsid = namedev;
461 vattrp->va_nodeid = namenodeno_alloc();
462 vattrp->va_nlink = 1;
463 vattrp->va_size = filevattr.va_size;
464 vattrp->va_rdev = filevattr.va_rdev;
465 vattrp->va_blksize = filevattr.va_blksize;
466 vattrp->va_nblocks = filevattr.va_nblocks;
467 vattrp->va_seq = 0;
470 * Initialize new vnode structure for the mounted file descriptor.
472 nodep->nm_vnode = vn_alloc(KM_SLEEP);
473 newvp = NMTOV(nodep);
475 newvp->v_flag = filevp->v_flag | VROOT | VNOMAP | VNOSWAP;
476 vn_setops(newvp, &nm_vnodeops);
477 newvp->v_vfsp = vfsp;
478 newvp->v_stream = filevp->v_stream;
479 newvp->v_type = filevp->v_type;
480 newvp->v_rdev = filevp->v_rdev;
481 newvp->v_data = (caddr_t)nodep;
482 VFS_HOLD(vfsp);
483 vn_exists(newvp);
486 * Initialize the vfs structure.
488 vfsp->vfs_vnodecovered = NULL;
489 vfsp->vfs_flag |= VFS_UNLINKABLE;
490 vfsp->vfs_bsize = 1024;
491 vfsp->vfs_fstype = namefstype;
492 vfs_make_fsid(&vfsp->vfs_fsid, namedev, namefstype);
493 vfsp->vfs_data = (caddr_t)nodep;
494 vfsp->vfs_dev = namedev;
495 vfsp->vfs_bcount = 0;
498 * Set the name we mounted from.
500 switch (filevp->v_type) {
501 case VPROC: /* fop_getattr() translates this to VREG */
502 case VREG: resource_nodetype = "file"; break;
503 case VDIR: resource_nodetype = "directory"; break;
504 case VBLK: resource_nodetype = "device"; break;
505 case VCHR: resource_nodetype = "device"; break;
506 case VLNK: resource_nodetype = "link"; break;
507 case VFIFO: resource_nodetype = "fifo"; break;
508 case VDOOR: resource_nodetype = "door"; break;
509 case VSOCK: resource_nodetype = "socket"; break;
510 default: resource_nodetype = "resource"; break;
513 #define RESOURCE_NAME_SZ 128 /* Maximum length of the resource name */
514 resource_name = kmem_alloc(RESOURCE_NAME_SZ, KM_SLEEP);
515 svfsp = kmem_alloc(sizeof (statvfs64_t), KM_SLEEP);
517 error = VFS_STATVFS(filevp->v_vfsp, svfsp);
518 if (error == 0) {
519 (void) snprintf(resource_name, RESOURCE_NAME_SZ,
520 "unspecified_%s_%s", svfsp->f_basetype, resource_nodetype);
521 } else {
522 (void) snprintf(resource_name, RESOURCE_NAME_SZ,
523 "unspecified_%s", resource_nodetype);
526 vfs_setresource(vfsp, resource_name, 0);
528 kmem_free(svfsp, sizeof (statvfs64_t));
529 kmem_free(resource_name, RESOURCE_NAME_SZ);
530 #undef RESOURCE_NAME_SZ
533 * Insert the namenode.
535 mutex_enter(&ntable_lock);
536 nameinsert(nodep);
537 mutex_exit(&ntable_lock);
538 return (0);
539 out:
540 releasef(namefdp.fd);
541 kmem_free(nodep, sizeof (struct namenode));
542 return (error);
546 * Unmount a file descriptor from a node in the file system.
547 * If the user is not the owner of the file and is not privileged,
548 * the request is denied.
549 * Otherwise, remove the namenode from the hash list.
550 * If the mounted file descriptor was that of a stream and this
551 * was the last mount of the stream, turn off the STRMOUNT flag.
552 * If the rootvp is referenced other than through the mount,
553 * nm_inactive will clean up.
555 static int
556 nm_unmount(vfs_t *vfsp, int flag, cred_t *crp)
558 struct namenode *nodep = (struct namenode *)vfsp->vfs_data;
559 vnode_t *vp, *thisvp;
560 struct file *fp = NULL;
562 ASSERT((nodep->nm_flag & NMNMNT) == 0);
565 * forced unmount is not supported by this file system
566 * and thus, ENOTSUP, is being returned.
568 if (flag & MS_FORCE) {
569 return (ENOTSUP);
572 vp = nodep->nm_filevp;
573 mutex_enter(&nodep->nm_lock);
574 if (secpolicy_vnode_owner(crp, nodep->nm_vattr.va_uid) != 0) {
575 mutex_exit(&nodep->nm_lock);
576 return (EPERM);
579 mutex_exit(&nodep->nm_lock);
581 mutex_enter(&ntable_lock);
582 nameremove(nodep);
583 thisvp = NMTOV(nodep);
584 mutex_enter(&thisvp->v_lock);
585 VN_RELE_LOCKED(thisvp);
586 if (thisvp->v_count == 0) {
587 fp = nodep->nm_filep;
588 mutex_exit(&thisvp->v_lock);
589 vn_invalid(thisvp);
590 vn_free(thisvp);
591 VFS_RELE(vfsp);
592 namenodeno_free(nodep->nm_vattr.va_nodeid);
593 kmem_free(nodep, sizeof (struct namenode));
594 } else {
595 thisvp->v_flag &= ~VROOT;
596 mutex_exit(&thisvp->v_lock);
598 if (namefind(vp, NULLVP) == NULL && vp->v_stream) {
599 struct stdata *stp = vp->v_stream;
600 mutex_enter(&stp->sd_lock);
601 stp->sd_flag &= ~STRMOUNT;
602 mutex_exit(&stp->sd_lock);
604 mutex_exit(&ntable_lock);
605 if (fp != NULL)
606 (void) closef(fp);
607 return (0);
611 * Create a reference to the root of a mounted file descriptor.
612 * This routine is called from lookupname() in the event a path
613 * is being searched that has a mounted file descriptor in it.
615 static int
616 nm_root(vfs_t *vfsp, vnode_t **vpp)
618 struct namenode *nodep = (struct namenode *)vfsp->vfs_data;
619 struct vnode *vp = NMTOV(nodep);
621 VN_HOLD(vp);
622 *vpp = vp;
623 return (0);
627 * Return in sp the status of this file system.
629 static int
630 nm_statvfs(vfs_t *vfsp, struct statvfs64 *sp)
632 dev32_t d32;
634 bzero(sp, sizeof (*sp));
635 sp->f_bsize = 1024;
636 sp->f_frsize = 1024;
637 (void) cmpldev(&d32, vfsp->vfs_dev);
638 sp->f_fsid = d32;
639 (void) strcpy(sp->f_basetype, vfssw[vfsp->vfs_fstype].vsw_name);
640 sp->f_flag = vf_to_stf(vfsp->vfs_flag);
641 return (0);
645 * Since this file system has no disk blocks of its own, apply
646 * the fop_fsync operation on the mounted file descriptor.
648 static int
649 nm_sync(vfs_t *vfsp, short flag, cred_t *crp)
651 struct namenode *nodep;
653 if (vfsp == NULL)
654 return (0);
656 nodep = (struct namenode *)vfsp->vfs_data;
657 if (flag & SYNC_CLOSE)
658 return (nm_umountall(nodep->nm_filevp, crp));
660 return (fop_fsync(nodep->nm_filevp, FSYNC, crp, NULL));
663 static const struct vfsops namefs_vfsops = {
664 .vfs_mount = nm_mount,
665 .vfs_unmount = nm_unmount,
666 .vfs_root = nm_root,
667 .vfs_statvfs = nm_statvfs,
668 .vfs_sync = nm_sync,
671 static const struct vfsops namefs_dummy_vfsops = {
672 .vfs_statvfs = nm_statvfs,
673 .vfs_sync = nm_sync,
677 * File system initialization routine. Save the file system type,
678 * establish a file system device number and initialize nm_filevp_hash[].
681 nameinit(int fstype, char *name)
683 int error;
684 int dev;
686 error = vfs_setfsops(fstype, &namefs_vfsops);
687 if (error != 0) {
688 cmn_err(CE_WARN, "nameinit: bad fsytpe");
689 return (error);
692 namefstype = fstype;
694 if ((dev = getudev()) == (major_t)-1) {
695 cmn_err(CE_WARN, "nameinit: can't get unique device");
696 dev = 0;
698 mutex_init(&ntable_lock, NULL, MUTEX_DEFAULT, NULL);
699 namedev = makedevice(dev, 0);
700 bzero(nm_filevp_hash, sizeof (nm_filevp_hash));
701 vfs_setops(&namevfs, &namefs_dummy_vfsops);
702 namevfs.vfs_vnodecovered = NULL;
703 namevfs.vfs_bsize = 1024;
704 namevfs.vfs_fstype = namefstype;
705 vfs_make_fsid(&namevfs.vfs_fsid, namedev, namefstype);
706 namevfs.vfs_dev = namedev;
707 return (0);
710 static mntopts_t nm_mntopts = {
712 NULL
715 static vfsdef_t vfw = {
716 VFSDEF_VERSION,
717 "namefs",
718 nameinit,
719 VSW_HASPROTO | VSW_ZMOUNT,
720 &nm_mntopts
724 * Module linkage information for the kernel.
726 static struct modlfs modlfs = {
727 &mod_fsops, "filesystem for namefs", &vfw
730 static struct modlinkage modlinkage = {
731 MODREV_1, (void *)&modlfs, NULL
735 _init(void)
737 namenodeno_init();
738 return (mod_install(&modlinkage));
742 _fini(void)
744 return (EBUSY);
748 _info(struct modinfo *modinfop)
750 return (mod_info(&modlinkage, modinfop));