xref: /dragonfly/sys/vfs/fuse/fuse_io.c (revision 7485684f)
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 #if 0
34 static void
35 fuse_fix_size(struct fuse_node *fnp, bool fixsize, size_t oldsize)
36 {
37 	if (fixsize)
38 		fuse_node_truncate(fnp, fnp->size, oldsize);
39 }
40 #endif
41 
42 #if 0
43 int
44 fuse_read(struct vop_read_args *ap)
45 {
46 	struct vnode *vp = ap->a_vp;
47 	struct uio *uio = ap->a_uio;
48 	struct fuse_mount *fmp = VFSTOFUSE(vp->v_mount);
49 	struct fuse_node *fnp = VTOI(vp);
50 	bool need_reopen = !curproc || fnp->closed; /* XXX */
51 	int error = 0;
52 
53 	while (uio->uio_resid > 0 && uio->uio_offset < fnp->size) {
54 		struct file *fp;
55 		struct buf *bp;
56 		struct fuse_ipc *fip;
57 		struct fuse_read_in *fri;
58 		off_t base_offset, buf_offset;
59 		size_t len;
60 		uint64_t fh;
61 
62 		fh = fuse_nfh(VTOI(vp));
63 		if (ap->a_fp)
64 			fh = fuse_fh(ap->a_fp);
65 
66 		buf_offset = (off_t)uio->uio_offset & FUSE_BLKMASK64;
67 		base_offset = (off_t)uio->uio_offset - buf_offset;
68 
69 		fuse_dbg("uio_offset=%ju uio_resid=%ju base_offset=%ju "
70 		    "buf_offset=%ju\n",
71 		    uio->uio_offset, uio->uio_resid, base_offset, buf_offset);
72 
73 		bp = getblk(vp, base_offset, FUSE_BLKSIZE, 0, 0);
74 		KKASSERT(bp);
75 		if ((bp->b_flags & (B_INVAL | B_CACHE | B_RAM)) == B_CACHE) {
76 			bp->b_flags &= ~B_AGE;
77 			goto skip;
78 		}
79 		if (ap->a_ioflag & IO_NRDELAY) {
80 			bqrelse(bp);
81 			return EWOULDBLOCK;
82 		}
83 
84 		error = breadnx(vp, base_offset, FUSE_BLKSIZE, B_NOTMETA, NULL,
85 				NULL, 0, &bp);
86 		KKASSERT(!error);
87 
88 		fuse_dbg("b_loffset=%ju b_bcount=%d b_flags=%x\n",
89 		    bp->b_loffset, bp->b_bcount, bp->b_flags);
90 
91 		if (need_reopen) {
92 			error = falloc(NULL, &fp, NULL);
93 			if (error) {
94 				fuse_brelse(bp);
95 				break;
96 			}
97 			error = VOP_OPEN(vp, FREAD | FWRITE, ap->a_cred, &fp);
98 			if (error) {
99 				fuse_brelse(bp);
100 				break;
101 			}
102 		}
103 
104 		fip = fuse_ipc_get(fmp, sizeof(*fri));
105 		fri = fuse_ipc_fill(fip, FUSE_READ, fnp->ino, ap->a_cred);
106 		fri->offset = bp->b_loffset;
107 		fri->size = bp->b_bcount;
108 		if (need_reopen)
109 			fri->fh = fuse_nfh(VTOI(vp));
110 		else
111 			fri->fh = fh;
112 
113 		fuse_dbg("fuse_read_in offset=%ju size=%u fh=%jx\n",
114 		    fri->offset, fri->size, fri->fh);
115 
116 		error = fuse_ipc_tx(fip);
117 		if (error) {
118 			fuse_brelse(bp);
119 			break;
120 		}
121 		memcpy(bp->b_data, fuse_out_data(fip), fuse_out_data_size(fip));
122 		fuse_ipc_put(fip);
123 
124 		if (need_reopen) {
125 			error = fdrop(fp); /* calls VOP_CLOSE() */
126 			if (error) {
127 				fuse_brelse(bp);
128 				break;
129 			}
130 		}
131 skip:
132 		len = FUSE_BLKSIZE - buf_offset;
133 		if (len > uio->uio_resid)
134 			len = uio->uio_resid;
135 		if (uio->uio_offset + len > fnp->size)
136 			len = (size_t)(fnp->size - uio->uio_offset);
137 		fuse_dbg("size=%ju len=%ju\n", fnp->size, len);
138 
139 		error = uiomovebp(bp, bp->b_data + buf_offset, len, uio);
140 		bqrelse(bp);
141 		if (error)
142 			break;
143 	}
144 
145 	fuse_dbg("uio_offset=%ju uio_resid=%ju error=%d done\n",
146 	    uio->uio_offset, uio->uio_resid, error);
147 
148 	return error;
149 }
150 
151 int
152 fuse_write(struct vop_write_args *ap)
153 {
154 	return fuse_dio_write(ap);
155 }
156 
157 int
158 fuse_dio_write(struct vop_write_args *ap)
159 {
160 	struct vnode *vp = ap->a_vp;
161 	struct uio *uio = ap->a_uio;
162 	struct fuse_mount *fmp = VFSTOFUSE(vp->v_mount);
163 	struct fuse_node *fnp = VTOI(vp);
164 	bool need_reopen = !curproc || fnp->closed; /* XXX */
165 	int kflags = 0;
166 	int error = 0;
167 
168 	if (ap->a_ioflag & IO_APPEND)
169 		uio->uio_offset = fnp->size;
170 
171 	while (uio->uio_resid > 0) {
172 		struct file *fp;
173 		struct buf *bp;
174 		struct fuse_ipc *fip;
175 		struct fuse_read_in *fri;
176 		struct fuse_write_in *fwi;
177 		struct fuse_write_out *fwo;
178 		off_t base_offset, buf_offset;
179 		size_t len, oldsize;
180 		uint64_t fh;
181 		bool fixsize = false;
182 		bool need_read = false;
183 
184 		fh = fuse_nfh(VTOI(vp));
185 		if (ap->a_fp)
186 			fh = fuse_fh(ap->a_fp);
187 
188 		buf_offset = (off_t)uio->uio_offset & FUSE_BLKMASK64;
189 		base_offset = (off_t)uio->uio_offset - buf_offset;
190 
191 		fuse_dbg("uio_offset=%ju uio_resid=%ju base_offset=%ju "
192 		    "buf_offset=%ju\n",
193 		    uio->uio_offset, uio->uio_resid, base_offset, buf_offset);
194 
195 		oldsize = fnp->size;
196 		len = FUSE_BLKSIZE - buf_offset;
197 		if (len > uio->uio_resid)
198 			len = uio->uio_resid;
199 		if (uio->uio_offset + len > fnp->size) {
200 			/* XXX trivial flag */
201 			error = fuse_node_truncate(fnp, fnp->size,
202 			    uio->uio_offset + len);
203 			if (error)
204 				break;
205 			fixsize = true;
206 			kflags |= NOTE_EXTEND;
207 		}
208 		fuse_dbg("size=%ju len=%ju\n", fnp->size, len);
209 
210 		bp = NULL;
211 		if (uio->uio_segflg == UIO_NOCOPY) {
212 			bp = getblk(ap->a_vp, base_offset, FUSE_BLKSIZE,
213 			    GETBLK_BHEAVY, 0);
214 			if (!(bp->b_flags & B_CACHE)) {
215 				bqrelse(bp);
216 				need_read = true;
217 			}
218 		} else if (!buf_offset && uio->uio_resid >= FUSE_BLKSIZE) {
219 			bp = getblk(ap->a_vp, base_offset, FUSE_BLKSIZE,
220 			    GETBLK_BHEAVY, 0);
221 			if (!(bp->b_flags & B_CACHE))
222 				vfs_bio_clrbuf(bp);
223 		} else if (base_offset >= fnp->size) {
224 			bp = getblk(ap->a_vp, base_offset, FUSE_BLKSIZE,
225 			    GETBLK_BHEAVY, 0);
226 			vfs_bio_clrbuf(bp);
227 		} else {
228 			need_read = true;
229 		}
230 
231 		if (bp)
232 			fuse_dbg("b_loffset=%ju b_bcount=%d b_flags=%x\n",
233 			    bp->b_loffset, bp->b_bcount, bp->b_flags);
234 
235 		if (need_reopen) {
236 			error = falloc(NULL, &fp, NULL);
237 			if (error) {
238 				fuse_brelse(bp);
239 				fuse_fix_size(fnp, fixsize, oldsize);
240 				break;
241 			}
242 			/* XXX can panic at vref() in vop_stdopen() */
243 			error = VOP_OPEN(vp, FREAD | FWRITE, ap->a_cred, &fp);
244 			if (error) {
245 				fuse_brelse(bp);
246 				fuse_fix_size(fnp, fixsize, oldsize);
247 				break;
248 			}
249 		}
250 
251 		if (need_read) {
252 			error = bread(ap->a_vp, base_offset, FUSE_BLKSIZE, &bp);
253 			KKASSERT(!error);
254 
255 			fuse_dbg("b_loffset=%ju b_bcount=%d b_flags=%x\n",
256 			    bp->b_loffset, bp->b_bcount, bp->b_flags);
257 
258 			if (bp->b_loffset + (buf_offset + len) > oldsize) {
259 				memset(bp->b_data, 0, FUSE_BLKSIZE); /* XXX */
260 				goto skip; /* prevent EBADF */
261 			}
262 
263 			fip = fuse_ipc_get(fmp, sizeof(*fri));
264 			fri = fuse_ipc_fill(fip, FUSE_READ, fnp->ino,
265 			    ap->a_cred);
266 			fri->offset = bp->b_loffset;
267 			fri->size = buf_offset + len;
268 			if (need_reopen)
269 				fri->fh = fuse_nfh(VTOI(vp));
270 			else
271 				fri->fh = fh;
272 
273 			fuse_dbg("fuse_read_in offset=%ju size=%u fh=%jx\n",
274 			    fri->offset, fri->size, fri->fh);
275 
276 			error = fuse_ipc_tx(fip);
277 			if (error) {
278 				fuse_brelse(bp);
279 				fuse_fix_size(fnp, fixsize, oldsize);
280 				break;
281 			}
282 			memcpy(bp->b_data, fuse_out_data(fip),
283 			    fuse_out_data_size(fip));
284 			fuse_ipc_put(fip);
285 		}
286 skip:
287 		error = uiomovebp(bp, bp->b_data + buf_offset, len, uio);
288 		if (error) {
289 			bqrelse(bp);
290 			fuse_fix_size(fnp, fixsize, oldsize);
291 			break;
292 		}
293 		kflags |= NOTE_WRITE;
294 
295 		fip = fuse_ipc_get(fmp, sizeof(*fwi) + len);
296 		fwi = fuse_ipc_fill(fip, FUSE_WRITE, fnp->ino, ap->a_cred);
297 		fwi->offset = bp->b_loffset + buf_offset;
298 		fwi->size = len;
299 		if (need_reopen)
300 			fwi->fh = fuse_nfh(VTOI(vp));
301 		else
302 			fwi->fh = fh;
303 		memcpy((void*)(fwi + 1), bp->b_data + buf_offset, len);
304 
305 		fuse_dbg("fuse_write_in offset=%ju size=%u fh=%jx\n",
306 		    fwi->offset, fwi->size, fwi->fh);
307 
308 		error = fuse_ipc_tx(fip);
309 		if (error) {
310 			fuse_brelse(bp);
311 			fuse_fix_size(fnp, fixsize, oldsize);
312 			break;
313 		}
314 		fwo = fuse_out_data(fip);
315 		if (fwo->size != len) {
316 			fuse_ipc_put(fip);
317 			fuse_brelse(bp);
318 			fuse_fix_size(fnp, fixsize, oldsize);
319 			break;
320 		}
321 		fuse_ipc_put(fip);
322 
323 		if (need_reopen) {
324 			error = fdrop(fp); /* calls VOP_CLOSE() */
325 			if (error) {
326 				fuse_brelse(bp);
327 				fuse_fix_size(fnp, fixsize, oldsize);
328 				break;
329 			}
330 		}
331 
332 		error = bwrite(bp);
333 		KKASSERT(!error);
334 	}
335 
336 	fuse_knote(ap->a_vp, kflags);
337 
338 	fuse_dbg("uio_offset=%ju uio_resid=%ju error=%d done\n",
339 	    uio->uio_offset, uio->uio_resid, error);
340 
341 	return error;
342 }
343 #endif
344