]> git.saurik.com Git - apple/xnu.git/blame - bsd/dev/vn/vn.c
xnu-517.tar.gz
[apple/xnu.git] / bsd / dev / vn / vn.c
CommitLineData
9bccf70c
A
1
2/*
3 * Copyright (c) 1988 University of Utah.
4 * Copyright (c) 1990, 1993
5 * The Regents of the University of California. All rights reserved.
6 *
7 * This code is derived from software contributed to Berkeley by
8 * the Systems Programming Group of the University of Utah Computer
9 * Science Department.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. All advertising materials mentioning features or use of this software
20 * must display the following acknowledgement:
21 * This product includes software developed by the University of
22 * California, Berkeley and its contributors.
23 * 4. Neither the name of the University nor the names of its contributors
24 * may be used to endorse or promote products derived from this software
25 * without specific prior written permission.
26 *
27 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37 * SUCH DAMAGE.
38 *
39 * from: Utah Hdr: vn.c 1.13 94/04/02
40 *
41 * from: @(#)vn.c 8.6 (Berkeley) 4/1/94
42 * $FreeBSD: src/sys/dev/vn/vn.c,v 1.105.2.4 2001/11/18 07:11:00 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: This uses the VOP_BMAP/VOP_STRATEGY interface to the vnode
52 * instead of a simple VOP_RDWR. We do this to avoid distorting the
53 * local buffer cache.
54 *
55 * NOTE 2: There is a security issue involved with this driver.
56 * Once mounted all access to the contents of the "mapped" file via
57 * the special file is controlled by the permissions on the special
58 * file, the protection of the mapped file is ignored (effectively,
59 * by using root credentials in all transactions).
60 *
61 * NOTE 3: Doesn't interact with leases, should it?
62 */
63
64#include "vndevice.h"
65
66#if NVNDEVICE > 0
67
68#include <sys/param.h>
69#include <sys/systm.h>
70#include <sys/kernel.h>
71#include <sys/mount.h>
72#include <sys/namei.h>
73#include <sys/proc.h>
74#include <sys/buf.h>
75#include <sys/malloc.h>
9bccf70c
A
76#include <sys/vnode.h>
77#include <sys/fcntl.h>
78#include <sys/conf.h>
55e303ae 79#include <sys/disk.h>
9bccf70c
A
80#include <sys/stat.h>
81#include <sys/conf.h>
82
83#include <sys/vnioctl.h>
84
85#include <sys/vm.h>
86
87#include <vm/vm_pager.h>
88#include <vm/vm_pageout.h>
89#include <mach/memory_object_types.h>
90
91#include <miscfs/devfs/devfs.h>
92
55e303ae
A
93extern void
94vfs_io_maxsegsize(struct vnode *vp,
95 int flags, /* B_READ or B_WRITE */
96 int *maxsegsize);
97
98extern void
99vfs_io_attributes(struct vnode *vp,
100 int flags, /* B_READ or B_WRITE */
101 int *iosize,
102 int *vectors);
103
9bccf70c
A
104#include "shadow.h"
105
106static ioctl_fcn_t vnioctl_chr;
107static ioctl_fcn_t vnioctl_blk;
108static open_close_fcn_t vnopen;
109static open_close_fcn_t vnclose;
110static psize_fcn_t vnsize;
111static strategy_fcn_t vnstrategy;
112static read_write_fcn_t vnread;
113static read_write_fcn_t vnwrite;
114
115static int vndevice_bdev_major;
116static int vndevice_cdev_major;
117
118/*
119 * cdevsw
120 * D_DISK we want to look like a disk
121 * D_CANFREE We support B_FREEBUF
122 */
123
124static struct bdevsw vn_bdevsw = {
125 /* open */ vnopen,
126 /* close */ vnclose,
127 /* strategy */ vnstrategy,
128 /* ioctl */ vnioctl_blk,
129 /* dump */ eno_dump,
130 /* psize */ vnsize,
131 /* flags */ D_DISK,
132};
133
134static struct cdevsw vn_cdevsw = {
135 /* open */ vnopen,
136 /* close */ vnclose,
137 /* read */ vnread,
138 /* write */ vnwrite,
139 /* ioctl */ vnioctl_chr,
140 /* stop */ eno_stop,
141 /* reset */ eno_reset,
142 /* ttys */ 0,
143 /* select */ eno_select,
144 /* mmap */ eno_mmap,
145 /* strategy */ eno_strat,
146 /* getc */ eno_getc,
147 /* putc */ eno_putc,
148 /* flags */ D_DISK,
149};
150
151struct vn_softc {
152 u_int64_t sc_fsize; /* file size in bytes */
153 u_int64_t sc_size; /* size of vn, sc_secsize scale */
154 int sc_flags; /* flags */
155 int sc_secsize; /* sector size */
156 struct vnode *sc_vp; /* vnode if not NULL */
157 int sc_open_flags;
158 struct vnode *sc_shadow_vp; /* shadow vnode if not NULL */
159 shadow_map_t * sc_shadow_map; /* shadow map if not NULL */
160 struct ucred *sc_cred; /* credentials */
161 u_long sc_options; /* options */
162 void * sc_bdev;
163 void * sc_cdev;
164} vn_table[NVNDEVICE];
165
166#define ROOT_IMAGE_UNIT 0
167
168/* sc_flags */
169#define VNF_INITED 0x01
170#define VNF_READONLY 0x02
171
172static u_long vn_options;
173
174#define IFOPT(vn,opt) if (((vn)->sc_options|vn_options) & (opt))
175#define TESTOPT(vn,opt) (((vn)->sc_options|vn_options) & (opt))
176
177static int vnsetcred (struct vn_softc *vn, struct proc *p);
178static void vnclear (struct vn_softc *vn);
179
180static int
181vniocattach_file(struct vn_softc *vn,
182 struct vn_ioctl *vio,
183 dev_t dev,
184 int in_kernel,
185 struct proc *p);
186static int
187vniocattach_shadow(struct vn_softc * vn,
188 struct vn_ioctl *vio,
189 dev_t dev,
190 int in_kernel,
191 struct proc *p);
192static __inline__
193vnunit(dev_t dev)
194{
195 return (minor(dev));
196}
197
198static int
199vnclose(dev_t dev, int flags, int devtype, struct proc *p)
200{
201 return (0);
202}
203
204static int
205vnopen(dev_t dev, int flags, int devtype, struct proc *p)
206{
207 struct vn_softc *vn;
208 int unit;
209
210 unit = vnunit(dev);
211 if (vnunit(dev) >= NVNDEVICE) {
212 return (ENXIO);
213 }
214 vn = vn_table + unit;
215 if ((flags & FWRITE) && (vn->sc_flags & VNF_READONLY))
216 return (EACCES);
217
218 return(0);
219}
220
221static int
222vnread(dev_t dev, struct uio *uio, int ioflag)
223{
224 struct proc * p = current_proc();
225 int status;
226 struct vn_softc * vn;
227 int unit;
228
229 unit = vnunit(dev);
230 if (vnunit(dev) >= NVNDEVICE) {
231 return (ENXIO);
232 }
233 vn = vn_table + unit;
234 if ((vn->sc_flags & VNF_INITED) == 0) {
235 return (ENXIO);
236 }
237 if (vn->sc_shadow_vp != NULL) {
238 return (ENODEV);
239 }
240 vn_lock(vn->sc_vp, LK_EXCLUSIVE | LK_RETRY, p);
241 status = VOP_READ(vn->sc_vp, uio, ioflag, vn->sc_cred);
242 VOP_UNLOCK(vn->sc_vp, 0, p);
243
244 return (status);
245}
246
247static int
248vnwrite(dev_t dev, struct uio *uio, int ioflag)
249{
250 struct proc * p = current_proc();
251 int status;
252 struct vn_softc * vn;
253 int unit;
254
255 unit = vnunit(dev);
256 if (vnunit(dev) >= NVNDEVICE) {
257 return (ENXIO);
258 }
259 vn = vn_table + unit;
260 if ((vn->sc_flags & VNF_INITED) == 0) {
261 return (ENXIO);
262 }
263 if (vn->sc_shadow_vp != NULL) {
264 return (ENODEV);
265 }
266 if (vn->sc_flags & VNF_READONLY) {
267 return (EROFS);
268 }
269
270 vn_lock(vn->sc_vp, LK_EXCLUSIVE | LK_RETRY, p);
271 status = VOP_WRITE(vn->sc_vp, uio, ioflag, vn->sc_cred);
272 VOP_UNLOCK(vn->sc_vp, 0, p);
273
274 return (status);
275}
276
277static boolean_t
278bp_is_mapped(struct buf * bp, vm_offset_t * vaddr)
279{
280 boolean_t is_mapped = FALSE;
281
282 if (bp->b_flags & B_NEED_IODONE) {
283 struct buf * real_bp = (struct buf *)bp->b_real_bp;
284
285 if (real_bp && real_bp->b_data) {
286 *vaddr = (vm_offset_t)real_bp->b_data;
287 is_mapped = TRUE;
288 }
289 }
290 return (is_mapped);
291}
292
293static __inline__ int
294file_io(struct vnode * vp, struct ucred * cred,
295 enum uio_rw op, char * base, off_t offset, long count,
296 struct proc * p, long * resid)
297{
298 struct uio auio;
299 struct iovec aiov;
300 int error;
301
302 bzero(&auio, sizeof(auio));
303 aiov.iov_base = base;
304 aiov.iov_len = count;
305 auio.uio_iov = &aiov;
306 auio.uio_iovcnt = 1;
307 auio.uio_segflg = UIO_SYSSPACE;
308 auio.uio_offset = offset;
309 auio.uio_rw = op;
310 auio.uio_resid = count;
311 auio.uio_procp = p;
312 vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p);
313 if (op == UIO_READ)
314 error = VOP_READ(vp, &auio, IO_SYNC, cred);
315 else
316 error = VOP_WRITE(vp, &auio, IO_SYNC, cred);
317 VOP_UNLOCK(vp, 0, p);
318 *resid = auio.uio_resid;
319 return (error);
320}
321
322static int
323shadow_read(struct vn_softc * vn, struct buf * bp, char * base, struct proc * p)
324{
325 int error = 0;
326 u_long offset;
327 boolean_t read_shadow;
328 u_long resid;
329 u_long start = 0;
330
331 offset = bp->b_blkno;
332 resid = bp->b_bcount / vn->sc_secsize;
333
334 while (resid > 0) {
335 u_long temp_resid;
336 u_long this_offset;
337 u_long this_resid;
338 struct vnode * vp;
339
340 read_shadow = shadow_map_read(vn->sc_shadow_map,
341 offset, resid,
342 &this_offset, &this_resid);
343 if (read_shadow) {
344 vp = vn->sc_shadow_vp;
345 }
346 else {
347 vp = vn->sc_vp;
348 }
349 error = file_io(vp, vn->sc_cred, UIO_READ, base + start,
350 (off_t)this_offset * vn->sc_secsize,
351 this_resid * vn->sc_secsize, p, &temp_resid);
352 if (error)
353 break;
354 temp_resid = this_resid - temp_resid / vn->sc_secsize;
355 if (temp_resid == 0) {
356 static int printed = 0;
357 printf("vn device: shadow_write zero length read (printed %d)\n", printed);
358 printed++;
359 break;
360 }
361 resid -= temp_resid;
362 offset += temp_resid;
363 start += temp_resid * vn->sc_secsize;;
364 }
365 bp->b_resid = resid * vn->sc_secsize;
366 return (error);
367}
368
369static int
370shadow_write(struct vn_softc * vn, struct buf * bp, char * base,
371 struct proc * p)
372{
373 int error = 0;
374 u_long offset;
375 boolean_t shadow_grew;
376 u_long resid;
377 u_long start = 0;
378
379 offset = bp->b_blkno;
380 resid = bp->b_bcount / vn->sc_secsize;
381
382 while (resid > 0) {
383 u_long temp_resid;
384 u_long this_offset;
385 u_long this_resid;
386 struct vnode * vp;
387
388 shadow_grew = shadow_map_write(vn->sc_shadow_map,
389 offset, resid,
390 &this_offset, &this_resid);
391 if (shadow_grew) {
392#if 0
393 off_t size;
394 /* truncate the file to its new length before write */
395 size = (off_t)shadow_map_shadow_size(vn->sc_shadow_map)
396 * vn->sc_secsize;
397 vn_lock(vn->sc_shadow_vp, LK_EXCLUSIVE | LK_RETRY, p);
398 VOP_TRUNCATE(vn->sc_shadow_vp, size,
399 IO_SYNC, vn->sc_cred, p);
400 VOP_UNLOCK(vn->sc_shadow_vp, 0, p);
55e303ae 401#endif
9bccf70c
A
402 }
403 error = file_io(vn->sc_shadow_vp, vn->sc_cred, UIO_WRITE,
404 base + start,
405 (off_t)this_offset * vn->sc_secsize,
406 this_resid * vn->sc_secsize, p, &temp_resid);
407 if (error) {
408 break;
409 }
410 temp_resid = this_resid - temp_resid / vn->sc_secsize;
411 if (temp_resid == 0) {
412 static int printed = 0;
413 printf("vn device: shadow_write zero length write (printed %d)\n", printed);
414 printed++;
415 break;
416 }
417 resid -= temp_resid;
418 offset += temp_resid;
419 start += temp_resid * vn->sc_secsize;;
420 }
421 bp->b_resid = resid * vn->sc_secsize;
422 return (error);
423}
424
425static int
426vn_readwrite_io(struct vn_softc * vn, struct buf * bp)
427{
428 int error = 0;
429 char * iov_base;
430 boolean_t need_unmap = FALSE;
431 struct proc * p = current_proc();
432 vm_offset_t vaddr = NULL;
433
434 if (bp->b_flags & B_VECTORLIST) {
435 if (bp_is_mapped(bp, &vaddr) == FALSE) {
436 if (ubc_upl_map(bp->b_pagelist, &vaddr)
437 != KERN_SUCCESS) {
438 panic("vn device: ubc_upl_map failed");
439 }
440 else {
441 need_unmap = TRUE;
442 }
443 }
444 }
445 if (error)
446 return (error);
447
448 if (vaddr != NULL)
449 iov_base = (caddr_t)(vaddr + bp->b_uploffset);
450 else
451 iov_base = bp->b_data;
452 if (vn->sc_shadow_vp == NULL) {
453 error = file_io(vn->sc_vp, vn->sc_cred,
454 bp->b_flags & B_READ ? UIO_READ : UIO_WRITE,
455 iov_base, (off_t)bp->b_blkno * vn->sc_secsize,
456 bp->b_bcount, p, &bp->b_resid);
457 }
458 else {
459 if (bp->b_flags & B_READ)
460 error = shadow_read(vn, bp, iov_base, p);
461 else
462 error = shadow_write(vn, bp, iov_base, p);
463 if (error == 0)
464 bp->b_resid = 0;
465
466 }
467 if (need_unmap) {
468 ubc_upl_unmap(bp->b_pagelist);
469 }
470 return (error);
471}
472
473static void
474vnstrategy(struct buf *bp)
475{
476 struct vn_softc *vn;
477 int error = 0;
478 long sz; /* in sc_secsize chunks */
479
480 vn = vn_table + vnunit(bp->b_dev);
481 if ((vn->sc_flags & VNF_INITED) == 0) {
482 bp->b_error = ENXIO;
483 bp->b_flags |= B_ERROR;
484 biodone(bp);
485 return;
486 }
487
488 bp->b_resid = bp->b_bcount;
489 /*
490 * Check for required alignment. Transfers must be a valid
491 * multiple of the sector size.
492 */
493 if (bp->b_bcount % vn->sc_secsize != 0 ||
494 bp->b_blkno % (vn->sc_secsize / DEV_BSIZE) != 0) {
495 bp->b_error = EINVAL;
496 bp->b_flags |= B_ERROR | B_INVAL;
497 biodone(bp);
498 return;
499 }
500 sz = howmany(bp->b_bcount, vn->sc_secsize);
501
502 /*
503 * If out of bounds return an error. If at the EOF point,
504 * simply read or write less.
505 */
506 if (bp->b_blkno >= vn->sc_size) {
55e303ae
A
507 if (bp->b_blkno > vn->sc_size) {
508 bp->b_error = EINVAL;
509 bp->b_flags |= B_ERROR | B_INVAL;
510 }
9bccf70c
A
511 biodone(bp);
512 return;
513 }
514 /*
515 * If the request crosses EOF, truncate the request.
516 */
517 if ((bp->b_blkno + sz) > vn->sc_size) {
518 bp->b_bcount = (vn->sc_size - bp->b_blkno) * vn->sc_secsize;
519 bp->b_resid = bp->b_bcount;
520 }
521
522 if (vn->sc_vp) {
523 error = vn_readwrite_io(vn, bp);
524 if (error) {
525 bp->b_error = error;
526 bp->b_flags |= B_ERROR;
527 }
528 biodone(bp);
529 }
530 else {
531 bp->b_flags |= B_ERROR;
532 bp->b_error = EINVAL;
533 biodone(bp);
534 }
535}
536
537/* ARGSUSED */
538static int
539vnioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p,
540 int is_char)
541{
542 struct vn_softc *vn;
543 struct vn_ioctl *vio;
544 int error;
545 u_long *f;
55e303ae 546 int num = 0;
9bccf70c
A
547 u_int64_t * o;
548 int unit;
55e303ae 549 int size = 0;
9bccf70c
A
550
551 unit = vnunit(dev);
552 if (vnunit(dev) >= NVNDEVICE) {
553 return (ENXIO);
554 }
555 vn = vn_table + unit;
556 error = suser(p->p_ucred, &p->p_acflag);
557 if (error)
558 return (error);
559
560 vio = (struct vn_ioctl *)data;
561 f = (u_long*)data;
562 o = (u_int64_t *)data;
563 switch (cmd) {
564 case VNIOCDETACH:
55e303ae
A
565 case DKIOCGETBLOCKSIZE:
566 case DKIOCSETBLOCKSIZE:
9bccf70c
A
567 case DKIOCGETMAXBLOCKCOUNTREAD:
568 case DKIOCGETMAXBLOCKCOUNTWRITE:
569 case DKIOCGETMAXSEGMENTCOUNTREAD:
570 case DKIOCGETMAXSEGMENTCOUNTWRITE:
55e303ae
A
571 case DKIOCGETMAXSEGMENTBYTECOUNTREAD:
572 case DKIOCGETMAXSEGMENTBYTECOUNTWRITE:
573 case DKIOCGETBLOCKCOUNT:
9bccf70c
A
574 case DKIOCGETBLOCKCOUNT32:
575 if ((vn->sc_flags & VNF_INITED) == 0) {
576 return (ENXIO);
577 }
578 break;
579 default:
580 break;
581 }
582 switch (cmd) {
583 case DKIOCGETMAXBLOCKCOUNTREAD:
55e303ae
A
584 vfs_io_attributes(vn->sc_vp, B_READ, &size, &num);
585 *o = size / vn->sc_secsize;
9bccf70c
A
586 break;
587 case DKIOCGETMAXBLOCKCOUNTWRITE:
55e303ae
A
588 vfs_io_attributes(vn->sc_vp, B_WRITE, &size, &num);
589 *o = size / vn->sc_secsize;
590 break;
591 case DKIOCGETMAXBYTECOUNTREAD:
592 vfs_io_attributes(vn->sc_vp, B_READ, &size, &num);
593 *o = size;
594 break;
595 case DKIOCGETMAXBYTECOUNTWRITE:
596 vfs_io_attributes(vn->sc_vp, B_WRITE, &size, &num);
597 *o = size;
9bccf70c
A
598 break;
599 case DKIOCGETMAXSEGMENTCOUNTREAD:
55e303ae
A
600 vfs_io_attributes(vn->sc_vp, B_READ, &size, &num);
601 *o = num;
9bccf70c
A
602 break;
603 case DKIOCGETMAXSEGMENTCOUNTWRITE:
55e303ae
A
604 vfs_io_attributes(vn->sc_vp, B_WRITE, &size, &num);
605 *o = num;
606 break;
607 case DKIOCGETMAXSEGMENTBYTECOUNTREAD:
608 vfs_io_maxsegsize(vn->sc_vp, B_READ, &size);
609 *o = size;
610 break;
611 case DKIOCGETMAXSEGMENTBYTECOUNTWRITE:
612 vfs_io_maxsegsize(vn->sc_vp, B_WRITE, &size);
613 *o = size;
9bccf70c
A
614 break;
615 case DKIOCGETBLOCKSIZE:
616 *f = vn->sc_secsize;
617 break;
618 case DKIOCSETBLOCKSIZE:
619 if (is_char) {
620 /* can only set block size on block device */
621 return (ENODEV);
622 }
623 if (vn->sc_shadow_vp != NULL) {
624 /* can't set the block size if already shadowing */
625 return (EBUSY);
626 }
627 if (*f < DEV_BSIZE) {
628 return (EINVAL);
629 }
630 vn->sc_secsize = *f;
631 /* recompute the size in terms of the new blocksize */
632 vn->sc_size = vn->sc_fsize / vn->sc_secsize;
633 break;
634 case DKIOCISWRITABLE:
635 *f = 1;
636 break;
637 case DKIOCGETBLOCKCOUNT32:
638 *f = vn->sc_size;
639 break;
55e303ae 640 case DKIOCGETBLOCKCOUNT:
9bccf70c
A
641 *o = vn->sc_size;
642 break;
643 case VNIOCSHADOW:
644 if (vn->sc_shadow_vp != NULL) {
645 return (EBUSY);
646 }
647 if (vn->sc_vp == NULL) {
648 /* much be attached before we can shadow */
649 return (EINVAL);
650 }
651 if (vio->vn_file == NULL) {
652 return (EINVAL);
653 }
654 error = vniocattach_shadow(vn, vio, dev, 0, p);
655 break;
656
657 case VNIOCATTACH:
658 if (is_char) {
659 /* attach only on block device */
660 return (ENODEV);
661 }
662 if (vn->sc_flags & VNF_INITED) {
663 return (EBUSY);
664 }
665 if (vio->vn_file == NULL) {
666 return (EINVAL);
667 }
668 error = vniocattach_file(vn, vio, dev, 0, p);
669 break;
670
671 case VNIOCDETACH:
672 if (is_char) {
673 /* detach only on block device */
674 return (ENODEV);
675 }
676 /* Note: spec_open won't open a mounted block device */
677
678 /*
679 * XXX handle i/o in progress. Return EBUSY, or wait, or
680 * flush the i/o.
681 * XXX handle multiple opens of the device. Return EBUSY,
682 * or revoke the fd's.
683 * How are these problems handled for removable and failing
684 * hardware devices? (Hint: They are not)
685 */
686 vnclear(vn);
687 break;
688
689 case VNIOCGSET:
690 vn_options |= *f;
691 *f = vn_options;
692 break;
693
694 case VNIOCGCLEAR:
695 vn_options &= ~(*f);
696 *f = vn_options;
697 break;
698
699 case VNIOCUSET:
700 vn->sc_options |= *f;
701 *f = vn->sc_options;
702 break;
703
704 case VNIOCUCLEAR:
705 vn->sc_options &= ~(*f);
706 *f = vn->sc_options;
707 break;
708
709 default:
710 error = ENOTTY;
711 break;
712 }
713 return(error);
714}
715
716static int
717vnioctl_chr(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
718{
719 return (vnioctl(dev, cmd, data, flag, p, TRUE));
720}
721
722static int
723vnioctl_blk(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
724{
725 return (vnioctl(dev, cmd, data, flag, p, FALSE));
726}
727
728/*
729 * vniocattach_file:
730 *
731 * Attach a file to a VN partition. Return the size in the vn_size
732 * field.
733 */
734
735static int
736vniocattach_file(struct vn_softc *vn,
737 struct vn_ioctl *vio,
738 dev_t dev,
739 int in_kernel,
740 struct proc *p)
741{
742 struct vattr vattr;
743 struct nameidata nd;
744 int error, flags;
745
746 flags = FREAD|FWRITE;
747 if (in_kernel) {
748 NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, vio->vn_file, p);
749 }
750 else {
751 NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, vio->vn_file, p);
752 }
753 error = vn_open(&nd, flags, 0);
754 if (error) {
755 if (error != EACCES && error != EPERM && error != EROFS)
756 return (error);
757 flags &= ~FWRITE;
758 if (in_kernel) {
759 NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE,
760 vio->vn_file, p);
761 }
762 else {
763 NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE,
764 vio->vn_file, p);
765 }
766 error = vn_open(&nd, flags, 0);
767 if (error)
768 return (error);
769 }
770 if (nd.ni_vp->v_type != VREG) {
771 error = EINVAL;
772 }
773 else if (ubc_isinuse(nd.ni_vp, 1)) {
774 error = EBUSY;
775 }
776 else {
777 error = VOP_GETATTR(nd.ni_vp, &vattr, p->p_ucred, p);
778 }
779 if (error != 0) {
780 VOP_UNLOCK(nd.ni_vp, 0, p);
781 (void) vn_close(nd.ni_vp, flags, p->p_ucred, p);
782 return (error);
783 }
784 vn->sc_vp = nd.ni_vp;
785 vn->sc_vp->v_flag |= VNOCACHE_DATA;
786 VOP_UNLOCK(nd.ni_vp, 0, p);
787
788 vn->sc_open_flags = flags;
789
790 /*
791 * If the size is specified, override the file attributes. Note that
792 * the vn_size argument is in PAGE_SIZE sized blocks.
793 */
794#if 0
795 if (vio->vn_size)
796 vn->sc_size = (quad_t)vio->vn_size * PAGE_SIZE / vn->sc_secsize;
797 else
798 vn->sc_size = vattr.va_size / vn->sc_secsize;
55e303ae 799#endif
9bccf70c
A
800 vn->sc_secsize = DEV_BSIZE;
801 vn->sc_fsize = vattr.va_size;
802 vn->sc_size = vattr.va_size / vn->sc_secsize;
803 error = vnsetcred(vn, p);
804 if (error) {
805 (void) vn_close(nd.ni_vp, flags, p->p_ucred, p);
806 return(error);
807 }
808 {
809 dev_t cdev = makedev(vndevice_cdev_major,
810 minor(dev));
811 vn->sc_cdev = devfs_make_node(cdev, DEVFS_CHAR,
812 UID_ROOT, GID_OPERATOR,
813 0600, "rvn%d",
814 minor(dev));
815 }
816 vn->sc_flags |= VNF_INITED;
817 if (flags == FREAD)
818 vn->sc_flags |= VNF_READONLY;
819 return(0);
820}
821
822static int
823vniocattach_shadow(vn, vio, dev, in_kernel, p)
824 struct vn_softc *vn;
825 struct vn_ioctl *vio;
826 dev_t dev;
827 int in_kernel;
828 struct proc *p;
829{
830 struct vattr vattr;
831 struct nameidata nd;
832 int error, flags;
833 shadow_map_t * map;
834
835 flags = FREAD|FWRITE;
836 if (in_kernel) {
837 NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, vio->vn_file, p);
838 }
839 else {
840 NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, vio->vn_file, p);
841 }
842 error = vn_open(&nd, flags, 0);
843 if (error) {
844 /* shadow MUST be writable! */
845 return (error);
846 }
847 if (nd.ni_vp->v_type != VREG ||
848 (error = VOP_GETATTR(nd.ni_vp, &vattr, p->p_ucred, p))) {
849 VOP_UNLOCK(nd.ni_vp, 0, p);
850 (void) vn_close(nd.ni_vp, flags, p->p_ucred, p);
851 return (error ? error : EINVAL);
852 }
853 vn->sc_shadow_vp = nd.ni_vp;
854 vn->sc_shadow_vp->v_flag |= VNOCACHE_DATA;
855 VOP_UNLOCK(nd.ni_vp, 0, p);
856
857 map = shadow_map_create(vn->sc_fsize, vattr.va_size,
858 0, vn->sc_secsize);
859 if (map == NULL) {
860 (void) vn_close(nd.ni_vp, flags, p->p_ucred, p);
861 vn->sc_shadow_vp = NULL;
862 return (ENOMEM);
863 }
864 vn->sc_shadow_map = map;
865 vn->sc_flags &= ~VNF_READONLY; /* we're now read/write */
866 return(0);
867}
868
869int
870vndevice_root_image(char * path, char devname[], dev_t * dev_p)
871{
872 int error = 0;
873 int flags;
874 struct vn_softc * vn;
875 struct vn_ioctl vio;
876
877 vio.vn_file = path;
878 vio.vn_size = 0;
879
880 vn = vn_table + ROOT_IMAGE_UNIT;
881 *dev_p = makedev(vndevice_bdev_major,
882 ROOT_IMAGE_UNIT);
883 sprintf(devname, "vn%d", ROOT_IMAGE_UNIT);
884 error = vniocattach_file(vn, &vio, *dev_p, 1, current_proc());
885 return (error);
886}
887
888/*
889 * Duplicate the current processes' credentials. Since we are called only
890 * as the result of a SET ioctl and only root can do that, any future access
891 * to this "disk" is essentially as root. Note that credentials may change
892 * if some other uid can write directly to the mapped file (NFS).
893 */
894int
895vnsetcred(struct vn_softc *vn, struct proc * p)
896{
897 char *tmpbuf;
898 int error = 0;
899 struct proc * current_proc();
900 struct ucred * cred = p->p_ucred;
901
902 /*
903 * Set credits in our softc
904 */
905
906 if (vn->sc_cred)
907 crfree(vn->sc_cred);
908 vn->sc_cred = crdup(cred);
909
910 /*
911 * Horrible kludge to establish credentials for NFS XXX.
912 */
913
914 if (vn->sc_vp) {
915 struct uio auio;
916 struct iovec aiov;
917
918 tmpbuf = _MALLOC(vn->sc_secsize, M_TEMP, M_WAITOK);
919 bzero(&auio, sizeof(auio));
920
921 aiov.iov_base = tmpbuf;
922 aiov.iov_len = vn->sc_secsize;
923 auio.uio_iov = &aiov;
924 auio.uio_iovcnt = 1;
925 auio.uio_offset = 0;
926 auio.uio_rw = UIO_READ;
927 auio.uio_segflg = UIO_SYSSPACE;
928 auio.uio_resid = aiov.iov_len;
929 vn_lock(vn->sc_vp, LK_EXCLUSIVE | LK_RETRY, p);
930 error = VOP_READ(vn->sc_vp, &auio, 0, vn->sc_cred);
931 VOP_UNLOCK(vn->sc_vp, 0, p);
932 FREE(tmpbuf, M_TEMP);
933 }
934 return (error);
935}
936
937void
938vnclear(struct vn_softc *vn)
939{
940 int flags;
941 struct proc * p = current_proc(); /* XXX */
942
943 if (vn->sc_vp != NULL) {
944 (void)vn_close(vn->sc_vp, vn->sc_open_flags, vn->sc_cred, p);
945 vn->sc_vp = NULL;
946 }
947 if (vn->sc_shadow_vp != NULL) {
948 (void)vn_close(vn->sc_shadow_vp, FREAD | FWRITE,
949 vn->sc_cred, p);
950 vn->sc_shadow_vp = NULL;
951 }
952 if (vn->sc_shadow_map != NULL) {
953 shadow_map_free(vn->sc_shadow_map);
954 vn->sc_shadow_map = NULL;
955 }
956 vn->sc_flags = ~(VNF_INITED | VNF_READONLY);
957 if (vn->sc_cred) {
958 crfree(vn->sc_cred);
959 vn->sc_cred = NULL;
960 }
961 vn->sc_size = 0;
962 vn->sc_fsize = 0;
963 if (vn->sc_cdev) {
964 devfs_remove(vn->sc_cdev);
965 vn->sc_cdev = NULL;
966 }
967}
968
969static int
970vnsize(dev_t dev)
971{
972 struct vn_softc *vn;
973 int unit;
974
975 unit = vnunit(dev);
976 if (vnunit(dev) >= NVNDEVICE) {
977 return (ENXIO);
978 }
979 vn = vn_table + unit;
980
981 if ((vn->sc_flags & VNF_INITED) == 0)
982 return(-1);
983
984 return(vn->sc_secsize);
985}
986
987#define CDEV_MAJOR -1
988#define BDEV_MAJOR -1
989static int vndevice_inited = 0;
990
991void
992vndevice_init()
993{
994 int i;
995
996 if (vndevice_inited)
997 return;
998 vndevice_bdev_major = bdevsw_add(BDEV_MAJOR, &vn_bdevsw);
999
1000 if (vndevice_bdev_major < 0) {
1001 printf("vndevice_init: bdevsw_add() returned %d\n",
1002 vndevice_bdev_major);
1003 return;
1004 }
1005 vndevice_cdev_major = cdevsw_add_with_bdev(CDEV_MAJOR, &vn_cdevsw,
1006 vndevice_bdev_major);
1007 if (vndevice_cdev_major < 0) {
1008 printf("vndevice_init: cdevsw_add() returned %d\n",
1009 vndevice_cdev_major);
1010 return;
1011 }
1012 for (i = 0; i < NVNDEVICE; i++) {
1013 dev_t dev = makedev(vndevice_bdev_major, i);
1014 vn_table[i].sc_bdev = devfs_make_node(dev, DEVFS_BLOCK,
1015 UID_ROOT, GID_OPERATOR,
1016 0600, "vn%d",
1017 i);
1018 if (vn_table[i].sc_bdev == NULL)
1019 printf("vninit: devfs_make_node failed!\n");
1020 }
1021}
55e303ae 1022#endif /* NVNDEVICE */