/* returns nonzero if inode modified */ static int check_inode_blocks(uint32_t ino, struct sfs_inode *sfi, int isdir) { uint32_t size, block, nblocks, badcount; badcount = 0; size = SFS_ROUNDUP(sfi->sfi_size, SFS_BLOCKSIZE); nblocks = size/SFS_BLOCKSIZE; for (block=0; block<SFS_NDIRECT; block++) { if (block < nblocks) { if (sfi->sfi_direct[block] != 0) { bitmap_mark(sfi->sfi_direct[block], isdir ? B_DIRDATA : B_DATA, ino); } } else { if (sfi->sfi_direct[block] != 0) { badcount++; bitmap_mark(sfi->sfi_direct[block], B_TOFREE, 0); } } } #ifdef SFS_NIDIRECT for (i=0; i<SFS_NIDIRECT; i++) { check_indirect_block(ino, &sfi->sfi_indirect[i], &block, nblocks, &badcount, isdir, 1); } #else check_indirect_block(ino, &sfi->sfi_indirect, &block, nblocks, &badcount, isdir, 1); #endif #ifdef SFS_NDIDIRECT for (i=0; i<SFS_NDIDIRECT; i++) { check_indirect_block(ino, &sfi->sfi_dindirect[i], &block, nblocks, &badcount, isdir, 2); } #else #ifdef HAS_DIDIRECT check_indirect_block(ino, &sfi->sfi_dindirect, &block, nblocks, &badcount, isdir, 2); #endif #endif #ifdef SFS_NTIDIRECT for (i=0; i<SFS_NTIDIRECT; i++) { check_indirect_block(ino, &sfi->sfi_tindirect[i], &block, nblocks, &badcount, isdir, 3); } #else #ifdef HAS_TIDIRECT check_indirect_block(ino, &sfi->sfi_tindirect, &block, nblocks, &badcount, isdir, 3); #endif #endif if (badcount > 0) { warnx("Inode %lu: %lu blocks after EOF (freed)", (unsigned long) ino, (unsigned long) badcount); setbadness(EXIT_RECOV); return 1; } return 0; }
/* * Traverse an indirect block, recording blocks that are in use, * dropping any entries that are past EOF, and clearing any entries * that point outside the volume. * * XXX: this should be extended to be able to recover from crosslinked * blocks. Currently it just complains in bitmap.c and sets * EXIT_UNRECOV. * * The traversal is recursive; the state is maintained in IBS (as * described above). IENTRY is a pointer to the entry in the parent * indirect block (or the inode) that names the block we're currently * scanning. IECHANGEDP should be set to 1 if *IENTRY is changed. * INDIRECTION is the indirection level of this block (1, 2, or 3). */ static void check_indirect_block(struct ibstate *ibs, uint32_t *ientry, int *iechangedp, int indirection) { uint32_t entries[SFS_DBPERIDB]; uint32_t i, ct; uint32_t coveredblocks; int localchanged = 0; int j; if (*ientry > 0 && *ientry < ibs->volblocks) { sfs_readindirect(*ientry, entries); bitmap_blockinuse(*ientry, B_IBLOCK, ibs->ino); } else { if (*ientry >= ibs->volblocks) { warnx("Inode %lu: indirect block pointer (level %d) " "for block %lu outside of volume " "(cleared)\n", (unsigned long)ibs->ino, indirection, (unsigned long)ibs->curfileblock); *ientry = 0; *iechangedp = 1; } coveredblocks = 1; for (j=0; j<indirection; j++) { coveredblocks *= SFS_DBPERIDB; } ibs->curfileblock += coveredblocks; return; } if (indirection > 1) { for (i=0; i<SFS_DBPERIDB; i++) { check_indirect_block(ibs, &entries[i], &localchanged, indirection-1); } } else { assert(indirection==1); for (i=0; i<SFS_DBPERIDB; i++) { if (entries[i] >= ibs->volblocks) { warnx("Inode %lu: direct block pointer for " "block %lu outside of volume " "(cleared)\n", (unsigned long)ibs->ino, (unsigned long)ibs->curfileblock); entries[i] = 0; localchanged = 1; } else if (entries[i] != 0) { if (ibs->curfileblock < ibs->fileblocks) { bitmap_blockinuse(entries[i], ibs->usagetype, ibs->ino); } else { ibs->pasteofcount++; bitmap_blockfree(entries[i]); entries[i] = 0; localchanged = 1; } } ibs->curfileblock++; } } ct=0; for (i=ct=0; i<SFS_DBPERIDB; i++) { if (entries[i]!=0) ct++; } if (ct==0) { if (*ientry != 0) { /* this is not necessarily correct */ /*ibs->pasteofcount++;*/ *iechangedp = 1; bitmap_blockfree(*ientry); *ientry = 0; } } else { assert(*ientry != 0); if (localchanged) { sfs_writeindirect(*ientry, entries); } } }
static void check_indirect_block(uint32_t ino, uint32_t *ientry, uint32_t *blockp, uint32_t nblocks, uint32_t *badcountp, int isdir, int indirection) { uint32_t entries[SFS_DBPERIDB]; uint32_t i, ct; if (*ientry !=0) { diskread(entries, *ientry); swapindir(entries); bitmap_mark(*ientry, B_IBLOCK, ino); } else { for (i=0; i<SFS_DBPERIDB; i++) { entries[i] = 0; } } if (indirection > 1) { for (i=0; i<SFS_DBPERIDB; i++) { check_indirect_block(ino, &entries[i], blockp, nblocks, badcountp, isdir, indirection-1); } } else { assert(indirection==1); for (i=0; i<SFS_DBPERIDB; i++) { if (*blockp < nblocks) { if (entries[i] != 0) { bitmap_mark(entries[i], isdir ? B_DIRDATA : B_DATA, ino); } } else { if (entries[i] != 0) { (*badcountp)++; bitmap_mark(entries[i], isdir ? B_DIRDATA : B_DATA, ino); entries[i] = 0; } } (*blockp)++; } } ct=0; for (i=ct=0; i<SFS_DBPERIDB; i++) { if (entries[i]!=0) ct++; } if (ct==0) { if (*ientry != 0) { (*badcountp)++; bitmap_mark(*ientry, B_TOFREE, 0); *ientry = 0; } } else { assert(*ientry != 0); if (*badcountp > 0) { swapindir(entries); diskwrite(entries, *ientry); } } }
/* * Check the blocks belonging to inode INO, whose inode has already * been loaded into SFI. ISDIR is a shortcut telling us if the inode * is a directory. * * Returns nonzero if SFI has been modified and needs to be written * back. */ static int check_inode_blocks(uint32_t ino, struct sfs_dinode *sfi, int isdir) { struct ibstate ibs; uint32_t size, datablock; int changed; int i; size = SFS_ROUNDUP(sfi->sfi_size, SFS_BLOCKSIZE); ibs.ino = ino; /*ibs.curfileblock = 0;*/ ibs.fileblocks = size/SFS_BLOCKSIZE; ibs.volblocks = sb_totalblocks(); ibs.pasteofcount = 0; ibs.usagetype = isdir ? B_DIRDATA : B_DATA; changed = 0; for (ibs.curfileblock=0; ibs.curfileblock<NUM_D; ibs.curfileblock++) { datablock = GET_D(sfi, ibs.curfileblock); if (datablock >= ibs.volblocks) { warnx("Inode %lu: direct block pointer for " "block %lu outside of volume " "(cleared)\n", (unsigned long)ibs.ino, (unsigned long)ibs.curfileblock); SET_D(sfi, ibs.curfileblock) = 0; changed = 1; } else if (datablock > 0) { if (ibs.curfileblock < ibs.fileblocks) { bitmap_blockinuse(datablock, ibs.usagetype, ibs.ino); } else { ibs.pasteofcount++; changed = 1; bitmap_blockfree(datablock); SET_D(sfi, ibs.curfileblock) = 0; } } } for (i=0; i<NUM_I; i++) { check_indirect_block(&ibs, &SET_I(sfi, i), &changed, 1); } for (i=0; i<NUM_II; i++) { check_indirect_block(&ibs, &SET_II(sfi, i), &changed, 2); } for (i=0; i<NUM_III; i++) { check_indirect_block(&ibs, &SET_III(sfi, i), &changed, 3); } if (ibs.pasteofcount > 0) { warnx("Inode %lu: %u blocks after EOF (freed)", (unsigned long) ibs.ino, ibs.pasteofcount); setbadness(EXIT_RECOV); } return changed; }