Ejemplo n.º 1
0
static int
find_indirblks64(uint64_t blk, int ind_level, uint64_t *wantedblk)
{
#define MAXNINDIR	(MAXBSIZE / sizeof(uint64_t))
	uint64_t idblk[MAXNINDIR];
	size_t i;

	bread(fsreadfd, (char *)idblk, FFS_FSBTODB(sblock, blk),
	    (int)sblock->fs_bsize);
	if (ind_level <= 0) {
		if (find_blks64(idblk,
		    sblock->fs_bsize / sizeof(uint64_t), wantedblk))
			return 1;
	} else {
		ind_level--;
		for (i = 0; i < sblock->fs_bsize / sizeof(uint64_t); i++) {
			if (compare_blk64(wantedblk, iswap64(idblk[i]))) {
				if (founddatablk(iswap64(idblk[i])))
					return 1;
			}
			if(idblk[i] != 0)
				if (find_indirblks64(iswap64(idblk[i]),
				    ind_level, wantedblk))
				return 1;
		}
	}
#undef MAXNINDIR
	return 0;
}
Ejemplo n.º 2
0
static int
find_blks64(uint64_t *buf, int size, uint64_t *wantedblk)
{
	int blk;
	for(blk = 0; blk < size; blk++) {
		if (buf[blk] == 0)
			continue;
		if (compare_blk64(wantedblk, iswap64(buf[blk]))) {
			if (founddatablk(iswap64(buf[blk])))
				return 1;
		}
	}
	return 0;
}
Ejemplo n.º 3
0
static void
print_indirblks64(uint64_t blk, int ind_level, uint64_t *blknum)
{
#define MAXNINDIR	(MAXBSIZE / sizeof(int64_t))
	const int ptrperblk_shift = sblock->fs_bshift - 3;
	const int ptrperblk = 1 << ptrperblk_shift;
	int64_t idblk[MAXNINDIR];
	int i;

	if (blk == 0) {
		*blknum += (uint64_t)ptrperblk << (ptrperblk_shift * ind_level);
		return;
	}
 
	printf("Indirect block %lld (level %d):\n", (long long)blk,
	    ind_level+1);
	bread(fsreadfd, (char *)idblk, FFS_FSBTODB(sblock, blk),
	    (int)sblock->fs_bsize);
	if (ind_level <= 0) {
		print_blks64(idblk, ptrperblk, blknum);
	} else {
		ind_level--;
		for (i = 0; i < ptrperblk; i++)
			print_indirblks64(iswap64(idblk[i]), ind_level, blknum);
	}
#undef MAXNINDIR
}
Ejemplo n.º 4
0
static void
print_blks64(int64_t *buf, int size, uint64_t *blknum)
{
	int chars;
	char prbuf[CHARS_PER_LINES+1];
	int blk;
 
	chars = 0;
	for(blk = 0; blk < size; blk++, (*blknum)++) {
		if (buf[blk] == 0)
			continue;
		snprintf(prbuf, CHARS_PER_LINES, "%lld ",
		    (long long)iswap64(buf[blk]));
		if ((chars + strlen(prbuf)) > CHARS_PER_LINES) {
			printf("\n");
			chars = 0;
		}
		if (chars == 0)
			printf("%" PRIu64 ": ", *blknum);
		printf("%s", prbuf);
		chars += strlen(prbuf);
	}
	printf("\n");
}
Ejemplo n.º 5
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);
}
Ejemplo n.º 6
0
int
main(int argc, char *argv[])
{
    ino_t ino;
    int dirty;
    union dinode *dp;
    struct fstab *dt;
    struct statvfs *mntinfo, fsbuf;
    char *map, *cp;
    int ch;
    int i, anydirskipped, bflag = 0, Tflag = 0, Fflag = 0, honorlevel = 1;
    int snap_internal = 0;
    ino_t maxino;
    time_t tnow, date;
    int dirc;
    char *mountpoint;
    int just_estimate = 0;
    char labelstr[LBLSIZE];
    char buf[MAXPATHLEN], rbuf[MAXPATHLEN];
    char *new_time_format;
    char *snap_backup = NULL;

    spcl.c_date = 0;
    (void)time(&tnow);
    spcl.c_date = tnow;
    tzset(); /* set up timezone for strftime */
    if ((new_time_format = getenv("TIMEFORMAT")) != NULL)
        time_string = new_time_format;

    tsize = 0;	/* Default later, based on 'c' option for cart tapes */
    if ((tape = getenv("TAPE")) == NULL)
        tape = _PATH_DEFTAPE;
    dumpdates = _PATH_DUMPDATES;
    temp = _PATH_DTMP;
    strcpy(labelstr, "none");	/* XXX safe strcpy. */
    if (TP_BSIZE / DEV_BSIZE == 0 || TP_BSIZE % DEV_BSIZE != 0)
        quit("TP_BSIZE must be a multiple of DEV_BSIZE\n");
    level = '0';
    timestamp = 0;

    if (argc < 2)
        usage();

    obsolete(&argc, &argv);
    while ((ch = getopt(argc, argv,
                        "0123456789aB:b:cd:eFf:h:ik:l:L:nr:s:StT:uWwx:X")) != -1)
        switch (ch) {
        /* dump level */
        case '0':
        case '1':
        case '2':
        case '3':
        case '4':
        case '5':
        case '6':
        case '7':
        case '8':
        case '9':
            level = ch;
            break;

        case 'a':		/* `auto-size', Write to EOM. */
            unlimited = 1;
            break;

        case 'B':		/* blocks per output file */
            blocksperfile = numarg("blocks per file", 1L, 0L);
            break;

        case 'b':		/* blocks per tape write */
            ntrec = numarg("blocks per write", 1L, 1000L);
            bflag = 1;
            break;

        case 'c':		/* Tape is cart. not 9-track */
            cartridge = 1;
            break;

        case 'd':		/* density, in bits per inch */
            density = numarg("density", 10L, 327670L) / 10;
            if (density >= 625 && !bflag)
                ntrec = HIGHDENSITYTREC;
            break;

        case 'e':		/* eject full tapes */
            eflag = 1;
            break;

        case 'F':		/* files-to-dump is an fs image */
            Fflag = 1;
            break;

        case 'f':		/* output file */
            tape = optarg;
            break;

        case 'h':
            honorlevel = numarg("honor level", 0L, 10L);
            break;

        case 'i':	/* "true incremental" regardless level */
            level = 'i';
            trueinc = 1;
            break;

        case 'k':
            readblksize = numarg("read block size", 0, 64) * 1024;
            break;

        case 'l':		/* autoload after eject full tapes */
            eflag = 1;
            lflag = numarg("timeout (in seconds)", 1, 0);
            break;

        case 'L':
            /*
             * Note that although there are LBLSIZE characters,
             * the last must be '\0', so the limit on strlen()
             * is really LBLSIZE-1.
             */
            if (strlcpy(labelstr, optarg, sizeof(labelstr))
                    >= sizeof(labelstr)) {
                msg(
                    "WARNING Label `%s' is larger than limit of %lu characters.\n",
                    optarg,
                    (unsigned long)sizeof(labelstr) - 1);
                msg("WARNING: Using truncated label `%s'.\n",
                    labelstr);
            }
            break;
        case 'n':		/* notify operators */
            notify = 1;
            break;

        case 'r':		/* read cache size */
            readcache = numarg("read cache size", 0, 512);
            break;

        case 's':		/* tape size, feet */
            tsize = numarg("tape size", 1L, 0L) * 12 * 10;
            break;

        case 'S':		/* exit after estimating # of tapes */
            just_estimate = 1;
            break;

        case 't':
            timestamp = 1;
            break;

        case 'T':		/* time of last dump */
            spcl.c_ddate = unctime(optarg);
            if (spcl.c_ddate < 0) {
                (void)fprintf(stderr, "bad time \"%s\"\n",
                              optarg);
                exit(X_STARTUP);
            }
            Tflag = 1;
            lastlevel = '?';
            break;

        case 'u':		/* update /etc/dumpdates */
            uflag = 1;
            break;

        case 'W':		/* what to do */
        case 'w':
            lastdump(ch);
            exit(X_FINOK);	/* do nothing else */

        case 'x':
            snap_backup = optarg;
            break;

        case 'X':
            snap_internal = 1;
            break;

        default:
            usage();
        }
    argc -= optind;
    argv += optind;

    if (argc < 1) {
        (void)fprintf(stderr,
                      "Must specify disk or image, or file list\n");
        exit(X_STARTUP);
    }


    /*
     *	determine if disk is a subdirectory, and setup appropriately
     */
    getfstab();		/* /etc/fstab snarfed */
    disk = NULL;
    disk_dev = NULL;
    mountpoint = NULL;
    dirc = 0;
    for (i = 0; i < argc; i++) {
        struct stat sb;
        int error;

        error = lstat(argv[i], &sb);
        if (Fflag || (!error && (S_ISCHR(sb.st_mode) || S_ISBLK(sb.st_mode)))) {
            if (error)
                quit("Cannot stat %s: %s\n", argv[i], strerror(errno));
            disk = argv[i];
multicheck:
            if (dirc != 0)
                quit("Can't dump a disk or image at the same time as a file list\n");
            break;
        }
        if ((dt = fstabsearch(argv[i])) != NULL) {
            disk = argv[i];
            mountpoint = xstrdup(dt->fs_file);
            goto multicheck;
        }
        if (statvfs(argv[i], &fsbuf) == -1)
            quit("Cannot statvfs %s: %s\n", argv[i],
                 strerror(errno));
        disk = fsbuf.f_mntfromname;
        if (strcmp(argv[i], fsbuf.f_mntonname) == 0)
            goto multicheck;
        if (mountpoint == NULL) {
            mountpoint = xstrdup(fsbuf.f_mntonname);
            if (uflag) {
                msg("Ignoring u flag for subdir dump\n");
                uflag = 0;
            }
            if (level > '0') {
                msg("Subdir dump is done at level 0\n");
                level = '0';
            }
            msg("Dumping sub files/directories from %s\n",
                mountpoint);
        } else {
            if (strcmp(mountpoint, fsbuf.f_mntonname) != 0)
                quit("%s is not on %s\n", argv[i], mountpoint);
        }
        msg("Dumping file/directory %s\n", argv[i]);
        dirc++;
    }
    if (mountpoint)
        free(mountpoint);

    if (dirc == 0) {
        argv++;
        if (argc != 1) {
            (void)fprintf(stderr, "Excess arguments to dump:");
            while (--argc)
                (void)fprintf(stderr, " %s", *argv++);
            (void)fprintf(stderr, "\n");
            exit(X_STARTUP);
        }
    }
    if (Tflag && uflag) {
        (void)fprintf(stderr,
                      "You cannot use the T and u flags together.\n");
        exit(X_STARTUP);
    }
    if (strcmp(tape, "-") == 0) {
        pipeout++;
        tape = "standard output";
    }

    if (blocksperfile)
        blocksperfile = blocksperfile / ntrec * ntrec; /* round down */
    else if (!unlimited) {
        /*
         * Determine how to default tape size and density
         *
         *		density				tape size
         * 9-track	1600 bpi (160 bytes/.1")	2300 ft.
         * 9-track	6250 bpi (625 bytes/.1")	2300 ft.
         * cartridge	8000 bpi (100 bytes/.1")	1700 ft.
         *						(450*4 - slop)
         */
        if (density == 0)
            density = cartridge ? 100 : 160;
        if (tsize == 0)
            tsize = cartridge ? 1700L*120L : 2300L*120L;
    }

    if ((cp = strchr(tape, ':')) != NULL) {
        host = tape;
        /* This is fine, because all the const strings don't have : */
        *cp++ = '\0';
        tape = cp;
#ifdef RDUMP
        if (rmthost(host) == 0)
            exit(X_STARTUP);
#else
        (void)fprintf(stderr, "remote dump not enabled\n");
        exit(X_STARTUP);
#endif
    }

    if (signal(SIGHUP, SIG_IGN) != SIG_IGN)
        signal(SIGHUP, sig);
    if (signal(SIGTRAP, SIG_IGN) != SIG_IGN)
        signal(SIGTRAP, sig);
    if (signal(SIGFPE, SIG_IGN) != SIG_IGN)
        signal(SIGFPE, sig);
    if (signal(SIGBUS, SIG_IGN) != SIG_IGN)
        signal(SIGBUS, sig);
#if 0
    if (signal(SIGSEGV, SIG_IGN) != SIG_IGN)
        signal(SIGSEGV, sig);
#endif
    if (signal(SIGTERM, SIG_IGN) != SIG_IGN)
        signal(SIGTERM, sig);
    if (signal(SIGINT, interrupt) == SIG_IGN)
        signal(SIGINT, SIG_IGN);

    /*
     *	disk can be either the full special file name, or
     *	the file system name.
     */
    mountpoint = NULL;
    mntinfo = mntinfosearch(disk);
    if ((dt = fstabsearch(disk)) != NULL) {
        if (getfsspecname(buf, sizeof(buf), dt->fs_spec) == NULL)
            quit("%s (%s)", buf, strerror(errno));
        if (getdiskrawname(rbuf, sizeof(rbuf), buf) == NULL)
            quit("Can't get disk raw name for `%s' (%s)",
                 buf, strerror(errno));
        disk = rbuf;
        mountpoint = dt->fs_file;
        msg("Found %s on %s in %s\n", disk, mountpoint, _PATH_FSTAB);
    } else if (mntinfo != NULL) {
        if (getdiskrawname(rbuf, sizeof(rbuf), mntinfo->f_mntfromname)
                == NULL)
            quit("Can't get disk raw name for `%s' (%s)",
                 mntinfo->f_mntfromname, strerror(errno));
        disk = rbuf;
        mountpoint = mntinfo->f_mntonname;
        msg("Found %s on %s in mount table\n", disk, mountpoint);
    }
    if (mountpoint != NULL) {
        if (dirc != 0)
            (void)snprintf(spcl.c_filesys, sizeof(spcl.c_filesys),
                           "a subset of %s", mountpoint);
        else
            (void)strlcpy(spcl.c_filesys, mountpoint,
                          sizeof(spcl.c_filesys));
    } else if (Fflag) {
        (void)strlcpy(spcl.c_filesys, "a file system image",
                      sizeof(spcl.c_filesys));
    } else {
        (void)strlcpy(spcl.c_filesys, "an unlisted file system",
                      sizeof(spcl.c_filesys));
    }
    (void)strlcpy(spcl.c_dev, disk, sizeof(spcl.c_dev));
    (void)strlcpy(spcl.c_label, labelstr, sizeof(spcl.c_label));
    (void)gethostname(spcl.c_host, sizeof(spcl.c_host));
    spcl.c_host[sizeof(spcl.c_host) - 1] = '\0';

    if ((snap_backup != NULL || snap_internal) && mntinfo == NULL) {
        msg("WARNING: Cannot use -x or -X on unmounted file system.\n");
        snap_backup = NULL;
        snap_internal = 0;
    }

#ifdef DUMP_LFS
    sync();
    if (snap_backup != NULL || snap_internal) {
        if (lfs_wrap_stop(mountpoint) < 0) {
            msg("Cannot stop writing on %s\n", mountpoint);
            exit(X_STARTUP);
        }
    }
    if ((diskfd = open(disk, O_RDONLY)) < 0) {
        msg("Cannot open %s\n", disk);
        exit(X_STARTUP);
    }
    disk_dev = disk;
#else /* ! DUMP_LFS */
    if (snap_backup != NULL || snap_internal) {
        diskfd = snap_open(mntinfo->f_mntonname, snap_backup,
                           &tnow, &disk_dev);
        if (diskfd < 0) {
            msg("Cannot open snapshot of %s\n",
                mntinfo->f_mntonname);
            exit(X_STARTUP);
        }
        spcl.c_date = tnow;
    } else {
        if ((diskfd = open(disk, O_RDONLY)) < 0) {
            msg("Cannot open %s\n", disk);
            exit(X_STARTUP);
        }
        disk_dev = disk;
    }
    sync();
#endif /* ! DUMP_LFS */

    needswap = fs_read_sblock(sblock_buf);

    /* true incremental is always a level 10 dump */
    spcl.c_level = trueinc? iswap32(10): iswap32(level - '0');
    spcl.c_type = iswap32(TS_TAPE);
    spcl.c_date = iswap32(spcl.c_date);
    spcl.c_ddate = iswap32(spcl.c_ddate);
    if (!Tflag)
        getdumptime();		/* /etc/dumpdates snarfed */

    date = iswap32(spcl.c_date);
    msg("Date of this level %c dump: %s", level,
        spcl.c_date == 0 ? "the epoch\n" : ctime(&date));
    date = iswap32(spcl.c_ddate);
    msg("Date of last level %c dump: %s", lastlevel,
        spcl.c_ddate == 0 ? "the epoch\n" : ctime(&date));
    msg("Dumping ");
    if (snap_backup != NULL || snap_internal)
        msgtail("a snapshot of ");
    if (dirc != 0)
        msgtail("a subset of ");
    msgtail("%s (%s) ", disk, spcl.c_filesys);
    if (host)
        msgtail("to %s on host %s\n", tape, host);
    else
        msgtail("to %s\n", tape);
    msg("Label: %s\n", labelstr);

    ufsib = fs_parametrize();

    dev_bshift = ffs(dev_bsize) - 1;
    if (dev_bsize != (1 << dev_bshift))
        quit("dev_bsize (%ld) is not a power of 2", dev_bsize);
    tp_bshift = ffs(TP_BSIZE) - 1;
    if (TP_BSIZE != (1 << tp_bshift))
        quit("TP_BSIZE (%d) is not a power of 2", TP_BSIZE);
    maxino = fs_maxino();
    mapsize = roundup(howmany(maxino, NBBY), TP_BSIZE);
    usedinomap = (char *)xcalloc((unsigned) mapsize, sizeof(char));
    dumpdirmap = (char *)xcalloc((unsigned) mapsize, sizeof(char));
    dumpinomap = (char *)xcalloc((unsigned) mapsize, sizeof(char));
    tapesize = 3 * (howmany(mapsize * sizeof(char), TP_BSIZE) + 1);

    nonodump = iswap32(spcl.c_level) < honorlevel;

    initcache(readcache, readblksize);

    (void)signal(SIGINFO, statussig);

    msg("mapping (Pass I) [regular files]\n");
    anydirskipped = mapfiles(maxino, &tapesize, mountpoint,
                             (dirc ? argv : NULL));

    msg("mapping (Pass II) [directories]\n");
    while (anydirskipped) {
        anydirskipped = mapdirs(maxino, &tapesize);
    }

    if (pipeout || unlimited) {
        tapesize += 10;	/* 10 trailer blocks */
        msg("estimated %llu tape blocks.\n",
            (unsigned long long)tapesize);
    } else {
        double fetapes;

        if (blocksperfile)
            fetapes = (double) tapesize / blocksperfile;
        else if (cartridge) {
            /* Estimate number of tapes, assuming streaming stops at
               the end of each block written, and not in mid-block.
               Assume no erroneous blocks; this can be compensated
               for with an artificially low tape size. */
            fetapes =
                (	  (double) tapesize	/* blocks */
                      * TP_BSIZE	/* bytes/block */
                      * (1.0/density)	/* 0.1" / byte */
                      +
                      (double) tapesize	/* blocks */
                      * (1.0/ntrec)	/* streaming-stops per block */
                      * 15.48		/* 0.1" / streaming-stop */
                ) * (1.0 / tsize );	/* tape / 0.1" */
        } else {
            /* Estimate number of tapes, for old fashioned 9-track
               tape */
            int tenthsperirg = (density == 625) ? 3 : 7;
            fetapes =
                (	  tapesize	/* blocks */
                      * TP_BSIZE	/* bytes / block */
                      * (1.0/density)	/* 0.1" / byte */
                      +
                      tapesize	/* blocks */
                      * (1.0/ntrec)	/* IRG's / block */
                      * tenthsperirg	/* 0.1" / IRG */
                ) * (1.0 / tsize );	/* tape / 0.1" */
        }
        etapes = fetapes;		/* truncating assignment */
        etapes++;
        /* count the dumped inodes map on each additional tape */
        tapesize += (etapes - 1) *
                    (howmany(mapsize * sizeof(char), TP_BSIZE) + 1);
        tapesize += etapes + 10;	/* headers + 10 trailer blks */
        msg("estimated %llu tape blocks on %3.2f tape(s).\n",
            (unsigned long long)tapesize, fetapes);
    }
    /*
     * If the user only wants an estimate of the number of
     * tapes, exit now.
     */
    if (just_estimate)
        exit(X_FINOK);

    /*
     * Allocate tape buffer.
     */
    if (!alloctape())
        quit("can't allocate tape buffers - try a smaller blocking factor.\n");

    startnewtape(1);
    (void)time((time_t *)&(tstart_writing));
    xferrate = 0;
    dumpmap(usedinomap, TS_CLRI, maxino - 1);

    msg("dumping (Pass III) [directories]\n");
    dirty = 0;		/* XXX just to get gcc to shut up */
    for (map = dumpdirmap, ino = 1; ino < maxino; ino++) {
        if (((ino - 1) % NBBY) == 0)	/* map is offset by 1 */
            dirty = *map++;
        else
            dirty >>= 1;
        if ((dirty & 1) == 0)
            continue;
        /*
         * Skip directory inodes deleted and maybe reallocated
         */
        dp = getino(ino);
        if ((DIP(dp, mode) & IFMT) != IFDIR)
            continue;
        (void)dumpino(dp, ino);
    }

    msg("dumping (Pass IV) [regular files]\n");
    for (map = dumpinomap, ino = 1; ino < maxino; ino++) {
        int mode;

        if (((ino - 1) % NBBY) == 0)	/* map is offset by 1 */
            dirty = *map++;
        else
            dirty >>= 1;
        if ((dirty & 1) == 0)
            continue;
        /*
         * Skip inodes deleted and reallocated as directories.
         */
        dp = getino(ino);
        mode = DIP(dp, mode) & IFMT;
        if (mode == IFDIR)
            continue;
        (void)dumpino(dp, ino);
    }

    spcl.c_type = iswap32(TS_END);
    for (i = 0; i < ntrec; i++)
        writeheader(maxino - 1);
    if (pipeout)
        msg("%lld tape blocks\n",(long long)iswap64(spcl.c_tapea));
    else
        msg("%lld tape blocks on %d volume%s\n",
            (long long)iswap64(spcl.c_tapea), iswap32(spcl.c_volume),
            (iswap32(spcl.c_volume) == 1) ? "" : "s");
    tnow = do_stats();
    date = iswap32(spcl.c_date);
    msg("Date of this level %c dump: %s", level,
        spcl.c_date == 0 ? "the epoch\n" : ctime(&date));
    msg("Date this dump completed:  %s", ctime(&tnow));
    msg("Average transfer rate: %d KB/s\n", xferrate / tapeno);
    putdumptime();
    trewind(0);
    broadcast("DUMP IS DONE!\a\a\n");
#ifdef DUMP_LFS
    lfs_wrap_go();
#endif /* DUMP_LFS */
    msg("DUMP IS DONE\n");
    Exit(X_FINOK);
    /* NOTREACHED */
    exit(X_FINOK);		/* XXX: to satisfy gcc */
}
Ejemplo n.º 7
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 */
}