pass4.c revision 77a343ab1b2bc518f178bc7b473e770e3da2bb79
/*
* 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 <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);
}