1 /* 2 * Copyright (c) 1982, 1986, 1989 Regents of the University of California. 3 * All rights reserved. 4 * 5 * %sccs.include.redist.c% 6 * 7 * @(#)ffs_inode.c 7.60 (Berkeley) 07/07/92 8 */ 9 10 #include <sys/param.h> 11 #include <sys/systm.h> 12 #include <sys/mount.h> 13 #include <sys/proc.h> 14 #include <sys/file.h> 15 #include <sys/buf.h> 16 #include <sys/vnode.h> 17 #include <sys/kernel.h> 18 #include <sys/malloc.h> 19 20 #include <vm/vm.h> 21 22 #include <ufs/ufs/quota.h> 23 #include <ufs/ufs/inode.h> 24 #include <ufs/ufs/ufsmount.h> 25 #include <ufs/ufs/ufs_extern.h> 26 27 #include <ufs/ffs/fs.h> 28 #include <ufs/ffs/ffs_extern.h> 29 30 static int ffs_indirtrunc __P((struct inode *, daddr_t, daddr_t, int, long *)); 31 32 int 33 ffs_init() 34 { 35 return (ufs_init()); 36 } 37 38 /* 39 * Update the access, modified, and inode change times as specified 40 * by the IACC, IUPD, and ICHG flags respectively. The IMOD flag 41 * is used to specify that the inode needs to be updated but that 42 * the times have already been set. The access and modified times 43 * are taken from the second and third parameters; the inode change 44 * time is always taken from the current time. If waitfor is set, 45 * then wait for the disk write of the inode to complete. 46 */ 47 int 48 ffs_update(ap) 49 struct vop_update_args /* { 50 struct vnode *a_vp; 51 struct timeval *a_ta; 52 struct timeval *a_tm; 53 int a_waitfor; 54 } */ *ap; 55 { 56 struct buf *bp; 57 struct inode *ip; 58 struct dinode *dp; 59 register struct fs *fs; 60 int error; 61 62 if (ap->a_vp->v_mount->mnt_flag & MNT_RDONLY) 63 return (0); 64 ip = VTOI(ap->a_vp); 65 if ((ip->i_flag & (IUPD|IACC|ICHG|IMOD)) == 0) 66 return (0); 67 if (ip->i_flag&IACC) 68 ip->i_atime.ts_sec = ap->a_ta->tv_sec; 69 if (ip->i_flag&IUPD) { 70 ip->i_mtime.ts_sec = ap->a_tm->tv_sec; 71 ip->i_modrev++; 72 } 73 if (ip->i_flag&ICHG) 74 ip->i_ctime.ts_sec = time.tv_sec; 75 ip->i_flag &= ~(IUPD|IACC|ICHG|IMOD); 76 fs = ip->i_fs; 77 /* 78 * Ensure that uid and gid are correct. This is a temporary 79 * fix until fsck has been changed to do the update. 80 */ 81 if (fs->fs_inodefmt < FS_44INODEFMT) { /* XXX */ 82 ip->i_din.di_ouid = ip->i_uid; /* XXX */ 83 ip->i_din.di_ogid = ip->i_gid; /* XXX */ 84 } /* XXX */ 85 if (error = bread(ip->i_devvp, fsbtodb(fs, itod(fs, ip->i_number)), 86 (int)fs->fs_bsize, NOCRED, &bp)) { 87 brelse(bp); 88 return (error); 89 } 90 dp = bp->b_un.b_dino + itoo(fs, ip->i_number); 91 *dp = ip->i_din; 92 if (ap->a_waitfor) 93 return (bwrite(bp)); 94 else { 95 bdwrite(bp); 96 return (0); 97 } 98 } 99 100 #define SINGLE 0 /* index of single indirect block */ 101 #define DOUBLE 1 /* index of double indirect block */ 102 #define TRIPLE 2 /* index of triple indirect block */ 103 /* 104 * Truncate the inode ip to at most length size. Free affected disk 105 * blocks -- the blocks of the file are removed in reverse order. 106 */ 107 ffs_truncate(ap) 108 struct vop_truncate_args /* { 109 struct vnode *a_vp; 110 off_t a_length; 111 int a_flags; 112 struct ucred *a_cred; 113 struct proc *a_p; 114 } */ *ap; 115 { 116 register struct vnode *ovp = ap->a_vp; 117 register daddr_t lastblock; 118 register struct inode *oip; 119 daddr_t bn, lbn, lastiblock[NIADDR]; 120 off_t length = ap->a_length; 121 register struct fs *fs; 122 register struct inode *ip; 123 struct buf *bp; 124 int offset, size, level; 125 long count, nblocks, blocksreleased = 0; 126 struct timeval tv; 127 register int i; 128 int aflags, error, allerror; 129 struct inode tip; 130 off_t osize; 131 132 oip = VTOI(ovp); 133 tv = time; 134 if (ovp->v_type == VLNK && ovp->v_mount->mnt_maxsymlinklen > 0) { 135 #ifdef DIAGNOSTIC 136 if (length != 0) 137 panic("ffs_truncate: partial truncate of symlink"); 138 #endif 139 bzero((char *)&oip->i_shortlink, (u_int)oip->i_size); 140 oip->i_size = 0; 141 oip->i_flag |= ICHG|IUPD; 142 return (VOP_UPDATE(ovp, &tv, &tv, 1)); 143 } 144 if (oip->i_size <= length) { 145 oip->i_flag |= ICHG|IUPD; 146 return (VOP_UPDATE(ovp, &tv, &tv, 1)); 147 } 148 vnode_pager_setsize(ovp, (u_long)length); 149 /* 150 * Calculate index into inode's block list of 151 * last direct and indirect blocks (if any) 152 * which we want to keep. Lastblock is -1 when 153 * the file is truncated to 0. 154 */ 155 fs = oip->i_fs; 156 lastblock = lblkno(fs, length + fs->fs_bsize - 1) - 1; 157 lastiblock[SINGLE] = lastblock - NDADDR; 158 lastiblock[DOUBLE] = lastiblock[SINGLE] - NINDIR(fs); 159 lastiblock[TRIPLE] = lastiblock[DOUBLE] - NINDIR(fs) * NINDIR(fs); 160 nblocks = btodb(fs->fs_bsize); 161 /* 162 * Update the size of the file. If the file is not being 163 * truncated to a block boundry, the contents of the 164 * partial block following the end of the file must be 165 * zero'ed in case it ever become accessable again because 166 * of subsequent file growth. 167 */ 168 osize = oip->i_size; 169 offset = blkoff(fs, length); 170 if (offset == 0) { 171 oip->i_size = length; 172 } else { 173 lbn = lblkno(fs, length); 174 aflags = B_CLRBUF; 175 if (ap->a_flags & IO_SYNC) 176 aflags |= B_SYNC; 177 #ifdef QUOTA 178 if (error = getinoquota(oip)) 179 return (error); 180 #endif 181 if (error = ffs_balloc(oip, lbn, offset, ap->a_cred, &bp, aflags)) 182 return (error); 183 oip->i_size = length; 184 size = blksize(fs, oip, lbn); 185 (void) vnode_pager_uncache(ovp); 186 bzero(bp->b_un.b_addr + offset, (unsigned)(size - offset)); 187 allocbuf(bp, size); 188 if (ap->a_flags & IO_SYNC) 189 bwrite(bp); 190 else 191 bdwrite(bp); 192 } 193 /* 194 * Update file and block pointers on disk before we start freeing 195 * blocks. If we crash before free'ing blocks below, the blocks 196 * will be returned to the free list. lastiblock values are also 197 * normalized to -1 for calls to ffs_indirtrunc below. 198 */ 199 tip = *oip; 200 tip.i_size = osize; 201 for (level = TRIPLE; level >= SINGLE; level--) 202 if (lastiblock[level] < 0) { 203 oip->i_ib[level] = 0; 204 lastiblock[level] = -1; 205 } 206 for (i = NDADDR - 1; i > lastblock; i--) 207 oip->i_db[i] = 0; 208 oip->i_flag |= ICHG|IUPD; 209 allerror = vinvalbuf(ovp, length > 0, ap->a_cred, ap->a_p); 210 if (error = VOP_UPDATE(ovp, &tv, &tv, MNT_WAIT)) 211 allerror = error; 212 213 /* 214 * Indirect blocks first. 215 */ 216 ip = &tip; 217 for (level = TRIPLE; level >= SINGLE; level--) { 218 bn = ip->i_ib[level]; 219 if (bn != 0) { 220 error = ffs_indirtrunc(ip, 221 bn, lastiblock[level], level, &count); 222 if (error) 223 allerror = error; 224 blocksreleased += count; 225 if (lastiblock[level] < 0) { 226 ip->i_ib[level] = 0; 227 ffs_blkfree(ip, bn, fs->fs_bsize); 228 blocksreleased += nblocks; 229 } 230 } 231 if (lastiblock[level] >= 0) 232 goto done; 233 } 234 235 /* 236 * All whole direct blocks or frags. 237 */ 238 for (i = NDADDR - 1; i > lastblock; i--) { 239 register long bsize; 240 241 bn = ip->i_db[i]; 242 if (bn == 0) 243 continue; 244 ip->i_db[i] = 0; 245 bsize = blksize(fs, ip, i); 246 ffs_blkfree(ip, bn, bsize); 247 blocksreleased += btodb(bsize); 248 } 249 if (lastblock < 0) 250 goto done; 251 252 /* 253 * Finally, look for a change in size of the 254 * last direct block; release any frags. 255 */ 256 bn = ip->i_db[lastblock]; 257 if (bn != 0) { 258 long oldspace, newspace; 259 260 /* 261 * Calculate amount of space we're giving 262 * back as old block size minus new block size. 263 */ 264 oldspace = blksize(fs, ip, lastblock); 265 ip->i_size = length; 266 newspace = blksize(fs, ip, lastblock); 267 if (newspace == 0) 268 panic("itrunc: newspace"); 269 if (oldspace - newspace > 0) { 270 /* 271 * Block number of space to be free'd is 272 * the old block # plus the number of frags 273 * required for the storage we're keeping. 274 */ 275 bn += numfrags(fs, newspace); 276 ffs_blkfree(ip, bn, oldspace - newspace); 277 blocksreleased += btodb(oldspace - newspace); 278 } 279 } 280 done: 281 /* BEGIN PARANOIA */ 282 for (level = SINGLE; level <= TRIPLE; level++) 283 if (ip->i_ib[level] != oip->i_ib[level]) 284 panic("itrunc1"); 285 for (i = 0; i < NDADDR; i++) 286 if (ip->i_db[i] != oip->i_db[i]) 287 panic("itrunc2"); 288 /* END PARANOIA */ 289 oip->i_blocks -= blocksreleased; 290 if (oip->i_blocks < 0) /* sanity */ 291 oip->i_blocks = 0; 292 oip->i_flag |= ICHG; 293 #ifdef QUOTA 294 if (!getinoquota(oip)) 295 (void) chkdq(oip, -blocksreleased, NOCRED, 0); 296 #endif 297 return (allerror); 298 } 299 300 /* 301 * Release blocks associated with the inode ip and stored in the indirect 302 * block bn. Blocks are free'd in LIFO order up to (but not including) 303 * lastbn. If level is greater than SINGLE, the block is an indirect block 304 * and recursive calls to indirtrunc must be used to cleanse other indirect 305 * blocks. 306 * 307 * NB: triple indirect blocks are untested. 308 */ 309 static int 310 ffs_indirtrunc(ip, bn, lastbn, level, countp) 311 register struct inode *ip; 312 daddr_t bn, lastbn; 313 int level; 314 long *countp; 315 { 316 register int i; 317 struct buf *bp; 318 register struct fs *fs = ip->i_fs; 319 register daddr_t *bap; 320 daddr_t *copy, nb, last; 321 long blkcount, factor; 322 int nblocks, blocksreleased = 0; 323 int error, allerror = 0; 324 325 /* 326 * Calculate index in current block of last 327 * block to be kept. -1 indicates the entire 328 * block so we need not calculate the index. 329 */ 330 factor = 1; 331 for (i = SINGLE; i < level; i++) 332 factor *= NINDIR(fs); 333 last = lastbn; 334 if (lastbn > 0) 335 last /= factor; 336 nblocks = btodb(fs->fs_bsize); 337 /* 338 * Get buffer of block pointers, zero those 339 * entries corresponding to blocks to be free'd, 340 * and update on disk copy first. 341 */ 342 error = bread(ip->i_devvp, fsbtodb(fs, bn), (int)fs->fs_bsize, 343 NOCRED, &bp); 344 if (error) { 345 brelse(bp); 346 *countp = 0; 347 return (error); 348 } 349 bap = bp->b_un.b_daddr; 350 MALLOC(copy, daddr_t *, fs->fs_bsize, M_TEMP, M_WAITOK); 351 bcopy((caddr_t)bap, (caddr_t)copy, (u_int)fs->fs_bsize); 352 bzero((caddr_t)&bap[last + 1], 353 (u_int)(NINDIR(fs) - (last + 1)) * sizeof (daddr_t)); 354 if (last == -1) 355 bp->b_flags |= B_INVAL; 356 error = bwrite(bp); 357 if (error) 358 allerror = error; 359 bap = copy; 360 361 /* 362 * Recursively free totally unused blocks. 363 */ 364 for (i = NINDIR(fs) - 1; i > last; i--) { 365 nb = bap[i]; 366 if (nb == 0) 367 continue; 368 if (level > SINGLE) { 369 if (error = ffs_indirtrunc(ip, 370 nb, (daddr_t)-1, level - 1, &blkcount)) 371 allerror = error; 372 blocksreleased += blkcount; 373 } 374 ffs_blkfree(ip, nb, fs->fs_bsize); 375 blocksreleased += nblocks; 376 } 377 378 /* 379 * Recursively free last partial block. 380 */ 381 if (level > SINGLE && lastbn >= 0) { 382 last = lastbn % factor; 383 nb = bap[i]; 384 if (nb != 0) { 385 if (error = 386 ffs_indirtrunc(ip, nb, last, level - 1, &blkcount)) 387 allerror = error; 388 blocksreleased += blkcount; 389 } 390 } 391 FREE(copy, M_TEMP); 392 *countp = blocksreleased; 393 return (allerror); 394 } 395