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