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