Exemplo n.º 1
0
/*
 * Propagate connected state through the tree.
 */
void
propagate(ino_t inumber)
{
	struct inoinfo *inp;

	inp = getinoinfo(inumber);

	for (;;) {
		inoinfo(inp->i_number)->ino_state = DMARK;
		if (inp->i_child &&
		    inoinfo(inp->i_child->i_number)->ino_state != DMARK)
			inp = inp->i_child;
		else if (inp->i_number == inumber)
			break;
		else if (inp->i_sibling)
			inp = inp->i_sibling;
		else
			inp = getinoinfo(inp->i_parent);
	}

	for (;;) {
		inoinfo(inp->i_number)->ino_state = DFOUND;
		if (inp->i_child &&
		    inoinfo(inp->i_child->i_number)->ino_state != DFOUND)
			inp = inp->i_child;
		else if (inp->i_number == inumber)
			break;
		else if (inp->i_sibling)
			inp = inp->i_sibling;
		else
			inp = getinoinfo(inp->i_parent);
	}
}
Exemplo n.º 2
0
/*
 * Propagate connected state through the tree.
 */
void
propagate(void)
{
	struct inoinfo **inpp, *inp, *pinp;
	struct inoinfo **inpend;

	/*
	 * Create a list of children for each directory.
	 */
	inpend = &inpsort[inplast];
	for (inpp = inpsort; inpp < inpend; inpp++) {
		inp = *inpp;
		if (inp->i_parent == 0 ||
		    inp->i_number == EXT2_ROOTINO)
			continue;
		pinp = getinoinfo(inp->i_parent);
		inp->i_parentp = pinp;
		inp->i_sibling = pinp->i_child;
		pinp->i_child = inp;
	}
	inp = getinoinfo(EXT2_ROOTINO);
	while (inp) {
		statemap[inp->i_number] = DFOUND;
		if (inp->i_child &&
		    statemap[inp->i_child->i_number] == DSTATE)
			inp = inp->i_child;
		else if (inp->i_sibling)
			inp = inp->i_sibling;
		else
			inp = inp->i_parentp;
	}
}
Exemplo n.º 3
0
void
reparent(ino_t inumber, ino_t parent)
{
	struct inoinfo *inp, *pinp;

	inp = getinoinfo(inumber);
	inp->i_parent = inp->i_dotdot = parent;
	pinp = getinoinfo(parent);
	inp->i_sibling = pinp->i_child;
	pinp->i_child = inp;
	propagate(inumber);
}
Exemplo n.º 4
0
pass3()
{
	register struct inoinfo **inpp, *inp;
	ino_t orphan;
	int loopcnt;

	for (inpp = &inpsort[inplast - 1]; inpp >= inpsort; inpp--) {
		inp = *inpp;
		if (inp->i_number == ROOTINO ||
		    !(inp->i_parent == 0 || statemap[inp->i_number] == DSTATE))
			continue;
		if (statemap[inp->i_number] == DCLEAR)
			continue;
		for (loopcnt = 0; ; loopcnt++) {
			orphan = inp->i_number;
			if (inp->i_parent == 0 ||
			    statemap[inp->i_parent] != DSTATE ||
			    loopcnt > numdirs)
				break;
			inp = getinoinfo(inp->i_parent);
		}
		(void)linkup(orphan, inp->i_dotdot);
		inp->i_parent = inp->i_dotdot = lfdir;
		lncntp[lfdir]--;
		statemap[orphan] = DFOUND;
		propagate();
	}
}
Exemplo n.º 5
0
/*
 * allocate a new directory
 */
ino_t
allocdir(ino_t parent, ino_t request, int mode)
{
	ino_t ino;
	char *cp;
	union dinode *dp;
	struct bufarea *bp;
	struct inoinfo *inp;
	struct dirtemplate *dirp;

	ino = allocino(request, IFDIR|mode);
	dirp = &dirhead;
	dirp->dot_ino = ino;
	dirp->dotdot_ino = parent;
	dp = ginode(ino);
	bp = getdirblk(DIP(dp, di_db[0]), sblock.fs_fsize);
	if (bp->b_errs) {
		freeino(ino);
		return (0);
	}
	memmove(bp->b_un.b_buf, dirp, sizeof(struct dirtemplate));
	for (cp = &bp->b_un.b_buf[DIRBLKSIZ];
	     cp < &bp->b_un.b_buf[sblock.fs_fsize];
	     cp += DIRBLKSIZ)
		memmove(cp, &emptydir, sizeof emptydir);
	dirty(bp);
	DIP_SET(dp, di_nlink, 2);
	inodirty();
	if (ino == ROOTINO) {
		inoinfo(ino)->ino_linkcnt = DIP(dp, di_nlink);
		cacheino(dp, ino);
		return(ino);
	}
	if (!INO_IS_DVALID(parent)) {
		freeino(ino);
		return (0);
	}
	cacheino(dp, ino);
	inp = getinoinfo(ino);
	inp->i_parent = parent;
	inp->i_dotdot = parent;
	inoinfo(ino)->ino_state = inoinfo(parent)->ino_state;
	if (inoinfo(ino)->ino_state == DSTATE) {
		inoinfo(ino)->ino_linkcnt = DIP(dp, di_nlink);
		inoinfo(parent)->ino_linkcnt++;
	}
	dp = ginode(parent);
	DIP_SET(dp, di_nlink, DIP(dp, di_nlink) + 1);
	inodirty();
	return (ino);
}
Exemplo n.º 6
0
/*
 * Propagate connected state through the tree.
 */
void
propagate(ino_t inumber)
{
	struct	inoinfo *inp;
	char	state;

	inp = getinoinfo(inumber);
	state = GET_ISTATE(inp->i_number);
	for (;;) {
		SET_ISTATE(inp->i_number, state);
		if (inp->i_child &&
		    GET_ISTATE(inp->i_child->i_number) != state)
			inp = inp->i_child;
		else if (inp->i_number == inumber)
			break;
		else if (inp->i_sibling)
			inp = inp->i_sibling;
		else
			inp = inp->i_parentp;
	}
}
Exemplo n.º 7
0
/*
 * Scan each entry in a directory block.
 */
int
dirscan(struct inodesc *idesc)
{
	struct direct *dp;
	struct bufarea *bp;
	int dsize, n;
	long blksiz;
#if DIRBLKSIZ > APPLEUFS_DIRBLKSIZ
	char dbuf[DIRBLKSIZ];
#else
	char dbuf[APPLEUFS_DIRBLKSIZ];
#endif

	if (idesc->id_type != DATA)
		errexit("wrong type to dirscan %d", idesc->id_type);
	if (idesc->id_entryno == 0 &&
	    (idesc->id_filesize & (dirblksiz - 1)) != 0)
		idesc->id_filesize = roundup(idesc->id_filesize, dirblksiz);
	blksiz = idesc->id_numfrags * sblock->fs_fsize;
	if (chkrange(idesc->id_blkno, idesc->id_numfrags)) {
		idesc->id_filesize -= blksiz;
		return (SKIP);
	}

	/*
	 * If we are are swapping byte order in directory entries, just swap
	 * this block and return.
	 */
	if (do_dirswap) {
		int off;
		bp = getdirblk(idesc->id_blkno, blksiz);
		for (off = 0; off < blksiz; off += iswap16(dp->d_reclen)) {
			dp = (struct direct *)(bp->b_un.b_buf + off);
			dp->d_ino = bswap32(dp->d_ino);
			dp->d_reclen = bswap16(dp->d_reclen);
			if (!newinofmt) {
				u_int8_t tmp = dp->d_namlen;
				dp->d_namlen = dp->d_type;
				dp->d_type = tmp;
			}
			if (dp->d_reclen == 0)
				break;
		}
		dirty(bp);
		idesc->id_filesize -= blksiz;
		return (idesc->id_filesize > 0 ? KEEPON : STOP);
	}

	idesc->id_loc = 0;
	for (dp = fsck_readdir(idesc); dp != NULL; dp = fsck_readdir(idesc)) {
		dsize = iswap16(dp->d_reclen);
		if (dsize > sizeof dbuf)
			dsize = sizeof dbuf;
		memmove(dbuf, dp, (size_t)dsize);
#		if (BYTE_ORDER == LITTLE_ENDIAN)
			if (!newinofmt && !needswap) {
#		else
			if (!newinofmt && needswap) {
#		endif
				struct direct *tdp = (struct direct *)dbuf;
				u_char tmp;

				tmp = tdp->d_namlen;
				tdp->d_namlen = tdp->d_type;
				tdp->d_type = tmp;
			}
		idesc->id_dirp = (struct direct *)dbuf;
		if ((n = (*idesc->id_func)(idesc)) & ALTERED) {
#			if (BYTE_ORDER == LITTLE_ENDIAN)
				if (!newinofmt && !doinglevel2 && !needswap) {
#			else
				if (!newinofmt && !doinglevel2 && needswap) {
#			endif
					struct direct *tdp;
					u_char tmp;

					tdp = (struct direct *)dbuf;
					tmp = tdp->d_namlen;
					tdp->d_namlen = tdp->d_type;
					tdp->d_type = tmp;
				}
			bp = getdirblk(idesc->id_blkno, blksiz);
			memmove(bp->b_un.b_buf + idesc->id_loc - dsize, dbuf,
			    (size_t)dsize);
			dirty(bp);
			sbdirty();
		}
		if (n & STOP) 
			return (n);
	}
	return (idesc->id_filesize > 0 ? KEEPON : STOP);
}

/*
 * get next entry in a directory.
 */
static struct direct *
fsck_readdir(struct inodesc *idesc)
{
	struct direct *dp, *ndp;
	struct bufarea *bp;
	long size, blksiz, fix, dploc;

	blksiz = idesc->id_numfrags * sblock->fs_fsize;
	bp = getdirblk(idesc->id_blkno, blksiz);
	if (idesc->id_loc % dirblksiz == 0 && idesc->id_filesize > 0 &&
	    idesc->id_loc < blksiz) {
		dp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc);
		if (dircheck(idesc, dp))
			goto dpok;
		if (idesc->id_fix == IGNORE)
			return (0);
		fix = dofix(idesc, "DIRECTORY CORRUPTED");
		bp = getdirblk(idesc->id_blkno, blksiz);
		dp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc);
		dp->d_reclen = iswap16(dirblksiz);
		dp->d_ino = 0;
		dp->d_type = 0;
		dp->d_namlen = 0;
		dp->d_name[0] = '\0';
		if (fix)
			dirty(bp);
		else 
			markclean = 0;
		idesc->id_loc += dirblksiz;
		idesc->id_filesize -= dirblksiz;
		return (dp);
	}
dpok:
	if (idesc->id_filesize <= 0 || idesc->id_loc >= blksiz)
		return NULL;
	dploc = idesc->id_loc;
	dp = (struct direct *)(bp->b_un.b_buf + dploc);
	idesc->id_loc += iswap16(dp->d_reclen);
	idesc->id_filesize -= iswap16(dp->d_reclen);
	if ((idesc->id_loc % dirblksiz) == 0)
		return (dp);
	ndp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc);
	if (idesc->id_loc < blksiz && idesc->id_filesize > 0 &&
	    dircheck(idesc, ndp) == 0) {
		size = dirblksiz - (idesc->id_loc % dirblksiz);
		idesc->id_loc += size;
		idesc->id_filesize -= size;
		if (idesc->id_fix == IGNORE)
			return (0);
		fix = dofix(idesc, "DIRECTORY CORRUPTED");
		bp = getdirblk(idesc->id_blkno, blksiz);
		dp = (struct direct *)(bp->b_un.b_buf + dploc);
		dp->d_reclen = iswap16(iswap16(dp->d_reclen) + size);
		if (fix)
			dirty(bp);
		else 
			markclean = 0;
	}
	return (dp);
}

/*
 * Verify that a directory entry is valid.
 * This is a superset of the checks made in the kernel.
 */
static int
dircheck(struct inodesc *idesc, struct direct *dp)
{
	int size;
	char *cp;
	u_char namlen, type;
	int spaceleft;

	spaceleft = dirblksiz - (idesc->id_loc % dirblksiz);
	if (iswap32(dp->d_ino) >= maxino ||
	    dp->d_reclen == 0 ||
	    iswap16(dp->d_reclen) > spaceleft ||
	    (iswap16(dp->d_reclen) & 0x3) != 0) 
		return (0);
	if (dp->d_ino == 0)
		return (1);
	size = DIRSIZ(!newinofmt, dp, needswap);
#	if (BYTE_ORDER == LITTLE_ENDIAN)
		if (!newinofmt && !needswap) {
#	else
		if (!newinofmt && needswap) {
#	endif
			type = dp->d_namlen;
			namlen = dp->d_type;
		} else {
			namlen = dp->d_namlen;
			type = dp->d_type;
		}
	if (iswap16(dp->d_reclen) < size ||
	    idesc->id_filesize < size ||
	    /* namlen > MAXNAMLEN || */
	    type > 15)
		return (0);
	for (cp = dp->d_name, size = 0; size < namlen; size++)
		if (*cp == '\0' || (*cp++ == '/'))
			return (0);
	if (*cp != '\0')
		return (0);
	return (1);
}

void
direrror(ino_t ino, const char *errmesg)
{

	fileerror(ino, ino, errmesg);
}

void
fileerror(ino_t cwd, ino_t ino, const char *errmesg)
{
	union dinode *dp;
	char pathbuf[MAXPATHLEN + 1];
	uint16_t mode;

	pwarn("%s ", errmesg);
	pinode(ino);
	printf("\n");
	getpathname(pathbuf, sizeof(pathbuf), cwd, ino);
	if (ino < ROOTINO || ino > maxino) {
		pfatal("NAME=%s\n", pathbuf);
		return;
	}
	dp = ginode(ino);
	if (ftypeok(dp)) {
		mode = DIP(dp, mode);
		pfatal("%s=%s\n",
		    (iswap16(mode) & IFMT) == IFDIR ? "DIR" : "FILE", pathbuf);
	}
	else
		pfatal("NAME=%s\n", pathbuf);
}

void
adjust(struct inodesc *idesc, int lcnt)
{
	union dinode *dp;
	int16_t nlink;
	int saveresolved;

	dp = ginode(idesc->id_number);
	nlink = iswap16(DIP(dp, nlink));
	if (nlink == lcnt) {
		/*
		 * If we have not hit any unresolved problems, are running
		 * in preen mode, and are on a file system using soft updates,
		 * then just toss any partially allocated files.
		 */
		if (resolved && preen && usedsoftdep) {
			clri(idesc, "UNREF", 1);
			return;
		} else {
			/*
			 * The file system can be marked clean even if
			 * a file is not linked up, but is cleared.
			 * Hence, resolved should not be cleared when
			 * linkup is answered no, but clri is answered yes.
			 */
			saveresolved = resolved;
			if (linkup(idesc->id_number, (ino_t)0, NULL) == 0) {
				resolved = saveresolved;
				clri(idesc, "UNREF", 0);
				return;
			}
			/*
			 * Account for the new reference created by linkup().
			 */
			dp = ginode(idesc->id_number);
			lcnt--;
		}
	}
	if (lcnt != 0) {
		pwarn("LINK COUNT %s", (lfdir == idesc->id_number) ? lfname :
			((iswap16(DIP(dp, mode)) & IFMT) == IFDIR ?
			"DIR" : "FILE"));
		pinode(idesc->id_number);
		printf(" COUNT %d SHOULD BE %d",
			nlink, nlink - lcnt);
		if (preen || usedsoftdep) {
			if (lcnt < 0) {
				printf("\n");
				pfatal("LINK COUNT INCREASING");
			}
			if (preen)
				printf(" (ADJUSTED)\n");
		}
		if (preen || reply("ADJUST") == 1) {
			DIP_SET(dp, nlink, iswap16(nlink - lcnt));
			inodirty();
		} else 
			markclean = 0;
	}
}

static int
mkentry(struct inodesc *idesc)
{
	struct direct *dirp = idesc->id_dirp;
	struct direct newent;
	int newlen, oldlen;

	newent.d_namlen = strlen(idesc->id_name);
	newlen = DIRSIZ(0, &newent, 0);
	if (dirp->d_ino != 0)
		oldlen = DIRSIZ(0, dirp, 0);
	else
		oldlen = 0;
	if (iswap16(dirp->d_reclen) - oldlen < newlen)
		return (KEEPON);
	newent.d_reclen = iswap16(iswap16(dirp->d_reclen) - oldlen);
	dirp->d_reclen = iswap16(oldlen);
	dirp = (struct direct *)(((char *)dirp) + oldlen);
	/* ino to be entered is in id_parent */
	dirp->d_ino = iswap32(idesc->id_parent);
	dirp->d_reclen = newent.d_reclen;
	if (newinofmt)
		dirp->d_type = inoinfo(idesc->id_parent)->ino_type;
	else
		dirp->d_type = 0;
	dirp->d_namlen = newent.d_namlen;
	memmove(dirp->d_name, idesc->id_name, (size_t)newent.d_namlen + 1);
#	if (BYTE_ORDER == LITTLE_ENDIAN)
		/*
		 * If the entry was split, dirscan() will only reverse the byte
		 * order of the original entry, and not the new one, before
		 * writing it back out.  So, we reverse the byte order here if
		 * necessary.
		 */
		if (oldlen != 0 && !newinofmt && !doinglevel2 && !needswap) {
#	else
		if (oldlen != 0 && !newinofmt && !doinglevel2 && needswap) {
#	endif
			u_char tmp;

			tmp = dirp->d_namlen;
			dirp->d_namlen = dirp->d_type;
			dirp->d_type = tmp;
		}
	return (ALTERED|STOP);
}

static int
chgino(struct inodesc *idesc)
{
	struct direct *dirp = idesc->id_dirp;

	if (memcmp(dirp->d_name, idesc->id_name, (int)dirp->d_namlen + 1))
		return (KEEPON);
	dirp->d_ino = iswap32(idesc->id_parent);
	if (newinofmt)
		dirp->d_type = inoinfo(idesc->id_parent)->ino_type;
	else
		dirp->d_type = 0;
	return (ALTERED|STOP);
}

int
linkup(ino_t orphan, ino_t parentdir, char *name)
{
	union dinode *dp;
	int lostdir;
	ino_t oldlfdir;
	struct inodesc idesc;
	char tempname[BUFSIZ];
	int16_t nlink;
	uint16_t mode;

	memset(&idesc, 0, sizeof(struct inodesc));
	dp = ginode(orphan);
	mode = iswap16(DIP(dp, mode));
	nlink = iswap16(DIP(dp, nlink));
	lostdir = (mode & IFMT) == IFDIR;
	pwarn("UNREF %s ", lostdir ? "DIR" : "FILE");
	pinode(orphan);
	if (preen  && DIP(dp, size) == 0)
		return (0);
	if (preen)
		printf(" (RECONNECTED)\n");
	else
		if (reply("RECONNECT") == 0) {
			markclean = 0;
			return (0);
		}
	if (parentdir != 0)
		inoinfo(parentdir)->ino_linkcnt++;
	if (lfdir == 0) {
		dp = ginode(ROOTINO);
		idesc.id_name = lfname;
		idesc.id_type = DATA;
		idesc.id_func = findino;
		idesc.id_number = ROOTINO;
		if ((ckinode(dp, &idesc) & FOUND) != 0) {
			lfdir = idesc.id_parent;
		} else {
			pwarn("NO lost+found DIRECTORY");
			if (preen || reply("CREATE")) {
				lfdir = allocdir(ROOTINO, (ino_t)0, lfmode);
				if (lfdir != 0) {
					if (makeentry(ROOTINO, lfdir, lfname) != 0) {
						numdirs++;
						if (preen)
							printf(" (CREATED)\n");
					} else {
						freedir(lfdir, ROOTINO);
						lfdir = 0;
						if (preen)
							printf("\n");
					}
				}
				if (lfdir != 0) {
					reparent(lfdir, ROOTINO);
				}
			}
		}
		if (lfdir == 0) {
			pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n");
			markclean = 0;
			return (0);
		}
	}
	dp = ginode(lfdir);
	mode = DIP(dp, mode);
	mode = iswap16(mode);
	if ((mode & IFMT) != IFDIR) {
		pfatal("lost+found IS NOT A DIRECTORY");
		if (reply("REALLOCATE") == 0) {
			markclean = 0;
			return (0);
		}
		oldlfdir = lfdir;
		lfdir = allocdir(ROOTINO, (ino_t)0, lfmode);
		if (lfdir == 0) {
			pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n");
			markclean = 0;
			return (0);
		}
		if ((changeino(ROOTINO, lfname, lfdir) & ALTERED) == 0) {
			pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n");
			markclean = 0;
			return (0);
		}
		inodirty();
		reparent(lfdir, ROOTINO);
		idesc.id_type = ADDR;
		idesc.id_func = pass4check;
		idesc.id_number = oldlfdir;
		adjust(&idesc, inoinfo(oldlfdir)->ino_linkcnt + 1);
		inoinfo(oldlfdir)->ino_linkcnt = 0;
		dp = ginode(lfdir);
	}
	if (inoinfo(lfdir)->ino_state != DFOUND) {
		pfatal("SORRY. NO lost+found DIRECTORY\n\n");
		markclean = 0;
		return (0);
	}
	(void)lftempname(tempname, orphan);
	if (makeentry(lfdir, orphan, (name ? name : tempname)) == 0) {
		pfatal("SORRY. NO SPACE IN lost+found DIRECTORY");
		printf("\n\n");
		markclean = 0;
		return (0);
	}
	inoinfo(orphan)->ino_linkcnt--;
	if (lostdir) {
		if ((changeino(orphan, "..", lfdir) & ALTERED) == 0 &&
		    parentdir != (ino_t)-1)
			(void)makeentry(orphan, lfdir, "..");
		dp = ginode(lfdir);
		nlink = DIP(dp, nlink);
		DIP_SET(dp, nlink, iswap16(iswap16(nlink) + 1));
		inodirty();
		inoinfo(lfdir)->ino_linkcnt++;
		reparent(orphan, lfdir);
		pwarn("DIR I=%llu CONNECTED. ", (unsigned long long)orphan);
		if (parentdir != (ino_t)-1)
			printf("PARENT WAS I=%llu\n",
			    (unsigned long long)parentdir);
		if (preen == 0)
			printf("\n");
	}
	return (1);
}

/*
 * fix an entry in a directory.
 */
int
changeino(ino_t dir, const char *name, ino_t newnum)
{
	struct inodesc idesc;

	memset(&idesc, 0, sizeof(struct inodesc));
	idesc.id_type = DATA;
	idesc.id_func = chgino;
	idesc.id_number = dir;
	idesc.id_fix = DONTKNOW;
	idesc.id_name = name;
	idesc.id_parent = newnum;	/* new value for name */
	return (ckinode(ginode(dir), &idesc));
}

/*
 * make an entry in a directory
 */
int
makeentry(ino_t parent, ino_t ino, const char *name)
{
	union dinode *dp;
	struct inodesc idesc;
	char pathbuf[MAXPATHLEN + 1];
	
	if (parent < ROOTINO || parent >= maxino ||
	    ino < ROOTINO || ino >= maxino)
		return (0);
	memset(&idesc, 0, sizeof(struct inodesc));
	idesc.id_type = DATA;
	idesc.id_func = mkentry;
	idesc.id_number = parent;
	idesc.id_parent = ino;	/* this is the inode to enter */
	idesc.id_fix = DONTKNOW;
	idesc.id_name = name;
	dp = ginode(parent);
	if (iswap64(DIP(dp, size)) % dirblksiz) {
		DIP_SET(dp, size,
		    iswap64(roundup(iswap64(DIP(dp, size)), dirblksiz)));
		inodirty();
	}
	if ((ckinode(dp, &idesc) & ALTERED) != 0)
		return (1);
	getpathname(pathbuf, sizeof(pathbuf), parent, parent);
	dp = ginode(parent);
	if (expanddir(dp, pathbuf) == 0)
		return (0);
	return (ckinode(dp, &idesc) & ALTERED);
}

/*
 * Attempt to expand the size of a directory
 */
static int
expanddir(union dinode *dp, char *name)
{
	daddr_t lastbn, newblk, dirblk;
	struct bufarea *bp;
	char *cp;
#if DIRBLKSIZ > APPLEUFS_DIRBLKSIZ
	char firstblk[DIRBLKSIZ];
#else
	char firstblk[APPLEUFS_DIRBLKSIZ];
#endif
	struct ufs1_dinode *dp1 = NULL;
	struct ufs2_dinode *dp2 = NULL;

	if (is_ufs2)
		dp2 = &dp->dp2;
	else
		dp1 = &dp->dp1;

	lastbn = lblkno(sblock, iswap64(DIP(dp, size)));
	if (lastbn >= NDADDR - 1 || DIP(dp, db[lastbn]) == 0 ||
	    DIP(dp, size) == 0)
		return (0);
	if ((newblk = allocblk(sblock->fs_frag)) == 0)
		return (0);
	if (is_ufs2) {
		dp2->di_db[lastbn + 1] = dp2->di_db[lastbn];
		dp2->di_db[lastbn] = iswap64(newblk);
		dp2->di_size = iswap64(iswap64(dp2->di_size)+sblock->fs_bsize);
		dp2->di_blocks = iswap64(iswap64(dp2->di_blocks) +
		    btodb(sblock->fs_bsize));
		dirblk = iswap64(dp2->di_db[lastbn + 1]);
	} else {
		dp1->di_db[lastbn + 1] = dp1->di_db[lastbn];
		dp1->di_db[lastbn] = iswap32((int32_t)newblk);
		dp1->di_size = iswap64(iswap64(dp1->di_size)+sblock->fs_bsize);
		dp1->di_blocks = iswap32(iswap32(dp1->di_blocks) +
		    btodb(sblock->fs_bsize));
		dirblk = iswap32(dp1->di_db[lastbn + 1]);
	}
	bp = getdirblk(dirblk, sblksize(sblock, DIP(dp, size), lastbn + 1));
	if (bp->b_errs)
		goto bad;
	memmove(firstblk, bp->b_un.b_buf, dirblksiz);
	bp = getdirblk(newblk, sblock->fs_bsize);
	if (bp->b_errs)
		goto bad;
	memmove(bp->b_un.b_buf, firstblk, dirblksiz);
	emptydir.dot_reclen = iswap16(dirblksiz);
	for (cp = &bp->b_un.b_buf[dirblksiz];
	     cp < &bp->b_un.b_buf[sblock->fs_bsize];
	     cp += dirblksiz)
		memmove(cp, &emptydir, sizeof emptydir);
	dirty(bp);
	bp = getdirblk(dirblk, sblksize(sblock, DIP(dp, size), lastbn + 1));
	if (bp->b_errs)
		goto bad;
	memmove(bp->b_un.b_buf, &emptydir, sizeof emptydir);
	pwarn("NO SPACE LEFT IN %s", name);
	if (preen)
		printf(" (EXPANDED)\n");
	else if (reply("EXPAND") == 0)
		goto bad;
	dirty(bp);
	inodirty();
	return (1);
bad:
	if (is_ufs2) {
		dp2->di_db[lastbn] = dp2->di_db[lastbn + 1];
		dp2->di_db[lastbn + 1] = 0;
		dp2->di_size = iswap64(iswap64(dp2->di_size)-sblock->fs_bsize);
		dp2->di_blocks = iswap64(iswap64(dp2->di_blocks) -
		    btodb(sblock->fs_bsize));
	} else {
		dp1->di_db[lastbn] = dp1->di_db[lastbn + 1];
		dp1->di_db[lastbn + 1] = 0;
		dp1->di_size = iswap64(iswap64(dp1->di_size)-sblock->fs_bsize);
		dp1->di_blocks = iswap32(iswap32(dp1->di_blocks) -
		    btodb(sblock->fs_bsize));
	}
	freeblk(newblk, sblock->fs_frag);
	markclean = 0;
	return (0);
}

/*
 * allocate a new directory
 */
ino_t
allocdir(ino_t parent, ino_t request, int mode)
{
	ino_t ino;
	char *cp;
	union dinode *dp;
	struct bufarea *bp;
	struct inoinfo *inp;
	struct dirtemplate *dirp;
	daddr_t dirblk;

	ino = allocino(request, IFDIR|mode);
	if (ino < ROOTINO)
		return 0;
	dirhead.dot_reclen = iswap16(12);
	dirhead.dotdot_reclen = iswap16(dirblksiz - 12);
	odirhead.dot_reclen = iswap16(12);
	odirhead.dotdot_reclen = iswap16(dirblksiz - 12);
	odirhead.dot_namlen = iswap16(1);
	odirhead.dotdot_namlen = iswap16(2);
	if (newinofmt)
		dirp = &dirhead;
	else
		dirp = (struct dirtemplate *)&odirhead;
	dirp->dot_ino = iswap32(ino);
	dirp->dotdot_ino = iswap32(parent);
	dp = ginode(ino);
	dirblk = is_ufs2 ? iswap64(dp->dp2.di_db[0])
		    : iswap32(dp->dp1.di_db[0]);
	bp = getdirblk(dirblk, sblock->fs_fsize);
	if (bp->b_errs) {
		freeino(ino);
		return (0);
	}
	memmove(bp->b_un.b_buf, dirp, sizeof(struct dirtemplate));
	emptydir.dot_reclen = iswap16(dirblksiz);
	for (cp = &bp->b_un.b_buf[dirblksiz];
	     cp < &bp->b_un.b_buf[sblock->fs_fsize];
	     cp += dirblksiz)
		memmove(cp, &emptydir, sizeof emptydir);
	dirty(bp);
	DIP_SET(dp, nlink, iswap16(2));
	inodirty();
	if (ino == ROOTINO) {
		inoinfo(ino)->ino_linkcnt = iswap16(DIP(dp, nlink));
		cacheino(dp, ino);
		return(ino);
	}
	if (inoinfo(parent)->ino_state != DSTATE &&
	    inoinfo(parent)->ino_state != DFOUND) {
		freeino(ino);
		return (0);
	}
	cacheino(dp, ino);
	inp = getinoinfo(ino);
	inp->i_parent = parent;
	inp->i_dotdot = parent;
	inoinfo(ino)->ino_state = inoinfo(parent)->ino_state;
	if (inoinfo(ino)->ino_state == DSTATE) {
		inoinfo(ino)->ino_linkcnt = iswap16(DIP(dp, nlink));
		inoinfo(parent)->ino_linkcnt++;
	}
	dp = ginode(parent);
	DIP_SET(dp, nlink, iswap16(iswap16(DIP(dp, nlink)) + 1));
	inodirty();
	return (ino);
}
Exemplo n.º 8
0
void
pass3(void)
{
	struct inoinfo *inp;
	int loopcnt, inpindex, state;
	ufs1_ino_t orphan;
	struct inodesc idesc;
	char namebuf[MAXNAMLEN+1];

	for (inpindex = inplast - 1; inpindex >= 0; inpindex--) {
		if (got_siginfo) {
			printf("%s: phase 3: dir %ld of %ld (%d%%)\n", cdevname,
			    inplast - inpindex - 1, inplast,
			    (int)((inplast - inpindex - 1) * 100 / inplast));
			got_siginfo = 0;
		}
		inp = inpsort[inpindex];
		state = inoinfo(inp->i_number)->ino_state;
		if (inp->i_number == UFS_ROOTINO ||
		    (inp->i_parent != 0 && state != DSTATE))
			continue;
		if (state == DCLEAR)
			continue;
		/*
		 * If we are running with soft updates and we come
		 * across unreferenced directories, we just leave
		 * them in DSTATE which will cause them to be pitched
		 * in pass 4.
		 */
		if (preen && resolved && usedsoftdep && state == DSTATE) {
			if (inp->i_dotdot >= UFS_ROOTINO)
				inoinfo(inp->i_dotdot)->ino_linkcnt++;
			continue;
		}
		for (loopcnt = 0; ; loopcnt++) {
			orphan = inp->i_number;
			if (inp->i_parent == 0 ||
			    inoinfo(inp->i_parent)->ino_state != DSTATE ||
			    loopcnt > countdirs)
				break;
			inp = getinoinfo(inp->i_parent);
		}
		if (loopcnt <= countdirs) {
			if (linkup(orphan, inp->i_dotdot, NULL)) {
				inp->i_parent = inp->i_dotdot = lfdir;
				inoinfo(lfdir)->ino_linkcnt--;
			}
			inoinfo(orphan)->ino_state = DFOUND;
			propagate();
			continue;
		}
		pfatal("ORPHANED DIRECTORY LOOP DETECTED I=%u", orphan);
		if (reply("RECONNECT") == 0)
			continue;
		memset(&idesc, 0, sizeof(struct inodesc));
		idesc.id_type = DATA;
		idesc.id_number = inp->i_parent;
		idesc.id_parent = orphan;
		idesc.id_func = findname;
		idesc.id_name = namebuf;
		if ((ckinode(ginode(inp->i_parent), &idesc) & FOUND) == 0)
			pfatal("COULD NOT FIND NAME IN PARENT DIRECTORY");
		if (linkup(orphan, inp->i_parent, namebuf)) {
			idesc.id_func = clearentry;
			if (ckinode(ginode(inp->i_parent), &idesc) & FOUND)
				inoinfo(orphan)->ino_linkcnt++;
			inp->i_parent = inp->i_dotdot = lfdir;
			inoinfo(lfdir)->ino_linkcnt--;
		}
		inoinfo(orphan)->ino_state = DFOUND;
		propagate();
	}
}
Exemplo n.º 9
0
static int
pass2check(struct inodesc *idesc)
{
	struct direct *dirp = idesc->id_dirp;
	char dirname[MAXPATHLEN + 1];
	struct inoinfo *inp;
	int n, entrysize, ret = 0;
	union dinode *dp;
	const char *errmsg;
	struct direct proto;

	/*
	 * check for "."
	 */
	if (dirp->d_ino > maxino)
		goto chk2;
	if (idesc->id_entryno != 0)
		goto chk1;
	if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") == 0) {
		if (dirp->d_ino != idesc->id_number) {
			direrror(idesc->id_number, "BAD INODE NUMBER FOR '.'");
			dirp->d_ino = idesc->id_number;
			if (reply("FIX") == 1)
				ret |= ALTERED;
		}
		if (dirp->d_type != DT_DIR) {
			direrror(idesc->id_number, "BAD TYPE VALUE FOR '.'");
			dirp->d_type = DT_DIR;
			if (reply("FIX") == 1)
				ret |= ALTERED;
		}
		goto chk1;
	}
	direrror(idesc->id_number, "MISSING '.'");
	proto.d_ino = idesc->id_number;
	proto.d_type = DT_DIR;
	proto.d_namlen = 1;
	(void)strcpy(proto.d_name, ".");
	entrysize = DIRSIZ(0, &proto);
	if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") != 0) {
		pfatal("CANNOT FIX, FIRST ENTRY IN DIRECTORY CONTAINS %s\n",
			dirp->d_name);
	} else if (dirp->d_reclen < entrysize) {
		pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '.'\n");
	} else if (dirp->d_reclen < 2 * entrysize) {
		proto.d_reclen = dirp->d_reclen;
		memmove(dirp, &proto, (size_t)entrysize);
		if (reply("FIX") == 1)
			ret |= ALTERED;
	} else {
		n = dirp->d_reclen - entrysize;
		proto.d_reclen = entrysize;
		memmove(dirp, &proto, (size_t)entrysize);
		idesc->id_entryno++;
		inoinfo(dirp->d_ino)->ino_linkcnt--;
		dirp = (struct direct *)((char *)(dirp) + entrysize);
		memset(dirp, 0, (size_t)n);
		dirp->d_reclen = n;
		if (reply("FIX") == 1)
			ret |= ALTERED;
	}
chk1:
	if (idesc->id_entryno > 1)
		goto chk2;
	inp = getinoinfo(idesc->id_number);
	proto.d_ino = inp->i_parent;
	proto.d_type = DT_DIR;
	proto.d_namlen = 2;
	(void)strcpy(proto.d_name, "..");
	entrysize = DIRSIZ(0, &proto);
	if (idesc->id_entryno == 0) {
		n = DIRSIZ(0, dirp);
		if (dirp->d_reclen < n + entrysize)
			goto chk2;
		proto.d_reclen = dirp->d_reclen - n;
		dirp->d_reclen = n;
		idesc->id_entryno++;
		inoinfo(dirp->d_ino)->ino_linkcnt--;
		dirp = (struct direct *)((char *)(dirp) + n);
		memset(dirp, 0, (size_t)proto.d_reclen);
		dirp->d_reclen = proto.d_reclen;
	}
	if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") == 0) {
		inp->i_dotdot = dirp->d_ino;
		if (dirp->d_type != DT_DIR) {
			direrror(idesc->id_number, "BAD TYPE VALUE FOR '..'");
			dirp->d_type = DT_DIR;
			if (reply("FIX") == 1)
				ret |= ALTERED;
		}
		goto chk2;
	}
	if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") != 0) {
		fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
		pfatal("CANNOT FIX, SECOND ENTRY IN DIRECTORY CONTAINS %s\n",
			dirp->d_name);
		inp->i_dotdot = (ino_t)-1;
	} else if (dirp->d_reclen < entrysize) {
		fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
		pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '..'\n");
		inp->i_dotdot = (ino_t)-1;
	} else if (inp->i_parent != 0) {
		/*
		 * We know the parent, so fix now.
		 */
		inp->i_dotdot = inp->i_parent;
		fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
		proto.d_reclen = dirp->d_reclen;
		memmove(dirp, &proto, (size_t)entrysize);
		if (reply("FIX") == 1)
			ret |= ALTERED;
	}
	idesc->id_entryno++;
	if (dirp->d_ino != 0)
		inoinfo(dirp->d_ino)->ino_linkcnt--;
	return (ret|KEEPON);
chk2:
	if (dirp->d_ino == 0)
		return (ret|KEEPON);
	if (dirp->d_namlen <= 2 &&
	    dirp->d_name[0] == '.' &&
	    idesc->id_entryno >= 2) {
		if (dirp->d_namlen == 1) {
			direrror(idesc->id_number, "EXTRA '.' ENTRY");
			dirp->d_ino = 0;
			if (reply("FIX") == 1)
				ret |= ALTERED;
			return (KEEPON | ret);
		}
		if (dirp->d_name[1] == '.') {
			direrror(idesc->id_number, "EXTRA '..' ENTRY");
			dirp->d_ino = 0;
			if (reply("FIX") == 1)
				ret |= ALTERED;
			return (KEEPON | ret);
		}
	}
	idesc->id_entryno++;
	n = 0;
	if (dirp->d_ino > maxino) {
		fileerror(idesc->id_number, dirp->d_ino, "I OUT OF RANGE");
		n = reply("REMOVE");
	} else if (((dirp->d_ino == WINO && dirp->d_type != DT_WHT) ||
		    (dirp->d_ino != WINO && dirp->d_type == DT_WHT))) {
		fileerror(idesc->id_number, dirp->d_ino, "BAD WHITEOUT ENTRY");
		dirp->d_ino = WINO;
		dirp->d_type = DT_WHT;
		if (reply("FIX") == 1)
			ret |= ALTERED;
	} else {
again:
		switch (inoinfo(dirp->d_ino)->ino_state) {
		case USTATE:
			if (idesc->id_entryno <= 2)
				break;
			fileerror(idesc->id_number, dirp->d_ino, "UNALLOCATED");
			n = reply("REMOVE");
			break;

		case DCLEAR:
		case FCLEAR:
			if (idesc->id_entryno <= 2)
				break;
			if (inoinfo(dirp->d_ino)->ino_state == FCLEAR)
				errmsg = "DUP/BAD";
			else if (!preen && !usedsoftdep)
				errmsg = "ZERO LENGTH DIRECTORY";
			else if (cursnapshot == 0) {
				n = 1;
				break;
			} else {
				getpathname(dirname, idesc->id_number,
				    dirp->d_ino);
				pwarn("ZERO LENGTH DIRECTORY %s I=%d",
					dirname, dirp->d_ino);
				/*
				 * We need to:
				 *    setcwd(idesc->id_parent);
				 *    rmdir(dirp->d_name);
				 */
				cmd.value = idesc->id_number;
				if (sysctlbyname("vfs.ffs.setcwd", 0, 0,
				    &cmd, sizeof cmd) == -1) {
					/* kernel lacks support */
					printf(" (IGNORED)\n");
					n = 1;
					break;
				}
				if (rmdir(dirp->d_name) == -1) {
					printf(" (REMOVAL FAILED: %s)\n",
					    strerror(errno));
					n = 1;
					break;
				}
				/* ".." reference to parent is removed */
				inoinfo(idesc->id_number)->ino_linkcnt--;
				printf(" (REMOVED)\n");
				break;
			}
			fileerror(idesc->id_number, dirp->d_ino, errmsg);
			if ((n = reply("REMOVE")) == 1)
				break;
			dp = ginode(dirp->d_ino);
			inoinfo(dirp->d_ino)->ino_state =
			   (DIP(dp, di_mode) & IFMT) == IFDIR ? DSTATE : FSTATE;
			inoinfo(dirp->d_ino)->ino_linkcnt = DIP(dp, di_nlink);
			goto again;

		case DSTATE:
		case DZLINK:
			if (inoinfo(idesc->id_number)->ino_state == DFOUND)
				inoinfo(dirp->d_ino)->ino_state = DFOUND;
			/* FALLTHROUGH */

		case DFOUND:
			inp = getinoinfo(dirp->d_ino);
			if (idesc->id_entryno > 2) {
				if (inp->i_parent == 0)
					inp->i_parent = idesc->id_number;
				else if ((n = fix_extraneous(inp, idesc)) == 1)
					break;
			}
			/* FALLTHROUGH */

		case FSTATE:
		case FZLINK:
			if (dirp->d_type != inoinfo(dirp->d_ino)->ino_type) {
				fileerror(idesc->id_number, dirp->d_ino,
				    "BAD TYPE VALUE");
				dirp->d_type = inoinfo(dirp->d_ino)->ino_type;
				if (reply("FIX") == 1)
					ret |= ALTERED;
			}
			inoinfo(dirp->d_ino)->ino_linkcnt--;
			break;

		default:
			errx(EEXIT, "BAD STATE %d FOR INODE I=%d",
			    inoinfo(dirp->d_ino)->ino_state, dirp->d_ino);
		}
	}
	if (n == 0)
		return (ret|KEEPON);
	dirp->d_ino = 0;
	return (ret|KEEPON|ALTERED);
}
Exemplo n.º 10
0
void
pass2(void)
{
	union dinode *dp;
	struct inoinfo **inpp, *inp, *pinp;
	struct inoinfo **inpend;
	struct inodesc curino;
	union dinode dino;
	char pathbuf[MAXPATHLEN + 1];
	int i;

	switch (GET_ISTATE(ROOTINO)) {

	case USTATE:
		pfatal("ROOT INODE UNALLOCATED");
		if (reply("ALLOCATE") == 0) {
			ckfini(0);
			errexit("%s", "");
		}
		if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO)
			errexit("CANNOT ALLOCATE ROOT INODE\n");
		break;

	case DCLEAR:
		pfatal("DUPS/BAD IN ROOT INODE");
		if (reply("REALLOCATE")) {
			freeino(ROOTINO);
			if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO)
				errexit("CANNOT ALLOCATE ROOT INODE\n");
			break;
		}
		if (reply("CONTINUE") == 0) {
			ckfini(0);
			errexit("%s", "");
		}
		break;

	case FSTATE:
	case FCLEAR:
		pfatal("ROOT INODE NOT DIRECTORY");
		if (reply("REALLOCATE")) {
			freeino(ROOTINO);
			if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO)
				errexit("CANNOT ALLOCATE ROOT INODE\n");
			break;
		}
		if (reply("FIX") == 0) {
			ckfini(0);
			errexit("%s", "");
		}
		dp = ginode(ROOTINO);
		DIP_SET(dp, di_mode, DIP(dp, di_mode) & ~IFMT);
		DIP_SET(dp, di_mode, DIP(dp, di_mode) | IFDIR);
		inodirty();
		break;

	case DSTATE:
		break;

	default:
		errexit("BAD STATE %d FOR ROOT INODE\n", GET_ISTATE(ROOTINO));
	}
	SET_ISTATE(ROOTINO, DFOUND);
	/*
	 * Sort the directory list into disk block order.
	 */
	qsort(inpsort, (size_t)inplast, sizeof *inpsort, blksort);
	/*
	 * Check the integrity of each directory.
	 */
	memset(&curino, 0, sizeof(struct inodesc));
	curino.id_type = DATA;
	curino.id_func = pass2check;
	inpend = &inpsort[inplast];
	info_pos = 0;
	info_max = inpend - inpsort;
	info_fn = pass2_info1;
	for (inpp = inpsort; inpp < inpend; inpp++) {
		inp = *inpp;
		info_pos ++;
		if (inp->i_isize == 0)
			continue;
		if (inp->i_isize < MINDIRSIZE) {
			direrror(inp->i_number, "DIRECTORY TOO SHORT");
			inp->i_isize = roundup(MINDIRSIZE, DIRBLKSIZ);
			if (reply("FIX") == 1) {
				dp = ginode(inp->i_number);
				DIP_SET(dp, di_size, inp->i_isize);
				inodirty();
			}
		} else if ((inp->i_isize & (DIRBLKSIZ - 1)) != 0) {
			getpathname(pathbuf, sizeof pathbuf,
			    inp->i_number, inp->i_number);
			if (usedsoftdep)
			        pfatal("%s %s: LENGTH %ld NOT MULTIPLE of %d",
				       "DIRECTORY", pathbuf, (long)inp->i_isize,
				       DIRBLKSIZ);
			else
				pwarn("%s %s: LENGTH %ld NOT MULTIPLE OF %d",
				      "DIRECTORY", pathbuf, (long)inp->i_isize,
				      DIRBLKSIZ);
			if (preen)
				printf(" (ADJUSTED)\n");
			inp->i_isize = roundup(inp->i_isize, DIRBLKSIZ);
			if (preen || reply("ADJUST") == 1) {
				dp = ginode(inp->i_number);
				DIP_SET(dp, di_size, inp->i_isize);
				inodirty();
			}
		}
		memset(&dino, 0, sizeof(union dinode));
		dp = &dino;
		DIP_SET(dp, di_mode, IFDIR);
		DIP_SET(dp, di_size, inp->i_isize);
		for (i = 0;
		     i < (inp->i_numblks<NDADDR ? inp->i_numblks : NDADDR);
		     i++)
			DIP_SET(dp, di_db[i], inp->i_blks[i]);
		if (inp->i_numblks > NDADDR)
			for (i = 0; i < NIADDR; i++)
				DIP_SET(dp, di_ib[i], inp->i_blks[NDADDR + i]);
		curino.id_number = inp->i_number;
		curino.id_parent = inp->i_parent;
		(void)ckinode(dp, &curino);
	}
	/*
	 * Now that the parents of all directories have been found,
	 * make another pass to verify the value of `..'
	 */
	info_pos = 0;
	info_fn = pass2_info2;
	for (inpp = inpsort; inpp < inpend; inpp++) {
		inp = *inpp;
		info_pos++;
		if (inp->i_parent == 0 || inp->i_isize == 0)
			continue;
		if (inp->i_dotdot == inp->i_parent ||
		    inp->i_dotdot == (ino_t)-1)
			continue;
		if (inp->i_dotdot == 0) {
			inp->i_dotdot = inp->i_parent;
			fileerror(inp->i_parent, inp->i_number, "MISSING '..'");
			if (reply("FIX") == 0)
				continue;
			(void)makeentry(inp->i_number, inp->i_parent, "..");
			lncntp[inp->i_parent]--;
			continue;
		}
		fileerror(inp->i_parent, inp->i_number,
		    "BAD INODE NUMBER FOR '..'");
		if (reply("FIX") == 0)
			continue;
		lncntp[inp->i_dotdot]++;
		lncntp[inp->i_parent]--;
		inp->i_dotdot = inp->i_parent;
		(void)changeino(inp->i_number, "..", inp->i_parent);
	}
	info_fn = NULL;
	/*
	 * Create a list of children for each directory.
	 */
	inpend = &inpsort[inplast];
	for (inpp = inpsort; inpp < inpend; inpp++) {
		inp = *inpp;
		if (inp->i_parent == 0 ||
		    inp->i_number == ROOTINO)
			continue;
		pinp = getinoinfo(inp->i_parent);
		inp->i_parentp = pinp;
		inp->i_sibling = pinp->i_child;
		pinp->i_child = inp;
	}
	/*
	 * Mark all the directories that can be found from the root.
	 */
	propagate(ROOTINO);
}
Exemplo n.º 11
0
static int
pass2check(struct inodesc *idesc)
{
	struct direct *dirp = idesc->id_dirp;
	struct inoinfo *inp;
	int n, entrysize, ret = 0;
	union dinode *dp;
	char *errmsg;
	struct direct proto;
	char namebuf[MAXPATHLEN + 1];
	char pathbuf[MAXPATHLEN + 1];

	/*
	 * If converting, set directory entry type.
	 */
	if (doinglevel2 && dirp->d_ino > 0 && dirp->d_ino < maxino) {
		dirp->d_type = GET_ITYPE(dirp->d_ino);
		ret |= ALTERED;
	}
	/*
	 * check for "."
	 */
	if (idesc->id_entryno != 0)
		goto chk1;
	if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") == 0) {
		if (dirp->d_ino != idesc->id_number) {
			direrror(idesc->id_number, "BAD INODE NUMBER FOR '.'");
			dirp->d_ino = idesc->id_number;
			if (reply("FIX") == 1)
				ret |= ALTERED;
		}
		if (newinofmt && dirp->d_type != DT_DIR) {
			direrror(idesc->id_number, "BAD TYPE VALUE FOR '.'");
			dirp->d_type = DT_DIR;
			if (reply("FIX") == 1)
				ret |= ALTERED;
		}
		goto chk1;
	}
	direrror(idesc->id_number, "MISSING '.'");
	proto.d_ino = idesc->id_number;
	if (newinofmt)
		proto.d_type = DT_DIR;
	else
		proto.d_type = 0;
	proto.d_namlen = 1;
	(void)strlcpy(proto.d_name, ".", sizeof proto.d_name);
#	if BYTE_ORDER == LITTLE_ENDIAN
		if (!newinofmt) {
			u_char tmp;

			tmp = proto.d_type;
			proto.d_type = proto.d_namlen;
			proto.d_namlen = tmp;
		}
#	endif
	entrysize = DIRSIZ(0, &proto);
	if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") != 0) {
		pfatal("CANNOT FIX, FIRST ENTRY IN DIRECTORY CONTAINS %s\n",
			dirp->d_name);
	} else if (dirp->d_reclen < entrysize) {
		pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '.'\n");
	} else if (dirp->d_reclen < 2 * entrysize) {
		proto.d_reclen = dirp->d_reclen;
		memcpy(dirp, &proto, (size_t)entrysize);
		if (reply("FIX") == 1)
			ret |= ALTERED;
	} else {
		n = dirp->d_reclen - entrysize;
		proto.d_reclen = entrysize;
		memcpy(dirp, &proto, (size_t)entrysize);
		idesc->id_entryno++;
		lncntp[dirp->d_ino]--;
		dirp = (struct direct *)((char *)(dirp) + entrysize);
		memset(dirp, 0, (size_t)n);
		dirp->d_reclen = n;
		if (reply("FIX") == 1)
			ret |= ALTERED;
	}
chk1:
	if (idesc->id_entryno > 1)
		goto chk2;
	inp = getinoinfo(idesc->id_number);
	proto.d_ino = inp->i_parent;
	if (newinofmt)
		proto.d_type = DT_DIR;
	else
		proto.d_type = 0;
	proto.d_namlen = 2;
	(void)strlcpy(proto.d_name, "..", sizeof proto.d_name);
#	if BYTE_ORDER == LITTLE_ENDIAN
		if (!newinofmt) {
			u_char tmp;

			tmp = proto.d_type;
			proto.d_type = proto.d_namlen;
			proto.d_namlen = tmp;
		}
#	endif
	entrysize = DIRSIZ(0, &proto);
	if (idesc->id_entryno == 0) {
		n = DIRSIZ(0, dirp);
		if (dirp->d_reclen < n + entrysize)
			goto chk2;
		proto.d_reclen = dirp->d_reclen - n;
		dirp->d_reclen = n;
		idesc->id_entryno++;
		lncntp[dirp->d_ino]--;
		dirp = (struct direct *)((char *)(dirp) + n);
		memset(dirp, 0, (size_t)proto.d_reclen);
		dirp->d_reclen = proto.d_reclen;
	}
	if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") == 0) {
		inp->i_dotdot = dirp->d_ino;
		if (newinofmt && dirp->d_type != DT_DIR) {
			direrror(idesc->id_number, "BAD TYPE VALUE FOR '..'");
			dirp->d_type = DT_DIR;
			if (reply("FIX") == 1)
				ret |= ALTERED;
		}
		goto chk2;
	}
	if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") != 0) {
		fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
		pfatal("CANNOT FIX, SECOND ENTRY IN DIRECTORY CONTAINS %s\n",
			dirp->d_name);
		inp->i_dotdot = -1;
	} else if (dirp->d_reclen < entrysize) {
		fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
		pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '..'\n");
		inp->i_dotdot = -1;
	} else if (inp->i_parent != 0) {
		/*
		 * We know the parent, so fix now.
		 */
		inp->i_dotdot = inp->i_parent;
		fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
		proto.d_reclen = dirp->d_reclen;
		memcpy(dirp, &proto, (size_t)entrysize);
		if (reply("FIX") == 1)
			ret |= ALTERED;
	}
	idesc->id_entryno++;
	if (dirp->d_ino != 0)
		lncntp[dirp->d_ino]--;
	return (ret|KEEPON);
chk2:
	if (dirp->d_ino == 0)
		return (ret|KEEPON);
	if (dirp->d_namlen <= 2 &&
	    dirp->d_name[0] == '.' &&
	    idesc->id_entryno >= 2) {
		if (dirp->d_namlen == 1) {
			direrror(idesc->id_number, "EXTRA '.' ENTRY");
			dirp->d_ino = 0;
			if (reply("FIX") == 1)
				ret |= ALTERED;
			return (KEEPON | ret);
		}
		if (dirp->d_name[1] == '.') {
			direrror(idesc->id_number, "EXTRA '..' ENTRY");
			dirp->d_ino = 0;
			if (reply("FIX") == 1)
				ret |= ALTERED;
			return (KEEPON | ret);
		}
	}
	idesc->id_entryno++;
	n = 0;
	if (dirp->d_ino > maxino) {
		fileerror(idesc->id_number, dirp->d_ino, "I OUT OF RANGE");
		n = reply("REMOVE");
	} else {
again:
		switch (GET_ISTATE(dirp->d_ino)) {
		case USTATE:
			if (idesc->id_entryno <= 2)
				break;
			fileerror(idesc->id_number, dirp->d_ino, "UNALLOCATED");
			n = reply("REMOVE");
			break;

		case DCLEAR:
		case FCLEAR:
			if (idesc->id_entryno <= 2)
				break;
			if (GET_ISTATE(dirp->d_ino) == FCLEAR)
				errmsg = "DUP/BAD";
			else if (!preen && !usedsoftdep)
				errmsg = "ZERO LENGTH DIRECTORY";
			else {
				n = 1;
				break;
			}
			fileerror(idesc->id_number, dirp->d_ino, errmsg);
			if ((n = reply("REMOVE")) == 1)
				break;
			dp = ginode(dirp->d_ino);
			SET_ISTATE(dirp->d_ino, (DIP(dp, di_mode) & IFMT) ==
			    IFDIR ? DSTATE : FSTATE);
			lncntp[dirp->d_ino] = DIP(dp, di_nlink);
			goto again;

		case DSTATE:
		case DFOUND:
			inp = getinoinfo(dirp->d_ino);
			if (inp->i_parent != 0 && idesc->id_entryno > 2) {
				getpathname(pathbuf, sizeof pathbuf,
				    idesc->id_number, idesc->id_number);
				getpathname(namebuf, sizeof namebuf,
				    dirp->d_ino, dirp->d_ino);
				pwarn("%s %s %s\n", pathbuf,
				    "IS AN EXTRANEOUS HARD LINK TO DIRECTORY",
				    namebuf);
				if (preen) {
					printf (" (REMOVED)\n");
					n = 1;
					break;
				}
				if ((n = reply("REMOVE")) == 1)
					break;
			}
			if (idesc->id_entryno > 2)
				inp->i_parent = idesc->id_number;
			/* FALLTHROUGH */

		case FSTATE:
			if (newinofmt && dirp->d_type !=
			    GET_ITYPE(dirp->d_ino)) {
				fileerror(idesc->id_number, dirp->d_ino,
				    "BAD TYPE VALUE");
				dirp->d_type = GET_ITYPE(dirp->d_ino);
				if (reply("FIX") == 1)
					ret |= ALTERED;
			}
			lncntp[dirp->d_ino]--;
			break;

		default:
			errexit("BAD STATE %d FOR INODE I=%d\n",
			    GET_ISTATE(dirp->d_ino), dirp->d_ino);
		}
	}
	if (n == 0)
		return (ret|KEEPON);
	dirp->d_ino = 0;
	return (ret|KEEPON|ALTERED);
}
Exemplo n.º 12
0
static int
pass2check(struct inodesc *idesc)
{
	struct ext2fs_direct *dirp = idesc->id_dirp;
	struct inoinfo *inp;
	int n, entrysize, ret = 0;
	struct ext2fs_dinode *dp;
	char *errmsg;
	struct ext2fs_direct proto;
	char namebuf[MAXPATHLEN + 1];
	char pathbuf[MAXPATHLEN + 1];

	/*
	 * check for "."
	 */
	if (idesc->id_entryno != 0)
		goto chk1;
	if (letoh32(dirp->e2d_ino) != 0 && dirp->e2d_namlen == 1 &&
		dirp->e2d_name[0] == '.') {
		if (letoh32(dirp->e2d_ino) != idesc->id_number) {
			direrror(idesc->id_number, "BAD INODE NUMBER FOR '.'");
			dirp->e2d_ino = htole32(idesc->id_number);
			if (reply("FIX") == 1)
				ret |= ALTERED;
		}
		if (sblock.e2fs.e2fs_rev > E2FS_REV0 &&
		    (sblock.e2fs.e2fs_features_incompat & EXT2F_INCOMPAT_FTYPE)
		    && (dirp->e2d_type != EXT2_FT_DIR)) {
			direrror(idesc->id_number, "BAD TYPE VALUE FOR '.'");
			dirp->e2d_type = EXT2_FT_DIR;
			if (reply("FIX") == 1)
				ret |= ALTERED;
		}
		goto chk1;
	}
	direrror(idesc->id_number, "MISSING '.'");
	proto.e2d_ino = htole32(idesc->id_number);
	proto.e2d_namlen = 1;
	if (sblock.e2fs.e2fs_rev > E2FS_REV0 &&
	    (sblock.e2fs.e2fs_features_incompat & EXT2F_INCOMPAT_FTYPE))
		proto.e2d_type = EXT2_FT_DIR;
	else
		proto.e2d_type = 0;
	(void)strlcpy(proto.e2d_name, ".", sizeof proto.e2d_name);
	entrysize = EXT2FS_DIRSIZ(proto.e2d_namlen);
	if (letoh32(dirp->e2d_ino) != 0 && strcmp(dirp->e2d_name, "..") != 0) {
		pfatal("CANNOT FIX, FIRST ENTRY IN DIRECTORY CONTAINS %s\n",
			dirp->e2d_name);
	} else if (letoh16(dirp->e2d_reclen) < entrysize) {
		pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '.'\n");
	} else if (letoh16(dirp->e2d_reclen) < 2 * entrysize) {
		proto.e2d_reclen = dirp->e2d_reclen;
		memcpy(dirp, &proto, (size_t)entrysize);
		if (reply("FIX") == 1)
			ret |= ALTERED;
	} else {
		n = letoh16(dirp->e2d_reclen) - entrysize;
		proto.e2d_reclen = htole16(entrysize);
		memcpy(dirp, &proto, (size_t)entrysize);
		idesc->id_entryno++;
		lncntp[letoh32(dirp->e2d_ino)]--;
		dirp = (struct ext2fs_direct *)((char *)(dirp) + entrysize);
		memset(dirp, 0, (size_t)n);
		dirp->e2d_reclen = htole16(n);
		if (reply("FIX") == 1)
			ret |= ALTERED;
	}
chk1:
	if (idesc->id_entryno > 1)
		goto chk2;
	inp = getinoinfo(idesc->id_number);
	proto.e2d_ino = htole32(inp->i_parent);
	proto.e2d_namlen = 2;
	if (sblock.e2fs.e2fs_rev > E2FS_REV0 &&
	    (sblock.e2fs.e2fs_features_incompat & EXT2F_INCOMPAT_FTYPE))
		proto.e2d_type = EXT2_FT_DIR;
	else
		proto.e2d_type = 0;
	(void)strlcpy(proto.e2d_name, "..", sizeof proto.e2d_name);
	entrysize = EXT2FS_DIRSIZ(2);
	if (idesc->id_entryno == 0) {
		n = EXT2FS_DIRSIZ(dirp->e2d_namlen);
		if (letoh16(dirp->e2d_reclen) < n + entrysize)
			goto chk2;
		proto.e2d_reclen = htole16(letoh16(dirp->e2d_reclen) - n);
		dirp->e2d_reclen = htole16(n);
		idesc->id_entryno++;
		lncntp[letoh32(dirp->e2d_ino)]--;
		dirp = (struct ext2fs_direct *)((char *)(dirp) + n);
		memset(dirp, 0, (size_t)letoh16(proto.e2d_reclen));
		dirp->e2d_reclen = proto.e2d_reclen;
	}
	if (letoh32(dirp->e2d_ino) != 0 &&
	    dirp->e2d_namlen == 2 &&
	    strncmp(dirp->e2d_name, "..", 2) == 0) {
		inp->i_dotdot = letoh32(dirp->e2d_ino);
		if (sblock.e2fs.e2fs_rev > E2FS_REV0 &&
		    (sblock.e2fs.e2fs_features_incompat & EXT2F_INCOMPAT_FTYPE)
		    && dirp->e2d_type != EXT2_FT_DIR) {
			direrror(idesc->id_number, "BAD TYPE VALUE FOR '..'");
			dirp->e2d_type = EXT2_FT_DIR;
			if (reply("FIX") == 1)
				ret |= ALTERED;
		}
		goto chk2;
	}
	if (letoh32(dirp->e2d_ino) != 0 &&
		dirp->e2d_namlen == 1 &&
		strncmp(dirp->e2d_name, ".", 1) != 0) {
		fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
		pfatal("CANNOT FIX, SECOND ENTRY IN DIRECTORY CONTAINS %s\n",
			dirp->e2d_name);
		inp->i_dotdot = (ino_t)-1;
	} else if (letoh16(dirp->e2d_reclen) < entrysize) {
		fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
		pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '..'\n");
		inp->i_dotdot = (ino_t)-1;
	} else if (inp->i_parent != 0) {
		/*
		 * We know the parent, so fix now.
		 */
		inp->i_dotdot = inp->i_parent;
		fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
		proto.e2d_reclen = dirp->e2d_reclen;
		memcpy(dirp, &proto, (size_t)entrysize);
		if (reply("FIX") == 1)
			ret |= ALTERED;
	}
	idesc->id_entryno++;
	if (letoh32(dirp->e2d_ino) != 0)
		lncntp[letoh32(dirp->e2d_ino)]--;
	return (ret|KEEPON);
chk2:
	if (letoh32(dirp->e2d_ino) == 0)
		return (ret|KEEPON);
	if (dirp->e2d_namlen <= 2 &&
	    dirp->e2d_name[0] == '.' &&
	    idesc->id_entryno >= 2) {
		if (dirp->e2d_namlen == 1) {
			direrror(idesc->id_number, "EXTRA '.' ENTRY");
			dirp->e2d_ino = 0;
			if (reply("FIX") == 1)
				ret |= ALTERED;
			return (KEEPON | ret);
		}
		if (dirp->e2d_name[1] == '.') {
			direrror(idesc->id_number, "EXTRA '..' ENTRY");
			dirp->e2d_ino = 0;
			if (reply("FIX") == 1)
				ret |= ALTERED;
			return (KEEPON | ret);
		}
	}
	idesc->id_entryno++;
	n = 0;
	if (letoh32(dirp->e2d_ino) > maxino ||
		(letoh32(dirp->e2d_ino) < EXT2_FIRSTINO &&
		 letoh32(dirp->e2d_ino) != EXT2_ROOTINO)) {
		fileerror(idesc->id_number, letoh32(dirp->e2d_ino), "I OUT OF RANGE");
		n = reply("REMOVE");
	} else {
again:
		switch (statemap[letoh32(dirp->e2d_ino)]) {
		case USTATE:
			if (idesc->id_entryno <= 2)
				break;
			fileerror(idesc->id_number, letoh32(dirp->e2d_ino), "UNALLOCATED");
			n = reply("REMOVE");
			break;

		case DCLEAR:
		case FCLEAR:
			if (idesc->id_entryno <= 2)
				break;
			if (statemap[letoh32(dirp->e2d_ino)] == FCLEAR)
				errmsg = "DUP/BAD";
			else if (!preen)
				errmsg = "ZERO LENGTH DIRECTORY";
			else {
				n = 1;
				break;
			}
			fileerror(idesc->id_number, letoh32(dirp->e2d_ino), errmsg);
			if ((n = reply("REMOVE")) == 1)
				break;
			dp = ginode(letoh32(dirp->e2d_ino));
			statemap[letoh32(dirp->e2d_ino)] =
			    (letoh16(dp->e2di_mode) & IFMT) == IFDIR ? DSTATE : FSTATE;
			lncntp[letoh32(dirp->e2d_ino)] = letoh16(dp->e2di_nlink);
			goto again;

		case DSTATE:
		case DFOUND:
			inp = getinoinfo(letoh32(dirp->e2d_ino));
			if (inp->i_parent != 0 && idesc->id_entryno > 2) {
				getpathname(pathbuf, sizeof pathbuf,
				    idesc->id_number, idesc->id_number);
				getpathname(namebuf, sizeof namebuf,
				    letoh32(dirp->e2d_ino), letoh32(dirp->e2d_ino));
				pwarn("%s %s %s\n", pathbuf,
				    "IS AN EXTRANEOUS HARD LINK TO DIRECTORY",
				    namebuf);
				if (preen)
					printf(" (IGNORED)\n");
				else if ((n = reply("REMOVE")) == 1)
					break;
			}
			if (idesc->id_entryno > 2)
				inp->i_parent = idesc->id_number;
			/* fall through */

		case FSTATE:
			if (sblock.e2fs.e2fs_rev > E2FS_REV0 &&
			    (sblock.e2fs.e2fs_features_incompat &
				EXT2F_INCOMPAT_FTYPE) &&
			    dirp->e2d_type !=
				inot2ext2dt(typemap[letoh32(dirp->e2d_ino)])) {
				dirp->e2d_type =
				    inot2ext2dt(typemap[letoh32(dirp->e2d_ino)]);
				fileerror(idesc->id_number,
				    letoh32(dirp->e2d_ino),
				    "BAD TYPE VALUE");
				if (reply("FIX") == 1)
					ret |= ALTERED;
			}
			lncntp[letoh32(dirp->e2d_ino)]--;
			break;

		default:
			errexit("BAD STATE %d FOR INODE I=%llu\n",
			    statemap[letoh32(dirp->e2d_ino)],
			    (unsigned long long)letoh32(dirp->e2d_ino));
		}
	}
	if (n == 0)
		return (ret|KEEPON);
	dirp->e2d_ino = 0;
	return (ret|KEEPON|ALTERED);
}
Exemplo n.º 13
0
void
pass2(void)
{
	union dinode *dp;
	struct inoinfo **inpp, *inp, *pinp;
	struct inoinfo **inpend;
	struct inostat *rinfo, *info;
	struct inodesc curino;
	union dinode dino;
	int i, maxblk;
	char pathbuf[MAXPATHLEN + 1];

	rinfo = inoinfo(ROOTINO);
	switch (rinfo->ino_state) {

	case USTATE:
		pfatal("ROOT INODE UNALLOCATED");
		if (reply("ALLOCATE") == 0) {
			markclean = 0;
			ckfini();
			exit(FSCK_EXIT_CHECK_FAILED);
		}
		if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO)
			errexit("CANNOT ALLOCATE ROOT INODE");
		break;

	case DCLEAR:
		pfatal("DUPS/BAD IN ROOT INODE");
		if (reply("REALLOCATE")) {
			freeino(ROOTINO);
			if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO)
				errexit("CANNOT ALLOCATE ROOT INODE");
			break;
		}
		markclean = 0;
		if (reply("CONTINUE") == 0) {
			ckfini();
			exit(FSCK_EXIT_CHECK_FAILED);
		}
		break;

	case FSTATE:
	case FCLEAR:
		pfatal("ROOT INODE NOT DIRECTORY");
		if (reply("REALLOCATE")) {
			freeino(ROOTINO);
			if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO)
				errexit("CANNOT ALLOCATE ROOT INODE");
			break;
		}
		if (reply("FIX") == 0) {
			markclean = 0;
			ckfini();
			exit(FSCK_EXIT_CHECK_FAILED);
		}
		dp = ginode(ROOTINO);
		DIP_SET(dp, mode,
		    iswap16((iswap16(DIP(dp, mode)) & ~IFMT) | IFDIR));
		inodirty();
		break;

	case DSTATE:
		break;

	default:
		errexit("BAD STATE %d FOR ROOT INODE", rinfo->ino_state);
	}
	if (newinofmt) {
		info = inoinfo(WINO);
		info->ino_state = FSTATE;
		info->ino_type = DT_WHT;
	}
	/*
	 * Sort the directory list into disk block order.
	 */
	qsort((char *)inpsort, (size_t)inplast, sizeof *inpsort, blksort);
	/*
	 * Check the integrity of each directory.
	 */
	memset(&curino, 0, sizeof(struct inodesc));
	curino.id_type = DATA;
	curino.id_func = pass2check;
	inpend = &inpsort[inplast];
	for (inpp = inpsort; inpp < inpend; inpp++) {
		if (got_siginfo) {
			fprintf(stderr,
			    "%s: phase 2: dir %ld of %d (%d%%)\n", cdevname(),
			    (long)(inpp - inpsort), (int)inplast, 
			    (int)((inpp - inpsort) * 100 / inplast));
			got_siginfo = 0;
		}
#ifdef PROGRESS
		progress_bar(cdevname(), preen ? NULL : "phase 2",
			    (inpp - inpsort), inplast);
#endif /* PROGRESS */
		inp = *inpp;
		if (inp->i_isize == 0)
			continue;
		if (inp->i_isize < MINDIRSIZE) {
			direrror(inp->i_number, "DIRECTORY TOO SHORT");
			inp->i_isize = roundup(MINDIRSIZE, dirblksiz);
			if (reply("FIX") == 1) {
				dp = ginode(inp->i_number);
				DIP_SET(dp, size, iswap64(inp->i_isize));
				inodirty();
			} else
				markclean = 0;
		} else if ((inp->i_isize & (dirblksiz - 1)) != 0) {
			getpathname(pathbuf, sizeof(pathbuf), inp->i_number,
			    inp->i_number);
			if (usedsoftdep)
				pfatal("%s %s: LENGTH %lld NOT MULTIPLE OF %d",
					"DIRECTORY", pathbuf,
					(long long)inp->i_isize, dirblksiz);
			else
				pwarn("%s %s: LENGTH %lld NOT MULTIPLE OF %d",
					"DIRECTORY", pathbuf,
					(long long)inp->i_isize, dirblksiz);
			if (preen)
				printf(" (ADJUSTED)\n");
			inp->i_isize = roundup(inp->i_isize, dirblksiz);
			if (preen || reply("ADJUST") == 1) {
				dp = ginode(inp->i_number);
				DIP_SET(dp, size, iswap64(inp->i_isize));
				inodirty();
			} else
				markclean = 0;
		}
		memset(&dino, 0, sizeof dino);
		dp = &dino;
		if (!is_ufs2) {
			dp->dp1.di_mode = iswap16(IFDIR);
			dp->dp1.di_size = iswap64(inp->i_isize);
			maxblk = inp->i_numblks < NDADDR ? inp->i_numblks :
			    NDADDR;
			for (i = 0; i < maxblk; i++)
				dp->dp1.di_db[i] = inp->i_blks[i];
			if (inp->i_numblks > NDADDR) {
				for (i = 0; i < NIADDR; i++)
					dp->dp1.di_ib[i] =
					    inp->i_blks[NDADDR + i];
			}
		} else {
			dp->dp2.di_mode = iswap16(IFDIR);
			dp->dp2.di_size = iswap64(inp->i_isize);
			maxblk = inp->i_numblks < NDADDR ? inp->i_numblks :
			    NDADDR;
			for (i = 0; i < maxblk; i++)
				dp->dp2.di_db[i] = inp->i_blks[i];
			if (inp->i_numblks > NDADDR) {
				for (i = 0; i < NIADDR; i++)
					dp->dp2.di_ib[i] =
					    inp->i_blks[NDADDR + i];
			}
		}
		curino.id_number = inp->i_number;
		curino.id_parent = inp->i_parent;
		(void)ckinode(&dino, &curino);
	}

	/*
	 * Byte swapping in directory entries, if needed, has been done.
	 * Now rescan dirs for pass2check()
	 */
	if (do_dirswap) { 
		do_dirswap = 0;
		for (inpp = inpsort; inpp < inpend; inpp++) {
			inp = *inpp;
			if (inp->i_isize == 0)
				continue;
			memset(&dino, 0, sizeof dino);
			if (!is_ufs2) {
				dino.dp1.di_mode = iswap16(IFDIR);
				dino.dp1.di_size = iswap64(inp->i_isize);
				for (i = 0; i < inp->i_numblks; i++)
					dino.dp1.di_db[i] = inp->i_blks[i];
			} else {
				dino.dp2.di_mode = iswap16(IFDIR);
				dino.dp2.di_size = iswap64(inp->i_isize);
				for (i = 0; i < inp->i_numblks; i++)
					dino.dp2.di_db[i] = inp->i_blks[i];
			}
			curino.id_number = inp->i_number;
			curino.id_parent = inp->i_parent;
			(void)ckinode(&dino, &curino);
		}
	}

	/*
	 * Now that the parents of all directories have been found,
	 * make another pass to verify the value of `..'
	 */
	for (inpp = inpsort; inpp < inpend; inpp++) {
		inp = *inpp;
		if (inp->i_parent == 0 || inp->i_isize == 0)
			continue;
		if (inp->i_dotdot == inp->i_parent ||
		    inp->i_dotdot == (ino_t)-1)
			continue;
		info = inoinfo(inp->i_parent);
		if (inp->i_dotdot == 0) {
			inp->i_dotdot = inp->i_parent;
			fileerror(inp->i_parent, inp->i_number, "MISSING '..'");
			if (reply("FIX") == 0) {
				markclean = 0;
				continue;
			}
			(void)makeentry(inp->i_number, inp->i_parent, "..");
			info->ino_linkcnt--;
			continue;
		}
		fileerror(inp->i_parent, inp->i_number,
		    "BAD INODE NUMBER FOR '..'");
		if (reply("FIX") == 0) {
			markclean = 0;
			continue;
		}
		inoinfo(inp->i_dotdot)->ino_linkcnt++;
		info->ino_linkcnt--;
		inp->i_dotdot = inp->i_parent;
		(void)changeino(inp->i_number, "..", inp->i_parent);
	}
	/*
	 * Create a list of children for each directory.
	 */
	inpend = &inpsort[inplast];
	for (inpp = inpsort; inpp < inpend; inpp++) {
		inp = *inpp;
		info = inoinfo(inp->i_number);
		inp->i_child = inp->i_sibling = 0;
		if (info->ino_state == DFOUND)
			info->ino_state = DSTATE;
	}
	for (inpp = inpsort; inpp < inpend; inpp++) {
		inp = *inpp;
		if (inp->i_parent == 0 ||
		    inp->i_number == ROOTINO)
			continue;
		pinp = getinoinfo(inp->i_parent);
		inp->i_sibling = pinp->i_child;
		pinp->i_child = inp;
	}
	/*
	 * Mark all the directories that can be found from the root.
	 */
	propagate(ROOTINO);

#ifdef PROGRESS
	if (!preen)
		progress_done();
#endif /* PROGRESS */
}
Exemplo n.º 14
0
static int
pass2check(struct inodesc *idesc)
{
	struct direct *dirp = idesc->id_dirp;
	struct inoinfo *inp;
	struct inostat *info;
	int n, entrysize, ret = 0;
	union dinode *dp;
	const char *errmsg;
	struct direct proto;
	char namebuf[MAXPATHLEN + 1];
	char pathbuf[MAXPATHLEN + 1];

	/*
	 * If converting, set directory entry type.
	 */
	if (!is_ufs2 && doinglevel2 && iswap32(dirp->d_ino) > 0 &&
	    iswap32(dirp->d_ino) < maxino) {
		dirp->d_type = inoinfo(iswap32(dirp->d_ino))->ino_type;
		ret |= ALTERED;
	}
	/* 
	 * check for "."
	 */
	if (idesc->id_entryno != 0)
		goto chk1;
	if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") == 0) {
		if (iswap32(dirp->d_ino) != idesc->id_number) {
			direrror(idesc->id_number, "BAD INODE NUMBER FOR '.'");
			dirp->d_ino = iswap32(idesc->id_number);
			if (reply("FIX") == 1)
				ret |= ALTERED;
			else
				markclean = 0;
		}
		if (newinofmt && dirp->d_type != DT_DIR) {
			direrror(idesc->id_number, "BAD TYPE VALUE FOR '.'");
			dirp->d_type = DT_DIR;
			if (reply("FIX") == 1)
				ret |= ALTERED;
			else
				markclean = 0;
		}
		goto chk1;
	}
	direrror(idesc->id_number, "MISSING '.'");
	proto.d_ino = iswap32(idesc->id_number);
	if (newinofmt)
		proto.d_type = DT_DIR;
	else
		proto.d_type = 0;
	proto.d_namlen = 1;
	(void)strlcpy(proto.d_name, ".", sizeof(proto.d_name));
#	if BYTE_ORDER == LITTLE_ENDIAN
		if (!newinofmt && !needswap) {
#	else
		if (!newinofmt && needswap) {
#	endif
			u_char tmp;

			tmp = proto.d_type;
			proto.d_type = proto.d_namlen;
			proto.d_namlen = tmp;
		}
	entrysize = DIRSIZ(0, &proto, 0);
	if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") != 0) {
		pfatal("CANNOT FIX, FIRST ENTRY IN DIRECTORY CONTAINS %s\n",
			dirp->d_name);
		markclean = 0;
	} else if (iswap16(dirp->d_reclen) < entrysize) {
		pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '.'\n");
		markclean = 0;
	} else if (iswap16(dirp->d_reclen) < 2 * entrysize) {
		proto.d_reclen = dirp->d_reclen;
		memmove(dirp, &proto, (size_t)entrysize);
		if (reply("FIX") == 1)
			ret |= ALTERED;
		else
			markclean = 0;
	} else {
		n = iswap16(dirp->d_reclen) - entrysize;
		proto.d_reclen = iswap16(entrysize);
		memmove(dirp, &proto, (size_t)entrysize);
		idesc->id_entryno++;
		inoinfo(iswap32(dirp->d_ino))->ino_linkcnt--;
		dirp = (struct direct *)((char *)(dirp) + entrysize);
		memset(dirp, 0, (size_t)n);
		dirp->d_reclen = iswap16(n);
		if (reply("FIX") == 1)
			ret |= ALTERED;
		else
			markclean = 0;
	}
chk1:
	if (idesc->id_entryno > 1)
		goto chk2;
	inp = getinoinfo(idesc->id_number);
	proto.d_ino = iswap32(inp->i_parent);
	if (newinofmt)
		proto.d_type = DT_DIR;
	else
		proto.d_type = 0;
	proto.d_namlen = 2;
	(void)strlcpy(proto.d_name, "..", sizeof(proto.d_name));
#if BYTE_ORDER == LITTLE_ENDIAN
	if (!newinofmt && !needswap) {
#else
	if (!newinofmt && needswap) {
#endif
		u_char tmp;

		tmp = proto.d_type;
		proto.d_type = proto.d_namlen;
		proto.d_namlen = tmp;
	}
	entrysize = DIRSIZ(0, &proto, 0);
	if (idesc->id_entryno == 0) {
		n = DIRSIZ(0, dirp, 0);
		if (iswap16(dirp->d_reclen) < n + entrysize)
			goto chk2;
		proto.d_reclen = iswap16(iswap16(dirp->d_reclen) - n);
		dirp->d_reclen = iswap16(n);
		idesc->id_entryno++;
		inoinfo(iswap32(dirp->d_ino))->ino_linkcnt--;
		dirp = (struct direct *)((char *)(dirp) + n);
		memset(dirp, 0, (size_t)iswap16(proto.d_reclen));
		dirp->d_reclen = proto.d_reclen;
	}
	if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") == 0) {
		inp->i_dotdot = iswap32(dirp->d_ino);
		if (newinofmt && dirp->d_type != DT_DIR) {
			direrror(idesc->id_number, "BAD TYPE VALUE FOR '..'");
			dirp->d_type = DT_DIR;
			if (reply("FIX") == 1)
				ret |= ALTERED;
			else
				markclean = 0;
		}
		goto chk2;
	}
	if (iswap32(dirp->d_ino) != 0 && strcmp(dirp->d_name, ".") != 0) {
		fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
		pfatal("CANNOT FIX, SECOND ENTRY IN DIRECTORY CONTAINS %s\n",
			dirp->d_name);
		inp->i_dotdot = (ino_t)-1;
		markclean = 0;
	} else if (iswap16(dirp->d_reclen) < entrysize) {
		fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
		pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '..'\n");
		inp->i_dotdot = (ino_t)-1;
		markclean = 0;
	} else if (inp->i_parent != 0) {
		/*
		 * We know the parent, so fix now.
		 */
		inp->i_dotdot = inp->i_parent;
		fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
		proto.d_reclen = dirp->d_reclen;
		memmove(dirp, &proto, (size_t)entrysize);
		if (reply("FIX") == 1)
			ret |= ALTERED;
		else
			markclean = 0;
	}
	idesc->id_entryno++;
	if (dirp->d_ino != 0)
		inoinfo(iswap32(dirp->d_ino))->ino_linkcnt--;
	return (ret|KEEPON);
chk2:
	if (dirp->d_ino == 0)
		return (ret|KEEPON);
	if (dirp->d_namlen <= 2 &&
	    dirp->d_name[0] == '.' &&
	    idesc->id_entryno >= 2) {
		if (dirp->d_namlen == 1) {
			direrror(idesc->id_number, "EXTRA '.' ENTRY");
			dirp->d_ino = 0;
			if (reply("FIX") == 1)
				ret |= ALTERED;
			else
				markclean = 0;
			return (KEEPON | ret);
		}
		if (dirp->d_name[1] == '.') {
			direrror(idesc->id_number, "EXTRA '..' ENTRY");
			dirp->d_ino = 0;
			if (reply("FIX") == 1)
				ret |= ALTERED;
			else
				markclean = 0;
			return (KEEPON | ret);
		}
	}
	idesc->id_entryno++;
	n = 0;
	if (iswap32(dirp->d_ino) > maxino) {
		fileerror(idesc->id_number, dirp->d_ino, "I OUT OF RANGE");
		n = reply("REMOVE");
		if (n == 0)
			markclean = 0;
	} else if (newinofmt &&
		   ((iswap32(dirp->d_ino) == WINO && dirp->d_type != DT_WHT) ||
		    (iswap32(dirp->d_ino) != WINO && dirp->d_type == DT_WHT))) {
		fileerror(idesc->id_number, iswap32(dirp->d_ino), "BAD WHITEOUT ENTRY");
		dirp->d_ino = iswap32(WINO);
		dirp->d_type = DT_WHT;
		if (reply("FIX") == 1)
			ret |= ALTERED;
		else
			markclean = 0;
	} else {
again:
		info = inoinfo(iswap32(dirp->d_ino));
		switch (info->ino_state) {
		case USTATE:
			if (idesc->id_entryno <= 2)
				break;
			fileerror(idesc->id_number, iswap32(dirp->d_ino), "UNALLOCATED");
			n = reply("REMOVE");
			if (n == 0)
				markclean = 0;
			break;

		case DCLEAR:
		case FCLEAR:
			if (idesc->id_entryno <= 2)
				break;
			if (info->ino_state == FCLEAR)
				errmsg = "DUP/BAD";
			else if (!preen && !usedsoftdep)
				errmsg = "ZERO LENGTH DIRECTORY";
			else {
				n = 1;
				break;
			}
			fileerror(idesc->id_number, iswap32(dirp->d_ino), errmsg);
			if ((n = reply("REMOVE")) == 1)
				break;
			dp = ginode(iswap32(dirp->d_ino));
			info->ino_state =
			    (iswap16(DIP(dp, mode)) & IFMT) == IFDIR ? DSTATE : FSTATE;
			info->ino_linkcnt = iswap16(DIP(dp, nlink));
			goto again;

		case DSTATE:
		case DFOUND:
			inp = getinoinfo(iswap32(dirp->d_ino));
			if (inp->i_parent != 0 && idesc->id_entryno > 2) {
				getpathname(pathbuf, sizeof(pathbuf),
				    idesc->id_number, idesc->id_number);
				getpathname(namebuf, sizeof(namebuf),
				    iswap32(dirp->d_ino), iswap32(dirp->d_ino));
				pwarn("%s %s %s\n", pathbuf,
				    "IS AN EXTRANEOUS HARD LINK TO DIRECTORY",
				    namebuf);
				if (preen)
					printf(" (IGNORED)\n");
				else if ((n = reply("REMOVE")) == 1)
					break;
			}
			if (idesc->id_entryno > 2)
				inp->i_parent = idesc->id_number;
			/* fall through */

		case FSTATE:
			if (newinofmt && dirp->d_type != info->ino_type) {
				fileerror(idesc->id_number, iswap32(dirp->d_ino),
				    "BAD TYPE VALUE");
				dirp->d_type = info->ino_type;
				if (reply("FIX") == 1)
					ret |= ALTERED;
				else
					markclean = 0;
			}
			info->ino_linkcnt--;
			break;

		default:
			errexit("BAD STATE %d FOR INODE I=%d",
			    info->ino_state, iswap32(dirp->d_ino));
		}
	}
	if (n == 0)
		return (ret|KEEPON);
	dirp->d_ino = 0;
	return (ret|KEEPON|ALTERED);
}

/*
 * Routine to sort disk blocks.
 */
static int
blksort(const void *arg1, const void *arg2)
{

	return ((*(const struct inoinfo *const *)arg1)->i_blks[0] -
		(*(const struct inoinfo *const *)arg2)->i_blks[0]);
}
Exemplo n.º 15
0
/*
 * Perform checks on an inode and setup/track the state of the inode
 * in maps (statemap[], lncntp[]) for future reference and validation.
 * Initiate the calls to ckinode and in turn pass1check() to handle
 * further validation.
 */
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) {
		/* mode and type of file is not set */
		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;
	}

	/*
	 * A more precise test of the type is done later on.  Just get
	 * rid of the blatantly-wrong ones before we do any
	 * significant work.
	 */
	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) {
		/* extra space to distinguish from previous pfatal() */
		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++) {
			/*
			 * It's a device, so all the block pointers
			 * should be zero except for di_ordev.
			 * di_ordev is overlayed on the block array,
			 * but where varies between big and little
			 * endian, so make sure that the only non-zero
			 * element is the correct one.  There can be
			 * a device whose ordev is zero, so we can't
			 * check for the reverse.
			 */
			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 {
		/*
		 * This assignment is mostly here to appease lint, but
		 * doesn't hurt.
		 */
		err = "Internal error: unexpected variant of having "
		    "blocks past end of file  I=%d";

		clear = 0;

		/*
		 * If it's not a device, it has to follow the
		 * rules for files.  In particular, no blocks after
		 * the last one that di_size says is in use.
		 */
		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;
			}
		}

		/*
		 * Find last indirect pointer that should be in use,
		 * and make sure any after it are clear.
		 */
		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) {
			/*
			 * The discarded blocks will be garbage-
			 * collected in pass5.  If we're told not to
			 * discard them, it's just lost blocks, which
			 * isn't worth setting iscorrupt for.
			 */
			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);

	/*
	 * We can't do anything about it right now, so note that its
	 * processing is being delayed.  Otherwise, we'd be changing
	 * the block allocations out from under ourselves, which causes
	 * no end of confusion.
	 */
	flags = statemap[inumber] & INDELAYD;

	/*
	 * if errorlocked or logging, then open deleted files will
	 * manifest as di_nlink <= 0 and di_mode != 0
	 * so skip them; they're ok.
	 * Also skip anything already marked to be cleared.
	 */
	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) {
			/*
			 * INCLEAR means it will be ignored by passes 2 & 3.
			 */
			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;	/* It will be cleared anyway */
		}
		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;	/* It will be cleared anyway */
		}
		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) {
		/*
		 * The kernel releases any blocks it finds in the lists,
		 * ignoring the block count itself.  So, a bad count is
		 * not grounds for setting iscorrupt.
		 */
		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)) {
		/*
		 * INCLEAR will cause passes 2 and 3 to skip it.
		 */
		(void) printf("DIR WITH ZERO BLOCKS  I=%d\n", inumber);
		statemap[inumber] = DCLEAR;
		add_orphan_dir(inumber);
	}

	/*
	 * Check that the ACL is on a valid file type
	 */
	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;

	/*
	 * If we got here, we've not had the chance to see if a
	 * directory has holes, but we know the directory's bad,
	 * so it's safe to always return false (no holes found).
	 *
	 * Also, a pfatal() is always done before jumping here, so
	 * we know we're not in preen mode.
	 */
bogus:
	if (isdir) {
		/*
		 * INCLEAR makes passes 2 & 3 skip it.
		 */
		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;
	}
}