1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #include <sys/zfs_context.h> 30 #include <sys/spa.h> 31 #include <sys/vdev_file.h> 32 #include <sys/vdev_impl.h> 33 #include <sys/zio.h> 34 #include <sys/fs/zfs.h> 35 36 /* 37 * Virtual device vector for files. 38 */ 39 40 static int 41 vdev_file_open(vdev_t *vd, uint64_t *psize, uint64_t *ashift) 42 { 43 vdev_file_t *vf; 44 vnode_t *vp; 45 vattr_t vattr; 46 int error; 47 48 /* 49 * We must have a pathname, and it must be absolute. 50 */ 51 if (vd->vdev_path == NULL || vd->vdev_path[0] != '/') { 52 vd->vdev_stat.vs_aux = VDEV_AUX_BAD_LABEL; 53 return (EINVAL); 54 } 55 56 vf = vd->vdev_tsd = kmem_zalloc(sizeof (vdev_file_t), KM_SLEEP); 57 58 #ifdef _KERNEL 59 /* 60 * When using a file vdev in kernel context, the underlying filesystem 61 * will already be caching the data. Don't cache it again here. 62 */ 63 vd->vdev_cache.vc_size = 0; 64 #endif 65 66 /* 67 * We always open the files from the root of the global zone, even if 68 * we're in a local zone. If the user has gotten to this point, the 69 * administrator has already decided that the pool should be available 70 * to local zone users, so the underlying devices should be as well. 71 */ 72 ASSERT(vd->vdev_path != NULL && vd->vdev_path[0] == '/'); 73 error = vn_openat(vd->vdev_path + 1, UIO_SYSSPACE, spa_mode | FOFFMAX, 74 0, &vp, 0, 0, rootdir); 75 76 if (error) { 77 vd->vdev_stat.vs_aux = VDEV_AUX_OPEN_FAILED; 78 return (error); 79 } 80 81 vf->vf_vnode = vp; 82 83 #ifdef _KERNEL 84 /* 85 * Make sure it's a regular file. 86 */ 87 if (vp->v_type != VREG) { 88 vd->vdev_stat.vs_aux = VDEV_AUX_OPEN_FAILED; 89 return (ENODEV); 90 } 91 #endif 92 93 /* 94 * Determine the physical size of the file. 95 */ 96 vattr.va_mask = AT_SIZE; 97 error = VOP_GETATTR(vp, &vattr, 0, kcred); 98 if (error) { 99 vd->vdev_stat.vs_aux = VDEV_AUX_OPEN_FAILED; 100 return (error); 101 } 102 103 *psize = vattr.va_size; 104 *ashift = SPA_MINBLOCKSHIFT; 105 106 return (0); 107 } 108 109 static void 110 vdev_file_close(vdev_t *vd) 111 { 112 vdev_file_t *vf = vd->vdev_tsd; 113 114 if (vf == NULL) 115 return; 116 117 if (vf->vf_vnode != NULL) { 118 (void) VOP_PUTPAGE(vf->vf_vnode, 0, 0, B_INVAL, kcred); 119 (void) VOP_CLOSE(vf->vf_vnode, spa_mode, 1, 0, kcred); 120 VN_RELE(vf->vf_vnode); 121 } 122 123 kmem_free(vf, sizeof (vdev_file_t)); 124 vd->vdev_tsd = NULL; 125 } 126 127 static void 128 vdev_file_io_start(zio_t *zio) 129 { 130 vdev_t *vd = zio->io_vd; 131 vdev_file_t *vf = vd->vdev_tsd; 132 ssize_t resid; 133 int error; 134 135 if (zio->io_type == ZIO_TYPE_IOCTL) { 136 zio_vdev_io_bypass(zio); 137 138 /* XXPOLICY */ 139 if (vdev_is_dead(vd)) { 140 zio->io_error = ENXIO; 141 zio_next_stage_async(zio); 142 return; 143 } 144 145 switch (zio->io_cmd) { 146 case DKIOCFLUSHWRITECACHE: 147 zio->io_error = VOP_FSYNC(vf->vf_vnode, FSYNC | FDSYNC, 148 kcred); 149 dprintf("fsync(%s) = %d\n", vdev_description(vd), 150 zio->io_error); 151 break; 152 default: 153 zio->io_error = ENOTSUP; 154 } 155 156 zio_next_stage_async(zio); 157 return; 158 } 159 160 if (zio->io_type == ZIO_TYPE_READ && vdev_cache_read(zio) == 0) 161 return; 162 163 if ((zio = vdev_queue_io(zio)) == NULL) 164 return; 165 166 /* XXPOLICY */ 167 error = vdev_is_dead(vd) ? ENXIO : vdev_error_inject(vd, zio); 168 if (error) { 169 zio->io_error = error; 170 zio_next_stage_async(zio); 171 return; 172 } 173 174 zio->io_error = vn_rdwr(zio->io_type == ZIO_TYPE_READ ? 175 UIO_READ : UIO_WRITE, vf->vf_vnode, zio->io_data, 176 zio->io_size, zio->io_offset, UIO_SYSSPACE, 177 0, RLIM64_INFINITY, kcred, &resid); 178 179 if (resid != 0 && zio->io_error == 0) 180 zio->io_error = ENOSPC; 181 182 zio_next_stage_async(zio); 183 } 184 185 static void 186 vdev_file_io_done(zio_t *zio) 187 { 188 vdev_queue_io_done(zio); 189 190 if (zio->io_type == ZIO_TYPE_WRITE) 191 vdev_cache_write(zio); 192 193 zio_next_stage(zio); 194 } 195 196 vdev_ops_t vdev_file_ops = { 197 vdev_file_open, 198 vdev_file_close, 199 vdev_default_asize, 200 vdev_file_io_start, 201 vdev_file_io_done, 202 NULL, 203 VDEV_TYPE_FILE, /* name of this vdev type */ 204 B_TRUE /* leaf vdev */ 205 }; 206 207 /* 208 * From userland we access disks just like files. 209 */ 210 #ifndef _KERNEL 211 212 vdev_ops_t vdev_disk_ops = { 213 vdev_file_open, 214 vdev_file_close, 215 vdev_default_asize, 216 vdev_file_io_start, 217 vdev_file_io_done, 218 NULL, 219 VDEV_TYPE_DISK, /* name of this vdev type */ 220 B_TRUE /* leaf vdev */ 221 }; 222 223 #endif 224