#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/mntent.h>
#include <sys/fs/ufs_fs.h>
#include <sys/vnode.h>
#define _KERNEL
#include <sys/fs/ufs_fsdir.h>
#undef _KERNEL
#include <sys/fs/ufs_inode.h>
#include "fsck.h"
static uint32_t badblk;
static uint32_t dupblk;
static void clear_attr_acl(fsck_ino_t, fsck_ino_t, char *);
static void verify_inode(fsck_ino_t, struct inodesc *, fsck_ino_t);
static void check_dirholes(fsck_ino_t, struct inodesc *);
static void collapse_dirhole(fsck_ino_t, struct inodesc *);
static void note_used(daddr32_t);
void
pass1(void)
{
uint_t c, i;
daddr32_t cgd;
struct inodesc idesc;
fsck_ino_t inumber;
fsck_ino_t maxinumber;
for (c = 0; c < sblock.fs_ncg; c++) {
cgd = cgdmin(&sblock, c);
if (c == 0) {
i = cgbase(&sblock, c);
cgd += howmany(sblock.fs_cssize, sblock.fs_fsize);
} else {
i = cgsblock(&sblock, c);
}
for (; i < cgd; i++) {
note_used(i);
}
}
if (islog && islogok && sblock.fs_logbno)
examinelog(¬e_used);
inumber = 0;
n_files = n_blks = 0;
resetinodebuf();
maxinumber = sblock.fs_ncg * sblock.fs_ipg;
for (c = 0; c < sblock.fs_ncg; c++) {
for (i = 0; i < sblock.fs_ipg; i++, inumber++) {
if (inumber < UFSROOTINO)
continue;
init_inodesc(&idesc);
idesc.id_type = ADDR;
idesc.id_func = pass1check;
verify_inode(inumber, &idesc, maxinumber);
}
}
freeinodebuf();
}
static void
verify_inode(fsck_ino_t inumber, struct inodesc *idesc, fsck_ino_t maxinumber)
{
int j, clear, flags;
int isdir;
char *err;
fsck_ino_t shadow, attrinode;
daddr32_t ndb;
struct dinode *dp;
struct inoinfo *iip;
dp = getnextinode(inumber);
if ((dp->di_mode & IFMT) == 0) {
if ((memcmp((void *)dp->di_db, (void *)zino.di_db,
NDADDR * sizeof (daddr32_t)) != 0) ||
(memcmp((void *)dp->di_ib, (void *)zino.di_ib,
NIADDR * sizeof (daddr32_t)) != 0) ||
(dp->di_mode != 0) || (dp->di_size != 0)) {
pfatal("PARTIALLY ALLOCATED INODE I=%u", inumber);
if (reply("CLEAR") == 1) {
dp = ginode(inumber);
clearinode(dp);
inodirty();
} else {
iscorrupt = 1;
}
}
statemap[inumber] = USTATE;
return;
}
isdir = ((dp->di_mode & IFMT) == IFDIR) ||
((dp->di_mode & IFMT) == IFATTRDIR);
lastino = inumber;
if (dp->di_size > (u_offset_t)UFS_MAXOFFSET_T) {
pfatal("NEGATIVE SIZE %lld I=%d",
(longlong_t)dp->di_size, inumber);
goto bogus;
}
if ((dp->di_mode & IFMT) == IFMT) {
pfatal("BAD MODE 0%o I=%d",
dp->di_mode & IFMT, inumber);
if (reply("BAD MODE: MAKE IT A FILE") == 1) {
statemap[inumber] = FSTATE;
dp = ginode(inumber);
dp->di_mode = IFREG | 0600;
inodirty();
truncino(inumber, sblock.fs_fsize, TI_NOPARENT);
dp = getnextrefresh();
} else {
iscorrupt = 1;
}
}
ndb = howmany(dp->di_size, (u_offset_t)sblock.fs_bsize);
if (ndb < 0) {
pfatal("NEGATIVE SIZE %lld I=%d",
(longlong_t)dp->di_size, inumber);
goto bogus;
}
if ((dp->di_mode & IFMT) == IFBLK ||
(dp->di_mode & IFMT) == IFCHR) {
if (dp->di_size != 0) {
pfatal("SPECIAL FILE WITH NON-ZERO LENGTH %lld I=%d",
(longlong_t)dp->di_size, inumber);
goto bogus;
}
for (j = 0; j < NDADDR; j++) {
if (dp->di_db[j] != 0 &&
&dp->di_db[j] != &dp->di_ordev) {
if (debug) {
(void) printf(
"spec file di_db[%d] has %d\n",
j, dp->di_db[j]);
}
pfatal(
"SPECIAL FILE WITH NON-ZERO FRAGMENT LIST I=%d",
inumber);
goto bogus;
}
}
for (j = 0; j < NIADDR; j++) {
if (dp->di_ib[j] != 0) {
if (debug)
(void) printf(
"special has %d at ib[%d]\n",
dp->di_ib[j], j);
pfatal(
"SPECIAL FILE WITH NON-ZERO FRAGMENT LIST I=%d",
inumber);
goto bogus;
}
}
} else {
err = "Internal error: unexpected variant of having "
"blocks past end of file I=%d";
clear = 0;
for (j = ndb; j < NDADDR; j++) {
if (dp->di_db[j] != 0) {
if (debug) {
(void) printf("bad file direct "
"addr[%d]: block 0x%x "
"format: 0%o\n",
j, dp->di_db[j],
dp->di_mode & IFMT);
}
err = "FILE WITH FRAGMENTS PAST END I=%d";
clear = 1;
break;
}
}
if (!clear) {
for (j = 0, ndb -= NDADDR; ndb > 0; j++) {
ndb /= NINDIR(&sblock);
}
for (; j < NIADDR; j++) {
if (dp->di_ib[j] != 0) {
if (debug) {
(void) printf("bad file "
"indirect addr: block %d\n",
dp->di_ib[j]);
}
err =
"FILE WITH FRAGMENTS PAST END I=%d";
clear = 2;
break;
}
}
}
if (clear) {
pwarn(err, inumber);
if (preen || reply("DISCARD EXCESS FRAGMENTS") == 1) {
dp = ginode(inumber);
if (clear == 1) {
for (; j < NDADDR; j++)
dp->di_db[j] = 0;
j = 0;
}
for (; j < NIADDR; j++)
dp->di_ib[j] = 0;
inodirty();
dp = getnextrefresh();
if (preen)
(void) printf(" (TRUNCATED)");
}
}
}
if (ftypeok(dp) == 0) {
pfatal("UNKNOWN FILE TYPE 0%o I=%d", dp->di_mode, inumber);
goto bogus;
}
n_files++;
TRACK_LNCNTP(inumber, lncntp[inumber] = dp->di_nlink);
flags = statemap[inumber] & INDELAYD;
if (dp->di_nlink <= 0 &&
!((errorlocked || islog) && dp->di_mode == 0) &&
!(flags & INCLEAR)) {
flags |= INZLINK;
if (debug)
(void) printf(
"marking i=%d INZLINK; nlink %d, mode 0%o, islog %d\n",
inumber, dp->di_nlink, dp->di_mode, islog);
}
switch (dp->di_mode & IFMT) {
case IFDIR:
case IFATTRDIR:
if (dp->di_size == 0) {
if ((dp->di_mode & IFMT) == IFDIR)
(void) printf("ZERO-LENGTH DIR I=%d\n",
inumber);
else
(void) printf("ZERO-LENGTH ATTRDIR I=%d\n",
inumber);
add_orphan_dir(inumber);
flags |= INCLEAR;
flags &= ~INZLINK;
}
statemap[inumber] = DSTATE | flags;
cacheino(dp, inumber);
countdirs++;
break;
case IFSHAD:
if (dp->di_size == 0) {
(void) printf("ZERO-LENGTH SHADOW I=%d\n", inumber);
flags |= INCLEAR;
flags &= ~INZLINK;
}
statemap[inumber] = SSTATE | flags;
cacheacl(dp, inumber);
break;
default:
statemap[inumber] = FSTATE | flags;
}
badblk = 0;
dupblk = 0;
idesc->id_number = inumber;
idesc->id_fix = DONTKNOW;
if (dp->di_size > (u_offset_t)MAXOFF_T) {
largefile_count++;
}
(void) ckinode(dp, idesc, CKI_TRAVERSE);
if (isdir && (idesc->id_firsthole >= 0))
check_dirholes(inumber, idesc);
if (dp->di_blocks != idesc->id_entryno) {
pwarn("INCORRECT DISK BLOCK COUNT I=%u (%d should be %d)",
inumber, (uint32_t)dp->di_blocks, idesc->id_entryno);
if (!preen && (reply("CORRECT") == 0))
return;
dp = ginode(inumber);
dp->di_blocks = idesc->id_entryno;
iip = getinoinfo(inumber);
if (iip != NULL)
iip->i_isize = dp->di_size;
inodirty();
if (preen)
(void) printf(" (CORRECTED)\n");
}
if (isdir && (dp->di_blocks == 0)) {
(void) printf("DIR WITH ZERO BLOCKS I=%d\n", inumber);
statemap[inumber] = DCLEAR;
add_orphan_dir(inumber);
}
shadow = dp->di_shadow;
if (shadow != 0) {
if (acltypeok(dp) == 0) {
clear_attr_acl(inumber, -1,
"NON-ZERO ACL REFERENCE, I=%d\n");
} else if ((shadow <= UFSROOTINO) ||
(shadow > maxinumber)) {
clear_attr_acl(inumber, -1,
"BAD ACL REFERENCE I=%d\n");
} else {
registershadowclient(shadow,
inumber, &shadowclientinfo);
}
}
attrinode = dp->di_oeftflag;
if (attrinode != 0) {
if ((attrinode <= UFSROOTINO) ||
(attrinode > maxinumber)) {
clear_attr_acl(attrinode, inumber,
"BAD ATTRIBUTE REFERENCE TO I=%d FROM I=%d\n");
} else {
dp = ginode(attrinode);
if ((dp->di_mode & IFMT) != IFATTRDIR) {
clear_attr_acl(attrinode, inumber,
"BAD ATTRIBUTE DIR REF TO I=%d FROM I=%d\n");
} else if (dp->di_size == 0) {
clear_attr_acl(attrinode, inumber,
"REFERENCE TO ZERO-LENGTH ATTRIBUTE DIR I=%d from I=%d\n");
} else {
registershadowclient(attrinode, inumber,
&attrclientinfo);
}
}
}
return;
bogus:
if (isdir) {
statemap[inumber] = DCLEAR;
add_orphan_dir(inumber);
cacheino(dp, inumber);
} else {
statemap[inumber] = FCLEAR;
}
if (reply("CLEAR") == 1) {
(void) tdelete((void *)inumber, &limbo_dirs, ino_t_cmp);
freeino(inumber, TI_PARENT);
inodirty();
} else {
iscorrupt = 1;
}
}
static void
clear_attr_acl(fsck_ino_t inumber, fsck_ino_t parent, char *fmt)
{
fsck_ino_t victim = inumber;
struct dinode *dp;
if (parent != -1)
victim = parent;
if (fmt != NULL) {
if (parent == -1)
pwarn(fmt, (int)inumber);
else
pwarn(fmt, (int)inumber, (int)parent);
}
if (debug)
(void) printf("parent file/dir I=%d\nvictim I=%d",
(int)parent, (int)victim);
if (!preen && (reply("REMOVE REFERENCE") == 0)) {
iscorrupt = 1;
return;
}
dp = ginode(victim);
if (parent == -1) {
dp->di_shadow = 0;
dp->di_mode &= IFMT;
} else {
dp->di_oeftflag = 0;
}
inodirty();
if (preen)
(void) printf(" (CORRECTED)\n");
}
static void
check_dirholes(fsck_ino_t inumber, struct inodesc *idesc)
{
char pathbuf[MAXPATHLEN + 1];
getpathname(pathbuf, idesc->id_number, idesc->id_number);
pfatal("I=%d DIRECTORY %s: CONTAINS EMPTY BLOCKS",
idesc->id_number, pathbuf);
if (reply("TRUNCATE AT FIRST EMPTY BLOCK") == 1) {
collapse_dirhole(inumber, idesc);
if (preen)
(void) printf(" (TRUNCATED)\n");
} else {
iscorrupt = 1;
}
}
static void
collapse_dirhole(fsck_ino_t inumber, struct inodesc *idesc)
{
offset_t new_size;
int blocks;
if (idesc->id_firsthole < 0) {
return;
}
new_size = idesc->id_firsthole * (offset_t)sblock.fs_bsize;
blocks = howmany(new_size, sblock.fs_bsize);
if (blocks > NDADDR) {
if (blocks < (NDADDR + NINDIR(&sblock)))
blocks = NDADDR;
else if (blocks < (NDADDR + NINDIR(&sblock) +
(NINDIR(&sblock) * NINDIR(&sblock))))
blocks = NDADDR + NINDIR(&sblock);
else
blocks = NDADDR + NINDIR(&sblock) +
(NINDIR(&sblock) * NINDIR(&sblock));
new_size = blocks * sblock.fs_bsize;
if (debug)
(void) printf("to %lld (blocks %d)\n",
(longlong_t)new_size, blocks);
}
truncino(inumber, new_size, TI_NOPARENT);
idesc->id_entryno = btodb(new_size);
}
int
pass1check(struct inodesc *idesc)
{
int res = KEEPON;
int anyout;
int nfrags;
daddr32_t lbn;
daddr32_t fragno = idesc->id_blkno;
struct dinode *dp;
dp = ginode(idesc->id_number);
if (dp->di_cflags & IFALLOCATE && fragno < 0)
fragno = -fragno;
if ((anyout = chkrange(fragno, idesc->id_numfrags)) != 0) {
blkerror(idesc->id_number, "OUT OF RANGE",
fragno, idesc->id_lbn * sblock.fs_frag);
dp = ginode(idesc->id_number);
if ((((dp->di_mode & IFMT) == IFDIR) ||
((dp->di_mode & IFMT) == IFATTRDIR)) &&
(idesc->id_firsthole < 0)) {
idesc->id_firsthole = idesc->id_lbn;
}
if (++badblk >= MAXBAD) {
pwarn("EXCESSIVE BAD FRAGMENTS I=%u",
idesc->id_number);
if (reply("CONTINUE") == 0)
errexit("Program terminated.");
return (SKIP | STOP);
}
}
for (nfrags = 0; nfrags < idesc->id_numfrags; fragno++, nfrags++) {
if (anyout && chkrange(fragno, 1)) {
res = SKIP;
} else if (!testbmap(fragno)) {
note_used(fragno);
} else {
lbn = idesc->id_lbn * sblock.fs_frag + nfrags;
if (dupblk < MAXDUP)
blkerror(idesc->id_number, "DUP", fragno, lbn);
if (++dupblk == MAXDUP) {
pwarn("EXCESSIVE DUPLICATE FRAGMENTS I=%u",
idesc->id_number);
if (reply("CONTINUE") == 0)
errexit("Program terminated.");
}
(void) find_dup_ref(fragno, idesc->id_number, lbn,
DB_CREATE | DB_INCR);
}
idesc->id_entryno += btodb(sblock.fs_fsize);
}
return (res);
}
static void
note_used(daddr32_t frag)
{
n_blks++;
setbmap(frag);
}