summaryrefslogtreecommitdiff
path: root/ufs-fsck
diff options
context:
space:
mode:
Diffstat (limited to 'ufs-fsck')
-rw-r--r--ufs-fsck/pass1.c367
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);
+ }
+ }
+}
+
+