xref: /dragonfly/sys/vfs/fuse/fuse_io.c (revision 5d0d0bafbc717a20af3abdbf6366fee0ae276685)
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