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 */ 24 25 #include <sys/zfs_context.h> 26 #include <sys/spa.h> 27 #include <sys/vdev_file.h> 28 #include <sys/vdev_impl.h> 29 #include <sys/zio.h> 30 #include <sys/fs/zfs.h> 31 #include <sys/fm/fs/zfs.h> 32 33 /* 34 * Virtual device vector for files. 35 */ 36 37 static void 38 vdev_file_hold(vdev_t *vd) 39 { 40 ASSERT(vd->vdev_path != NULL); 41 } 42 43 static void 44 vdev_file_rele(vdev_t *vd) 45 { 46 ASSERT(vd->vdev_path != NULL); 47 } 48 49 static int 50 vdev_file_open(vdev_t *vd, uint64_t *psize, uint64_t *ashift) 51 { 52 vdev_file_t *vf; 53 vnode_t *vp; 54 vattr_t vattr; 55 int error; 56 57 /* 58 * We must have a pathname, and it must be absolute. 59 */ 60 if (vd->vdev_path == NULL || vd->vdev_path[0] != '/') { 61 vd->vdev_stat.vs_aux = VDEV_AUX_BAD_LABEL; 62 return (EINVAL); 63 } 64 65 /* 66 * Reopen the device if it's not currently open. Otherwise, 67 * just update the physical size of the device. 68 */ 69 if (vd->vdev_tsd != NULL) { 70 ASSERT(vd->vdev_reopening); 71 vf = vd->vdev_tsd; 72 goto skip_open; 73 } 74 75 vf = vd->vdev_tsd = kmem_zalloc(sizeof (vdev_file_t), KM_SLEEP); 76 77 /* 78 * We always open the files from the root of the global zone, even if 79 * we're in a local zone. If the user has gotten to this point, the 80 * administrator has already decided that the pool should be available 81 * to local zone users, so the underlying devices should be as well. 82 */ 83 ASSERT(vd->vdev_path != NULL && vd->vdev_path[0] == '/'); 84 error = vn_openat(vd->vdev_path + 1, UIO_SYSSPACE, 85 spa_mode(vd->vdev_spa) | FOFFMAX, 0, &vp, 0, 0, rootdir, -1); 86 87 if (error) { 88 vd->vdev_stat.vs_aux = VDEV_AUX_OPEN_FAILED; 89 return (error); 90 } 91 92 vf->vf_vnode = vp; 93 94 #ifdef _KERNEL 95 /* 96 * Make sure it's a regular file. 97 */ 98 if (vp->v_type != VREG) { 99 vd->vdev_stat.vs_aux = VDEV_AUX_OPEN_FAILED; 100 return (ENODEV); 101 } 102 #endif 103 104 skip_open: 105 /* 106 * Determine the physical size of the file. 107 */ 108 vattr.va_mask = AT_SIZE; 109 error = VOP_GETATTR(vf->vf_vnode, &vattr, 0, kcred, NULL); 110 if (error) { 111 vd->vdev_stat.vs_aux = VDEV_AUX_OPEN_FAILED; 112 return (error); 113 } 114 115 *psize = vattr.va_size; 116 *ashift = SPA_MINBLOCKSHIFT; 117 118 return (0); 119 } 120 121 static void 122 vdev_file_close(vdev_t *vd) 123 { 124 vdev_file_t *vf = vd->vdev_tsd; 125 126 if (vd->vdev_reopening || vf == NULL) 127 return; 128 129 if (vf->vf_vnode != NULL) { 130 (void) VOP_PUTPAGE(vf->vf_vnode, 0, 0, B_INVAL, kcred, NULL); 131 (void) VOP_CLOSE(vf->vf_vnode, spa_mode(vd->vdev_spa), 1, 0, 132 kcred, NULL); 133 VN_RELE(vf->vf_vnode); 134 } 135 136 vd->vdev_delayed_close = B_FALSE; 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