Replace the the buffer cache's B_READ, B_WRITE, B_FORMAT, and B_FREEBUF
[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.19 2006/04/30 17:22:16 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
278 static  void
279 vnstrategy(dev_t dev, struct bio *bio)
280 {
281         struct buf *bp;
282         struct bio *nbio;
283         int unit;
284         struct vn_softc *vn;
285         int error;
286
287         unit = dkunit(dev);
288         if ((vn = dev->si_drv1) == NULL)
289                 vn = vnfindvn(dev);
290
291         bp = bio->bio_buf;
292
293         IFOPT(vn, VN_DEBUG)
294                 printf("vnstrategy(%p): unit %d\n", bp, unit);
295
296         if ((vn->sc_flags & VNF_INITED) == 0) {
297                 bp->b_error = ENXIO;
298                 bp->b_flags |= B_ERROR;
299                 biodone(bio);
300                 return;
301         }
302
303         bp->b_resid = bp->b_bcount;
304
305         IFOPT(vn, VN_LABELS) {
306                 /*
307                  * The vnode device is using disk/slice label support.
308                  *
309                  * The dscheck() function is called for validating the
310                  * slices that exist ON the vnode device itself, and
311                  * translate the "slice-relative" block number, again.
312                  */
313                 if (vn->sc_slices == NULL) {
314                         nbio = bio;
315                 } else if ((nbio = dscheck(dev, bio, vn->sc_slices)) == NULL) {
316                         bp->b_flags |= B_INVAL;
317                         biodone(bio);
318                         return;
319                 }
320         } else {
321                 int pbn;        /* in sc_secsize chunks */
322                 long sz;        /* in sc_secsize chunks */
323
324                 /*
325                  * Check for required alignment.  Transfers must be a valid
326                  * multiple of the sector size.
327                  */
328                 if (bp->b_bcount % vn->sc_secsize != 0 ||
329                     bio->bio_offset % vn->sc_secsize != 0) {
330                         bp->b_error = EINVAL;
331                         bp->b_flags |= B_ERROR | B_INVAL;
332                         biodone(bio);
333                         return;
334                 }
335
336                 pbn = bio->bio_offset / vn->sc_secsize;
337                 sz = howmany(bp->b_bcount, vn->sc_secsize);
338
339                 /*
340                  * If out of bounds return an error.  If at the EOF point,
341                  * simply read or write less.
342                  */
343                 if (pbn < 0 || pbn >= vn->sc_size) {
344                         if (pbn != vn->sc_size) {
345                                 bp->b_error = EINVAL;
346                                 bp->b_flags |= B_ERROR | B_INVAL;
347                         }
348                         biodone(bio);
349                         return;
350                 }
351
352                 /*
353                  * If the request crosses EOF, truncate the request.
354                  */
355                 if (pbn + sz > vn->sc_size) {
356                         bp->b_bcount = (vn->sc_size - pbn) * vn->sc_secsize;
357                         bp->b_resid = bp->b_bcount;
358                 }
359                 nbio = push_bio(bio);
360                 nbio->bio_offset = pbn * vn->sc_secsize;
361         }
362
363         /*
364          * Use the translated nbio from this point on
365          */
366         if (vn->sc_vp && bp->b_cmd == BUF_CMD_FREEBLKS) {
367                 /*
368                  * Freeblks is not handled for vnode-backed elements yet.
369                  */
370                 biodone(nbio);
371         } else if (vn->sc_vp) {
372                 /*
373                  * VNODE I/O
374                  *
375                  * If an error occurs, we set B_ERROR but we do not set 
376                  * B_INVAL because (for a write anyway), the buffer is 
377                  * still valid.
378                  */
379                 struct uio auio;
380                 struct iovec aiov;
381
382                 bzero(&auio, sizeof(auio));
383
384                 aiov.iov_base = bp->b_data;
385                 aiov.iov_len = bp->b_bcount;
386                 auio.uio_iov = &aiov;
387                 auio.uio_iovcnt = 1;
388                 auio.uio_offset = nbio->bio_offset;
389                 auio.uio_segflg = UIO_SYSSPACE;
390                 if (bp->b_cmd == BUF_CMD_READ)
391                         auio.uio_rw = UIO_READ;
392                 else
393                         auio.uio_rw = UIO_WRITE;
394                 auio.uio_resid = bp->b_bcount;
395                 auio.uio_td = curthread;
396                 vn_lock(vn->sc_vp, LK_EXCLUSIVE | LK_RETRY, curthread);
397                 if (bp->b_cmd == BUF_CMD_READ)
398                         error = VOP_READ(vn->sc_vp, &auio, IO_DIRECT, vn->sc_cred);
399                 else
400                         error = VOP_WRITE(vn->sc_vp, &auio, IO_NOWDRAIN, vn->sc_cred);
401                 VOP_UNLOCK(vn->sc_vp, 0, curthread);
402                 bp->b_resid = auio.uio_resid;
403
404                 if (error) {
405                         bp->b_error = error;
406                         bp->b_flags |= B_ERROR;
407                 }
408                 biodone(nbio);
409         } else if (vn->sc_object) {
410                 /*
411                  * OBJT_SWAP I/O
412                  *
413                  * ( handles read, write, freebuf )
414                  *
415                  * Note: freeblks is not supported with pre-reserved swap.
416                  */
417                 KASSERT((bp->b_bufsize & (vn->sc_secsize - 1)) == 0,
418                     ("vnstrategy: buffer %p too small for physio", bp));
419
420                 if (bp->b_cmd == BUF_CMD_FREEBLKS && TESTOPT(vn, VN_RESERVE)) {
421                         biodone(nbio);
422                 } else {
423                         vm_pager_strategy(vn->sc_object, nbio);
424                 }
425         } else {
426                 bp->b_flags |= B_ERROR;
427                 bp->b_error = EINVAL;
428                 biodone(nbio);
429         }
430 }
431
432 /* ARGSUSED */
433 static  int
434 vnioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct thread *td)
435 {
436         struct vn_softc *vn;
437         struct vn_ioctl *vio;
438         int error;
439         u_long *f;
440
441         vn = dev->si_drv1;
442         IFOPT(vn,VN_FOLLOW)
443                 printf("vnioctl(%s, 0x%lx, %p, 0x%x, %p): unit %d\n",
444                     devtoname(dev), cmd, (void *)data, flag, (void *)td,
445                     dkunit(dev));
446
447         switch (cmd) {
448         case VNIOCATTACH:
449         case VNIOCDETACH:
450         case VNIOCGSET:
451         case VNIOCGCLEAR:
452         case VNIOCUSET:
453         case VNIOCUCLEAR:
454                 goto vn_specific;
455         }
456
457         IFOPT(vn,VN_LABELS) {
458                 if (vn->sc_slices != NULL) {
459                         error = dsioctl(dev, cmd, data, flag, &vn->sc_slices);
460                         if (error != ENOIOCTL)
461                                 return (error);
462                 }
463                 if (dkslice(dev) != WHOLE_DISK_SLICE ||
464                     dkpart(dev) != RAW_PART)
465                         return (ENOTTY);
466         }
467
468     vn_specific:
469
470         error = suser(td);
471         if (error)
472                 return (error);
473
474         vio = (struct vn_ioctl *)data;
475         f = (u_long*)data;
476         switch (cmd) {
477
478         case VNIOCATTACH:
479                 if (vn->sc_flags & VNF_INITED)
480                         return(EBUSY);
481
482                 if (vio->vn_file == NULL)
483                         error = vniocattach_swap(vn, vio, dev, flag, td);
484                 else
485                         error = vniocattach_file(vn, vio, dev, flag, td);
486                 break;
487
488         case VNIOCDETACH:
489                 if ((vn->sc_flags & VNF_INITED) == 0)
490                         return(ENXIO);
491                 /*
492                  * XXX handle i/o in progress.  Return EBUSY, or wait, or
493                  * flush the i/o.
494                  * XXX handle multiple opens of the device.  Return EBUSY,
495                  * or revoke the fd's.
496                  * How are these problems handled for removable and failing
497                  * hardware devices? (Hint: They are not)
498                  */
499                 vnclear(vn);
500                 IFOPT(vn, VN_FOLLOW)
501                         printf("vnioctl: CLRed\n");
502                 break;
503
504         case VNIOCGSET:
505                 vn_options |= *f;
506                 *f = vn_options;
507                 break;
508
509         case VNIOCGCLEAR:
510                 vn_options &= ~(*f);
511                 *f = vn_options;
512                 break;
513
514         case VNIOCUSET:
515                 vn->sc_options |= *f;
516                 *f = vn->sc_options;
517                 break;
518
519         case VNIOCUCLEAR:
520                 vn->sc_options &= ~(*f);
521                 *f = vn->sc_options;
522                 break;
523
524         default:
525                 error = ENOTTY;
526                 break;
527         }
528         return(error);
529 }
530
531 /*
532  *      vniocattach_file:
533  *
534  *      Attach a file to a VN partition.  Return the size in the vn_size
535  *      field.
536  */
537
538 static int
539 vniocattach_file(struct vn_softc *vn, struct vn_ioctl *vio, dev_t dev,
540                  int flag, struct thread *td)
541 {
542         struct vattr vattr;
543         struct nlookupdata nd;
544         int error, flags;
545         struct vnode *vp;
546         struct proc *p = td->td_proc;
547
548         KKASSERT(p != NULL);
549
550         flags = FREAD|FWRITE;
551         error = nlookup_init(&nd, vio->vn_file, 
552                                 UIO_USERSPACE, NLC_FOLLOW|NLC_LOCKVP);
553         if (error)
554                 return (error);
555         if ((error = vn_open(&nd, NULL, flags, 0)) != 0) {
556                 if (error != EACCES && error != EPERM && error != EROFS)
557                         goto done;
558                 flags &= ~FWRITE;
559                 nlookup_done(&nd);
560                 error = nlookup_init(&nd, vio->vn_file, UIO_USERSPACE, NLC_FOLLOW|NLC_LOCKVP);
561                 if (error)
562                         return (error);
563                 if ((error = vn_open(&nd, NULL, flags, 0)) != 0)
564                         goto done;
565         }
566         vp = nd.nl_open_vp;
567         if (vp->v_type != VREG ||
568             (error = VOP_GETATTR(vp, &vattr, td))) {
569                 if (error == 0)
570                         error = EINVAL;
571                 goto done;
572         }
573         VOP_UNLOCK(vp, 0, td);
574         vn->sc_secsize = DEV_BSIZE;
575         vn->sc_vp = vp;
576         nd.nl_open_vp = NULL;
577
578         /*
579          * If the size is specified, override the file attributes.  Note that
580          * the vn_size argument is in PAGE_SIZE sized blocks.
581          */
582         if (vio->vn_size)
583                 vn->sc_size = (quad_t)vio->vn_size * PAGE_SIZE / vn->sc_secsize;
584         else
585                 vn->sc_size = vattr.va_size / vn->sc_secsize;
586         error = vnsetcred(vn, p->p_ucred);
587         if (error) {
588                 vn->sc_vp = NULL;
589                 vn_close(vp, flags, td);
590                 goto done;
591         }
592         vn->sc_flags |= VNF_INITED;
593         if (flags == FREAD)
594                 vn->sc_flags |= VNF_READONLY;
595         IFOPT(vn, VN_LABELS) {
596                 /*
597                  * Reopen so that `ds' knows which devices are open.
598                  * If this is the first VNIOCSET, then we've
599                  * guaranteed that the device is the cdev and that
600                  * no other slices or labels are open.  Otherwise,
601                  * we rely on VNIOCCLR not being abused.
602                  */
603                 error = vnopen(dev, flag, S_IFCHR, td);
604                 if (error)
605                         vnclear(vn);
606         }
607         IFOPT(vn, VN_FOLLOW)
608                 printf("vnioctl: SET vp %p size %x blks\n",
609                        vn->sc_vp, vn->sc_size);
610 done:
611         nlookup_done(&nd);
612         return(error);
613 }
614
615 /*
616  *      vniocattach_swap:
617  *
618  *      Attach swap backing store to a VN partition of the size specified
619  *      in vn_size.
620  */
621
622 static int
623 vniocattach_swap(struct vn_softc *vn, struct vn_ioctl *vio, dev_t dev,
624                  int flag, struct thread *td)
625 {
626         int error;
627         struct proc *p = td->td_proc;
628
629         KKASSERT(p != NULL);
630         /*
631          * Range check.  Disallow negative sizes or any size less then the
632          * size of a page.  Then round to a page.
633          */
634
635         if (vio->vn_size <= 0)
636                 return(EDOM);
637
638         /*
639          * Allocate an OBJT_SWAP object.
640          *
641          * sc_secsize is PAGE_SIZE'd
642          *
643          * vio->vn_size is in PAGE_SIZE'd chunks.
644          * sc_size must be in PAGE_SIZE'd chunks.  
645          * Note the truncation.
646          */
647
648         vn->sc_secsize = PAGE_SIZE;
649         vn->sc_size = vio->vn_size;
650         vn->sc_object = vm_pager_allocate(OBJT_SWAP, NULL,
651                                           vn->sc_secsize * (off_t)vio->vn_size,
652                                           VM_PROT_DEFAULT, 0);
653         IFOPT(vn, VN_RESERVE) {
654                 if (swap_pager_reserve(vn->sc_object, 0, vn->sc_size) < 0) {
655                         vm_pager_deallocate(vn->sc_object);
656                         vn->sc_object = NULL;
657                         return(EDOM);
658                 }
659         }
660         vn->sc_flags |= VNF_INITED;
661
662         error = vnsetcred(vn, p->p_ucred);
663         if (error == 0) {
664                 IFOPT(vn, VN_LABELS) {
665                         /*
666                          * Reopen so that `ds' knows which devices are open.
667                          * If this is the first VNIOCSET, then we've
668                          * guaranteed that the device is the cdev and that
669                          * no other slices or labels are open.  Otherwise,
670                          * we rely on VNIOCCLR not being abused.
671                          */
672                         error = vnopen(dev, flag, S_IFCHR, td);
673                 }
674         }
675         if (error == 0) {
676                 IFOPT(vn, VN_FOLLOW) {
677                         printf("vnioctl: SET vp %p size %x\n",
678                                vn->sc_vp, vn->sc_size);
679                 }
680         }
681         if (error)
682                 vnclear(vn);
683         return(error);
684 }
685
686 /*
687  * Duplicate the current processes' credentials.  Since we are called only
688  * as the result of a SET ioctl and only root can do that, any future access
689  * to this "disk" is essentially as root.  Note that credentials may change
690  * if some other uid can write directly to the mapped file (NFS).
691  */
692 int
693 vnsetcred(struct vn_softc *vn, struct ucred *cred)
694 {
695         char *tmpbuf;
696         int error = 0;
697
698         /*
699          * Set credits in our softc
700          */
701
702         if (vn->sc_cred)
703                 crfree(vn->sc_cred);
704         vn->sc_cred = crdup(cred);
705
706         /*
707          * Horrible kludge to establish credentials for NFS  XXX.
708          */
709
710         if (vn->sc_vp) {
711                 struct uio auio;
712                 struct iovec aiov;
713
714                 tmpbuf = malloc(vn->sc_secsize, M_TEMP, M_WAITOK);
715                 bzero(&auio, sizeof(auio));
716
717                 aiov.iov_base = tmpbuf;
718                 aiov.iov_len = vn->sc_secsize;
719                 auio.uio_iov = &aiov;
720                 auio.uio_iovcnt = 1;
721                 auio.uio_offset = 0;
722                 auio.uio_rw = UIO_READ;
723                 auio.uio_segflg = UIO_SYSSPACE;
724                 auio.uio_resid = aiov.iov_len;
725                 vn_lock(vn->sc_vp, LK_EXCLUSIVE | LK_RETRY, curthread);
726                 error = VOP_READ(vn->sc_vp, &auio, 0, vn->sc_cred);
727                 VOP_UNLOCK(vn->sc_vp, 0, curthread);
728                 free(tmpbuf, M_TEMP);
729         }
730         return (error);
731 }
732
733 void
734 vnclear(struct vn_softc *vn)
735 {
736         struct thread *td = curthread;          /* XXX */
737
738         IFOPT(vn, VN_FOLLOW)
739                 printf("vnclear(%p): vp=%p\n", vn, vn->sc_vp);
740         if (vn->sc_slices != NULL)
741                 dsgone(&vn->sc_slices);
742         vn->sc_flags &= ~VNF_INITED;
743         if (vn->sc_vp != NULL) {
744                 vn_close(vn->sc_vp, vn->sc_flags & VNF_READONLY ?
745                     FREAD : (FREAD|FWRITE), td);
746                 vn->sc_vp = NULL;
747         }
748         vn->sc_flags &= ~VNF_READONLY;
749         if (vn->sc_cred) {
750                 crfree(vn->sc_cred);
751                 vn->sc_cred = NULL;
752         }
753         if (vn->sc_object != NULL) {
754                 vm_pager_deallocate(vn->sc_object);
755                 vn->sc_object = NULL;
756         }
757         vn->sc_size = 0;
758 }
759
760 static  int
761 vnsize(dev_t dev)
762 {
763         struct vn_softc *vn;
764
765         vn = dev->si_drv1;
766         if (!vn)
767                 return(-1);
768         if ((vn->sc_flags & VNF_INITED) == 0)
769                 return(-1);
770
771         return(vn->sc_size);
772 }
773
774 static int 
775 vn_modevent(module_t mod, int type, void *data)
776 {
777         struct vn_softc *vn;
778         dev_t dev;
779
780         switch (type) {
781         case MOD_LOAD:
782                 cdevsw_add(&vn_cdevsw, 0, 0);
783                 break;
784         case MOD_UNLOAD:
785                 /* fall through */
786         case MOD_SHUTDOWN:
787                 while ((vn = SLIST_FIRST(&vn_list)) != NULL) {
788                         SLIST_REMOVE_HEAD(&vn_list, sc_list);
789                         if (vn->sc_flags & VNF_INITED)
790                                 vnclear(vn);
791                         /* Cleanup all dev_t's that refer to this unit */
792                         while ((dev = vn->sc_devlist) != NULL) {
793                                 vn->sc_devlist = dev->si_drv2;
794                                 dev->si_drv1 = dev->si_drv2 = NULL;
795                                 destroy_dev(dev);
796                         }
797                         free(vn, M_DEVBUF);
798                 }
799                 cdevsw_remove(&vn_cdevsw, 0, 0);
800                 break;
801         default:
802                 break;
803         }
804         return 0;
805 }
806
807 DEV_MODULE(vn, vn_modevent, 0);