1 /* $OpenBSD: ext2fs_readwrite.c,v 1.32 2014/07/13 13:28:26 pelikan 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/file.h> 42 #include <sys/stat.h> 43 #include <sys/buf.h> 44 #include <sys/proc.h> 45 #include <sys/conf.h> 46 #include <sys/mount.h> 47 #include <sys/vnode.h> 48 #include <sys/malloc.h> 49 #include <sys/signalvar.h> 50 51 #include <ufs/ufs/quota.h> 52 #include <ufs/ufs/ufsmount.h> 53 #include <ufs/ufs/inode.h> 54 #include <ufs/ext2fs/ext2fs.h> 55 #include <ufs/ext2fs/ext2fs_extern.h> 56 57 58 static int ext2_ind_read(struct vnode *, struct inode *, struct m_ext2fs *, struct uio *); 59 static int ext4_ext_read(struct vnode *, struct inode *, struct m_ext2fs *, struct uio *); 60 61 #define doclusterread 0 /* XXX underway */ 62 #define doclusterwrite 0 63 64 /* 65 * Vnode op for reading. 66 */ 67 /* ARGSUSED */ 68 int 69 ext2fs_read(void *v) 70 { 71 struct vop_read_args *ap = v; 72 struct vnode *vp; 73 struct inode *ip; 74 struct uio *uio; 75 struct m_ext2fs *fs; 76 77 vp = ap->a_vp; 78 ip = VTOI(vp); 79 uio = ap->a_uio; 80 fs = ip->i_e2fs; 81 82 if (ip->i_e2fs_flags & EXT4_EXTENTS) 83 return ext4_ext_read(vp, ip, fs, uio); 84 else 85 return ext2_ind_read(vp, ip, fs, uio); 86 } 87 88 static int 89 ext2_ind_read(struct vnode *vp, struct inode *ip, struct m_ext2fs *fs, 90 struct uio *uio) 91 { 92 struct buf *bp; 93 daddr_t lbn, nextlbn; 94 off_t bytesinfile; 95 long size, xfersize, blkoffset; 96 int error; 97 98 #ifdef DIAGNOSTIC 99 if (uio->uio_rw != UIO_READ) 100 panic("%s: mode", "ext2fs_read"); 101 102 if (vp->v_type == VLNK) { 103 if ((int)ext2fs_size(ip) < vp->v_mount->mnt_maxsymlinklen || 104 (vp->v_mount->mnt_maxsymlinklen == 0 && 105 ip->i_e2fs_nblock == 0)) 106 panic("%s: short symlink", "ext2fs_read"); 107 } else if (vp->v_type != VREG && vp->v_type != VDIR) 108 panic("%s: type %d", "ext2fs_read", vp->v_type); 109 #endif 110 if (e2fs_overflow(fs, 0, uio->uio_offset)) 111 return (EFBIG); 112 if (uio->uio_resid == 0) 113 return (0); 114 115 for (error = 0, bp = NULL; uio->uio_resid > 0; bp = NULL) { 116 if ((bytesinfile = ext2fs_size(ip) - uio->uio_offset) <= 0) 117 break; 118 lbn = lblkno(fs, uio->uio_offset); 119 nextlbn = lbn + 1; 120 size = fs->e2fs_bsize; 121 blkoffset = blkoff(fs, uio->uio_offset); 122 xfersize = fs->e2fs_bsize - blkoffset; 123 if (uio->uio_resid < xfersize) 124 xfersize = uio->uio_resid; 125 if (bytesinfile < xfersize) 126 xfersize = bytesinfile; 127 128 if (lblktosize(fs, nextlbn) >= ext2fs_size(ip)) 129 error = bread(vp, lbn, size, &bp); 130 else if (lbn - 1 == ip->i_ci.ci_lastr) { 131 int nextsize = fs->e2fs_bsize; 132 error = breadn(vp, lbn, size, &nextlbn, &nextsize, 133 1, &bp); 134 } else 135 error = bread(vp, lbn, size, &bp); 136 if (error) 137 break; 138 ip->i_ci.ci_lastr = lbn; 139 140 /* 141 * We should only get non-zero b_resid when an I/O error 142 * has occurred, which should cause us to break above. 143 * However, if the short read did not cause an error, 144 * then we want to ensure that we do not uiomove bad 145 * or uninitialized data. 146 */ 147 size -= bp->b_resid; 148 if (size < xfersize) { 149 if (size == 0) 150 break; 151 xfersize = size; 152 } 153 error = uiomove((char *)bp->b_data + blkoffset, xfersize, uio); 154 if (error) 155 break; 156 brelse(bp); 157 } 158 if (bp != NULL) 159 brelse(bp); 160 161 if (!(vp->v_mount->mnt_flag & MNT_NOATIME)) { 162 ip->i_flag |= IN_ACCESS; 163 } 164 return (error); 165 } 166 167 int 168 ext4_ext_read(struct vnode *vp, struct inode *ip, struct m_ext2fs *fs, struct uio *uio) 169 { 170 struct ext4_extent_path path; 171 struct ext4_extent nex, *ep; 172 struct buf *bp; 173 size_t orig_resid; 174 daddr_t lbn, pos; 175 off_t bytesinfile; 176 long size, xfersize, blkoffset; 177 int error, cache_type; 178 179 memset(&path, 0, sizeof path); 180 181 orig_resid = uio->uio_resid; 182 if (orig_resid == 0) 183 return (0); 184 185 if (e2fs_overflow(fs, 0, uio->uio_offset)) 186 return (EFBIG); 187 188 while (uio->uio_resid > 0) { 189 if ((bytesinfile = ext2fs_size(ip) - uio->uio_offset) <= 0) 190 break; 191 lbn = lblkno(fs, uio->uio_offset); 192 size = fs->e2fs_bsize; 193 blkoffset = blkoff(fs, uio->uio_offset); 194 195 xfersize = fs->e2fs_fsize - blkoffset; 196 xfersize = MIN(xfersize, uio->uio_resid); 197 xfersize = MIN(xfersize, bytesinfile); 198 199 cache_type = ext4_ext_in_cache(ip, lbn, &nex); 200 switch (cache_type) { 201 case EXT4_EXT_CACHE_NO: 202 ext4_ext_find_extent(fs, ip, lbn, &path); 203 if ((ep = path.ep_ext) == NULL) 204 return (EIO); 205 ext4_ext_put_cache(ip, ep, EXT4_EXT_CACHE_IN); 206 207 pos = lbn - ep->e_blk + (((daddr_t) ep->e_start_hi << 32) | ep->e_start_lo); 208 if (path.ep_bp != NULL) { 209 brelse(path.ep_bp); 210 path.ep_bp = NULL; 211 } 212 break; 213 case EXT4_EXT_CACHE_GAP: 214 /* block has not been allocated yet */ 215 return (0); 216 case EXT4_EXT_CACHE_IN: 217 pos = lbn - nex.e_blk + (((daddr_t) nex.e_start_hi << 32) | nex.e_start_lo); 218 break; 219 } 220 error = bread(ip->i_devvp, fsbtodb(fs, pos), size, &bp); 221 if (error) { 222 brelse(bp); 223 return (error); 224 } 225 size -= bp->b_resid; 226 if (size < xfersize) { 227 if (size == 0) { 228 brelse(bp); 229 break; 230 } 231 xfersize = size; 232 } 233 error = uiomove(bp->b_data + blkoffset, xfersize, uio); 234 brelse(bp); 235 if (error) 236 return (error); 237 } 238 return (0); 239 } 240 241 /* 242 * Vnode op for writing. 243 */ 244 int 245 ext2fs_write(void *v) 246 { 247 struct vop_write_args *ap = v; 248 struct vnode *vp; 249 struct uio *uio; 250 struct inode *ip; 251 struct m_ext2fs *fs; 252 struct buf *bp; 253 int32_t lbn; 254 off_t osize; 255 int blkoffset, error, flags, ioflag, size, xfersize; 256 ssize_t resid, overrun; 257 258 ioflag = ap->a_ioflag; 259 uio = ap->a_uio; 260 vp = ap->a_vp; 261 ip = VTOI(vp); 262 263 #ifdef DIAGNOSTIC 264 if (uio->uio_rw != UIO_WRITE) 265 panic("%s: mode", "ext2fs_write"); 266 #endif 267 268 /* 269 * If writing 0 bytes, succeed and do not change 270 * update time or file offset (standards compliance) 271 */ 272 if (uio->uio_resid == 0) 273 return (0); 274 275 switch (vp->v_type) { 276 case VREG: 277 if (ioflag & IO_APPEND) 278 uio->uio_offset = ext2fs_size(ip); 279 if ((ip->i_e2fs_flags & EXT2_APPEND) && 280 uio->uio_offset != ext2fs_size(ip)) 281 return (EPERM); 282 /* FALLTHROUGH */ 283 case VLNK: 284 break; 285 case VDIR: 286 if ((ioflag & IO_SYNC) == 0) 287 panic("%s: nonsync dir write", "ext2fs_write"); 288 break; 289 default: 290 panic("%s: type", "ext2fs_write"); 291 } 292 293 fs = ip->i_e2fs; 294 if (e2fs_overflow(fs, uio->uio_resid, uio->uio_offset + uio->uio_resid)) 295 return (EFBIG); 296 297 /* do the filesize rlimit check */ 298 if ((error = vn_fsizechk(vp, uio, ioflag, &overrun))) 299 return (error); 300 301 resid = uio->uio_resid; 302 osize = ext2fs_size(ip); 303 flags = ioflag & IO_SYNC ? B_SYNC : 0; 304 305 for (error = 0; uio->uio_resid > 0;) { 306 lbn = lblkno(fs, uio->uio_offset); 307 blkoffset = blkoff(fs, uio->uio_offset); 308 xfersize = fs->e2fs_bsize - blkoffset; 309 if (uio->uio_resid < xfersize) 310 xfersize = uio->uio_resid; 311 if (fs->e2fs_bsize > xfersize) 312 flags |= B_CLRBUF; 313 else 314 flags &= ~B_CLRBUF; 315 316 error = ext2fs_buf_alloc(ip, 317 lbn, blkoffset + xfersize, ap->a_cred, &bp, flags); 318 if (error) 319 break; 320 if (uio->uio_offset + xfersize > ext2fs_size(ip)) { 321 error = ext2fs_setsize(ip, uio->uio_offset + xfersize); 322 if (error) 323 break; 324 uvm_vnp_setsize(vp, ip->i_e2fs_size); 325 } 326 uvm_vnp_uncache(vp); 327 328 size = fs->e2fs_bsize - bp->b_resid; 329 if (size < xfersize) 330 xfersize = size; 331 332 error = 333 uiomove((char *)bp->b_data + blkoffset, (int)xfersize, uio); 334 #if 0 335 if (ioflag & IO_NOCACHE) 336 bp->b_flags |= B_NOCACHE; 337 #endif 338 if (ioflag & IO_SYNC) 339 (void)bwrite(bp); 340 else if (xfersize + blkoffset == fs->e2fs_bsize) { 341 if (doclusterwrite) 342 cluster_write(bp, &ip->i_ci, ext2fs_size(ip)); 343 else 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 (error) { 359 if (ioflag & IO_UNIT) { 360 (void)ext2fs_truncate(ip, osize, 361 ioflag & IO_SYNC, ap->a_cred); 362 uio->uio_offset -= resid - uio->uio_resid; 363 uio->uio_resid = resid; 364 } 365 } else if (resid > uio->uio_resid && (ioflag & IO_SYNC)) { 366 error = ext2fs_update(ip, 1); 367 } 368 /* correct the result for writes clamped by vn_fsizechk() */ 369 uio->uio_resid += overrun; 370 return (error); 371 } 372