1 /* $NetBSD: ext2fs_inode.c,v 1.10 1998/10/23 00:33:24 thorpej Exp $ */ 2 3 /* 4 * Copyright (c) 1997 Manuel Bouyer. 5 * Copyright (c) 1982, 1986, 1989, 1993 6 * The Regents of the University of California. All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. All advertising materials mentioning features or use of this software 17 * must display the following acknowledgement: 18 * This product includes software developed by the University of 19 * California, Berkeley and its contributors. 20 * 4. Neither the name of the University nor the names of its contributors 21 * may be used to endorse or promote products derived from this software 22 * without specific prior written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34 * SUCH DAMAGE. 35 * 36 * @(#)ffs_inode.c 8.8 (Berkeley) 10/19/94 37 * Modified for ext2fs by Manuel Bouyer. 38 */ 39 40 #if defined(_KERNEL) && !defined(_LKM) 41 #include "opt_uvm.h" 42 #endif 43 44 #include <sys/param.h> 45 #include <sys/systm.h> 46 #include <sys/mount.h> 47 #include <sys/proc.h> 48 #include <sys/file.h> 49 #include <sys/buf.h> 50 #include <sys/vnode.h> 51 #include <sys/kernel.h> 52 #include <sys/malloc.h> 53 #include <sys/trace.h> 54 #include <sys/resourcevar.h> 55 56 #include <vm/vm.h> 57 #if defined(UVM) 58 #include <uvm/uvm_extern.h> 59 #endif 60 61 #include <ufs/ufs/quota.h> 62 #include <ufs/ufs/inode.h> 63 #include <ufs/ufs/ufsmount.h> 64 #include <ufs/ufs/ufs_extern.h> 65 66 #include <ufs/ext2fs/ext2fs.h> 67 #include <ufs/ext2fs/ext2fs_extern.h> 68 69 static int ext2fs_indirtrunc __P((struct inode *, ufs_daddr_t, ufs_daddr_t, 70 ufs_daddr_t, int, long *)); 71 72 /* 73 * Last reference to an inode. If necessary, write or delete it. 74 */ 75 int 76 ext2fs_inactive(v) 77 void *v; 78 { 79 struct vop_inactive_args /* { 80 struct vnode *a_vp; 81 struct proc *a_p; 82 } */ *ap = v; 83 struct vnode *vp = ap->a_vp; 84 struct inode *ip = VTOI(vp); 85 struct proc *p = ap->a_p; 86 struct timespec ts; 87 int error = 0; 88 extern int prtactive; 89 90 if (prtactive && vp->v_usecount != 0) 91 vprint("ext2fs_inactive: pushing active", vp); 92 /* Get rid of inodes related to stale file handles. */ 93 if (ip->i_e2fs_mode == 0 || ip->i_e2fs_dtime != 0) 94 goto out; 95 96 error = 0; 97 if (ip->i_e2fs_nlink == 0 && (vp->v_mount->mnt_flag & MNT_RDONLY) == 0) { 98 error = VOP_TRUNCATE(vp, (off_t)0, 0, NOCRED, NULL); 99 TIMEVAL_TO_TIMESPEC(&time, &ts); 100 ip->i_e2fs_dtime = ts.tv_sec; 101 ip->i_flag |= IN_CHANGE | IN_UPDATE; 102 VOP_VFREE(vp, ip->i_number, ip->i_e2fs_mode); 103 } 104 if (ip->i_flag & (IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE)) { 105 TIMEVAL_TO_TIMESPEC(&time, &ts); 106 VOP_UPDATE(vp, &ts, &ts, 0); 107 } 108 out: 109 VOP_UNLOCK(vp, 0); 110 /* 111 * If we are done with the inode, reclaim it 112 * so that it can be reused immediately. 113 */ 114 if (ip->i_e2fs_dtime != 0) 115 vrecycle(vp, (struct simplelock *)0, p); 116 return (error); 117 } 118 119 120 /* 121 * Update the access, modified, and inode change times as specified by the 122 * IACCESS, IUPDATE, and ICHANGE flags respectively. The IMODIFIED flag is 123 * used to specify that the inode needs to be updated but that the times have 124 * already been set. The access and modified times are taken from the second 125 * and third parameters; the inode change time is always taken from the current 126 * time. If waitfor is set, then wait for the disk write of the inode to 127 * complete. 128 */ 129 int 130 ext2fs_update(v) 131 void *v; 132 { 133 struct vop_update_args /* { 134 struct vnode *a_vp; 135 struct timespec *a_access; 136 struct timespec *a_modify; 137 int a_waitfor; 138 } */ *ap = v; 139 register struct m_ext2fs *fs; 140 struct buf *bp; 141 struct inode *ip; 142 int error; 143 struct timespec ts; 144 caddr_t cp; 145 146 if (ap->a_vp->v_mount->mnt_flag & MNT_RDONLY) 147 return (0); 148 ip = VTOI(ap->a_vp); 149 TIMEVAL_TO_TIMESPEC(&time, &ts); 150 EXT2FS_ITIMES(ip, ap->a_access, ap->a_modify, &ts); 151 if ((ip->i_flag & IN_MODIFIED) == 0) 152 return (0); 153 ip->i_flag &= ~IN_MODIFIED; 154 fs = ip->i_e2fs; 155 error = bread(ip->i_devvp, 156 fsbtodb(fs, ino_to_fsba(fs, ip->i_number)), 157 (int)fs->e2fs_bsize, NOCRED, &bp); 158 if (error) { 159 brelse(bp); 160 return (error); 161 } 162 cp = (caddr_t)bp->b_data + 163 (ino_to_fsbo(fs, ip->i_number) * EXT2_DINODE_SIZE); 164 e2fs_isave(&ip->i_din.e2fs_din, (struct ext2fs_dinode *)cp); 165 if (ap->a_waitfor && (ap->a_vp->v_mount->mnt_flag & MNT_ASYNC) == 0) 166 return (bwrite(bp)); 167 else { 168 bdwrite(bp); 169 return (0); 170 } 171 } 172 173 #define SINGLE 0 /* index of single indirect block */ 174 #define DOUBLE 1 /* index of double indirect block */ 175 #define TRIPLE 2 /* index of triple indirect block */ 176 /* 177 * Truncate the inode oip to at most length size, freeing the 178 * disk blocks. 179 */ 180 int 181 ext2fs_truncate(v) 182 void *v; 183 { 184 struct vop_truncate_args /* { 185 struct vnode *a_vp; 186 off_t a_length; 187 int a_flags; 188 struct ucred *a_cred; 189 struct proc *a_p; 190 } */ *ap = v; 191 register struct vnode *ovp = ap->a_vp; 192 register ufs_daddr_t lastblock; 193 register struct inode *oip; 194 ufs_daddr_t bn, lbn, lastiblock[NIADDR], indir_lbn[NIADDR]; 195 ufs_daddr_t oldblks[NDADDR + NIADDR], newblks[NDADDR + NIADDR]; 196 off_t length = ap->a_length; 197 register struct m_ext2fs *fs; 198 struct buf *bp; 199 int offset, size, level; 200 long count, nblocks, vflags, blocksreleased = 0; 201 struct timespec ts; 202 register int i; 203 int aflags, error, allerror; 204 off_t osize; 205 206 if (length < 0) 207 return (EINVAL); 208 209 oip = VTOI(ovp); 210 TIMEVAL_TO_TIMESPEC(&time, &ts); 211 if (ovp->v_type == VLNK && 212 (oip->i_e2fs_size < ovp->v_mount->mnt_maxsymlinklen || 213 (ovp->v_mount->mnt_maxsymlinklen == 0 && 214 oip->i_e2fs_nblock == 0))) { 215 #ifdef DIAGNOSTIC 216 if (length != 0) 217 panic("ext2fs_truncate: partial truncate of symlink"); 218 #endif 219 memset((char *)&oip->i_din.e2fs_din.e2di_shortlink, 0, 220 (u_int)oip->i_e2fs_size); 221 oip->i_e2fs_size = 0; 222 oip->i_flag |= IN_CHANGE | IN_UPDATE; 223 return (VOP_UPDATE(ovp, &ts, &ts, 1)); 224 } 225 if (oip->i_e2fs_size == length) { 226 oip->i_flag |= IN_CHANGE | IN_UPDATE; 227 return (VOP_UPDATE(ovp, &ts, &ts, 0)); 228 } 229 fs = oip->i_e2fs; 230 osize = oip->i_e2fs_size; 231 /* 232 * Lengthen the size of the file. We must ensure that the 233 * last byte of the file is allocated. Since the smallest 234 * value of osize is 0, length will be at least 1. 235 */ 236 if (osize < length) { 237 #if 0 /* XXX */ 238 if (length > fs->fs_maxfilesize) 239 return (EFBIG); 240 #endif 241 offset = blkoff(fs, length - 1); 242 lbn = lblkno(fs, length - 1); 243 aflags = B_CLRBUF; 244 if (ap->a_flags & IO_SYNC) 245 aflags |= B_SYNC; 246 error = ext2fs_balloc(oip, lbn, offset + 1, ap->a_cred, &bp, 247 aflags); 248 if (error) 249 return (error); 250 oip->i_e2fs_size = length; 251 #if defined(UVM) 252 uvm_vnp_setsize(ovp, length); 253 (void) uvm_vnp_uncache(ovp); 254 #else 255 vnode_pager_setsize(ovp, length); 256 (void) vnode_pager_uncache(ovp); 257 #endif 258 if (aflags & B_SYNC) 259 bwrite(bp); 260 else 261 bawrite(bp); 262 oip->i_flag |= IN_CHANGE | IN_UPDATE; 263 return (VOP_UPDATE(ovp, &ts, &ts, 1)); 264 } 265 /* 266 * Shorten the size of the file. If the file is not being 267 * truncated to a block boundry, the contents of the 268 * partial block following the end of the file must be 269 * zero'ed in case it ever become accessable again because 270 * of subsequent file growth. 271 */ 272 offset = blkoff(fs, length); 273 if (offset == 0) { 274 oip->i_e2fs_size = length; 275 } else { 276 lbn = lblkno(fs, length); 277 aflags = B_CLRBUF; 278 if (ap->a_flags & IO_SYNC) 279 aflags |= B_SYNC; 280 error = ext2fs_balloc(oip, lbn, offset, ap->a_cred, &bp, aflags); 281 if (error) 282 return (error); 283 oip->i_e2fs_size = length; 284 size = fs->e2fs_bsize; 285 #if defined(UVM) 286 (void) uvm_vnp_uncache(ovp); 287 #else 288 (void) vnode_pager_uncache(ovp); 289 #endif 290 memset((char *)bp->b_data + offset, 0, (u_int)(size - offset)); 291 allocbuf(bp, size); 292 if (aflags & B_SYNC) 293 bwrite(bp); 294 else 295 bawrite(bp); 296 } 297 #if defined(UVM) 298 uvm_vnp_setsize(ovp, length); 299 #else 300 vnode_pager_setsize(ovp, length); 301 #endif 302 /* 303 * Calculate index into inode's block list of 304 * last direct and indirect blocks (if any) 305 * which we want to keep. Lastblock is -1 when 306 * the file is truncated to 0. 307 */ 308 lastblock = lblkno(fs, length + fs->e2fs_bsize - 1) - 1; 309 lastiblock[SINGLE] = lastblock - NDADDR; 310 lastiblock[DOUBLE] = lastiblock[SINGLE] - NINDIR(fs); 311 lastiblock[TRIPLE] = lastiblock[DOUBLE] - NINDIR(fs) * NINDIR(fs); 312 nblocks = btodb(fs->e2fs_bsize); 313 /* 314 * Update file and block pointers on disk before we start freeing 315 * blocks. If we crash before free'ing blocks below, the blocks 316 * will be returned to the free list. lastiblock values are also 317 * normalized to -1 for calls to ext2fs_indirtrunc below. 318 */ 319 memcpy((caddr_t)oldblks, (caddr_t)&oip->i_e2fs_blocks[0], sizeof oldblks); 320 for (level = TRIPLE; level >= SINGLE; level--) 321 if (lastiblock[level] < 0) { 322 oip->i_e2fs_blocks[NDADDR + level] = 0; 323 lastiblock[level] = -1; 324 } 325 for (i = NDADDR - 1; i > lastblock; i--) 326 oip->i_e2fs_blocks[i] = 0; 327 oip->i_flag |= IN_CHANGE | IN_UPDATE; 328 if ((error = VOP_UPDATE(ovp, &ts, &ts, 1)) != 0) 329 allerror = error; 330 /* 331 * Having written the new inode to disk, save its new configuration 332 * and put back the old block pointers long enough to process them. 333 * Note that we save the new block configuration so we can check it 334 * when we are done. 335 */ 336 memcpy((caddr_t)newblks, (caddr_t)&oip->i_e2fs_blocks[0], sizeof newblks); 337 memcpy((caddr_t)&oip->i_e2fs_blocks[0], (caddr_t)oldblks, sizeof oldblks); 338 oip->i_e2fs_size = osize; 339 vflags = ((length > 0) ? V_SAVE : 0) | V_SAVEMETA; 340 allerror = vinvalbuf(ovp, vflags, ap->a_cred, ap->a_p, 0, 0); 341 342 /* 343 * Indirect blocks first. 344 */ 345 indir_lbn[SINGLE] = -NDADDR; 346 indir_lbn[DOUBLE] = indir_lbn[SINGLE] - NINDIR(fs) -1; 347 indir_lbn[TRIPLE] = indir_lbn[DOUBLE] - NINDIR(fs) * NINDIR(fs) - 1; 348 for (level = TRIPLE; level >= SINGLE; level--) { 349 bn = fs2h32(oip->i_e2fs_blocks[NDADDR + level]); 350 if (bn != 0) { 351 error = ext2fs_indirtrunc(oip, indir_lbn[level], 352 fsbtodb(fs, bn), lastiblock[level], level, &count); 353 if (error) 354 allerror = error; 355 blocksreleased += count; 356 if (lastiblock[level] < 0) { 357 oip->i_e2fs_blocks[NDADDR + level] = 0; 358 ext2fs_blkfree(oip, bn); 359 blocksreleased += nblocks; 360 } 361 } 362 if (lastiblock[level] >= 0) 363 goto done; 364 } 365 366 /* 367 * All whole direct blocks or frags. 368 */ 369 for (i = NDADDR - 1; i > lastblock; i--) { 370 bn = fs2h32(oip->i_e2fs_blocks[i]); 371 if (bn == 0) 372 continue; 373 oip->i_e2fs_blocks[i] = 0; 374 ext2fs_blkfree(oip, bn); 375 blocksreleased += btodb(fs->e2fs_bsize); 376 } 377 if (lastblock < 0) 378 goto done; 379 380 done: 381 #ifdef DIAGNOSTIC 382 for (level = SINGLE; level <= TRIPLE; level++) 383 if (newblks[NDADDR + level] != 384 fs2h32(oip->i_e2fs_blocks[NDADDR + level])) 385 panic("itrunc1"); 386 for (i = 0; i < NDADDR; i++) 387 if (newblks[i] != fs2h32(oip->i_e2fs_blocks[i])) 388 panic("itrunc2"); 389 if (length == 0 && 390 (ovp->v_dirtyblkhd.lh_first || ovp->v_cleanblkhd.lh_first)) 391 panic("itrunc3"); 392 #endif /* DIAGNOSTIC */ 393 /* 394 * Put back the real size. 395 */ 396 oip->i_e2fs_size = length; 397 oip->i_e2fs_nblock -= blocksreleased; 398 if (oip->i_e2fs_nblock < 0) /* sanity */ 399 oip->i_e2fs_nblock = 0; 400 oip->i_flag |= IN_CHANGE; 401 return (allerror); 402 } 403 404 /* 405 * Release blocks associated with the inode ip and stored in the indirect 406 * block bn. Blocks are free'd in LIFO order up to (but not including) 407 * lastbn. If level is greater than SINGLE, the block is an indirect block 408 * and recursive calls to indirtrunc must be used to cleanse other indirect 409 * blocks. 410 * 411 * NB: triple indirect blocks are untested. 412 */ 413 static int 414 ext2fs_indirtrunc(ip, lbn, dbn, lastbn, level, countp) 415 register struct inode *ip; 416 ufs_daddr_t lbn, lastbn; 417 ufs_daddr_t dbn; 418 int level; 419 long *countp; 420 { 421 register int i; 422 struct buf *bp; 423 register struct m_ext2fs *fs = ip->i_e2fs; 424 register ufs_daddr_t *bap; 425 struct vnode *vp; 426 ufs_daddr_t *copy = NULL, nb, nlbn, last; 427 long blkcount, factor; 428 int nblocks, blocksreleased = 0; 429 int error = 0, allerror = 0; 430 431 /* 432 * Calculate index in current block of last 433 * block to be kept. -1 indicates the entire 434 * block so we need not calculate the index. 435 */ 436 factor = 1; 437 for (i = SINGLE; i < level; i++) 438 factor *= NINDIR(fs); 439 last = lastbn; 440 if (lastbn > 0) 441 last /= factor; 442 nblocks = btodb(fs->e2fs_bsize); 443 /* 444 * Get buffer of block pointers, zero those entries corresponding 445 * to blocks to be free'd, and update on disk copy first. Since 446 * double(triple) indirect before single(double) indirect, calls 447 * to bmap on these blocks will fail. However, we already have 448 * the on disk address, so we have to set the b_blkno field 449 * explicitly instead of letting bread do everything for us. 450 */ 451 vp = ITOV(ip); 452 bp = getblk(vp, lbn, (int)fs->e2fs_bsize, 0, 0); 453 if (bp->b_flags & (B_DONE | B_DELWRI)) { 454 /* Braces must be here in case trace evaluates to nothing. */ 455 trace(TR_BREADHIT, pack(vp, fs->e2fs_bsize), lbn); 456 } else { 457 trace(TR_BREADMISS, pack(vp, fs->e2fs_bsize), lbn); 458 curproc->p_stats->p_ru.ru_inblock++; /* pay for read */ 459 bp->b_flags |= B_READ; 460 if (bp->b_bcount > bp->b_bufsize) 461 panic("ext2fs_indirtrunc: bad buffer size"); 462 bp->b_blkno = dbn; 463 VOP_STRATEGY(bp); 464 error = biowait(bp); 465 } 466 if (error) { 467 brelse(bp); 468 *countp = 0; 469 return (error); 470 } 471 472 bap = (ufs_daddr_t *)bp->b_data; 473 if (lastbn != -1) { 474 MALLOC(copy, ufs_daddr_t *, fs->e2fs_bsize, M_TEMP, M_WAITOK); 475 memcpy((caddr_t)copy, (caddr_t)bap, (u_int)fs->e2fs_bsize); 476 memset((caddr_t)&bap[last + 1], 0, 477 (u_int)(NINDIR(fs) - (last + 1)) * sizeof (u_int32_t)); 478 error = bwrite(bp); 479 if (error) 480 allerror = error; 481 bap = copy; 482 } 483 484 /* 485 * Recursively free totally unused blocks. 486 */ 487 for (i = NINDIR(fs) - 1, 488 nlbn = lbn + 1 - i * factor; i > last; 489 i--, nlbn += factor) { 490 nb = fs2h32(bap[i]); 491 if (nb == 0) 492 continue; 493 if (level > SINGLE) { 494 error = ext2fs_indirtrunc(ip, nlbn, fsbtodb(fs, nb), 495 (ufs_daddr_t)-1, level - 1, 496 &blkcount); 497 if (error) 498 allerror = error; 499 blocksreleased += blkcount; 500 } 501 ext2fs_blkfree(ip, nb); 502 blocksreleased += nblocks; 503 } 504 505 /* 506 * Recursively free last partial block. 507 */ 508 if (level > SINGLE && lastbn >= 0) { 509 last = lastbn % factor; 510 nb = fs2h32(bap[i]); 511 if (nb != 0) { 512 error = ext2fs_indirtrunc(ip, nlbn, fsbtodb(fs, nb), 513 last, level - 1, &blkcount); 514 if (error) 515 allerror = error; 516 blocksreleased += blkcount; 517 } 518 } 519 520 if (copy != NULL) { 521 FREE(copy, M_TEMP); 522 } else { 523 bp->b_flags |= B_INVAL; 524 brelse(bp); 525 } 526 527 *countp = blocksreleased; 528 return (allerror); 529 } 530