Merge branch 'vendor/MDOCML'
[dragonfly.git] / sys / vfs / fuse / fuse_io.c
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 }