/* * 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. */ #pragma ident "%Z%%M% %I% %E% SMI" #include #include #include #include #include #include #include #include #include #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); }