diff options
Diffstat (limited to 'ufs-fsck')
-rw-r--r-- | ufs-fsck/pass1.c | 367 |
1 files changed, 352 insertions, 15 deletions
diff --git a/ufs-fsck/pass1.c b/ufs-fsck/pass1.c index 650af963..a59657fc 100644 --- a/ufs-fsck/pass1.c +++ b/ufs-fsck/pass1.c @@ -1,4 +1,4 @@ -/* Pass one of GNU fsck +/* Pass one of GNU fsck -- count blocks and verify inodes Copyright (C) 1994 Free Software Foundation, Inc. Written by Michael I. Bushnell. @@ -25,12 +25,108 @@ /* Find all the blocks in use by files and filesystem reserved blocks. Set them in the global block map. For each file, if a block is found allocated twice, then record the block and inode in DUPLIST. - Set ISTATE to be USTATE, FSTATE, or DSTATE as appropriate */ + Initialize INODESTATE, LINKCOUNT, and TYPEMAP. */ pass1 () { - ino_t number; /* inode number */ - ino_t i; /* cg-relative inode number */ - int cg; /* cylinder group number */ + ino_t number; + ino_t i; + int cg; + struct dinode dino; + struct dinode *dp = &dino; + mode_t mode, type; + int ndb; + int holdallblocks; + int lbn; + int nblocks; + int blkerror; + int nblkrngerrors; + int nblkduperrors; + + /* This function is called for each block of DP. Check to see + if the block number is valid. If so, set the entry in the + block map. If the block map entry is already set, then keep + track of this block and see if the user wants to clear the + node. Increment NBLOCKS by the number of data blocks held. + Set BLKERROR if this block is invalid. + Return RET_GOOD, RET_BAD, RET_STOP if the block is good, + bad, or if we should entirely stop checking blocks in this + inode. */ + int + checkblock (daddr_t bno, int nfrags) + { +#define MAXBAD 10 + int outofrange; + struct dups *dlp, *new; + int wasbad = 0; + + /* Check to see if this block is in range */ + if (outofrange = check_range (bno, nfrags)) + { + blkerror = 1; + wasbad = 1; + if (nblkrngerrors == 0) + printf ("I=%lu HAS BAD BLOCKS\n"); + if (nblkrngerrors++ > MAXBAD) + { + pwarn ("EXCESSIVE BAD BLKS I=%lu", number); + if (preen || reply ("SKIP")) + { + if (preen) + printf (" (SKIPPING)\n"); + return RET_STOP; + } + } + } + + for (; nfrags > 0; bno++, nfrags--) + { + if (outofrange && check_range (bno, 1)) + printf ("BAD BLOCK %d\n", bno); + else + { + if (!testbmap (bno)) + setbmap (bno); + else + { + blkerror = 1; + if (nblkduperrors == 0) + printf ("I=%lu HAS DUPLICATE BLOCKS\n"); + printf ("DUPLICATE BLOCK %d\n", bno); + wasbad = 1; + if (nblkduperrors++ > MAXBAD) + { + pwarn ("EXCESSIVE DUP BLKS I=%lu", number); + if (preen || reply ("SKIP")) + { + if (preen) + printf (" (SKIPPING)\n"); + return RET_STOP; + } + } + new = malloc (sizeof (struct dups)); + new->dup = blkno; + if (muldup == 0) + { + duplist = muldup = new; + new->next = 0; + } + else + { + new->next = muldup->next; + muldup->next = new; + } + for (dlp = duplist; dlp != muldup; dlp = dlp->next) + if (dlp->dup == blkno) + break; + if (dlp == muldup && dlp->dup != blkno) + muldup = new; + } + } + nblocks += btodb (sblock->fs_fsize); + } + return wasbad ? RET_BAD : RET_GOOD; + } + /* Account for blocks used by meta data */ for (cg = 0, cg < sblock.fs_ncg; cg++) @@ -55,22 +151,23 @@ pass1 () /* Mark the blocks set */ for (bno = firstcgblock; bno < firstdata; bno++) - set_block_used (bno): + setbmap (bno): } /* Loop through each inode, doing initial checks */ for (number = 0, cg = 0; cg < sblock.fs_ncg; cg++) for (i = 0; i < sblock.fs_ipg; i++, number++) { - struct dinode *dp; - mode_t mode; - - dp = getinode (number); - mode = DI_MODE (dp) & IFMT; + if (number < ROOTINO) + continue; + + getinode (number, dp); + mode = DI_MODE (dp); + type = mode & IFMT; /* If the node is not allocated, then make sure it's properly clear */ - if (mode == 0) + if (type == 0) { if (bcmp (dp->di_db, zino.di_db, NDADDR * sizeof (daddr_t)) || bcmp (dp->di_ib, zino->di_ix, NIADDR * sizeof (daddr_t)) @@ -83,13 +180,253 @@ pass1 () { if (preen) printf (" (CLEARED)\n"); - clear_inode (dp); + clear_inode (number, dp); } - + } + inodestate[number] = UNALLOC; + } + else + { + /* Node is allocated. */ + + /* Check to see if we think the node should be cleared */ + + /* Verify size for basic validity */ + holdallblocks = 0; + + if (dp->di_size + sblock.fs_bsize - 1 < dp->di_size) + { + pfatal ("OVERFLOW IN FILE SIZE I=%lu (SIZE == %lu)", inumber, + dp->di_size); + if (reply ("CLEAR")) + { + clear_inode (number, dp); + inodestate[number] = UNALLOC; + continue; + } + inodestate[number] = UNALLOC; + printf ("WILL TREAT ANY BLOCKS HELD BY I=%lu AS ALLOCATED\n"); + holdallblocks = 1; + } + + /* Decode type and set NDB + also set inodestate correctly. */ + inodestate[number] = FILE; + switch (type) + { + case IFBLK: + case IFCHR: + ndb = 1; + break; + case IFIFO: + ndb = 0; + break; + case IFLNK: + if (sblock.fs_maxsymlinklen != -1) + { + /* Check to see if this is a fastlink. The + old fast link format has fs_maxsymlinklen + of zero and di_blocks zero; the new format has + fs_maxsymlinklen set and we ignore di_blocks. + So check for either. */ + if ((sblock.fs_maxsymlinklen + && dp->di_size < sblock.fs_maxsymlinklen) + || (!sblock.fs_maxsymlinklen && !dp->di_blocks)) + { + /* Fake NDB value so that we will check + all the block pointers past the symlink */ + ndb = howmany (dp->di_size, sizeof (daddr_t)); + if (ndb > NDADDR) + { + int j = ndb - NDADDR; + for (ndb = 1; j > 1; i--) + ndb *= NINDIR (&sblock); + ndb += NDADDR; + } + } + else + ndb = howmany (dp->di_size, sblock.fs_bsize); + } + else + ndb = howmany (dp->di_size, sblock.fs_bsize); + break; + case IFDIR: + inodestate[number] = DIR; + /* Fall through */ + case IFREG: + ndb = howmany (dp->di_size, sblock.fs_bsize); + break; + + default: + pfatal ("UNKNOWN FILE TYPE I=%lu (MODE=%lo)\n", + number, mode); + if (reply ("CLEAR")) + { + clear_inode (number, dp); + inodestate[number] = UNALLOC; + continue; + } + inodestate[number] = UNALLOC; + holdallblocks = 1; + printf ("WILL TREAT ANY BLOCKS HELD BY I=%lu " + "AS ALLOCATED\n", number); + } + if (ndb < 0) + { + pfatal ("BAD FILE SIZE I= %lu (SIZE == %lu)", inumber, + dp->di_size); + if (reply ("CLEAR")) + { + clear_inode (number, dp); + inodestate[number] = UNALLOC; + continue; + } + inodestate[number] = UNALLOC; + printf ("WILL TREAT ANY BLOCKS HELD BY I=%lu AS ALLOCATED\n", + number); + holdallblocks = 1; + } + /* Make sure that direct and indirect block pointers + past the file size are zero. If the size is bogus, then + don't bother (they should all be zero, but the user has + requested that they be treated as allocated). */ + if (!holdallblocks) + { + if (dp->di_size + && (type == IFBLK || type == IFCHR + || type == IFSOCK || type == IFIFO)) + { + pfatal ("SPECIAL NODE I=%du (MODE=%ol) HAS SIZE %lu\n", + number, mode, dp->di_size); + if (reply ("TRUNCATE")) + { + dp->di_size = 0; + write_inode (number, dp); + } + } + + /* If we haven't set NDB speciall above, then it is set from + the file size correctly by the size check. */ + + /* Check all the direct and indirect blocks that are past the + amount necessary to be zero. */ + for (lbn = ndb; lbn < NDADDR; lbn++) + { + int dbwarn = 0; + if (dp->di_db[lbn]) + { + if (!dbwarn) + { + dbwarn = 1; + pwarn ("INODE I=%lu HAS EXTRA DIRECT BLOCKS", + number); + if (preen || reply ("DEALLOCATE")) + { + if (preen) + printf (" (DEALLOCATED)\n"); + dp->di_db[lbn] = 0; + dbwarn = 2; + } + } + else if (dbwarn == 2) + dp->di_db[lbn] = 0; + } + if (dbwarn == 2) + write_inode (number, dp); + } - + for (lbn = 0, ndb -= NDADDR; ndb > 0; lbn++) + ndb /= NINDIR (&sblock); + for (; lbn < NIADDR; lbn++) + { + ind ibwarn = 0; + if (dp->di_ib[lbn]) + { + if (ibwarn) + { + ibwarn = 1; + pwarn ("INODE I=%lu HAS EXTRA INDIRECT BLOCKS", + number); + if (preen || reply ("DEALLOCATE")) + { + if (preen) + printf (" (DEALLOCATED)\n"); + dp->di_ib[lbn] = 0; + ibwarn = 2; + } + } + else if (ibwarn == 2) + dp->di_ib[lbn] = 0; + } + if (ibwarn == 2) + write_inode (number, dp); + } + } + + /* If this node is really allocated (as opposed to something + that we should clear but the user won't) then set LINKCOUNT + and TYPEMAP entries. */ + if (inodestate[number] != UNALLOC) + { + linkcount[number] = dp->di_nlink; + typemap[number] = IFTODT (mode); + } + + /* Iterate over the blocks of the file, + calling CHECKBLOCK for each file. */ + nblocks = 0; + blkerror = 0; + nblkduperrors = 0; + nblkrngerrors = 0; + allblock_iterate (dp, checkblock); + + if (blkerror) + { + if (preen) + pfatal ("DUPLICATE or BAD BLOCKS"); + else + { + printf ("I=%ld has "); + if (nblkduperrors) + { + printf ("%d DUPLICATE BLOCKS", nblkduperrors); + if (nblkrngerrors) + printf (" and "); + } + if (nblkrngerrors) + printf ("%d BAD BLOCKS", nblkrngerrors); + printf ("\n"); + if (reply ("CLEAR")) + { + clear_inode (number, dp); + inodestate[number] = UNALLOC; + } + else if (inodestate[number] == DIR) + inodestate[number] = BADDIR; + } + } + else if (dp->di_blocks != nblocks) + { + pwarn ("INCORRECT BLOCK COUNT I=%lu (%ld should be %ld)", + number, di->di_blocks, nblocks); + if (preen || reply ("CORRECT")) + { + if (preen) + printf (" (CORRECTED)"); + dp->di_blocks = nblocks; + write_inode (number, dp); + } + } + + if (type == IFDIR) + record_directory (dp, number); + } + } +} + + |