d8a11fee81029db97cddf2df307dfdc0c12074c8
[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.21 2006/05/04 18:32:20 dillon 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 error:
430         bp->b_flags |= B_ERROR | B_INVAL;
431 done:
432         biodone(bio);
433 }
434
435 /* ARGSUSED */
436 static  int
437 vnioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct thread *td)
438 {
439         struct vn_softc *vn;
440         struct vn_ioctl *vio;
441         int error;
442         u_long *f;
443
444         vn = dev->si_drv1;
445         IFOPT(vn,VN_FOLLOW)
446                 printf("vnioctl(%s, 0x%lx, %p, 0x%x, %p): unit %d\n",
447                     devtoname(dev), cmd, (void *)data, flag, (void *)td,
448                     dkunit(dev));
449
450         switch (cmd) {
451         case VNIOCATTACH:
452         case VNIOCDETACH:
453         case VNIOCGSET:
454         case VNIOCGCLEAR:
455         case VNIOCUSET:
456         case VNIOCUCLEAR:
457                 goto vn_specific;
458         }
459
460         IFOPT(vn,VN_LABELS) {
461                 if (vn->sc_slices != NULL) {
462                         error = dsioctl(dev, cmd, data, flag, &vn->sc_slices);
463                         if (error != ENOIOCTL)
464                                 return (error);
465                 }
466                 if (dkslice(dev) != WHOLE_DISK_SLICE ||
467                     dkpart(dev) != RAW_PART)
468                         return (ENOTTY);
469         }
470
471     vn_specific:
472
473         error = suser(td);
474         if (error)
475                 return (error);
476
477         vio = (struct vn_ioctl *)data;
478         f = (u_long*)data;
479         switch (cmd) {
480
481         case VNIOCATTACH:
482                 if (vn->sc_flags & VNF_INITED)
483                         return(EBUSY);
484
485                 if (vio->vn_file == NULL)
486                         error = vniocattach_swap(vn, vio, dev, flag, td);
487                 else
488                         error = vniocattach_file(vn, vio, dev, flag, td);
489                 break;
490
491         case VNIOCDETACH:
492                 if ((vn->sc_flags & VNF_INITED) == 0)
493                         return(ENXIO);
494                 /*
495                  * XXX handle i/o in progress.  Return EBUSY, or wait, or
496                  * flush the i/o.
497                  * XXX handle multiple opens of the device.  Return EBUSY,
498                  * or revoke the fd's.
499                  * How are these problems handled for removable and failing
500                  * hardware devices? (Hint: They are not)
501                  */
502                 vnclear(vn);
503                 IFOPT(vn, VN_FOLLOW)
504                         printf("vnioctl: CLRed\n");
505                 break;
506
507         case VNIOCGSET:
508                 vn_options |= *f;
509                 *f = vn_options;
510                 break;
511
512         case VNIOCGCLEAR:
513                 vn_options &= ~(*f);
514                 *f = vn_options;
515                 break;
516
517         case VNIOCUSET:
518                 vn->sc_options |= *f;
519                 *f = vn->sc_options;
520                 break;
521
522         case VNIOCUCLEAR:
523                 vn->sc_options &= ~(*f);
524                 *f = vn->sc_options;
525                 break;
526
527         default:
528                 error = ENOTTY;
529                 break;
530         }
531         return(error);
532 }
533
534 /*
535  *      vniocattach_file:
536  *
537  *      Attach a file to a VN partition.  Return the size in the vn_size
538  *      field.
539  */
540
541 static int
542 vniocattach_file(struct vn_softc *vn, struct vn_ioctl *vio, dev_t dev,
543                  int flag, struct thread *td)
544 {
545         struct vattr vattr;
546         struct nlookupdata nd;
547         int error, flags;
548         struct vnode *vp;
549         struct proc *p = td->td_proc;
550
551         KKASSERT(p != NULL);
552
553         flags = FREAD|FWRITE;
554         error = nlookup_init(&nd, vio->vn_file, 
555                                 UIO_USERSPACE, NLC_FOLLOW|NLC_LOCKVP);
556         if (error)
557                 return (error);
558         if ((error = vn_open(&nd, NULL, flags, 0)) != 0) {
559                 if (error != EACCES && error != EPERM && error != EROFS)
560                         goto done;
561                 flags &= ~FWRITE;
562                 nlookup_done(&nd);
563                 error = nlookup_init(&nd, vio->vn_file, UIO_USERSPACE, NLC_FOLLOW|NLC_LOCKVP);
564                 if (error)
565                         return (error);
566                 if ((error = vn_open(&nd, NULL, flags, 0)) != 0)
567                         goto done;
568         }
569         vp = nd.nl_open_vp;
570         if (vp->v_type != VREG ||
571             (error = VOP_GETATTR(vp, &vattr, td))) {
572                 if (error == 0)
573                         error = EINVAL;
574                 goto done;
575         }
576         VOP_UNLOCK(vp, 0, td);
577         vn->sc_secsize = DEV_BSIZE;
578         vn->sc_vp = vp;
579         nd.nl_open_vp = NULL;
580
581         /*
582          * If the size is specified, override the file attributes.  Note that
583          * the vn_size argument is in PAGE_SIZE sized blocks.
584          */
585         if (vio->vn_size)
586                 vn->sc_size = (quad_t)vio->vn_size * PAGE_SIZE / vn->sc_secsize;
587         else
588                 vn->sc_size = vattr.va_size / vn->sc_secsize;
589         error = vnsetcred(vn, p->p_ucred);
590         if (error) {
591                 vn->sc_vp = NULL;
592                 vn_close(vp, flags, td);
593                 goto done;
594         }
595         vn->sc_flags |= VNF_INITED;
596         if (flags == FREAD)
597                 vn->sc_flags |= VNF_READONLY;
598         IFOPT(vn, VN_LABELS) {
599                 /*
600                  * Reopen so that `ds' knows which devices are open.
601                  * If this is the first VNIOCSET, then we've
602                  * guaranteed that the device is the cdev and that
603                  * no other slices or labels are open.  Otherwise,
604                  * we rely on VNIOCCLR not being abused.
605                  */
606                 error = vnopen(dev, flag, S_IFCHR, td);
607                 if (error)
608                         vnclear(vn);
609         }
610         IFOPT(vn, VN_FOLLOW)
611                 printf("vnioctl: SET vp %p size %x blks\n",
612                        vn->sc_vp, vn->sc_size);
613 done:
614         nlookup_done(&nd);
615         return(error);
616 }
617
618 /*
619  *      vniocattach_swap:
620  *
621  *      Attach swap backing store to a VN partition of the size specified
622  *      in vn_size.
623  */
624
625 static int
626 vniocattach_swap(struct vn_softc *vn, struct vn_ioctl *vio, dev_t dev,
627                  int flag, struct thread *td)
628 {
629         int error;
630         struct proc *p = td->td_proc;
631
632         KKASSERT(p != NULL);
633         /*
634          * Range check.  Disallow negative sizes or any size less then the
635          * size of a page.  Then round to a page.
636          */
637
638         if (vio->vn_size <= 0)
639                 return(EDOM);
640
641         /*
642          * Allocate an OBJT_SWAP object.
643          *
644          * sc_secsize is PAGE_SIZE'd
645          *
646          * vio->vn_size is in PAGE_SIZE'd chunks.
647          * sc_size must be in PAGE_SIZE'd chunks.  
648          * Note the truncation.
649          */
650
651         vn->sc_secsize = PAGE_SIZE;
652         vn->sc_size = vio->vn_size;
653         vn->sc_object = vm_pager_allocate(OBJT_SWAP, NULL,
654                                           vn->sc_secsize * (off_t)vio->vn_size,
655                                           VM_PROT_DEFAULT, 0);
656         IFOPT(vn, VN_RESERVE) {
657                 if (swap_pager_reserve(vn->sc_object, 0, vn->sc_size) < 0) {
658                         vm_pager_deallocate(vn->sc_object);
659                         vn->sc_object = NULL;
660                         return(EDOM);
661                 }
662         }
663         vn->sc_flags |= VNF_INITED;
664
665         error = vnsetcred(vn, p->p_ucred);
666         if (error == 0) {
667                 IFOPT(vn, VN_LABELS) {
668                         /*
669                          * Reopen so that `ds' knows which devices are open.
670                          * If this is the first VNIOCSET, then we've
671                          * guaranteed that the device is the cdev and that
672                          * no other slices or labels are open.  Otherwise,
673                          * we rely on VNIOCCLR not being abused.
674                          */
675                         error = vnopen(dev, flag, S_IFCHR, td);
676                 }
677         }
678         if (error == 0) {
679                 IFOPT(vn, VN_FOLLOW) {
680                         printf("vnioctl: SET vp %p size %x\n",
681                                vn->sc_vp, vn->sc_size);
682                 }
683         }
684         if (error)
685                 vnclear(vn);
686         return(error);
687 }
688
689 /*
690  * Duplicate the current processes' credentials.  Since we are called only
691  * as the result of a SET ioctl and only root can do that, any future access
692  * to this "disk" is essentially as root.  Note that credentials may change
693  * if some other uid can write directly to the mapped file (NFS).
694  */
695 int
696 vnsetcred(struct vn_softc *vn, struct ucred *cred)
697 {
698         char *tmpbuf;
699         int error = 0;
700
701         /*
702          * Set credits in our softc
703          */
704
705         if (vn->sc_cred)
706                 crfree(vn->sc_cred);
707         vn->sc_cred = crdup(cred);
708
709         /*
710          * Horrible kludge to establish credentials for NFS  XXX.
711          */
712
713         if (vn->sc_vp) {
714                 struct uio auio;
715                 struct iovec aiov;
716
717                 tmpbuf = malloc(vn->sc_secsize, M_TEMP, M_WAITOK);
718                 bzero(&auio, sizeof(auio));
719
720                 aiov.iov_base = tmpbuf;
721                 aiov.iov_len = vn->sc_secsize;
722                 auio.uio_iov = &aiov;
723                 auio.uio_iovcnt = 1;
724                 auio.uio_offset = 0;
725                 auio.uio_rw = UIO_READ;
726                 auio.uio_segflg = UIO_SYSSPACE;
727                 auio.uio_resid = aiov.iov_len;
728                 vn_lock(vn->sc_vp, LK_EXCLUSIVE | LK_RETRY, curthread);
729                 error = VOP_READ(vn->sc_vp, &auio, 0, vn->sc_cred);
730                 VOP_UNLOCK(vn->sc_vp, 0, curthread);
731                 free(tmpbuf, M_TEMP);
732         }
733         return (error);
734 }
735
736 void
737 vnclear(struct vn_softc *vn)
738 {
739         struct thread *td = curthread;          /* XXX */
740
741         IFOPT(vn, VN_FOLLOW)
742                 printf("vnclear(%p): vp=%p\n", vn, vn->sc_vp);
743         if (vn->sc_slices != NULL)
744                 dsgone(&vn->sc_slices);
745         vn->sc_flags &= ~VNF_INITED;
746         if (vn->sc_vp != NULL) {
747                 vn_close(vn->sc_vp, vn->sc_flags & VNF_READONLY ?
748                     FREAD : (FREAD|FWRITE), td);
749                 vn->sc_vp = NULL;
750         }
751         vn->sc_flags &= ~VNF_READONLY;
752         if (vn->sc_cred) {
753                 crfree(vn->sc_cred);
754                 vn->sc_cred = NULL;
755         }
756         if (vn->sc_object != NULL) {
757                 vm_pager_deallocate(vn->sc_object);
758                 vn->sc_object = NULL;
759         }
760         vn->sc_size = 0;
761 }
762
763 static  int
764 vnsize(dev_t dev)
765 {
766         struct vn_softc *vn;
767
768         vn = dev->si_drv1;
769         if (!vn)
770                 return(-1);
771         if ((vn->sc_flags & VNF_INITED) == 0)
772                 return(-1);
773
774         return(vn->sc_size);
775 }
776
777 static int 
778 vn_modevent(module_t mod, int type, void *data)
779 {
780         struct vn_softc *vn;
781         dev_t dev;
782
783         switch (type) {
784         case MOD_LOAD:
785                 cdevsw_add(&vn_cdevsw, 0, 0);
786                 break;
787         case MOD_UNLOAD:
788                 /* fall through */
789         case MOD_SHUTDOWN:
790                 while ((vn = SLIST_FIRST(&vn_list)) != NULL) {
791                         SLIST_REMOVE_HEAD(&vn_list, sc_list);
792                         if (vn->sc_flags & VNF_INITED)
793                                 vnclear(vn);
794                         /* Cleanup all dev_t's that refer to this unit */
795                         while ((dev = vn->sc_devlist) != NULL) {
796                                 vn->sc_devlist = dev->si_drv2;
797                                 dev->si_drv1 = dev->si_drv2 = NULL;
798                                 destroy_dev(dev);
799                         }
800                         free(vn, M_DEVBUF);
801                 }
802                 cdevsw_remove(&vn_cdevsw, 0, 0);
803                 break;
804         default:
805                 break;
806         }
807         return 0;
808 }
809
810 DEV_MODULE(vn, vn_modevent, 0);