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