1 /* 2 * Copyright (c) 1986, 1989, 1991, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * %sccs.include.redist.c% 6 * 7 * @(#)lfs_inode.c 8.9 (Berkeley) 05/08/95 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/lfs/lfs.h> 28 #include <ufs/lfs/lfs_extern.h> 29 30 /* Search a block for a specific dinode. */ 31 struct dinode * 32 lfs_ifind(fs, ino, dip) 33 struct lfs *fs; 34 ino_t ino; 35 register struct dinode *dip; 36 { 37 register int cnt; 38 register struct dinode *ldip; 39 40 for (cnt = INOPB(fs), ldip = dip + (cnt - 1); cnt--; --ldip) 41 if (ldip->di_inumber == ino) 42 return (ldip); 43 44 panic("lfs_ifind: dinode %u not found", ino); 45 /* NOTREACHED */ 46 } 47 48 int 49 lfs_update(ap) 50 struct vop_update_args /* { 51 struct vnode *a_vp; 52 struct timeval *a_access; 53 struct timeval *a_modify; 54 int a_waitfor; 55 } */ *ap; 56 { 57 struct vnode *vp = ap->a_vp; 58 struct inode *ip; 59 60 if (vp->v_mount->mnt_flag & MNT_RDONLY) 61 return (0); 62 ip = VTOI(vp); 63 if ((ip->i_flag & 64 (IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE)) == 0) 65 return (0); 66 if (ip->i_flag & IN_ACCESS) 67 ip->i_atime = ap->a_access->tv_sec; 68 if (ip->i_flag & IN_UPDATE) { 69 ip->i_mtime = ap->a_modify->tv_sec; 70 (ip)->i_modrev++; 71 } 72 if (ip->i_flag & IN_CHANGE) 73 ip->i_ctime = time.tv_sec; 74 ip->i_flag &= ~(IN_ACCESS | IN_CHANGE | IN_UPDATE); 75 76 if (!(ip->i_flag & IN_MODIFIED)) 77 ++(VFSTOUFS(vp->v_mount)->um_lfs->lfs_uinodes); 78 ip->i_flag |= IN_MODIFIED; 79 80 /* If sync, push back the vnode and any dirty blocks it may have. */ 81 return (ap->a_waitfor & LFS_SYNC ? lfs_vflush(vp) : 0); 82 } 83 84 /* Update segment usage information when removing a block. */ 85 #define UPDATE_SEGUSE \ 86 if (lastseg != -1) { \ 87 LFS_SEGENTRY(sup, fs, lastseg, sup_bp); \ 88 if (num > sup->su_nbytes) \ 89 panic("lfs_truncate: negative bytes in segment %d\n", \ 90 lastseg); \ 91 sup->su_nbytes -= num; \ 92 e1 = VOP_BWRITE(sup_bp); \ 93 fragsreleased += numfrags(fs, num); \ 94 } 95 96 #define SEGDEC(S) { \ 97 if (daddr != 0) { \ 98 if (lastseg != (seg = datosn(fs, daddr))) { \ 99 UPDATE_SEGUSE; \ 100 num = (S); \ 101 lastseg = seg; \ 102 } else \ 103 num += (S); \ 104 } \ 105 } 106 107 /* 108 * Truncate the inode ip to at most length size. Update segment usage 109 * table information. 110 */ 111 /* ARGSUSED */ 112 int 113 lfs_truncate(ap) 114 struct vop_truncate_args /* { 115 struct vnode *a_vp; 116 off_t a_length; 117 int a_flags; 118 struct ucred *a_cred; 119 struct proc *a_p; 120 } */ *ap; 121 { 122 register struct indir *inp; 123 register int i; 124 register ufs_daddr_t *daddrp; 125 register struct vnode *vp = ap->a_vp; 126 off_t length = ap->a_length; 127 struct buf *bp, *sup_bp; 128 struct timeval tv; 129 struct ifile *ifp; 130 struct inode *ip; 131 struct lfs *fs; 132 struct indir a[NIADDR + 2], a_end[NIADDR + 2]; 133 SEGUSE *sup; 134 ufs_daddr_t daddr, lastblock, lbn, olastblock; 135 ufs_daddr_t oldsize_lastblock, oldsize_newlast, newsize; 136 long off, a_released, fragsreleased, i_released; 137 int e1, e2, depth, lastseg, num, offset, seg, freesize; 138 139 ip = VTOI(vp); 140 tv = time; 141 if (vp->v_type == VLNK && vp->v_mount->mnt_maxsymlinklen > 0) { 142 #ifdef DIAGNOSTIC 143 if (length != 0) 144 panic("lfs_truncate: partial truncate of symlink"); 145 #endif 146 bzero((char *)&ip->i_shortlink, (u_int)ip->i_size); 147 ip->i_size = 0; 148 ip->i_flag |= IN_CHANGE | IN_UPDATE; 149 return (VOP_UPDATE(vp, &tv, &tv, 0)); 150 } 151 vnode_pager_setsize(vp, (u_long)length); 152 153 fs = ip->i_lfs; 154 155 /* If length is larger than the file, just update the times. */ 156 if (ip->i_size <= length) { 157 ip->i_flag |= IN_CHANGE | IN_UPDATE; 158 return (VOP_UPDATE(vp, &tv, &tv, 0)); 159 } 160 161 /* 162 * Calculate index into inode's block list of last direct and indirect 163 * blocks (if any) which we want to keep. Lastblock is 0 when the 164 * file is truncated to 0. 165 */ 166 lastblock = lblkno(fs, length + fs->lfs_bsize - 1); 167 olastblock = lblkno(fs, ip->i_size + fs->lfs_bsize - 1) - 1; 168 169 /* 170 * Update the size of the file. If the file is not being truncated to 171 * a block boundry, the contents of the partial block following the end 172 * of the file must be zero'ed in case it ever become accessable again 173 * because of subsequent file growth. For this part of the code, 174 * oldsize_newlast refers to the old size of the new last block in the file. 175 */ 176 offset = blkoff(fs, length); 177 lbn = lblkno(fs, length); 178 oldsize_newlast = blksize(fs, ip, lbn); 179 180 /* Now set oldsize to the current size of the current last block */ 181 oldsize_lastblock = blksize(fs, ip, olastblock); 182 if (offset == 0) 183 ip->i_size = length; 184 else { 185 #ifdef QUOTA 186 if (e1 = getinoquota(ip)) 187 return (e1); 188 #endif 189 if (e1 = bread(vp, lbn, oldsize_newlast, NOCRED, &bp)) 190 return (e1); 191 ip->i_size = length; 192 (void)vnode_pager_uncache(vp); 193 newsize = blksize(fs, ip, lbn); 194 bzero((char *)bp->b_data + offset, (u_int)(newsize - offset)); 195 allocbuf(bp, newsize); 196 if (e1 = VOP_BWRITE(bp)) 197 return (e1); 198 } 199 /* 200 * Modify sup->su_nbyte counters for each deleted block; keep track 201 * of number of blocks removed for ip->i_blocks. 202 */ 203 fragsreleased = 0; 204 num = 0; 205 lastseg = -1; 206 207 for (lbn = olastblock; lbn >= lastblock;) { 208 /* XXX use run length from bmap array to make this faster */ 209 ufs_bmaparray(vp, lbn, &daddr, a, &depth, NULL); 210 if (lbn == olastblock) { 211 for (i = NIADDR + 2; i--;) 212 a_end[i] = a[i]; 213 freesize = oldsize_lastblock; 214 } else 215 freesize = fs->lfs_bsize; 216 217 switch (depth) { 218 case 0: /* Direct block. */ 219 daddr = ip->i_db[lbn]; 220 SEGDEC(freesize); 221 ip->i_db[lbn] = 0; 222 --lbn; 223 break; 224 #ifdef DIAGNOSTIC 225 case 1: /* An indirect block. */ 226 panic("lfs_truncate: ufs_bmaparray returned depth 1"); 227 /* NOTREACHED */ 228 #endif 229 default: /* Chain of indirect blocks. */ 230 inp = a + --depth; 231 if (inp->in_off > 0 && lbn != lastblock) { 232 lbn -= inp->in_off < lbn - lastblock ? 233 inp->in_off : lbn - lastblock; 234 break; 235 } 236 for (; depth && (inp->in_off == 0 || lbn == lastblock); 237 --inp, --depth) { 238 if (bread(vp, 239 inp->in_lbn, fs->lfs_bsize, NOCRED, &bp)) 240 panic("lfs_truncate: bread bno %d", 241 inp->in_lbn); 242 daddrp = (ufs_daddr_t *)bp->b_data + 243 inp->in_off; 244 for (i = inp->in_off; 245 i++ <= a_end[depth].in_off;) { 246 daddr = *daddrp++; 247 SEGDEC(freesize); 248 } 249 a_end[depth].in_off = NINDIR(fs) - 1; 250 if (inp->in_off == 0) 251 brelse (bp); 252 else { 253 bzero((ufs_daddr_t *)bp->b_data + 254 inp->in_off, fs->lfs_bsize - 255 inp->in_off * sizeof(ufs_daddr_t)); 256 if (e1 = VOP_BWRITE(bp)) 257 return (e1); 258 } 259 } 260 if (depth == 0 && a[1].in_off == 0) { 261 off = a[0].in_off; 262 daddr = ip->i_ib[off]; 263 SEGDEC(freesize); 264 ip->i_ib[off] = 0; 265 } 266 if (lbn == lastblock || lbn <= NDADDR) 267 --lbn; 268 else { 269 lbn -= NINDIR(fs); 270 if (lbn < lastblock) 271 lbn = lastblock; 272 } 273 } 274 } 275 UPDATE_SEGUSE; 276 277 /* If truncating the file to 0, update the version number. */ 278 if (length == 0) { 279 LFS_IENTRY(ifp, fs, ip->i_number, bp); 280 ++ifp->if_version; 281 (void) VOP_BWRITE(bp); 282 } 283 284 #ifdef DIAGNOSTIC 285 if (ip->i_blocks < fragstodb(fs, fragsreleased)) { 286 printf("lfs_truncate: frag count < 0\n"); 287 fragsreleased = dbtofrags(fs, ip->i_blocks); 288 panic("lfs_truncate: frag count < 0\n"); 289 } 290 #endif 291 ip->i_blocks -= fragstodb(fs, fragsreleased); 292 fs->lfs_bfree += fragstodb(fs, fragsreleased); 293 ip->i_flag |= IN_CHANGE | IN_UPDATE; 294 /* 295 * Traverse dirty block list counting number of dirty buffers 296 * that are being deleted out of the cache, so that the lfs_avail 297 * field can be updated. 298 */ 299 a_released = 0; 300 i_released = 0; 301 for (bp = vp->v_dirtyblkhd.lh_first; bp; bp = bp->b_vnbufs.le_next) 302 if (bp->b_flags & B_LOCKED) { 303 a_released += numfrags(fs, bp->b_bcount); 304 /* 305 * XXX 306 * When buffers are created in the cache, their block 307 * number is set equal to their logical block number. 308 * If that is still true, we are assuming that the 309 * blocks are new (not yet on disk) and weren't 310 * counted above. However, there is a slight chance 311 * that a block's disk address is equal to its logical 312 * block number in which case, we'll get an overcounting 313 * here. 314 */ 315 if (bp->b_blkno == bp->b_lblkno) 316 i_released += numfrags(fs, bp->b_bcount); 317 } 318 fragsreleased = i_released; 319 #ifdef DIAGNOSTIC 320 if (fragsreleased > dbtofrags(fs, ip->i_blocks)) { 321 printf("lfs_inode: Warning! %s\n", 322 "more frags released from inode than are in inode"); 323 fragsreleased = dbtofrags(fs, ip->i_blocks); 324 panic("lfs_inode: Warning. More frags released\n"); 325 } 326 #endif 327 fs->lfs_bfree += fragstodb(fs, fragsreleased); 328 ip->i_blocks -= fragstodb(fs, fragsreleased); 329 #ifdef DIAGNOSTIC 330 if (length == 0 && ip->i_blocks != 0) { 331 printf("lfs_inode: Warning! %s%d%s\n", 332 "Truncation to zero, but ", ip->i_blocks, 333 " blocks left on inode"); 334 panic("lfs_inode"); 335 } 336 #endif 337 fs->lfs_avail += fragstodb(fs, a_released); 338 e1 = vinvalbuf(vp, (length > 0) ? V_SAVE : 0, ap->a_cred, ap->a_p, 339 0, 0); 340 e2 = VOP_UPDATE(vp, &tv, &tv, 0); 341 return (e1 ? e1 : e2 ? e2 : 0); 342 } 343