]> git.saurik.com Git - apple/xnu.git/blame - bsd/dev/vn/vn.c
xnu-6153.11.26.tar.gz
[apple/xnu.git] / bsd / dev / vn / vn.c
CommitLineData
91447636 1/*
cb323159 2 * Copyright (c) 2000-2019 Apple Inc. All rights reserved.
5d5c5d0d 3 *
2d21ac55 4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
0a7de745 5 *
2d21ac55
A
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
0a7de745 14 *
2d21ac55
A
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
0a7de745 17 *
2d21ac55
A
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
8f6c56a5
A
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
2d21ac55
A
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
0a7de745 25 *
2d21ac55 26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
91447636 27 */
9bccf70c
A
28
29/*
30 * Copyright (c) 1988 University of Utah.
31 * Copyright (c) 1990, 1993
32 * The Regents of the University of California. All rights reserved.
33 *
34 * This code is derived from software contributed to Berkeley by
35 * the Systems Programming Group of the University of Utah Computer
36 * Science Department.
37 *
38 * Redistribution and use in source and binary forms, with or without
39 * modification, are permitted provided that the following conditions
40 * are met:
41 * 1. Redistributions of source code must retain the above copyright
42 * notice, this list of conditions and the following disclaimer.
43 * 2. Redistributions in binary form must reproduce the above copyright
44 * notice, this list of conditions and the following disclaimer in the
45 * documentation and/or other materials provided with the distribution.
46 * 3. All advertising materials mentioning features or use of this software
47 * must display the following acknowledgement:
48 * This product includes software developed by the University of
49 * California, Berkeley and its contributors.
50 * 4. Neither the name of the University nor the names of its contributors
51 * may be used to endorse or promote products derived from this software
52 * without specific prior written permission.
53 *
54 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
55 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
56 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
57 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
58 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
59 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
60 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
61 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
62 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
63 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
64 * SUCH DAMAGE.
65 *
66 * from: Utah Hdr: vn.c 1.13 94/04/02
67 *
68 * from: @(#)vn.c 8.6 (Berkeley) 4/1/94
69 * $FreeBSD: src/sys/dev/vn/vn.c,v 1.105.2.4 2001/11/18 07:11:00 dillon Exp $
70 */
71
72/*
73 * Vnode disk driver.
74 *
75 * Block/character interface to a vnode. Allows one to treat a file
76 * as a disk (e.g. build a filesystem in it, mount it, etc.).
77 *
91447636 78 * NOTE 1: This uses the vnop_blockmap/vnop_strategy interface to the vnode
9bccf70c
A
79 * instead of a simple VOP_RDWR. We do this to avoid distorting the
80 * local buffer cache.
81 *
82 * NOTE 2: There is a security issue involved with this driver.
83 * Once mounted all access to the contents of the "mapped" file via
84 * the special file is controlled by the permissions on the special
85 * file, the protection of the mapped file is ignored (effectively,
86 * by using root credentials in all transactions).
87 *
88 * NOTE 3: Doesn't interact with leases, should it?
89 */
90
91#include "vndevice.h"
92
93#if NVNDEVICE > 0
94
95#include <sys/param.h>
96#include <sys/systm.h>
97#include <sys/kernel.h>
98#include <sys/mount.h>
99#include <sys/namei.h>
100#include <sys/proc.h>
91447636 101#include <sys/kauth.h>
9bccf70c
A
102#include <sys/buf.h>
103#include <sys/malloc.h>
91447636 104#include <sys/vnode_internal.h>
9bccf70c
A
105#include <sys/fcntl.h>
106#include <sys/conf.h>
55e303ae 107#include <sys/disk.h>
9bccf70c
A
108#include <sys/stat.h>
109#include <sys/conf.h>
91447636 110#include <sys/uio_internal.h>
9bccf70c
A
111
112#include <sys/vnioctl.h>
113
114#include <sys/vm.h>
115
116#include <vm/vm_pager.h>
9bccf70c
A
117#include <mach/memory_object_types.h>
118
119#include <miscfs/devfs/devfs.h>
120
55e303ae 121
9bccf70c 122#include "shadow.h"
0a7de745 123static void
39236c6e 124vndevice_do_init(void);
9bccf70c 125
0a7de745
A
126static ioctl_fcn_t vnioctl_chr;
127static ioctl_fcn_t vnioctl_blk;
128static open_close_fcn_t vnopen;
129static open_close_fcn_t vnclose;
130static psize_fcn_t vnsize;
131static strategy_fcn_t vnstrategy;
132static read_write_fcn_t vnread;
133static read_write_fcn_t vnwrite;
9bccf70c 134
0a7de745
A
135static int vndevice_bdev_major;
136static int vndevice_cdev_major;
9bccf70c
A
137
138/*
139 * cdevsw
140 * D_DISK we want to look like a disk
141 * D_CANFREE We support B_FREEBUF
142 */
143
144static struct bdevsw vn_bdevsw = {
cb323159
A
145 .d_open = vnopen,
146 .d_close = vnclose,
147 .d_strategy = vnstrategy,
148 .d_ioctl = vnioctl_blk,
149 .d_dump = eno_dump,
150 .d_psize = vnsize,
151 .d_type = D_DISK,
9bccf70c
A
152};
153
154static struct cdevsw vn_cdevsw = {
cb323159
A
155 .d_open = vnopen,
156 .d_close = vnclose,
157 .d_read = vnread,
158 .d_write = vnwrite,
159 .d_ioctl = vnioctl_chr,
160 .d_stop = eno_stop,
161 .d_reset = eno_reset,
162 .d_ttys = NULL,
163 .d_select = eno_select,
164 .d_mmap = eno_mmap,
165 .d_strategy = eno_strat,
166 .d_reserved_1 = eno_getc,
167 .d_reserved_2 = eno_putc,
168 .d_type = D_DISK,
9bccf70c
A
169};
170
171struct vn_softc {
0a7de745
A
172 u_int64_t sc_fsize; /* file size in bytes */
173 u_int64_t sc_size; /* size of vn, sc_secsize scale */
174 int sc_flags; /* flags */
175 u_int32_t sc_secsize; /* sector size */
176 struct vnode *sc_vp; /* vnode if not NULL */
177 uint32_t sc_vid;
178 int sc_open_flags;
179 struct vnode *sc_shadow_vp; /* shadow vnode if not NULL */
180 uint32_t sc_shadow_vid;
181 shadow_map_t * sc_shadow_map; /* shadow map if not NULL */
182 kauth_cred_t sc_cred; /* credentials */
183 u_int32_t sc_options; /* options */
184 void * sc_bdev;
185 void * sc_cdev;
9bccf70c
A
186} vn_table[NVNDEVICE];
187
0a7de745 188#define ROOT_IMAGE_UNIT 0
9bccf70c
A
189
190/* sc_flags */
0a7de745
A
191#define VNF_INITED 0x01
192#define VNF_READONLY 0x02
9bccf70c 193
0a7de745 194static u_int32_t vn_options;
9bccf70c 195
0a7de745
A
196#define IFOPT(vn, opt) if (((vn)->sc_options|vn_options) & (opt))
197#define TESTOPT(vn, opt) (((vn)->sc_options|vn_options) & (opt))
9bccf70c 198
0a7de745
A
199static int setcred(struct vnode * vp, kauth_cred_t cred);
200static void vnclear(struct vn_softc *vn, vfs_context_t ctx);
b0d623f7 201static void vn_ioctl_to_64(struct vn_ioctl_32 *from, struct vn_ioctl_64 *to);
91447636
A
202void vndevice_init(void);
203int vndevice_root_image(char * path, char devname[], dev_t * dev_p);
9bccf70c
A
204
205static int
206vniocattach_file(struct vn_softc *vn,
0a7de745
A
207 struct vn_ioctl_64 *vniop,
208 dev_t dev,
209 int in_kernel,
210 proc_t p);
9bccf70c
A
211static int
212vniocattach_shadow(struct vn_softc * vn,
0a7de745
A
213 struct vn_ioctl_64 *vniop,
214 dev_t dev,
215 int in_kernel,
216 proc_t p);
91447636 217static __inline__ int
9bccf70c
A
218vnunit(dev_t dev)
219{
0a7de745 220 return minor(dev);
9bccf70c
A
221}
222
0a7de745
A
223static int
224vnclose(__unused dev_t dev, __unused int flags,
225 __unused int devtype, __unused proc_t p)
9bccf70c 226{
0a7de745 227 return 0;
9bccf70c
A
228}
229
0a7de745 230static int
2d21ac55 231vnopen(dev_t dev, int flags, __unused int devtype, __unused proc_t p)
9bccf70c
A
232{
233 struct vn_softc *vn;
234 int unit;
235
236 unit = vnunit(dev);
237 if (vnunit(dev) >= NVNDEVICE) {
0a7de745 238 return ENXIO;
9bccf70c
A
239 }
240 vn = vn_table + unit;
0a7de745
A
241 if ((flags & FWRITE) && (vn->sc_flags & VNF_READONLY)) {
242 return EACCES;
243 }
9bccf70c 244
0a7de745 245 return 0;
9bccf70c
A
246}
247
91447636 248static int
0a7de745
A
249file_io(struct vnode * vp, vfs_context_t ctx,
250 enum uio_rw op, char * base, off_t offset, user_ssize_t count,
251 user_ssize_t * resid)
91447636 252{
0a7de745
A
253 uio_t auio;
254 int error;
255 char uio_buf[UIO_SIZEOF(1)];
256
257 auio = uio_createwithbuffer(1, offset, UIO_SYSSPACE, op,
258 &uio_buf[0], sizeof(uio_buf));
91447636 259 uio_addiov(auio, CAST_USER_ADDR_T(base), count);
0a7de745 260 if (op == UIO_READ) {
2d21ac55 261 error = VNOP_READ(vp, auio, IO_SYNC, ctx);
0a7de745 262 } else {
2d21ac55 263 error = VNOP_WRITE(vp, auio, IO_SYNC, ctx);
0a7de745 264 }
91447636
A
265
266 if (resid != NULL) {
267 *resid = uio_resid(auio);
268 }
0a7de745 269 return error;
91447636
A
270}
271
272static __inline__ off_t
273block_round(off_t o, int blocksize)
274{
0a7de745 275 return (o + blocksize - 1) / blocksize;
91447636
A
276}
277
278static __inline__ off_t
279block_truncate(off_t o, int blocksize)
280{
0a7de745 281 return o / blocksize;
91447636
A
282}
283
284static __inline__ int
285block_remainder(off_t o, int blocksize)
286{
0a7de745 287 return o % blocksize;
91447636
A
288}
289
290static int
0a7de745
A
291vnread_shadow(struct vn_softc * vn, struct uio *uio, int ioflag,
292 vfs_context_t ctx)
91447636 293{
0a7de745
A
294 u_int32_t blocksize = vn->sc_secsize;
295 int error = 0;
296 off_t offset;
297 user_ssize_t resid;
298 off_t orig_offset;
299 user_ssize_t orig_resid;
91447636
A
300
301 orig_resid = resid = uio_resid(uio);
302 orig_offset = offset = uio_offset(uio);
303
304 while (resid > 0) {
0a7de745
A
305 u_int32_t remainder;
306 u_int32_t this_block_number;
307 u_int32_t this_block_count;
308 off_t this_offset;
309 user_ssize_t this_resid;
310 struct vnode * vp;
91447636
A
311
312 /* figure out which blocks to read */
313 remainder = block_remainder(offset, blocksize);
314 if (shadow_map_read(vn->sc_shadow_map,
0a7de745
A
315 block_truncate(offset, blocksize),
316 block_round(resid + remainder, blocksize),
317 &this_block_number, &this_block_count)) {
91447636 318 vp = vn->sc_shadow_vp;
0a7de745 319 } else {
91447636
A
320 vp = vn->sc_vp;
321 }
322
323 /* read the blocks (or parts thereof) */
324 this_offset = (off_t)this_block_number * blocksize + remainder;
325 uio_setoffset(uio, this_offset);
326 this_resid = this_block_count * blocksize - remainder;
327 if (this_resid > resid) {
328 this_resid = resid;
329 }
330 uio_setresid(uio, this_resid);
2d21ac55 331 error = VNOP_READ(vp, uio, ioflag, ctx);
91447636
A
332 if (error) {
333 break;
334 }
335
336 /* figure out how much we actually read */
337 this_resid -= uio_resid(uio);
338 if (this_resid == 0) {
339 printf("vn device: vnread_shadow zero length read\n");
340 break;
341 }
342 resid -= this_resid;
343 offset += this_resid;
344 }
345 uio_setresid(uio, resid);
346 uio_setoffset(uio, offset);
0a7de745 347 return error;
91447636
A
348}
349
350static int
2d21ac55 351vncopy_block_to_shadow(struct vn_softc * vn, vfs_context_t ctx,
0a7de745 352 u_int32_t file_block, u_int32_t shadow_block)
91447636 353{
0a7de745
A
354 int error;
355 char * tmpbuf;
91447636
A
356
357 tmpbuf = _MALLOC(vn->sc_secsize, M_TEMP, M_WAITOK);
358 if (tmpbuf == NULL) {
0a7de745 359 return ENOMEM;
91447636
A
360 }
361 /* read one block from file at file_block offset */
2d21ac55 362 error = file_io(vn->sc_vp, ctx, UIO_READ,
0a7de745
A
363 tmpbuf, (off_t)file_block * vn->sc_secsize,
364 vn->sc_secsize, NULL);
91447636
A
365 if (error) {
366 goto done;
367 }
368 /* write one block to shadow file at shadow_block offset */
2d21ac55 369 error = file_io(vn->sc_shadow_vp, ctx, UIO_WRITE,
0a7de745
A
370 tmpbuf, (off_t)shadow_block * vn->sc_secsize,
371 vn->sc_secsize, NULL);
372done:
91447636 373 FREE(tmpbuf, M_TEMP);
0a7de745 374 return error;
91447636
A
375}
376
377enum {
378 FLAGS_FIRST_BLOCK_PARTIAL = 0x1,
379 FLAGS_LAST_BLOCK_PARTIAL = 0x2
380};
381
382static int
0a7de745
A
383vnwrite_shadow(struct vn_softc * vn, struct uio *uio, int ioflag,
384 vfs_context_t ctx)
91447636 385{
0a7de745
A
386 u_int32_t blocksize = vn->sc_secsize;
387 int error = 0;
388 user_ssize_t resid;
389 off_t offset;
91447636
A
390
391 resid = uio_resid(uio);
392 offset = uio_offset(uio);
393
394 while (resid > 0) {
0a7de745
A
395 int flags = 0;
396 u_int32_t offset_block_number;
397 u_int32_t remainder;
398 u_int32_t resid_block_count;
399 u_int32_t shadow_block_count;
400 u_int32_t shadow_block_number;
401 user_ssize_t this_resid;
91447636
A
402
403 /* figure out which blocks to write */
404 offset_block_number = block_truncate(offset, blocksize);
405 remainder = block_remainder(offset, blocksize);
406 resid_block_count = block_round(resid + remainder, blocksize);
407 /* figure out if the first or last blocks are partial writes */
408 if (remainder > 0
409 && !shadow_map_is_written(vn->sc_shadow_map,
0a7de745 410 offset_block_number)) {
91447636
A
411 /* the first block is a partial write */
412 flags |= FLAGS_FIRST_BLOCK_PARTIAL;
413 }
414 if (resid_block_count > 1
415 && !shadow_map_is_written(vn->sc_shadow_map,
0a7de745
A
416 offset_block_number
417 + resid_block_count - 1)
91447636
A
418 && block_remainder(offset + resid, blocksize) > 0) {
419 /* the last block is a partial write */
420 flags |= FLAGS_LAST_BLOCK_PARTIAL;
421 }
422 if (shadow_map_write(vn->sc_shadow_map,
0a7de745
A
423 offset_block_number, resid_block_count,
424 &shadow_block_number,
425 &shadow_block_count)) {
91447636
A
426 /* shadow file is growing */
427#if 0
428 /* truncate the file to its new length before write */
0a7de745
A
429 off_t size;
430 size = (off_t)shadow_map_shadow_size(vn->sc_shadow_map)
431 * vn->sc_secsize;
2d21ac55 432 vnode_setsize(vn->sc_shadow_vp, size, IO_SYNC, ctx);
b0d623f7 433#endif
91447636
A
434 }
435 /* write the blocks (or parts thereof) */
436 uio_setoffset(uio, (off_t)
0a7de745 437 shadow_block_number * blocksize + remainder);
91447636
A
438 this_resid = (off_t)shadow_block_count * blocksize - remainder;
439 if (this_resid >= resid) {
440 this_resid = resid;
441 if ((flags & FLAGS_LAST_BLOCK_PARTIAL) != 0) {
442 /* copy the last block to the shadow */
0a7de745
A
443 u_int32_t d;
444 u_int32_t s;
91447636 445
0a7de745
A
446 s = offset_block_number
447 + resid_block_count - 1;
448 d = shadow_block_number
449 + shadow_block_count - 1;
2d21ac55 450 error = vncopy_block_to_shadow(vn, ctx, s, d);
91447636
A
451 if (error) {
452 printf("vnwrite_shadow: failed to copy"
0a7de745
A
453 " block %u to shadow block %u\n",
454 s, d);
91447636
A
455 break;
456 }
457 }
458 }
459 uio_setresid(uio, this_resid);
460 if ((flags & FLAGS_FIRST_BLOCK_PARTIAL) != 0) {
461 /* copy the first block to the shadow */
2d21ac55 462 error = vncopy_block_to_shadow(vn, ctx,
0a7de745
A
463 offset_block_number,
464 shadow_block_number);
91447636
A
465 if (error) {
466 printf("vnwrite_shadow: failed to"
0a7de745
A
467 " copy block %u to shadow block %u\n",
468 offset_block_number,
469 shadow_block_number);
91447636
A
470 break;
471 }
472 }
2d21ac55 473 error = VNOP_WRITE(vn->sc_shadow_vp, uio, ioflag, ctx);
91447636
A
474 if (error) {
475 break;
476 }
477 /* figure out how much we actually wrote */
478 this_resid -= uio_resid(uio);
479 if (this_resid == 0) {
480 printf("vn device: vnwrite_shadow zero length write\n");
481 break;
482 }
483 resid -= this_resid;
484 offset += this_resid;
485 }
486 uio_setresid(uio, resid);
487 uio_setoffset(uio, offset);
0a7de745 488 return error;
91447636
A
489}
490
0a7de745 491static int
9bccf70c
A
492vnread(dev_t dev, struct uio *uio, int ioflag)
493{
0a7de745
A
494 struct vfs_context context;
495 int error = 0;
496 off_t offset;
497 proc_t p;
498 user_ssize_t resid;
499 struct vn_softc * vn;
500 int unit;
9bccf70c
A
501
502 unit = vnunit(dev);
503 if (vnunit(dev) >= NVNDEVICE) {
0a7de745 504 return ENXIO;
9bccf70c 505 }
91447636 506 p = current_proc();
9bccf70c
A
507 vn = vn_table + unit;
508 if ((vn->sc_flags & VNF_INITED) == 0) {
91447636
A
509 error = ENXIO;
510 goto done;
9bccf70c 511 }
2d21ac55
A
512
513 context.vc_thread = current_thread();
514 context.vc_ucred = vn->sc_cred;
515
91447636
A
516 error = vnode_getwithvid(vn->sc_vp, vn->sc_vid);
517 if (error != 0) {
518 /* the vnode is no longer available, abort */
519 error = ENXIO;
2d21ac55 520 vnclear(vn, &context);
91447636
A
521 goto done;
522 }
523
524 resid = uio_resid(uio);
525 offset = uio_offset(uio);
526
527 /*
528 * If out of bounds return an error. If at the EOF point,
529 * simply read less.
530 */
531 if (offset >= (off_t)vn->sc_fsize) {
532 if (offset > (off_t)vn->sc_fsize) {
533 error = EINVAL;
534 }
535 goto done;
536 }
537 /*
538 * If the request crosses EOF, truncate the request.
539 */
540 if ((offset + resid) > (off_t)vn->sc_fsize) {
541 resid = vn->sc_fsize - offset;
542 uio_setresid(uio, resid);
9bccf70c 543 }
9bccf70c 544
91447636
A
545 if (vn->sc_shadow_vp != NULL) {
546 error = vnode_getwithvid(vn->sc_shadow_vp,
0a7de745 547 vn->sc_shadow_vid);
91447636
A
548 if (error != 0) {
549 /* the vnode is no longer available, abort */
550 error = ENXIO;
551 vnode_put(vn->sc_vp);
2d21ac55 552 vnclear(vn, &context);
91447636
A
553 goto done;
554 }
555 error = vnread_shadow(vn, uio, ioflag, &context);
556 vnode_put(vn->sc_shadow_vp);
557 } else {
558 error = VNOP_READ(vn->sc_vp, uio, ioflag, &context);
559 }
560 vnode_put(vn->sc_vp);
0a7de745
A
561done:
562 return error;
9bccf70c
A
563}
564
0a7de745 565static int
9bccf70c
A
566vnwrite(dev_t dev, struct uio *uio, int ioflag)
567{
0a7de745
A
568 struct vfs_context context;
569 int error;
570 off_t offset;
571 proc_t p;
572 user_ssize_t resid;
573 struct vn_softc * vn;
574 int unit;
9bccf70c
A
575
576 unit = vnunit(dev);
577 if (vnunit(dev) >= NVNDEVICE) {
0a7de745 578 return ENXIO;
9bccf70c 579 }
91447636 580 p = current_proc();
9bccf70c
A
581 vn = vn_table + unit;
582 if ((vn->sc_flags & VNF_INITED) == 0) {
91447636
A
583 error = ENXIO;
584 goto done;
9bccf70c
A
585 }
586 if (vn->sc_flags & VNF_READONLY) {
91447636
A
587 error = EROFS;
588 goto done;
9bccf70c 589 }
2d21ac55
A
590
591 context.vc_thread = current_thread();
592 context.vc_ucred = vn->sc_cred;
593
91447636
A
594 error = vnode_getwithvid(vn->sc_vp, vn->sc_vid);
595 if (error != 0) {
596 /* the vnode is no longer available, abort */
597 error = ENXIO;
2d21ac55 598 vnclear(vn, &context);
91447636
A
599 goto done;
600 }
601 resid = uio_resid(uio);
602 offset = uio_offset(uio);
9bccf70c 603
91447636
A
604 /*
605 * If out of bounds return an error. If at the EOF point,
606 * simply write less.
607 */
608 if (offset >= (off_t)vn->sc_fsize) {
609 if (offset > (off_t)vn->sc_fsize) {
610 error = EINVAL;
611 }
612 goto done;
613 }
614 /*
615 * If the request crosses EOF, truncate the request.
616 */
617 if ((offset + resid) > (off_t)vn->sc_fsize) {
618 resid = (off_t)vn->sc_fsize - offset;
619 uio_setresid(uio, resid);
620 }
9bccf70c 621
91447636
A
622 if (vn->sc_shadow_vp != NULL) {
623 error = vnode_getwithvid(vn->sc_shadow_vp,
0a7de745 624 vn->sc_shadow_vid);
91447636
A
625 if (error != 0) {
626 /* the vnode is no longer available, abort */
627 error = ENXIO;
628 vnode_put(vn->sc_vp);
2d21ac55 629 vnclear(vn, &context);
91447636 630 goto done;
9bccf70c 631 }
91447636
A
632 error = vnwrite_shadow(vn, uio, ioflag, &context);
633 vnode_put(vn->sc_shadow_vp);
634 } else {
635 error = VNOP_WRITE(vn->sc_vp, uio, ioflag, &context);
9bccf70c 636 }
91447636 637 vnode_put(vn->sc_vp);
0a7de745
A
638done:
639 return error;
9bccf70c
A
640}
641
642static int
2d21ac55 643shadow_read(struct vn_softc * vn, struct buf * bp, char * base,
0a7de745 644 vfs_context_t ctx)
9bccf70c 645{
0a7de745
A
646 u_int32_t blocksize = vn->sc_secsize;
647 int error = 0;
648 u_int32_t offset;
649 boolean_t read_shadow;
650 u_int32_t resid;
651 u_int32_t start = 0;
9bccf70c 652
91447636
A
653 offset = buf_blkno(bp);
654 resid = buf_resid(bp) / blocksize;
9bccf70c 655 while (resid > 0) {
0a7de745
A
656 user_ssize_t temp_resid;
657 u_int32_t this_offset;
658 u_int32_t this_resid;
659 struct vnode * vp;
9bccf70c
A
660
661 read_shadow = shadow_map_read(vn->sc_shadow_map,
0a7de745
A
662 offset, resid,
663 &this_offset, &this_resid);
9bccf70c
A
664 if (read_shadow) {
665 vp = vn->sc_shadow_vp;
0a7de745 666 } else {
9bccf70c
A
667 vp = vn->sc_vp;
668 }
2d21ac55 669 error = file_io(vp, ctx, UIO_READ, base + start,
0a7de745
A
670 (off_t)this_offset * blocksize,
671 (user_ssize_t)this_resid * blocksize,
672 &temp_resid);
91447636 673 if (error) {
9bccf70c 674 break;
91447636
A
675 }
676 this_resid -= (temp_resid / blocksize);
677 if (this_resid == 0) {
678 printf("vn device: shadow_read zero length read\n");
9bccf70c
A
679 break;
680 }
91447636
A
681 resid -= this_resid;
682 offset += this_resid;
683 start += this_resid * blocksize;
9bccf70c 684 }
91447636 685 buf_setresid(bp, resid * blocksize);
0a7de745 686 return error;
9bccf70c
A
687}
688
689static int
0a7de745
A
690shadow_write(struct vn_softc * vn, struct buf * bp, char * base,
691 vfs_context_t ctx)
9bccf70c 692{
0a7de745
A
693 u_int32_t blocksize = vn->sc_secsize;
694 int error = 0;
695 u_int32_t offset;
696 boolean_t shadow_grew;
697 u_int32_t resid;
698 u_int32_t start = 0;
9bccf70c 699
91447636
A
700 offset = buf_blkno(bp);
701 resid = buf_resid(bp) / blocksize;
9bccf70c 702 while (resid > 0) {
0a7de745
A
703 user_ssize_t temp_resid;
704 u_int32_t this_offset;
705 u_int32_t this_resid;
9bccf70c 706
0a7de745
A
707 shadow_grew = shadow_map_write(vn->sc_shadow_map,
708 offset, resid,
709 &this_offset, &this_resid);
9bccf70c
A
710 if (shadow_grew) {
711#if 0
0a7de745 712 off_t size;
9bccf70c 713 /* truncate the file to its new length before write */
0a7de745
A
714 size = (off_t)shadow_map_shadow_size(vn->sc_shadow_map)
715 * blocksize;
2d21ac55 716 vnode_setsize(vn->sc_shadow_vp, size, IO_SYNC, ctx);
55e303ae 717#endif
9bccf70c 718 }
0a7de745
A
719 error = file_io(vn->sc_shadow_vp, ctx, UIO_WRITE,
720 base + start,
721 (off_t)this_offset * blocksize,
722 (user_ssize_t)this_resid * blocksize,
723 &temp_resid);
9bccf70c
A
724 if (error) {
725 break;
726 }
91447636
A
727 this_resid -= (temp_resid / blocksize);
728 if (this_resid == 0) {
729 printf("vn device: shadow_write zero length write\n");
9bccf70c
A
730 break;
731 }
91447636
A
732 resid -= this_resid;
733 offset += this_resid;
734 start += this_resid * blocksize;
9bccf70c 735 }
91447636 736 buf_setresid(bp, resid * blocksize);
0a7de745 737 return error;
9bccf70c
A
738}
739
740static int
2d21ac55 741vn_readwrite_io(struct vn_softc * vn, struct buf * bp, vfs_context_t ctx)
9bccf70c 742{
0a7de745
A
743 int error = 0;
744 char * iov_base;
745 caddr_t vaddr;
9bccf70c 746
0a7de745
A
747 if (buf_map(bp, &vaddr)) {
748 panic("vn device: buf_map failed");
749 }
91447636
A
750 iov_base = (char *)vaddr;
751
9bccf70c 752 if (vn->sc_shadow_vp == NULL) {
0a7de745 753 user_ssize_t temp_resid;
91447636 754
2d21ac55 755 error = file_io(vn->sc_vp, ctx,
0a7de745
A
756 buf_flags(bp) & B_READ ? UIO_READ : UIO_WRITE,
757 iov_base,
758 (off_t)buf_blkno(bp) * vn->sc_secsize,
759 buf_resid(bp), &temp_resid);
91447636 760 buf_setresid(bp, temp_resid);
0a7de745
A
761 } else {
762 if (buf_flags(bp) & B_READ) {
2d21ac55 763 error = shadow_read(vn, bp, iov_base, ctx);
0a7de745 764 } else {
2d21ac55 765 error = shadow_write(vn, bp, iov_base, ctx);
0a7de745 766 }
9bccf70c 767 }
91447636
A
768 buf_unmap(bp);
769
0a7de745 770 return error;
9bccf70c
A
771}
772
773static void
774vnstrategy(struct buf *bp)
775{
776 struct vn_softc *vn;
777 int error = 0;
0a7de745 778 long sz; /* in sc_secsize chunks */
91447636 779 daddr64_t blk_num;
0a7de745
A
780 struct vnode * shadow_vp = NULL;
781 struct vnode * vp = NULL;
782 struct vfs_context context;
9bccf70c 783
91447636 784 vn = vn_table + vnunit(buf_device(bp));
9bccf70c 785 if ((vn->sc_flags & VNF_INITED) == 0) {
91447636
A
786 error = ENXIO;
787 goto done;
9bccf70c
A
788 }
789
2d21ac55
A
790 context.vc_thread = current_thread();
791 context.vc_ucred = vn->sc_cred;
792
91447636 793 buf_setresid(bp, buf_count(bp));
9bccf70c
A
794 /*
795 * Check for required alignment. Transfers must be a valid
796 * multiple of the sector size.
797 */
91447636
A
798 blk_num = buf_blkno(bp);
799 if (buf_count(bp) % vn->sc_secsize != 0) {
800 error = EINVAL;
801 goto done;
9bccf70c 802 }
91447636 803 sz = howmany(buf_count(bp), vn->sc_secsize);
9bccf70c
A
804
805 /*
806 * If out of bounds return an error. If at the EOF point,
807 * simply read or write less.
808 */
91447636
A
809 if (blk_num >= 0 && (u_int64_t)blk_num >= vn->sc_size) {
810 if (blk_num > 0 && (u_int64_t)blk_num > vn->sc_size) {
811 error = EINVAL;
55e303ae 812 }
91447636 813 goto done;
9bccf70c
A
814 }
815 /*
816 * If the request crosses EOF, truncate the request.
817 */
91447636
A
818 if ((blk_num + sz) > 0 && ((u_int64_t)(blk_num + sz)) > vn->sc_size) {
819 buf_setcount(bp, (vn->sc_size - blk_num) * vn->sc_secsize);
820 buf_setresid(bp, buf_count(bp));
9bccf70c 821 }
91447636
A
822 vp = vn->sc_vp;
823 if (vp == NULL) {
824 error = ENXIO;
825 goto done;
826 }
2d21ac55 827
91447636
A
828 error = vnode_getwithvid(vp, vn->sc_vid);
829 if (error != 0) {
830 /* the vnode is no longer available, abort */
831 error = ENXIO;
2d21ac55 832 vnclear(vn, &context);
91447636
A
833 goto done;
834 }
835 shadow_vp = vn->sc_shadow_vp;
836 if (shadow_vp != NULL) {
837 error = vnode_getwithvid(shadow_vp,
0a7de745 838 vn->sc_shadow_vid);
91447636
A
839 if (error != 0) {
840 /* the vnode is no longer available, abort */
841 error = ENXIO;
842 vnode_put(vn->sc_vp);
2d21ac55 843 vnclear(vn, &context);
91447636 844 goto done;
9bccf70c 845 }
9bccf70c 846 }
2d21ac55
A
847
848 error = vn_readwrite_io(vn, bp, &context);
91447636
A
849 vnode_put(vp);
850 if (shadow_vp != NULL) {
851 vnode_put(shadow_vp);
9bccf70c 852 }
91447636 853
0a7de745 854done:
91447636 855 if (error) {
0a7de745 856 buf_seterror(bp, error);
91447636
A
857 }
858 buf_biodone(bp);
859 return;
9bccf70c
A
860}
861
862/* ARGSUSED */
0a7de745 863static int
91447636 864vnioctl(dev_t dev, u_long cmd, caddr_t data,
0a7de745
A
865 __unused int flag, proc_t p,
866 int is_char)
9bccf70c
A
867{
868 struct vn_softc *vn;
b0d623f7 869 struct vn_ioctl_64 *viop;
9bccf70c 870 int error;
91447636 871 u_int32_t *f;
9bccf70c
A
872 u_int64_t * o;
873 int unit;
91447636 874 struct vfsioattr ioattr;
b0d623f7 875 struct vn_ioctl_64 user_vnio;
0a7de745 876 struct vfs_context context;
9bccf70c
A
877
878 unit = vnunit(dev);
879 if (vnunit(dev) >= NVNDEVICE) {
0a7de745 880 return ENXIO;
9bccf70c 881 }
91447636 882
9bccf70c 883 vn = vn_table + unit;
91447636
A
884 error = proc_suser(p);
885 if (error) {
886 goto done;
887 }
9bccf70c 888
2d21ac55
A
889 context.vc_thread = current_thread();
890 context.vc_ucred = vn->sc_cred;
891
b0d623f7 892 viop = (struct vn_ioctl_64 *)data;
91447636 893 f = (u_int32_t *)data;
9bccf70c
A
894 o = (u_int64_t *)data;
895 switch (cmd) {
b0d623f7
A
896#ifdef __LP64__
897 case VNIOCDETACH32:
898 case VNIOCDETACH:
899#else
9bccf70c 900 case VNIOCDETACH:
91447636 901 case VNIOCDETACH64:
b0d623f7 902#endif
55e303ae 903 case DKIOCGETBLOCKSIZE:
91447636 904 case DKIOCSETBLOCKSIZE:
9bccf70c
A
905 case DKIOCGETMAXBLOCKCOUNTREAD:
906 case DKIOCGETMAXBLOCKCOUNTWRITE:
907 case DKIOCGETMAXSEGMENTCOUNTREAD:
908 case DKIOCGETMAXSEGMENTCOUNTWRITE:
55e303ae
A
909 case DKIOCGETMAXSEGMENTBYTECOUNTREAD:
910 case DKIOCGETMAXSEGMENTBYTECOUNTWRITE:
911 case DKIOCGETBLOCKCOUNT:
9bccf70c 912 if ((vn->sc_flags & VNF_INITED) == 0) {
91447636
A
913 error = ENXIO;
914 goto done;
9bccf70c
A
915 }
916 break;
917 default:
918 break;
919 }
91447636 920
0a7de745 921 if (vn->sc_vp != NULL) {
91447636 922 vfs_ioattr(vnode_mount(vn->sc_vp), &ioattr);
0a7de745 923 } else {
91447636 924 bzero(&ioattr, sizeof(ioattr));
0a7de745 925 }
91447636 926
9bccf70c 927 switch (cmd) {
91447636
A
928 case DKIOCISVIRTUAL:
929 *f = 1;
930 break;
9bccf70c 931 case DKIOCGETMAXBLOCKCOUNTREAD:
91447636 932 *o = ioattr.io_maxreadcnt / vn->sc_secsize;
9bccf70c
A
933 break;
934 case DKIOCGETMAXBLOCKCOUNTWRITE:
91447636 935 *o = ioattr.io_maxwritecnt / vn->sc_secsize;
55e303ae
A
936 break;
937 case DKIOCGETMAXBYTECOUNTREAD:
91447636 938 *o = ioattr.io_maxreadcnt;
55e303ae
A
939 break;
940 case DKIOCGETMAXBYTECOUNTWRITE:
91447636 941 *o = ioattr.io_maxwritecnt;
9bccf70c
A
942 break;
943 case DKIOCGETMAXSEGMENTCOUNTREAD:
91447636 944 *o = ioattr.io_segreadcnt;
9bccf70c
A
945 break;
946 case DKIOCGETMAXSEGMENTCOUNTWRITE:
91447636 947 *o = ioattr.io_segwritecnt;
55e303ae
A
948 break;
949 case DKIOCGETMAXSEGMENTBYTECOUNTREAD:
91447636 950 *o = ioattr.io_maxsegreadsize;
55e303ae
A
951 break;
952 case DKIOCGETMAXSEGMENTBYTECOUNTWRITE:
91447636 953 *o = ioattr.io_maxsegwritesize;
9bccf70c 954 break;
91447636 955 case DKIOCGETBLOCKSIZE:
0a7de745 956 *f = vn->sc_secsize;
9bccf70c 957 break;
91447636 958 case DKIOCSETBLOCKSIZE:
9bccf70c
A
959 if (is_char) {
960 /* can only set block size on block device */
91447636
A
961 error = ENODEV;
962 break;
9bccf70c
A
963 }
964 if (*f < DEV_BSIZE) {
91447636
A
965 error = EINVAL;
966 break;
967 }
968 if (vn->sc_shadow_vp != NULL) {
969 if (*f == (unsigned)vn->sc_secsize) {
970 break;
971 }
972 /* can't change the block size if already shadowing */
973 error = EBUSY;
974 break;
9bccf70c
A
975 }
976 vn->sc_secsize = *f;
977 /* recompute the size in terms of the new blocksize */
978 vn->sc_size = vn->sc_fsize / vn->sc_secsize;
979 break;
980 case DKIOCISWRITABLE:
981 *f = 1;
982 break;
55e303ae 983 case DKIOCGETBLOCKCOUNT:
9bccf70c
A
984 *o = vn->sc_size;
985 break;
b0d623f7
A
986#ifdef __LP64__
987 case VNIOCSHADOW32:
988 case VNIOCSHADOW:
989#else
9bccf70c 990 case VNIOCSHADOW:
91447636 991 case VNIOCSHADOW64:
b0d623f7 992#endif
9bccf70c 993 if (vn->sc_shadow_vp != NULL) {
91447636
A
994 error = EBUSY;
995 break;
9bccf70c
A
996 }
997 if (vn->sc_vp == NULL) {
998 /* much be attached before we can shadow */
91447636
A
999 error = EINVAL;
1000 break;
1001 }
1002 if (!proc_is64bit(p)) {
1003 /* downstream code expects LP64 version of vn_ioctl structure */
b0d623f7 1004 vn_ioctl_to_64((struct vn_ioctl_32 *)viop, &user_vnio);
91447636 1005 viop = &user_vnio;
9bccf70c 1006 }
91447636
A
1007 if (viop->vn_file == USER_ADDR_NULL) {
1008 error = EINVAL;
1009 break;
9bccf70c 1010 }
91447636 1011 error = vniocattach_shadow(vn, viop, dev, 0, p);
9bccf70c
A
1012 break;
1013
b0d623f7
A
1014#ifdef __LP64__
1015 case VNIOCATTACH32:
1016 case VNIOCATTACH:
1017#else
9bccf70c 1018 case VNIOCATTACH:
91447636 1019 case VNIOCATTACH64:
b0d623f7 1020#endif
9bccf70c
A
1021 if (is_char) {
1022 /* attach only on block device */
91447636
A
1023 error = ENODEV;
1024 break;
9bccf70c
A
1025 }
1026 if (vn->sc_flags & VNF_INITED) {
91447636
A
1027 error = EBUSY;
1028 break;
1029 }
1030 if (!proc_is64bit(p)) {
1031 /* downstream code expects LP64 version of vn_ioctl structure */
b0d623f7 1032 vn_ioctl_to_64((struct vn_ioctl_32 *)viop, &user_vnio);
91447636 1033 viop = &user_vnio;
9bccf70c 1034 }
91447636
A
1035 if (viop->vn_file == USER_ADDR_NULL) {
1036 error = EINVAL;
1037 break;
9bccf70c 1038 }
91447636 1039 error = vniocattach_file(vn, viop, dev, 0, p);
9bccf70c
A
1040 break;
1041
b0d623f7
A
1042#ifdef __LP64__
1043 case VNIOCDETACH32:
1044 case VNIOCDETACH:
1045#else
9bccf70c 1046 case VNIOCDETACH:
91447636 1047 case VNIOCDETACH64:
b0d623f7 1048#endif
9bccf70c
A
1049 if (is_char) {
1050 /* detach only on block device */
91447636
A
1051 error = ENODEV;
1052 break;
9bccf70c
A
1053 }
1054 /* Note: spec_open won't open a mounted block device */
1055
1056 /*
1057 * XXX handle i/o in progress. Return EBUSY, or wait, or
1058 * flush the i/o.
1059 * XXX handle multiple opens of the device. Return EBUSY,
1060 * or revoke the fd's.
1061 * How are these problems handled for removable and failing
1062 * hardware devices? (Hint: They are not)
1063 */
2d21ac55 1064 vnclear(vn, &context);
9bccf70c
A
1065 break;
1066
1067 case VNIOCGSET:
1068 vn_options |= *f;
1069 *f = vn_options;
1070 break;
1071
1072 case VNIOCGCLEAR:
1073 vn_options &= ~(*f);
1074 *f = vn_options;
1075 break;
1076
1077 case VNIOCUSET:
1078 vn->sc_options |= *f;
1079 *f = vn->sc_options;
1080 break;
1081
1082 case VNIOCUCLEAR:
1083 vn->sc_options &= ~(*f);
1084 *f = vn->sc_options;
1085 break;
1086
1087 default:
1088 error = ENOTTY;
1089 break;
1090 }
0a7de745
A
1091done:
1092 return error;
9bccf70c
A
1093}
1094
0a7de745 1095static int
2d21ac55 1096vnioctl_chr(dev_t dev, u_long cmd, caddr_t data, int flag, proc_t p)
9bccf70c 1097{
0a7de745 1098 return vnioctl(dev, cmd, data, flag, p, TRUE);
9bccf70c
A
1099}
1100
0a7de745 1101static int
2d21ac55 1102vnioctl_blk(dev_t dev, u_long cmd, caddr_t data, int flag, proc_t p)
9bccf70c 1103{
0a7de745 1104 return vnioctl(dev, cmd, data, flag, p, FALSE);
9bccf70c
A
1105}
1106
1107/*
1108 * vniocattach_file:
1109 *
1110 * Attach a file to a VN partition. Return the size in the vn_size
1111 * field.
1112 */
1113
1114static int
1115vniocattach_file(struct vn_softc *vn,
0a7de745
A
1116 struct vn_ioctl_64 *vniop,
1117 dev_t dev,
1118 int in_kernel,
1119 proc_t p)
9bccf70c 1120{
0a7de745 1121 dev_t cdev;
2d21ac55 1122 vfs_context_t ctx = vfs_context_current();
91447636 1123 kauth_cred_t cred;
9bccf70c 1124 struct nameidata nd;
91447636 1125 off_t file_size;
9bccf70c 1126 int error, flags;
91447636 1127
0a7de745 1128 flags = FREAD | FWRITE;
9bccf70c 1129 if (in_kernel) {
6d2010ae 1130 NDINIT(&nd, LOOKUP, OP_OPEN, FOLLOW, UIO_SYSSPACE, vniop->vn_file, ctx);
0a7de745
A
1131 } else {
1132 NDINIT(&nd, LOOKUP, OP_OPEN, FOLLOW,
1133 (IS_64BIT_PROCESS(p) ? UIO_USERSPACE64 : UIO_USERSPACE32),
1134 vniop->vn_file, ctx);
9bccf70c 1135 }
91447636 1136 /* vn_open gives both long- and short-term references */
9bccf70c
A
1137 error = vn_open(&nd, flags, 0);
1138 if (error) {
0c530ab8 1139 if (error != EACCES && error != EPERM && error != EROFS) {
0a7de745 1140 return error;
0c530ab8 1141 }
9bccf70c
A
1142 flags &= ~FWRITE;
1143 if (in_kernel) {
0a7de745
A
1144 NDINIT(&nd, LOOKUP, OP_OPEN, FOLLOW, UIO_SYSSPACE,
1145 vniop->vn_file, ctx);
1146 } else {
1147 NDINIT(&nd, LOOKUP, OP_OPEN, FOLLOW,
1148 (IS_64BIT_PROCESS(p) ? UIO_USERSPACE64 : UIO_USERSPACE32),
1149 vniop->vn_file, ctx);
9bccf70c
A
1150 }
1151 error = vn_open(&nd, flags, 0);
0c530ab8 1152 if (error) {
0a7de745 1153 return error;
0c530ab8 1154 }
9bccf70c
A
1155 }
1156 if (nd.ni_vp->v_type != VREG) {
1157 error = EINVAL;
0a7de745 1158 } else {
2d21ac55 1159 error = vnode_size(nd.ni_vp, &file_size, ctx);
9bccf70c
A
1160 }
1161 if (error != 0) {
2d21ac55 1162 (void) vn_close(nd.ni_vp, flags, ctx);
91447636 1163 vnode_put(nd.ni_vp);
0a7de745 1164 return error;
9bccf70c 1165 }
91447636
A
1166 cred = kauth_cred_proc_ref(p);
1167 nd.ni_vp->v_flag |= VNOCACHE_DATA;
2d21ac55 1168 error = setcred(nd.ni_vp, cred);
9bccf70c 1169 if (error) {
2d21ac55 1170 (void)vn_close(nd.ni_vp, flags, ctx);
91447636 1171 vnode_put(nd.ni_vp);
0c530ab8 1172 kauth_cred_unref(&cred);
0a7de745 1173 return error;
9bccf70c 1174 }
91447636
A
1175 vn->sc_secsize = DEV_BSIZE;
1176 vn->sc_fsize = file_size;
1177 vn->sc_size = file_size / vn->sc_secsize;
1178 vn->sc_vp = nd.ni_vp;
1179 vn->sc_vid = vnode_vid(nd.ni_vp);
1180 vn->sc_open_flags = flags;
1181 vn->sc_cred = cred;
1182 cdev = makedev(vndevice_cdev_major, minor(dev));
1183 vn->sc_cdev = devfs_make_node(cdev, DEVFS_CHAR,
0a7de745
A
1184 UID_ROOT, GID_OPERATOR,
1185 0600, "rvn%d",
1186 minor(dev));
9bccf70c 1187 vn->sc_flags |= VNF_INITED;
0a7de745 1188 if (flags == FREAD) {
9bccf70c 1189 vn->sc_flags |= VNF_READONLY;
0a7de745 1190 }
91447636
A
1191 /* lose the short-term reference */
1192 vnode_put(nd.ni_vp);
0a7de745 1193 return 0;
9bccf70c
A
1194}
1195
1196static int
0a7de745
A
1197vniocattach_shadow(struct vn_softc *vn, struct vn_ioctl_64 *vniop,
1198 __unused dev_t dev, int in_kernel, proc_t p)
9bccf70c 1199{
2d21ac55 1200 vfs_context_t ctx = vfs_context_current();
9bccf70c
A
1201 struct nameidata nd;
1202 int error, flags;
0a7de745 1203 shadow_map_t * map;
91447636
A
1204 off_t file_size;
1205
0a7de745 1206 flags = FREAD | FWRITE;
9bccf70c 1207 if (in_kernel) {
6d2010ae 1208 NDINIT(&nd, LOOKUP, OP_OPEN, FOLLOW, UIO_SYSSPACE, vniop->vn_file, ctx);
0a7de745
A
1209 } else {
1210 NDINIT(&nd, LOOKUP, OP_OPEN, FOLLOW,
1211 (IS_64BIT_PROCESS(p) ? UIO_USERSPACE64 : UIO_USERSPACE32),
1212 vniop->vn_file, ctx);
9bccf70c 1213 }
91447636 1214 /* vn_open gives both long- and short-term references */
9bccf70c
A
1215 error = vn_open(&nd, flags, 0);
1216 if (error) {
1217 /* shadow MUST be writable! */
0a7de745 1218 return error;
9bccf70c 1219 }
0a7de745 1220 if (nd.ni_vp->v_type != VREG
2d21ac55
A
1221 || (error = vnode_size(nd.ni_vp, &file_size, ctx))) {
1222 (void)vn_close(nd.ni_vp, flags, ctx);
91447636 1223 vnode_put(nd.ni_vp);
0a7de745 1224 return error ? error : EINVAL;
9bccf70c 1225 }
91447636 1226 map = shadow_map_create(vn->sc_fsize, file_size,
0a7de745 1227 0, vn->sc_secsize);
9bccf70c 1228 if (map == NULL) {
2d21ac55 1229 (void)vn_close(nd.ni_vp, flags, ctx);
91447636 1230 vnode_put(nd.ni_vp);
9bccf70c 1231 vn->sc_shadow_vp = NULL;
0a7de745 1232 return ENOMEM;
9bccf70c 1233 }
91447636
A
1234 vn->sc_shadow_vp = nd.ni_vp;
1235 vn->sc_shadow_vid = vnode_vid(nd.ni_vp);
1236 vn->sc_shadow_vp->v_flag |= VNOCACHE_DATA;
9bccf70c
A
1237 vn->sc_shadow_map = map;
1238 vn->sc_flags &= ~VNF_READONLY; /* we're now read/write */
91447636
A
1239
1240 /* lose the short-term reference */
1241 vnode_put(nd.ni_vp);
0a7de745 1242 return 0;
9bccf70c
A
1243}
1244
1245int
1246vndevice_root_image(char * path, char devname[], dev_t * dev_p)
1247{
0a7de745
A
1248 int error = 0;
1249 struct vn_softc * vn;
1250 struct vn_ioctl_64 vnio;
9bccf70c 1251
91447636
A
1252 vnio.vn_file = CAST_USER_ADDR_T(path);
1253 vnio.vn_size = 0;
9bccf70c
A
1254
1255 vn = vn_table + ROOT_IMAGE_UNIT;
0a7de745
A
1256 *dev_p = makedev(vndevice_bdev_major,
1257 ROOT_IMAGE_UNIT);
2d21ac55 1258 snprintf(devname, 16, "vn%d", ROOT_IMAGE_UNIT);
91447636 1259 error = vniocattach_file(vn, &vnio, *dev_p, 1, current_proc());
0a7de745 1260 return error;
9bccf70c
A
1261}
1262
1263/*
1264 * Duplicate the current processes' credentials. Since we are called only
1265 * as the result of a SET ioctl and only root can do that, any future access
1266 * to this "disk" is essentially as root. Note that credentials may change
1267 * if some other uid can write directly to the mapped file (NFS).
1268 */
91447636 1269static int
2d21ac55 1270setcred(struct vnode * vp, kauth_cred_t cred)
9bccf70c
A
1271{
1272 char *tmpbuf;
1273 int error = 0;
0a7de745 1274 struct vfs_context context;
9bccf70c
A
1275
1276 /*
1277 * Horrible kludge to establish credentials for NFS XXX.
1278 */
2d21ac55 1279 context.vc_thread = current_thread();
91447636
A
1280 context.vc_ucred = cred;
1281 tmpbuf = _MALLOC(DEV_BSIZE, M_TEMP, M_WAITOK);
1282 error = file_io(vp, &context, UIO_READ, tmpbuf, 0, DEV_BSIZE, NULL);
1283 FREE(tmpbuf, M_TEMP);
0a7de745 1284 return error;
9bccf70c
A
1285}
1286
1287void
2d21ac55 1288vnclear(struct vn_softc *vn, vfs_context_t ctx)
9bccf70c 1289{
9bccf70c 1290 if (vn->sc_vp != NULL) {
91447636 1291 /* release long-term reference */
2d21ac55 1292 (void)vn_close(vn->sc_vp, vn->sc_open_flags, ctx);
9bccf70c
A
1293 vn->sc_vp = NULL;
1294 }
1295 if (vn->sc_shadow_vp != NULL) {
91447636 1296 /* release long-term reference */
2d21ac55 1297 (void)vn_close(vn->sc_shadow_vp, FREAD | FWRITE, ctx);
9bccf70c
A
1298 vn->sc_shadow_vp = NULL;
1299 }
1300 if (vn->sc_shadow_map != NULL) {
1301 shadow_map_free(vn->sc_shadow_map);
1302 vn->sc_shadow_map = NULL;
1303 }
91447636 1304 vn->sc_flags &= ~(VNF_INITED | VNF_READONLY);
9bccf70c 1305 if (vn->sc_cred) {
0c530ab8 1306 kauth_cred_unref(&vn->sc_cred);
9bccf70c
A
1307 }
1308 vn->sc_size = 0;
1309 vn->sc_fsize = 0;
1310 if (vn->sc_cdev) {
1311 devfs_remove(vn->sc_cdev);
1312 vn->sc_cdev = NULL;
1313 }
1314}
1315
0a7de745 1316static int
9bccf70c
A
1317vnsize(dev_t dev)
1318{
0a7de745 1319 int secsize;
9bccf70c
A
1320 struct vn_softc *vn;
1321 int unit;
1322
1323 unit = vnunit(dev);
1324 if (vnunit(dev) >= NVNDEVICE) {
0a7de745 1325 return -1;
9bccf70c 1326 }
9bccf70c 1327
91447636 1328 vn = vn_table + unit;
0a7de745 1329 if ((vn->sc_flags & VNF_INITED) == 0) {
91447636 1330 secsize = -1;
0a7de745 1331 } else {
91447636 1332 secsize = vn->sc_secsize;
0a7de745 1333 }
fe8ab488 1334
0a7de745 1335 return secsize;
9bccf70c
A
1336}
1337
0a7de745
A
1338#define CDEV_MAJOR -1
1339#define BDEV_MAJOR -1
9bccf70c
A
1340static int vndevice_inited = 0;
1341
0a7de745 1342void
91447636 1343vndevice_init(void)
9bccf70c 1344{
0a7de745 1345 if (vndevice_inited) {
9bccf70c 1346 return;
0a7de745
A
1347 }
1348
2d21ac55
A
1349 vndevice_do_init();
1350}
0a7de745
A
1351
1352static void
2d21ac55
A
1353vndevice_do_init( void )
1354{
0a7de745 1355 int i;
2d21ac55 1356
9bccf70c
A
1357 vndevice_bdev_major = bdevsw_add(BDEV_MAJOR, &vn_bdevsw);
1358
1359 if (vndevice_bdev_major < 0) {
1360 printf("vndevice_init: bdevsw_add() returned %d\n",
0a7de745 1361 vndevice_bdev_major);
9bccf70c
A
1362 return;
1363 }
1364 vndevice_cdev_major = cdevsw_add_with_bdev(CDEV_MAJOR, &vn_cdevsw,
0a7de745 1365 vndevice_bdev_major);
9bccf70c
A
1366 if (vndevice_cdev_major < 0) {
1367 printf("vndevice_init: cdevsw_add() returned %d\n",
0a7de745 1368 vndevice_cdev_major);
9bccf70c
A
1369 return;
1370 }
1371 for (i = 0; i < NVNDEVICE; i++) {
0a7de745 1372 dev_t dev = makedev(vndevice_bdev_major, i);
9bccf70c 1373 vn_table[i].sc_bdev = devfs_make_node(dev, DEVFS_BLOCK,
0a7de745
A
1374 UID_ROOT, GID_OPERATOR,
1375 0600, "vn%d",
1376 i);
1377 if (vn_table[i].sc_bdev == NULL) {
9bccf70c 1378 printf("vninit: devfs_make_node failed!\n");
0a7de745 1379 }
9bccf70c
A
1380 }
1381}
91447636 1382
0a7de745
A
1383static void
1384vn_ioctl_to_64(struct vn_ioctl_32 *from, struct vn_ioctl_64 *to)
91447636
A
1385{
1386 to->vn_file = CAST_USER_ADDR_T(from->vn_file);
1387 to->vn_size = from->vn_size;
1388 to->vn_control = from->vn_control;
1389}
1390
55e303ae 1391#endif /* NVNDEVICE */