Subject: kern/11470: fsck_lfs doesn't check or fix bad dirty/clean LFS segment count
To: None <gnats-bugs@gnats.netbsd.org>
From: None <joff@gci-net.com>
List: netbsd-bugs
Date: 11/11/2000 22:19:17
>Number:         11470
>Category:       kern
>Synopsis:       fsck_lfs doesn't check or fix bad dirty/clean LFS segment count
>Confidential:   no
>Severity:       serious
>Priority:       high
>Responsible:    kern-bug-people
>State:          open
>Class:          change-request
>Submitter-Id:   net
>Arrival-Date:   Sat Nov 11 22:19:01 PST 2000
>Closed-Date:
>Last-Modified:
>Originator:     Jesse Off
>Release:        1.5BETA
>Organization:
>Environment:
NetBSD construct.home 1.5_BETA NetBSD 1.5_BETA (CONSTRUCT) #9: Sat Nov 11 21:36:11 MST 2000     joff@construct.home:/usr/src/sys/arch/i386/compile/CONSTRUCT i386
>Description:
I recently found out that the source of some of my problems in LFS were
due to the fact that the nclean member of the superblock and cleanerinfo
block were wrong on my FS.  I don't know how my FS became corrupted in
this way, but I'm sure it had to do with some of my past 
testing and may not be any fault of LFS.  Anyway, it would have been nice 
if fsck_lfs could have detected and fixed this.  

The problem shows up as an abnormally higher than actual number of free 
space returned from a 'df'.  It also is apparent if you
dumplfs and count dirty/clean segments in the segment summary section.
Somehow, my filesystem with 299 segments was listing 101 segments clean
when in fact there were only 71.


  
>How-To-Repeat:
Not quite sure how my FS got this way, but I suppose you could take
a hexeditor on the superblock to make nclean wrong and then do
a df and fsck_lfs and see that it does not fix or detect the problem.


>Fix:
I've included a few patches here:

 1) change to pass5.c for fsck_lfs that warns and gives the option
    to fix nclean if it detects that it is wrong
 2) chang to sys/ufs/lfs/lfs_vfsops.c that reinitializes the cleanerinfo
    block on mount according to information its found in the superblock.
 3) change to /usr/sbin/dumplfs to display the nclean and minfreeseg in
    the superblock and also the clean, dirty, avail, and bfree counts 
    found in the cleanerinfo block. (was useful during debugging, but 
    somewhat unrelated to the fix)

#1
===================================================================
RCS file: /cvsroot/basesrc/sbin/fsck_lfs/pass5.c,v
retrieving revision 1.5.2.1
diff -u -r1.5.2.1 pass5.c
--- pass5.c     2000/09/14 18:53:21     1.5.2.1
+++ pass5.c     2000/11/12 06:10:38
@@ -59,11 +59,13 @@
        unsigned long   bb; /* total number of used blocks (lower bound) */
        unsigned long   ubb; /* upper bound number of used blocks */
        unsigned long   avail; /* blocks available for writing */
+       int             nclean; /* clean segments */
 
        /*
         * Check segment holdings against actual holdings.  Check for
         * "clean" segments that contain live data.
         */
+       nclean = 0;
        avail = 0;
        bb = ubb = 0;
        for (i = 0; i < sblock.lfs_nseg; i++) {
@@ -96,6 +98,7 @@
                        bb += btodb(su->su_nbytes) + su->su_nsums;
                        ubb += btodb(su->su_nbytes) + su->su_nsums + fsbtodb(&sblock, su->su_ninos);
                } else {
+                       nclean++;
                        avail += fsbtodb(&sblock, sblock.lfs_ssize);
                        if (su->su_flags & SEGUSE_SUPERBLOCK)
                                avail -= btodb(LFS_SBPAD);
@@ -117,6 +120,15 @@
                        sbdirty();
                }
        }
+       if (nclean != sblock.lfs_nclean) {
+               pwarn("nclean given as %d, should be %d\n", sblock.lfs_nclean,
+                     nclean);
+               if (preen || reply("fix")) {
+                       sblock.lfs_nclean = nclean;
+                       sbdirty();
+               }
+       }
+
        if (sblock.lfs_bfree > sblock.lfs_dsize - bb ||
            sblock.lfs_bfree < sblock.lfs_dsize - ubb) {
                pwarn("bfree given as %d, should be between %ld and %ld\n",







#2
Index: lfs_vfsops.c
===================================================================
RCS file: /cvsroot/syssrc/sys/ufs/lfs/lfs_vfsops.c,v
retrieving revision 1.52.4.2
diff -u -r1.52.4.2 lfs_vfsops.c
--- lfs_vfsops.c        2000/09/14 18:50:20     1.52.4.2
+++ lfs_vfsops.c        2000/11/12 06:11:57
@@ -339,7 +339,8 @@
        dev_t dev;
        int error, i, ronly, size;
        struct ucred *cred;
-        SEGUSE *sup;
+       CLEANERINFO *cip;
+       SEGUSE *sup;
 
        cred = p ? p->p_ucred : NOCRED;
        /*
@@ -475,6 +476,17 @@
        fs->lfs_ivnode = vp;
        VREF(vp);
        vput(vp);
+
+       /*
+        * Initialize the ifile cleaner info with information from 
+        * the superblock.
+        */ 
+       LFS_CLEANERINFO(cip, fs, bp);
+       cip->clean = fs->lfs_nclean;
+       cip->dirty = fs->lfs_nseg - fs->lfs_nclean;
+       cip->avail = fs->lfs_avail;
+       cip->bfree = fs->lfs_bfree;
+       (void) VOP_BWRITE(bp);
 
        /*
         * Mark the current segment as ACTIVE, since we're going to 




#3
Index: dumplfs.c
===================================================================
RCS file: /cvsroot/basesrc/usr.sbin/dumplfs/dumplfs.c,v
retrieving revision 1.14.2.1
diff -u -r1.14.2.1 dumplfs.c
--- dumplfs.c   2000/08/28 05:52:34     1.14.2.1
+++ dumplfs.c   2000/11/12 06:13:02
@@ -606,6 +606,10 @@
                "cksum    ", lfsp->lfs_cksum,
                "maxfilesize  ", (long long)lfsp->lfs_maxfilesize);
 
+       (void)printf("%s%d\t%s%d\n",
+               "nclean       ", lfsp->lfs_nclean,
+               "minfreeseg   ", lfsp->lfs_minfreeseg);
+
        (void)printf("Superblock disk addresses:\t");
        for (i = 0; i < LFS_MAXNUMSB; i++) {
                (void)printf(" 0x%x", lfsp->lfs_sboffs[i]);
@@ -667,8 +671,11 @@
        CLEANERINFO *cip;
 
        cip = (CLEANERINFO *)ipage;
-       (void)printf("segments clean\t%d\tsegments dirty\t%d\n\n",
+       (void)printf("segments clean\t%d\tsegments dirty\t%d\n",
            cip->clean, cip->dirty);
+       (void)printf("avail\t%d\tbfree\t%d\n\n",
+           cip->avail, cip->bfree);
+
 }
 
 static void

>Release-Note:
>Audit-Trail:
>Unformatted: