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