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 (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. 23 * Copyright (c) 2012 by Delphix. All rights reserved. 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 *max_psize, 52 uint64_t *ashift) 53 { 54 vdev_file_t *vf; 55 vnode_t *vp; 56 vattr_t vattr; 57 int error; 58 59 /* 60 * We must have a pathname, and it must be absolute. 61 */ 62 if (vd->vdev_path == NULL || vd->vdev_path[0] != '/') { 63 vd->vdev_stat.vs_aux = VDEV_AUX_BAD_LABEL; 64 return (EINVAL); 65 } 66 67 /* 68 * Reopen the device if it's not currently open. Otherwise, 69 * just update the physical size of the device. 70 */ 71 if (vd->vdev_tsd != NULL) { 72 ASSERT(vd->vdev_reopening); 73 vf = vd->vdev_tsd; 74 goto skip_open; 75 } 76 77 vf = vd->vdev_tsd = kmem_zalloc(sizeof (vdev_file_t), KM_SLEEP); 78 79 /* 80 * We always open the files from the root of the global zone, even if 81 * we're in a local zone. If the user has gotten to this point, the 82 * administrator has already decided that the pool should be available 83 * to local zone users, so the underlying devices should be as well. 84 */ 85 ASSERT(vd->vdev_path != NULL && vd->vdev_path[0] == '/'); 86 error = vn_openat(vd->vdev_path + 1, UIO_SYSSPACE, 87 spa_mode(vd->vdev_spa) | FOFFMAX, 0, &vp, 0, 0, rootdir, -1); 88 89 if (error) { 90 vd->vdev_stat.vs_aux = VDEV_AUX_OPEN_FAILED; 91 return (error); 92 } 93 94 vf->vf_vnode = vp; 95 96 #ifdef _KERNEL 97 /* 98 * Make sure it's a regular file. 99 */ 100 if (vp->v_type != VREG) { 101 vd->vdev_stat.vs_aux = VDEV_AUX_OPEN_FAILED; 102 return (ENODEV); 103 } 104 #endif 105 106 skip_open: 107 /* 108 * Determine the physical size of the file. 109 */ 110 vattr.va_mask = AT_SIZE; 111 error = VOP_GETATTR(vf->vf_vnode, &vattr, 0, kcred, NULL); 112 if (error) { 113 vd->vdev_stat.vs_aux = VDEV_AUX_OPEN_FAILED; 114 return (error); 115 } 116 117 *max_psize = *psize = vattr.va_size; 118 *ashift = SPA_MINBLOCKSHIFT; 119 120 return (0); 121 } 122 123 static void 124 vdev_file_close(vdev_t *vd) 125 { 126 vdev_file_t *vf = vd->vdev_tsd; 127 128 if (vd->vdev_reopening || vf == NULL) 129 return; 130 131 if (vf->vf_vnode != NULL) { 132 (void) VOP_PUTPAGE(vf->vf_vnode, 0, 0, B_INVAL, kcred, NULL); 133 (void) VOP_CLOSE(vf->vf_vnode, spa_mode(vd->vdev_spa), 1, 0, 134 kcred, NULL); 135 VN_RELE(vf->vf_vnode); 136 } 137 138 vd->vdev_delayed_close = B_FALSE; 139 kmem_free(vf, sizeof (vdev_file_t)); 140 vd->vdev_tsd = NULL; 141 } 142 143 static int 144 vdev_file_io_start(zio_t *zio) 145 { 146 vdev_t *vd = zio->io_vd; 147 vdev_file_t *vf = vd->vdev_tsd; 148 ssize_t resid; 149 150 if (zio->io_type == ZIO_TYPE_IOCTL) { 151 /* XXPOLICY */ 152 if (!vdev_readable(vd)) { 153 zio->io_error = ENXIO; 154 return (ZIO_PIPELINE_CONTINUE); 155 } 156 157 switch (zio->io_cmd) { 158 case DKIOCFLUSHWRITECACHE: 159 zio->io_error = VOP_FSYNC(vf->vf_vnode, FSYNC | FDSYNC, 160 kcred, NULL); 161 break; 162 default: 163 zio->io_error = ENOTSUP; 164 } 165 166 return (ZIO_PIPELINE_CONTINUE); 167 } 168 169 zio->io_error = vn_rdwr(zio->io_type == ZIO_TYPE_READ ? 170 UIO_READ : UIO_WRITE, vf->vf_vnode, zio->io_data, 171 zio->io_size, zio->io_offset, UIO_SYSSPACE, 172 0, RLIM64_INFINITY, kcred, &resid); 173 174 if (resid != 0 && zio->io_error == 0) 175 zio->io_error = ENOSPC; 176 177 zio_interrupt(zio); 178 179 return (ZIO_PIPELINE_STOP); 180 } 181 182 /* ARGSUSED */ 183 static void 184 vdev_file_io_done(zio_t *zio) 185 { 186 } 187 188 vdev_ops_t vdev_file_ops = { 189 vdev_file_open, 190 vdev_file_close, 191 vdev_default_asize, 192 vdev_file_io_start, 193 vdev_file_io_done, 194 NULL, 195 vdev_file_hold, 196 vdev_file_rele, 197 VDEV_TYPE_FILE, /* name of this vdev type */ 198 B_TRUE /* leaf vdev */ 199 }; 200 201 /* 202 * From userland we access disks just like files. 203 */ 204 #ifndef _KERNEL 205 206 vdev_ops_t vdev_disk_ops = { 207 vdev_file_open, 208 vdev_file_close, 209 vdev_default_asize, 210 vdev_file_io_start, 211 vdev_file_io_done, 212 NULL, 213 vdev_file_hold, 214 vdev_file_rele, 215 VDEV_TYPE_DISK, /* name of this vdev type */ 216 B_TRUE /* leaf vdev */ 217 }; 218 219 #endif 220