FreeBSD/Linux Kernel Cross Reference
sys/dev/vn/vn.c
1 /*
2 * Copyright (c) 1988 University of Utah.
3 * Copyright (c) 1990, 1993
4 * The Regents of the University of California. All rights reserved.
5 *
6 * This code is derived from software contributed to Berkeley by
7 * the Systems Programming Group of the University of Utah Computer
8 * Science Department.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
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.
25 *
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
36 * SUCH DAMAGE.
37 *
38 * from: Utah Hdr: vn.c 1.13 94/04/02
39 *
40 * from: @(#)vn.c 8.6 (Berkeley) 4/1/94
41 * $FreeBSD$
42 */
43
44 /*
45 * Vnode disk driver.
46 *
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.).
49 *
50 * NOTE 1: This uses the VOP_BMAP/VOP_STRATEGY interface to the vnode
51 * instead of a simple VOP_RDWR. We do this to avoid distorting the
52 * local buffer cache.
53 *
54 * NOTE 2: There is a security issue involved with this driver.
55 * Once mounted all access to the contents of the "mapped" file via
56 * the special file is controlled by the permissions on the special
57 * file, the protection of the mapped file is ignored (effectively,
58 * by using root credentials in all transactions).
59 *
60 * NOTE 3: Doesn't interact with leases, should it?
61 */
62 #include "vn.h"
63 #if NVN > 0
64
65 /* default is to have 8 VN's */
66 #if NVN < 8
67 #undef NVN
68 #define NVN 8
69 #endif
70
71 #include "opt_devfs.h"
72
73 #include <sys/param.h>
74 #include <sys/systm.h>
75 #include <sys/kernel.h>
76 #include <sys/namei.h>
77 #include <sys/proc.h>
78 #include <sys/buf.h>
79 #include <sys/malloc.h>
80 #include <sys/mount.h>
81 #include <sys/vnode.h>
82 #include <sys/fcntl.h>
83 #include <sys/disklabel.h>
84 #include <sys/diskslice.h>
85 #include <sys/stat.h>
86 #include <sys/conf.h>
87 #ifdef DEVFS
88 #include <sys/devfsext.h>
89 #endif /*DEVFS*/
90 #include <miscfs/specfs/specdev.h>
91 #include <sys/vnioctl.h>
92
93 static d_ioctl_t vnioctl;
94 static d_open_t vnopen;
95 static d_read_t vnread;
96 static d_write_t vnwrite;
97 static d_close_t vnclose;
98 static d_dump_t vndump;
99 static d_psize_t vnsize;
100 static d_strategy_t vnstrategy;
101
102 #define CDEV_MAJOR 43
103 #define BDEV_MAJOR 15
104
105
106 static struct cdevsw vn_cdevsw = {
107 vnopen, vnclose, vnread, vnwrite,
108 vnioctl, nostop, nullreset, nodevtotty,
109 seltrue, nommap, vnstrategy, "vn",
110 NULL, -1, vndump, vnsize,
111 D_DISK|D_NOCLUSTERRW, 0, -1 };
112
113
114 #define vnunit(dev) dkunit(dev)
115
116 #define getvnbuf() \
117 ((struct buf *)malloc(sizeof(struct buf), M_DEVBUF, M_WAITOK))
118
119 #define putvnbuf(bp) \
120 free((caddr_t)(bp), M_DEVBUF)
121
122 struct vn_softc {
123 int sc_flags; /* flags */
124 size_t sc_size; /* size of vn */
125 struct diskslices *sc_slices;
126 struct vnode *sc_vp; /* vnode */
127 struct ucred *sc_cred; /* credentials */
128 int sc_maxactive; /* max # of active requests */
129 struct buf sc_tab; /* transfer queue */
130 u_long sc_options; /* options */
131 #ifdef DEVFS
132 void *r_devfs_token;
133 void *devfs_token;
134 #endif
135 };
136
137 /* sc_flags */
138 #define VNF_INITED 0x01
139
140 static struct vn_softc *vn_softc[NVN];
141 static u_long vn_options;
142
143 #define IFOPT(vn,opt) if (((vn)->sc_options|vn_options) & (opt))
144
145 static void vniodone (struct buf *bp);
146 static int vnsetcred (struct vn_softc *vn, struct ucred *cred);
147 static void vnshutdown (int, void *);
148 static void vnclear (struct vn_softc *vn);
149
150 static int
151 vnclose(dev_t dev, int flags, int mode, struct proc *p)
152 {
153 struct vn_softc *vn = vn_softc[vnunit(dev)];
154
155 IFOPT(vn, VN_LABELS)
156 if (vn->sc_slices != NULL)
157 dsclose(dev, mode, vn->sc_slices);
158 return (0);
159 }
160
161 static int
162 vnopen(dev_t dev, int flags, int mode, struct proc *p)
163 {
164 int unit = vnunit(dev);
165 struct vn_softc *vn;
166
167 if (unit >= NVN) {
168 if (vn_options & VN_FOLLOW)
169 printf("vnopen(0x%lx, 0x%x, 0x%x, %p)\n",
170 (u_long)dev, flags, mode, (void *)p);
171 return(ENOENT);
172 }
173
174 vn = vn_softc[unit];
175 if (!vn) {
176 vn = malloc(sizeof *vn, M_DEVBUF, M_WAITOK);
177 if (!vn)
178 return (ENOMEM);
179 bzero(vn, sizeof *vn);
180 vn_softc[unit] = vn;
181 }
182
183 IFOPT(vn, VN_FOLLOW)
184 printf("vnopen(0x%lx, 0x%x, 0x%x, %p)\n",
185 (u_long)dev, flags, mode, (void *)p);
186
187 IFOPT(vn, VN_LABELS) {
188 if (vn->sc_flags & VNF_INITED) {
189 struct disklabel label;
190
191 /* Build label for whole disk. */
192 bzero(&label, sizeof label);
193 label.d_secsize = DEV_BSIZE;
194 label.d_nsectors = 32;
195 label.d_ntracks = 64;
196 label.d_ncylinders = vn->sc_size / (32 * 64);
197 label.d_secpercyl = 32 * 64;
198 label.d_secperunit =
199 label.d_partitions[RAW_PART].p_size =
200 vn->sc_size;
201
202 return (dsopen("vn", dev, mode, 0, &vn->sc_slices,
203 &label, vnstrategy, (ds_setgeom_t *)NULL,
204 &vn_cdevsw));
205 }
206 if (dkslice(dev) != WHOLE_DISK_SLICE ||
207 dkpart(dev) != RAW_PART ||
208 mode != S_IFCHR)
209 return (ENXIO);
210 }
211 return(0);
212 }
213
214 static int
215 vnread(dev_t dev, struct uio *uio, int ioflag)
216 {
217 return (physio(vnstrategy, NULL, dev, 1, minphys, uio));
218 }
219
220 static int
221 vnwrite(dev_t dev, struct uio *uio, int ioflag)
222 {
223 return (physio(vnstrategy, NULL, dev, 0, minphys, uio));
224 }
225
226 /*
227 * this code does I/O calls through the appropriate VOP entry point...
228 * unless a swap_pager I/O request is being done. This strategy (-))
229 * allows for coherency with mmap except in the case of paging. This
230 * is necessary, because the VOP calls use lots of memory (and actually
231 * are not extremely efficient -- but we want to keep semantics correct),
232 * and the pageout daemon gets really unhappy (and so does the rest of the
233 * system) when it runs out of memory.
234 */
235 static void
236 vnstrategy(struct buf *bp)
237 {
238 int unit = vnunit(bp->b_dev);
239 register struct vn_softc *vn = vn_softc[unit];
240 register daddr_t bn;
241 int error;
242 int isvplocked = 0;
243 long sz;
244 struct uio auio;
245 struct iovec aiov;
246
247 IFOPT(vn, VN_DEBUG)
248 printf("vnstrategy(%p): unit %d\n", bp, unit);
249
250 if ((vn->sc_flags & VNF_INITED) == 0) {
251 bp->b_error = ENXIO;
252 bp->b_flags |= B_ERROR;
253 biodone(bp);
254 return;
255 }
256 IFOPT(vn, VN_LABELS) {
257 bp->b_resid = bp->b_bcount;/* XXX best place to set this? */
258 if (vn->sc_slices != NULL && dscheck(bp, vn->sc_slices) <= 0) {
259 biodone(bp);
260 return;
261 }
262 bn = bp->b_pblkno;
263 bp->b_resid = bp->b_bcount;/* XXX best place to set this? */
264 } else {
265 bn = bp->b_blkno;
266 sz = howmany(bp->b_bcount, DEV_BSIZE);
267 bp->b_resid = bp->b_bcount;
268 if (bn < 0 || bn + sz > vn->sc_size) {
269 if (bn != vn->sc_size) {
270 bp->b_error = EINVAL;
271 bp->b_flags |= B_ERROR;
272 }
273 biodone(bp);
274 return;
275 }
276 }
277
278 if( (bp->b_flags & B_PAGING) == 0) {
279 aiov.iov_base = bp->b_data;
280 aiov.iov_len = bp->b_bcount;
281 auio.uio_iov = &aiov;
282 auio.uio_iovcnt = 1;
283 auio.uio_offset = dbtob(bn);
284 auio.uio_segflg = UIO_SYSSPACE;
285 if( bp->b_flags & B_READ)
286 auio.uio_rw = UIO_READ;
287 else
288 auio.uio_rw = UIO_WRITE;
289 auio.uio_resid = bp->b_bcount;
290 auio.uio_procp = curproc;
291 if (!VOP_ISLOCKED(vn->sc_vp)) {
292 isvplocked = 1;
293 vn_lock(vn->sc_vp, LK_EXCLUSIVE | LK_RETRY, curproc);
294 }
295 if( bp->b_flags & B_READ)
296 error = VOP_READ(vn->sc_vp, &auio, 0, vn->sc_cred);
297 else
298 error = VOP_WRITE(vn->sc_vp, &auio, 0, vn->sc_cred);
299 if (isvplocked) {
300 VOP_UNLOCK(vn->sc_vp, 0, curproc);
301 isvplocked = 0;
302 }
303 bp->b_resid = auio.uio_resid;
304
305 if( error )
306 bp->b_flags |= B_ERROR;
307 biodone(bp);
308 } else {
309 long bsize, resid;
310 off_t byten;
311 int flags;
312 caddr_t addr;
313 struct buf *nbp;
314
315 nbp = getvnbuf();
316 bzero(nbp, sizeof(struct buf));
317 LIST_INIT(&nbp->b_dep);
318 byten = dbtob(bn);
319 bsize = vn->sc_vp->v_mount->mnt_stat.f_iosize;
320 addr = bp->b_data;
321 flags = bp->b_flags | B_CALL;
322 for (resid = bp->b_resid; resid; ) {
323 struct vnode *vp;
324 daddr_t nbn;
325 int off, s, nra;
326
327 nra = 0;
328 if (!VOP_ISLOCKED(vn->sc_vp)) {
329 isvplocked = 1;
330 vn_lock(vn->sc_vp, LK_EXCLUSIVE | LK_RETRY, curproc);
331 }
332 error = VOP_BMAP(vn->sc_vp, (daddr_t)(byten / bsize),
333 &vp, &nbn, &nra, NULL);
334 if (isvplocked) {
335 VOP_UNLOCK(vn->sc_vp, 0, curproc);
336 isvplocked = 0;
337 }
338 if (error == 0 && nbn == -1)
339 error = EIO;
340
341 IFOPT(vn, VN_DONTCLUSTER)
342 nra = 0;
343
344 off = byten % bsize;
345 if (off)
346 sz = bsize - off;
347 else
348 sz = (1 + nra) * bsize;
349 if (resid < sz)
350 sz = resid;
351
352 if (error) {
353 bp->b_resid -= (resid - sz);
354 bp->b_flags |= B_ERROR;
355 biodone(bp);
356 putvnbuf(nbp);
357 return;
358 }
359
360 IFOPT(vn,VN_IO)
361 printf(
362 /* XXX no %qx in kernel. Synthesize it. */
363 "vnstrategy: vp %p/%p bn 0x%lx%08lx/0x%lx sz 0x%lx\n",
364 (void *)vn->sc_vp, (void *)vp,
365 (u_long)(byten >> 32), (u_long)byten,
366 (u_long)nbn, sz);
367
368 nbp->b_flags = flags;
369 nbp->b_bcount = sz;
370 nbp->b_bufsize = sz;
371 nbp->b_error = 0;
372 if (vp->v_type == VBLK || vp->v_type == VCHR)
373 nbp->b_dev = vp->v_rdev;
374 else
375 nbp->b_dev = NODEV;
376 nbp->b_data = addr;
377 nbp->b_blkno = nbn + btodb(off);
378 nbp->b_offset = dbtob(nbn) + off;
379 nbp->b_proc = bp->b_proc;
380 nbp->b_iodone = vniodone;
381 nbp->b_vp = vp;
382 nbp->b_rcred = vn->sc_cred; /* XXX crdup? */
383 nbp->b_wcred = vn->sc_cred; /* XXX crdup? */
384 nbp->b_dirtyoff = bp->b_dirtyoff;
385 nbp->b_dirtyend = bp->b_dirtyend;
386 nbp->b_validoff = bp->b_validoff;
387 nbp->b_validend = bp->b_validend;
388
389 if ((nbp->b_flags & B_READ) == 0)
390 nbp->b_vp->v_numoutput++;
391
392 VOP_STRATEGY(vp, nbp);
393
394 s = splbio();
395 while ((nbp->b_flags & B_DONE) == 0) {
396 nbp->b_flags |= B_WANTED;
397 tsleep(nbp, PRIBIO, "vnwait", 0);
398 }
399 splx(s);
400
401 if( nbp->b_flags & B_ERROR) {
402 bp->b_flags |= B_ERROR;
403 bp->b_resid -= (resid - sz);
404 biodone(bp);
405 putvnbuf(nbp);
406 return;
407 }
408
409 byten += sz;
410 addr += sz;
411 resid -= sz;
412 }
413 bp->b_resid = resid;
414 biodone(bp);
415 putvnbuf(nbp);
416 }
417 }
418
419
420 void
421 vniodone( struct buf *bp) {
422 bp->b_flags |= B_DONE;
423 wakeup((caddr_t) bp);
424 }
425
426 /* ARGSUSED */
427 static int
428 vnioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
429 {
430 struct vn_softc *vn = vn_softc[vnunit(dev)];
431 struct vn_ioctl *vio;
432 struct vattr vattr;
433 struct nameidata nd;
434 int error;
435 u_long *f;
436
437 IFOPT(vn,VN_FOLLOW)
438 printf("vnioctl(0x%lx, 0x%lx, %p, 0x%x, %p): unit %d\n",
439 (u_long)dev, cmd, (void *)data, flag, (void *)p,
440 vnunit(dev));
441
442 switch (cmd) {
443 case VNIOCATTACH:
444 case VNIOCDETACH:
445 case VNIOCGSET:
446 case VNIOCGCLEAR:
447 case VNIOCUSET:
448 case VNIOCUCLEAR:
449 goto vn_specific;
450 }
451
452 IFOPT(vn,VN_LABELS) {
453 if (vn->sc_slices != NULL) {
454 error = dsioctl("vn", dev, cmd, data, flag,
455 &vn->sc_slices, vnstrategy,
456 (ds_setgeom_t *)NULL);
457 if (error != ENOIOCTL)
458 return (error);
459 }
460 if (dkslice(dev) != WHOLE_DISK_SLICE ||
461 dkpart(dev) != RAW_PART)
462 return (ENOTTY);
463 }
464
465 vn_specific:
466
467 error = suser(p->p_ucred, &p->p_acflag);
468 if (error)
469 return (error);
470
471 vio = (struct vn_ioctl *)data;
472 f = (u_long*)data;
473 switch (cmd) {
474
475 case VNIOCATTACH:
476 if (vn->sc_flags & VNF_INITED)
477 return(EBUSY);
478 /*
479 * Always open for read and write.
480 * This is probably bogus, but it lets vn_open()
481 * weed out directories, sockets, etc. so we don't
482 * have to worry about them.
483 */
484 NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, vio->vn_file, p);
485 error = vn_open(&nd, FREAD|FWRITE, 0);
486 if (error)
487 return(error);
488 error = VOP_GETATTR(nd.ni_vp, &vattr, p->p_ucred, p);
489 if (error) {
490 VOP_UNLOCK(nd.ni_vp, 0, p);
491 (void) vn_close(nd.ni_vp, FREAD|FWRITE, p->p_ucred, p);
492 return(error);
493 }
494 VOP_UNLOCK(nd.ni_vp, 0, p);
495 vn->sc_vp = nd.ni_vp;
496 vn->sc_size = btodb(vattr.va_size); /* note truncation */
497 error = vnsetcred(vn, p->p_ucred);
498 if (error) {
499 (void) vn_close(nd.ni_vp, FREAD|FWRITE, p->p_ucred, p);
500 return(error);
501 }
502 vio->vn_size = dbtob(vn->sc_size);
503 vn->sc_flags |= VNF_INITED;
504 IFOPT(vn, VN_LABELS) {
505 /*
506 * Reopen so that `ds' knows which devices are open.
507 * If this is the first VNIOCSET, then we've
508 * guaranteed that the device is the cdev and that
509 * no other slices or labels are open. Otherwise,
510 * we rely on VNIOCCLR not being abused.
511 */
512 error = vnopen(dev, flag, S_IFCHR, p);
513 if (error)
514 vnclear(vn);
515 }
516 IFOPT(vn, VN_FOLLOW)
517 printf("vnioctl: SET vp %p size %x\n",
518 vn->sc_vp, vn->sc_size);
519 break;
520
521 case VNIOCDETACH:
522 if ((vn->sc_flags & VNF_INITED) == 0)
523 return(ENXIO);
524 /*
525 * XXX handle i/o in progress. Return EBUSY, or wait, or
526 * flush the i/o.
527 * XXX handle multiple opens of the device. Return EBUSY,
528 * or revoke the fd's.
529 * How are these problems handled for removable and failing
530 * hardware devices?
531 */
532 vnclear(vn);
533 IFOPT(vn, VN_FOLLOW)
534 printf("vnioctl: CLRed\n");
535 break;
536
537 case VNIOCGSET:
538 vn_options |= *f;
539 *f = vn_options;
540 break;
541
542 case VNIOCGCLEAR:
543 vn_options &= ~(*f);
544 *f = vn_options;
545 break;
546
547 case VNIOCUSET:
548 vn->sc_options |= *f;
549 *f = vn->sc_options;
550 break;
551
552 case VNIOCUCLEAR:
553 vn->sc_options &= ~(*f);
554 *f = vn->sc_options;
555 break;
556
557 default:
558 return (ENOTTY);
559 }
560 return(0);
561 }
562
563 /*
564 * Duplicate the current processes' credentials. Since we are called only
565 * as the result of a SET ioctl and only root can do that, any future access
566 * to this "disk" is essentially as root. Note that credentials may change
567 * if some other uid can write directly to the mapped file (NFS).
568 */
569 int
570 vnsetcred(struct vn_softc *vn, struct ucred *cred)
571 {
572 struct uio auio;
573 struct iovec aiov;
574 char *tmpbuf;
575 int error;
576
577 vn->sc_cred = crdup(cred);
578 tmpbuf = malloc(DEV_BSIZE, M_TEMP, M_WAITOK);
579
580 /* XXX: Horrible kludge to establish credentials for NFS */
581 aiov.iov_base = tmpbuf;
582 aiov.iov_len = min(DEV_BSIZE, dbtob(vn->sc_size));
583 auio.uio_iov = &aiov;
584 auio.uio_iovcnt = 1;
585 auio.uio_offset = 0;
586 auio.uio_rw = UIO_READ;
587 auio.uio_segflg = UIO_SYSSPACE;
588 auio.uio_resid = aiov.iov_len;
589 vn_lock(vn->sc_vp, LK_EXCLUSIVE | LK_RETRY, curproc);
590 error = VOP_READ(vn->sc_vp, &auio, 0, vn->sc_cred);
591 VOP_UNLOCK(vn->sc_vp, 0, curproc);
592
593 free(tmpbuf, M_TEMP);
594 return (error);
595 }
596
597 void
598 vnshutdown(int howto, void *ignored)
599 {
600 int i;
601
602 for (i = 0; i < NVN; i++)
603 if (vn_softc[i] && vn_softc[i]->sc_flags & VNF_INITED)
604 vnclear(vn_softc[i]);
605 }
606
607 void
608 vnclear(struct vn_softc *vn)
609 {
610 register struct vnode *vp = vn->sc_vp;
611 struct proc *p = curproc; /* XXX */
612
613 IFOPT(vn, VN_FOLLOW)
614 printf("vnclear(%p): vp=%p\n", vn, vp);
615 if (vn->sc_slices != NULL)
616 dsgone(&vn->sc_slices);
617 vn->sc_flags &= ~VNF_INITED;
618 if (vp == (struct vnode *)0)
619 panic("vnclear: null vp");
620 (void) vn_close(vp, FREAD|FWRITE, vn->sc_cred, p);
621 crfree(vn->sc_cred);
622 vn->sc_vp = (struct vnode *)0;
623 vn->sc_cred = (struct ucred *)0;
624 vn->sc_size = 0;
625 }
626
627 static int
628 vnsize(dev_t dev)
629 {
630 int unit = vnunit(dev);
631
632 if (unit >= NVN || (!vn_softc[unit]) ||
633 (vn_softc[unit]->sc_flags & VNF_INITED) == 0)
634 return(-1);
635 return(vn_softc[unit]->sc_size);
636 }
637
638 static int
639 vndump(dev_t dev)
640 {
641 return (ENODEV);
642 }
643
644 static vn_devsw_installed = 0;
645
646 static void
647 vn_drvinit(void *unused)
648 {
649 #ifdef DEVFS
650 int unit;
651 #endif
652 if(!vn_devsw_installed ) {
653 if (at_shutdown(&vnshutdown, NULL, SHUTDOWN_POST_SYNC)) {
654 printf("vn: could not install shutdown hook\n");
655 return;
656 }
657 cdevsw_add_generic(BDEV_MAJOR, CDEV_MAJOR, &vn_cdevsw);
658 vn_devsw_installed = 1;
659 }
660 #ifdef DEVFS
661 for (unit = 0; unit < NVN; unit++) {
662 struct vn_softc *vn;
663
664 vn = malloc(sizeof *vn, M_DEVBUF, M_NOWAIT);
665 if (!vn)
666 return;
667 bzero(vn, sizeof *vn);
668 vn_softc[unit] = vn;
669 vn->r_devfs_token = devfs_add_devswf(&vn_cdevsw,
670 dkmakeminor(unit, 0, 0),
671 DV_CHR, UID_ROOT,
672 GID_OPERATOR, 0640,
673 "rvn%d", unit);
674 vn->devfs_token = devfs_add_devswf(&vn_cdevsw,
675 dkmakeminor(unit, 0, 0),
676 DV_BLK, UID_ROOT,
677 GID_OPERATOR, 0640,
678 "vn%d", unit);
679 }
680 #endif
681 }
682
683 SYSINIT(vndev, SI_SUB_DRIVERS, SI_ORDER_ANY, vn_drvinit, NULL)
684
685 #endif
Cache object: f9a178ec4910d01ac91e9e1b225b6a94
|