#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include <limits.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/sysmacros.h>
#include <sys/mntent.h>
#include <sys/vnode.h>
#include <sys/fs/ufs_inode.h>
#include <sys/fs/ufs_fs.h>
#define _KERNEL
#include <sys/fs/ufs_fsdir.h>
#undef _KERNEL
#include <pwd.h>
#include "fsck.h"
uint_t largefile_count = 0;
fsck_ino_t lastino;
struct bufarea cgblk;
struct inoinfo **aclphead, **aclpsort;
struct dinode zino;
static int get_indir_offsets(int, daddr_t, int *, int *);
static int clearanentry(struct inodesc *);
static void pdinode(struct dinode *);
static void inoflush(void);
static void mark_delayed_inodes(fsck_ino_t, daddr32_t);
static int iblock(struct inodesc *, int, u_offset_t, enum cki_action);
static struct inoinfo *search_cache(struct inoinfo *, fsck_ino_t);
static int ckinode_common(struct dinode *, struct inodesc *, enum cki_action);
static int lookup_dotdot_ino(fsck_ino_t);
int
ckinode(struct dinode *dp, struct inodesc *idesc, enum cki_action action)
{
struct inodesc cleardesc;
mode_t mode;
if (idesc->id_filesize == 0)
idesc->id_filesize = (offset_t)dp->di_size;
mode = dp->di_mode & IFMT;
if (mode == IFBLK || mode == IFCHR)
return (KEEPON);
if (mode == IFLNK && dp->di_size > MAXPATHLEN) {
pwarn("I=%d Symlink longer than supported maximum\n",
idesc->id_number);
init_inodesc(&cleardesc);
cleardesc.id_type = ADDR;
cleardesc.id_number = idesc->id_number;
cleardesc.id_fix = DONTKNOW;
clri(&cleardesc, "BAD", CLRI_VERBOSE, CLRI_NOP_CORRUPT);
return (STOP);
}
return (ckinode_common(dp, idesc, action));
}
static int
ckinode_common(struct dinode *dp, struct inodesc *idesc,
enum cki_action action)
{
offset_t offset;
struct dinode dino;
daddr_t ndb;
int indir_data_blks, last_indir_blk;
int ret, i, frags;
(void) memmove(&dino, dp, sizeof (struct dinode));
ndb = howmany(dino.di_size, (u_offset_t)sblock.fs_bsize);
for (i = 0; i < NDADDR; i++) {
idesc->id_lbn++;
offset = blkoff(&sblock, dino.di_size);
if ((--ndb == 0) && (offset != 0)) {
idesc->id_numfrags =
numfrags(&sblock, fragroundup(&sblock, offset));
} else {
idesc->id_numfrags = sblock.fs_frag;
}
if (dino.di_db[i] == 0) {
if ((ndb > 0) && (idesc->id_firsthole < 0)) {
idesc->id_firsthole = i;
}
continue;
}
idesc->id_blkno = dino.di_db[i];
if (idesc->id_type == ADDR || idesc->id_type == ACL)
ret = (*idesc->id_func)(idesc);
else
ret = dirscan(idesc);
if ((action == CKI_TRUNCATE) &&
(idesc->id_truncto >= 0) &&
(idesc->id_lbn >= idesc->id_truncto)) {
dp = ginode(idesc->id_number);
frags = howmany((int)dp->di_size, sblock.fs_fsize) %
sblock.fs_frag;
if (frags == 0)
frags = sblock.fs_frag;
freeblk(idesc->id_number, dp->di_db[i],
frags);
dp = ginode(idesc->id_number);
dp->di_db[i] = 0;
inodirty();
ret |= ALTERED;
}
if (ret & STOP)
return (ret);
}
#ifdef lint
indir_data_blks = 0;
#endif
idesc->id_numfrags = sblock.fs_frag;
ndb = howmany(dino.di_size, (u_offset_t)sblock.fs_bsize);
for (i = 0; i < NIADDR; i++) {
(void) get_indir_offsets(i, ndb, &indir_data_blks,
&last_indir_blk);
if (dino.di_ib[i] != 0) {
idesc->id_blkno = dino.di_ib[i];
ret = iblock(idesc, i + 1,
(u_offset_t)howmany(dino.di_size,
(u_offset_t)sblock.fs_bsize) - indir_data_blks,
action);
if ((action == CKI_TRUNCATE) &&
(idesc->id_truncto <= indir_data_blks) &&
((idesc->id_lbn + 1) >= indir_data_blks) &&
((idesc->id_lbn + 1) <= last_indir_blk)) {
dp = ginode(idesc->id_number);
if (dp->di_ib[i] != 0) {
freeblk(idesc->id_number, dp->di_ib[i],
sblock.fs_frag);
}
}
if (ret & STOP)
return (ret);
} else {
if ((indir_data_blks < ndb) &&
(idesc->id_firsthole < 0)) {
idesc->id_firsthole = indir_data_blks;
}
}
}
return (KEEPON);
}
static int
get_indir_offsets(int ilevel_wanted, daddr_t ndb, int *data_blks,
int *last_blk)
{
int ndb_ilevel = -1;
int ilevel;
int dblks, lblk;
for (ilevel = 0; ilevel < NIADDR; ilevel++) {
switch (ilevel) {
case 0:
dblks = NDADDR;
lblk = dblks + NINDIR(&sblock) - 1;
break;
case 1:
dblks = NDADDR + NINDIR(&sblock);
lblk = dblks + (NINDIR(&sblock) * NINDIR(&sblock)) - 1;
break;
case 2:
dblks = NDADDR + NINDIR(&sblock) +
(NINDIR(&sblock) * NINDIR(&sblock));
lblk = dblks + (NINDIR(&sblock) * NINDIR(&sblock) *
NINDIR(&sblock)) - 1;
break;
default:
exitstat = EXERRFATAL;
errexit("panic: indirection level %d not 1, 2, or 3",
ilevel + 1);
}
if (dblks < ndb && ndb <= lblk)
ndb_ilevel = ilevel;
if (ilevel == ilevel_wanted) {
if (data_blks != NULL)
*data_blks = dblks;
if (last_blk != NULL)
*last_blk = lblk;
}
}
return (ndb_ilevel);
}
static int
iblock(struct inodesc *idesc, int ilevel, u_offset_t iblks,
enum cki_action action)
{
struct bufarea *bp;
int i, n;
int (*func)(struct inodesc *) = NULL;
u_offset_t fsbperindirb;
daddr32_t last_lbn;
int nif;
char buf[BUFSIZ];
n = KEEPON;
switch (idesc->id_type) {
case ADDR:
func = idesc->id_func;
if (((n = (*func)(idesc)) & KEEPON) == 0)
return (n);
break;
case ACL:
func = idesc->id_func;
break;
case DATA:
func = dirscan;
break;
default:
errexit("unknown inodesc type %d in iblock()", idesc->id_type);
}
if (chkrange(idesc->id_blkno, idesc->id_numfrags)) {
return ((idesc->id_type == ACL) ? STOP : SKIP);
}
bp = getdatablk(idesc->id_blkno, (size_t)sblock.fs_bsize);
if (bp->b_errs != 0) {
brelse(bp);
return (SKIP);
}
ilevel--;
for (fsbperindirb = 1, i = 0; i < ilevel; i++) {
fsbperindirb *= (u_offset_t)NINDIR(&sblock);
}
nif = (offset_t)howmany(iblks, fsbperindirb);
if (nif > NINDIR(&sblock))
nif = NINDIR(&sblock);
else if (nif < 0)
nif = 0;
if (nif < NINDIR(&sblock) && (idesc->id_func == pass1check ||
idesc->id_func == pass3bcheck)) {
for (i = nif; i < NINDIR(&sblock); i++) {
if (bp->b_un.b_indir[i] == 0)
continue;
(void) sprintf(buf, "PARTIALLY TRUNCATED INODE I=%lu",
(ulong_t)idesc->id_number);
if (preen) {
pfatal(buf);
} else if (dofix(idesc, buf)) {
freeblk(idesc->id_number,
bp->b_un.b_indir[i],
sblock.fs_frag);
bp->b_un.b_indir[i] = 0;
dirty(bp);
}
}
flush(fswritefd, bp);
}
last_lbn = howmany(idesc->id_filesize, sblock.fs_bsize);
for (i = 0; i < nif; i++) {
if (ilevel == 0)
idesc->id_lbn++;
if (bp->b_un.b_indir[i] != 0) {
idesc->id_blkno = bp->b_un.b_indir[i];
if (ilevel > 0) {
n = iblock(idesc, ilevel, iblks, action);
iblks -= fsbperindirb;
} else {
n = (*func)(idesc);
}
if ((action == CKI_TRUNCATE) &&
(idesc->id_truncto >= 0) &&
(idesc->id_lbn >= idesc->id_truncto)) {
freeblk(idesc->id_number, bp->b_un.b_indir[i],
sblock.fs_frag);
}
if ((n & STOP) && (action != CKI_TRUNCATE)) {
brelse(bp);
return (n);
}
} else {
if ((idesc->id_lbn < last_lbn) &&
(idesc->id_firsthole < 0)) {
idesc->id_firsthole = idesc->id_lbn;
}
if (idesc->id_type == DATA) {
brelse(bp);
return ((n & ~KEEPON) | STOP);
}
}
}
brelse(bp);
return (KEEPON);
}
int
chkrange(daddr32_t blk, int cnt)
{
int c;
if (cnt <= 0 || blk <= 0 || ((unsigned)blk >= (unsigned)maxfsblock) ||
((cnt - 1) > (maxfsblock - blk))) {
if (debug)
(void) printf(
"Bad fragment range: should be 1 <= %d..%d < %d\n",
blk, blk + cnt, maxfsblock);
return (1);
}
if ((cnt > sblock.fs_frag) ||
((fragnum(&sblock, blk) + cnt) > sblock.fs_frag)) {
if (debug)
(void) printf("Bad fragment size: size %d\n", cnt);
return (1);
}
c = dtog(&sblock, blk);
if (blk < cgdmin(&sblock, c)) {
if ((unsigned)(blk + cnt) > (unsigned)cgsblock(&sblock, c)) {
if (debug)
(void) printf(
"Bad fragment position: %d..%d spans start of cg metadata\n",
blk, blk + cnt);
return (1);
}
} else {
if ((unsigned)(blk + cnt) > (unsigned)cgbase(&sblock, c+1)) {
if (debug)
(void) printf(
"Bad frag pos: %d..%d crosses end of cg\n",
blk, blk + cnt);
return (1);
}
}
return (0);
}
static fsck_ino_t startinum = -1;
struct dinode *
ginode(fsck_ino_t inum)
{
daddr32_t iblk;
struct dinode *dp;
if (inum < UFSROOTINO || inum > maxino) {
errexit("bad inode number %d to ginode\n", inum);
}
if (startinum == -1 ||
pbp == NULL ||
inum < startinum ||
inum >= (fsck_ino_t)(startinum + (fsck_ino_t)INOPB(&sblock))) {
iblk = itod(&sblock, inum);
if (pbp != NULL) {
brelse(pbp);
}
pbp = getdatablk(iblk, (size_t)sblock.fs_bsize);
startinum =
(fsck_ino_t)((inum / INOPB(&sblock)) * INOPB(&sblock));
}
dp = &pbp->b_un.b_dinode[inum % INOPB(&sblock)];
if (dp->di_suid != UID_LONG)
dp->di_uid = dp->di_suid;
if (dp->di_sgid != GID_LONG)
dp->di_gid = dp->di_sgid;
return (dp);
}
static fsck_ino_t nextino, lastinum;
static int64_t readcnt, readpercg, fullcnt, inobufsize;
static int64_t partialcnt, partialsize;
static size_t lastsize;
static struct dinode *inodebuf;
static diskaddr_t currentdblk;
static struct dinode *currentinode;
struct dinode *
getnextinode(fsck_ino_t inum)
{
size_t size;
diskaddr_t dblk;
static struct dinode *dp;
if (inum != nextino++ || inum > maxino)
errexit("bad inode number %d to nextinode\n", inum);
if (inum >= lastinum) {
readcnt++;
dblk = fsbtodb(&sblock, itod(&sblock, lastinum));
currentdblk = dblk;
if (readcnt % readpercg == 0) {
if (partialsize > SIZE_MAX)
errexit(
"Internal error: partialsize overflow");
size = (size_t)partialsize;
lastinum += partialcnt;
} else {
if (inobufsize > SIZE_MAX)
errexit("Internal error: inobufsize overflow");
size = (size_t)inobufsize;
lastinum += fullcnt;
}
(void) fsck_bread(fsreadfd, (caddr_t)inodebuf, dblk, size);
lastsize = size;
dp = inodebuf;
}
currentinode = dp;
return (dp++);
}
struct dinode *
getnextrefresh(void)
{
if (inodebuf == NULL) {
return (NULL);
}
inoflush();
(void) fsck_bread(fsreadfd, (caddr_t)inodebuf, currentdblk, lastsize);
return (currentinode);
}
void
resetinodebuf(void)
{
startinum = 0;
nextino = 0;
lastinum = 0;
readcnt = 0;
inobufsize = blkroundup(&sblock, INOBUFSIZE);
fullcnt = inobufsize / sizeof (struct dinode);
readpercg = sblock.fs_ipg / fullcnt;
partialcnt = sblock.fs_ipg % fullcnt;
partialsize = partialcnt * sizeof (struct dinode);
if (partialcnt != 0) {
readpercg++;
} else {
partialcnt = fullcnt;
partialsize = inobufsize;
}
if (inodebuf == NULL &&
(inodebuf = (struct dinode *)malloc((unsigned)inobufsize)) == NULL)
errexit("Cannot allocate space for inode buffer\n");
while (nextino < UFSROOTINO)
(void) getnextinode(nextino);
}
void
freeinodebuf(void)
{
if (inodebuf != NULL) {
free((void *)inodebuf);
}
inodebuf = NULL;
}
void
cacheino(struct dinode *dp, fsck_ino_t inum)
{
struct inoinfo *inp;
struct inoinfo **inpp;
uint_t blks;
blks = NDADDR + NIADDR;
inp = (struct inoinfo *)
malloc(sizeof (*inp) + (blks - 1) * sizeof (daddr32_t));
if (inp == NULL)
errexit("Cannot increase directory list\n");
init_inoinfo(inp, dp, inum);
inpp = &inphead[inum % numdirs];
inp->i_nextlist = *inpp;
*inpp = inp;
inp->i_number = inum;
if (inplast == listmax) {
listmax += 100;
inpsort = (struct inoinfo **)realloc((void *)inpsort,
(unsigned)listmax * sizeof (struct inoinfo *));
if (inpsort == NULL)
errexit("cannot increase directory list");
}
inpsort[inplast++] = inp;
}
struct inoinfo *
getinoinfo(fsck_ino_t inum)
{
struct inoinfo *inp;
inp = search_cache(inphead[inum % numdirs], inum);
return (inp);
}
int
inocached(fsck_ino_t inum)
{
return (search_cache(inphead[inum % numdirs], inum) != NULL);
}
void
inocleanup(void)
{
struct inoinfo **inpp;
if (inphead == NULL)
return;
for (inpp = &inpsort[inplast - 1]; inpp >= inpsort; inpp--) {
free((void *)(*inpp));
}
free((void *)inphead);
free((void *)inpsort);
inphead = inpsort = NULL;
}
void
cacheacl(struct dinode *dp, fsck_ino_t inum)
{
struct inoinfo *aclp;
struct inoinfo **aclpp;
uint_t blks;
blks = NDADDR + NIADDR;
aclp = (struct inoinfo *)
malloc(sizeof (*aclp) + (blks - 1) * sizeof (daddr32_t));
if (aclp == NULL)
return;
aclpp = &aclphead[inum % numacls];
aclp->i_nextlist = *aclpp;
*aclpp = aclp;
aclp->i_number = inum;
aclp->i_isize = (offset_t)dp->di_size;
aclp->i_blkssize = (size_t)(blks * sizeof (daddr32_t));
(void) memmove(&aclp->i_blks[0], &dp->di_db[0], aclp->i_blkssize);
if (aclplast == aclmax) {
aclmax += 100;
aclpsort = (struct inoinfo **)realloc((char *)aclpsort,
(unsigned)aclmax * sizeof (struct inoinfo *));
if (aclpsort == NULL)
errexit("cannot increase acl list");
}
aclpsort[aclplast++] = aclp;
}
static struct inoinfo *
search_cache(struct inoinfo *element, fsck_ino_t key)
{
while (element != NULL) {
if (element->i_number == key)
break;
element = element->i_nextlist;
}
return (element);
}
void
inodirty(void)
{
dirty(pbp);
}
static void
inoflush(void)
{
if (pbp != NULL)
flush(fswritefd, pbp);
}
void
clri(struct inodesc *idesc, char *type, int verbose, int corrupting)
{
int need_parent;
struct dinode *dp;
if (statemap[idesc->id_number] == USTATE)
return;
dp = ginode(idesc->id_number);
if (verbose == CLRI_VERBOSE) {
pwarn("%s %s", type, file_id(idesc->id_number, dp->di_mode));
pinode(idesc->id_number);
}
if (preen || (reply("CLEAR") == 1)) {
need_parent = (corrupting == CLRI_NOP_OK) ?
TI_NOPARENT : TI_PARENT;
freeino(idesc->id_number, need_parent);
if (preen)
(void) printf(" (CLEARED)\n");
remove_orphan_dir(idesc->id_number);
} else if (corrupting == CLRI_NOP_CORRUPT) {
iscorrupt = 1;
}
(void) printf("\n");
}
int
findname(struct inodesc *idesc)
{
struct direct *dirp = idesc->id_dirp;
if (dirp->d_ino != idesc->id_parent)
return (KEEPON);
(void) memmove(idesc->id_name, dirp->d_name,
MIN(dirp->d_namlen, MAXNAMLEN) + 1);
return (STOP|FOUND);
}
int
findino(struct inodesc *idesc)
{
struct direct *dirp = idesc->id_dirp;
if (dirp->d_ino == 0)
return (KEEPON);
if (strcmp(dirp->d_name, idesc->id_name) == 0 &&
dirp->d_ino >= UFSROOTINO && dirp->d_ino <= maxino) {
idesc->id_parent = dirp->d_ino;
return (STOP|FOUND);
}
return (KEEPON);
}
int
cleardirentry(fsck_ino_t parentdir, fsck_ino_t target)
{
struct inodesc idesc;
struct dinode *dp;
dp = ginode(parentdir);
init_inodesc(&idesc);
idesc.id_func = clearanentry;
idesc.id_parent = target;
idesc.id_type = DATA;
idesc.id_fix = NOFIX;
return (ckinode(dp, &idesc, CKI_TRAVERSE));
}
static int
clearanentry(struct inodesc *idesc)
{
struct direct *dirp = idesc->id_dirp;
if (dirp->d_ino != idesc->id_parent || idesc->id_entryno < 2) {
idesc->id_entryno++;
return (KEEPON);
}
dirp->d_ino = 0;
return (STOP|FOUND|ALTERED);
}
void
pinode(fsck_ino_t ino)
{
struct dinode *dp;
(void) printf(" I=%lu ", (ulong_t)ino);
if (ino < UFSROOTINO || ino > maxino)
return;
dp = ginode(ino);
pdinode(dp);
}
static void
pdinode(struct dinode *dp)
{
char *p;
struct passwd *pw;
time_t t;
(void) printf(" OWNER=");
if ((pw = getpwuid((int)dp->di_uid)) != 0)
(void) printf("%s ", pw->pw_name);
else
(void) printf("%lu ", (ulong_t)dp->di_uid);
(void) printf("MODE=%o\n", dp->di_mode);
if (preen)
(void) printf("%s: ", devname);
(void) printf("SIZE=%lld ", (longlong_t)dp->di_size);
t = (time_t)dp->di_mtime;
p = ctime(&t);
(void) printf("MTIME=%12.12s %4.4s ", p + 4, p + 20);
}
void
blkerror(fsck_ino_t ino, char *type, daddr32_t blk, daddr32_t lbn)
{
pfatal("FRAGMENT %d %s I=%u LFN %d", blk, type, ino, lbn);
(void) printf("\n");
switch (statemap[ino] & ~INDELAYD) {
case FSTATE:
case FZLINK:
statemap[ino] = FCLEAR;
return;
case DFOUND:
case DSTATE:
case DZLINK:
statemap[ino] = DCLEAR;
add_orphan_dir(ino);
return;
case SSTATE:
statemap[ino] = SCLEAR;
return;
case FCLEAR:
case DCLEAR:
case SCLEAR:
return;
default:
errexit("BAD STATE 0x%x TO BLKERR\n", statemap[ino]);
}
}
fsck_ino_t
allocino(fsck_ino_t request, int type)
{
fsck_ino_t ino;
struct dinode *dp;
struct cg *cgp = &cgrp;
int cg;
time_t t;
caddr_t err;
if (debug && (request != 0) && (request != UFSROOTINO))
errexit("assertion failed: allocino() asked for "
"inode %d instead of 0 or %d",
(int)request, (int)UFSROOTINO);
if (request == 0)
request = UFSROOTINO;
else if (statemap[request] != USTATE)
return (0);
for (ino = request; ino < maxino; ino++)
if (statemap[ino] == USTATE)
break;
if (ino == maxino)
return (0);
cg = itog(&sblock, ino);
(void) getblk(&cgblk, cgtod(&sblock, cg), (size_t)sblock.fs_cgsize);
err = cg_sanity(cgp, cg);
if (err != NULL) {
pfatal("CG %d: %s\n", cg, err);
free((void *)err);
if (reply("REPAIR") == 0)
errexit("Program terminated.");
fix_cg(cgp, cg);
}
setbit(cg_inosused(cgp), ino % sblock.fs_ipg);
cgp->cg_cs.cs_nifree--;
cgdirty();
if (lastino < ino)
lastino = ino;
switch (type & IFMT) {
case IFDIR:
statemap[ino] = DSTATE;
cgp->cg_cs.cs_ndir++;
break;
case IFREG:
case IFLNK:
statemap[ino] = FSTATE;
break;
default:
initbarea(&cgblk);
if (debug)
(void) printf("allocino: unknown type 0%o\n",
type & IFMT);
return (0);
}
dp = ginode(ino);
(void) memset((void *)dp, 0, sizeof (struct dinode));
dp->di_db[0] = allocblk(1);
if (dp->di_db[0] == 0) {
statemap[ino] = USTATE;
return (0);
}
dp->di_mode = (mode_t)type;
(void) time(&t);
dp->di_atime = (time32_t)t;
dp->di_ctime = dp->di_atime;
dp->di_mtime = dp->di_ctime;
dp->di_size = (u_offset_t)sblock.fs_fsize;
dp->di_blocks = btodb(sblock.fs_fsize);
n_files++;
inodirty();
return (ino);
}
void
truncino(fsck_ino_t ino, offset_t new_length, int update)
{
struct inodesc idesc;
struct inoinfo *iip;
struct dinode *dp;
fsck_ino_t parent;
mode_t mode;
caddr_t message;
int isdir, islink;
int ilevel, dblk;
dp = ginode(ino);
mode = (dp->di_mode & IFMT);
isdir = (mode == IFDIR) || (mode == IFATTRDIR);
islink = (mode == IFLNK);
if (isdir) {
if (update == TI_NOPARENT) {
parent = -1;
} else {
iip = getinoinfo(ino);
if (iip != NULL) {
parent = iip->i_parent;
} else {
parent = lookup_dotdot_ino(ino);
if (parent != 0) {
dp = ginode(parent);
idesc.id_name = lfname;
idesc.id_type = DATA;
idesc.id_func = findino;
idesc.id_number = ino;
idesc.id_fix = DONTKNOW;
if ((ckinode(dp, &idesc,
CKI_TRAVERSE) & FOUND) == 0)
parent = 0;
}
}
}
mark_delayed_inodes(ino, numfrags(&sblock, new_length));
if (parent > 0) {
dp = ginode(parent);
LINK_RANGE(message, dp->di_nlink, -1);
if (message != NULL) {
LINK_CLEAR(message, parent, dp->di_mode,
&idesc);
if (statemap[parent] == USTATE)
goto no_parent_update;
}
TRACK_LNCNTP(parent, lncntp[parent]--);
} else if ((mode == IFDIR) && (parent == 0)) {
pwarn("Could not determine former parent of "
"inode %d, link counts are possibly\n"
"incorrect. Please rerun fsck(8) to "
"correct this.\n",
ino);
iscorrupt = 1;
}
}
no_parent_update:
init_inodesc(&idesc);
idesc.id_type = ADDR;
idesc.id_func = pass4check;
idesc.id_number = ino;
idesc.id_fix = DONTKNOW;
idesc.id_truncto = howmany(new_length, sblock.fs_bsize);
dp = ginode(ino);
if (!islink && ckinode(dp, &idesc, CKI_TRUNCATE) & ALTERED)
inodirty();
dp = ginode(ino);
dp->di_size = new_length;
for (dblk = idesc.id_truncto + 1; dblk < NDADDR; dblk++) {
dp->di_db[dblk] = 0;
}
ilevel = get_indir_offsets(-1, idesc.id_truncto, NULL, NULL);
for (ilevel++; ilevel < NIADDR; ilevel++) {
dp->di_ib[ilevel] = 0;
}
inodirty();
}
void
freeino(fsck_ino_t ino, int update_parent)
{
int cg;
struct dinode *dp;
struct cg *cgp;
n_files--;
dp = ginode(ino);
if (dp->di_size > (u_offset_t)MAXOFF_T &&
dp->di_size <= (u_offset_t)UFS_MAXOFFSET_T &&
ftypeok(dp) &&
(dp->di_mode & IFMT) != IFBLK &&
(dp->di_mode & IFMT) != IFCHR) {
largefile_count--;
}
truncino(ino, 0, update_parent);
dp = ginode(ino);
if ((dp->di_mode & IFMT) == IFATTRDIR) {
clearshadow(ino, &attrclientinfo);
dp = ginode(ino);
}
clearinode(dp);
inodirty();
statemap[ino] = USTATE;
cg = itog(&sblock, ino);
(void) getblk(&cgblk, (diskaddr_t)cgtod(&sblock, cg),
(size_t)sblock.fs_cgsize);
cgp = cgblk.b_un.b_cg;
clrbit(cg_inosused(cgp), ino % sblock.fs_ipg);
cgp->cg_cs.cs_nifree += 1;
cgdirty();
sblock.fs_cstotal.cs_nifree += 1;
sbdirty();
}
void
init_inoinfo(struct inoinfo *inp, struct dinode *dp, fsck_ino_t inum)
{
inp->i_parent = ((inum == UFSROOTINO) ? UFSROOTINO : (fsck_ino_t)0);
inp->i_dotdot = (fsck_ino_t)0;
inp->i_isize = (offset_t)dp->di_size;
inp->i_blkssize = (NDADDR + NIADDR) * sizeof (daddr32_t);
inp->i_extattr = dp->di_oeftflag;
(void) memmove((void *)&inp->i_blks[0], (void *)&dp->di_db[0],
inp->i_blkssize);
}
static int
lookup_dotdot_ino(fsck_ino_t ino)
{
struct inodesc idesc;
init_inodesc(&idesc);
idesc.id_type = DATA;
idesc.id_func = findino;
idesc.id_name = "..";
idesc.id_number = ino;
idesc.id_fix = NOFIX;
if ((ckinode(ginode(ino), &idesc, CKI_TRAVERSE) & FOUND) != 0) {
return (idesc.id_parent);
}
return (0);
}
int
lookup_named_ino(fsck_ino_t dir, caddr_t name)
{
struct inodesc idesc;
init_inodesc(&idesc);
idesc.id_type = DATA;
idesc.id_func = findino;
idesc.id_name = name;
idesc.id_number = dir;
idesc.id_fix = NOFIX;
if ((ckinode(ginode(dir), &idesc, CKI_TRAVERSE) & FOUND) != 0) {
return (idesc.id_parent);
}
return (0);
}
static int
mark_a_delayed_inode(struct inodesc *idesc)
{
struct direct *dirp = idesc->id_dirp;
if (idesc->id_lbn < idesc->id_parent) {
return (KEEPON);
}
if (dirp->d_ino != 0 &&
strcmp(dirp->d_name, ".") != 0 &&
strcmp(dirp->d_name, "..") != 0) {
statemap[dirp->d_ino] &= ~INFOUND;
statemap[dirp->d_ino] |= INDELAYD;
}
return (KEEPON);
}
static void
mark_delayed_inodes(fsck_ino_t ino, daddr32_t first_lfn)
{
struct dinode *dp;
struct inodesc idelayed;
init_inodesc(&idelayed);
idelayed.id_number = ino;
idelayed.id_type = DATA;
idelayed.id_fix = NOFIX;
idelayed.id_func = mark_a_delayed_inode;
idelayed.id_parent = first_lfn;
idelayed.id_entryno = 2;
dp = ginode(ino);
(void) ckinode(dp, &idelayed, CKI_TRAVERSE);
}
void
clearattrref(fsck_ino_t ino)
{
struct dinode *dp;
dp = ginode(ino);
if (debug) {
if (dp->di_oeftflag == 0)
(void) printf("clearattref: no attr to clear on %d\n",
ino);
}
dp->di_oeftflag = 0;
inodirty();
}