10Sstevel@tonic-gate /* 20Sstevel@tonic-gate * CDDL HEADER START 30Sstevel@tonic-gate * 40Sstevel@tonic-gate * The contents of this file are subject to the terms of the 51657Sheppo * Common Development and Distribution License (the "License"). 61657Sheppo * You may not use this file except in compliance with the License. 70Sstevel@tonic-gate * 80Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 90Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 100Sstevel@tonic-gate * See the License for the specific language governing permissions 110Sstevel@tonic-gate * and limitations under the License. 120Sstevel@tonic-gate * 130Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 140Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 150Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 160Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 170Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 180Sstevel@tonic-gate * 190Sstevel@tonic-gate * CDDL HEADER END 200Sstevel@tonic-gate */ 210Sstevel@tonic-gate /* 221657Sheppo * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 230Sstevel@tonic-gate * Use is subject to license terms. 240Sstevel@tonic-gate */ 250Sstevel@tonic-gate 260Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 270Sstevel@tonic-gate 280Sstevel@tonic-gate /* 290Sstevel@tonic-gate * lofi (loopback file) driver - allows you to attach a file to a device, 300Sstevel@tonic-gate * which can then be accessed through that device. The simple model is that 310Sstevel@tonic-gate * you tell lofi to open a file, and then use the block device you get as 320Sstevel@tonic-gate * you would any block device. lofi translates access to the block device 330Sstevel@tonic-gate * into I/O on the underlying file. This is mostly useful for 340Sstevel@tonic-gate * mounting images of filesystems. 350Sstevel@tonic-gate * 360Sstevel@tonic-gate * lofi is controlled through /dev/lofictl - this is the only device exported 370Sstevel@tonic-gate * during attach, and is minor number 0. lofiadm communicates with lofi through 380Sstevel@tonic-gate * ioctls on this device. When a file is attached to lofi, block and character 390Sstevel@tonic-gate * devices are exported in /dev/lofi and /dev/rlofi. Currently, these devices 400Sstevel@tonic-gate * are identified by their minor number, and the minor number is also used 410Sstevel@tonic-gate * as the name in /dev/lofi. If we ever decide to support virtual disks, 420Sstevel@tonic-gate * we'll have to divide the minor number space to identify fdisk partitions 430Sstevel@tonic-gate * and slices, and the name will then be the minor number shifted down a 440Sstevel@tonic-gate * few bits. Minor devices are tracked with state structures handled with 450Sstevel@tonic-gate * ddi_soft_state(9F) for simplicity. 460Sstevel@tonic-gate * 470Sstevel@tonic-gate * A file attached to lofi is opened when attached and not closed until 480Sstevel@tonic-gate * explicitly detached from lofi. This seems more sensible than deferring 490Sstevel@tonic-gate * the open until the /dev/lofi device is opened, for a number of reasons. 500Sstevel@tonic-gate * One is that any failure is likely to be noticed by the person (or script) 510Sstevel@tonic-gate * running lofiadm. Another is that it would be a security problem if the 520Sstevel@tonic-gate * file was replaced by another one after being added but before being opened. 530Sstevel@tonic-gate * 540Sstevel@tonic-gate * The only hard part about lofi is the ioctls. In order to support things 550Sstevel@tonic-gate * like 'newfs' on a lofi device, it needs to support certain disk ioctls. 560Sstevel@tonic-gate * So it has to fake disk geometry and partition information. More may need 570Sstevel@tonic-gate * to be faked if your favorite utility doesn't work and you think it should 580Sstevel@tonic-gate * (fdformat doesn't work because it really wants to know the type of floppy 590Sstevel@tonic-gate * controller to talk to, and that didn't seem easy to fake. Or possibly even 600Sstevel@tonic-gate * necessary, since we have mkfs_pcfs now). 610Sstevel@tonic-gate * 620Sstevel@tonic-gate * Known problems: 630Sstevel@tonic-gate * 640Sstevel@tonic-gate * UFS logging. Mounting a UFS filesystem image "logging" 650Sstevel@tonic-gate * works for basic copy testing but wedges during a build of ON through 660Sstevel@tonic-gate * that image. Some deadlock in lufs holding the log mutex and then 670Sstevel@tonic-gate * getting stuck on a buf. So for now, don't do that. 680Sstevel@tonic-gate * 690Sstevel@tonic-gate * Direct I/O. Since the filesystem data is being cached in the buffer 700Sstevel@tonic-gate * cache, _and_ again in the underlying filesystem, it's tempting to 710Sstevel@tonic-gate * enable direct I/O on the underlying file. Don't, because that deadlocks. 720Sstevel@tonic-gate * I think to fix the cache-twice problem we might need filesystem support. 730Sstevel@tonic-gate * 740Sstevel@tonic-gate * lofi on itself. The simple lock strategy (lofi_lock) precludes this 750Sstevel@tonic-gate * because you'll be in lofi_ioctl, holding the lock when you open the 760Sstevel@tonic-gate * file, which, if it's lofi, will grab lofi_lock. We prevent this for 770Sstevel@tonic-gate * now, though not using ddi_soft_state(9F) would make it possible to 780Sstevel@tonic-gate * do. Though it would still be silly. 790Sstevel@tonic-gate * 800Sstevel@tonic-gate * Interesting things to do: 810Sstevel@tonic-gate * 820Sstevel@tonic-gate * Allow multiple files for each device. A poor-man's metadisk, basically. 830Sstevel@tonic-gate * 840Sstevel@tonic-gate * Pass-through ioctls on block devices. You can (though it's not 850Sstevel@tonic-gate * documented), give lofi a block device as a file name. Then we shouldn't 860Sstevel@tonic-gate * need to fake a geometry. But this is also silly unless you're replacing 870Sstevel@tonic-gate * metadisk. 880Sstevel@tonic-gate * 890Sstevel@tonic-gate * Encryption. tpm would like this. Apparently Windows 2000 has it, and 900Sstevel@tonic-gate * so does Linux. 910Sstevel@tonic-gate */ 920Sstevel@tonic-gate 930Sstevel@tonic-gate #include <sys/types.h> 940Sstevel@tonic-gate #include <sys/sysmacros.h> 950Sstevel@tonic-gate #include <sys/cmn_err.h> 960Sstevel@tonic-gate #include <sys/uio.h> 970Sstevel@tonic-gate #include <sys/kmem.h> 980Sstevel@tonic-gate #include <sys/cred.h> 990Sstevel@tonic-gate #include <sys/mman.h> 1000Sstevel@tonic-gate #include <sys/errno.h> 1010Sstevel@tonic-gate #include <sys/aio_req.h> 1020Sstevel@tonic-gate #include <sys/stat.h> 1030Sstevel@tonic-gate #include <sys/file.h> 1040Sstevel@tonic-gate #include <sys/modctl.h> 1050Sstevel@tonic-gate #include <sys/conf.h> 1060Sstevel@tonic-gate #include <sys/debug.h> 1070Sstevel@tonic-gate #include <sys/vnode.h> 1080Sstevel@tonic-gate #include <sys/lofi.h> 1090Sstevel@tonic-gate #include <sys/fcntl.h> 1100Sstevel@tonic-gate #include <sys/pathname.h> 1110Sstevel@tonic-gate #include <sys/filio.h> 1120Sstevel@tonic-gate #include <sys/fdio.h> 1130Sstevel@tonic-gate #include <sys/open.h> 1140Sstevel@tonic-gate #include <sys/disp.h> 1150Sstevel@tonic-gate #include <vm/seg_map.h> 1160Sstevel@tonic-gate #include <sys/ddi.h> 1170Sstevel@tonic-gate #include <sys/sunddi.h> 1180Sstevel@tonic-gate 1190Sstevel@tonic-gate /* seems safer than having to get the string right many times */ 1200Sstevel@tonic-gate #define NBLOCKS_PROP_NAME "Nblocks" 1210Sstevel@tonic-gate #define SIZE_PROP_NAME "Size" 1220Sstevel@tonic-gate 1230Sstevel@tonic-gate static dev_info_t *lofi_dip; 1240Sstevel@tonic-gate static void *lofi_statep; 1250Sstevel@tonic-gate static kmutex_t lofi_lock; /* state lock */ 1260Sstevel@tonic-gate 1270Sstevel@tonic-gate /* 1280Sstevel@tonic-gate * Because lofi_taskq_nthreads limits the actual swamping of the device, the 1290Sstevel@tonic-gate * maxalloc parameter (lofi_taskq_maxalloc) should be tuned conservatively 1300Sstevel@tonic-gate * high. If we want to be assured that the underlying device is always busy, 1310Sstevel@tonic-gate * we must be sure that the number of bytes enqueued when the number of 1320Sstevel@tonic-gate * enqueued tasks exceeds maxalloc is sufficient to keep the device busy for 1330Sstevel@tonic-gate * the duration of the sleep time in taskq_ent_alloc(). That is, lofi should 1340Sstevel@tonic-gate * set maxalloc to be the maximum throughput (in bytes per second) of the 1350Sstevel@tonic-gate * underlying device divided by the minimum I/O size. We assume a realistic 1360Sstevel@tonic-gate * maximum throughput of one hundred megabytes per second; we set maxalloc on 1370Sstevel@tonic-gate * the lofi task queue to be 104857600 divided by DEV_BSIZE. 1380Sstevel@tonic-gate */ 1390Sstevel@tonic-gate static int lofi_taskq_maxalloc = 104857600 / DEV_BSIZE; 1400Sstevel@tonic-gate static int lofi_taskq_nthreads = 4; /* # of taskq threads per device */ 1410Sstevel@tonic-gate 1420Sstevel@tonic-gate uint32_t lofi_max_files = LOFI_MAX_FILES; 1430Sstevel@tonic-gate 1440Sstevel@tonic-gate static int 1450Sstevel@tonic-gate lofi_busy(void) 1460Sstevel@tonic-gate { 1470Sstevel@tonic-gate minor_t minor; 1480Sstevel@tonic-gate 1490Sstevel@tonic-gate /* 1500Sstevel@tonic-gate * We need to make sure no mappings exist - mod_remove won't 1510Sstevel@tonic-gate * help because the device isn't open. 1520Sstevel@tonic-gate */ 1530Sstevel@tonic-gate mutex_enter(&lofi_lock); 1540Sstevel@tonic-gate for (minor = 1; minor <= lofi_max_files; minor++) { 1550Sstevel@tonic-gate if (ddi_get_soft_state(lofi_statep, minor) != NULL) { 1560Sstevel@tonic-gate mutex_exit(&lofi_lock); 1570Sstevel@tonic-gate return (EBUSY); 1580Sstevel@tonic-gate } 1590Sstevel@tonic-gate } 1600Sstevel@tonic-gate mutex_exit(&lofi_lock); 1610Sstevel@tonic-gate return (0); 1620Sstevel@tonic-gate } 1630Sstevel@tonic-gate 1640Sstevel@tonic-gate static int 1650Sstevel@tonic-gate is_opened(struct lofi_state *lsp) 1660Sstevel@tonic-gate { 1670Sstevel@tonic-gate ASSERT(mutex_owned(&lofi_lock)); 1680Sstevel@tonic-gate return (lsp->ls_chr_open || lsp->ls_blk_open || lsp->ls_lyr_open_count); 1690Sstevel@tonic-gate } 1700Sstevel@tonic-gate 1710Sstevel@tonic-gate static int 1720Sstevel@tonic-gate mark_opened(struct lofi_state *lsp, int otyp) 1730Sstevel@tonic-gate { 1740Sstevel@tonic-gate ASSERT(mutex_owned(&lofi_lock)); 1750Sstevel@tonic-gate switch (otyp) { 1760Sstevel@tonic-gate case OTYP_CHR: 1770Sstevel@tonic-gate lsp->ls_chr_open = 1; 1780Sstevel@tonic-gate break; 1790Sstevel@tonic-gate case OTYP_BLK: 1800Sstevel@tonic-gate lsp->ls_blk_open = 1; 1810Sstevel@tonic-gate break; 1820Sstevel@tonic-gate case OTYP_LYR: 1830Sstevel@tonic-gate lsp->ls_lyr_open_count++; 1840Sstevel@tonic-gate break; 1850Sstevel@tonic-gate default: 1860Sstevel@tonic-gate return (-1); 1870Sstevel@tonic-gate } 1880Sstevel@tonic-gate return (0); 1890Sstevel@tonic-gate } 1900Sstevel@tonic-gate 1910Sstevel@tonic-gate static void 1920Sstevel@tonic-gate mark_closed(struct lofi_state *lsp, int otyp) 1930Sstevel@tonic-gate { 1940Sstevel@tonic-gate ASSERT(mutex_owned(&lofi_lock)); 1950Sstevel@tonic-gate switch (otyp) { 1960Sstevel@tonic-gate case OTYP_CHR: 1970Sstevel@tonic-gate lsp->ls_chr_open = 0; 1980Sstevel@tonic-gate break; 1990Sstevel@tonic-gate case OTYP_BLK: 2000Sstevel@tonic-gate lsp->ls_blk_open = 0; 2010Sstevel@tonic-gate break; 2020Sstevel@tonic-gate case OTYP_LYR: 2030Sstevel@tonic-gate lsp->ls_lyr_open_count--; 2040Sstevel@tonic-gate break; 2050Sstevel@tonic-gate default: 2060Sstevel@tonic-gate break; 2070Sstevel@tonic-gate } 2080Sstevel@tonic-gate } 2090Sstevel@tonic-gate 2100Sstevel@tonic-gate /*ARGSUSED3*/ 2110Sstevel@tonic-gate static int 2120Sstevel@tonic-gate lofi_open(dev_t *devp, int flag, int otyp, struct cred *credp) 2130Sstevel@tonic-gate { 2140Sstevel@tonic-gate minor_t minor; 2150Sstevel@tonic-gate struct lofi_state *lsp; 2160Sstevel@tonic-gate 2170Sstevel@tonic-gate mutex_enter(&lofi_lock); 2180Sstevel@tonic-gate minor = getminor(*devp); 2190Sstevel@tonic-gate if (minor == 0) { 2200Sstevel@tonic-gate /* master control device */ 2210Sstevel@tonic-gate /* must be opened exclusively */ 2220Sstevel@tonic-gate if (((flag & FEXCL) != FEXCL) || (otyp != OTYP_CHR)) { 2230Sstevel@tonic-gate mutex_exit(&lofi_lock); 2240Sstevel@tonic-gate return (EINVAL); 2250Sstevel@tonic-gate } 2260Sstevel@tonic-gate lsp = ddi_get_soft_state(lofi_statep, 0); 2270Sstevel@tonic-gate if (lsp == NULL) { 2280Sstevel@tonic-gate mutex_exit(&lofi_lock); 2290Sstevel@tonic-gate return (ENXIO); 2300Sstevel@tonic-gate } 2310Sstevel@tonic-gate if (is_opened(lsp)) { 2320Sstevel@tonic-gate mutex_exit(&lofi_lock); 2330Sstevel@tonic-gate return (EBUSY); 2340Sstevel@tonic-gate } 2350Sstevel@tonic-gate (void) mark_opened(lsp, OTYP_CHR); 2360Sstevel@tonic-gate mutex_exit(&lofi_lock); 2370Sstevel@tonic-gate return (0); 2380Sstevel@tonic-gate } 2390Sstevel@tonic-gate 2400Sstevel@tonic-gate /* otherwise, the mapping should already exist */ 2410Sstevel@tonic-gate lsp = ddi_get_soft_state(lofi_statep, minor); 2420Sstevel@tonic-gate if (lsp == NULL) { 2430Sstevel@tonic-gate mutex_exit(&lofi_lock); 2440Sstevel@tonic-gate return (EINVAL); 2450Sstevel@tonic-gate } 2460Sstevel@tonic-gate 2470Sstevel@tonic-gate if (mark_opened(lsp, otyp) == -1) { 2480Sstevel@tonic-gate mutex_exit(&lofi_lock); 2490Sstevel@tonic-gate return (EINVAL); 2500Sstevel@tonic-gate } 2510Sstevel@tonic-gate 2520Sstevel@tonic-gate mutex_exit(&lofi_lock); 2530Sstevel@tonic-gate return (0); 2540Sstevel@tonic-gate } 2550Sstevel@tonic-gate 2560Sstevel@tonic-gate /*ARGSUSED3*/ 2570Sstevel@tonic-gate static int 2580Sstevel@tonic-gate lofi_close(dev_t dev, int flag, int otyp, struct cred *credp) 2590Sstevel@tonic-gate { 2600Sstevel@tonic-gate minor_t minor; 2610Sstevel@tonic-gate struct lofi_state *lsp; 2620Sstevel@tonic-gate 2630Sstevel@tonic-gate #ifdef lint 2640Sstevel@tonic-gate flag = flag; 2650Sstevel@tonic-gate #endif 2660Sstevel@tonic-gate mutex_enter(&lofi_lock); 2670Sstevel@tonic-gate minor = getminor(dev); 2680Sstevel@tonic-gate lsp = ddi_get_soft_state(lofi_statep, minor); 2690Sstevel@tonic-gate if (lsp == NULL) { 2700Sstevel@tonic-gate mutex_exit(&lofi_lock); 2710Sstevel@tonic-gate return (EINVAL); 2720Sstevel@tonic-gate } 2730Sstevel@tonic-gate mark_closed(lsp, otyp); 2740Sstevel@tonic-gate mutex_exit(&lofi_lock); 2750Sstevel@tonic-gate return (0); 2760Sstevel@tonic-gate } 2770Sstevel@tonic-gate 2780Sstevel@tonic-gate /* 2790Sstevel@tonic-gate * This is basically what strategy used to be before we found we 2800Sstevel@tonic-gate * needed task queues. 2810Sstevel@tonic-gate */ 2820Sstevel@tonic-gate static void 2830Sstevel@tonic-gate lofi_strategy_task(void *arg) 2840Sstevel@tonic-gate { 2850Sstevel@tonic-gate struct buf *bp = (struct buf *)arg; 2860Sstevel@tonic-gate int error; 2870Sstevel@tonic-gate struct lofi_state *lsp; 2880Sstevel@tonic-gate offset_t offset, alignedoffset; 2890Sstevel@tonic-gate offset_t mapoffset; 2900Sstevel@tonic-gate caddr_t bufaddr; 2910Sstevel@tonic-gate caddr_t mapaddr; 2920Sstevel@tonic-gate size_t xfersize; 2930Sstevel@tonic-gate size_t len; 2940Sstevel@tonic-gate int isread; 2950Sstevel@tonic-gate int smflags; 2960Sstevel@tonic-gate enum seg_rw srw; 2970Sstevel@tonic-gate 2980Sstevel@tonic-gate lsp = ddi_get_soft_state(lofi_statep, getminor(bp->b_edev)); 2990Sstevel@tonic-gate if (lsp->ls_kstat) { 3000Sstevel@tonic-gate mutex_enter(lsp->ls_kstat->ks_lock); 3010Sstevel@tonic-gate kstat_waitq_to_runq(KSTAT_IO_PTR(lsp->ls_kstat)); 3020Sstevel@tonic-gate mutex_exit(lsp->ls_kstat->ks_lock); 3030Sstevel@tonic-gate } 3040Sstevel@tonic-gate bp_mapin(bp); 3050Sstevel@tonic-gate bufaddr = bp->b_un.b_addr; 3060Sstevel@tonic-gate offset = bp->b_lblkno * DEV_BSIZE; /* offset within file */ 3070Sstevel@tonic-gate 3080Sstevel@tonic-gate /* 3090Sstevel@tonic-gate * We used to always use vn_rdwr here, but we cannot do that because 3100Sstevel@tonic-gate * we might decide to read or write from the the underlying 3110Sstevel@tonic-gate * file during this call, which would be a deadlock because 3120Sstevel@tonic-gate * we have the rw_lock. So instead we page, unless it's not 3130Sstevel@tonic-gate * mapable or it's a character device. 3140Sstevel@tonic-gate */ 3150Sstevel@tonic-gate if (((lsp->ls_vp->v_flag & VNOMAP) == 0) && 3160Sstevel@tonic-gate (lsp->ls_vp->v_type != VCHR)) { 3170Sstevel@tonic-gate /* 3180Sstevel@tonic-gate * segmap always gives us an 8K (MAXBSIZE) chunk, aligned on 3190Sstevel@tonic-gate * an 8K boundary, but the buf transfer address may not be 3200Sstevel@tonic-gate * aligned on more than a 512-byte boundary (we don't 3210Sstevel@tonic-gate * enforce that, though we could). This matters since the 3220Sstevel@tonic-gate * initial part of the transfer may not start at offset 0 3230Sstevel@tonic-gate * within the segmap'd chunk. So we have to compensate for 3240Sstevel@tonic-gate * that with 'mapoffset'. Subsequent chunks always start 3250Sstevel@tonic-gate * off at the beginning, and the last is capped by b_resid. 3260Sstevel@tonic-gate */ 3270Sstevel@tonic-gate mapoffset = offset & MAXBOFFSET; 3280Sstevel@tonic-gate alignedoffset = offset - mapoffset; /* now map-aligned */ 3290Sstevel@tonic-gate bp->b_resid = bp->b_bcount; 3300Sstevel@tonic-gate isread = bp->b_flags & B_READ; 3310Sstevel@tonic-gate srw = isread ? S_READ : S_WRITE; 3320Sstevel@tonic-gate do { 3330Sstevel@tonic-gate xfersize = MIN(lsp->ls_vp_size - offset, 3340Sstevel@tonic-gate MIN(MAXBSIZE - mapoffset, bp->b_resid)); 3350Sstevel@tonic-gate len = roundup(mapoffset + xfersize, PAGESIZE); 3360Sstevel@tonic-gate mapaddr = segmap_getmapflt(segkmap, lsp->ls_vp, 3370Sstevel@tonic-gate alignedoffset, MAXBSIZE, 1, srw); 3380Sstevel@tonic-gate /* 3390Sstevel@tonic-gate * Now fault in the pages. This lets us check 3400Sstevel@tonic-gate * for errors before we reference mapaddr and 3410Sstevel@tonic-gate * try to resolve the fault in bcopy (which would 3420Sstevel@tonic-gate * panic instead). And this can easily happen, 3430Sstevel@tonic-gate * particularly if you've lofi'd a file over NFS 3440Sstevel@tonic-gate * and someone deletes the file on the server. 3450Sstevel@tonic-gate */ 3460Sstevel@tonic-gate error = segmap_fault(kas.a_hat, segkmap, mapaddr, 3470Sstevel@tonic-gate len, F_SOFTLOCK, srw); 3480Sstevel@tonic-gate if (error) { 3490Sstevel@tonic-gate (void) segmap_release(segkmap, mapaddr, 0); 3500Sstevel@tonic-gate if (FC_CODE(error) == FC_OBJERR) 3510Sstevel@tonic-gate error = FC_ERRNO(error); 3520Sstevel@tonic-gate else 3530Sstevel@tonic-gate error = EIO; 3540Sstevel@tonic-gate break; 3550Sstevel@tonic-gate } 3560Sstevel@tonic-gate smflags = 0; 3570Sstevel@tonic-gate if (isread) { 3580Sstevel@tonic-gate bcopy(mapaddr + mapoffset, bufaddr, xfersize); 3590Sstevel@tonic-gate } else { 3600Sstevel@tonic-gate smflags |= SM_WRITE; 3610Sstevel@tonic-gate bcopy(bufaddr, mapaddr + mapoffset, xfersize); 3620Sstevel@tonic-gate } 3630Sstevel@tonic-gate bp->b_resid -= xfersize; 3640Sstevel@tonic-gate bufaddr += xfersize; 3650Sstevel@tonic-gate offset += xfersize; 3660Sstevel@tonic-gate (void) segmap_fault(kas.a_hat, segkmap, mapaddr, 3670Sstevel@tonic-gate len, F_SOFTUNLOCK, srw); 3680Sstevel@tonic-gate error = segmap_release(segkmap, mapaddr, smflags); 3690Sstevel@tonic-gate /* only the first map may start partial */ 3700Sstevel@tonic-gate mapoffset = 0; 3710Sstevel@tonic-gate alignedoffset += MAXBSIZE; 3720Sstevel@tonic-gate } while ((error == 0) && (bp->b_resid > 0) && 3730Sstevel@tonic-gate (offset < lsp->ls_vp_size)); 3740Sstevel@tonic-gate } else { 3750Sstevel@tonic-gate ssize_t resid; 3760Sstevel@tonic-gate enum uio_rw rw; 3770Sstevel@tonic-gate 3780Sstevel@tonic-gate if (bp->b_flags & B_READ) 3790Sstevel@tonic-gate rw = UIO_READ; 3800Sstevel@tonic-gate else 3810Sstevel@tonic-gate rw = UIO_WRITE; 3820Sstevel@tonic-gate error = vn_rdwr(rw, lsp->ls_vp, bufaddr, bp->b_bcount, 3830Sstevel@tonic-gate offset, UIO_SYSSPACE, 0, RLIM64_INFINITY, kcred, &resid); 3840Sstevel@tonic-gate bp->b_resid = resid; 3850Sstevel@tonic-gate } 3860Sstevel@tonic-gate 3870Sstevel@tonic-gate if (lsp->ls_kstat) { 3880Sstevel@tonic-gate size_t n_done = bp->b_bcount - bp->b_resid; 3890Sstevel@tonic-gate kstat_io_t *kioptr; 3900Sstevel@tonic-gate 3910Sstevel@tonic-gate mutex_enter(lsp->ls_kstat->ks_lock); 3920Sstevel@tonic-gate kioptr = KSTAT_IO_PTR(lsp->ls_kstat); 3930Sstevel@tonic-gate if (bp->b_flags & B_READ) { 3940Sstevel@tonic-gate kioptr->nread += n_done; 3950Sstevel@tonic-gate kioptr->reads++; 3960Sstevel@tonic-gate } else { 3970Sstevel@tonic-gate kioptr->nwritten += n_done; 3980Sstevel@tonic-gate kioptr->writes++; 3990Sstevel@tonic-gate } 4000Sstevel@tonic-gate kstat_runq_exit(kioptr); 4010Sstevel@tonic-gate mutex_exit(lsp->ls_kstat->ks_lock); 4020Sstevel@tonic-gate } 4030Sstevel@tonic-gate bioerror(bp, error); 4040Sstevel@tonic-gate biodone(bp); 4050Sstevel@tonic-gate } 4060Sstevel@tonic-gate 4070Sstevel@tonic-gate static int 4080Sstevel@tonic-gate lofi_strategy(struct buf *bp) 4090Sstevel@tonic-gate { 4100Sstevel@tonic-gate struct lofi_state *lsp; 4110Sstevel@tonic-gate offset_t offset; 4120Sstevel@tonic-gate 4130Sstevel@tonic-gate /* 4140Sstevel@tonic-gate * We cannot just do I/O here, because the current thread 4150Sstevel@tonic-gate * _might_ end up back in here because the underlying filesystem 4160Sstevel@tonic-gate * wants a buffer, which eventually gets into bio_recycle and 4170Sstevel@tonic-gate * might call into lofi to write out a delayed-write buffer. 4180Sstevel@tonic-gate * This is bad if the filesystem above lofi is the same as below. 4190Sstevel@tonic-gate * 4200Sstevel@tonic-gate * We could come up with a complex strategy using threads to 4210Sstevel@tonic-gate * do the I/O asynchronously, or we could use task queues. task 4220Sstevel@tonic-gate * queues were incredibly easy so they win. 4230Sstevel@tonic-gate */ 4240Sstevel@tonic-gate lsp = ddi_get_soft_state(lofi_statep, getminor(bp->b_edev)); 4250Sstevel@tonic-gate offset = bp->b_lblkno * DEV_BSIZE; /* offset within file */ 4260Sstevel@tonic-gate if (offset == lsp->ls_vp_size) { 4270Sstevel@tonic-gate /* EOF */ 4280Sstevel@tonic-gate if ((bp->b_flags & B_READ) != 0) { 4290Sstevel@tonic-gate bp->b_resid = bp->b_bcount; 4300Sstevel@tonic-gate bioerror(bp, 0); 4310Sstevel@tonic-gate } else { 4320Sstevel@tonic-gate /* writes should fail */ 4330Sstevel@tonic-gate bioerror(bp, ENXIO); 4340Sstevel@tonic-gate } 4350Sstevel@tonic-gate biodone(bp); 4360Sstevel@tonic-gate return (0); 4370Sstevel@tonic-gate } 4380Sstevel@tonic-gate if (offset > lsp->ls_vp_size) { 4390Sstevel@tonic-gate bioerror(bp, ENXIO); 4400Sstevel@tonic-gate biodone(bp); 4410Sstevel@tonic-gate return (0); 4420Sstevel@tonic-gate } 4430Sstevel@tonic-gate if (lsp->ls_kstat) { 4440Sstevel@tonic-gate mutex_enter(lsp->ls_kstat->ks_lock); 4450Sstevel@tonic-gate kstat_waitq_enter(KSTAT_IO_PTR(lsp->ls_kstat)); 4460Sstevel@tonic-gate mutex_exit(lsp->ls_kstat->ks_lock); 4470Sstevel@tonic-gate } 4480Sstevel@tonic-gate (void) taskq_dispatch(lsp->ls_taskq, lofi_strategy_task, bp, KM_SLEEP); 4490Sstevel@tonic-gate return (0); 4500Sstevel@tonic-gate } 4510Sstevel@tonic-gate 4520Sstevel@tonic-gate /*ARGSUSED2*/ 4530Sstevel@tonic-gate static int 4540Sstevel@tonic-gate lofi_read(dev_t dev, struct uio *uio, struct cred *credp) 4550Sstevel@tonic-gate { 4560Sstevel@tonic-gate if (getminor(dev) == 0) 4570Sstevel@tonic-gate return (EINVAL); 4580Sstevel@tonic-gate return (physio(lofi_strategy, NULL, dev, B_READ, minphys, uio)); 4590Sstevel@tonic-gate } 4600Sstevel@tonic-gate 4610Sstevel@tonic-gate /*ARGSUSED2*/ 4620Sstevel@tonic-gate static int 4630Sstevel@tonic-gate lofi_write(dev_t dev, struct uio *uio, struct cred *credp) 4640Sstevel@tonic-gate { 4650Sstevel@tonic-gate if (getminor(dev) == 0) 4660Sstevel@tonic-gate return (EINVAL); 4670Sstevel@tonic-gate return (physio(lofi_strategy, NULL, dev, B_WRITE, minphys, uio)); 4680Sstevel@tonic-gate } 4690Sstevel@tonic-gate 4700Sstevel@tonic-gate /*ARGSUSED2*/ 4710Sstevel@tonic-gate static int 4720Sstevel@tonic-gate lofi_aread(dev_t dev, struct aio_req *aio, struct cred *credp) 4730Sstevel@tonic-gate { 4740Sstevel@tonic-gate if (getminor(dev) == 0) 4750Sstevel@tonic-gate return (EINVAL); 4760Sstevel@tonic-gate return (aphysio(lofi_strategy, anocancel, dev, B_READ, minphys, aio)); 4770Sstevel@tonic-gate } 4780Sstevel@tonic-gate 4790Sstevel@tonic-gate /*ARGSUSED2*/ 4800Sstevel@tonic-gate static int 4810Sstevel@tonic-gate lofi_awrite(dev_t dev, struct aio_req *aio, struct cred *credp) 4820Sstevel@tonic-gate { 4830Sstevel@tonic-gate if (getminor(dev) == 0) 4840Sstevel@tonic-gate return (EINVAL); 4850Sstevel@tonic-gate return (aphysio(lofi_strategy, anocancel, dev, B_WRITE, minphys, aio)); 4860Sstevel@tonic-gate } 4870Sstevel@tonic-gate 4880Sstevel@tonic-gate /*ARGSUSED*/ 4890Sstevel@tonic-gate static int 4900Sstevel@tonic-gate lofi_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result) 4910Sstevel@tonic-gate { 4920Sstevel@tonic-gate switch (infocmd) { 4930Sstevel@tonic-gate case DDI_INFO_DEVT2DEVINFO: 4940Sstevel@tonic-gate *result = lofi_dip; 4950Sstevel@tonic-gate return (DDI_SUCCESS); 4960Sstevel@tonic-gate case DDI_INFO_DEVT2INSTANCE: 4970Sstevel@tonic-gate *result = 0; 4980Sstevel@tonic-gate return (DDI_SUCCESS); 4990Sstevel@tonic-gate } 5000Sstevel@tonic-gate return (DDI_FAILURE); 5010Sstevel@tonic-gate } 5020Sstevel@tonic-gate 5030Sstevel@tonic-gate static int 5040Sstevel@tonic-gate lofi_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 5050Sstevel@tonic-gate { 5060Sstevel@tonic-gate int error; 5070Sstevel@tonic-gate 5080Sstevel@tonic-gate if (cmd != DDI_ATTACH) 5090Sstevel@tonic-gate return (DDI_FAILURE); 5100Sstevel@tonic-gate error = ddi_soft_state_zalloc(lofi_statep, 0); 5110Sstevel@tonic-gate if (error == DDI_FAILURE) { 5120Sstevel@tonic-gate return (DDI_FAILURE); 5130Sstevel@tonic-gate } 5140Sstevel@tonic-gate error = ddi_create_minor_node(dip, LOFI_CTL_NODE, S_IFCHR, 0, 5150Sstevel@tonic-gate DDI_PSEUDO, NULL); 5160Sstevel@tonic-gate if (error == DDI_FAILURE) { 5170Sstevel@tonic-gate ddi_soft_state_free(lofi_statep, 0); 5180Sstevel@tonic-gate return (DDI_FAILURE); 5190Sstevel@tonic-gate } 5200Sstevel@tonic-gate lofi_dip = dip; 5210Sstevel@tonic-gate ddi_report_dev(dip); 5220Sstevel@tonic-gate return (DDI_SUCCESS); 5230Sstevel@tonic-gate } 5240Sstevel@tonic-gate 5250Sstevel@tonic-gate static int 5260Sstevel@tonic-gate lofi_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 5270Sstevel@tonic-gate { 5280Sstevel@tonic-gate if (cmd != DDI_DETACH) 5290Sstevel@tonic-gate return (DDI_FAILURE); 5300Sstevel@tonic-gate if (lofi_busy()) 5310Sstevel@tonic-gate return (DDI_FAILURE); 5320Sstevel@tonic-gate lofi_dip = NULL; 5330Sstevel@tonic-gate ddi_remove_minor_node(dip, NULL); 5340Sstevel@tonic-gate ddi_soft_state_free(lofi_statep, 0); 5350Sstevel@tonic-gate return (DDI_SUCCESS); 5360Sstevel@tonic-gate } 5370Sstevel@tonic-gate 5380Sstevel@tonic-gate /* 5390Sstevel@tonic-gate * These two just simplify the rest of the ioctls that need to copyin/out 5400Sstevel@tonic-gate * the lofi_ioctl structure. 5410Sstevel@tonic-gate */ 5420Sstevel@tonic-gate struct lofi_ioctl * 5431657Sheppo copy_in_lofi_ioctl(const struct lofi_ioctl *ulip, int flag) 5440Sstevel@tonic-gate { 5450Sstevel@tonic-gate struct lofi_ioctl *klip; 5460Sstevel@tonic-gate int error; 5470Sstevel@tonic-gate 5480Sstevel@tonic-gate klip = kmem_alloc(sizeof (struct lofi_ioctl), KM_SLEEP); 5491657Sheppo error = ddi_copyin(ulip, klip, sizeof (struct lofi_ioctl), flag); 5500Sstevel@tonic-gate if (error) { 5510Sstevel@tonic-gate kmem_free(klip, sizeof (struct lofi_ioctl)); 5520Sstevel@tonic-gate return (NULL); 5530Sstevel@tonic-gate } 5540Sstevel@tonic-gate 5550Sstevel@tonic-gate /* make sure filename is always null-terminated */ 5560Sstevel@tonic-gate klip->li_filename[MAXPATHLEN] = '\0'; 5570Sstevel@tonic-gate 5580Sstevel@tonic-gate /* validate minor number */ 5590Sstevel@tonic-gate if (klip->li_minor > lofi_max_files) { 5600Sstevel@tonic-gate kmem_free(klip, sizeof (struct lofi_ioctl)); 5610Sstevel@tonic-gate return (NULL); 5620Sstevel@tonic-gate } 5630Sstevel@tonic-gate return (klip); 5640Sstevel@tonic-gate } 5650Sstevel@tonic-gate 5660Sstevel@tonic-gate int 5671657Sheppo copy_out_lofi_ioctl(const struct lofi_ioctl *klip, struct lofi_ioctl *ulip, 5681657Sheppo int flag) 5690Sstevel@tonic-gate { 5700Sstevel@tonic-gate int error; 5710Sstevel@tonic-gate 5721657Sheppo error = ddi_copyout(klip, ulip, sizeof (struct lofi_ioctl), flag); 5730Sstevel@tonic-gate if (error) 5740Sstevel@tonic-gate return (EFAULT); 5750Sstevel@tonic-gate return (0); 5760Sstevel@tonic-gate } 5770Sstevel@tonic-gate 5780Sstevel@tonic-gate void 5790Sstevel@tonic-gate free_lofi_ioctl(struct lofi_ioctl *klip) 5800Sstevel@tonic-gate { 5810Sstevel@tonic-gate kmem_free(klip, sizeof (struct lofi_ioctl)); 5820Sstevel@tonic-gate } 5830Sstevel@tonic-gate 5840Sstevel@tonic-gate /* 5850Sstevel@tonic-gate * Return the minor number 'filename' is mapped to, if it is. 5860Sstevel@tonic-gate */ 5870Sstevel@tonic-gate static int 5880Sstevel@tonic-gate file_to_minor(char *filename) 5890Sstevel@tonic-gate { 5900Sstevel@tonic-gate minor_t minor; 5910Sstevel@tonic-gate struct lofi_state *lsp; 5920Sstevel@tonic-gate 5930Sstevel@tonic-gate ASSERT(mutex_owned(&lofi_lock)); 5940Sstevel@tonic-gate for (minor = 1; minor <= lofi_max_files; minor++) { 5950Sstevel@tonic-gate lsp = ddi_get_soft_state(lofi_statep, minor); 5960Sstevel@tonic-gate if (lsp == NULL) 5970Sstevel@tonic-gate continue; 5980Sstevel@tonic-gate if (strcmp(lsp->ls_filename, filename) == 0) 5990Sstevel@tonic-gate return (minor); 6000Sstevel@tonic-gate } 6010Sstevel@tonic-gate return (0); 6020Sstevel@tonic-gate } 6030Sstevel@tonic-gate 6040Sstevel@tonic-gate /* 6050Sstevel@tonic-gate * lofiadm does some validation, but since Joe Random (or crashme) could 6060Sstevel@tonic-gate * do our ioctls, we need to do some validation too. 6070Sstevel@tonic-gate */ 6080Sstevel@tonic-gate static int 6090Sstevel@tonic-gate valid_filename(const char *filename) 6100Sstevel@tonic-gate { 6110Sstevel@tonic-gate static char *blkprefix = "/dev/" LOFI_BLOCK_NAME "/"; 6120Sstevel@tonic-gate static char *charprefix = "/dev/" LOFI_CHAR_NAME "/"; 6130Sstevel@tonic-gate 6140Sstevel@tonic-gate /* must be absolute path */ 6150Sstevel@tonic-gate if (filename[0] != '/') 6160Sstevel@tonic-gate return (0); 6170Sstevel@tonic-gate /* must not be lofi */ 6180Sstevel@tonic-gate if (strncmp(filename, blkprefix, strlen(blkprefix)) == 0) 6190Sstevel@tonic-gate return (0); 6200Sstevel@tonic-gate if (strncmp(filename, charprefix, strlen(charprefix)) == 0) 6210Sstevel@tonic-gate return (0); 6220Sstevel@tonic-gate return (1); 6230Sstevel@tonic-gate } 6240Sstevel@tonic-gate 6250Sstevel@tonic-gate /* 6260Sstevel@tonic-gate * Fakes up a disk geometry, and one big partition, based on the size 6270Sstevel@tonic-gate * of the file. This is needed because we allow newfs'ing the device, 6280Sstevel@tonic-gate * and newfs will do several disk ioctls to figure out the geometry and 6290Sstevel@tonic-gate * partition information. It uses that information to determine the parameters 630*3517Smp204432 * to pass to mkfs. Geometry is pretty much irrelevant these days, but we 6310Sstevel@tonic-gate * have to support it. 6320Sstevel@tonic-gate */ 6330Sstevel@tonic-gate static void 6340Sstevel@tonic-gate fake_disk_geometry(struct lofi_state *lsp) 6350Sstevel@tonic-gate { 6360Sstevel@tonic-gate /* dk_geom - see dkio(7I) */ 6370Sstevel@tonic-gate /* 6380Sstevel@tonic-gate * dkg_ncyl _could_ be set to one here (one big cylinder with gobs 6390Sstevel@tonic-gate * of sectors), but that breaks programs like fdisk which want to 6400Sstevel@tonic-gate * partition a disk by cylinder. With one cylinder, you can't create 6410Sstevel@tonic-gate * an fdisk partition and put pcfs on it for testing (hard to pick 6420Sstevel@tonic-gate * a number between one and one). 6430Sstevel@tonic-gate * 6440Sstevel@tonic-gate * The cheezy floppy test is an attempt to not have too few cylinders 6450Sstevel@tonic-gate * for a small file, or so many on a big file that you waste space 6460Sstevel@tonic-gate * for backup superblocks or cylinder group structures. 6470Sstevel@tonic-gate */ 6480Sstevel@tonic-gate if (lsp->ls_vp_size < (2 * 1024 * 1024)) /* floppy? */ 6490Sstevel@tonic-gate lsp->ls_dkg.dkg_ncyl = lsp->ls_vp_size / (100 * 1024); 6500Sstevel@tonic-gate else 6510Sstevel@tonic-gate lsp->ls_dkg.dkg_ncyl = lsp->ls_vp_size / (300 * 1024); 6520Sstevel@tonic-gate /* in case file file is < 100k */ 6530Sstevel@tonic-gate if (lsp->ls_dkg.dkg_ncyl == 0) 6540Sstevel@tonic-gate lsp->ls_dkg.dkg_ncyl = 1; 6550Sstevel@tonic-gate lsp->ls_dkg.dkg_acyl = 0; 6560Sstevel@tonic-gate lsp->ls_dkg.dkg_bcyl = 0; 6570Sstevel@tonic-gate lsp->ls_dkg.dkg_nhead = 1; 6580Sstevel@tonic-gate lsp->ls_dkg.dkg_obs1 = 0; 6590Sstevel@tonic-gate lsp->ls_dkg.dkg_intrlv = 0; 6600Sstevel@tonic-gate lsp->ls_dkg.dkg_obs2 = 0; 6610Sstevel@tonic-gate lsp->ls_dkg.dkg_obs3 = 0; 6620Sstevel@tonic-gate lsp->ls_dkg.dkg_apc = 0; 6630Sstevel@tonic-gate lsp->ls_dkg.dkg_rpm = 7200; 6640Sstevel@tonic-gate lsp->ls_dkg.dkg_pcyl = lsp->ls_dkg.dkg_ncyl + lsp->ls_dkg.dkg_acyl; 6650Sstevel@tonic-gate lsp->ls_dkg.dkg_nsect = lsp->ls_vp_size / 6660Sstevel@tonic-gate (DEV_BSIZE * lsp->ls_dkg.dkg_ncyl); 6670Sstevel@tonic-gate lsp->ls_dkg.dkg_write_reinstruct = 0; 6680Sstevel@tonic-gate lsp->ls_dkg.dkg_read_reinstruct = 0; 6690Sstevel@tonic-gate 6700Sstevel@tonic-gate /* vtoc - see dkio(7I) */ 6710Sstevel@tonic-gate bzero(&lsp->ls_vtoc, sizeof (struct vtoc)); 6720Sstevel@tonic-gate lsp->ls_vtoc.v_sanity = VTOC_SANE; 6730Sstevel@tonic-gate lsp->ls_vtoc.v_version = V_VERSION; 6740Sstevel@tonic-gate bcopy(LOFI_DRIVER_NAME, lsp->ls_vtoc.v_volume, 7); 6750Sstevel@tonic-gate lsp->ls_vtoc.v_sectorsz = DEV_BSIZE; 6760Sstevel@tonic-gate lsp->ls_vtoc.v_nparts = 1; 6770Sstevel@tonic-gate lsp->ls_vtoc.v_part[0].p_tag = V_UNASSIGNED; 6780Sstevel@tonic-gate lsp->ls_vtoc.v_part[0].p_flag = V_UNMNT; 6790Sstevel@tonic-gate lsp->ls_vtoc.v_part[0].p_start = (daddr_t)0; 6800Sstevel@tonic-gate /* 6810Sstevel@tonic-gate * The partition size cannot just be the number of sectors, because 6820Sstevel@tonic-gate * that might not end on a cylinder boundary. And if that's the case, 6830Sstevel@tonic-gate * newfs/mkfs will print a scary warning. So just figure the size 6840Sstevel@tonic-gate * based on the number of cylinders and sectors/cylinder. 6850Sstevel@tonic-gate */ 6860Sstevel@tonic-gate lsp->ls_vtoc.v_part[0].p_size = lsp->ls_dkg.dkg_pcyl * 6870Sstevel@tonic-gate lsp->ls_dkg.dkg_nsect * lsp->ls_dkg.dkg_nhead; 6880Sstevel@tonic-gate 6890Sstevel@tonic-gate /* dk_cinfo - see dkio(7I) */ 6900Sstevel@tonic-gate bzero(&lsp->ls_ci, sizeof (struct dk_cinfo)); 6910Sstevel@tonic-gate (void) strcpy(lsp->ls_ci.dki_cname, LOFI_DRIVER_NAME); 6920Sstevel@tonic-gate lsp->ls_ci.dki_ctype = DKC_MD; 6930Sstevel@tonic-gate lsp->ls_ci.dki_flags = 0; 6940Sstevel@tonic-gate lsp->ls_ci.dki_cnum = 0; 6950Sstevel@tonic-gate lsp->ls_ci.dki_addr = 0; 6960Sstevel@tonic-gate lsp->ls_ci.dki_space = 0; 6970Sstevel@tonic-gate lsp->ls_ci.dki_prio = 0; 6980Sstevel@tonic-gate lsp->ls_ci.dki_vec = 0; 6990Sstevel@tonic-gate (void) strcpy(lsp->ls_ci.dki_dname, LOFI_DRIVER_NAME); 7000Sstevel@tonic-gate lsp->ls_ci.dki_unit = 0; 7010Sstevel@tonic-gate lsp->ls_ci.dki_slave = 0; 7020Sstevel@tonic-gate lsp->ls_ci.dki_partition = 0; 7030Sstevel@tonic-gate /* 7040Sstevel@tonic-gate * newfs uses this to set maxcontig. Must not be < 16, or it 7050Sstevel@tonic-gate * will be 0 when newfs multiplies it by DEV_BSIZE and divides 7060Sstevel@tonic-gate * it by the block size. Then tunefs doesn't work because 7070Sstevel@tonic-gate * maxcontig is 0. 7080Sstevel@tonic-gate */ 7090Sstevel@tonic-gate lsp->ls_ci.dki_maxtransfer = 16; 7100Sstevel@tonic-gate } 7110Sstevel@tonic-gate 7120Sstevel@tonic-gate /* 7130Sstevel@tonic-gate * map a file to a minor number. Return the minor number. 7140Sstevel@tonic-gate */ 7150Sstevel@tonic-gate static int 7160Sstevel@tonic-gate lofi_map_file(dev_t dev, struct lofi_ioctl *ulip, int pickminor, 7171657Sheppo int *rvalp, struct cred *credp, int ioctl_flag) 7180Sstevel@tonic-gate { 7190Sstevel@tonic-gate minor_t newminor; 7200Sstevel@tonic-gate struct lofi_state *lsp; 7210Sstevel@tonic-gate struct lofi_ioctl *klip; 7220Sstevel@tonic-gate int error; 7230Sstevel@tonic-gate char namebuf[50]; 7240Sstevel@tonic-gate struct vnode *vp; 7250Sstevel@tonic-gate int64_t Nblocks_prop_val; 7260Sstevel@tonic-gate int64_t Size_prop_val; 7270Sstevel@tonic-gate vattr_t vattr; 7280Sstevel@tonic-gate int flag; 7290Sstevel@tonic-gate enum vtype v_type; 7300Sstevel@tonic-gate dev_t newdev; 7310Sstevel@tonic-gate int zalloced = 0; 7320Sstevel@tonic-gate 7331657Sheppo klip = copy_in_lofi_ioctl(ulip, ioctl_flag); 7340Sstevel@tonic-gate if (klip == NULL) 7350Sstevel@tonic-gate return (EFAULT); 7360Sstevel@tonic-gate 7370Sstevel@tonic-gate mutex_enter(&lofi_lock); 7380Sstevel@tonic-gate 7390Sstevel@tonic-gate if (!valid_filename(klip->li_filename)) { 7400Sstevel@tonic-gate error = EINVAL; 7410Sstevel@tonic-gate goto out; 7420Sstevel@tonic-gate } 7430Sstevel@tonic-gate 7440Sstevel@tonic-gate if (file_to_minor(klip->li_filename) != 0) { 7450Sstevel@tonic-gate error = EBUSY; 7460Sstevel@tonic-gate goto out; 7470Sstevel@tonic-gate } 7480Sstevel@tonic-gate 7490Sstevel@tonic-gate if (pickminor) { 7500Sstevel@tonic-gate /* Find a free one */ 7510Sstevel@tonic-gate for (newminor = 1; newminor <= lofi_max_files; newminor++) 7520Sstevel@tonic-gate if (ddi_get_soft_state(lofi_statep, newminor) == NULL) 7530Sstevel@tonic-gate break; 7540Sstevel@tonic-gate if (newminor >= lofi_max_files) { 7550Sstevel@tonic-gate error = EAGAIN; 7560Sstevel@tonic-gate goto out; 7570Sstevel@tonic-gate } 7580Sstevel@tonic-gate } else { 7590Sstevel@tonic-gate newminor = klip->li_minor; 7600Sstevel@tonic-gate if (ddi_get_soft_state(lofi_statep, newminor) != NULL) { 7610Sstevel@tonic-gate error = EEXIST; 7620Sstevel@tonic-gate goto out; 7630Sstevel@tonic-gate } 7640Sstevel@tonic-gate } 7650Sstevel@tonic-gate 7660Sstevel@tonic-gate /* make sure it's valid */ 7670Sstevel@tonic-gate error = lookupname(klip->li_filename, UIO_SYSSPACE, FOLLOW, 7680Sstevel@tonic-gate NULLVPP, &vp); 7690Sstevel@tonic-gate if (error) { 7700Sstevel@tonic-gate goto out; 7710Sstevel@tonic-gate } 7720Sstevel@tonic-gate v_type = vp->v_type; 7730Sstevel@tonic-gate VN_RELE(vp); 7740Sstevel@tonic-gate if (!V_ISLOFIABLE(v_type)) { 7750Sstevel@tonic-gate error = EINVAL; 7760Sstevel@tonic-gate goto out; 7770Sstevel@tonic-gate } 7780Sstevel@tonic-gate flag = FREAD | FWRITE | FOFFMAX | FEXCL; 7790Sstevel@tonic-gate error = vn_open(klip->li_filename, UIO_SYSSPACE, flag, 0, &vp, 0, 0); 7800Sstevel@tonic-gate if (error) { 7810Sstevel@tonic-gate /* try read-only */ 7820Sstevel@tonic-gate flag &= ~FWRITE; 7830Sstevel@tonic-gate error = vn_open(klip->li_filename, UIO_SYSSPACE, flag, 0, 7840Sstevel@tonic-gate &vp, 0, 0); 7850Sstevel@tonic-gate if (error) { 7860Sstevel@tonic-gate goto out; 7870Sstevel@tonic-gate } 7880Sstevel@tonic-gate } 7890Sstevel@tonic-gate vattr.va_mask = AT_SIZE; 7900Sstevel@tonic-gate error = VOP_GETATTR(vp, &vattr, 0, credp); 7910Sstevel@tonic-gate if (error) { 7920Sstevel@tonic-gate goto closeout; 7930Sstevel@tonic-gate } 7940Sstevel@tonic-gate /* the file needs to be a multiple of the block size */ 7950Sstevel@tonic-gate if ((vattr.va_size % DEV_BSIZE) != 0) { 7960Sstevel@tonic-gate error = EINVAL; 7970Sstevel@tonic-gate goto closeout; 7980Sstevel@tonic-gate } 7990Sstevel@tonic-gate newdev = makedevice(getmajor(dev), newminor); 8000Sstevel@tonic-gate Size_prop_val = vattr.va_size; 8010Sstevel@tonic-gate if ((ddi_prop_update_int64(newdev, lofi_dip, 8020Sstevel@tonic-gate SIZE_PROP_NAME, Size_prop_val)) != DDI_PROP_SUCCESS) { 8030Sstevel@tonic-gate error = EINVAL; 8040Sstevel@tonic-gate goto closeout; 8050Sstevel@tonic-gate } 8060Sstevel@tonic-gate Nblocks_prop_val = vattr.va_size / DEV_BSIZE; 8070Sstevel@tonic-gate if ((ddi_prop_update_int64(newdev, lofi_dip, 8080Sstevel@tonic-gate NBLOCKS_PROP_NAME, Nblocks_prop_val)) != DDI_PROP_SUCCESS) { 8090Sstevel@tonic-gate error = EINVAL; 8100Sstevel@tonic-gate goto propout; 8110Sstevel@tonic-gate } 8120Sstevel@tonic-gate error = ddi_soft_state_zalloc(lofi_statep, newminor); 8130Sstevel@tonic-gate if (error == DDI_FAILURE) { 8140Sstevel@tonic-gate error = ENOMEM; 8150Sstevel@tonic-gate goto propout; 8160Sstevel@tonic-gate } 8170Sstevel@tonic-gate zalloced = 1; 8180Sstevel@tonic-gate (void) snprintf(namebuf, sizeof (namebuf), "%d", newminor); 8190Sstevel@tonic-gate (void) ddi_create_minor_node(lofi_dip, namebuf, S_IFBLK, newminor, 8200Sstevel@tonic-gate DDI_PSEUDO, NULL); 8210Sstevel@tonic-gate if (error != DDI_SUCCESS) { 8220Sstevel@tonic-gate error = ENXIO; 8230Sstevel@tonic-gate goto propout; 8240Sstevel@tonic-gate } 8250Sstevel@tonic-gate (void) snprintf(namebuf, sizeof (namebuf), "%d,raw", newminor); 8260Sstevel@tonic-gate error = ddi_create_minor_node(lofi_dip, namebuf, S_IFCHR, newminor, 8270Sstevel@tonic-gate DDI_PSEUDO, NULL); 8280Sstevel@tonic-gate if (error != DDI_SUCCESS) { 8290Sstevel@tonic-gate /* remove block node */ 8300Sstevel@tonic-gate (void) snprintf(namebuf, sizeof (namebuf), "%d", newminor); 8310Sstevel@tonic-gate ddi_remove_minor_node(lofi_dip, namebuf); 8320Sstevel@tonic-gate error = ENXIO; 8330Sstevel@tonic-gate goto propout; 8340Sstevel@tonic-gate } 8350Sstevel@tonic-gate lsp = ddi_get_soft_state(lofi_statep, newminor); 8360Sstevel@tonic-gate lsp->ls_filename_sz = strlen(klip->li_filename) + 1; 8370Sstevel@tonic-gate lsp->ls_filename = kmem_alloc(lsp->ls_filename_sz, KM_SLEEP); 8380Sstevel@tonic-gate (void) snprintf(namebuf, sizeof (namebuf), "%s_taskq_%d", 8390Sstevel@tonic-gate LOFI_DRIVER_NAME, newminor); 8400Sstevel@tonic-gate lsp->ls_taskq = taskq_create(namebuf, lofi_taskq_nthreads, 8410Sstevel@tonic-gate minclsyspri, 1, lofi_taskq_maxalloc, 0); 8420Sstevel@tonic-gate lsp->ls_kstat = kstat_create(LOFI_DRIVER_NAME, newminor, 8430Sstevel@tonic-gate NULL, "disk", KSTAT_TYPE_IO, 1, 0); 8440Sstevel@tonic-gate if (lsp->ls_kstat) { 8450Sstevel@tonic-gate mutex_init(&lsp->ls_kstat_lock, NULL, MUTEX_DRIVER, NULL); 8460Sstevel@tonic-gate lsp->ls_kstat->ks_lock = &lsp->ls_kstat_lock; 8470Sstevel@tonic-gate kstat_install(lsp->ls_kstat); 8480Sstevel@tonic-gate } 8490Sstevel@tonic-gate /* 8500Sstevel@tonic-gate * save open mode so file can be closed properly and vnode counts 8510Sstevel@tonic-gate * updated correctly. 8520Sstevel@tonic-gate */ 8530Sstevel@tonic-gate lsp->ls_openflag = flag; 8540Sstevel@tonic-gate 8550Sstevel@tonic-gate /* 8560Sstevel@tonic-gate * Try to handle stacked lofs vnodes. 8570Sstevel@tonic-gate */ 8580Sstevel@tonic-gate if (vp->v_type == VREG) { 8590Sstevel@tonic-gate if (VOP_REALVP(vp, &lsp->ls_vp) != 0) { 8600Sstevel@tonic-gate lsp->ls_vp = vp; 8610Sstevel@tonic-gate } else { 8620Sstevel@tonic-gate /* 8630Sstevel@tonic-gate * Even though vp was obtained via vn_open(), we 8640Sstevel@tonic-gate * can't call vn_close() on it, since lofs will 8650Sstevel@tonic-gate * pass the VOP_CLOSE() on down to the realvp 8660Sstevel@tonic-gate * (which we are about to use). Hence we merely 8670Sstevel@tonic-gate * drop the reference to the lofs vnode and hold 8680Sstevel@tonic-gate * the realvp so things behave as if we've 8690Sstevel@tonic-gate * opened the realvp without any interaction 8700Sstevel@tonic-gate * with lofs. 8710Sstevel@tonic-gate */ 8720Sstevel@tonic-gate VN_HOLD(lsp->ls_vp); 8730Sstevel@tonic-gate VN_RELE(vp); 8740Sstevel@tonic-gate } 8750Sstevel@tonic-gate } else { 8760Sstevel@tonic-gate lsp->ls_vp = vp; 8770Sstevel@tonic-gate } 8780Sstevel@tonic-gate lsp->ls_vp_size = vattr.va_size; 8790Sstevel@tonic-gate (void) strcpy(lsp->ls_filename, klip->li_filename); 8800Sstevel@tonic-gate if (rvalp) 8810Sstevel@tonic-gate *rvalp = (int)newminor; 8820Sstevel@tonic-gate klip->li_minor = newminor; 8830Sstevel@tonic-gate 8840Sstevel@tonic-gate fake_disk_geometry(lsp); 8850Sstevel@tonic-gate mutex_exit(&lofi_lock); 8861657Sheppo (void) copy_out_lofi_ioctl(klip, ulip, ioctl_flag); 8870Sstevel@tonic-gate free_lofi_ioctl(klip); 8880Sstevel@tonic-gate return (0); 8890Sstevel@tonic-gate 8900Sstevel@tonic-gate propout: 8910Sstevel@tonic-gate (void) ddi_prop_remove(newdev, lofi_dip, SIZE_PROP_NAME); 8920Sstevel@tonic-gate (void) ddi_prop_remove(newdev, lofi_dip, NBLOCKS_PROP_NAME); 8930Sstevel@tonic-gate closeout: 8940Sstevel@tonic-gate (void) VOP_CLOSE(vp, flag, 1, 0, credp); 8950Sstevel@tonic-gate VN_RELE(vp); 8960Sstevel@tonic-gate out: 8970Sstevel@tonic-gate if (zalloced) 8980Sstevel@tonic-gate ddi_soft_state_free(lofi_statep, newminor); 8990Sstevel@tonic-gate mutex_exit(&lofi_lock); 9000Sstevel@tonic-gate free_lofi_ioctl(klip); 9010Sstevel@tonic-gate return (error); 9020Sstevel@tonic-gate } 9030Sstevel@tonic-gate 9040Sstevel@tonic-gate /* 9050Sstevel@tonic-gate * unmap a file. 9060Sstevel@tonic-gate */ 9070Sstevel@tonic-gate static int 9080Sstevel@tonic-gate lofi_unmap_file(dev_t dev, struct lofi_ioctl *ulip, int byfilename, 9091657Sheppo struct cred *credp, int ioctl_flag) 9100Sstevel@tonic-gate { 9110Sstevel@tonic-gate struct lofi_state *lsp; 9120Sstevel@tonic-gate struct lofi_ioctl *klip; 9130Sstevel@tonic-gate minor_t minor; 9140Sstevel@tonic-gate char namebuf[20]; 9150Sstevel@tonic-gate dev_t newdev; 9160Sstevel@tonic-gate 9171657Sheppo klip = copy_in_lofi_ioctl(ulip, ioctl_flag); 9180Sstevel@tonic-gate if (klip == NULL) 9190Sstevel@tonic-gate return (EFAULT); 9200Sstevel@tonic-gate 9210Sstevel@tonic-gate mutex_enter(&lofi_lock); 9220Sstevel@tonic-gate if (byfilename) { 9230Sstevel@tonic-gate minor = file_to_minor(klip->li_filename); 9240Sstevel@tonic-gate } else { 9250Sstevel@tonic-gate minor = klip->li_minor; 9260Sstevel@tonic-gate } 9270Sstevel@tonic-gate if (minor == 0) { 9280Sstevel@tonic-gate mutex_exit(&lofi_lock); 9290Sstevel@tonic-gate free_lofi_ioctl(klip); 9300Sstevel@tonic-gate return (ENXIO); 9310Sstevel@tonic-gate } 9320Sstevel@tonic-gate lsp = ddi_get_soft_state(lofi_statep, minor); 9330Sstevel@tonic-gate if (lsp == NULL) { 9340Sstevel@tonic-gate mutex_exit(&lofi_lock); 9350Sstevel@tonic-gate free_lofi_ioctl(klip); 9360Sstevel@tonic-gate return (ENXIO); 9370Sstevel@tonic-gate } 9380Sstevel@tonic-gate if (is_opened(lsp)) { 9390Sstevel@tonic-gate mutex_exit(&lofi_lock); 9400Sstevel@tonic-gate free_lofi_ioctl(klip); 9410Sstevel@tonic-gate return (EBUSY); 9420Sstevel@tonic-gate } 9430Sstevel@tonic-gate /* 9440Sstevel@tonic-gate * Use saved open mode to properly update vnode counts 9450Sstevel@tonic-gate */ 9460Sstevel@tonic-gate (void) VOP_CLOSE(lsp->ls_vp, lsp->ls_openflag, 1, 0, credp); 9470Sstevel@tonic-gate VN_RELE(lsp->ls_vp); 9480Sstevel@tonic-gate lsp->ls_vp = NULL; 9490Sstevel@tonic-gate newdev = makedevice(getmajor(dev), minor); 9500Sstevel@tonic-gate (void) ddi_prop_remove(newdev, lofi_dip, SIZE_PROP_NAME); 9510Sstevel@tonic-gate (void) ddi_prop_remove(newdev, lofi_dip, NBLOCKS_PROP_NAME); 9520Sstevel@tonic-gate 9530Sstevel@tonic-gate (void) snprintf(namebuf, sizeof (namebuf), "%d", minor); 9540Sstevel@tonic-gate ddi_remove_minor_node(lofi_dip, namebuf); 9550Sstevel@tonic-gate (void) snprintf(namebuf, sizeof (namebuf), "%d,raw", minor); 9560Sstevel@tonic-gate ddi_remove_minor_node(lofi_dip, namebuf); 9570Sstevel@tonic-gate 9580Sstevel@tonic-gate kmem_free(lsp->ls_filename, lsp->ls_filename_sz); 9590Sstevel@tonic-gate taskq_destroy(lsp->ls_taskq); 9600Sstevel@tonic-gate if (lsp->ls_kstat) { 9610Sstevel@tonic-gate kstat_delete(lsp->ls_kstat); 9620Sstevel@tonic-gate mutex_destroy(&lsp->ls_kstat_lock); 9630Sstevel@tonic-gate } 9640Sstevel@tonic-gate ddi_soft_state_free(lofi_statep, minor); 9650Sstevel@tonic-gate klip->li_minor = minor; 9660Sstevel@tonic-gate mutex_exit(&lofi_lock); 9671657Sheppo (void) copy_out_lofi_ioctl(klip, ulip, ioctl_flag); 9680Sstevel@tonic-gate free_lofi_ioctl(klip); 9690Sstevel@tonic-gate return (0); 9700Sstevel@tonic-gate } 9710Sstevel@tonic-gate 9720Sstevel@tonic-gate /* 9730Sstevel@tonic-gate * get the filename given the minor number, or the minor number given 9740Sstevel@tonic-gate * the name. 9750Sstevel@tonic-gate */ 9760Sstevel@tonic-gate /*ARGSUSED3*/ 9770Sstevel@tonic-gate static int 9780Sstevel@tonic-gate lofi_get_info(dev_t dev, struct lofi_ioctl *ulip, int which, 9791657Sheppo struct cred *credp, int ioctl_flag) 9800Sstevel@tonic-gate { 9810Sstevel@tonic-gate struct lofi_state *lsp; 9820Sstevel@tonic-gate struct lofi_ioctl *klip; 9830Sstevel@tonic-gate int error; 9840Sstevel@tonic-gate minor_t minor; 9850Sstevel@tonic-gate 9860Sstevel@tonic-gate #ifdef lint 9870Sstevel@tonic-gate dev = dev; 9880Sstevel@tonic-gate #endif 9891657Sheppo klip = copy_in_lofi_ioctl(ulip, ioctl_flag); 9900Sstevel@tonic-gate if (klip == NULL) 9910Sstevel@tonic-gate return (EFAULT); 9920Sstevel@tonic-gate 9930Sstevel@tonic-gate switch (which) { 9940Sstevel@tonic-gate case LOFI_GET_FILENAME: 9950Sstevel@tonic-gate minor = klip->li_minor; 9960Sstevel@tonic-gate if (minor == 0) { 9970Sstevel@tonic-gate free_lofi_ioctl(klip); 9980Sstevel@tonic-gate return (EINVAL); 9990Sstevel@tonic-gate } 10000Sstevel@tonic-gate 10010Sstevel@tonic-gate mutex_enter(&lofi_lock); 10020Sstevel@tonic-gate lsp = ddi_get_soft_state(lofi_statep, minor); 10030Sstevel@tonic-gate if (lsp == NULL) { 10040Sstevel@tonic-gate mutex_exit(&lofi_lock); 10050Sstevel@tonic-gate free_lofi_ioctl(klip); 10060Sstevel@tonic-gate return (ENXIO); 10070Sstevel@tonic-gate } 10080Sstevel@tonic-gate (void) strcpy(klip->li_filename, lsp->ls_filename); 10090Sstevel@tonic-gate mutex_exit(&lofi_lock); 10101657Sheppo error = copy_out_lofi_ioctl(klip, ulip, ioctl_flag); 10110Sstevel@tonic-gate free_lofi_ioctl(klip); 10120Sstevel@tonic-gate return (error); 10130Sstevel@tonic-gate case LOFI_GET_MINOR: 10140Sstevel@tonic-gate mutex_enter(&lofi_lock); 10150Sstevel@tonic-gate klip->li_minor = file_to_minor(klip->li_filename); 10160Sstevel@tonic-gate mutex_exit(&lofi_lock); 10170Sstevel@tonic-gate if (klip->li_minor == 0) { 10180Sstevel@tonic-gate free_lofi_ioctl(klip); 10190Sstevel@tonic-gate return (ENOENT); 10200Sstevel@tonic-gate } 10211657Sheppo error = copy_out_lofi_ioctl(klip, ulip, ioctl_flag); 10220Sstevel@tonic-gate free_lofi_ioctl(klip); 10230Sstevel@tonic-gate return (error); 10240Sstevel@tonic-gate default: 10250Sstevel@tonic-gate free_lofi_ioctl(klip); 10260Sstevel@tonic-gate return (EINVAL); 10270Sstevel@tonic-gate } 10280Sstevel@tonic-gate 10290Sstevel@tonic-gate } 10300Sstevel@tonic-gate 10310Sstevel@tonic-gate static int 10320Sstevel@tonic-gate lofi_ioctl(dev_t dev, int cmd, intptr_t arg, int flag, cred_t *credp, 10330Sstevel@tonic-gate int *rvalp) 10340Sstevel@tonic-gate { 10350Sstevel@tonic-gate int error; 10360Sstevel@tonic-gate enum dkio_state dkstate; 10370Sstevel@tonic-gate struct lofi_state *lsp; 10380Sstevel@tonic-gate minor_t minor; 10390Sstevel@tonic-gate 10400Sstevel@tonic-gate #ifdef lint 10410Sstevel@tonic-gate credp = credp; 10420Sstevel@tonic-gate #endif 10430Sstevel@tonic-gate 10440Sstevel@tonic-gate minor = getminor(dev); 10450Sstevel@tonic-gate /* lofi ioctls only apply to the master device */ 10460Sstevel@tonic-gate if (minor == 0) { 10470Sstevel@tonic-gate struct lofi_ioctl *lip = (struct lofi_ioctl *)arg; 10480Sstevel@tonic-gate 10490Sstevel@tonic-gate /* 10500Sstevel@tonic-gate * the query command only need read-access - i.e., normal 10510Sstevel@tonic-gate * users are allowed to do those on the ctl device as 10520Sstevel@tonic-gate * long as they can open it read-only. 10530Sstevel@tonic-gate */ 10540Sstevel@tonic-gate switch (cmd) { 10550Sstevel@tonic-gate case LOFI_MAP_FILE: 10560Sstevel@tonic-gate if ((flag & FWRITE) == 0) 10570Sstevel@tonic-gate return (EPERM); 10581657Sheppo return (lofi_map_file(dev, lip, 1, rvalp, credp, flag)); 10590Sstevel@tonic-gate case LOFI_MAP_FILE_MINOR: 10600Sstevel@tonic-gate if ((flag & FWRITE) == 0) 10610Sstevel@tonic-gate return (EPERM); 10621657Sheppo return (lofi_map_file(dev, lip, 0, rvalp, credp, flag)); 10630Sstevel@tonic-gate case LOFI_UNMAP_FILE: 10640Sstevel@tonic-gate if ((flag & FWRITE) == 0) 10650Sstevel@tonic-gate return (EPERM); 10661657Sheppo return (lofi_unmap_file(dev, lip, 1, credp, flag)); 10670Sstevel@tonic-gate case LOFI_UNMAP_FILE_MINOR: 10680Sstevel@tonic-gate if ((flag & FWRITE) == 0) 10690Sstevel@tonic-gate return (EPERM); 10701657Sheppo return (lofi_unmap_file(dev, lip, 0, credp, flag)); 10710Sstevel@tonic-gate case LOFI_GET_FILENAME: 10720Sstevel@tonic-gate return (lofi_get_info(dev, lip, LOFI_GET_FILENAME, 10731657Sheppo credp, flag)); 10740Sstevel@tonic-gate case LOFI_GET_MINOR: 10750Sstevel@tonic-gate return (lofi_get_info(dev, lip, LOFI_GET_MINOR, 10761657Sheppo credp, flag)); 10770Sstevel@tonic-gate case LOFI_GET_MAXMINOR: 10781657Sheppo error = ddi_copyout(&lofi_max_files, &lip->li_minor, 10791657Sheppo sizeof (lofi_max_files), flag); 10800Sstevel@tonic-gate if (error) 10810Sstevel@tonic-gate return (EFAULT); 10820Sstevel@tonic-gate return (0); 10830Sstevel@tonic-gate default: 10840Sstevel@tonic-gate break; 10850Sstevel@tonic-gate } 10860Sstevel@tonic-gate } 10870Sstevel@tonic-gate 10880Sstevel@tonic-gate lsp = ddi_get_soft_state(lofi_statep, minor); 10890Sstevel@tonic-gate if (lsp == NULL) 10900Sstevel@tonic-gate return (ENXIO); 10910Sstevel@tonic-gate 10920Sstevel@tonic-gate /* these are for faking out utilities like newfs */ 10930Sstevel@tonic-gate switch (cmd) { 10940Sstevel@tonic-gate case DKIOCGVTOC: 10950Sstevel@tonic-gate switch (ddi_model_convert_from(flag & FMODELS)) { 10960Sstevel@tonic-gate case DDI_MODEL_ILP32: { 10970Sstevel@tonic-gate struct vtoc32 vtoc32; 10980Sstevel@tonic-gate 10990Sstevel@tonic-gate vtoctovtoc32(lsp->ls_vtoc, vtoc32); 11000Sstevel@tonic-gate if (ddi_copyout(&vtoc32, (void *)arg, 11010Sstevel@tonic-gate sizeof (struct vtoc32), flag)) 11020Sstevel@tonic-gate return (EFAULT); 11030Sstevel@tonic-gate break; 11040Sstevel@tonic-gate } 11050Sstevel@tonic-gate 11060Sstevel@tonic-gate case DDI_MODEL_NONE: 11070Sstevel@tonic-gate if (ddi_copyout(&lsp->ls_vtoc, (void *)arg, 11080Sstevel@tonic-gate sizeof (struct vtoc), flag)) 11090Sstevel@tonic-gate return (EFAULT); 11100Sstevel@tonic-gate break; 11110Sstevel@tonic-gate } 11120Sstevel@tonic-gate return (0); 11130Sstevel@tonic-gate case DKIOCINFO: 11141657Sheppo error = ddi_copyout(&lsp->ls_ci, (void *)arg, 11151657Sheppo sizeof (struct dk_cinfo), flag); 11160Sstevel@tonic-gate if (error) 11170Sstevel@tonic-gate return (EFAULT); 11180Sstevel@tonic-gate return (0); 11190Sstevel@tonic-gate case DKIOCG_VIRTGEOM: 11200Sstevel@tonic-gate case DKIOCG_PHYGEOM: 11210Sstevel@tonic-gate case DKIOCGGEOM: 11221657Sheppo error = ddi_copyout(&lsp->ls_dkg, (void *)arg, 11231657Sheppo sizeof (struct dk_geom), flag); 11240Sstevel@tonic-gate if (error) 11250Sstevel@tonic-gate return (EFAULT); 11260Sstevel@tonic-gate return (0); 11270Sstevel@tonic-gate case DKIOCSTATE: 11280Sstevel@tonic-gate /* the file is always there */ 11290Sstevel@tonic-gate dkstate = DKIO_INSERTED; 11301657Sheppo error = ddi_copyout(&dkstate, (void *)arg, 11311657Sheppo sizeof (enum dkio_state), flag); 11320Sstevel@tonic-gate if (error) 11330Sstevel@tonic-gate return (EFAULT); 11340Sstevel@tonic-gate return (0); 11350Sstevel@tonic-gate default: 11360Sstevel@tonic-gate return (ENOTTY); 11370Sstevel@tonic-gate } 11380Sstevel@tonic-gate } 11390Sstevel@tonic-gate 11400Sstevel@tonic-gate static struct cb_ops lofi_cb_ops = { 11410Sstevel@tonic-gate lofi_open, /* open */ 11420Sstevel@tonic-gate lofi_close, /* close */ 11430Sstevel@tonic-gate lofi_strategy, /* strategy */ 11440Sstevel@tonic-gate nodev, /* print */ 11450Sstevel@tonic-gate nodev, /* dump */ 11460Sstevel@tonic-gate lofi_read, /* read */ 11470Sstevel@tonic-gate lofi_write, /* write */ 11480Sstevel@tonic-gate lofi_ioctl, /* ioctl */ 11490Sstevel@tonic-gate nodev, /* devmap */ 11500Sstevel@tonic-gate nodev, /* mmap */ 11510Sstevel@tonic-gate nodev, /* segmap */ 11520Sstevel@tonic-gate nochpoll, /* poll */ 11530Sstevel@tonic-gate ddi_prop_op, /* prop_op */ 11540Sstevel@tonic-gate 0, /* streamtab */ 11550Sstevel@tonic-gate D_64BIT | D_NEW | D_MP, /* Driver compatibility flag */ 11560Sstevel@tonic-gate CB_REV, 11570Sstevel@tonic-gate lofi_aread, 11580Sstevel@tonic-gate lofi_awrite 11590Sstevel@tonic-gate }; 11600Sstevel@tonic-gate 11610Sstevel@tonic-gate static struct dev_ops lofi_ops = { 11620Sstevel@tonic-gate DEVO_REV, /* devo_rev, */ 11630Sstevel@tonic-gate 0, /* refcnt */ 11640Sstevel@tonic-gate lofi_info, /* info */ 11650Sstevel@tonic-gate nulldev, /* identify */ 11660Sstevel@tonic-gate nulldev, /* probe */ 11670Sstevel@tonic-gate lofi_attach, /* attach */ 11680Sstevel@tonic-gate lofi_detach, /* detach */ 11690Sstevel@tonic-gate nodev, /* reset */ 11700Sstevel@tonic-gate &lofi_cb_ops, /* driver operations */ 11710Sstevel@tonic-gate NULL /* no bus operations */ 11720Sstevel@tonic-gate }; 11730Sstevel@tonic-gate 11740Sstevel@tonic-gate static struct modldrv modldrv = { 11750Sstevel@tonic-gate &mod_driverops, 11760Sstevel@tonic-gate "loopback file driver (%I%)", 11770Sstevel@tonic-gate &lofi_ops, 11780Sstevel@tonic-gate }; 11790Sstevel@tonic-gate 11800Sstevel@tonic-gate static struct modlinkage modlinkage = { 11810Sstevel@tonic-gate MODREV_1, 11820Sstevel@tonic-gate &modldrv, 11830Sstevel@tonic-gate NULL 11840Sstevel@tonic-gate }; 11850Sstevel@tonic-gate 11860Sstevel@tonic-gate int 11870Sstevel@tonic-gate _init(void) 11880Sstevel@tonic-gate { 11890Sstevel@tonic-gate int error; 11900Sstevel@tonic-gate 11910Sstevel@tonic-gate error = ddi_soft_state_init(&lofi_statep, 11920Sstevel@tonic-gate sizeof (struct lofi_state), 0); 11930Sstevel@tonic-gate if (error) 11940Sstevel@tonic-gate return (error); 11950Sstevel@tonic-gate 11960Sstevel@tonic-gate mutex_init(&lofi_lock, NULL, MUTEX_DRIVER, NULL); 11970Sstevel@tonic-gate error = mod_install(&modlinkage); 11980Sstevel@tonic-gate if (error) { 11990Sstevel@tonic-gate mutex_destroy(&lofi_lock); 12000Sstevel@tonic-gate ddi_soft_state_fini(&lofi_statep); 12010Sstevel@tonic-gate } 12020Sstevel@tonic-gate 12030Sstevel@tonic-gate return (error); 12040Sstevel@tonic-gate } 12050Sstevel@tonic-gate 12060Sstevel@tonic-gate int 12070Sstevel@tonic-gate _fini(void) 12080Sstevel@tonic-gate { 12090Sstevel@tonic-gate int error; 12100Sstevel@tonic-gate 12110Sstevel@tonic-gate if (lofi_busy()) 12120Sstevel@tonic-gate return (EBUSY); 12130Sstevel@tonic-gate 12140Sstevel@tonic-gate error = mod_remove(&modlinkage); 12150Sstevel@tonic-gate if (error) 12160Sstevel@tonic-gate return (error); 12170Sstevel@tonic-gate 12180Sstevel@tonic-gate mutex_destroy(&lofi_lock); 12190Sstevel@tonic-gate ddi_soft_state_fini(&lofi_statep); 12200Sstevel@tonic-gate 12210Sstevel@tonic-gate return (error); 12220Sstevel@tonic-gate } 12230Sstevel@tonic-gate 12240Sstevel@tonic-gate int 12250Sstevel@tonic-gate _info(struct modinfo *modinfop) 12260Sstevel@tonic-gate { 12270Sstevel@tonic-gate return (mod_info(&modlinkage, modinfop)); 12280Sstevel@tonic-gate } 1229