xref: /dragonfly/sys/vfs/fuse/fuse_io.c (revision 0b2c5ee3)
1 /*-
2  * Copyright (c) 2019 Tomohiro Kusumi <tkusumi@netbsd.org>
3  * Copyright (c) 2019 The DragonFly Project
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  */
27 
28 #include "fuse.h"
29 
30 #include <sys/uio.h>
31 #include <sys/buf2.h>
32 
33 static void
34 fuse_brelse(struct buf *bp)
35 {
36 	bp->b_flags |= B_INVAL | B_RELBUF;
37 	brelse(bp);
38 }
39 
40 static void
41 fuse_fix_size(struct fuse_node *fnp, bool fixsize, size_t oldsize)
42 {
43 	if (fixsize)
44 		fuse_node_truncate(fnp, fnp->size, oldsize);
45 }
46 
47 int
48 fuse_read(struct vop_read_args *ap)
49 {
50 	struct vnode *vp = ap->a_vp;
51 	struct uio *uio = ap->a_uio;
52 	struct fuse_mount *fmp = VFSTOFUSE(vp->v_mount);
53 	struct fuse_node *fnp = VTOI(vp);
54 	bool need_reopen = !curproc || fnp->closed; /* XXX */
55 	int error = 0;
56 
57 	while (uio->uio_resid > 0 && uio->uio_offset < fnp->size) {
58 		struct file *fp;
59 		struct buf *bp;
60 		struct fuse_ipc *fip;
61 		struct fuse_read_in *fri;
62 		off_t base_offset, buf_offset;
63 		size_t len;
64 		uint64_t fh;
65 
66 		fh = fuse_nfh(VTOI(vp));
67 		if (ap->a_fp)
68 			fh = fuse_fh(ap->a_fp);
69 
70 		buf_offset = (off_t)uio->uio_offset & FUSE_BLKMASK64;
71 		base_offset = (off_t)uio->uio_offset - buf_offset;
72 
73 		fuse_dbg("uio_offset=%ju uio_resid=%ju base_offset=%ju "
74 		    "buf_offset=%ju\n",
75 		    uio->uio_offset, uio->uio_resid, base_offset, buf_offset);
76 
77 		bp = getblk(vp, base_offset, FUSE_BLKSIZE, 0, 0);
78 		KKASSERT(bp);
79 		if ((bp->b_flags & (B_INVAL | B_CACHE | B_RAM)) == B_CACHE) {
80 			bp->b_flags &= ~B_AGE;
81 			goto skip;
82 		}
83 		if (ap->a_ioflag & IO_NRDELAY) {
84 			bqrelse(bp);
85 			return EWOULDBLOCK;
86 		}
87 
88 		error = breadnx(vp, base_offset, FUSE_BLKSIZE, B_NOTMETA, NULL,
89 		    NULL, 0, &bp);
90 		KKASSERT(!error);
91 
92 		fuse_dbg("b_loffset=%ju b_bcount=%d b_flags=%x\n",
93 		    bp->b_loffset, bp->b_bcount, bp->b_flags);
94 
95 		if (need_reopen) {
96 			error = falloc(NULL, &fp, NULL);
97 			if (error) {
98 				fuse_brelse(bp);
99 				break;
100 			}
101 			error = VOP_OPEN(vp, FREAD | FWRITE, ap->a_cred, &fp);
102 			if (error) {
103 				fuse_brelse(bp);
104 				break;
105 			}
106 		}
107 
108 		fip = fuse_ipc_get(fmp, sizeof(*fri));
109 		fri = fuse_ipc_fill(fip, FUSE_READ, fnp->ino, ap->a_cred);
110 		fri->offset = bp->b_loffset;
111 		fri->size = bp->b_bcount;
112 		if (need_reopen)
113 			fri->fh = fuse_nfh(VTOI(vp));
114 		else
115 			fri->fh = fh;
116 
117 		fuse_dbg("fuse_read_in offset=%ju size=%u fh=%jx\n",
118 		    fri->offset, fri->size, fri->fh);
119 
120 		error = fuse_ipc_tx(fip);
121 		if (error) {
122 			fuse_brelse(bp);
123 			break;
124 		}
125 		memcpy(bp->b_data, fuse_out_data(fip), fuse_out_data_size(fip));
126 		fuse_ipc_put(fip);
127 
128 		if (need_reopen) {
129 			error = fdrop(fp); /* calls VOP_CLOSE() */
130 			if (error) {
131 				fuse_brelse(bp);
132 				break;
133 			}
134 		}
135 skip:
136 		len = FUSE_BLKSIZE - buf_offset;
137 		if (len > uio->uio_resid)
138 			len = uio->uio_resid;
139 		if (uio->uio_offset + len > fnp->size)
140 			len = (size_t)(fnp->size - uio->uio_offset);
141 		fuse_dbg("size=%ju len=%ju\n", fnp->size, len);
142 
143 		error = uiomovebp(bp, bp->b_data + buf_offset, len, uio);
144 		bqrelse(bp);
145 		if (error)
146 			break;
147 	}
148 
149 	fuse_dbg("uio_offset=%ju uio_resid=%ju error=%d done\n",
150 	    uio->uio_offset, uio->uio_resid, error);
151 
152 	return error;
153 }
154 
155 int
156 fuse_write(struct vop_write_args *ap)
157 {
158 	return fuse_dio_write(ap);
159 }
160 
161 int
162 fuse_dio_write(struct vop_write_args *ap)
163 {
164 	struct vnode *vp = ap->a_vp;
165 	struct uio *uio = ap->a_uio;
166 	struct fuse_mount *fmp = VFSTOFUSE(vp->v_mount);
167 	struct fuse_node *fnp = VTOI(vp);
168 	bool need_reopen = !curproc || fnp->closed; /* XXX */
169 	int kflags = 0;
170 	int error = 0;
171 
172 	if (ap->a_ioflag & IO_APPEND)
173 		uio->uio_offset = fnp->size;
174 
175 	while (uio->uio_resid > 0) {
176 		struct file *fp;
177 		struct buf *bp;
178 		struct fuse_ipc *fip;
179 		struct fuse_read_in *fri;
180 		struct fuse_write_in *fwi;
181 		struct fuse_write_out *fwo;
182 		off_t base_offset, buf_offset;
183 		size_t len, oldsize;
184 		uint64_t fh;
185 		bool fixsize = false;
186 		bool need_read = false;
187 
188 		fh = fuse_nfh(VTOI(vp));
189 		if (ap->a_fp)
190 			fh = fuse_fh(ap->a_fp);
191 
192 		buf_offset = (off_t)uio->uio_offset & FUSE_BLKMASK64;
193 		base_offset = (off_t)uio->uio_offset - buf_offset;
194 
195 		fuse_dbg("uio_offset=%ju uio_resid=%ju base_offset=%ju "
196 		    "buf_offset=%ju\n",
197 		    uio->uio_offset, uio->uio_resid, base_offset, buf_offset);
198 
199 		oldsize = fnp->size;
200 		len = FUSE_BLKSIZE - buf_offset;
201 		if (len > uio->uio_resid)
202 			len = uio->uio_resid;
203 		if (uio->uio_offset + len > fnp->size) {
204 			/* XXX trivial flag */
205 			error = fuse_node_truncate(fnp, fnp->size,
206 			    uio->uio_offset + len);
207 			if (error)
208 				break;
209 			fixsize = true;
210 			kflags |= NOTE_EXTEND;
211 		}
212 		fuse_dbg("size=%ju len=%ju\n", fnp->size, len);
213 
214 		bp = NULL;
215 		if (uio->uio_segflg == UIO_NOCOPY) {
216 			bp = getblk(ap->a_vp, base_offset, FUSE_BLKSIZE,
217 			    GETBLK_BHEAVY, 0);
218 			if (!(bp->b_flags & B_CACHE)) {
219 				bqrelse(bp);
220 				need_read = true;
221 			}
222 		} else if (!buf_offset && uio->uio_resid >= FUSE_BLKSIZE) {
223 			bp = getblk(ap->a_vp, base_offset, FUSE_BLKSIZE,
224 			    GETBLK_BHEAVY, 0);
225 			if (!(bp->b_flags & B_CACHE))
226 				vfs_bio_clrbuf(bp);
227 		} else if (base_offset >= fnp->size) {
228 			bp = getblk(ap->a_vp, base_offset, FUSE_BLKSIZE,
229 			    GETBLK_BHEAVY, 0);
230 			vfs_bio_clrbuf(bp);
231 		} else {
232 			need_read = true;
233 		}
234 
235 		if (bp)
236 			fuse_dbg("b_loffset=%ju b_bcount=%d b_flags=%x\n",
237 			    bp->b_loffset, bp->b_bcount, bp->b_flags);
238 
239 		if (need_reopen) {
240 			error = falloc(NULL, &fp, NULL);
241 			if (error) {
242 				fuse_brelse(bp);
243 				fuse_fix_size(fnp, fixsize, oldsize);
244 				break;
245 			}
246 			/* XXX can panic at vref() in vop_stdopen() */
247 			error = VOP_OPEN(vp, FREAD | FWRITE, ap->a_cred, &fp);
248 			if (error) {
249 				fuse_brelse(bp);
250 				fuse_fix_size(fnp, fixsize, oldsize);
251 				break;
252 			}
253 		}
254 
255 		if (need_read) {
256 			error = bread(ap->a_vp, base_offset, FUSE_BLKSIZE, &bp);
257 			KKASSERT(!error);
258 
259 			fuse_dbg("b_loffset=%ju b_bcount=%d b_flags=%x\n",
260 			    bp->b_loffset, bp->b_bcount, bp->b_flags);
261 
262 			if (bp->b_loffset + (buf_offset + len) > oldsize) {
263 				memset(bp->b_data, 0, FUSE_BLKSIZE); /* XXX */
264 				goto skip; /* prevent EBADF */
265 			}
266 
267 			fip = fuse_ipc_get(fmp, sizeof(*fri));
268 			fri = fuse_ipc_fill(fip, FUSE_READ, fnp->ino,
269 			    ap->a_cred);
270 			fri->offset = bp->b_loffset;
271 			fri->size = buf_offset + len;
272 			if (need_reopen)
273 				fri->fh = fuse_nfh(VTOI(vp));
274 			else
275 				fri->fh = fh;
276 
277 			fuse_dbg("fuse_read_in offset=%ju size=%u fh=%jx\n",
278 			    fri->offset, fri->size, fri->fh);
279 
280 			error = fuse_ipc_tx(fip);
281 			if (error) {
282 				fuse_brelse(bp);
283 				fuse_fix_size(fnp, fixsize, oldsize);
284 				break;
285 			}
286 			memcpy(bp->b_data, fuse_out_data(fip),
287 			    fuse_out_data_size(fip));
288 			fuse_ipc_put(fip);
289 		}
290 skip:
291 		error = uiomovebp(bp, bp->b_data + buf_offset, len, uio);
292 		if (error) {
293 			bqrelse(bp);
294 			fuse_fix_size(fnp, fixsize, oldsize);
295 			break;
296 		}
297 		kflags |= NOTE_WRITE;
298 
299 		fip = fuse_ipc_get(fmp, sizeof(*fwi) + len);
300 		fwi = fuse_ipc_fill(fip, FUSE_WRITE, fnp->ino, ap->a_cred);
301 		fwi->offset = bp->b_loffset + buf_offset;
302 		fwi->size = len;
303 		if (need_reopen)
304 			fwi->fh = fuse_nfh(VTOI(vp));
305 		else
306 			fwi->fh = fh;
307 		memcpy((void*)(fwi + 1), bp->b_data + buf_offset, len);
308 
309 		fuse_dbg("fuse_write_in offset=%ju size=%u fh=%jx\n",
310 		    fwi->offset, fwi->size, fwi->fh);
311 
312 		error = fuse_ipc_tx(fip);
313 		if (error) {
314 			fuse_brelse(bp);
315 			fuse_fix_size(fnp, fixsize, oldsize);
316 			break;
317 		}
318 		fwo = fuse_out_data(fip);
319 		if (fwo->size != len) {
320 			fuse_ipc_put(fip);
321 			fuse_brelse(bp);
322 			fuse_fix_size(fnp, fixsize, oldsize);
323 			break;
324 		}
325 		fuse_ipc_put(fip);
326 
327 		if (need_reopen) {
328 			error = fdrop(fp); /* calls VOP_CLOSE() */
329 			if (error) {
330 				fuse_brelse(bp);
331 				fuse_fix_size(fnp, fixsize, oldsize);
332 				break;
333 			}
334 		}
335 
336 		error = bwrite(bp);
337 		KKASSERT(!error);
338 	}
339 
340 	fuse_knote(ap->a_vp, kflags);
341 
342 	fuse_dbg("uio_offset=%ju uio_resid=%ju error=%d done\n",
343 	    uio->uio_offset, uio->uio_resid, error);
344 
345 	return error;
346 }
347