xref: /illumos-gate/usr/src/uts/common/fs/zfs/vdev_file.c (revision e8031f0a)
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