1 /* $OpenBSD: ext2fs_readwrite.c,v 1.45 2019/11/27 16:12:13 beck Exp $ */ 2 /* $NetBSD: ext2fs_readwrite.c,v 1.16 2001/02/27 04:37:47 chs Exp $ */ 3 4 /*- 5 * Copyright (c) 1997 Manuel Bouyer. 6 * Copyright (c) 1993 7 * The Regents of the University of California. All rights reserved. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 3. Neither the name of the University nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 * 33 * @(#)ufs_readwrite.c 8.8 (Berkeley) 8/4/94 34 * Modified for ext2fs by Manuel Bouyer. 35 */ 36 37 #include <sys/param.h> 38 #include <sys/systm.h> 39 #include <sys/resourcevar.h> 40 #include <sys/kernel.h> 41 #include <sys/stat.h> 42 #include <sys/buf.h> 43 #include <sys/mount.h> 44 #include <sys/vnode.h> 45 #include <sys/malloc.h> 46 #include <sys/signalvar.h> 47 #include <sys/event.h> 48 49 #include <ufs/ufs/quota.h> 50 #include <ufs/ufs/ufsmount.h> 51 #include <ufs/ufs/inode.h> 52 #include <ufs/ext2fs/ext2fs.h> 53 #include <ufs/ext2fs/ext2fs_extern.h> 54 55 56 static int ext2_ind_read(struct vnode *, struct inode *, struct m_ext2fs *, struct uio *); 57 static int ext4_ext_read(struct vnode *, struct inode *, struct m_ext2fs *, struct uio *); 58 59 /* 60 * Vnode op for reading. 61 */ 62 /* ARGSUSED */ 63 int 64 ext2fs_read(void *v) 65 { 66 struct vop_read_args *ap = v; 67 struct vnode *vp; 68 struct inode *ip; 69 struct uio *uio; 70 struct m_ext2fs *fs; 71 72 vp = ap->a_vp; 73 ip = VTOI(vp); 74 uio = ap->a_uio; 75 fs = ip->i_e2fs; 76 77 if (ip->i_e2fs_flags & EXT4_EXTENTS) 78 return ext4_ext_read(vp, ip, fs, uio); 79 else 80 return ext2_ind_read(vp, ip, fs, uio); 81 } 82 83 static int 84 ext2_ind_read(struct vnode *vp, struct inode *ip, struct m_ext2fs *fs, 85 struct uio *uio) 86 { 87 struct buf *bp; 88 daddr_t lbn, nextlbn; 89 off_t bytesinfile; 90 int size, xfersize, blkoffset; 91 int error; 92 93 #ifdef DIAGNOSTIC 94 if (uio->uio_rw != UIO_READ) 95 panic("%s: mode", "ext2fs_read"); 96 97 if (vp->v_type == VLNK) { 98 if (ext2fs_size(ip) < EXT2_MAXSYMLINKLEN) 99 panic("%s: short symlink", "ext2fs_read"); 100 } else if (vp->v_type != VREG && vp->v_type != VDIR) 101 panic("%s: type %d", "ext2fs_read", vp->v_type); 102 #endif 103 if (uio->uio_offset < 0) 104 return (EINVAL); 105 if (uio->uio_resid == 0) 106 return (0); 107 108 for (error = 0, bp = NULL; uio->uio_resid > 0; bp = NULL) { 109 if ((bytesinfile = ext2fs_size(ip) - uio->uio_offset) <= 0) 110 break; 111 lbn = lblkno(fs, uio->uio_offset); 112 nextlbn = lbn + 1; 113 size = fs->e2fs_bsize; 114 blkoffset = blkoff(fs, uio->uio_offset); 115 xfersize = fs->e2fs_bsize - blkoffset; 116 if (uio->uio_resid < xfersize) 117 xfersize = uio->uio_resid; 118 if (bytesinfile < xfersize) 119 xfersize = bytesinfile; 120 121 if (lblktosize(fs, nextlbn) >= ext2fs_size(ip)) 122 error = bread(vp, lbn, size, &bp); 123 else if (lbn - 1 == ip->i_ci.ci_lastr) { 124 int nextsize = fs->e2fs_bsize; 125 error = breadn(vp, lbn, size, &nextlbn, &nextsize, 126 1, &bp); 127 } else 128 error = bread(vp, lbn, size, &bp); 129 if (error) 130 break; 131 ip->i_ci.ci_lastr = lbn; 132 133 /* 134 * We should only get non-zero b_resid when an I/O error 135 * has occurred, which should cause us to break above. 136 * However, if the short read did not cause an error, 137 * then we want to ensure that we do not uiomove bad 138 * or uninitialized data. 139 */ 140 size -= bp->b_resid; 141 if (size < xfersize) { 142 if (size == 0) 143 break; 144 xfersize = size; 145 } 146 error = uiomove((char *)bp->b_data + blkoffset, xfersize, uio); 147 if (error) 148 break; 149 brelse(bp); 150 } 151 if (bp != NULL) 152 brelse(bp); 153 154 if (!(vp->v_mount->mnt_flag & MNT_NOATIME)) { 155 ip->i_flag |= IN_ACCESS; 156 } 157 return (error); 158 } 159 160 int 161 ext4_ext_read(struct vnode *vp, struct inode *ip, struct m_ext2fs *fs, struct uio *uio) 162 { 163 struct ext4_extent_path path; 164 struct ext4_extent nex, *ep; 165 struct buf *bp; 166 daddr_t lbn, pos; 167 off_t bytesinfile; 168 int size, xfersize, blkoffset; 169 int error, cache_type; 170 171 memset(&path, 0, sizeof path); 172 173 if (uio->uio_offset < 0) 174 return (EINVAL); 175 if (uio->uio_resid == 0) 176 return (0); 177 178 while (uio->uio_resid > 0) { 179 if ((bytesinfile = ext2fs_size(ip) - uio->uio_offset) <= 0) 180 break; 181 lbn = lblkno(fs, uio->uio_offset); 182 size = fs->e2fs_bsize; 183 blkoffset = blkoff(fs, uio->uio_offset); 184 185 xfersize = fs->e2fs_fsize - blkoffset; 186 xfersize = MIN(xfersize, uio->uio_resid); 187 xfersize = MIN(xfersize, bytesinfile); 188 189 cache_type = ext4_ext_in_cache(ip, lbn, &nex); 190 switch (cache_type) { 191 case EXT4_EXT_CACHE_NO: 192 ext4_ext_find_extent(fs, ip, lbn, &path); 193 if ((ep = path.ep_ext) == NULL) 194 return (EIO); 195 ext4_ext_put_cache(ip, ep, EXT4_EXT_CACHE_IN); 196 197 pos = lbn - ep->e_blk + (((daddr_t) ep->e_start_hi << 32) | ep->e_start_lo); 198 if (path.ep_bp != NULL) { 199 brelse(path.ep_bp); 200 path.ep_bp = NULL; 201 } 202 break; 203 case EXT4_EXT_CACHE_GAP: 204 /* block has not been allocated yet */ 205 return (0); 206 case EXT4_EXT_CACHE_IN: 207 pos = lbn - nex.e_blk + (((daddr_t) nex.e_start_hi << 32) | nex.e_start_lo); 208 break; 209 } 210 error = bread(ip->i_devvp, fsbtodb(fs, pos), size, &bp); 211 if (error) { 212 brelse(bp); 213 return (error); 214 } 215 size -= bp->b_resid; 216 if (size < xfersize) { 217 if (size == 0) { 218 brelse(bp); 219 break; 220 } 221 xfersize = size; 222 } 223 error = uiomove(bp->b_data + blkoffset, xfersize, uio); 224 brelse(bp); 225 if (error) 226 return (error); 227 } 228 return (0); 229 } 230 231 /* 232 * Vnode op for writing. 233 */ 234 int 235 ext2fs_write(void *v) 236 { 237 struct vop_write_args *ap = v; 238 struct vnode *vp; 239 struct uio *uio; 240 struct inode *ip; 241 struct m_ext2fs *fs; 242 struct buf *bp; 243 int32_t lbn; 244 off_t osize; 245 int blkoffset, error, extended, flags, ioflag, size, xfersize; 246 size_t resid; 247 ssize_t overrun; 248 249 extended = 0; 250 ioflag = ap->a_ioflag; 251 uio = ap->a_uio; 252 vp = ap->a_vp; 253 ip = VTOI(vp); 254 255 #ifdef DIAGNOSTIC 256 if (uio->uio_rw != UIO_WRITE) 257 panic("%s: mode", "ext2fs_write"); 258 #endif 259 260 /* 261 * If writing 0 bytes, succeed and do not change 262 * update time or file offset (standards compliance) 263 */ 264 if (uio->uio_resid == 0) 265 return (0); 266 267 switch (vp->v_type) { 268 case VREG: 269 if (ioflag & IO_APPEND) 270 uio->uio_offset = ext2fs_size(ip); 271 if ((ip->i_e2fs_flags & EXT2_APPEND) && 272 uio->uio_offset != ext2fs_size(ip)) 273 return (EPERM); 274 /* FALLTHROUGH */ 275 case VLNK: 276 break; 277 case VDIR: 278 if ((ioflag & IO_SYNC) == 0) 279 panic("%s: nonsync dir write", "ext2fs_write"); 280 break; 281 default: 282 panic("%s: type", "ext2fs_write"); 283 } 284 285 fs = ip->i_e2fs; 286 if (e2fs_overflow(fs, uio->uio_resid, uio->uio_offset + uio->uio_resid)) 287 return (EFBIG); 288 289 /* do the filesize rlimit check */ 290 if ((error = vn_fsizechk(vp, uio, ioflag, &overrun))) 291 return (error); 292 293 resid = uio->uio_resid; 294 osize = ext2fs_size(ip); 295 flags = ioflag & IO_SYNC ? B_SYNC : 0; 296 297 for (error = 0; uio->uio_resid > 0;) { 298 lbn = lblkno(fs, uio->uio_offset); 299 blkoffset = blkoff(fs, uio->uio_offset); 300 xfersize = fs->e2fs_bsize - blkoffset; 301 if (uio->uio_resid < xfersize) 302 xfersize = uio->uio_resid; 303 if (fs->e2fs_bsize > xfersize) 304 flags |= B_CLRBUF; 305 else 306 flags &= ~B_CLRBUF; 307 308 error = ext2fs_buf_alloc(ip, 309 lbn, blkoffset + xfersize, ap->a_cred, &bp, flags); 310 if (error) 311 break; 312 if (uio->uio_offset + xfersize > ext2fs_size(ip)) { 313 error = ext2fs_setsize(ip, uio->uio_offset + xfersize); 314 if (error) 315 break; 316 uvm_vnp_setsize(vp, ext2fs_size(ip)); 317 extended = 1; 318 } 319 uvm_vnp_uncache(vp); 320 321 size = fs->e2fs_bsize - bp->b_resid; 322 if (size < xfersize) 323 xfersize = size; 324 325 error = uiomove(bp->b_data + blkoffset, xfersize, uio); 326 /* 327 * If the buffer is not already filled and we encounter an 328 * error while trying to fill it, we have to clear out any 329 * garbage data from the pages instantiated for the buffer. 330 * If we do not, a failed uiomove() during a write can leave 331 * the prior contents of the pages exposed to a userland mmap. 332 * 333 * Note that we don't need to clear buffers that were 334 * allocated with the B_CLRBUF flag set. 335 */ 336 if (error != 0 && !(flags & B_CLRBUF)) 337 memset(bp->b_data + blkoffset, 0, xfersize); 338 339 if (ioflag & IO_NOCACHE) 340 bp->b_flags |= B_NOCACHE; 341 342 if (ioflag & IO_SYNC) 343 (void)bwrite(bp); 344 else if (xfersize + blkoffset == fs->e2fs_bsize) 345 bawrite(bp); 346 else 347 bdwrite(bp); 348 if (error || xfersize == 0) 349 break; 350 ip->i_flag |= IN_CHANGE | IN_UPDATE; 351 } 352 /* 353 * If we successfully wrote any data, and we are not the superuser 354 * we clear the setuid and setgid bits as a precaution against 355 * tampering. 356 */ 357 if (resid > uio->uio_resid && ap->a_cred && ap->a_cred->cr_uid != 0) 358 ip->i_e2fs_mode &= ~(ISUID | ISGID); 359 if (resid > uio->uio_resid) 360 VN_KNOTE(vp, NOTE_WRITE | (extended ? NOTE_EXTEND : 0)); 361 if (error) { 362 if (ioflag & IO_UNIT) { 363 (void)ext2fs_truncate(ip, osize, 364 ioflag & IO_SYNC, ap->a_cred); 365 uio->uio_offset -= resid - uio->uio_resid; 366 uio->uio_resid = resid; 367 } 368 } else if (resid > uio->uio_resid && (ioflag & IO_SYNC)) { 369 error = ext2fs_update(ip, 1); 370 } 371 /* correct the result for writes clamped by vn_fsizechk() */ 372 uio->uio_resid += overrun; 373 return (error); 374 } 375