root/usr/src/cmd/fs.d/ufs/fsck/pass4.c
/*
 * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/*      Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T     */
/*        All Rights Reserved   */

/*
 * Copyright (c) 1980, 1986, 1990 The Regents of the University of California.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms are permitted
 * provided that: (1) source distributions retain this entire copyright
 * notice and comment, and (2) distributions including binaries display
 * the following acknowledgement:  ``This product includes software
 * developed by the University of California, Berkeley and its contributors''
 * in the documentation or other materials provided with the distribution
 * and in all advertising materials mentioning features or use of this
 * software. Neither the name of the University nor the names of its
 * contributors may be used to endorse or promote products derived
 * from this software without specific prior written permission.
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/mntent.h>
#include <sys/fs/ufs_fs.h>
#include <sys/vnode.h>
#include <sys/fs/ufs_inode.h>
#include "fsck.h"

void
pass4(void)
{
        fsck_ino_t inumber;
        struct dinode *dp;
        struct inodesc idesc;
        int n, was_dir;
        int need_rescan;
        int scan_pass = 0;

        /*
         * If we clear a directory, it may have produced orphans which
         * we need to go pick up.  So, do this until done.  It can be
         * proven that the loop terminates because at most there can
         * be lastino directories, and we only rescan if we clear a
         * directory.
         */
        do {
                if (debug)
                        (void) printf("pass4 scan %d\n", scan_pass++);

                need_rescan = 0;
                for (inumber = UFSROOTINO; inumber <= lastino; inumber++) {
                        init_inodesc(&idesc);
                        idesc.id_type = ADDR;
                        idesc.id_func = pass4check;
                        idesc.id_number = inumber;

                        was_dir = (statemap[inumber] & DSTATE) == DSTATE;

                        switch (statemap[inumber] & ~(INORPHAN | INDELAYD
                            | INZLINK)) {

                        case FZLINK:
                        case DZLINK:
                                /*
                                 * INZLINK gets set if the inode claimed zero
                                 * links when we first looked at it in pass 1.
                                 * If lncntp[] also claims it has zero links,
                                 * it really is unreferenced.  However, we
                                 * could have found a link to it during one of
                                 * the other passes, so we have to check the
                                 * final count in lncntp[].
                                 */
                                if (lncntp[inumber] == 0) {
                                        clri(&idesc, "UNREF", CLRI_VERBOSE,
                                            CLRI_NOP_OK);
                                        if (was_dir &&
                                            (statemap[inumber] == USTATE))
                                                need_rescan = 1;
                                        break;
                                }
                                /* FALLTHROUGH */

                        case FSTATE:
                        case DFOUND:
                        case SSTATE:
                                n = lncntp[inumber];
                                if (n || (statemap[inumber] &
                                    (INDELAYD | INZLINK))) {
                                        /*
                                         * adjust() will clear the inode if
                                         * the link count goes to zero.  If
                                         * it isn't cleared, we need to note
                                         * that we've adjusted the count
                                         * already, so we don't do it again
                                         * on a rescan.
                                         */
                                        adjust(&idesc, n);
                                        if (was_dir &&
                                            (statemap[inumber] == USTATE)) {
                                                need_rescan = 1;
                                        } else {
                                                TRACK_LNCNTP(inumber,
                                                    lncntp[inumber] = 0);
                                        }
                                }
                                break;

                        case DSTATE:
                                clri(&idesc, "UNREF", CLRI_VERBOSE,
                                    CLRI_NOP_OK);
                                if (was_dir && (statemap[inumber] == USTATE))
                                        need_rescan = 1;
                                break;

                        case DCLEAR:
                                dp = ginode(inumber);
                                if (dp->di_size == 0) {
                                        clri(&idesc, "ZERO LENGTH",
                                            CLRI_VERBOSE, CLRI_NOP_CORRUPT);
                                        break;
                                }
                                /* FALLTHROUGH */

                        case FCLEAR:
                                clri(&idesc, "BAD/DUP", CLRI_VERBOSE,
                                    CLRI_NOP_CORRUPT);
                                break;

                        case SCLEAR:
                                clri(&idesc, "BAD", CLRI_VERBOSE,
                                    CLRI_NOP_CORRUPT);
                                break;

                        case USTATE:
                                break;

                        default:
                                errexit("BAD STATE 0x%x FOR INODE I=%d",
                                        (int)statemap[inumber], inumber);
                        }
                }
        } while (need_rescan);
}

int
pass4check(struct inodesc *idesc)
{
        int fragnum, cg_frag;
        int res = KEEPON;
        daddr32_t blkno = idesc->id_blkno;
        int cylno;
        struct cg *cgp = &cgrp;
        caddr_t err;

        if ((idesc->id_truncto >= 0) && (idesc->id_lbn < idesc->id_truncto)) {
                if (debug)
                        (void) printf(
                    "pass4check: skipping inode %d lbn %d with truncto %d\n",
                            idesc->id_number, idesc->id_lbn,
                            idesc->id_truncto);
                return (KEEPON);
        }

        for (fragnum = 0; fragnum < idesc->id_numfrags; fragnum++) {
                if (chkrange(blkno + fragnum, 1)) {
                        res = SKIP;
                } else if (testbmap(blkno + fragnum)) {
                        /*
                         * The block's in use.  Remove our reference
                         * from it.
                         *
                         * If it wasn't a dup, or everybody's done with
                         * it, then this is the last reference and it's
                         * safe to actually deallocate the on-disk block.
                         *
                         * We depend on pass 5 resolving the on-disk bitmap
                         * effects.
                         */
                        cg_frag = blkno + fragnum;
                        if (!find_dup_ref(cg_frag, idesc->id_number,
                            idesc->id_lbn * sblock.fs_frag + fragnum,
                            DB_DECR)) {

                                if (debug)
                                        (void) printf("p4c marking %d avail\n",
                                            cg_frag);
                                clrbmap(cg_frag);
                                n_blks--;

                                /*
                                 * Do the same for the on-disk bitmap, so
                                 * that we don't need another pass to figure
                                 * out what's really being used.  We'll let
                                 * pass5() work out the fragment/block
                                 * accounting.
                                 */
                                cylno = dtog(&sblock, cg_frag);
                                (void) getblk(&cgblk, cgtod(&sblock, cylno),
                                    (size_t)sblock.fs_cgsize);
                                err = cg_sanity(cgp, cylno);
                                if (err != NULL) {
                                        pfatal("CG %d: %s\n", cylno, err);
                                        free((void *)err);
                                        if (reply("REPAIR") == 0)
                                                errexit("Program terminated.");
                                        fix_cg(cgp, cylno);
                                }
                                clrbit(cg_blksfree(cgp),
                                    dtogd(&sblock, cg_frag));
                                cgdirty();

                                res |= ALTERED;
                        }
                }
        }
        return (res);
}