1 /* $NetBSD: ext2fs_readwrite.c,v 1.65 2014/08/12 06:49:10 maxv Exp $ */ 2 3 /*- 4 * Copyright (c) 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. Neither the name of the University nor the names of its contributors 16 * may be used to endorse or promote products derived from this software 17 * without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 * 31 * @(#)ufs_readwrite.c 8.8 (Berkeley) 8/4/94 32 * Modified for ext2fs by Manuel Bouyer. 33 */ 34 35 /*- 36 * Copyright (c) 1997 Manuel Bouyer. 37 * 38 * Redistribution and use in source and binary forms, with or without 39 * modification, are permitted provided that the following conditions 40 * are met: 41 * 1. Redistributions of source code must retain the above copyright 42 * notice, this list of conditions and the following disclaimer. 43 * 2. Redistributions in binary form must reproduce the above copyright 44 * notice, this list of conditions and the following disclaimer in the 45 * documentation and/or other materials provided with the distribution. 46 * 47 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 48 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 49 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 50 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 51 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 52 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 53 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 54 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 55 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 56 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 57 * 58 * @(#)ufs_readwrite.c 8.8 (Berkeley) 8/4/94 59 * Modified for ext2fs by Manuel Bouyer. 60 */ 61 62 #include <sys/cdefs.h> 63 __KERNEL_RCSID(0, "$NetBSD: ext2fs_readwrite.c,v 1.65 2014/08/12 06:49:10 maxv Exp $"); 64 65 #include <sys/param.h> 66 #include <sys/systm.h> 67 #include <sys/resourcevar.h> 68 #include <sys/kernel.h> 69 #include <sys/file.h> 70 #include <sys/stat.h> 71 #include <sys/buf.h> 72 #include <sys/proc.h> 73 #include <sys/mount.h> 74 #include <sys/vnode.h> 75 #include <sys/malloc.h> 76 #include <sys/signalvar.h> 77 #include <sys/kauth.h> 78 79 #include <ufs/ufs/inode.h> 80 #include <ufs/ufs/ufsmount.h> 81 #include <ufs/ufs/ufs_extern.h> 82 #include <ufs/ext2fs/ext2fs.h> 83 #include <ufs/ext2fs/ext2fs_extern.h> 84 85 /* 86 * Vnode op for reading. 87 */ 88 /* ARGSUSED */ 89 int 90 ext2fs_read(void *v) 91 { 92 struct vop_read_args /* { 93 struct vnode *a_vp; 94 struct uio *a_uio; 95 int a_ioflag; 96 kauth_cred_t a_cred; 97 } */ *ap = v; 98 struct vnode *vp; 99 struct inode *ip; 100 struct uio *uio; 101 struct m_ext2fs *fs; 102 struct buf *bp; 103 struct ufsmount *ump; 104 vsize_t bytelen; 105 daddr_t lbn, nextlbn; 106 off_t bytesinfile; 107 long size, xfersize, blkoffset; 108 int error; 109 110 vp = ap->a_vp; 111 ip = VTOI(vp); 112 ump = ip->i_ump; 113 uio = ap->a_uio; 114 error = 0; 115 116 #ifdef DIAGNOSTIC 117 if (uio->uio_rw != UIO_READ) 118 panic("%s: mode", "ext2fs_read"); 119 120 if (vp->v_type == VLNK) { 121 if (ext2fs_size(ip) < ump->um_maxsymlinklen || 122 (ump->um_maxsymlinklen == 0 && ext2fs_nblock(ip) == 0)) 123 panic("%s: short symlink", "ext2fs_read"); 124 } else if (vp->v_type != VREG && vp->v_type != VDIR) 125 panic("%s: type %d", "ext2fs_read", vp->v_type); 126 #endif 127 fs = ip->i_e2fs; 128 if ((uint64_t)uio->uio_offset > ump->um_maxfilesize) 129 return (EFBIG); 130 if (uio->uio_resid == 0) 131 return (0); 132 if (uio->uio_offset >= ext2fs_size(ip)) 133 goto out; 134 135 if (vp->v_type == VREG) { 136 const int advice = IO_ADV_DECODE(ap->a_ioflag); 137 138 while (uio->uio_resid > 0) { 139 bytelen = MIN(ext2fs_size(ip) - uio->uio_offset, 140 uio->uio_resid); 141 if (bytelen == 0) 142 break; 143 144 error = ubc_uiomove(&vp->v_uobj, uio, bytelen, advice, 145 UBC_READ | UBC_PARTIALOK | UBC_UNMAP_FLAG(vp)); 146 if (error) 147 break; 148 } 149 goto out; 150 } 151 152 for (error = 0, bp = NULL; uio->uio_resid > 0; bp = NULL) { 153 bytesinfile = ext2fs_size(ip) - uio->uio_offset; 154 if (bytesinfile <= 0) 155 break; 156 lbn = ext2_lblkno(fs, uio->uio_offset); 157 nextlbn = lbn + 1; 158 size = fs->e2fs_bsize; 159 blkoffset = ext2_blkoff(fs, uio->uio_offset); 160 xfersize = fs->e2fs_bsize - blkoffset; 161 if (uio->uio_resid < xfersize) 162 xfersize = uio->uio_resid; 163 if (bytesinfile < xfersize) 164 xfersize = bytesinfile; 165 166 if (ext2_lblktosize(fs, nextlbn) >= ext2fs_size(ip)) 167 error = bread(vp, lbn, size, NOCRED, 0, &bp); 168 else { 169 int nextsize = fs->e2fs_bsize; 170 error = breadn(vp, lbn, 171 size, &nextlbn, &nextsize, 1, NOCRED, 0, &bp); 172 } 173 if (error) 174 break; 175 176 /* 177 * We should only get non-zero b_resid when an I/O error 178 * has occurred, which should cause us to break above. 179 * However, if the short read did not cause an error, 180 * then we want to ensure that we do not uiomove bad 181 * or uninitialized data. 182 */ 183 size -= bp->b_resid; 184 if (size < xfersize) { 185 if (size == 0) 186 break; 187 xfersize = size; 188 } 189 error = uiomove((char *)bp->b_data + blkoffset, xfersize, uio); 190 if (error) 191 break; 192 brelse(bp, 0); 193 } 194 if (bp != NULL) 195 brelse(bp, 0); 196 197 out: 198 if (!(vp->v_mount->mnt_flag & MNT_NOATIME)) { 199 ip->i_flag |= IN_ACCESS; 200 if ((ap->a_ioflag & IO_SYNC) == IO_SYNC) 201 error = ext2fs_update(vp, NULL, NULL, UPDATE_WAIT); 202 } 203 return (error); 204 } 205 206 /* 207 * Vnode op for writing. 208 */ 209 int 210 ext2fs_write(void *v) 211 { 212 struct vop_write_args /* { 213 struct vnode *a_vp; 214 struct uio *a_uio; 215 int a_ioflag; 216 kauth_cred_t a_cred; 217 } */ *ap = v; 218 struct vnode *vp; 219 struct uio *uio; 220 struct inode *ip; 221 struct m_ext2fs *fs; 222 struct buf *bp; 223 struct ufsmount *ump; 224 daddr_t lbn; 225 off_t osize; 226 int blkoffset, error, flags, ioflag, resid, xfersize; 227 vsize_t bytelen; 228 off_t oldoff = 0; /* XXX */ 229 bool async; 230 int extended = 0; 231 int advice; 232 233 ioflag = ap->a_ioflag; 234 advice = IO_ADV_DECODE(ioflag); 235 uio = ap->a_uio; 236 vp = ap->a_vp; 237 ip = VTOI(vp); 238 ump = ip->i_ump; 239 error = 0; 240 241 #ifdef DIAGNOSTIC 242 if (uio->uio_rw != UIO_WRITE) 243 panic("%s: mode", "ext2fs_write"); 244 #endif 245 246 switch (vp->v_type) { 247 case VREG: 248 if (ioflag & IO_APPEND) 249 uio->uio_offset = ext2fs_size(ip); 250 if ((ip->i_e2fs_flags & EXT2_APPEND) && 251 uio->uio_offset != ext2fs_size(ip)) 252 return (EPERM); 253 /* FALLTHROUGH */ 254 case VLNK: 255 break; 256 case VDIR: 257 if ((ioflag & IO_SYNC) == 0) 258 panic("%s: nonsync dir write", "ext2fs_write"); 259 break; 260 default: 261 panic("%s: type", "ext2fs_write"); 262 } 263 264 fs = ip->i_e2fs; 265 if (uio->uio_offset < 0 || 266 (uint64_t)uio->uio_offset + uio->uio_resid > ump->um_maxfilesize) 267 return (EFBIG); 268 if (uio->uio_resid == 0) 269 return (0); 270 271 async = vp->v_mount->mnt_flag & MNT_ASYNC; 272 resid = uio->uio_resid; 273 osize = ext2fs_size(ip); 274 275 if (vp->v_type == VREG) { 276 while (uio->uio_resid > 0) { 277 oldoff = uio->uio_offset; 278 blkoffset = ext2_blkoff(fs, uio->uio_offset); 279 bytelen = MIN(fs->e2fs_bsize - blkoffset, 280 uio->uio_resid); 281 282 if (vp->v_size < oldoff + bytelen) { 283 uvm_vnp_setwritesize(vp, oldoff + bytelen); 284 } 285 error = ufs_balloc_range(vp, uio->uio_offset, 286 bytelen, ap->a_cred, 0); 287 if (error) 288 break; 289 error = ubc_uiomove(&vp->v_uobj, uio, bytelen, advice, 290 UBC_WRITE | UBC_UNMAP_FLAG(vp)); 291 if (error) 292 break; 293 294 /* 295 * update UVM's notion of the size now that we've 296 * copied the data into the vnode's pages. 297 */ 298 299 if (vp->v_size < uio->uio_offset) { 300 uvm_vnp_setsize(vp, uio->uio_offset); 301 extended = 1; 302 } 303 304 /* 305 * flush what we just wrote if necessary. 306 * XXXUBC simplistic async flushing. 307 */ 308 309 if (!async && oldoff >> 16 != uio->uio_offset >> 16) { 310 mutex_enter(vp->v_interlock); 311 error = VOP_PUTPAGES(vp, (oldoff >> 16) << 16, 312 (uio->uio_offset >> 16) << 16, 313 PGO_CLEANIT | PGO_LAZY); 314 } 315 } 316 if (error == 0 && ioflag & IO_SYNC) { 317 mutex_enter(vp->v_interlock); 318 error = VOP_PUTPAGES(vp, trunc_page(oldoff), 319 round_page(ext2_blkroundup(fs, uio->uio_offset)), 320 PGO_CLEANIT | PGO_SYNCIO); 321 } 322 323 goto out; 324 } 325 326 flags = ioflag & IO_SYNC ? B_SYNC : 0; 327 for (error = 0; uio->uio_resid > 0;) { 328 lbn = ext2_lblkno(fs, uio->uio_offset); 329 blkoffset = ext2_blkoff(fs, uio->uio_offset); 330 xfersize = MIN(fs->e2fs_bsize - blkoffset, uio->uio_resid); 331 if (xfersize < fs->e2fs_bsize) 332 flags |= B_CLRBUF; 333 else 334 flags &= ~B_CLRBUF; 335 error = ext2fs_balloc(ip, 336 lbn, blkoffset + xfersize, ap->a_cred, &bp, flags); 337 if (error) 338 break; 339 if (ext2fs_size(ip) < uio->uio_offset + xfersize) { 340 error = ext2fs_setsize(ip, uio->uio_offset + xfersize); 341 if (error) 342 break; 343 } 344 error = uiomove((char *)bp->b_data + blkoffset, xfersize, uio); 345 346 /* 347 * update UVM's notion of the size now that we've 348 * copied the data into the vnode's pages. 349 */ 350 351 if (vp->v_size < uio->uio_offset) { 352 uvm_vnp_setsize(vp, uio->uio_offset); 353 extended = 1; 354 } 355 356 if (ioflag & IO_SYNC) 357 (void)bwrite(bp); 358 else if (xfersize + blkoffset == fs->e2fs_bsize) 359 bawrite(bp); 360 else 361 bdwrite(bp); 362 if (error || xfersize == 0) 363 break; 364 } 365 366 /* 367 * If we successfully wrote any data, and we are not the superuser 368 * we clear the setuid and setgid bits as a precaution against 369 * tampering. 370 */ 371 372 out: 373 ip->i_flag |= IN_CHANGE | IN_UPDATE; 374 if (vp->v_mount->mnt_flag & MNT_RELATIME) 375 ip->i_flag |= IN_ACCESS; 376 if (resid > uio->uio_resid && ap->a_cred) { 377 if (ip->i_e2fs_mode & ISUID) { 378 if (kauth_authorize_vnode(ap->a_cred, 379 KAUTH_VNODE_RETAIN_SUID, vp, NULL, EPERM) != 0) 380 ip->i_e2fs_mode &= ISUID; 381 } 382 383 if (ip->i_e2fs_mode & ISGID) { 384 if (kauth_authorize_vnode(ap->a_cred, 385 KAUTH_VNODE_RETAIN_SGID, vp, NULL, EPERM) != 0) 386 ip->i_e2fs_mode &= ~ISGID; 387 } 388 } 389 if (resid > uio->uio_resid) 390 VN_KNOTE(vp, NOTE_WRITE | (extended ? NOTE_EXTEND : 0)); 391 if (error) { 392 (void) ext2fs_truncate(vp, osize, ioflag & IO_SYNC, ap->a_cred); 393 uio->uio_offset -= resid - uio->uio_resid; 394 uio->uio_resid = resid; 395 } else if (resid > uio->uio_resid && (ioflag & IO_SYNC) == IO_SYNC) 396 error = ext2fs_update(vp, NULL, NULL, UPDATE_WAIT); 397 KASSERT(vp->v_size == ext2fs_size(ip)); 398 return (error); 399 } 400