2 * Copyright (c) 1988 University of Utah.
3 * Copyright (c) 1990, 1993
4 * The Regents of the University of California. All rights reserved.
6 * This code is derived from software contributed to Berkeley by
7 * the Systems Programming Group of the University of Utah Computer
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed by the University of
21 * California, Berkeley and its contributors.
22 * 4. Neither the name of the University nor the names of its contributors
23 * may be used to endorse or promote products derived from this software
24 * without specific prior written permission.
26 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
38 * from: Utah Hdr: vn.c 1.13 94/04/02
40 * from: @(#)vn.c 8.6 (Berkeley) 4/1/94
41 * $FreeBSD: src/sys/dev/vn/vn.c,v 1.105.2.4 2001/11/18 07:11:00 dillon Exp $
47 * Block/character interface to a vnode. Allows one to treat a file
48 * as a disk (e.g. build a filesystem in it, mount it, etc.).
50 * NOTE 1: There is a security issue involved with this driver.
51 * Once mounted all access to the contents of the "mapped" file via
52 * the special file is controlled by the permissions on the special
53 * file, the protection of the mapped file is ignored (effectively,
54 * by using root credentials in all transactions).
56 * NOTE 2: Doesn't interact with leases, should it?
60 #include <sys/param.h>
61 #include <sys/systm.h>
62 #include <sys/kernel.h>
65 #include <sys/nlookup.h>
67 #include <sys/malloc.h>
68 #include <sys/mount.h>
69 #include <sys/vnode.h>
70 #include <sys/fcntl.h>
72 #include <sys/diskslice.h>
75 #include <sys/module.h>
76 #include <sys/vnioctl.h>
79 #include <vm/vm_object.h>
80 #include <vm/vm_page.h>
81 #include <vm/vm_pager.h>
82 #include <vm/vm_pageout.h>
83 #include <vm/swap_pager.h>
84 #include <vm/vm_extern.h>
85 #include <vm/vm_zone.h>
86 #include <sys/devfs.h>
88 static d_ioctl_t vnioctl
;
89 static d_open_t vnopen
;
90 static d_close_t vnclose
;
91 static d_psize_t vnsize
;
92 static d_strategy_t vnstrategy
;
93 static d_clone_t vnclone
;
95 MALLOC_DEFINE(M_VN
, "vn_softc", "vn driver structures");
96 DEVFS_DECLARE_CLONE_BITMAP(vn
);
99 #define VN_PREALLOCATED_UNITS 4
101 #define VN_PREALLOCATED_UNITS NVN
104 #define VN_BSIZE_BEST 8192
108 * D_DISK we want to look like a disk
109 * D_CANFREE We support BUF_CMD_FREEBLKS
112 static struct dev_ops vn_ops
= {
113 { "vn", 0, D_DISK
| D_CANFREE
},
117 .d_write
= physwrite
,
119 .d_strategy
= vnstrategy
,
125 int sc_flags
; /* flags */
126 u_int64_t sc_size
; /* size of vn, sc_secsize scale */
127 int sc_secsize
; /* sector size */
129 struct vnode
*sc_vp
; /* vnode if not NULL */
130 vm_object_t sc_object
; /* backing object if not NULL */
131 struct ucred
*sc_cred
; /* credentials */
132 int sc_maxactive
; /* max # of active requests */
133 struct buf sc_tab
; /* transfer queue */
134 u_long sc_options
; /* options */
135 cdev_t sc_dev
; /* devices that refer to this unit */
136 SLIST_ENTRY(vn_softc
) sc_list
;
139 static SLIST_HEAD(, vn_softc
) vn_list
;
142 #define VNF_INITED 0x01
143 #define VNF_READONLY 0x02
144 #define VNF_OPENED 0x10
145 #define VNF_DESTROY 0x20
147 static u_long vn_options
;
149 #define IFOPT(vn,opt) if (((vn)->sc_options|vn_options) & (opt))
150 #define TESTOPT(vn,opt) (((vn)->sc_options|vn_options) & (opt))
152 static int vnsetcred (struct vn_softc
*vn
, struct ucred
*cred
);
153 static void vnclear (struct vn_softc
*vn
);
154 static int vnget (cdev_t dev
, struct vn_softc
*vn
, struct vn_user
*vnu
);
155 static int vn_modevent (module_t
, int, void *);
156 static int vniocattach_file (struct vn_softc
*, struct vn_ioctl
*, cdev_t dev
, int flag
, struct ucred
*cred
);
157 static int vniocattach_swap (struct vn_softc
*, struct vn_ioctl
*, cdev_t dev
, int flag
, struct ucred
*cred
);
158 static cdev_t
vn_create(int unit
, struct devfs_bitmap
*bitmap
, int clone
);
161 vnclone(struct dev_clone_args
*ap
)
165 unit
= devfs_clone_bitmap_get(&DEVFS_CLONE_BITMAP(vn
), 0);
166 ap
->a_dev
= vn_create(unit
, &DEVFS_CLONE_BITMAP(vn
), 1);
172 vnclose(struct dev_close_args
*ap
)
174 cdev_t dev
= ap
->a_head
.a_dev
;
178 KKASSERT(vn
!= NULL
);
180 vn
->sc_flags
&= ~VNF_OPENED
;
182 /* The disk has been detached and can now be safely destroyed */
183 if (vn
->sc_flags
& VNF_DESTROY
) {
184 KKASSERT(disk_getopencount(&vn
->sc_disk
) == 0);
185 disk_destroy(&vn
->sc_disk
);
186 devfs_clone_bitmap_put(&DEVFS_CLONE_BITMAP(vn
), dkunit(dev
));
187 SLIST_REMOVE(&vn_list
, vn
, vn_softc
, sc_list
);
193 static struct vn_softc
*
198 vn
= kmalloc(sizeof *vn
, M_VN
, M_WAITOK
| M_ZERO
);
203 vninitvn(struct vn_softc
*vn
, cdev_t dev
)
207 KKASSERT(vn
!= NULL
);
208 KKASSERT(dev
!= NULL
);
215 SLIST_INSERT_HEAD(&vn_list
, vn
, sc_list
);
219 vnopen(struct dev_open_args
*ap
)
221 cdev_t dev
= ap
->a_head
.a_dev
;
225 * Locate preexisting device
229 KKASSERT(vn
!= NULL
);
232 * Update si_bsize fields for device. This data will be overriden by
233 * the slice/parition code for vn accesses through partitions, and
234 * used directly if you open the 'whole disk' device.
236 * si_bsize_best must be reinitialized in case VN has been
237 * reconfigured, plus make it at least VN_BSIZE_BEST for efficiency.
239 dev
->si_bsize_phys
= vn
->sc_secsize
;
240 dev
->si_bsize_best
= vn
->sc_secsize
;
241 if (dev
->si_bsize_best
< VN_BSIZE_BEST
)
242 dev
->si_bsize_best
= VN_BSIZE_BEST
;
244 if ((ap
->a_oflags
& FWRITE
) && (vn
->sc_flags
& VNF_READONLY
))
248 kprintf("vnopen(%s, 0x%x, 0x%x)\n",
249 devtoname(dev
), ap
->a_oflags
, ap
->a_devtype
);
251 vn
->sc_flags
|= VNF_OPENED
;
258 * Run strategy routine for VN device. We use VOP_READ/VOP_WRITE calls
259 * for vnode-backed vn's, and the swap_pager_strategy() call for
260 * vm_object-backed vn's.
263 vnstrategy(struct dev_strategy_args
*ap
)
265 cdev_t dev
= ap
->a_head
.a_dev
;
266 struct bio
*bio
= ap
->a_bio
;
275 KKASSERT(vn
!= NULL
);
280 kprintf("vnstrategy(%p): unit %d\n", bp
, unit
);
282 if ((vn
->sc_flags
& VNF_INITED
) == 0) {
284 bp
->b_flags
|= B_ERROR
;
289 bp
->b_resid
= bp
->b_bcount
;
292 * The vnode device is using disk/slice label support.
294 * The dscheck() function is called for validating the
295 * slices that exist ON the vnode device itself, and
296 * translate the "slice-relative" block number, again.
297 * dscheck() will call biodone() and return NULL if
298 * we are at EOF or beyond the device size.
304 * Use the translated nbio from this point on
306 if (vn
->sc_vp
&& bp
->b_cmd
== BUF_CMD_FREEBLKS
) {
308 * Freeblks is not handled for vnode-backed elements yet.
311 /* operation complete */
312 } else if (vn
->sc_vp
) {
316 * If an error occurs, we set B_ERROR but we do not set
317 * B_INVAL because (for a write anyway), the buffer is
323 bzero(&auio
, sizeof(auio
));
325 aiov
.iov_base
= bp
->b_data
;
326 aiov
.iov_len
= bp
->b_bcount
;
327 auio
.uio_iov
= &aiov
;
329 auio
.uio_offset
= nbio
->bio_offset
;
330 auio
.uio_segflg
= UIO_SYSSPACE
;
331 if (bp
->b_cmd
== BUF_CMD_READ
)
332 auio
.uio_rw
= UIO_READ
;
334 auio
.uio_rw
= UIO_WRITE
;
335 auio
.uio_resid
= bp
->b_bcount
;
336 auio
.uio_td
= curthread
;
339 * Don't use IO_DIRECT here, it really gets in the way
340 * due to typical blocksize differences between the
341 * fs backing the VN device and whatever is running on
346 vn_lock(vn
->sc_vp
, LK_SHARED
| LK_RETRY
);
347 error
= VOP_READ(vn
->sc_vp
, &auio
, IO_RECURSE
,
351 case (BUF_CMD_WRITE
):
352 vn_lock(vn
->sc_vp
, LK_EXCLUSIVE
| LK_RETRY
);
353 error
= VOP_WRITE(vn
->sc_vp
, &auio
, IO_RECURSE
,
357 case (BUF_CMD_FLUSH
):
359 vn_lock(vn
->sc_vp
, LK_EXCLUSIVE
| LK_RETRY
);
360 error
= VOP_FSYNC(vn
->sc_vp
, MNT_WAIT
, 0);
367 vn_unlock(vn
->sc_vp
);
369 bp
->b_resid
= auio
.uio_resid
;
372 bp
->b_flags
|= B_ERROR
;
374 /* operation complete */
375 } else if (vn
->sc_object
) {
377 * OBJT_SWAP I/O (handles read, write, freebuf)
379 * We have nothing to do if freeing blocks on a reserved
380 * swap area, othrewise execute the op.
382 if (bp
->b_cmd
== BUF_CMD_FREEBLKS
&& TESTOPT(vn
, VN_RESERVE
)) {
384 /* operation complete */
386 swap_pager_strategy(vn
->sc_object
, nbio
);
391 bp
->b_resid
= bp
->b_bcount
;
392 bp
->b_flags
|= B_ERROR
| B_INVAL
;
393 bp
->b_error
= EINVAL
;
394 /* operation complete */
402 vnioctl(struct dev_ioctl_args
*ap
)
404 cdev_t dev
= ap
->a_head
.a_dev
;
406 struct vn_ioctl
*vio
;
411 IFOPT(vn
,VN_FOLLOW
) {
412 kprintf("vnioctl(%s, 0x%lx, %p, 0x%x): unit %d\n",
413 devtoname(dev
), ap
->a_cmd
, ap
->a_data
, ap
->a_fflag
,
429 if (dkslice(dev
) != WHOLE_DISK_SLICE
||
430 dkpart(dev
) != WHOLE_SLICE_PART
)
436 error
= priv_check_cred(ap
->a_cred
, PRIV_ROOT
, 0);
440 vio
= (struct vn_ioctl
*)ap
->a_data
;
441 f
= (u_long
*)ap
->a_data
;
445 if (vn
->sc_flags
& VNF_INITED
)
448 if (vn
->sc_flags
& VNF_DESTROY
)
451 if (vio
->vn_file
== NULL
)
452 error
= vniocattach_swap(vn
, vio
, dev
, ap
->a_fflag
, ap
->a_cred
);
454 error
= vniocattach_file(vn
, vio
, dev
, ap
->a_fflag
, ap
->a_cred
);
458 if ((vn
->sc_flags
& VNF_INITED
) == 0)
461 * XXX handle i/o in progress. Return EBUSY, or wait, or
463 * XXX handle multiple opens of the device. Return EBUSY,
464 * or revoke the fd's.
465 * How are these problems handled for removable and failing
466 * hardware devices? (Hint: They are not)
468 if ((disk_getopencount(&vn
->sc_disk
)) > 1)
473 kprintf("vnioctl: CLRed\n");
475 if (dkunit(dev
) >= VN_PREALLOCATED_UNITS
) {
476 vn
->sc_flags
|= VNF_DESTROY
;
482 error
= vnget(dev
, vn
, (struct vn_user
*) ap
->a_data
);
496 vn
->sc_options
|= *f
;
501 vn
->sc_options
&= ~(*f
);
515 * Attach a file to a VN partition. Return the size in the vn_size
520 vniocattach_file(struct vn_softc
*vn
, struct vn_ioctl
*vio
, cdev_t dev
,
521 int flag
, struct ucred
*cred
)
524 struct nlookupdata nd
;
527 struct disk_info info
;
529 flags
= FREAD
|FWRITE
;
530 error
= nlookup_init(&nd
, vio
->vn_file
,
531 UIO_USERSPACE
, NLC_FOLLOW
|NLC_LOCKVP
);
534 if ((error
= vn_open(&nd
, NULL
, flags
, 0)) != 0) {
535 if (error
!= EACCES
&& error
!= EPERM
&& error
!= EROFS
)
539 error
= nlookup_init(&nd
, vio
->vn_file
, UIO_USERSPACE
, NLC_FOLLOW
|NLC_LOCKVP
);
542 if ((error
= vn_open(&nd
, NULL
, flags
, 0)) != 0)
546 if (vp
->v_type
!= VREG
||
547 (error
= VOP_GETATTR(vp
, &vattr
))) {
553 vn
->sc_secsize
= DEV_BSIZE
;
555 nd
.nl_open_vp
= NULL
;
558 * If the size is specified, override the file attributes. Note that
559 * the vn_size argument is in PAGE_SIZE sized blocks.
562 vn
->sc_size
= vio
->vn_size
* PAGE_SIZE
/ vn
->sc_secsize
;
564 vn
->sc_size
= vattr
.va_size
/ vn
->sc_secsize
;
565 error
= vnsetcred(vn
, cred
);
568 vn_close(vp
, flags
, NULL
);
571 vn
->sc_flags
|= VNF_INITED
;
573 vn
->sc_flags
|= VNF_READONLY
;
576 * Set the disk info so that probing is triggered
578 bzero(&info
, sizeof(struct disk_info
));
579 info
.d_media_blksize
= vn
->sc_secsize
;
580 info
.d_media_blocks
= vn
->sc_size
;
582 * reserve mbr sector for backwards compatibility
583 * when no slices exist.
585 info
.d_dsflags
= DSO_COMPATMBR
| DSO_RAWPSIZE
;
586 info
.d_secpertrack
= 32;
587 info
.d_nheads
= 64 / (vn
->sc_secsize
/ DEV_BSIZE
);
588 info
.d_secpercyl
= info
.d_secpertrack
* info
.d_nheads
;
589 info
.d_ncylinders
= vn
->sc_size
/ info
.d_secpercyl
;
590 disk_setdiskinfo_sync(&vn
->sc_disk
, &info
);
592 error
= dev_dopen(dev
, flag
, S_IFCHR
, cred
, NULL
);
597 kprintf("vnioctl: SET vp %p size %llx blks\n",
598 vn
->sc_vp
, (long long)vn
->sc_size
);
607 * Attach swap backing store to a VN partition of the size specified
612 vniocattach_swap(struct vn_softc
*vn
, struct vn_ioctl
*vio
, cdev_t dev
,
613 int flag
, struct ucred
*cred
)
616 struct disk_info info
;
619 * Range check. Disallow negative sizes or any size less then the
620 * size of a page. Then round to a page.
623 if (vio
->vn_size
<= 0)
627 * Allocate an OBJT_SWAP object.
629 * sc_secsize is PAGE_SIZE'd
631 * vio->vn_size is in PAGE_SIZE'd chunks.
632 * sc_size must be in PAGE_SIZE'd chunks.
633 * Note the truncation.
636 vn
->sc_secsize
= PAGE_SIZE
;
637 vn
->sc_size
= vio
->vn_size
;
638 vn
->sc_object
= swap_pager_alloc(NULL
,
639 vn
->sc_secsize
* (off_t
)vio
->vn_size
,
641 IFOPT(vn
, VN_RESERVE
) {
642 if (swap_pager_reserve(vn
->sc_object
, 0, vn
->sc_size
) < 0) {
643 vm_pager_deallocate(vn
->sc_object
);
644 vn
->sc_object
= NULL
;
648 vn
->sc_flags
|= VNF_INITED
;
650 error
= vnsetcred(vn
, cred
);
653 * Set the disk info so that probing is triggered
655 bzero(&info
, sizeof(struct disk_info
));
656 info
.d_media_blksize
= vn
->sc_secsize
;
657 info
.d_media_blocks
= vn
->sc_size
;
659 * reserve mbr sector for backwards compatibility
660 * when no slices exist.
662 info
.d_dsflags
= DSO_COMPATMBR
| DSO_RAWPSIZE
;
663 info
.d_secpertrack
= 32;
664 info
.d_nheads
= 64 / (vn
->sc_secsize
/ DEV_BSIZE
);
665 info
.d_secpercyl
= info
.d_secpertrack
* info
.d_nheads
;
666 info
.d_ncylinders
= vn
->sc_size
/ info
.d_secpercyl
;
667 disk_setdiskinfo_sync(&vn
->sc_disk
, &info
);
669 error
= dev_dopen(dev
, flag
, S_IFCHR
, cred
, NULL
);
672 IFOPT(vn
, VN_FOLLOW
) {
673 kprintf("vnioctl: SET vp %p size %llx\n",
674 vn
->sc_vp
, (long long)vn
->sc_size
);
683 * Duplicate the current processes' credentials. Since we are called only
684 * as the result of a SET ioctl and only root can do that, any future access
685 * to this "disk" is essentially as root. Note that credentials may change
686 * if some other uid can write directly to the mapped file (NFS).
689 vnsetcred(struct vn_softc
*vn
, struct ucred
*cred
)
695 * Set credits in our softc
700 vn
->sc_cred
= crdup(cred
);
703 * Horrible kludge to establish credentials for NFS XXX.
710 tmpbuf
= kmalloc(vn
->sc_secsize
, M_TEMP
, M_WAITOK
);
711 bzero(&auio
, sizeof(auio
));
713 aiov
.iov_base
= tmpbuf
;
714 aiov
.iov_len
= vn
->sc_secsize
;
715 auio
.uio_iov
= &aiov
;
718 auio
.uio_rw
= UIO_READ
;
719 auio
.uio_segflg
= UIO_SYSSPACE
;
720 auio
.uio_resid
= aiov
.iov_len
;
721 vn_lock(vn
->sc_vp
, LK_EXCLUSIVE
| LK_RETRY
);
722 error
= VOP_READ(vn
->sc_vp
, &auio
, 0, vn
->sc_cred
);
723 vn_unlock(vn
->sc_vp
);
724 kfree(tmpbuf
, M_TEMP
);
730 vnclear(struct vn_softc
*vn
)
733 kprintf("vnclear(%p): vp=%p\n", vn
, vn
->sc_vp
);
734 vn
->sc_flags
&= ~VNF_INITED
;
735 if (vn
->sc_vp
!= NULL
) {
737 (vn
->sc_flags
& VNF_READONLY
) ? FREAD
: (FREAD
|FWRITE
),
741 vn
->sc_flags
&= ~VNF_READONLY
;
746 if (vn
->sc_object
!= NULL
) {
747 vm_pager_deallocate(vn
->sc_object
);
748 vn
->sc_object
= NULL
;
751 disk_unprobe(&vn
->sc_disk
);
759 * populate a struct vn_user for the VNIOCGET ioctl.
760 * interface conventions defined in sys/sys/vnioctl.h.
764 vnget(cdev_t dev
, struct vn_softc
*vn
, struct vn_user
*vnu
)
766 int error
, found
= 0;
767 char *freepath
, *fullpath
;
770 if (vnu
->vnu_unit
== -1) {
771 vnu
->vnu_unit
= dkunit(dev
);
773 else if (vnu
->vnu_unit
< 0)
776 SLIST_FOREACH(vn
, &vn_list
, sc_list
) {
778 if(vn
->sc_unit
!= vnu
->vnu_unit
)
783 if (vn
->sc_flags
& VNF_INITED
&& vn
->sc_vp
!= NULL
) {
785 /* note: u_cred checked in vnioctl above */
786 error
= VOP_GETATTR(vn
->sc_vp
, &vattr
);
788 kprintf("vnget: VOP_GETATTR for %p failed\n",
793 error
= vn_fullpath(curproc
, vn
->sc_vp
,
794 &fullpath
, &freepath
, 0);
797 kprintf("vnget: unable to resolve vp %p\n",
802 strlcpy(vnu
->vnu_file
, fullpath
,
803 sizeof(vnu
->vnu_file
));
804 kfree(freepath
, M_TEMP
);
805 vnu
->vnu_dev
= vattr
.va_fsid
;
806 vnu
->vnu_ino
= vattr
.va_fileid
;
809 else if (vn
->sc_flags
& VNF_INITED
&& vn
->sc_object
!= NULL
){
811 strlcpy(vnu
->vnu_file
, _VN_USER_SWAP
,
812 sizeof(vnu
->vnu_file
));
813 vnu
->vnu_size
= vn
->sc_size
;
814 vnu
->vnu_secsize
= vn
->sc_secsize
;
818 bzero(vnu
->vnu_file
, sizeof(vnu
->vnu_file
));
833 vnsize(struct dev_psize_args
*ap
)
835 cdev_t dev
= ap
->a_head
.a_dev
;
841 if ((vn
->sc_flags
& VNF_INITED
) == 0)
843 ap
->a_result
= (int64_t)vn
->sc_size
;
848 vn_create(int unit
, struct devfs_bitmap
*bitmap
, int clone
)
851 struct disk_info info
;
857 * For clone devices we need to return the top-level cdev,
858 * not the raw dev we'd normally work with.
860 dev
= disk_create_clone(unit
, &vn
->sc_disk
, &vn_ops
);
861 ret_dev
= vn
->sc_disk
.d_cdev
;
863 ret_dev
= dev
= disk_create(unit
, &vn
->sc_disk
, &vn_ops
);
867 bzero(&info
, sizeof(struct disk_info
));
868 info
.d_media_blksize
= 512;
869 info
.d_media_blocks
= 0;
870 info
.d_dsflags
= DSO_MBRQUIET
| DSO_RAWPSIZE
;
871 info
.d_secpertrack
= 32;
873 info
.d_secpercyl
= info
.d_secpertrack
* info
.d_nheads
;
874 info
.d_ncylinders
= 0;
875 disk_setdiskinfo_sync(&vn
->sc_disk
, &info
);
878 devfs_clone_bitmap_set(bitmap
, unit
);
884 vn_modevent(module_t mod
, int type
, void *data
)
887 static cdev_t dev
= NULL
;
892 dev
= make_autoclone_dev(&vn_ops
, &DEVFS_CLONE_BITMAP(vn
), vnclone
, UID_ROOT
,
893 GID_OPERATOR
, 0640, "vn");
895 for (i
= 0; i
< VN_PREALLOCATED_UNITS
; i
++) {
896 vn_create(i
, &DEVFS_CLONE_BITMAP(vn
), 0);
902 while ((vn
= SLIST_FIRST(&vn_list
)) != NULL
) {
904 * XXX: no idea if we can return EBUSY even in the
905 * shutdown case, so err on the side of caution
906 * and just rip stuff out on shutdown.
908 if (type
!= MOD_SHUTDOWN
) {
909 if (vn
->sc_flags
& VNF_OPENED
)
913 disk_destroy(&vn
->sc_disk
);
915 SLIST_REMOVE_HEAD(&vn_list
, sc_list
);
917 if (vn
->sc_flags
& VNF_INITED
)
922 destroy_autoclone_dev(dev
, &DEVFS_CLONE_BITMAP(vn
));
923 dev_ops_remove_all(&vn_ops
);
931 DEV_MODULE(vn
, vn_modevent
, 0);