17c478bd9Sstevel@tonic-gate /* 27c478bd9Sstevel@tonic-gate * CDDL HEADER START 37c478bd9Sstevel@tonic-gate * 47c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5*bd07e074Sheppo * Common Development and Distribution License (the "License"). 6*bd07e074Sheppo * You may not use this file except in compliance with the License. 77c478bd9Sstevel@tonic-gate * 87c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 97c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 107c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions 117c478bd9Sstevel@tonic-gate * and limitations under the License. 127c478bd9Sstevel@tonic-gate * 137c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 147c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 157c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 167c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 177c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 187c478bd9Sstevel@tonic-gate * 197c478bd9Sstevel@tonic-gate * CDDL HEADER END 207c478bd9Sstevel@tonic-gate */ 217c478bd9Sstevel@tonic-gate /* 22*bd07e074Sheppo * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 237c478bd9Sstevel@tonic-gate * Use is subject to license terms. 247c478bd9Sstevel@tonic-gate */ 257c478bd9Sstevel@tonic-gate 267c478bd9Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 277c478bd9Sstevel@tonic-gate 287c478bd9Sstevel@tonic-gate /* 297c478bd9Sstevel@tonic-gate * lofi (loopback file) driver - allows you to attach a file to a device, 307c478bd9Sstevel@tonic-gate * which can then be accessed through that device. The simple model is that 317c478bd9Sstevel@tonic-gate * you tell lofi to open a file, and then use the block device you get as 327c478bd9Sstevel@tonic-gate * you would any block device. lofi translates access to the block device 337c478bd9Sstevel@tonic-gate * into I/O on the underlying file. This is mostly useful for 347c478bd9Sstevel@tonic-gate * mounting images of filesystems. 357c478bd9Sstevel@tonic-gate * 367c478bd9Sstevel@tonic-gate * lofi is controlled through /dev/lofictl - this is the only device exported 377c478bd9Sstevel@tonic-gate * during attach, and is minor number 0. lofiadm communicates with lofi through 387c478bd9Sstevel@tonic-gate * ioctls on this device. When a file is attached to lofi, block and character 397c478bd9Sstevel@tonic-gate * devices are exported in /dev/lofi and /dev/rlofi. Currently, these devices 407c478bd9Sstevel@tonic-gate * are identified by their minor number, and the minor number is also used 417c478bd9Sstevel@tonic-gate * as the name in /dev/lofi. If we ever decide to support virtual disks, 427c478bd9Sstevel@tonic-gate * we'll have to divide the minor number space to identify fdisk partitions 437c478bd9Sstevel@tonic-gate * and slices, and the name will then be the minor number shifted down a 447c478bd9Sstevel@tonic-gate * few bits. Minor devices are tracked with state structures handled with 457c478bd9Sstevel@tonic-gate * ddi_soft_state(9F) for simplicity. 467c478bd9Sstevel@tonic-gate * 477c478bd9Sstevel@tonic-gate * A file attached to lofi is opened when attached and not closed until 487c478bd9Sstevel@tonic-gate * explicitly detached from lofi. This seems more sensible than deferring 497c478bd9Sstevel@tonic-gate * the open until the /dev/lofi device is opened, for a number of reasons. 507c478bd9Sstevel@tonic-gate * One is that any failure is likely to be noticed by the person (or script) 517c478bd9Sstevel@tonic-gate * running lofiadm. Another is that it would be a security problem if the 527c478bd9Sstevel@tonic-gate * file was replaced by another one after being added but before being opened. 537c478bd9Sstevel@tonic-gate * 547c478bd9Sstevel@tonic-gate * The only hard part about lofi is the ioctls. In order to support things 557c478bd9Sstevel@tonic-gate * like 'newfs' on a lofi device, it needs to support certain disk ioctls. 567c478bd9Sstevel@tonic-gate * So it has to fake disk geometry and partition information. More may need 577c478bd9Sstevel@tonic-gate * to be faked if your favorite utility doesn't work and you think it should 587c478bd9Sstevel@tonic-gate * (fdformat doesn't work because it really wants to know the type of floppy 597c478bd9Sstevel@tonic-gate * controller to talk to, and that didn't seem easy to fake. Or possibly even 607c478bd9Sstevel@tonic-gate * necessary, since we have mkfs_pcfs now). 617c478bd9Sstevel@tonic-gate * 627c478bd9Sstevel@tonic-gate * Known problems: 637c478bd9Sstevel@tonic-gate * 647c478bd9Sstevel@tonic-gate * UFS logging. Mounting a UFS filesystem image "logging" 657c478bd9Sstevel@tonic-gate * works for basic copy testing but wedges during a build of ON through 667c478bd9Sstevel@tonic-gate * that image. Some deadlock in lufs holding the log mutex and then 677c478bd9Sstevel@tonic-gate * getting stuck on a buf. So for now, don't do that. 687c478bd9Sstevel@tonic-gate * 697c478bd9Sstevel@tonic-gate * Direct I/O. Since the filesystem data is being cached in the buffer 707c478bd9Sstevel@tonic-gate * cache, _and_ again in the underlying filesystem, it's tempting to 717c478bd9Sstevel@tonic-gate * enable direct I/O on the underlying file. Don't, because that deadlocks. 727c478bd9Sstevel@tonic-gate * I think to fix the cache-twice problem we might need filesystem support. 737c478bd9Sstevel@tonic-gate * 747c478bd9Sstevel@tonic-gate * lofi on itself. The simple lock strategy (lofi_lock) precludes this 757c478bd9Sstevel@tonic-gate * because you'll be in lofi_ioctl, holding the lock when you open the 767c478bd9Sstevel@tonic-gate * file, which, if it's lofi, will grab lofi_lock. We prevent this for 777c478bd9Sstevel@tonic-gate * now, though not using ddi_soft_state(9F) would make it possible to 787c478bd9Sstevel@tonic-gate * do. Though it would still be silly. 797c478bd9Sstevel@tonic-gate * 807c478bd9Sstevel@tonic-gate * Interesting things to do: 817c478bd9Sstevel@tonic-gate * 827c478bd9Sstevel@tonic-gate * Allow multiple files for each device. A poor-man's metadisk, basically. 837c478bd9Sstevel@tonic-gate * 847c478bd9Sstevel@tonic-gate * Pass-through ioctls on block devices. You can (though it's not 857c478bd9Sstevel@tonic-gate * documented), give lofi a block device as a file name. Then we shouldn't 867c478bd9Sstevel@tonic-gate * need to fake a geometry. But this is also silly unless you're replacing 877c478bd9Sstevel@tonic-gate * metadisk. 887c478bd9Sstevel@tonic-gate * 897c478bd9Sstevel@tonic-gate * Encryption. tpm would like this. Apparently Windows 2000 has it, and 907c478bd9Sstevel@tonic-gate * so does Linux. 917c478bd9Sstevel@tonic-gate */ 927c478bd9Sstevel@tonic-gate 937c478bd9Sstevel@tonic-gate #include <sys/types.h> 947c478bd9Sstevel@tonic-gate #include <sys/sysmacros.h> 957c478bd9Sstevel@tonic-gate #include <sys/cmn_err.h> 967c478bd9Sstevel@tonic-gate #include <sys/uio.h> 977c478bd9Sstevel@tonic-gate #include <sys/kmem.h> 987c478bd9Sstevel@tonic-gate #include <sys/cred.h> 997c478bd9Sstevel@tonic-gate #include <sys/mman.h> 1007c478bd9Sstevel@tonic-gate #include <sys/errno.h> 1017c478bd9Sstevel@tonic-gate #include <sys/aio_req.h> 1027c478bd9Sstevel@tonic-gate #include <sys/stat.h> 1037c478bd9Sstevel@tonic-gate #include <sys/file.h> 1047c478bd9Sstevel@tonic-gate #include <sys/modctl.h> 1057c478bd9Sstevel@tonic-gate #include <sys/conf.h> 1067c478bd9Sstevel@tonic-gate #include <sys/debug.h> 1077c478bd9Sstevel@tonic-gate #include <sys/vnode.h> 1087c478bd9Sstevel@tonic-gate #include <sys/lofi.h> 1097c478bd9Sstevel@tonic-gate #include <sys/vol.h> 1107c478bd9Sstevel@tonic-gate #include <sys/fcntl.h> 1117c478bd9Sstevel@tonic-gate #include <sys/pathname.h> 1127c478bd9Sstevel@tonic-gate #include <sys/filio.h> 1137c478bd9Sstevel@tonic-gate #include <sys/fdio.h> 1147c478bd9Sstevel@tonic-gate #include <sys/open.h> 1157c478bd9Sstevel@tonic-gate #include <sys/disp.h> 1167c478bd9Sstevel@tonic-gate #include <vm/seg_map.h> 1177c478bd9Sstevel@tonic-gate #include <sys/ddi.h> 1187c478bd9Sstevel@tonic-gate #include <sys/sunddi.h> 1197c478bd9Sstevel@tonic-gate 1207c478bd9Sstevel@tonic-gate /* seems safer than having to get the string right many times */ 1217c478bd9Sstevel@tonic-gate #define NBLOCKS_PROP_NAME "Nblocks" 1227c478bd9Sstevel@tonic-gate #define SIZE_PROP_NAME "Size" 1237c478bd9Sstevel@tonic-gate 1247c478bd9Sstevel@tonic-gate static dev_info_t *lofi_dip; 1257c478bd9Sstevel@tonic-gate static void *lofi_statep; 1267c478bd9Sstevel@tonic-gate static kmutex_t lofi_lock; /* state lock */ 1277c478bd9Sstevel@tonic-gate 1287c478bd9Sstevel@tonic-gate /* 1297c478bd9Sstevel@tonic-gate * Because lofi_taskq_nthreads limits the actual swamping of the device, the 1307c478bd9Sstevel@tonic-gate * maxalloc parameter (lofi_taskq_maxalloc) should be tuned conservatively 1317c478bd9Sstevel@tonic-gate * high. If we want to be assured that the underlying device is always busy, 1327c478bd9Sstevel@tonic-gate * we must be sure that the number of bytes enqueued when the number of 1337c478bd9Sstevel@tonic-gate * enqueued tasks exceeds maxalloc is sufficient to keep the device busy for 1347c478bd9Sstevel@tonic-gate * the duration of the sleep time in taskq_ent_alloc(). That is, lofi should 1357c478bd9Sstevel@tonic-gate * set maxalloc to be the maximum throughput (in bytes per second) of the 1367c478bd9Sstevel@tonic-gate * underlying device divided by the minimum I/O size. We assume a realistic 1377c478bd9Sstevel@tonic-gate * maximum throughput of one hundred megabytes per second; we set maxalloc on 1387c478bd9Sstevel@tonic-gate * the lofi task queue to be 104857600 divided by DEV_BSIZE. 1397c478bd9Sstevel@tonic-gate */ 1407c478bd9Sstevel@tonic-gate static int lofi_taskq_maxalloc = 104857600 / DEV_BSIZE; 1417c478bd9Sstevel@tonic-gate static int lofi_taskq_nthreads = 4; /* # of taskq threads per device */ 1427c478bd9Sstevel@tonic-gate 1437c478bd9Sstevel@tonic-gate uint32_t lofi_max_files = LOFI_MAX_FILES; 1447c478bd9Sstevel@tonic-gate 1457c478bd9Sstevel@tonic-gate static int 1467c478bd9Sstevel@tonic-gate lofi_busy(void) 1477c478bd9Sstevel@tonic-gate { 1487c478bd9Sstevel@tonic-gate minor_t minor; 1497c478bd9Sstevel@tonic-gate 1507c478bd9Sstevel@tonic-gate /* 1517c478bd9Sstevel@tonic-gate * We need to make sure no mappings exist - mod_remove won't 1527c478bd9Sstevel@tonic-gate * help because the device isn't open. 1537c478bd9Sstevel@tonic-gate */ 1547c478bd9Sstevel@tonic-gate mutex_enter(&lofi_lock); 1557c478bd9Sstevel@tonic-gate for (minor = 1; minor <= lofi_max_files; minor++) { 1567c478bd9Sstevel@tonic-gate if (ddi_get_soft_state(lofi_statep, minor) != NULL) { 1577c478bd9Sstevel@tonic-gate mutex_exit(&lofi_lock); 1587c478bd9Sstevel@tonic-gate return (EBUSY); 1597c478bd9Sstevel@tonic-gate } 1607c478bd9Sstevel@tonic-gate } 1617c478bd9Sstevel@tonic-gate mutex_exit(&lofi_lock); 1627c478bd9Sstevel@tonic-gate return (0); 1637c478bd9Sstevel@tonic-gate } 1647c478bd9Sstevel@tonic-gate 1657c478bd9Sstevel@tonic-gate static int 1667c478bd9Sstevel@tonic-gate is_opened(struct lofi_state *lsp) 1677c478bd9Sstevel@tonic-gate { 1687c478bd9Sstevel@tonic-gate ASSERT(mutex_owned(&lofi_lock)); 1697c478bd9Sstevel@tonic-gate return (lsp->ls_chr_open || lsp->ls_blk_open || lsp->ls_lyr_open_count); 1707c478bd9Sstevel@tonic-gate } 1717c478bd9Sstevel@tonic-gate 1727c478bd9Sstevel@tonic-gate static int 1737c478bd9Sstevel@tonic-gate mark_opened(struct lofi_state *lsp, int otyp) 1747c478bd9Sstevel@tonic-gate { 1757c478bd9Sstevel@tonic-gate ASSERT(mutex_owned(&lofi_lock)); 1767c478bd9Sstevel@tonic-gate switch (otyp) { 1777c478bd9Sstevel@tonic-gate case OTYP_CHR: 1787c478bd9Sstevel@tonic-gate lsp->ls_chr_open = 1; 1797c478bd9Sstevel@tonic-gate break; 1807c478bd9Sstevel@tonic-gate case OTYP_BLK: 1817c478bd9Sstevel@tonic-gate lsp->ls_blk_open = 1; 1827c478bd9Sstevel@tonic-gate break; 1837c478bd9Sstevel@tonic-gate case OTYP_LYR: 1847c478bd9Sstevel@tonic-gate lsp->ls_lyr_open_count++; 1857c478bd9Sstevel@tonic-gate break; 1867c478bd9Sstevel@tonic-gate default: 1877c478bd9Sstevel@tonic-gate return (-1); 1887c478bd9Sstevel@tonic-gate } 1897c478bd9Sstevel@tonic-gate return (0); 1907c478bd9Sstevel@tonic-gate } 1917c478bd9Sstevel@tonic-gate 1927c478bd9Sstevel@tonic-gate static void 1937c478bd9Sstevel@tonic-gate mark_closed(struct lofi_state *lsp, int otyp) 1947c478bd9Sstevel@tonic-gate { 1957c478bd9Sstevel@tonic-gate ASSERT(mutex_owned(&lofi_lock)); 1967c478bd9Sstevel@tonic-gate switch (otyp) { 1977c478bd9Sstevel@tonic-gate case OTYP_CHR: 1987c478bd9Sstevel@tonic-gate lsp->ls_chr_open = 0; 1997c478bd9Sstevel@tonic-gate break; 2007c478bd9Sstevel@tonic-gate case OTYP_BLK: 2017c478bd9Sstevel@tonic-gate lsp->ls_blk_open = 0; 2027c478bd9Sstevel@tonic-gate break; 2037c478bd9Sstevel@tonic-gate case OTYP_LYR: 2047c478bd9Sstevel@tonic-gate lsp->ls_lyr_open_count--; 2057c478bd9Sstevel@tonic-gate break; 2067c478bd9Sstevel@tonic-gate default: 2077c478bd9Sstevel@tonic-gate break; 2087c478bd9Sstevel@tonic-gate } 2097c478bd9Sstevel@tonic-gate } 2107c478bd9Sstevel@tonic-gate 2117c478bd9Sstevel@tonic-gate /*ARGSUSED3*/ 2127c478bd9Sstevel@tonic-gate static int 2137c478bd9Sstevel@tonic-gate lofi_open(dev_t *devp, int flag, int otyp, struct cred *credp) 2147c478bd9Sstevel@tonic-gate { 2157c478bd9Sstevel@tonic-gate minor_t minor; 2167c478bd9Sstevel@tonic-gate struct lofi_state *lsp; 2177c478bd9Sstevel@tonic-gate 2187c478bd9Sstevel@tonic-gate mutex_enter(&lofi_lock); 2197c478bd9Sstevel@tonic-gate minor = getminor(*devp); 2207c478bd9Sstevel@tonic-gate if (minor == 0) { 2217c478bd9Sstevel@tonic-gate /* master control device */ 2227c478bd9Sstevel@tonic-gate /* must be opened exclusively */ 2237c478bd9Sstevel@tonic-gate if (((flag & FEXCL) != FEXCL) || (otyp != OTYP_CHR)) { 2247c478bd9Sstevel@tonic-gate mutex_exit(&lofi_lock); 2257c478bd9Sstevel@tonic-gate return (EINVAL); 2267c478bd9Sstevel@tonic-gate } 2277c478bd9Sstevel@tonic-gate lsp = ddi_get_soft_state(lofi_statep, 0); 2287c478bd9Sstevel@tonic-gate if (lsp == NULL) { 2297c478bd9Sstevel@tonic-gate mutex_exit(&lofi_lock); 2307c478bd9Sstevel@tonic-gate return (ENXIO); 2317c478bd9Sstevel@tonic-gate } 2327c478bd9Sstevel@tonic-gate if (is_opened(lsp)) { 2337c478bd9Sstevel@tonic-gate mutex_exit(&lofi_lock); 2347c478bd9Sstevel@tonic-gate return (EBUSY); 2357c478bd9Sstevel@tonic-gate } 2367c478bd9Sstevel@tonic-gate (void) mark_opened(lsp, OTYP_CHR); 2377c478bd9Sstevel@tonic-gate mutex_exit(&lofi_lock); 2387c478bd9Sstevel@tonic-gate return (0); 2397c478bd9Sstevel@tonic-gate } 2407c478bd9Sstevel@tonic-gate 2417c478bd9Sstevel@tonic-gate /* otherwise, the mapping should already exist */ 2427c478bd9Sstevel@tonic-gate lsp = ddi_get_soft_state(lofi_statep, minor); 2437c478bd9Sstevel@tonic-gate if (lsp == NULL) { 2447c478bd9Sstevel@tonic-gate mutex_exit(&lofi_lock); 2457c478bd9Sstevel@tonic-gate return (EINVAL); 2467c478bd9Sstevel@tonic-gate } 2477c478bd9Sstevel@tonic-gate 2487c478bd9Sstevel@tonic-gate if (mark_opened(lsp, otyp) == -1) { 2497c478bd9Sstevel@tonic-gate mutex_exit(&lofi_lock); 2507c478bd9Sstevel@tonic-gate return (EINVAL); 2517c478bd9Sstevel@tonic-gate } 2527c478bd9Sstevel@tonic-gate 2537c478bd9Sstevel@tonic-gate mutex_exit(&lofi_lock); 2547c478bd9Sstevel@tonic-gate return (0); 2557c478bd9Sstevel@tonic-gate } 2567c478bd9Sstevel@tonic-gate 2577c478bd9Sstevel@tonic-gate /*ARGSUSED3*/ 2587c478bd9Sstevel@tonic-gate static int 2597c478bd9Sstevel@tonic-gate lofi_close(dev_t dev, int flag, int otyp, struct cred *credp) 2607c478bd9Sstevel@tonic-gate { 2617c478bd9Sstevel@tonic-gate minor_t minor; 2627c478bd9Sstevel@tonic-gate struct lofi_state *lsp; 2637c478bd9Sstevel@tonic-gate 2647c478bd9Sstevel@tonic-gate #ifdef lint 2657c478bd9Sstevel@tonic-gate flag = flag; 2667c478bd9Sstevel@tonic-gate #endif 2677c478bd9Sstevel@tonic-gate mutex_enter(&lofi_lock); 2687c478bd9Sstevel@tonic-gate minor = getminor(dev); 2697c478bd9Sstevel@tonic-gate lsp = ddi_get_soft_state(lofi_statep, minor); 2707c478bd9Sstevel@tonic-gate if (lsp == NULL) { 2717c478bd9Sstevel@tonic-gate mutex_exit(&lofi_lock); 2727c478bd9Sstevel@tonic-gate return (EINVAL); 2737c478bd9Sstevel@tonic-gate } 2747c478bd9Sstevel@tonic-gate mark_closed(lsp, otyp); 2757c478bd9Sstevel@tonic-gate mutex_exit(&lofi_lock); 2767c478bd9Sstevel@tonic-gate return (0); 2777c478bd9Sstevel@tonic-gate } 2787c478bd9Sstevel@tonic-gate 2797c478bd9Sstevel@tonic-gate /* 2807c478bd9Sstevel@tonic-gate * This is basically what strategy used to be before we found we 2817c478bd9Sstevel@tonic-gate * needed task queues. 2827c478bd9Sstevel@tonic-gate */ 2837c478bd9Sstevel@tonic-gate static void 2847c478bd9Sstevel@tonic-gate lofi_strategy_task(void *arg) 2857c478bd9Sstevel@tonic-gate { 2867c478bd9Sstevel@tonic-gate struct buf *bp = (struct buf *)arg; 2877c478bd9Sstevel@tonic-gate int error; 2887c478bd9Sstevel@tonic-gate struct lofi_state *lsp; 2897c478bd9Sstevel@tonic-gate offset_t offset, alignedoffset; 2907c478bd9Sstevel@tonic-gate offset_t mapoffset; 2917c478bd9Sstevel@tonic-gate caddr_t bufaddr; 2927c478bd9Sstevel@tonic-gate caddr_t mapaddr; 2937c478bd9Sstevel@tonic-gate size_t xfersize; 2947c478bd9Sstevel@tonic-gate size_t len; 2957c478bd9Sstevel@tonic-gate int isread; 2967c478bd9Sstevel@tonic-gate int smflags; 2977c478bd9Sstevel@tonic-gate enum seg_rw srw; 2987c478bd9Sstevel@tonic-gate 2997c478bd9Sstevel@tonic-gate lsp = ddi_get_soft_state(lofi_statep, getminor(bp->b_edev)); 3007c478bd9Sstevel@tonic-gate if (lsp->ls_kstat) { 3017c478bd9Sstevel@tonic-gate mutex_enter(lsp->ls_kstat->ks_lock); 3027c478bd9Sstevel@tonic-gate kstat_waitq_to_runq(KSTAT_IO_PTR(lsp->ls_kstat)); 3037c478bd9Sstevel@tonic-gate mutex_exit(lsp->ls_kstat->ks_lock); 3047c478bd9Sstevel@tonic-gate } 3057c478bd9Sstevel@tonic-gate bp_mapin(bp); 3067c478bd9Sstevel@tonic-gate bufaddr = bp->b_un.b_addr; 3077c478bd9Sstevel@tonic-gate offset = bp->b_lblkno * DEV_BSIZE; /* offset within file */ 3087c478bd9Sstevel@tonic-gate 3097c478bd9Sstevel@tonic-gate /* 3107c478bd9Sstevel@tonic-gate * We used to always use vn_rdwr here, but we cannot do that because 3117c478bd9Sstevel@tonic-gate * we might decide to read or write from the the underlying 3127c478bd9Sstevel@tonic-gate * file during this call, which would be a deadlock because 3137c478bd9Sstevel@tonic-gate * we have the rw_lock. So instead we page, unless it's not 3147c478bd9Sstevel@tonic-gate * mapable or it's a character device. 3157c478bd9Sstevel@tonic-gate */ 3167c478bd9Sstevel@tonic-gate if (((lsp->ls_vp->v_flag & VNOMAP) == 0) && 3177c478bd9Sstevel@tonic-gate (lsp->ls_vp->v_type != VCHR)) { 3187c478bd9Sstevel@tonic-gate /* 3197c478bd9Sstevel@tonic-gate * segmap always gives us an 8K (MAXBSIZE) chunk, aligned on 3207c478bd9Sstevel@tonic-gate * an 8K boundary, but the buf transfer address may not be 3217c478bd9Sstevel@tonic-gate * aligned on more than a 512-byte boundary (we don't 3227c478bd9Sstevel@tonic-gate * enforce that, though we could). This matters since the 3237c478bd9Sstevel@tonic-gate * initial part of the transfer may not start at offset 0 3247c478bd9Sstevel@tonic-gate * within the segmap'd chunk. So we have to compensate for 3257c478bd9Sstevel@tonic-gate * that with 'mapoffset'. Subsequent chunks always start 3267c478bd9Sstevel@tonic-gate * off at the beginning, and the last is capped by b_resid. 3277c478bd9Sstevel@tonic-gate */ 3287c478bd9Sstevel@tonic-gate mapoffset = offset & MAXBOFFSET; 3297c478bd9Sstevel@tonic-gate alignedoffset = offset - mapoffset; /* now map-aligned */ 3307c478bd9Sstevel@tonic-gate bp->b_resid = bp->b_bcount; 3317c478bd9Sstevel@tonic-gate isread = bp->b_flags & B_READ; 3327c478bd9Sstevel@tonic-gate srw = isread ? S_READ : S_WRITE; 3337c478bd9Sstevel@tonic-gate do { 3347c478bd9Sstevel@tonic-gate xfersize = MIN(lsp->ls_vp_size - offset, 3357c478bd9Sstevel@tonic-gate MIN(MAXBSIZE - mapoffset, bp->b_resid)); 3367c478bd9Sstevel@tonic-gate len = roundup(mapoffset + xfersize, PAGESIZE); 3377c478bd9Sstevel@tonic-gate mapaddr = segmap_getmapflt(segkmap, lsp->ls_vp, 3387c478bd9Sstevel@tonic-gate alignedoffset, MAXBSIZE, 1, srw); 3397c478bd9Sstevel@tonic-gate /* 3407c478bd9Sstevel@tonic-gate * Now fault in the pages. This lets us check 3417c478bd9Sstevel@tonic-gate * for errors before we reference mapaddr and 3427c478bd9Sstevel@tonic-gate * try to resolve the fault in bcopy (which would 3437c478bd9Sstevel@tonic-gate * panic instead). And this can easily happen, 3447c478bd9Sstevel@tonic-gate * particularly if you've lofi'd a file over NFS 3457c478bd9Sstevel@tonic-gate * and someone deletes the file on the server. 3467c478bd9Sstevel@tonic-gate */ 3477c478bd9Sstevel@tonic-gate error = segmap_fault(kas.a_hat, segkmap, mapaddr, 3487c478bd9Sstevel@tonic-gate len, F_SOFTLOCK, srw); 3497c478bd9Sstevel@tonic-gate if (error) { 3507c478bd9Sstevel@tonic-gate (void) segmap_release(segkmap, mapaddr, 0); 3517c478bd9Sstevel@tonic-gate if (FC_CODE(error) == FC_OBJERR) 3527c478bd9Sstevel@tonic-gate error = FC_ERRNO(error); 3537c478bd9Sstevel@tonic-gate else 3547c478bd9Sstevel@tonic-gate error = EIO; 3557c478bd9Sstevel@tonic-gate break; 3567c478bd9Sstevel@tonic-gate } 3577c478bd9Sstevel@tonic-gate smflags = 0; 3587c478bd9Sstevel@tonic-gate if (isread) { 3597c478bd9Sstevel@tonic-gate bcopy(mapaddr + mapoffset, bufaddr, xfersize); 3607c478bd9Sstevel@tonic-gate } else { 3617c478bd9Sstevel@tonic-gate smflags |= SM_WRITE; 3627c478bd9Sstevel@tonic-gate bcopy(bufaddr, mapaddr + mapoffset, xfersize); 3637c478bd9Sstevel@tonic-gate } 3647c478bd9Sstevel@tonic-gate bp->b_resid -= xfersize; 3657c478bd9Sstevel@tonic-gate bufaddr += xfersize; 3667c478bd9Sstevel@tonic-gate offset += xfersize; 3677c478bd9Sstevel@tonic-gate (void) segmap_fault(kas.a_hat, segkmap, mapaddr, 3687c478bd9Sstevel@tonic-gate len, F_SOFTUNLOCK, srw); 3697c478bd9Sstevel@tonic-gate error = segmap_release(segkmap, mapaddr, smflags); 3707c478bd9Sstevel@tonic-gate /* only the first map may start partial */ 3717c478bd9Sstevel@tonic-gate mapoffset = 0; 3727c478bd9Sstevel@tonic-gate alignedoffset += MAXBSIZE; 3737c478bd9Sstevel@tonic-gate } while ((error == 0) && (bp->b_resid > 0) && 3747c478bd9Sstevel@tonic-gate (offset < lsp->ls_vp_size)); 3757c478bd9Sstevel@tonic-gate } else { 3767c478bd9Sstevel@tonic-gate ssize_t resid; 3777c478bd9Sstevel@tonic-gate enum uio_rw rw; 3787c478bd9Sstevel@tonic-gate 3797c478bd9Sstevel@tonic-gate if (bp->b_flags & B_READ) 3807c478bd9Sstevel@tonic-gate rw = UIO_READ; 3817c478bd9Sstevel@tonic-gate else 3827c478bd9Sstevel@tonic-gate rw = UIO_WRITE; 3837c478bd9Sstevel@tonic-gate error = vn_rdwr(rw, lsp->ls_vp, bufaddr, bp->b_bcount, 3847c478bd9Sstevel@tonic-gate offset, UIO_SYSSPACE, 0, RLIM64_INFINITY, kcred, &resid); 3857c478bd9Sstevel@tonic-gate bp->b_resid = resid; 3867c478bd9Sstevel@tonic-gate } 3877c478bd9Sstevel@tonic-gate 3887c478bd9Sstevel@tonic-gate if (lsp->ls_kstat) { 3897c478bd9Sstevel@tonic-gate size_t n_done = bp->b_bcount - bp->b_resid; 3907c478bd9Sstevel@tonic-gate kstat_io_t *kioptr; 3917c478bd9Sstevel@tonic-gate 3927c478bd9Sstevel@tonic-gate mutex_enter(lsp->ls_kstat->ks_lock); 3937c478bd9Sstevel@tonic-gate kioptr = KSTAT_IO_PTR(lsp->ls_kstat); 3947c478bd9Sstevel@tonic-gate if (bp->b_flags & B_READ) { 3957c478bd9Sstevel@tonic-gate kioptr->nread += n_done; 3967c478bd9Sstevel@tonic-gate kioptr->reads++; 3977c478bd9Sstevel@tonic-gate } else { 3987c478bd9Sstevel@tonic-gate kioptr->nwritten += n_done; 3997c478bd9Sstevel@tonic-gate kioptr->writes++; 4007c478bd9Sstevel@tonic-gate } 4017c478bd9Sstevel@tonic-gate kstat_runq_exit(kioptr); 4027c478bd9Sstevel@tonic-gate mutex_exit(lsp->ls_kstat->ks_lock); 4037c478bd9Sstevel@tonic-gate } 4047c478bd9Sstevel@tonic-gate bioerror(bp, error); 4057c478bd9Sstevel@tonic-gate biodone(bp); 4067c478bd9Sstevel@tonic-gate } 4077c478bd9Sstevel@tonic-gate 4087c478bd9Sstevel@tonic-gate static int 4097c478bd9Sstevel@tonic-gate lofi_strategy(struct buf *bp) 4107c478bd9Sstevel@tonic-gate { 4117c478bd9Sstevel@tonic-gate struct lofi_state *lsp; 4127c478bd9Sstevel@tonic-gate offset_t offset; 4137c478bd9Sstevel@tonic-gate 4147c478bd9Sstevel@tonic-gate /* 4157c478bd9Sstevel@tonic-gate * We cannot just do I/O here, because the current thread 4167c478bd9Sstevel@tonic-gate * _might_ end up back in here because the underlying filesystem 4177c478bd9Sstevel@tonic-gate * wants a buffer, which eventually gets into bio_recycle and 4187c478bd9Sstevel@tonic-gate * might call into lofi to write out a delayed-write buffer. 4197c478bd9Sstevel@tonic-gate * This is bad if the filesystem above lofi is the same as below. 4207c478bd9Sstevel@tonic-gate * 4217c478bd9Sstevel@tonic-gate * We could come up with a complex strategy using threads to 4227c478bd9Sstevel@tonic-gate * do the I/O asynchronously, or we could use task queues. task 4237c478bd9Sstevel@tonic-gate * queues were incredibly easy so they win. 4247c478bd9Sstevel@tonic-gate */ 4257c478bd9Sstevel@tonic-gate lsp = ddi_get_soft_state(lofi_statep, getminor(bp->b_edev)); 4267c478bd9Sstevel@tonic-gate offset = bp->b_lblkno * DEV_BSIZE; /* offset within file */ 4277c478bd9Sstevel@tonic-gate if (offset == lsp->ls_vp_size) { 4287c478bd9Sstevel@tonic-gate /* EOF */ 4297c478bd9Sstevel@tonic-gate if ((bp->b_flags & B_READ) != 0) { 4307c478bd9Sstevel@tonic-gate bp->b_resid = bp->b_bcount; 4317c478bd9Sstevel@tonic-gate bioerror(bp, 0); 4327c478bd9Sstevel@tonic-gate } else { 4337c478bd9Sstevel@tonic-gate /* writes should fail */ 4347c478bd9Sstevel@tonic-gate bioerror(bp, ENXIO); 4357c478bd9Sstevel@tonic-gate } 4367c478bd9Sstevel@tonic-gate biodone(bp); 4377c478bd9Sstevel@tonic-gate return (0); 4387c478bd9Sstevel@tonic-gate } 4397c478bd9Sstevel@tonic-gate if (offset > lsp->ls_vp_size) { 4407c478bd9Sstevel@tonic-gate bioerror(bp, ENXIO); 4417c478bd9Sstevel@tonic-gate biodone(bp); 4427c478bd9Sstevel@tonic-gate return (0); 4437c478bd9Sstevel@tonic-gate } 4447c478bd9Sstevel@tonic-gate if (lsp->ls_kstat) { 4457c478bd9Sstevel@tonic-gate mutex_enter(lsp->ls_kstat->ks_lock); 4467c478bd9Sstevel@tonic-gate kstat_waitq_enter(KSTAT_IO_PTR(lsp->ls_kstat)); 4477c478bd9Sstevel@tonic-gate mutex_exit(lsp->ls_kstat->ks_lock); 4487c478bd9Sstevel@tonic-gate } 4497c478bd9Sstevel@tonic-gate (void) taskq_dispatch(lsp->ls_taskq, lofi_strategy_task, bp, KM_SLEEP); 4507c478bd9Sstevel@tonic-gate return (0); 4517c478bd9Sstevel@tonic-gate } 4527c478bd9Sstevel@tonic-gate 4537c478bd9Sstevel@tonic-gate /*ARGSUSED2*/ 4547c478bd9Sstevel@tonic-gate static int 4557c478bd9Sstevel@tonic-gate lofi_read(dev_t dev, struct uio *uio, struct cred *credp) 4567c478bd9Sstevel@tonic-gate { 4577c478bd9Sstevel@tonic-gate if (getminor(dev) == 0) 4587c478bd9Sstevel@tonic-gate return (EINVAL); 4597c478bd9Sstevel@tonic-gate return (physio(lofi_strategy, NULL, dev, B_READ, minphys, uio)); 4607c478bd9Sstevel@tonic-gate } 4617c478bd9Sstevel@tonic-gate 4627c478bd9Sstevel@tonic-gate /*ARGSUSED2*/ 4637c478bd9Sstevel@tonic-gate static int 4647c478bd9Sstevel@tonic-gate lofi_write(dev_t dev, struct uio *uio, struct cred *credp) 4657c478bd9Sstevel@tonic-gate { 4667c478bd9Sstevel@tonic-gate if (getminor(dev) == 0) 4677c478bd9Sstevel@tonic-gate return (EINVAL); 4687c478bd9Sstevel@tonic-gate return (physio(lofi_strategy, NULL, dev, B_WRITE, minphys, uio)); 4697c478bd9Sstevel@tonic-gate } 4707c478bd9Sstevel@tonic-gate 4717c478bd9Sstevel@tonic-gate /*ARGSUSED2*/ 4727c478bd9Sstevel@tonic-gate static int 4737c478bd9Sstevel@tonic-gate lofi_aread(dev_t dev, struct aio_req *aio, struct cred *credp) 4747c478bd9Sstevel@tonic-gate { 4757c478bd9Sstevel@tonic-gate if (getminor(dev) == 0) 4767c478bd9Sstevel@tonic-gate return (EINVAL); 4777c478bd9Sstevel@tonic-gate return (aphysio(lofi_strategy, anocancel, dev, B_READ, minphys, aio)); 4787c478bd9Sstevel@tonic-gate } 4797c478bd9Sstevel@tonic-gate 4807c478bd9Sstevel@tonic-gate /*ARGSUSED2*/ 4817c478bd9Sstevel@tonic-gate static int 4827c478bd9Sstevel@tonic-gate lofi_awrite(dev_t dev, struct aio_req *aio, struct cred *credp) 4837c478bd9Sstevel@tonic-gate { 4847c478bd9Sstevel@tonic-gate if (getminor(dev) == 0) 4857c478bd9Sstevel@tonic-gate return (EINVAL); 4867c478bd9Sstevel@tonic-gate return (aphysio(lofi_strategy, anocancel, dev, B_WRITE, minphys, aio)); 4877c478bd9Sstevel@tonic-gate } 4887c478bd9Sstevel@tonic-gate 4897c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 4907c478bd9Sstevel@tonic-gate static int 4917c478bd9Sstevel@tonic-gate lofi_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result) 4927c478bd9Sstevel@tonic-gate { 4937c478bd9Sstevel@tonic-gate switch (infocmd) { 4947c478bd9Sstevel@tonic-gate case DDI_INFO_DEVT2DEVINFO: 4957c478bd9Sstevel@tonic-gate *result = lofi_dip; 4967c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 4977c478bd9Sstevel@tonic-gate case DDI_INFO_DEVT2INSTANCE: 4987c478bd9Sstevel@tonic-gate *result = 0; 4997c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 5007c478bd9Sstevel@tonic-gate } 5017c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 5027c478bd9Sstevel@tonic-gate } 5037c478bd9Sstevel@tonic-gate 5047c478bd9Sstevel@tonic-gate static int 5057c478bd9Sstevel@tonic-gate lofi_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 5067c478bd9Sstevel@tonic-gate { 5077c478bd9Sstevel@tonic-gate int error; 5087c478bd9Sstevel@tonic-gate 5097c478bd9Sstevel@tonic-gate if (cmd != DDI_ATTACH) 5107c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 5117c478bd9Sstevel@tonic-gate error = ddi_soft_state_zalloc(lofi_statep, 0); 5127c478bd9Sstevel@tonic-gate if (error == DDI_FAILURE) { 5137c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 5147c478bd9Sstevel@tonic-gate } 5157c478bd9Sstevel@tonic-gate error = ddi_create_minor_node(dip, LOFI_CTL_NODE, S_IFCHR, 0, 5167c478bd9Sstevel@tonic-gate DDI_PSEUDO, NULL); 5177c478bd9Sstevel@tonic-gate if (error == DDI_FAILURE) { 5187c478bd9Sstevel@tonic-gate ddi_soft_state_free(lofi_statep, 0); 5197c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 5207c478bd9Sstevel@tonic-gate } 5217c478bd9Sstevel@tonic-gate lofi_dip = dip; 5227c478bd9Sstevel@tonic-gate ddi_report_dev(dip); 5237c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 5247c478bd9Sstevel@tonic-gate } 5257c478bd9Sstevel@tonic-gate 5267c478bd9Sstevel@tonic-gate static int 5277c478bd9Sstevel@tonic-gate lofi_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 5287c478bd9Sstevel@tonic-gate { 5297c478bd9Sstevel@tonic-gate if (cmd != DDI_DETACH) 5307c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 5317c478bd9Sstevel@tonic-gate if (lofi_busy()) 5327c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 5337c478bd9Sstevel@tonic-gate lofi_dip = NULL; 5347c478bd9Sstevel@tonic-gate ddi_remove_minor_node(dip, NULL); 5357c478bd9Sstevel@tonic-gate ddi_soft_state_free(lofi_statep, 0); 5367c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 5377c478bd9Sstevel@tonic-gate } 5387c478bd9Sstevel@tonic-gate 5397c478bd9Sstevel@tonic-gate /* 5407c478bd9Sstevel@tonic-gate * These two just simplify the rest of the ioctls that need to copyin/out 5417c478bd9Sstevel@tonic-gate * the lofi_ioctl structure. 5427c478bd9Sstevel@tonic-gate */ 5437c478bd9Sstevel@tonic-gate struct lofi_ioctl * 544*bd07e074Sheppo copy_in_lofi_ioctl(const struct lofi_ioctl *ulip, int flag) 5457c478bd9Sstevel@tonic-gate { 5467c478bd9Sstevel@tonic-gate struct lofi_ioctl *klip; 5477c478bd9Sstevel@tonic-gate int error; 5487c478bd9Sstevel@tonic-gate 5497c478bd9Sstevel@tonic-gate klip = kmem_alloc(sizeof (struct lofi_ioctl), KM_SLEEP); 550*bd07e074Sheppo error = ddi_copyin(ulip, klip, sizeof (struct lofi_ioctl), flag); 5517c478bd9Sstevel@tonic-gate if (error) { 5527c478bd9Sstevel@tonic-gate kmem_free(klip, sizeof (struct lofi_ioctl)); 5537c478bd9Sstevel@tonic-gate return (NULL); 5547c478bd9Sstevel@tonic-gate } 5557c478bd9Sstevel@tonic-gate 5567c478bd9Sstevel@tonic-gate /* make sure filename is always null-terminated */ 5577c478bd9Sstevel@tonic-gate klip->li_filename[MAXPATHLEN] = '\0'; 5587c478bd9Sstevel@tonic-gate 5597c478bd9Sstevel@tonic-gate /* validate minor number */ 5607c478bd9Sstevel@tonic-gate if (klip->li_minor > lofi_max_files) { 5617c478bd9Sstevel@tonic-gate kmem_free(klip, sizeof (struct lofi_ioctl)); 5627c478bd9Sstevel@tonic-gate return (NULL); 5637c478bd9Sstevel@tonic-gate } 5647c478bd9Sstevel@tonic-gate return (klip); 5657c478bd9Sstevel@tonic-gate } 5667c478bd9Sstevel@tonic-gate 5677c478bd9Sstevel@tonic-gate int 568*bd07e074Sheppo copy_out_lofi_ioctl(const struct lofi_ioctl *klip, struct lofi_ioctl *ulip, 569*bd07e074Sheppo int flag) 5707c478bd9Sstevel@tonic-gate { 5717c478bd9Sstevel@tonic-gate int error; 5727c478bd9Sstevel@tonic-gate 573*bd07e074Sheppo error = ddi_copyout(klip, ulip, sizeof (struct lofi_ioctl), flag); 5747c478bd9Sstevel@tonic-gate if (error) 5757c478bd9Sstevel@tonic-gate return (EFAULT); 5767c478bd9Sstevel@tonic-gate return (0); 5777c478bd9Sstevel@tonic-gate } 5787c478bd9Sstevel@tonic-gate 5797c478bd9Sstevel@tonic-gate void 5807c478bd9Sstevel@tonic-gate free_lofi_ioctl(struct lofi_ioctl *klip) 5817c478bd9Sstevel@tonic-gate { 5827c478bd9Sstevel@tonic-gate kmem_free(klip, sizeof (struct lofi_ioctl)); 5837c478bd9Sstevel@tonic-gate } 5847c478bd9Sstevel@tonic-gate 5857c478bd9Sstevel@tonic-gate /* 5867c478bd9Sstevel@tonic-gate * Return the minor number 'filename' is mapped to, if it is. 5877c478bd9Sstevel@tonic-gate */ 5887c478bd9Sstevel@tonic-gate static int 5897c478bd9Sstevel@tonic-gate file_to_minor(char *filename) 5907c478bd9Sstevel@tonic-gate { 5917c478bd9Sstevel@tonic-gate minor_t minor; 5927c478bd9Sstevel@tonic-gate struct lofi_state *lsp; 5937c478bd9Sstevel@tonic-gate 5947c478bd9Sstevel@tonic-gate ASSERT(mutex_owned(&lofi_lock)); 5957c478bd9Sstevel@tonic-gate for (minor = 1; minor <= lofi_max_files; minor++) { 5967c478bd9Sstevel@tonic-gate lsp = ddi_get_soft_state(lofi_statep, minor); 5977c478bd9Sstevel@tonic-gate if (lsp == NULL) 5987c478bd9Sstevel@tonic-gate continue; 5997c478bd9Sstevel@tonic-gate if (strcmp(lsp->ls_filename, filename) == 0) 6007c478bd9Sstevel@tonic-gate return (minor); 6017c478bd9Sstevel@tonic-gate } 6027c478bd9Sstevel@tonic-gate return (0); 6037c478bd9Sstevel@tonic-gate } 6047c478bd9Sstevel@tonic-gate 6057c478bd9Sstevel@tonic-gate /* 6067c478bd9Sstevel@tonic-gate * lofiadm does some validation, but since Joe Random (or crashme) could 6077c478bd9Sstevel@tonic-gate * do our ioctls, we need to do some validation too. 6087c478bd9Sstevel@tonic-gate */ 6097c478bd9Sstevel@tonic-gate static int 6107c478bd9Sstevel@tonic-gate valid_filename(const char *filename) 6117c478bd9Sstevel@tonic-gate { 6127c478bd9Sstevel@tonic-gate static char *blkprefix = "/dev/" LOFI_BLOCK_NAME "/"; 6137c478bd9Sstevel@tonic-gate static char *charprefix = "/dev/" LOFI_CHAR_NAME "/"; 6147c478bd9Sstevel@tonic-gate 6157c478bd9Sstevel@tonic-gate /* must be absolute path */ 6167c478bd9Sstevel@tonic-gate if (filename[0] != '/') 6177c478bd9Sstevel@tonic-gate return (0); 6187c478bd9Sstevel@tonic-gate /* must not be lofi */ 6197c478bd9Sstevel@tonic-gate if (strncmp(filename, blkprefix, strlen(blkprefix)) == 0) 6207c478bd9Sstevel@tonic-gate return (0); 6217c478bd9Sstevel@tonic-gate if (strncmp(filename, charprefix, strlen(charprefix)) == 0) 6227c478bd9Sstevel@tonic-gate return (0); 6237c478bd9Sstevel@tonic-gate return (1); 6247c478bd9Sstevel@tonic-gate } 6257c478bd9Sstevel@tonic-gate 6267c478bd9Sstevel@tonic-gate /* 6277c478bd9Sstevel@tonic-gate * Fakes up a disk geometry, and one big partition, based on the size 6287c478bd9Sstevel@tonic-gate * of the file. This is needed because we allow newfs'ing the device, 6297c478bd9Sstevel@tonic-gate * and newfs will do several disk ioctls to figure out the geometry and 6307c478bd9Sstevel@tonic-gate * partition information. It uses that information to determine the parameters 6317c478bd9Sstevel@tonic-gate * to pass to mkfs. Geometry is pretty much irrelevent these days, but we 6327c478bd9Sstevel@tonic-gate * have to support it. 6337c478bd9Sstevel@tonic-gate */ 6347c478bd9Sstevel@tonic-gate static void 6357c478bd9Sstevel@tonic-gate fake_disk_geometry(struct lofi_state *lsp) 6367c478bd9Sstevel@tonic-gate { 6377c478bd9Sstevel@tonic-gate /* dk_geom - see dkio(7I) */ 6387c478bd9Sstevel@tonic-gate /* 6397c478bd9Sstevel@tonic-gate * dkg_ncyl _could_ be set to one here (one big cylinder with gobs 6407c478bd9Sstevel@tonic-gate * of sectors), but that breaks programs like fdisk which want to 6417c478bd9Sstevel@tonic-gate * partition a disk by cylinder. With one cylinder, you can't create 6427c478bd9Sstevel@tonic-gate * an fdisk partition and put pcfs on it for testing (hard to pick 6437c478bd9Sstevel@tonic-gate * a number between one and one). 6447c478bd9Sstevel@tonic-gate * 6457c478bd9Sstevel@tonic-gate * The cheezy floppy test is an attempt to not have too few cylinders 6467c478bd9Sstevel@tonic-gate * for a small file, or so many on a big file that you waste space 6477c478bd9Sstevel@tonic-gate * for backup superblocks or cylinder group structures. 6487c478bd9Sstevel@tonic-gate */ 6497c478bd9Sstevel@tonic-gate if (lsp->ls_vp_size < (2 * 1024 * 1024)) /* floppy? */ 6507c478bd9Sstevel@tonic-gate lsp->ls_dkg.dkg_ncyl = lsp->ls_vp_size / (100 * 1024); 6517c478bd9Sstevel@tonic-gate else 6527c478bd9Sstevel@tonic-gate lsp->ls_dkg.dkg_ncyl = lsp->ls_vp_size / (300 * 1024); 6537c478bd9Sstevel@tonic-gate /* in case file file is < 100k */ 6547c478bd9Sstevel@tonic-gate if (lsp->ls_dkg.dkg_ncyl == 0) 6557c478bd9Sstevel@tonic-gate lsp->ls_dkg.dkg_ncyl = 1; 6567c478bd9Sstevel@tonic-gate lsp->ls_dkg.dkg_acyl = 0; 6577c478bd9Sstevel@tonic-gate lsp->ls_dkg.dkg_bcyl = 0; 6587c478bd9Sstevel@tonic-gate lsp->ls_dkg.dkg_nhead = 1; 6597c478bd9Sstevel@tonic-gate lsp->ls_dkg.dkg_obs1 = 0; 6607c478bd9Sstevel@tonic-gate lsp->ls_dkg.dkg_intrlv = 0; 6617c478bd9Sstevel@tonic-gate lsp->ls_dkg.dkg_obs2 = 0; 6627c478bd9Sstevel@tonic-gate lsp->ls_dkg.dkg_obs3 = 0; 6637c478bd9Sstevel@tonic-gate lsp->ls_dkg.dkg_apc = 0; 6647c478bd9Sstevel@tonic-gate lsp->ls_dkg.dkg_rpm = 7200; 6657c478bd9Sstevel@tonic-gate lsp->ls_dkg.dkg_pcyl = lsp->ls_dkg.dkg_ncyl + lsp->ls_dkg.dkg_acyl; 6667c478bd9Sstevel@tonic-gate lsp->ls_dkg.dkg_nsect = lsp->ls_vp_size / 6677c478bd9Sstevel@tonic-gate (DEV_BSIZE * lsp->ls_dkg.dkg_ncyl); 6687c478bd9Sstevel@tonic-gate lsp->ls_dkg.dkg_write_reinstruct = 0; 6697c478bd9Sstevel@tonic-gate lsp->ls_dkg.dkg_read_reinstruct = 0; 6707c478bd9Sstevel@tonic-gate 6717c478bd9Sstevel@tonic-gate /* vtoc - see dkio(7I) */ 6727c478bd9Sstevel@tonic-gate bzero(&lsp->ls_vtoc, sizeof (struct vtoc)); 6737c478bd9Sstevel@tonic-gate lsp->ls_vtoc.v_sanity = VTOC_SANE; 6747c478bd9Sstevel@tonic-gate lsp->ls_vtoc.v_version = V_VERSION; 6757c478bd9Sstevel@tonic-gate bcopy(LOFI_DRIVER_NAME, lsp->ls_vtoc.v_volume, 7); 6767c478bd9Sstevel@tonic-gate lsp->ls_vtoc.v_sectorsz = DEV_BSIZE; 6777c478bd9Sstevel@tonic-gate lsp->ls_vtoc.v_nparts = 1; 6787c478bd9Sstevel@tonic-gate lsp->ls_vtoc.v_part[0].p_tag = V_UNASSIGNED; 6797c478bd9Sstevel@tonic-gate lsp->ls_vtoc.v_part[0].p_flag = V_UNMNT; 6807c478bd9Sstevel@tonic-gate lsp->ls_vtoc.v_part[0].p_start = (daddr_t)0; 6817c478bd9Sstevel@tonic-gate /* 6827c478bd9Sstevel@tonic-gate * The partition size cannot just be the number of sectors, because 6837c478bd9Sstevel@tonic-gate * that might not end on a cylinder boundary. And if that's the case, 6847c478bd9Sstevel@tonic-gate * newfs/mkfs will print a scary warning. So just figure the size 6857c478bd9Sstevel@tonic-gate * based on the number of cylinders and sectors/cylinder. 6867c478bd9Sstevel@tonic-gate */ 6877c478bd9Sstevel@tonic-gate lsp->ls_vtoc.v_part[0].p_size = lsp->ls_dkg.dkg_pcyl * 6887c478bd9Sstevel@tonic-gate lsp->ls_dkg.dkg_nsect * lsp->ls_dkg.dkg_nhead; 6897c478bd9Sstevel@tonic-gate 6907c478bd9Sstevel@tonic-gate /* dk_cinfo - see dkio(7I) */ 6917c478bd9Sstevel@tonic-gate bzero(&lsp->ls_ci, sizeof (struct dk_cinfo)); 6927c478bd9Sstevel@tonic-gate (void) strcpy(lsp->ls_ci.dki_cname, LOFI_DRIVER_NAME); 6937c478bd9Sstevel@tonic-gate lsp->ls_ci.dki_ctype = DKC_MD; 6947c478bd9Sstevel@tonic-gate lsp->ls_ci.dki_flags = 0; 6957c478bd9Sstevel@tonic-gate lsp->ls_ci.dki_cnum = 0; 6967c478bd9Sstevel@tonic-gate lsp->ls_ci.dki_addr = 0; 6977c478bd9Sstevel@tonic-gate lsp->ls_ci.dki_space = 0; 6987c478bd9Sstevel@tonic-gate lsp->ls_ci.dki_prio = 0; 6997c478bd9Sstevel@tonic-gate lsp->ls_ci.dki_vec = 0; 7007c478bd9Sstevel@tonic-gate (void) strcpy(lsp->ls_ci.dki_dname, LOFI_DRIVER_NAME); 7017c478bd9Sstevel@tonic-gate lsp->ls_ci.dki_unit = 0; 7027c478bd9Sstevel@tonic-gate lsp->ls_ci.dki_slave = 0; 7037c478bd9Sstevel@tonic-gate lsp->ls_ci.dki_partition = 0; 7047c478bd9Sstevel@tonic-gate /* 7057c478bd9Sstevel@tonic-gate * newfs uses this to set maxcontig. Must not be < 16, or it 7067c478bd9Sstevel@tonic-gate * will be 0 when newfs multiplies it by DEV_BSIZE and divides 7077c478bd9Sstevel@tonic-gate * it by the block size. Then tunefs doesn't work because 7087c478bd9Sstevel@tonic-gate * maxcontig is 0. 7097c478bd9Sstevel@tonic-gate */ 7107c478bd9Sstevel@tonic-gate lsp->ls_ci.dki_maxtransfer = 16; 7117c478bd9Sstevel@tonic-gate } 7127c478bd9Sstevel@tonic-gate 7137c478bd9Sstevel@tonic-gate /* 7147c478bd9Sstevel@tonic-gate * map a file to a minor number. Return the minor number. 7157c478bd9Sstevel@tonic-gate */ 7167c478bd9Sstevel@tonic-gate static int 7177c478bd9Sstevel@tonic-gate lofi_map_file(dev_t dev, struct lofi_ioctl *ulip, int pickminor, 718*bd07e074Sheppo int *rvalp, struct cred *credp, int ioctl_flag) 7197c478bd9Sstevel@tonic-gate { 7207c478bd9Sstevel@tonic-gate minor_t newminor; 7217c478bd9Sstevel@tonic-gate struct lofi_state *lsp; 7227c478bd9Sstevel@tonic-gate struct lofi_ioctl *klip; 7237c478bd9Sstevel@tonic-gate int error; 7247c478bd9Sstevel@tonic-gate char namebuf[50]; 7257c478bd9Sstevel@tonic-gate struct vnode *vp; 7267c478bd9Sstevel@tonic-gate int64_t Nblocks_prop_val; 7277c478bd9Sstevel@tonic-gate int64_t Size_prop_val; 7287c478bd9Sstevel@tonic-gate vattr_t vattr; 7297c478bd9Sstevel@tonic-gate int flag; 7307c478bd9Sstevel@tonic-gate enum vtype v_type; 7317c478bd9Sstevel@tonic-gate dev_t newdev; 7327c478bd9Sstevel@tonic-gate int zalloced = 0; 7337c478bd9Sstevel@tonic-gate 734*bd07e074Sheppo klip = copy_in_lofi_ioctl(ulip, ioctl_flag); 7357c478bd9Sstevel@tonic-gate if (klip == NULL) 7367c478bd9Sstevel@tonic-gate return (EFAULT); 7377c478bd9Sstevel@tonic-gate 7387c478bd9Sstevel@tonic-gate mutex_enter(&lofi_lock); 7397c478bd9Sstevel@tonic-gate 7407c478bd9Sstevel@tonic-gate if (!valid_filename(klip->li_filename)) { 7417c478bd9Sstevel@tonic-gate error = EINVAL; 7427c478bd9Sstevel@tonic-gate goto out; 7437c478bd9Sstevel@tonic-gate } 7447c478bd9Sstevel@tonic-gate 7457c478bd9Sstevel@tonic-gate if (file_to_minor(klip->li_filename) != 0) { 7467c478bd9Sstevel@tonic-gate error = EBUSY; 7477c478bd9Sstevel@tonic-gate goto out; 7487c478bd9Sstevel@tonic-gate } 7497c478bd9Sstevel@tonic-gate 7507c478bd9Sstevel@tonic-gate if (pickminor) { 7517c478bd9Sstevel@tonic-gate /* Find a free one */ 7527c478bd9Sstevel@tonic-gate for (newminor = 1; newminor <= lofi_max_files; newminor++) 7537c478bd9Sstevel@tonic-gate if (ddi_get_soft_state(lofi_statep, newminor) == NULL) 7547c478bd9Sstevel@tonic-gate break; 7557c478bd9Sstevel@tonic-gate if (newminor >= lofi_max_files) { 7567c478bd9Sstevel@tonic-gate error = EAGAIN; 7577c478bd9Sstevel@tonic-gate goto out; 7587c478bd9Sstevel@tonic-gate } 7597c478bd9Sstevel@tonic-gate } else { 7607c478bd9Sstevel@tonic-gate newminor = klip->li_minor; 7617c478bd9Sstevel@tonic-gate if (ddi_get_soft_state(lofi_statep, newminor) != NULL) { 7627c478bd9Sstevel@tonic-gate error = EEXIST; 7637c478bd9Sstevel@tonic-gate goto out; 7647c478bd9Sstevel@tonic-gate } 7657c478bd9Sstevel@tonic-gate } 7667c478bd9Sstevel@tonic-gate 7677c478bd9Sstevel@tonic-gate /* make sure it's valid */ 7687c478bd9Sstevel@tonic-gate error = lookupname(klip->li_filename, UIO_SYSSPACE, FOLLOW, 7697c478bd9Sstevel@tonic-gate NULLVPP, &vp); 7707c478bd9Sstevel@tonic-gate if (error) { 7717c478bd9Sstevel@tonic-gate goto out; 7727c478bd9Sstevel@tonic-gate } 7737c478bd9Sstevel@tonic-gate v_type = vp->v_type; 7747c478bd9Sstevel@tonic-gate VN_RELE(vp); 7757c478bd9Sstevel@tonic-gate if (!V_ISLOFIABLE(v_type)) { 7767c478bd9Sstevel@tonic-gate error = EINVAL; 7777c478bd9Sstevel@tonic-gate goto out; 7787c478bd9Sstevel@tonic-gate } 7797c478bd9Sstevel@tonic-gate flag = FREAD | FWRITE | FOFFMAX | FEXCL; 7807c478bd9Sstevel@tonic-gate error = vn_open(klip->li_filename, UIO_SYSSPACE, flag, 0, &vp, 0, 0); 7817c478bd9Sstevel@tonic-gate if (error) { 7827c478bd9Sstevel@tonic-gate /* try read-only */ 7837c478bd9Sstevel@tonic-gate flag &= ~FWRITE; 7847c478bd9Sstevel@tonic-gate error = vn_open(klip->li_filename, UIO_SYSSPACE, flag, 0, 7857c478bd9Sstevel@tonic-gate &vp, 0, 0); 7867c478bd9Sstevel@tonic-gate if (error) { 7877c478bd9Sstevel@tonic-gate goto out; 7887c478bd9Sstevel@tonic-gate } 7897c478bd9Sstevel@tonic-gate } 7907c478bd9Sstevel@tonic-gate vattr.va_mask = AT_SIZE; 7917c478bd9Sstevel@tonic-gate error = VOP_GETATTR(vp, &vattr, 0, credp); 7927c478bd9Sstevel@tonic-gate if (error) { 7937c478bd9Sstevel@tonic-gate goto closeout; 7947c478bd9Sstevel@tonic-gate } 7957c478bd9Sstevel@tonic-gate /* the file needs to be a multiple of the block size */ 7967c478bd9Sstevel@tonic-gate if ((vattr.va_size % DEV_BSIZE) != 0) { 7977c478bd9Sstevel@tonic-gate error = EINVAL; 7987c478bd9Sstevel@tonic-gate goto closeout; 7997c478bd9Sstevel@tonic-gate } 8007c478bd9Sstevel@tonic-gate newdev = makedevice(getmajor(dev), newminor); 8017c478bd9Sstevel@tonic-gate Size_prop_val = vattr.va_size; 8027c478bd9Sstevel@tonic-gate if ((ddi_prop_update_int64(newdev, lofi_dip, 8037c478bd9Sstevel@tonic-gate SIZE_PROP_NAME, Size_prop_val)) != DDI_PROP_SUCCESS) { 8047c478bd9Sstevel@tonic-gate error = EINVAL; 8057c478bd9Sstevel@tonic-gate goto closeout; 8067c478bd9Sstevel@tonic-gate } 8077c478bd9Sstevel@tonic-gate Nblocks_prop_val = vattr.va_size / DEV_BSIZE; 8087c478bd9Sstevel@tonic-gate if ((ddi_prop_update_int64(newdev, lofi_dip, 8097c478bd9Sstevel@tonic-gate NBLOCKS_PROP_NAME, Nblocks_prop_val)) != DDI_PROP_SUCCESS) { 8107c478bd9Sstevel@tonic-gate error = EINVAL; 8117c478bd9Sstevel@tonic-gate goto propout; 8127c478bd9Sstevel@tonic-gate } 8137c478bd9Sstevel@tonic-gate error = ddi_soft_state_zalloc(lofi_statep, newminor); 8147c478bd9Sstevel@tonic-gate if (error == DDI_FAILURE) { 8157c478bd9Sstevel@tonic-gate error = ENOMEM; 8167c478bd9Sstevel@tonic-gate goto propout; 8177c478bd9Sstevel@tonic-gate } 8187c478bd9Sstevel@tonic-gate zalloced = 1; 8197c478bd9Sstevel@tonic-gate (void) snprintf(namebuf, sizeof (namebuf), "%d", newminor); 8207c478bd9Sstevel@tonic-gate (void) ddi_create_minor_node(lofi_dip, namebuf, S_IFBLK, newminor, 8217c478bd9Sstevel@tonic-gate DDI_PSEUDO, NULL); 8227c478bd9Sstevel@tonic-gate if (error != DDI_SUCCESS) { 8237c478bd9Sstevel@tonic-gate error = ENXIO; 8247c478bd9Sstevel@tonic-gate goto propout; 8257c478bd9Sstevel@tonic-gate } 8267c478bd9Sstevel@tonic-gate (void) snprintf(namebuf, sizeof (namebuf), "%d,raw", newminor); 8277c478bd9Sstevel@tonic-gate error = ddi_create_minor_node(lofi_dip, namebuf, S_IFCHR, newminor, 8287c478bd9Sstevel@tonic-gate DDI_PSEUDO, NULL); 8297c478bd9Sstevel@tonic-gate if (error != DDI_SUCCESS) { 8307c478bd9Sstevel@tonic-gate /* remove block node */ 8317c478bd9Sstevel@tonic-gate (void) snprintf(namebuf, sizeof (namebuf), "%d", newminor); 8327c478bd9Sstevel@tonic-gate ddi_remove_minor_node(lofi_dip, namebuf); 8337c478bd9Sstevel@tonic-gate error = ENXIO; 8347c478bd9Sstevel@tonic-gate goto propout; 8357c478bd9Sstevel@tonic-gate } 8367c478bd9Sstevel@tonic-gate lsp = ddi_get_soft_state(lofi_statep, newminor); 8377c478bd9Sstevel@tonic-gate lsp->ls_filename_sz = strlen(klip->li_filename) + 1; 8387c478bd9Sstevel@tonic-gate lsp->ls_filename = kmem_alloc(lsp->ls_filename_sz, KM_SLEEP); 8397c478bd9Sstevel@tonic-gate (void) snprintf(namebuf, sizeof (namebuf), "%s_taskq_%d", 8407c478bd9Sstevel@tonic-gate LOFI_DRIVER_NAME, newminor); 8417c478bd9Sstevel@tonic-gate lsp->ls_taskq = taskq_create(namebuf, lofi_taskq_nthreads, 8427c478bd9Sstevel@tonic-gate minclsyspri, 1, lofi_taskq_maxalloc, 0); 8437c478bd9Sstevel@tonic-gate lsp->ls_kstat = kstat_create(LOFI_DRIVER_NAME, newminor, 8447c478bd9Sstevel@tonic-gate NULL, "disk", KSTAT_TYPE_IO, 1, 0); 8457c478bd9Sstevel@tonic-gate if (lsp->ls_kstat) { 8467c478bd9Sstevel@tonic-gate mutex_init(&lsp->ls_kstat_lock, NULL, MUTEX_DRIVER, NULL); 8477c478bd9Sstevel@tonic-gate lsp->ls_kstat->ks_lock = &lsp->ls_kstat_lock; 8487c478bd9Sstevel@tonic-gate kstat_install(lsp->ls_kstat); 8497c478bd9Sstevel@tonic-gate } 8507c478bd9Sstevel@tonic-gate /* 8517c478bd9Sstevel@tonic-gate * save open mode so file can be closed properly and vnode counts 8527c478bd9Sstevel@tonic-gate * updated correctly. 8537c478bd9Sstevel@tonic-gate */ 8547c478bd9Sstevel@tonic-gate lsp->ls_openflag = flag; 8557c478bd9Sstevel@tonic-gate 8567c478bd9Sstevel@tonic-gate /* 8577c478bd9Sstevel@tonic-gate * Try to handle stacked lofs vnodes. 8587c478bd9Sstevel@tonic-gate */ 8597c478bd9Sstevel@tonic-gate if (vp->v_type == VREG) { 8607c478bd9Sstevel@tonic-gate if (VOP_REALVP(vp, &lsp->ls_vp) != 0) { 8617c478bd9Sstevel@tonic-gate lsp->ls_vp = vp; 8627c478bd9Sstevel@tonic-gate } else { 8637c478bd9Sstevel@tonic-gate /* 8647c478bd9Sstevel@tonic-gate * Even though vp was obtained via vn_open(), we 8657c478bd9Sstevel@tonic-gate * can't call vn_close() on it, since lofs will 8667c478bd9Sstevel@tonic-gate * pass the VOP_CLOSE() on down to the realvp 8677c478bd9Sstevel@tonic-gate * (which we are about to use). Hence we merely 8687c478bd9Sstevel@tonic-gate * drop the reference to the lofs vnode and hold 8697c478bd9Sstevel@tonic-gate * the realvp so things behave as if we've 8707c478bd9Sstevel@tonic-gate * opened the realvp without any interaction 8717c478bd9Sstevel@tonic-gate * with lofs. 8727c478bd9Sstevel@tonic-gate */ 8737c478bd9Sstevel@tonic-gate VN_HOLD(lsp->ls_vp); 8747c478bd9Sstevel@tonic-gate VN_RELE(vp); 8757c478bd9Sstevel@tonic-gate } 8767c478bd9Sstevel@tonic-gate } else { 8777c478bd9Sstevel@tonic-gate lsp->ls_vp = vp; 8787c478bd9Sstevel@tonic-gate } 8797c478bd9Sstevel@tonic-gate lsp->ls_vp_size = vattr.va_size; 8807c478bd9Sstevel@tonic-gate (void) strcpy(lsp->ls_filename, klip->li_filename); 8817c478bd9Sstevel@tonic-gate if (rvalp) 8827c478bd9Sstevel@tonic-gate *rvalp = (int)newminor; 8837c478bd9Sstevel@tonic-gate klip->li_minor = newminor; 8847c478bd9Sstevel@tonic-gate 8857c478bd9Sstevel@tonic-gate fake_disk_geometry(lsp); 8867c478bd9Sstevel@tonic-gate mutex_exit(&lofi_lock); 887*bd07e074Sheppo (void) copy_out_lofi_ioctl(klip, ulip, ioctl_flag); 8887c478bd9Sstevel@tonic-gate free_lofi_ioctl(klip); 8897c478bd9Sstevel@tonic-gate return (0); 8907c478bd9Sstevel@tonic-gate 8917c478bd9Sstevel@tonic-gate propout: 8927c478bd9Sstevel@tonic-gate (void) ddi_prop_remove(newdev, lofi_dip, SIZE_PROP_NAME); 8937c478bd9Sstevel@tonic-gate (void) ddi_prop_remove(newdev, lofi_dip, NBLOCKS_PROP_NAME); 8947c478bd9Sstevel@tonic-gate closeout: 8957c478bd9Sstevel@tonic-gate (void) VOP_CLOSE(vp, flag, 1, 0, credp); 8967c478bd9Sstevel@tonic-gate VN_RELE(vp); 8977c478bd9Sstevel@tonic-gate out: 8987c478bd9Sstevel@tonic-gate if (zalloced) 8997c478bd9Sstevel@tonic-gate ddi_soft_state_free(lofi_statep, newminor); 9007c478bd9Sstevel@tonic-gate mutex_exit(&lofi_lock); 9017c478bd9Sstevel@tonic-gate free_lofi_ioctl(klip); 9027c478bd9Sstevel@tonic-gate return (error); 9037c478bd9Sstevel@tonic-gate } 9047c478bd9Sstevel@tonic-gate 9057c478bd9Sstevel@tonic-gate /* 9067c478bd9Sstevel@tonic-gate * unmap a file. 9077c478bd9Sstevel@tonic-gate */ 9087c478bd9Sstevel@tonic-gate static int 9097c478bd9Sstevel@tonic-gate lofi_unmap_file(dev_t dev, struct lofi_ioctl *ulip, int byfilename, 910*bd07e074Sheppo struct cred *credp, int ioctl_flag) 9117c478bd9Sstevel@tonic-gate { 9127c478bd9Sstevel@tonic-gate struct lofi_state *lsp; 9137c478bd9Sstevel@tonic-gate struct lofi_ioctl *klip; 9147c478bd9Sstevel@tonic-gate minor_t minor; 9157c478bd9Sstevel@tonic-gate char namebuf[20]; 9167c478bd9Sstevel@tonic-gate dev_t newdev; 9177c478bd9Sstevel@tonic-gate 918*bd07e074Sheppo klip = copy_in_lofi_ioctl(ulip, ioctl_flag); 9197c478bd9Sstevel@tonic-gate if (klip == NULL) 9207c478bd9Sstevel@tonic-gate return (EFAULT); 9217c478bd9Sstevel@tonic-gate 9227c478bd9Sstevel@tonic-gate mutex_enter(&lofi_lock); 9237c478bd9Sstevel@tonic-gate if (byfilename) { 9247c478bd9Sstevel@tonic-gate minor = file_to_minor(klip->li_filename); 9257c478bd9Sstevel@tonic-gate } else { 9267c478bd9Sstevel@tonic-gate minor = klip->li_minor; 9277c478bd9Sstevel@tonic-gate } 9287c478bd9Sstevel@tonic-gate if (minor == 0) { 9297c478bd9Sstevel@tonic-gate mutex_exit(&lofi_lock); 9307c478bd9Sstevel@tonic-gate free_lofi_ioctl(klip); 9317c478bd9Sstevel@tonic-gate return (ENXIO); 9327c478bd9Sstevel@tonic-gate } 9337c478bd9Sstevel@tonic-gate lsp = ddi_get_soft_state(lofi_statep, minor); 9347c478bd9Sstevel@tonic-gate if (lsp == NULL) { 9357c478bd9Sstevel@tonic-gate mutex_exit(&lofi_lock); 9367c478bd9Sstevel@tonic-gate free_lofi_ioctl(klip); 9377c478bd9Sstevel@tonic-gate return (ENXIO); 9387c478bd9Sstevel@tonic-gate } 9397c478bd9Sstevel@tonic-gate if (is_opened(lsp)) { 9407c478bd9Sstevel@tonic-gate mutex_exit(&lofi_lock); 9417c478bd9Sstevel@tonic-gate free_lofi_ioctl(klip); 9427c478bd9Sstevel@tonic-gate return (EBUSY); 9437c478bd9Sstevel@tonic-gate } 9447c478bd9Sstevel@tonic-gate /* 9457c478bd9Sstevel@tonic-gate * Use saved open mode to properly update vnode counts 9467c478bd9Sstevel@tonic-gate */ 9477c478bd9Sstevel@tonic-gate (void) VOP_CLOSE(lsp->ls_vp, lsp->ls_openflag, 1, 0, credp); 9487c478bd9Sstevel@tonic-gate VN_RELE(lsp->ls_vp); 9497c478bd9Sstevel@tonic-gate lsp->ls_vp = NULL; 9507c478bd9Sstevel@tonic-gate newdev = makedevice(getmajor(dev), minor); 9517c478bd9Sstevel@tonic-gate (void) ddi_prop_remove(newdev, lofi_dip, SIZE_PROP_NAME); 9527c478bd9Sstevel@tonic-gate (void) ddi_prop_remove(newdev, lofi_dip, NBLOCKS_PROP_NAME); 9537c478bd9Sstevel@tonic-gate 9547c478bd9Sstevel@tonic-gate (void) snprintf(namebuf, sizeof (namebuf), "%d", minor); 9557c478bd9Sstevel@tonic-gate ddi_remove_minor_node(lofi_dip, namebuf); 9567c478bd9Sstevel@tonic-gate (void) snprintf(namebuf, sizeof (namebuf), "%d,raw", minor); 9577c478bd9Sstevel@tonic-gate ddi_remove_minor_node(lofi_dip, namebuf); 9587c478bd9Sstevel@tonic-gate 9597c478bd9Sstevel@tonic-gate kmem_free(lsp->ls_filename, lsp->ls_filename_sz); 9607c478bd9Sstevel@tonic-gate taskq_destroy(lsp->ls_taskq); 9617c478bd9Sstevel@tonic-gate if (lsp->ls_kstat) { 9627c478bd9Sstevel@tonic-gate kstat_delete(lsp->ls_kstat); 9637c478bd9Sstevel@tonic-gate mutex_destroy(&lsp->ls_kstat_lock); 9647c478bd9Sstevel@tonic-gate } 9657c478bd9Sstevel@tonic-gate ddi_soft_state_free(lofi_statep, minor); 9667c478bd9Sstevel@tonic-gate klip->li_minor = minor; 9677c478bd9Sstevel@tonic-gate mutex_exit(&lofi_lock); 968*bd07e074Sheppo (void) copy_out_lofi_ioctl(klip, ulip, ioctl_flag); 9697c478bd9Sstevel@tonic-gate free_lofi_ioctl(klip); 9707c478bd9Sstevel@tonic-gate return (0); 9717c478bd9Sstevel@tonic-gate } 9727c478bd9Sstevel@tonic-gate 9737c478bd9Sstevel@tonic-gate /* 9747c478bd9Sstevel@tonic-gate * get the filename given the minor number, or the minor number given 9757c478bd9Sstevel@tonic-gate * the name. 9767c478bd9Sstevel@tonic-gate */ 9777c478bd9Sstevel@tonic-gate /*ARGSUSED3*/ 9787c478bd9Sstevel@tonic-gate static int 9797c478bd9Sstevel@tonic-gate lofi_get_info(dev_t dev, struct lofi_ioctl *ulip, int which, 980*bd07e074Sheppo struct cred *credp, int ioctl_flag) 9817c478bd9Sstevel@tonic-gate { 9827c478bd9Sstevel@tonic-gate struct lofi_state *lsp; 9837c478bd9Sstevel@tonic-gate struct lofi_ioctl *klip; 9847c478bd9Sstevel@tonic-gate int error; 9857c478bd9Sstevel@tonic-gate minor_t minor; 9867c478bd9Sstevel@tonic-gate 9877c478bd9Sstevel@tonic-gate #ifdef lint 9887c478bd9Sstevel@tonic-gate dev = dev; 9897c478bd9Sstevel@tonic-gate #endif 990*bd07e074Sheppo klip = copy_in_lofi_ioctl(ulip, ioctl_flag); 9917c478bd9Sstevel@tonic-gate if (klip == NULL) 9927c478bd9Sstevel@tonic-gate return (EFAULT); 9937c478bd9Sstevel@tonic-gate 9947c478bd9Sstevel@tonic-gate switch (which) { 9957c478bd9Sstevel@tonic-gate case LOFI_GET_FILENAME: 9967c478bd9Sstevel@tonic-gate minor = klip->li_minor; 9977c478bd9Sstevel@tonic-gate if (minor == 0) { 9987c478bd9Sstevel@tonic-gate free_lofi_ioctl(klip); 9997c478bd9Sstevel@tonic-gate return (EINVAL); 10007c478bd9Sstevel@tonic-gate } 10017c478bd9Sstevel@tonic-gate 10027c478bd9Sstevel@tonic-gate mutex_enter(&lofi_lock); 10037c478bd9Sstevel@tonic-gate lsp = ddi_get_soft_state(lofi_statep, minor); 10047c478bd9Sstevel@tonic-gate if (lsp == NULL) { 10057c478bd9Sstevel@tonic-gate mutex_exit(&lofi_lock); 10067c478bd9Sstevel@tonic-gate free_lofi_ioctl(klip); 10077c478bd9Sstevel@tonic-gate return (ENXIO); 10087c478bd9Sstevel@tonic-gate } 10097c478bd9Sstevel@tonic-gate (void) strcpy(klip->li_filename, lsp->ls_filename); 10107c478bd9Sstevel@tonic-gate mutex_exit(&lofi_lock); 1011*bd07e074Sheppo error = copy_out_lofi_ioctl(klip, ulip, ioctl_flag); 10127c478bd9Sstevel@tonic-gate free_lofi_ioctl(klip); 10137c478bd9Sstevel@tonic-gate return (error); 10147c478bd9Sstevel@tonic-gate case LOFI_GET_MINOR: 10157c478bd9Sstevel@tonic-gate mutex_enter(&lofi_lock); 10167c478bd9Sstevel@tonic-gate klip->li_minor = file_to_minor(klip->li_filename); 10177c478bd9Sstevel@tonic-gate mutex_exit(&lofi_lock); 10187c478bd9Sstevel@tonic-gate if (klip->li_minor == 0) { 10197c478bd9Sstevel@tonic-gate free_lofi_ioctl(klip); 10207c478bd9Sstevel@tonic-gate return (ENOENT); 10217c478bd9Sstevel@tonic-gate } 1022*bd07e074Sheppo error = copy_out_lofi_ioctl(klip, ulip, ioctl_flag); 10237c478bd9Sstevel@tonic-gate free_lofi_ioctl(klip); 10247c478bd9Sstevel@tonic-gate return (error); 10257c478bd9Sstevel@tonic-gate default: 10267c478bd9Sstevel@tonic-gate free_lofi_ioctl(klip); 10277c478bd9Sstevel@tonic-gate return (EINVAL); 10287c478bd9Sstevel@tonic-gate } 10297c478bd9Sstevel@tonic-gate 10307c478bd9Sstevel@tonic-gate } 10317c478bd9Sstevel@tonic-gate 10327c478bd9Sstevel@tonic-gate static int 10337c478bd9Sstevel@tonic-gate lofi_ioctl(dev_t dev, int cmd, intptr_t arg, int flag, cred_t *credp, 10347c478bd9Sstevel@tonic-gate int *rvalp) 10357c478bd9Sstevel@tonic-gate { 10367c478bd9Sstevel@tonic-gate int error; 10377c478bd9Sstevel@tonic-gate enum dkio_state dkstate; 10387c478bd9Sstevel@tonic-gate struct lofi_state *lsp; 10397c478bd9Sstevel@tonic-gate minor_t minor; 10407c478bd9Sstevel@tonic-gate 10417c478bd9Sstevel@tonic-gate #ifdef lint 10427c478bd9Sstevel@tonic-gate credp = credp; 10437c478bd9Sstevel@tonic-gate #endif 10447c478bd9Sstevel@tonic-gate 10457c478bd9Sstevel@tonic-gate minor = getminor(dev); 10467c478bd9Sstevel@tonic-gate /* lofi ioctls only apply to the master device */ 10477c478bd9Sstevel@tonic-gate if (minor == 0) { 10487c478bd9Sstevel@tonic-gate struct lofi_ioctl *lip = (struct lofi_ioctl *)arg; 10497c478bd9Sstevel@tonic-gate 10507c478bd9Sstevel@tonic-gate /* 10517c478bd9Sstevel@tonic-gate * the query command only need read-access - i.e., normal 10527c478bd9Sstevel@tonic-gate * users are allowed to do those on the ctl device as 10537c478bd9Sstevel@tonic-gate * long as they can open it read-only. 10547c478bd9Sstevel@tonic-gate */ 10557c478bd9Sstevel@tonic-gate switch (cmd) { 10567c478bd9Sstevel@tonic-gate case LOFI_MAP_FILE: 10577c478bd9Sstevel@tonic-gate if ((flag & FWRITE) == 0) 10587c478bd9Sstevel@tonic-gate return (EPERM); 1059*bd07e074Sheppo return (lofi_map_file(dev, lip, 1, rvalp, credp, flag)); 10607c478bd9Sstevel@tonic-gate case LOFI_MAP_FILE_MINOR: 10617c478bd9Sstevel@tonic-gate if ((flag & FWRITE) == 0) 10627c478bd9Sstevel@tonic-gate return (EPERM); 1063*bd07e074Sheppo return (lofi_map_file(dev, lip, 0, rvalp, credp, flag)); 10647c478bd9Sstevel@tonic-gate case LOFI_UNMAP_FILE: 10657c478bd9Sstevel@tonic-gate if ((flag & FWRITE) == 0) 10667c478bd9Sstevel@tonic-gate return (EPERM); 1067*bd07e074Sheppo return (lofi_unmap_file(dev, lip, 1, credp, flag)); 10687c478bd9Sstevel@tonic-gate case LOFI_UNMAP_FILE_MINOR: 10697c478bd9Sstevel@tonic-gate if ((flag & FWRITE) == 0) 10707c478bd9Sstevel@tonic-gate return (EPERM); 1071*bd07e074Sheppo return (lofi_unmap_file(dev, lip, 0, credp, flag)); 10727c478bd9Sstevel@tonic-gate case LOFI_GET_FILENAME: 10737c478bd9Sstevel@tonic-gate return (lofi_get_info(dev, lip, LOFI_GET_FILENAME, 1074*bd07e074Sheppo credp, flag)); 10757c478bd9Sstevel@tonic-gate case LOFI_GET_MINOR: 10767c478bd9Sstevel@tonic-gate return (lofi_get_info(dev, lip, LOFI_GET_MINOR, 1077*bd07e074Sheppo credp, flag)); 10787c478bd9Sstevel@tonic-gate case LOFI_GET_MAXMINOR: 1079*bd07e074Sheppo error = ddi_copyout(&lofi_max_files, &lip->li_minor, 1080*bd07e074Sheppo sizeof (lofi_max_files), flag); 10817c478bd9Sstevel@tonic-gate if (error) 10827c478bd9Sstevel@tonic-gate return (EFAULT); 10837c478bd9Sstevel@tonic-gate return (0); 10847c478bd9Sstevel@tonic-gate default: 10857c478bd9Sstevel@tonic-gate break; 10867c478bd9Sstevel@tonic-gate } 10877c478bd9Sstevel@tonic-gate } 10887c478bd9Sstevel@tonic-gate 10897c478bd9Sstevel@tonic-gate lsp = ddi_get_soft_state(lofi_statep, minor); 10907c478bd9Sstevel@tonic-gate if (lsp == NULL) 10917c478bd9Sstevel@tonic-gate return (ENXIO); 10927c478bd9Sstevel@tonic-gate 10937c478bd9Sstevel@tonic-gate /* these are for faking out utilities like newfs */ 10947c478bd9Sstevel@tonic-gate switch (cmd) { 10957c478bd9Sstevel@tonic-gate case VOLIOCINFO: 10967c478bd9Sstevel@tonic-gate /* pcfs does this to see if it needs to set PCFS_NOCHK */ 10977c478bd9Sstevel@tonic-gate /* 0 means it should set it */ 10987c478bd9Sstevel@tonic-gate return (0); 10997c478bd9Sstevel@tonic-gate case DKIOCGVTOC: 11007c478bd9Sstevel@tonic-gate switch (ddi_model_convert_from(flag & FMODELS)) { 11017c478bd9Sstevel@tonic-gate case DDI_MODEL_ILP32: { 11027c478bd9Sstevel@tonic-gate struct vtoc32 vtoc32; 11037c478bd9Sstevel@tonic-gate 11047c478bd9Sstevel@tonic-gate vtoctovtoc32(lsp->ls_vtoc, vtoc32); 11057c478bd9Sstevel@tonic-gate if (ddi_copyout(&vtoc32, (void *)arg, 11067c478bd9Sstevel@tonic-gate sizeof (struct vtoc32), flag)) 11077c478bd9Sstevel@tonic-gate return (EFAULT); 11087c478bd9Sstevel@tonic-gate break; 11097c478bd9Sstevel@tonic-gate } 11107c478bd9Sstevel@tonic-gate 11117c478bd9Sstevel@tonic-gate case DDI_MODEL_NONE: 11127c478bd9Sstevel@tonic-gate if (ddi_copyout(&lsp->ls_vtoc, (void *)arg, 11137c478bd9Sstevel@tonic-gate sizeof (struct vtoc), flag)) 11147c478bd9Sstevel@tonic-gate return (EFAULT); 11157c478bd9Sstevel@tonic-gate break; 11167c478bd9Sstevel@tonic-gate } 11177c478bd9Sstevel@tonic-gate return (0); 11187c478bd9Sstevel@tonic-gate case DKIOCINFO: 1119*bd07e074Sheppo error = ddi_copyout(&lsp->ls_ci, (void *)arg, 1120*bd07e074Sheppo sizeof (struct dk_cinfo), flag); 11217c478bd9Sstevel@tonic-gate if (error) 11227c478bd9Sstevel@tonic-gate return (EFAULT); 11237c478bd9Sstevel@tonic-gate return (0); 11247c478bd9Sstevel@tonic-gate case DKIOCG_VIRTGEOM: 11257c478bd9Sstevel@tonic-gate case DKIOCG_PHYGEOM: 11267c478bd9Sstevel@tonic-gate case DKIOCGGEOM: 1127*bd07e074Sheppo error = ddi_copyout(&lsp->ls_dkg, (void *)arg, 1128*bd07e074Sheppo sizeof (struct dk_geom), flag); 11297c478bd9Sstevel@tonic-gate if (error) 11307c478bd9Sstevel@tonic-gate return (EFAULT); 11317c478bd9Sstevel@tonic-gate return (0); 11327c478bd9Sstevel@tonic-gate case DKIOCSTATE: 11337c478bd9Sstevel@tonic-gate /* the file is always there */ 11347c478bd9Sstevel@tonic-gate dkstate = DKIO_INSERTED; 1135*bd07e074Sheppo error = ddi_copyout(&dkstate, (void *)arg, 1136*bd07e074Sheppo sizeof (enum dkio_state), flag); 11377c478bd9Sstevel@tonic-gate if (error) 11387c478bd9Sstevel@tonic-gate return (EFAULT); 11397c478bd9Sstevel@tonic-gate return (0); 11407c478bd9Sstevel@tonic-gate default: 11417c478bd9Sstevel@tonic-gate return (ENOTTY); 11427c478bd9Sstevel@tonic-gate } 11437c478bd9Sstevel@tonic-gate } 11447c478bd9Sstevel@tonic-gate 11457c478bd9Sstevel@tonic-gate static struct cb_ops lofi_cb_ops = { 11467c478bd9Sstevel@tonic-gate lofi_open, /* open */ 11477c478bd9Sstevel@tonic-gate lofi_close, /* close */ 11487c478bd9Sstevel@tonic-gate lofi_strategy, /* strategy */ 11497c478bd9Sstevel@tonic-gate nodev, /* print */ 11507c478bd9Sstevel@tonic-gate nodev, /* dump */ 11517c478bd9Sstevel@tonic-gate lofi_read, /* read */ 11527c478bd9Sstevel@tonic-gate lofi_write, /* write */ 11537c478bd9Sstevel@tonic-gate lofi_ioctl, /* ioctl */ 11547c478bd9Sstevel@tonic-gate nodev, /* devmap */ 11557c478bd9Sstevel@tonic-gate nodev, /* mmap */ 11567c478bd9Sstevel@tonic-gate nodev, /* segmap */ 11577c478bd9Sstevel@tonic-gate nochpoll, /* poll */ 11587c478bd9Sstevel@tonic-gate ddi_prop_op, /* prop_op */ 11597c478bd9Sstevel@tonic-gate 0, /* streamtab */ 11607c478bd9Sstevel@tonic-gate D_64BIT | D_NEW | D_MP, /* Driver compatibility flag */ 11617c478bd9Sstevel@tonic-gate CB_REV, 11627c478bd9Sstevel@tonic-gate lofi_aread, 11637c478bd9Sstevel@tonic-gate lofi_awrite 11647c478bd9Sstevel@tonic-gate }; 11657c478bd9Sstevel@tonic-gate 11667c478bd9Sstevel@tonic-gate static struct dev_ops lofi_ops = { 11677c478bd9Sstevel@tonic-gate DEVO_REV, /* devo_rev, */ 11687c478bd9Sstevel@tonic-gate 0, /* refcnt */ 11697c478bd9Sstevel@tonic-gate lofi_info, /* info */ 11707c478bd9Sstevel@tonic-gate nulldev, /* identify */ 11717c478bd9Sstevel@tonic-gate nulldev, /* probe */ 11727c478bd9Sstevel@tonic-gate lofi_attach, /* attach */ 11737c478bd9Sstevel@tonic-gate lofi_detach, /* detach */ 11747c478bd9Sstevel@tonic-gate nodev, /* reset */ 11757c478bd9Sstevel@tonic-gate &lofi_cb_ops, /* driver operations */ 11767c478bd9Sstevel@tonic-gate NULL /* no bus operations */ 11777c478bd9Sstevel@tonic-gate }; 11787c478bd9Sstevel@tonic-gate 11797c478bd9Sstevel@tonic-gate static struct modldrv modldrv = { 11807c478bd9Sstevel@tonic-gate &mod_driverops, 11817c478bd9Sstevel@tonic-gate "loopback file driver (%I%)", 11827c478bd9Sstevel@tonic-gate &lofi_ops, 11837c478bd9Sstevel@tonic-gate }; 11847c478bd9Sstevel@tonic-gate 11857c478bd9Sstevel@tonic-gate static struct modlinkage modlinkage = { 11867c478bd9Sstevel@tonic-gate MODREV_1, 11877c478bd9Sstevel@tonic-gate &modldrv, 11887c478bd9Sstevel@tonic-gate NULL 11897c478bd9Sstevel@tonic-gate }; 11907c478bd9Sstevel@tonic-gate 11917c478bd9Sstevel@tonic-gate int 11927c478bd9Sstevel@tonic-gate _init(void) 11937c478bd9Sstevel@tonic-gate { 11947c478bd9Sstevel@tonic-gate int error; 11957c478bd9Sstevel@tonic-gate 11967c478bd9Sstevel@tonic-gate error = ddi_soft_state_init(&lofi_statep, 11977c478bd9Sstevel@tonic-gate sizeof (struct lofi_state), 0); 11987c478bd9Sstevel@tonic-gate if (error) 11997c478bd9Sstevel@tonic-gate return (error); 12007c478bd9Sstevel@tonic-gate 12017c478bd9Sstevel@tonic-gate mutex_init(&lofi_lock, NULL, MUTEX_DRIVER, NULL); 12027c478bd9Sstevel@tonic-gate error = mod_install(&modlinkage); 12037c478bd9Sstevel@tonic-gate if (error) { 12047c478bd9Sstevel@tonic-gate mutex_destroy(&lofi_lock); 12057c478bd9Sstevel@tonic-gate ddi_soft_state_fini(&lofi_statep); 12067c478bd9Sstevel@tonic-gate } 12077c478bd9Sstevel@tonic-gate 12087c478bd9Sstevel@tonic-gate return (error); 12097c478bd9Sstevel@tonic-gate } 12107c478bd9Sstevel@tonic-gate 12117c478bd9Sstevel@tonic-gate int 12127c478bd9Sstevel@tonic-gate _fini(void) 12137c478bd9Sstevel@tonic-gate { 12147c478bd9Sstevel@tonic-gate int error; 12157c478bd9Sstevel@tonic-gate 12167c478bd9Sstevel@tonic-gate if (lofi_busy()) 12177c478bd9Sstevel@tonic-gate return (EBUSY); 12187c478bd9Sstevel@tonic-gate 12197c478bd9Sstevel@tonic-gate error = mod_remove(&modlinkage); 12207c478bd9Sstevel@tonic-gate if (error) 12217c478bd9Sstevel@tonic-gate return (error); 12227c478bd9Sstevel@tonic-gate 12237c478bd9Sstevel@tonic-gate mutex_destroy(&lofi_lock); 12247c478bd9Sstevel@tonic-gate ddi_soft_state_fini(&lofi_statep); 12257c478bd9Sstevel@tonic-gate 12267c478bd9Sstevel@tonic-gate return (error); 12277c478bd9Sstevel@tonic-gate } 12287c478bd9Sstevel@tonic-gate 12297c478bd9Sstevel@tonic-gate int 12307c478bd9Sstevel@tonic-gate _info(struct modinfo *modinfop) 12317c478bd9Sstevel@tonic-gate { 12327c478bd9Sstevel@tonic-gate return (mod_info(&modlinkage, modinfop)); 12337c478bd9Sstevel@tonic-gate } 1234