Update the actions previously attempted by the -D option to make them

robust. With these changes fsck is now able to detect and reliably
rebuild corrupted cylinder group maps. The -D option is no longer
necessary as it has been replaced by a prompt asking whether the
corrupted cylinder group should be rebuilt and doing so when requested.
These actions are only offered and taken when running fsck in manual
mode. Corrupted cylinder groups found during preen mode cause the fsck
to fail.

Add the -r option to free up excess unused inodes. Decreasing the
number of preallocated inodes reduces the running time of future
runs of fsck and frees up space that can allocated to files. The -r
option is ignored when running in preen mode.

Reviewed by: Xin LI <delphij@>
Sponsored by: Rsync.net
This commit is contained in:
Kirk McKusick 2009-02-04 01:02:56 +00:00
parent c3f10abd7e
commit 910b491e7e
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=188110
6 changed files with 205 additions and 76 deletions

View File

@ -270,7 +270,7 @@ char yflag; /* assume a yes response */
int bkgrdflag; /* use a snapshot to run on an active system */ int bkgrdflag; /* use a snapshot to run on an active system */
int bflag; /* location of alternate super block */ int bflag; /* location of alternate super block */
int debug; /* output debugging info */ int debug; /* output debugging info */
char damagedflag; /* run in damaged mode */ int inoopt; /* trim out unused inodes */
char ckclean; /* only do work if not cleanly unmounted */ char ckclean; /* only do work if not cleanly unmounted */
int cvtlevel; /* convert to newer file system format */ int cvtlevel; /* convert to newer file system format */
int bkgrdcheck; /* determine if background check is possible */ int bkgrdcheck; /* determine if background check is possible */
@ -337,7 +337,7 @@ void cacheino(union dinode *dp, ino_t inumber);
void catch(int); void catch(int);
void catchquit(int); void catchquit(int);
int changeino(ino_t dir, const char *name, ino_t newnum); int changeino(ino_t dir, const char *name, ino_t newnum);
void check_cgmagic(int cg, struct cg *cgp); int check_cgmagic(int cg, struct cg *cgp);
int chkrange(ufs2_daddr_t blk, int cnt); int chkrange(ufs2_daddr_t blk, int cnt);
void ckfini(int markclean); void ckfini(int markclean);
int ckinode(union dinode *dp, struct inodesc *); int ckinode(union dinode *dp, struct inodesc *);
@ -362,7 +362,7 @@ int ftypeok(union dinode *dp);
void getblk(struct bufarea *bp, ufs2_daddr_t blk, long size); void getblk(struct bufarea *bp, ufs2_daddr_t blk, long size);
struct bufarea *getdatablk(ufs2_daddr_t blkno, long size); struct bufarea *getdatablk(ufs2_daddr_t blkno, long size);
struct inoinfo *getinoinfo(ino_t inumber); struct inoinfo *getinoinfo(ino_t inumber);
union dinode *getnextinode(ino_t inumber); union dinode *getnextinode(ino_t inumber, int rebuildcg);
void getpathname(char *namebuf, ino_t curdir, ino_t ino); void getpathname(char *namebuf, ino_t curdir, ino_t ino);
union dinode *ginode(ino_t inumber); union dinode *ginode(ino_t inumber);
void infohandler(int sig); void infohandler(int sig);

View File

@ -38,7 +38,7 @@
.Nd file system consistency check and interactive repair .Nd file system consistency check and interactive repair
.Sh SYNOPSIS .Sh SYNOPSIS
.Nm .Nm
.Op Fl BDFpfny .Op Fl BFprfny
.Op Fl b Ar block .Op Fl b Ar block
.Op Fl c Ar level .Op Fl c Ar level
.Op Fl m Ar mode .Op Fl m Ar mode
@ -216,22 +216,6 @@ are being converted at once.
The format of a file system can be determined from the The format of a file system can be determined from the
first line of output from first line of output from
.Xr dumpfs 8 . .Xr dumpfs 8 .
.It Fl D
Run
.Nm
in 'damaged recovery' mode, which will enable certain aggressive
operations that can make
.Nm
to survive with file systems that has very serious data damage, which
is an useful last resort when on disk data damage is very serious
and causes
.Nm
to crash otherwise. Be
.Em very careful
using this flag, it is dangerous if there are data transmission hazards
because a false positive cylinder group magic number mismatch could
cause
.Em irrevertible data loss!
.Pp .Pp
This option implies the This option implies the
.Fl f .Fl f
@ -259,6 +243,15 @@ which is assumed to be affirmative;
do not open the file system for writing. do not open the file system for writing.
.It Fl p .It Fl p
Preen file systems (see above). Preen file systems (see above).
.It Fl r
Free up excess unused inodes.
Decreasing the number of preallocated inodes reduces the
running time of future runs of
.Nm
and frees up space that can allocated to files.
The
.Fl r
option is ignored when running in preen mode.
.It Fl y .It Fl y
Assume a yes response to all questions asked by Assume a yes response to all questions asked by
.Nm ; .Nm ;

View File

@ -333,9 +333,13 @@ ckfini(int markclean)
if (!markclean) if (!markclean)
rerun = 1; rerun = 1;
} }
} else if (!preen && !markclean) { } else if (!preen) {
printf("\n***** FILE SYSTEM STILL DIRTY *****\n"); if (markclean) {
rerun = 1; printf("\n***** FILE SYSTEM IS CLEAN *****\n");
} else {
printf("\n***** FILE SYSTEM STILL DIRTY *****\n");
rerun = 1;
}
} }
if (debug && totalreads > 0) if (debug && totalreads > 0)
printf("cache missed %ld of %ld (%d%%)\n", diskreads, printf("cache missed %ld of %ld (%d%%)\n", diskreads,
@ -418,32 +422,73 @@ blwrite(int fd, char *buf, ufs2_daddr_t blk, long size)
} }
/* /*
* Check cg's magic number. If catastrophic mode is enabled and the cg's * Verify cylinder group's magic number and other parameters. If the
* magic number is bad, offer an option to clear the whole cg. * test fails, offer an option to rebuild the whole cylinder group.
*/ */
void int
check_cgmagic(int cg, struct cg *cgp) check_cgmagic(int cg, struct cg *cgp)
{ {
if (!cg_chkmagic(cgp)) { /*
pwarn("CG %d: BAD MAGIC NUMBER\n", cg); * Extended cylinder group checks.
if (damagedflag) { */
if (reply("CLEAR CG")) { if (cg_chkmagic(cgp) &&
memset(cgp, 0, (size_t)sblock.fs_cgsize); ((sblock.fs_magic == FS_UFS1_MAGIC &&
cgp->cg_initediblk = sblock.fs_ipg; cgp->cg_old_niblk == sblock.fs_ipg &&
cgp->cg_old_niblk = sblock.fs_ipg; cgp->cg_ndblk <= sblock.fs_fpg &&
cgp->cg_old_ncyl = sblock.fs_old_cpg; cgp->cg_old_ncyl == sblock.fs_old_cpg) ||
cgp->cg_cgx = cg; (sblock.fs_magic == FS_UFS2_MAGIC &&
cgp->cg_niblk = sblock.fs_ipg; cgp->cg_niblk == sblock.fs_ipg &&
cgp->cg_ndblk = sblock.fs_size - cgbase(&sblock, cg); cgp->cg_ndblk <= sblock.fs_fpg &&
cgp->cg_magic = CG_MAGIC; cgp->cg_initediblk <= sblock.fs_ipg))) {
cgdirty(); return (1);
printf("PLEASE RERUN FSCK.\n");
rerun = 1;
}
} else
printf("YOU MAY NEED TO RERUN FSCK WITH -D IF IT CRASHED.\n");
} }
pfatal("CYLINDER GROUP %d: BAD MAGIC NUMBER", cg);
if (!reply("REBUILD CYLINDER GROUP")) {
printf("YOU WILL NEED TO RERUN FSCK.\n");
rerun = 1;
return (1);
}
/*
* Zero out the cylinder group and then initialize critical fields.
* Bit maps and summaries will be recalculated by later passes.
*/
memset(cgp, 0, (size_t)sblock.fs_cgsize);
cgp->cg_magic = CG_MAGIC;
cgp->cg_cgx = cg;
cgp->cg_niblk = sblock.fs_ipg;
cgp->cg_initediblk = sblock.fs_ipg < 2 * INOPB(&sblock) ?
sblock.fs_ipg : 2 * INOPB(&sblock);
if (cgbase(&sblock, cg) + sblock.fs_fpg < sblock.fs_size)
cgp->cg_ndblk = sblock.fs_fpg;
else
cgp->cg_ndblk = sblock.fs_size - cgbase(&sblock, cg);
cgp->cg_iusedoff = &cgp->cg_space[0] - (u_char *)(&cgp->cg_firstfield);
if (sblock.fs_magic == FS_UFS1_MAGIC) {
cgp->cg_niblk = 0;
cgp->cg_initediblk = 0;
cgp->cg_old_ncyl = sblock.fs_old_cpg;
cgp->cg_old_niblk = sblock.fs_ipg;
cgp->cg_old_btotoff = cgp->cg_iusedoff;
cgp->cg_old_boff = cgp->cg_old_btotoff +
sblock.fs_old_cpg * sizeof(int32_t);
cgp->cg_iusedoff = cgp->cg_old_boff +
sblock.fs_old_cpg * sizeof(u_int16_t);
}
cgp->cg_freeoff = cgp->cg_iusedoff + howmany(sblock.fs_ipg, CHAR_BIT);
cgp->cg_nextfreeoff = cgp->cg_freeoff + howmany(sblock.fs_fpg,CHAR_BIT);
if (sblock.fs_contigsumsize > 0) {
cgp->cg_nclusterblks = cgp->cg_ndblk / sblock.fs_frag;
cgp->cg_clustersumoff =
roundup(cgp->cg_nextfreeoff, sizeof(u_int32_t));
cgp->cg_clustersumoff -= sizeof(u_int32_t);
cgp->cg_clusteroff = cgp->cg_clustersumoff +
(sblock.fs_contigsumsize + 1) * sizeof(u_int32_t);
cgp->cg_nextfreeoff = cgp->cg_clusteroff +
howmany(fragstoblks(&sblock, sblock.fs_fpg), CHAR_BIT);
}
cgdirty();
return (0);
} }
/* /*
@ -470,7 +515,8 @@ allocblk(long frags)
} }
cg = dtog(&sblock, i + j); cg = dtog(&sblock, i + j);
getblk(&cgblk, cgtod(&sblock, cg), sblock.fs_cgsize); getblk(&cgblk, cgtod(&sblock, cg), sblock.fs_cgsize);
check_cgmagic(cg, cgp); if (!check_cgmagic(cg, cgp))
return (0);
baseblk = dtogd(&sblock, i + j); baseblk = dtogd(&sblock, i + j);
for (k = 0; k < frags; k++) { for (k = 0; k < frags; k++) {
setbmap(i + j + k); setbmap(i + j + k);

View File

@ -309,10 +309,12 @@ static long readcnt, readpercg, fullcnt, inobufsize, partialcnt, partialsize;
static caddr_t inodebuf; static caddr_t inodebuf;
union dinode * union dinode *
getnextinode(ino_t inumber) getnextinode(ino_t inumber, int rebuildcg)
{ {
int j;
long size; long size;
ufs2_daddr_t dblk; mode_t mode;
ufs2_daddr_t ndb, dblk;
union dinode *dp; union dinode *dp;
static caddr_t nextinop; static caddr_t nextinop;
@ -336,6 +338,54 @@ getnextinode(ino_t inumber)
nextinop = inodebuf; nextinop = inodebuf;
} }
dp = (union dinode *)nextinop; dp = (union dinode *)nextinop;
if (rebuildcg && nextinop == inodebuf) {
/*
* Try to determine if we have reached the end of the
* allocated inodes.
*/
mode = DIP(dp, di_mode) & IFMT;
if (mode == 0) {
if (memcmp(dp->dp2.di_db, ufs2_zino.di_db,
NDADDR * sizeof(ufs2_daddr_t)) ||
memcmp(dp->dp2.di_ib, ufs2_zino.di_ib,
NIADDR * sizeof(ufs2_daddr_t)) ||
dp->dp2.di_mode || dp->dp2.di_size)
return (NULL);
goto inodegood;
}
if (!ftypeok(dp))
return (NULL);
ndb = howmany(DIP(dp, di_size), sblock.fs_bsize);
if (ndb < 0)
return (NULL);
if (mode == IFBLK || mode == IFCHR)
ndb++;
if (mode == IFLNK) {
/*
* Fake ndb value so direct/indirect block checks below
* will detect any garbage after symlink string.
*/
if (DIP(dp, di_size) < (off_t)sblock.fs_maxsymlinklen) {
ndb = howmany(DIP(dp, di_size),
sizeof(ufs2_daddr_t));
if (ndb > NDADDR) {
j = ndb - NDADDR;
for (ndb = 1; j > 1; j--)
ndb *= NINDIR(&sblock);
ndb += NDADDR;
}
}
}
for (j = ndb; ndb < NDADDR && j < NDADDR; j++)
if (DIP(dp, di_db[j]) != 0)
return (NULL);
for (j = 0, ndb -= NDADDR; ndb > 0; j++)
ndb /= NINDIR(&sblock);
for (; j < NIADDR; j++)
if (DIP(dp, di_ib[j]) != 0)
return (NULL);
}
inodegood:
if (sblock.fs_magic == FS_UFS1_MAGIC) if (sblock.fs_magic == FS_UFS1_MAGIC)
nextinop += sizeof(struct ufs1_dinode); nextinop += sizeof(struct ufs1_dinode);
else else
@ -617,7 +667,8 @@ allocino(ino_t request, int type)
return (0); return (0);
cg = ino_to_cg(&sblock, ino); cg = ino_to_cg(&sblock, ino);
getblk(&cgblk, cgtod(&sblock, cg), sblock.fs_cgsize); getblk(&cgblk, cgtod(&sblock, cg), sblock.fs_cgsize);
check_cgmagic(cg, cgp); if (!check_cgmagic(cg, cgp))
return (0);
setbit(cg_inosused(cgp), ino % sblock.fs_ipg); setbit(cg_inosused(cgp), ino % sblock.fs_ipg);
cgp->cg_cs.cs_nifree--; cgp->cg_cs.cs_nifree--;
switch (type & IFMT) { switch (type & IFMT) {

View File

@ -81,8 +81,8 @@ main(int argc, char *argv[])
sync(); sync();
skipclean = 1; skipclean = 1;
damagedflag = 0; inoopt = 0;
while ((ch = getopt(argc, argv, "b:Bc:CdDfFm:npy")) != -1) { while ((ch = getopt(argc, argv, "b:Bc:CdfFm:npry")) != -1) {
switch (ch) { switch (ch) {
case 'b': case 'b':
skipclean = 0; skipclean = 0;
@ -106,10 +106,6 @@ main(int argc, char *argv[])
debug++; debug++;
break; break;
case 'D':
damagedflag = 1;
/* FALLTHROUGH */
case 'f': case 'f':
skipclean = 0; skipclean = 0;
break; break;
@ -138,6 +134,10 @@ main(int argc, char *argv[])
ckclean++; ckclean++;
break; break;
case 'r':
inoopt++;
break;
case 'y': case 'y':
yflag++; yflag++;
nflag = 0; nflag = 0;
@ -606,7 +606,7 @@ static void
usage(void) usage(void)
{ {
(void) fprintf(stderr, (void) fprintf(stderr,
"usage: %s [-BCFpfny] [-b block] [-c level] [-m mode] " "usage: %s [-BFprfny] [-b block] [-c level] [-m mode] "
"filesystem ...\n", "filesystem ...\n",
getprogname()); getprogname());
exit(1); exit(1);

View File

@ -54,17 +54,17 @@ static ufs2_daddr_t badblk;
static ufs2_daddr_t dupblk; static ufs2_daddr_t dupblk;
static ino_t lastino; /* last inode in use */ static ino_t lastino; /* last inode in use */
static void checkinode(ino_t inumber, struct inodesc *); static int checkinode(ino_t inumber, struct inodesc *, int rebuildcg);
void void
pass1(void) pass1(void)
{ {
struct inostat *info; struct inostat *info;
struct inodesc idesc; struct inodesc idesc;
ino_t inumber, inosused; ino_t inumber, inosused, mininos;
ufs2_daddr_t i, cgd; ufs2_daddr_t i, cgd;
u_int8_t *cp; u_int8_t *cp;
int c; int c, rebuildcg;
/* /*
* Set file system reserved blocks in used block map. * Set file system reserved blocks in used block map.
@ -93,7 +93,10 @@ pass1(void)
inumber = c * sblock.fs_ipg; inumber = c * sblock.fs_ipg;
setinodebuf(inumber); setinodebuf(inumber);
getblk(&cgblk, cgtod(&sblock, c), sblock.fs_cgsize); getblk(&cgblk, cgtod(&sblock, c), sblock.fs_cgsize);
if (sblock.fs_magic == FS_UFS2_MAGIC) { rebuildcg = 0;
if (!check_cgmagic(c, &cgrp))
rebuildcg = 1;
if (!rebuildcg && sblock.fs_magic == FS_UFS2_MAGIC) {
inosused = cgrp.cg_initediblk; inosused = cgrp.cg_initediblk;
if (inosused > sblock.fs_ipg) if (inosused > sblock.fs_ipg)
inosused = sblock.fs_ipg; inosused = sblock.fs_ipg;
@ -117,9 +120,7 @@ pass1(void)
* to find the inodes that are really in use, and then * to find the inodes that are really in use, and then
* read only those inodes in from disk. * read only those inodes in from disk.
*/ */
if (preen && usedsoftdep) { if ((preen || inoopt) && usedsoftdep && !rebuildcg) {
if (!cg_chkmagic(&cgrp))
pfatal("CG %d: BAD MAGIC NUMBER\n", c);
cp = &cg_inosused(&cgrp)[(inosused - 1) / CHAR_BIT]; cp = &cg_inosused(&cgrp)[(inosused - 1) / CHAR_BIT];
for ( ; inosused > 0; inosused -= CHAR_BIT, cp--) { for ( ; inosused > 0; inosused -= CHAR_BIT, cp--) {
if (*cp == 0) if (*cp == 0)
@ -152,24 +153,60 @@ pass1(void)
*/ */
for (i = 0; i < inosused; i++, inumber++) { for (i = 0; i < inosused; i++, inumber++) {
if (inumber < ROOTINO) { if (inumber < ROOTINO) {
(void)getnextinode(inumber); (void)getnextinode(inumber, rebuildcg);
continue; continue;
} }
checkinode(inumber, &idesc); /*
* NULL return indicates probable end of allocated
* inodes during cylinder group rebuild attempt.
* We always keep trying until we get to the minimum
* valid number for this cylinder group.
*/
if (checkinode(inumber, &idesc, rebuildcg) == 0 &&
i > cgrp.cg_initediblk)
break;
} }
lastino += 1; /*
if (inosused < sblock.fs_ipg || inumber == lastino) * This optimization speeds up future runs of fsck
* by trimming down the number of inodes in cylinder
* groups that formerly had many inodes but now have
* fewer in use.
*/
mininos = roundup(inosused + INOPB(&sblock), INOPB(&sblock));
if (inoopt && !preen && !rebuildcg &&
sblock.fs_magic == FS_UFS2_MAGIC &&
cgrp.cg_initediblk > 2 * INOPB(&sblock) &&
mininos < cgrp.cg_initediblk) {
i = cgrp.cg_initediblk;
if (mininos < 2 * INOPB(&sblock))
cgrp.cg_initediblk = 2 * INOPB(&sblock);
else
cgrp.cg_initediblk = mininos;
pwarn("CYLINDER GROUP %d: RESET FROM %ju TO %d %s\n",
c, i, cgrp.cg_initediblk, "VALID INODES");
cgdirty();
}
if (inosused < sblock.fs_ipg)
continue; continue;
lastino += 1;
if (lastino < (c * sblock.fs_ipg))
inosused = 0;
else
inosused = lastino - (c * sblock.fs_ipg);
if (rebuildcg && inosused > cgrp.cg_initediblk &&
sblock.fs_magic == FS_UFS2_MAGIC) {
cgrp.cg_initediblk = roundup(inosused, INOPB(&sblock));
pwarn("CYLINDER GROUP %d: FOUND %d VALID INODES\n", c,
cgrp.cg_initediblk);
}
/* /*
* If we were not able to determine in advance which inodes * If we were not able to determine in advance which inodes
* were in use, then reduce the size of the inoinfo structure * were in use, then reduce the size of the inoinfo structure
* to the size necessary to describe the inodes that we * to the size necessary to describe the inodes that we
* really found. * really found.
*/ */
if (lastino < (c * sblock.fs_ipg)) if (inumber == lastino)
inosused = 0; continue;
else
inosused = lastino - (c * sblock.fs_ipg);
inostathead[c].il_numalloced = inosused; inostathead[c].il_numalloced = inosused;
if (inosused == 0) { if (inosused == 0) {
free(inostathead[c].il_stat); free(inostathead[c].il_stat);
@ -187,8 +224,8 @@ pass1(void)
freeinodebuf(); freeinodebuf();
} }
static void static int
checkinode(ino_t inumber, struct inodesc *idesc) checkinode(ino_t inumber, struct inodesc *idesc, int rebuildcg)
{ {
union dinode *dp; union dinode *dp;
off_t kernmaxfilesize; off_t kernmaxfilesize;
@ -196,7 +233,8 @@ checkinode(ino_t inumber, struct inodesc *idesc)
mode_t mode; mode_t mode;
int j, ret, offset; int j, ret, offset;
dp = getnextinode(inumber); if ((dp = getnextinode(inumber, rebuildcg)) == NULL)
return (0);
mode = DIP(dp, di_mode) & IFMT; mode = DIP(dp, di_mode) & IFMT;
if (mode == 0) { if (mode == 0) {
if ((sblock.fs_magic == FS_UFS1_MAGIC && if ((sblock.fs_magic == FS_UFS1_MAGIC &&
@ -220,7 +258,7 @@ checkinode(ino_t inumber, struct inodesc *idesc)
} }
} }
inoinfo(inumber)->ino_state = USTATE; inoinfo(inumber)->ino_state = USTATE;
return; return (1);
} }
lastino = inumber; lastino = inumber;
/* This should match the file size limit in ffs_mountfs(). */ /* This should match the file size limit in ffs_mountfs(). */
@ -352,7 +390,7 @@ checkinode(ino_t inumber, struct inodesc *idesc)
if (preen) if (preen)
printf(" (CORRECTED)\n"); printf(" (CORRECTED)\n");
else if (reply("CORRECT") == 0) else if (reply("CORRECT") == 0)
return; return (1);
if (bkgrdflag == 0) { if (bkgrdflag == 0) {
dp = ginode(inumber); dp = ginode(inumber);
DIP_SET(dp, di_blocks, idesc->id_entryno); DIP_SET(dp, di_blocks, idesc->id_entryno);
@ -368,7 +406,7 @@ checkinode(ino_t inumber, struct inodesc *idesc)
rwerror("ADJUST INODE BLOCK COUNT", cmd.value); rwerror("ADJUST INODE BLOCK COUNT", cmd.value);
} }
} }
return; return (1);
unknown: unknown:
pfatal("UNKNOWN FILE TYPE I=%lu", (u_long)inumber); pfatal("UNKNOWN FILE TYPE I=%lu", (u_long)inumber);
inoinfo(inumber)->ino_state = FCLEAR; inoinfo(inumber)->ino_state = FCLEAR;
@ -378,6 +416,7 @@ checkinode(ino_t inumber, struct inodesc *idesc)
clearinode(dp); clearinode(dp);
inodirty(); inodirty();
} }
return (1);
} }
int int