1 /* $OpenBSD: ext2fs_readwrite.c,v 1.46 2023/03/08 04:43:09 guenther 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 int 63 ext2fs_read(void *v) 64 { 65 struct vop_read_args *ap = v; 66 struct vnode *vp; 67 struct inode *ip; 68 struct uio *uio; 69 struct m_ext2fs *fs; 70 71 vp = ap->a_vp; 72 ip = VTOI(vp); 73 uio = ap->a_uio; 74 fs = ip->i_e2fs; 75 76 if (ip->i_e2fs_flags & EXT4_EXTENTS) 77 return ext4_ext_read(vp, ip, fs, uio); 78 else 79 return ext2_ind_read(vp, ip, fs, uio); 80 } 81 82 static int 83 ext2_ind_read(struct vnode *vp, struct inode *ip, struct m_ext2fs *fs, 84 struct uio *uio) 85 { 86 struct buf *bp; 87 daddr_t lbn, nextlbn; 88 off_t bytesinfile; 89 int size, xfersize, blkoffset; 90 int error; 91 92 #ifdef DIAGNOSTIC 93 if (uio->uio_rw != UIO_READ) 94 panic("%s: mode", "ext2fs_read"); 95 96 if (vp->v_type == VLNK) { 97 if (ext2fs_size(ip) < EXT2_MAXSYMLINKLEN) 98 panic("%s: short symlink", "ext2fs_read"); 99 } else if (vp->v_type != VREG && vp->v_type != VDIR) 100 panic("%s: type %d", "ext2fs_read", vp->v_type); 101 #endif 102 if (uio->uio_offset < 0) 103 return (EINVAL); 104 if (uio->uio_resid == 0) 105 return (0); 106 107 for (error = 0, bp = NULL; uio->uio_resid > 0; bp = NULL) { 108 if ((bytesinfile = ext2fs_size(ip) - uio->uio_offset) <= 0) 109 break; 110 lbn = lblkno(fs, uio->uio_offset); 111 nextlbn = lbn + 1; 112 size = fs->e2fs_bsize; 113 blkoffset = blkoff(fs, uio->uio_offset); 114 xfersize = fs->e2fs_bsize - blkoffset; 115 if (uio->uio_resid < xfersize) 116 xfersize = uio->uio_resid; 117 if (bytesinfile < xfersize) 118 xfersize = bytesinfile; 119 120 if (lblktosize(fs, nextlbn) >= ext2fs_size(ip)) 121 error = bread(vp, lbn, size, &bp); 122 else if (lbn - 1 == ip->i_ci.ci_lastr) { 123 int nextsize = fs->e2fs_bsize; 124 error = breadn(vp, lbn, size, &nextlbn, &nextsize, 125 1, &bp); 126 } else 127 error = bread(vp, lbn, size, &bp); 128 if (error) 129 break; 130 ip->i_ci.ci_lastr = lbn; 131 132 /* 133 * We should only get non-zero b_resid when an I/O error 134 * has occurred, which should cause us to break above. 135 * However, if the short read did not cause an error, 136 * then we want to ensure that we do not uiomove bad 137 * or uninitialized data. 138 */ 139 size -= bp->b_resid; 140 if (size < xfersize) { 141 if (size == 0) 142 break; 143 xfersize = size; 144 } 145 error = uiomove((char *)bp->b_data + blkoffset, xfersize, uio); 146 if (error) 147 break; 148 brelse(bp); 149 } 150 if (bp != NULL) 151 brelse(bp); 152 153 if (!(vp->v_mount->mnt_flag & MNT_NOATIME)) { 154 ip->i_flag |= IN_ACCESS; 155 } 156 return (error); 157 } 158 159 int 160 ext4_ext_read(struct vnode *vp, struct inode *ip, struct m_ext2fs *fs, struct uio *uio) 161 { 162 struct ext4_extent_path path; 163 struct ext4_extent nex, *ep; 164 struct buf *bp; 165 daddr_t lbn, pos; 166 off_t bytesinfile; 167 int size, xfersize, blkoffset; 168 int error, cache_type; 169 170 memset(&path, 0, sizeof path); 171 172 if (uio->uio_offset < 0) 173 return (EINVAL); 174 if (uio->uio_resid == 0) 175 return (0); 176 177 while (uio->uio_resid > 0) { 178 if ((bytesinfile = ext2fs_size(ip) - uio->uio_offset) <= 0) 179 break; 180 lbn = lblkno(fs, uio->uio_offset); 181 size = fs->e2fs_bsize; 182 blkoffset = blkoff(fs, uio->uio_offset); 183 184 xfersize = fs->e2fs_fsize - blkoffset; 185 xfersize = MIN(xfersize, uio->uio_resid); 186 xfersize = MIN(xfersize, bytesinfile); 187 188 cache_type = ext4_ext_in_cache(ip, lbn, &nex); 189 switch (cache_type) { 190 case EXT4_EXT_CACHE_NO: 191 ext4_ext_find_extent(fs, ip, lbn, &path); 192 if ((ep = path.ep_ext) == NULL) 193 return (EIO); 194 ext4_ext_put_cache(ip, ep, EXT4_EXT_CACHE_IN); 195 196 pos = lbn - ep->e_blk + (((daddr_t) ep->e_start_hi << 32) | ep->e_start_lo); 197 if (path.ep_bp != NULL) { 198 brelse(path.ep_bp); 199 path.ep_bp = NULL; 200 } 201 break; 202 case EXT4_EXT_CACHE_GAP: 203 /* block has not been allocated yet */ 204 return (0); 205 case EXT4_EXT_CACHE_IN: 206 pos = lbn - nex.e_blk + (((daddr_t) nex.e_start_hi << 32) | nex.e_start_lo); 207 break; 208 } 209 error = bread(ip->i_devvp, fsbtodb(fs, pos), size, &bp); 210 if (error) { 211 brelse(bp); 212 return (error); 213 } 214 size -= bp->b_resid; 215 if (size < xfersize) { 216 if (size == 0) { 217 brelse(bp); 218 break; 219 } 220 xfersize = size; 221 } 222 error = uiomove(bp->b_data + blkoffset, xfersize, uio); 223 brelse(bp); 224 if (error) 225 return (error); 226 } 227 return (0); 228 } 229 230 /* 231 * Vnode op for writing. 232 */ 233 int 234 ext2fs_write(void *v) 235 { 236 struct vop_write_args *ap = v; 237 struct vnode *vp; 238 struct uio *uio; 239 struct inode *ip; 240 struct m_ext2fs *fs; 241 struct buf *bp; 242 int32_t lbn; 243 off_t osize; 244 int blkoffset, error, extended, flags, ioflag, size, xfersize; 245 size_t resid; 246 ssize_t overrun; 247 248 extended = 0; 249 ioflag = ap->a_ioflag; 250 uio = ap->a_uio; 251 vp = ap->a_vp; 252 ip = VTOI(vp); 253 254 #ifdef DIAGNOSTIC 255 if (uio->uio_rw != UIO_WRITE) 256 panic("%s: mode", "ext2fs_write"); 257 #endif 258 259 /* 260 * If writing 0 bytes, succeed and do not change 261 * update time or file offset (standards compliance) 262 */ 263 if (uio->uio_resid == 0) 264 return (0); 265 266 switch (vp->v_type) { 267 case VREG: 268 if (ioflag & IO_APPEND) 269 uio->uio_offset = ext2fs_size(ip); 270 if ((ip->i_e2fs_flags & EXT2_APPEND) && 271 uio->uio_offset != ext2fs_size(ip)) 272 return (EPERM); 273 /* FALLTHROUGH */ 274 case VLNK: 275 break; 276 case VDIR: 277 if ((ioflag & IO_SYNC) == 0) 278 panic("%s: nonsync dir write", "ext2fs_write"); 279 break; 280 default: 281 panic("%s: type", "ext2fs_write"); 282 } 283 284 fs = ip->i_e2fs; 285 if (e2fs_overflow(fs, uio->uio_resid, uio->uio_offset + uio->uio_resid)) 286 return (EFBIG); 287 288 /* do the filesize rlimit check */ 289 if ((error = vn_fsizechk(vp, uio, ioflag, &overrun))) 290 return (error); 291 292 resid = uio->uio_resid; 293 osize = ext2fs_size(ip); 294 flags = ioflag & IO_SYNC ? B_SYNC : 0; 295 296 for (error = 0; uio->uio_resid > 0;) { 297 lbn = lblkno(fs, uio->uio_offset); 298 blkoffset = blkoff(fs, uio->uio_offset); 299 xfersize = fs->e2fs_bsize - blkoffset; 300 if (uio->uio_resid < xfersize) 301 xfersize = uio->uio_resid; 302 if (fs->e2fs_bsize > xfersize) 303 flags |= B_CLRBUF; 304 else 305 flags &= ~B_CLRBUF; 306 307 error = ext2fs_buf_alloc(ip, 308 lbn, blkoffset + xfersize, ap->a_cred, &bp, flags); 309 if (error) 310 break; 311 if (uio->uio_offset + xfersize > ext2fs_size(ip)) { 312 error = ext2fs_setsize(ip, uio->uio_offset + xfersize); 313 if (error) 314 break; 315 uvm_vnp_setsize(vp, ext2fs_size(ip)); 316 extended = 1; 317 } 318 uvm_vnp_uncache(vp); 319 320 size = fs->e2fs_bsize - bp->b_resid; 321 if (size < xfersize) 322 xfersize = size; 323 324 error = uiomove(bp->b_data + blkoffset, xfersize, uio); 325 /* 326 * If the buffer is not already filled and we encounter an 327 * error while trying to fill it, we have to clear out any 328 * garbage data from the pages instantiated for the buffer. 329 * If we do not, a failed uiomove() during a write can leave 330 * the prior contents of the pages exposed to a userland mmap. 331 * 332 * Note that we don't need to clear buffers that were 333 * allocated with the B_CLRBUF flag set. 334 */ 335 if (error != 0 && !(flags & B_CLRBUF)) 336 memset(bp->b_data + blkoffset, 0, xfersize); 337 338 if (ioflag & IO_NOCACHE) 339 bp->b_flags |= B_NOCACHE; 340 341 if (ioflag & IO_SYNC) 342 (void)bwrite(bp); 343 else if (xfersize + blkoffset == fs->e2fs_bsize) 344 bawrite(bp); 345 else 346 bdwrite(bp); 347 if (error || xfersize == 0) 348 break; 349 ip->i_flag |= IN_CHANGE | IN_UPDATE; 350 } 351 /* 352 * If we successfully wrote any data, and we are not the superuser 353 * we clear the setuid and setgid bits as a precaution against 354 * tampering. 355 */ 356 if (resid > uio->uio_resid && ap->a_cred && ap->a_cred->cr_uid != 0) 357 ip->i_e2fs_mode &= ~(ISUID | ISGID); 358 if (resid > uio->uio_resid) 359 VN_KNOTE(vp, NOTE_WRITE | (extended ? NOTE_EXTEND : 0)); 360 if (error) { 361 if (ioflag & IO_UNIT) { 362 (void)ext2fs_truncate(ip, osize, 363 ioflag & IO_SYNC, ap->a_cred); 364 uio->uio_offset -= resid - uio->uio_resid; 365 uio->uio_resid = resid; 366 } 367 } else if (resid > uio->uio_resid && (ioflag & IO_SYNC)) { 368 error = ext2fs_update(ip, 1); 369 } 370 /* correct the result for writes clamped by vn_fsizechk() */ 371 uio->uio_resid += overrun; 372 return (error); 373 } 374