Remove unused label.
[dragonfly.git] / sys / dev / disk / 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: src/sys/dev/vn/vn.c,v 1.105.2.4 2001/11/18 07:11:00 dillon Exp $
42  * $DragonFly: src/sys/dev/disk/vn/vn.c,v 1.22 2006/05/05 09:28:05 swildner Exp $
43  */
44
45 /*
46  * Vnode disk driver.
47  *
48  * Block/character interface to a vnode.  Allows one to treat a file
49  * as a disk (e.g. build a filesystem in it, mount it, etc.).
50  *
51  * NOTE 1: There is a security issue involved with this driver.
52  * Once mounted all access to the contents of the "mapped" file via
53  * the special file is controlled by the permissions on the special
54  * file, the protection of the mapped file is ignored (effectively,
55  * by using root credentials in all transactions).
56  *
57  * NOTE 2: Doesn't interact with leases, should it?
58  */
59
60 #include <sys/param.h>
61 #include <sys/systm.h>
62 #include <sys/kernel.h>
63 #include <sys/proc.h>
64 #include <sys/nlookup.h>
65 #include <sys/buf.h>
66 #include <sys/malloc.h>
67 #include <sys/mount.h>
68 #include <sys/vnode.h>
69 #include <sys/fcntl.h>
70 #include <sys/conf.h>
71 #include <sys/disklabel.h>
72 #include <sys/diskslice.h>
73 #include <sys/stat.h>
74 #include <sys/module.h>
75 #include <sys/vnioctl.h>
76
77 #include <vm/vm.h>
78 #include <vm/vm_object.h>
79 #include <vm/vm_page.h>
80 #include <vm/vm_pager.h>
81 #include <vm/vm_pageout.h>
82 #include <vm/swap_pager.h>
83 #include <vm/vm_extern.h>
84 #include <vm/vm_zone.h>
85
86 static  d_ioctl_t       vnioctl;
87 static  d_open_t        vnopen;
88 static  d_close_t       vnclose;
89 static  d_psize_t       vnsize;
90 static  d_strategy_t    vnstrategy;
91
92 #define CDEV_MAJOR 43
93
94 #define VN_BSIZE_BEST   8192
95
96 /*
97  * cdevsw
98  *      D_DISK          we want to look like a disk
99  *      D_CANFREE       We support BUF_CMD_FREEBLKS
100  */
101
102 static struct cdevsw vn_cdevsw = {
103         /* name */      "vn",
104         /* maj */       CDEV_MAJOR,
105         /* flags */     D_DISK|D_CANFREE,
106         /* port */      NULL,
107         /* clone */     NULL,
108
109         /* open */      vnopen,
110         /* close */     vnclose,
111         /* read */      physread,
112         /* write */     physwrite,
113         /* ioctl */     vnioctl,
114         /* poll */      nopoll,
115         /* mmap */      nommap,
116         /* strategy */  vnstrategy,
117         /* dump */      nodump,
118         /* psize */     vnsize
119 };
120
121 struct vn_softc {
122         int             sc_unit;
123         int             sc_flags;       /* flags                        */
124         int             sc_size;        /* size of vn, sc_secsize scale */
125         int             sc_secsize;     /* sector size                  */
126         struct diskslices *sc_slices;
127         struct vnode    *sc_vp;         /* vnode if not NULL            */
128         vm_object_t     sc_object;      /* backing object if not NULL   */
129         struct ucred    *sc_cred;       /* credentials                  */
130         int              sc_maxactive;  /* max # of active requests     */
131         struct buf       sc_tab;        /* transfer queue               */
132         u_long           sc_options;    /* options                      */
133         dev_t            sc_devlist;    /* devices that refer to this unit */
134         SLIST_ENTRY(vn_softc) sc_list;
135 };
136
137 static SLIST_HEAD(, vn_softc) vn_list;
138
139 /* sc_flags */
140 #define VNF_INITED      0x01
141 #define VNF_READONLY    0x02
142
143 static u_long   vn_options;
144
145 #define IFOPT(vn,opt) if (((vn)->sc_options|vn_options) & (opt))
146 #define TESTOPT(vn,opt) (((vn)->sc_options|vn_options) & (opt))
147
148 static int      vnsetcred (struct vn_softc *vn, struct ucred *cred);
149 static void     vnclear (struct vn_softc *vn);
150 static int      vn_modevent (module_t, int, void *);
151 static int      vniocattach_file (struct vn_softc *, struct vn_ioctl *, dev_t dev, int flag, struct thread *p);
152 static int      vniocattach_swap (struct vn_softc *, struct vn_ioctl *, dev_t dev, int flag, struct thread *p);
153
154 static  int
155 vnclose(dev_t dev, int flags, int mode, struct thread *td)
156 {
157         struct vn_softc *vn = dev->si_drv1;
158
159         IFOPT(vn, VN_LABELS)
160                 if (vn->sc_slices != NULL)
161                         dsclose(dev, mode, vn->sc_slices);
162         return (0);
163 }
164
165 /*
166  * Called only when si_drv1 is NULL.  Locate the associated vn node and
167  * attach the device to it.
168  */
169 static struct vn_softc *
170 vnfindvn(dev_t dev)
171 {
172         int unit;
173         struct vn_softc *vn;
174
175         unit = dkunit(dev);
176         SLIST_FOREACH(vn, &vn_list, sc_list) {
177                 if (vn->sc_unit == unit) {
178                         dev->si_drv1 = vn;
179                         dev->si_drv2 = vn->sc_devlist;
180                         vn->sc_devlist = dev;
181                         reference_dev(dev);
182                         break;
183                 }
184         }
185         if (vn == NULL) {
186                 vn = malloc(sizeof *vn, M_DEVBUF, M_WAITOK | M_ZERO);
187                 vn->sc_unit = unit;
188                 dev->si_drv1 = vn;
189                 vn->sc_devlist = make_dev(&vn_cdevsw, 0, UID_ROOT,
190                                         GID_OPERATOR, 0640, "vn%d", unit);
191                 if (vn->sc_devlist->si_drv1 == NULL) {
192                         reference_dev(vn->sc_devlist);
193                         vn->sc_devlist->si_drv1 = vn;
194                         vn->sc_devlist->si_drv2 = NULL;
195                 }
196                 if (vn->sc_devlist != dev) {
197                         dev->si_drv1 = vn;
198                         dev->si_drv2 = vn->sc_devlist;
199                         vn->sc_devlist = dev;
200                         reference_dev(dev);
201                 }
202                 SLIST_INSERT_HEAD(&vn_list, vn, sc_list);
203         }
204         return (vn);
205 }
206
207 static  int
208 vnopen(dev_t dev, int flags, int mode, struct thread *td)
209 {
210         struct vn_softc *vn;
211
212         /*
213          * Locate preexisting device
214          */
215
216         if ((vn = dev->si_drv1) == NULL)
217                 vn = vnfindvn(dev);
218
219         /*
220          * Update si_bsize fields for device.  This data will be overriden by
221          * the slice/parition code for vn accesses through partitions, and
222          * used directly if you open the 'whole disk' device.
223          *
224          * si_bsize_best must be reinitialized in case VN has been 
225          * reconfigured, plus make it at least VN_BSIZE_BEST for efficiency.
226          */
227         dev->si_bsize_phys = vn->sc_secsize;
228         dev->si_bsize_best = vn->sc_secsize;
229         if (dev->si_bsize_best < VN_BSIZE_BEST)
230                 dev->si_bsize_best = VN_BSIZE_BEST;
231
232         if ((flags & FWRITE) && (vn->sc_flags & VNF_READONLY))
233                 return (EACCES);
234
235         IFOPT(vn, VN_FOLLOW)
236                 printf("vnopen(%s, 0x%x, 0x%x, %p)\n",
237                     devtoname(dev), flags, mode, (void *)td);
238
239         /*
240          * Initialize label
241          */
242
243         IFOPT(vn, VN_LABELS) {
244                 if (vn->sc_flags & VNF_INITED) {
245                         struct disklabel label;
246
247                         /* Build label for whole disk. */
248                         bzero(&label, sizeof label);
249                         label.d_secsize = vn->sc_secsize;
250                         label.d_nsectors = 32;
251                         label.d_ntracks = 64 / (vn->sc_secsize / DEV_BSIZE);
252                         label.d_secpercyl = label.d_nsectors * label.d_ntracks;
253                         label.d_ncylinders = vn->sc_size / label.d_secpercyl;
254                         label.d_secperunit = vn->sc_size;
255                         label.d_partitions[RAW_PART].p_size = vn->sc_size;
256
257                         return (dsopen(dev, mode, 0, &vn->sc_slices, &label));
258                 }
259                 if (dkslice(dev) != WHOLE_DISK_SLICE ||
260                     dkpart(dev) != RAW_PART ||
261                     mode != S_IFCHR) {
262                         return (ENXIO);
263                 }
264         }
265         return(0);
266 }
267
268 /*
269  *      vnstrategy:
270  *
271  *      Run strategy routine for VN device.  We use VOP_READ/VOP_WRITE calls
272  *      for vnode-backed vn's, and the new vm_pager_strategy() call for
273  *      vm_object-backed vn's.
274  *
275  *      Currently B_ASYNC is only partially handled - for OBJT_SWAP I/O only.
276  */
277 static  void
278 vnstrategy(dev_t dev, struct bio *bio)
279 {
280         struct buf *bp;
281         struct bio *nbio;
282         int unit;
283         struct vn_softc *vn;
284         int error;
285
286         unit = dkunit(dev);
287         if ((vn = dev->si_drv1) == NULL)
288                 vn = vnfindvn(dev);
289
290         bp = bio->bio_buf;
291
292         IFOPT(vn, VN_DEBUG)
293                 printf("vnstrategy(%p): unit %d\n", bp, unit);
294
295         if ((vn->sc_flags & VNF_INITED) == 0) {
296                 bp->b_error = ENXIO;
297                 bp->b_flags |= B_ERROR;
298                 biodone(bio);
299                 return;
300         }
301
302         bp->b_resid = bp->b_bcount;
303
304         IFOPT(vn, VN_LABELS) {
305                 /*
306                  * The vnode device is using disk/slice label support.
307                  *
308                  * The dscheck() function is called for validating the
309                  * slices that exist ON the vnode device itself, and
310                  * translate the "slice-relative" block number, again.
311                  * dscheck() will call biodone() and return NULL if
312                  * we are at EOF or beyond the device size.
313                  */
314                 if (vn->sc_slices == NULL) {
315                         nbio = bio;
316                 } else if ((nbio = dscheck(dev, bio, vn->sc_slices)) == NULL) {
317                         goto done;
318                 }
319         } else {
320                 int pbn;        /* in sc_secsize chunks */
321                 long sz;        /* in sc_secsize chunks */
322
323                 /*
324                  * Check for required alignment.  Transfers must be a valid
325                  * multiple of the sector size.
326                  */
327                 if (bp->b_bcount % vn->sc_secsize != 0 ||
328                     bio->bio_offset % vn->sc_secsize != 0) {
329                         goto bad;
330                 }
331
332                 pbn = bio->bio_offset / vn->sc_secsize;
333                 sz = howmany(bp->b_bcount, vn->sc_secsize);
334
335                 /*
336                  * Check for an illegal pbn or EOF truncation
337                  */
338                 if (pbn < 0)
339                         goto bad;
340                 if (pbn + sz > vn->sc_size) {
341                         if (pbn > vn->sc_size || (bp->b_flags & B_BNOCLIP))
342                                 goto bad;
343                         if (pbn == vn->sc_size) {
344                                 bp->b_resid = bp->b_bcount;
345                                 bp->b_flags |= B_INVAL;
346                                 goto done;
347                         }
348                         bp->b_bcount = (vn->sc_size - pbn) * vn->sc_secsize;
349                 }
350                 nbio = push_bio(bio);
351                 nbio->bio_offset = pbn * vn->sc_secsize;
352         }
353
354         /*
355          * Use the translated nbio from this point on
356          */
357         if (vn->sc_vp && bp->b_cmd == BUF_CMD_FREEBLKS) {
358                 /*
359                  * Freeblks is not handled for vnode-backed elements yet.
360                  */
361                 bp->b_resid = 0;
362                 /* operation complete */
363         } else if (vn->sc_vp) {
364                 /*
365                  * VNODE I/O
366                  *
367                  * If an error occurs, we set B_ERROR but we do not set 
368                  * B_INVAL because (for a write anyway), the buffer is 
369                  * still valid.
370                  */
371                 struct uio auio;
372                 struct iovec aiov;
373
374                 bzero(&auio, sizeof(auio));
375
376                 aiov.iov_base = bp->b_data;
377                 aiov.iov_len = bp->b_bcount;
378                 auio.uio_iov = &aiov;
379                 auio.uio_iovcnt = 1;
380                 auio.uio_offset = nbio->bio_offset;
381                 auio.uio_segflg = UIO_SYSSPACE;
382                 if (bp->b_cmd == BUF_CMD_READ)
383                         auio.uio_rw = UIO_READ;
384                 else
385                         auio.uio_rw = UIO_WRITE;
386                 auio.uio_resid = bp->b_bcount;
387                 auio.uio_td = curthread;
388                 vn_lock(vn->sc_vp, LK_EXCLUSIVE | LK_RETRY, curthread);
389                 if (bp->b_cmd == BUF_CMD_READ)
390                         error = VOP_READ(vn->sc_vp, &auio, IO_DIRECT, vn->sc_cred);
391                 else
392                         error = VOP_WRITE(vn->sc_vp, &auio, IO_NOWDRAIN, vn->sc_cred);
393                 VOP_UNLOCK(vn->sc_vp, 0, curthread);
394                 bp->b_resid = auio.uio_resid;
395                 if (error) {
396                         bp->b_error = error;
397                         bp->b_flags |= B_ERROR;
398                 }
399                 /* operation complete */
400         } else if (vn->sc_object) {
401                 /*
402                  * OBJT_SWAP I/O (handles read, write, freebuf)
403                  *
404                  * We have nothing to do if freeing  blocks on a reserved
405                  * swap area, othrewise execute the op.
406                  */
407                 if (bp->b_cmd == BUF_CMD_FREEBLKS && TESTOPT(vn, VN_RESERVE)) {
408                         bp->b_resid = 0;
409                         /* operation complete */
410                 } else {
411                         vm_pager_strategy(vn->sc_object, nbio);
412                         return;
413                         /* NOT REACHED */
414                 }
415         } else {
416                 bp->b_resid = bp->b_bcount;
417                 bp->b_flags |= B_ERROR | B_INVAL;
418                 bp->b_error = EINVAL;
419                 /* operation complete */
420         }
421         biodone(nbio);
422         return;
423
424         /*
425          * Shortcuts / check failures on the original bio (not nbio).
426          */
427 bad:
428         bp->b_error = EINVAL;
429         bp->b_flags |= B_ERROR | B_INVAL;
430 done:
431         biodone(bio);
432 }
433
434 /* ARGSUSED */
435 static  int
436 vnioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct thread *td)
437 {
438         struct vn_softc *vn;
439         struct vn_ioctl *vio;
440         int error;
441         u_long *f;
442
443         vn = dev->si_drv1;
444         IFOPT(vn,VN_FOLLOW)
445                 printf("vnioctl(%s, 0x%lx, %p, 0x%x, %p): unit %d\n",
446                     devtoname(dev), cmd, (void *)data, flag, (void *)td,
447                     dkunit(dev));
448
449         switch (cmd) {
450         case VNIOCATTACH:
451         case VNIOCDETACH:
452         case VNIOCGSET:
453         case VNIOCGCLEAR:
454         case VNIOCUSET:
455         case VNIOCUCLEAR:
456                 goto vn_specific;
457         }
458
459         IFOPT(vn,VN_LABELS) {
460                 if (vn->sc_slices != NULL) {
461                         error = dsioctl(dev, cmd, data, flag, &vn->sc_slices);
462                         if (error != ENOIOCTL)
463                                 return (error);
464                 }
465                 if (dkslice(dev) != WHOLE_DISK_SLICE ||
466                     dkpart(dev) != RAW_PART)
467                         return (ENOTTY);
468         }
469
470     vn_specific:
471
472         error = suser(td);
473         if (error)
474                 return (error);
475
476         vio = (struct vn_ioctl *)data;
477         f = (u_long*)data;
478         switch (cmd) {
479
480         case VNIOCATTACH:
481                 if (vn->sc_flags & VNF_INITED)
482                         return(EBUSY);
483
484                 if (vio->vn_file == NULL)
485                         error = vniocattach_swap(vn, vio, dev, flag, td);
486                 else
487                         error = vniocattach_file(vn, vio, dev, flag, td);
488                 break;
489
490         case VNIOCDETACH:
491                 if ((vn->sc_flags & VNF_INITED) == 0)
492                         return(ENXIO);
493                 /*
494                  * XXX handle i/o in progress.  Return EBUSY, or wait, or
495                  * flush the i/o.
496                  * XXX handle multiple opens of the device.  Return EBUSY,
497                  * or revoke the fd's.
498                  * How are these problems handled for removable and failing
499                  * hardware devices? (Hint: They are not)
500                  */
501                 vnclear(vn);
502                 IFOPT(vn, VN_FOLLOW)
503                         printf("vnioctl: CLRed\n");
504                 break;
505
506         case VNIOCGSET:
507                 vn_options |= *f;
508                 *f = vn_options;
509                 break;
510
511         case VNIOCGCLEAR:
512                 vn_options &= ~(*f);
513                 *f = vn_options;
514                 break;
515
516         case VNIOCUSET:
517                 vn->sc_options |= *f;
518                 *f = vn->sc_options;
519                 break;
520
521         case VNIOCUCLEAR:
522                 vn->sc_options &= ~(*f);
523                 *f = vn->sc_options;
524                 break;
525
526         default:
527                 error = ENOTTY;
528                 break;
529         }
530         return(error);
531 }
532
533 /*
534  *      vniocattach_file:
535  *
536  *      Attach a file to a VN partition.  Return the size in the vn_size
537  *      field.
538  */
539
540 static int
541 vniocattach_file(struct vn_softc *vn, struct vn_ioctl *vio, dev_t dev,
542                  int flag, struct thread *td)
543 {
544         struct vattr vattr;
545         struct nlookupdata nd;
546         int error, flags;
547         struct vnode *vp;
548         struct proc *p = td->td_proc;
549
550         KKASSERT(p != NULL);
551
552         flags = FREAD|FWRITE;
553         error = nlookup_init(&nd, vio->vn_file, 
554                                 UIO_USERSPACE, NLC_FOLLOW|NLC_LOCKVP);
555         if (error)
556                 return (error);
557         if ((error = vn_open(&nd, NULL, flags, 0)) != 0) {
558                 if (error != EACCES && error != EPERM && error != EROFS)
559                         goto done;
560                 flags &= ~FWRITE;
561                 nlookup_done(&nd);
562                 error = nlookup_init(&nd, vio->vn_file, UIO_USERSPACE, NLC_FOLLOW|NLC_LOCKVP);
563                 if (error)
564                         return (error);
565                 if ((error = vn_open(&nd, NULL, flags, 0)) != 0)
566                         goto done;
567         }
568         vp = nd.nl_open_vp;
569         if (vp->v_type != VREG ||
570             (error = VOP_GETATTR(vp, &vattr, td))) {
571                 if (error == 0)
572                         error = EINVAL;
573                 goto done;
574         }
575         VOP_UNLOCK(vp, 0, td);
576         vn->sc_secsize = DEV_BSIZE;
577         vn->sc_vp = vp;
578         nd.nl_open_vp = NULL;
579
580         /*
581          * If the size is specified, override the file attributes.  Note that
582          * the vn_size argument is in PAGE_SIZE sized blocks.
583          */
584         if (vio->vn_size)
585                 vn->sc_size = (quad_t)vio->vn_size * PAGE_SIZE / vn->sc_secsize;
586         else
587                 vn->sc_size = vattr.va_size / vn->sc_secsize;
588         error = vnsetcred(vn, p->p_ucred);
589         if (error) {
590                 vn->sc_vp = NULL;
591                 vn_close(vp, flags, td);
592                 goto done;
593         }
594         vn->sc_flags |= VNF_INITED;
595         if (flags == FREAD)
596                 vn->sc_flags |= VNF_READONLY;
597         IFOPT(vn, VN_LABELS) {
598                 /*
599                  * Reopen so that `ds' knows which devices are open.
600                  * If this is the first VNIOCSET, then we've
601                  * guaranteed that the device is the cdev and that
602                  * no other slices or labels are open.  Otherwise,
603                  * we rely on VNIOCCLR not being abused.
604                  */
605                 error = vnopen(dev, flag, S_IFCHR, td);
606                 if (error)
607                         vnclear(vn);
608         }
609         IFOPT(vn, VN_FOLLOW)
610                 printf("vnioctl: SET vp %p size %x blks\n",
611                        vn->sc_vp, vn->sc_size);
612 done:
613         nlookup_done(&nd);
614         return(error);
615 }
616
617 /*
618  *      vniocattach_swap:
619  *
620  *      Attach swap backing store to a VN partition of the size specified
621  *      in vn_size.
622  */
623
624 static int
625 vniocattach_swap(struct vn_softc *vn, struct vn_ioctl *vio, dev_t dev,
626                  int flag, struct thread *td)
627 {
628         int error;
629         struct proc *p = td->td_proc;
630
631         KKASSERT(p != NULL);
632         /*
633          * Range check.  Disallow negative sizes or any size less then the
634          * size of a page.  Then round to a page.
635          */
636
637         if (vio->vn_size <= 0)
638                 return(EDOM);
639
640         /*
641          * Allocate an OBJT_SWAP object.
642          *
643          * sc_secsize is PAGE_SIZE'd
644          *
645          * vio->vn_size is in PAGE_SIZE'd chunks.
646          * sc_size must be in PAGE_SIZE'd chunks.  
647          * Note the truncation.
648          */
649
650         vn->sc_secsize = PAGE_SIZE;
651         vn->sc_size = vio->vn_size;
652         vn->sc_object = vm_pager_allocate(OBJT_SWAP, NULL,
653                                           vn->sc_secsize * (off_t)vio->vn_size,
654                                           VM_PROT_DEFAULT, 0);
655         IFOPT(vn, VN_RESERVE) {
656                 if (swap_pager_reserve(vn->sc_object, 0, vn->sc_size) < 0) {
657                         vm_pager_deallocate(vn->sc_object);
658                         vn->sc_object = NULL;
659                         return(EDOM);
660                 }
661         }
662         vn->sc_flags |= VNF_INITED;
663
664         error = vnsetcred(vn, p->p_ucred);
665         if (error == 0) {
666                 IFOPT(vn, VN_LABELS) {
667                         /*
668                          * Reopen so that `ds' knows which devices are open.
669                          * If this is the first VNIOCSET, then we've
670                          * guaranteed that the device is the cdev and that
671                          * no other slices or labels are open.  Otherwise,
672                          * we rely on VNIOCCLR not being abused.
673                          */
674                         error = vnopen(dev, flag, S_IFCHR, td);
675                 }
676         }
677         if (error == 0) {
678                 IFOPT(vn, VN_FOLLOW) {
679                         printf("vnioctl: SET vp %p size %x\n",
680                                vn->sc_vp, vn->sc_size);
681                 }
682         }
683         if (error)
684                 vnclear(vn);
685         return(error);
686 }
687
688 /*
689  * Duplicate the current processes' credentials.  Since we are called only
690  * as the result of a SET ioctl and only root can do that, any future access
691  * to this "disk" is essentially as root.  Note that credentials may change
692  * if some other uid can write directly to the mapped file (NFS).
693  */
694 int
695 vnsetcred(struct vn_softc *vn, struct ucred *cred)
696 {
697         char *tmpbuf;
698         int error = 0;
699
700         /*
701          * Set credits in our softc
702          */
703
704         if (vn->sc_cred)
705                 crfree(vn->sc_cred);
706         vn->sc_cred = crdup(cred);
707
708         /*
709          * Horrible kludge to establish credentials for NFS  XXX.
710          */
711
712         if (vn->sc_vp) {
713                 struct uio auio;
714                 struct iovec aiov;
715
716                 tmpbuf = malloc(vn->sc_secsize, M_TEMP, M_WAITOK);
717                 bzero(&auio, sizeof(auio));
718
719                 aiov.iov_base = tmpbuf;
720                 aiov.iov_len = vn->sc_secsize;
721                 auio.uio_iov = &aiov;
722                 auio.uio_iovcnt = 1;
723                 auio.uio_offset = 0;
724                 auio.uio_rw = UIO_READ;
725                 auio.uio_segflg = UIO_SYSSPACE;
726                 auio.uio_resid = aiov.iov_len;
727                 vn_lock(vn->sc_vp, LK_EXCLUSIVE | LK_RETRY, curthread);
728                 error = VOP_READ(vn->sc_vp, &auio, 0, vn->sc_cred);
729                 VOP_UNLOCK(vn->sc_vp, 0, curthread);
730                 free(tmpbuf, M_TEMP);
731         }
732         return (error);
733 }
734
735 void
736 vnclear(struct vn_softc *vn)
737 {
738         struct thread *td = curthread;          /* XXX */
739
740         IFOPT(vn, VN_FOLLOW)
741                 printf("vnclear(%p): vp=%p\n", vn, vn->sc_vp);
742         if (vn->sc_slices != NULL)
743                 dsgone(&vn->sc_slices);
744         vn->sc_flags &= ~VNF_INITED;
745         if (vn->sc_vp != NULL) {
746                 vn_close(vn->sc_vp, vn->sc_flags & VNF_READONLY ?
747                     FREAD : (FREAD|FWRITE), td);
748                 vn->sc_vp = NULL;
749         }
750         vn->sc_flags &= ~VNF_READONLY;
751         if (vn->sc_cred) {
752                 crfree(vn->sc_cred);
753                 vn->sc_cred = NULL;
754         }
755         if (vn->sc_object != NULL) {
756                 vm_pager_deallocate(vn->sc_object);
757                 vn->sc_object = NULL;
758         }
759         vn->sc_size = 0;
760 }
761
762 static  int
763 vnsize(dev_t dev)
764 {
765         struct vn_softc *vn;
766
767         vn = dev->si_drv1;
768         if (!vn)
769                 return(-1);
770         if ((vn->sc_flags & VNF_INITED) == 0)
771                 return(-1);
772
773         return(vn->sc_size);
774 }
775
776 static int 
777 vn_modevent(module_t mod, int type, void *data)
778 {
779         struct vn_softc *vn;
780         dev_t dev;
781
782         switch (type) {
783         case MOD_LOAD:
784                 cdevsw_add(&vn_cdevsw, 0, 0);
785                 break;
786         case MOD_UNLOAD:
787                 /* fall through */
788         case MOD_SHUTDOWN:
789                 while ((vn = SLIST_FIRST(&vn_list)) != NULL) {
790                         SLIST_REMOVE_HEAD(&vn_list, sc_list);
791                         if (vn->sc_flags & VNF_INITED)
792                                 vnclear(vn);
793                         /* Cleanup all dev_t's that refer to this unit */
794                         while ((dev = vn->sc_devlist) != NULL) {
795                                 vn->sc_devlist = dev->si_drv2;
796                                 dev->si_drv1 = dev->si_drv2 = NULL;
797                                 destroy_dev(dev);
798                         }
799                         free(vn, M_DEVBUF);
800                 }
801                 cdevsw_remove(&vn_cdevsw, 0, 0);
802                 break;
803         default:
804                 break;
805         }
806         return 0;
807 }
808
809 DEV_MODULE(vn, vn_modevent, 0);