Пример #1
0
static int check_dotdot(e2fsck_t ctx,
			struct ext2_dir_entry *dirent,
			ext2_ino_t ino, struct problem_context *pctx)
{
	int	rec_len, problem = 0;

	if (!dirent->inode)
		problem = PR_2_MISSING_DOT_DOT;
	else if (((dirent->name_len & 0xFF) != 2) ||
		 (dirent->name[0] != '.') ||
		 (dirent->name[1] != '.'))
		problem = PR_2_2ND_NOT_DOT_DOT;
	else if (dirent->name[2] != '\0')
		problem = PR_2_DOT_DOT_NULL_TERM;

	(void) ext2fs_get_rec_len(ctx->fs, dirent, &rec_len);
	if (problem) {
		if (fix_problem(ctx, problem, pctx)) {
			if (rec_len < 12)
				dirent->rec_len = 12;
			dirent->inode = EXT2_ROOT_INO;
			dirent->name_len = 2;
			dirent->name[0] = '.';
			dirent->name[1] = '.';
			dirent->name[2] = '\0';
			return 1;
		}
		return 0;
	}
	if (e2fsck_dir_info_set_dotdot(ctx, ino, dirent->inode)) {
		fix_problem(ctx, PR_2_NO_DIRINFO, pctx);
		return -1;
	}
	return 0;
}
Пример #2
0
static void salvage_directory(ext2_filsys fs,
			      struct ext2_dir_entry *dirent,
			      struct ext2_dir_entry *prev,
			      unsigned int *offset)
{
	char	*cp = (char *) dirent;
	int left;
	unsigned int rec_len, prev_rec_len;
	unsigned int name_len = dirent->name_len & 0xFF;

	(void) ext2fs_get_rec_len(fs, dirent, &rec_len);
	left = fs->blocksize - *offset - rec_len;

	if ((left >= 12) && (rec_len == 8)) {
		memmove(cp, cp+8, left);
		memset(cp + left, 0, 8);
		return;
	}
	if ((left < 0) &&
	    ((int) rec_len + left > 8) &&
	    (name_len + 8 <= (int) rec_len + left) &&
	    dirent->inode <= fs->super->s_inodes_count &&
	    strnlen(dirent->name, name_len) == name_len) {
		(void) ext2fs_set_rec_len(fs, (int) rec_len + left, dirent);
		return;
	}
	if (prev && rec_len && (rec_len % 4) == 0 &&
	    (*offset + rec_len <= fs->blocksize)) {
		(void) ext2fs_get_rec_len(fs, prev, &prev_rec_len);
		prev_rec_len += rec_len;
		(void) ext2fs_set_rec_len(fs, prev_rec_len, prev);
		*offset += rec_len;
		return;
	}
	if (prev) {
		(void) ext2fs_get_rec_len(fs, prev, &prev_rec_len);
		prev_rec_len += fs->blocksize - *offset;
		(void) ext2fs_set_rec_len(fs, prev_rec_len, prev);
		*offset = fs->blocksize;
	} else {
		rec_len = fs->blocksize - *offset;
		(void) ext2fs_set_rec_len(fs, rec_len, dirent);
		dirent->name_len = 0;
		dirent->inode = 0;
	}
}
Пример #3
0
/*
 * Make sure the first entry in the directory is '.', and that the
 * directory entry is sane.
 */
static int check_dot(e2fsck_t ctx,
		     struct ext2_dir_entry *dirent,
		     ext2_ino_t ino, struct problem_context *pctx)
{
	struct ext2_dir_entry *nextdir;
	unsigned int	rec_len, new_len;
	int		status = 0;
	int		created = 0;
	problem_t	problem = 0;

	if (!dirent->inode)
		problem = PR_2_MISSING_DOT;
	else if ((ext2fs_dirent_name_len(dirent) != 1) ||
		 (dirent->name[0] != '.'))
		problem = PR_2_1ST_NOT_DOT;
	else if (dirent->name[1] != '\0')
		problem = PR_2_DOT_NULL_TERM;

	(void) ext2fs_get_rec_len(ctx->fs, dirent, &rec_len);
	if (problem) {
		if (fix_problem(ctx, problem, pctx)) {
			if (rec_len < 12)
				rec_len = dirent->rec_len = 12;
			dirent->inode = ino;
			ext2fs_dirent_set_name_len(dirent, 1);
			ext2fs_dirent_set_file_type(dirent, EXT2_FT_UNKNOWN);
			dirent->name[0] = '.';
			dirent->name[1] = '\0';
			status = 1;
			created = 1;
		}
	}
	if (dirent->inode != ino) {
		if (fix_problem(ctx, PR_2_BAD_INODE_DOT, pctx)) {
			dirent->inode = ino;
			status = 1;
		}
	}
	if (rec_len > 12) {
		new_len = rec_len - 12;
		if (new_len > 12) {
			if (created ||
			    fix_problem(ctx, PR_2_SPLIT_DOT, pctx)) {
				nextdir = (struct ext2_dir_entry *)
					((char *) dirent + 12);
				dirent->rec_len = 12;
				(void) ext2fs_set_rec_len(ctx->fs, new_len,
							  nextdir);
				nextdir->inode = 0;
				ext2fs_dirent_set_name_len(nextdir, 0);
				ext2fs_dirent_set_file_type(nextdir,
							    EXT2_FT_UNKNOWN);
				status = 1;
			}
		}
	}
	return status;
}
Пример #4
0
/*
 * This function expands '%dX' expressions
 */
static _INLINE_ void expand_dirent_expression(ext2_filsys fs, char ch,
					      struct problem_context *ctx)
{
	struct ext2_dir_entry	*dirent;
	unsigned int rec_len;
	int	len;

	if (!ctx || !ctx->dirent)
		goto no_dirent;

	dirent = ctx->dirent;

	switch (ch) {
	case 'i':
		printf("%u", dirent->inode);
		break;
	case 'n':
		len = dirent->name_len & 0xFF;
		if (len > EXT2_NAME_LEN)
			len = EXT2_NAME_LEN;
		if ((ext2fs_get_rec_len(fs, dirent, &rec_len) == 0) &&
		    (len > rec_len))
			len = rec_len;
		safe_print(dirent->name, len);
		break;
	case 'r':
		(void) ext2fs_get_rec_len(fs, dirent, &rec_len);
		printf("%u", rec_len);
		break;
	case 'l':
		printf("%u", dirent->name_len & 0xFF);
		break;
	case 't':
		printf("%u", dirent->name_len >> 8);
		break;
	default:
	no_dirent:
		printf("%%D%c", ch);
		break;
	}
}
Пример #5
0
/*
 * This function expands '%dX' expressions
 */
static _INLINE_ void expand_dirent_expression(FILE *f, ext2_filsys fs, char ch,
					      struct problem_context *ctx)
{
	struct ext2_dir_entry	*dirent;
	unsigned int rec_len, len;

	if (!ctx || !ctx->dirent)
		goto no_dirent;

	dirent = ctx->dirent;

	switch (ch) {
	case 'i':
		fprintf(f, "%u", dirent->inode);
		break;
	case 'n':
		len = ext2fs_dirent_name_len(dirent);
		if ((ext2fs_get_rec_len(fs, dirent, &rec_len) == 0) &&
		    (len > rec_len))
			len = rec_len;
		safe_print(f, dirent->name, len);
		break;
	case 'r':
		(void) ext2fs_get_rec_len(fs, dirent, &rec_len);
		fprintf(f, "%u", rec_len);
		break;
	case 'l':
		fprintf(f, "%u", ext2fs_dirent_name_len(dirent));
		break;
	case 't':
		fprintf(f, "%u", ext2fs_dirent_file_type(dirent));
		break;
	default:
	no_dirent:
		fprintf(f, "%%D%c", ch);
		break;
	}
}
Пример #6
0
static int search_dir_block(ext2_filsys fs, blk_t *blocknr,
			    e2_blkcnt_t blockcnt,
			    blk_t ref_blk EXT2FS_ATTR((unused)),
			    int ref_offset EXT2FS_ATTR((unused)),
			    void *priv_data)
{
	struct process_block_struct *p;
	struct ext2_dir_entry *dirent;
	errcode_t	       	errcode;
	unsigned int		offset = 0;
	unsigned int		rec_len;

	if (blockcnt < 0)
		return 0;

	p = (struct process_block_struct *) priv_data;

	errcode = io_channel_read_blk(current_fs->io, *blocknr, 1, p->buf);
	if (errcode) {
		com_err("search_dir_block", errcode,
			"while reading block %lu", (unsigned long) *blocknr);
		return BLOCK_ABORT;
	}

	while (offset < fs->blocksize) {
		dirent = (struct ext2_dir_entry *) (p->buf + offset);
		errcode = ext2fs_get_rec_len(fs, dirent, &rec_len);
		if (errcode) {
			com_err("htree_dump_leaf_inode", errcode,
				"while getting rec_len for block %lu",
				(unsigned long) *blocknr);
			return BLOCK_ABORT;
		}
		if (dirent->inode &&
		    p->len == (dirent->name_len & 0xFF) &&
		    strncmp(p->search_name, dirent->name,
			    p->len) == 0) {
			printf("Entry found at logical block %lld, "
			       "phys %u, offset %u\n", (long long)blockcnt,
			       *blocknr, offset);
			printf("offset %u\n", offset);
			return BLOCK_ABORT;
		}
		offset += rec_len;
	}
	return 0;
}
Пример #7
0
/*
 * Make sure the second entry in the directory is '..', and that the
 * directory entry is sane.  We do not check the inode number of '..'
 * here; this gets done in pass 3.
 */
static int check_dotdot(e2fsck_t ctx,
			struct ext2_dir_entry *dirent,
			ext2_ino_t ino, struct problem_context *pctx)
{
	problem_t	problem = 0;
	unsigned int	rec_len;

	if (!dirent->inode)
		problem = PR_2_MISSING_DOT_DOT;
	else if ((ext2fs_dirent_name_len(dirent) != 2) ||
		 (dirent->name[0] != '.') ||
		 (dirent->name[1] != '.'))
		problem = PR_2_2ND_NOT_DOT_DOT;
	else if (dirent->name[2] != '\0')
		problem = PR_2_DOT_DOT_NULL_TERM;

	(void) ext2fs_get_rec_len(ctx->fs, dirent, &rec_len);
	if (problem) {
		if (fix_problem(ctx, problem, pctx)) {
			if (rec_len < 12)
				dirent->rec_len = 12;
			/*
			 * Note: we don't have the parent inode just
			 * yet, so we will fill it in with the root
			 * inode.  This will get fixed in pass 3.
			 */
			dirent->inode = EXT2_ROOT_INO;
			ext2fs_dirent_set_name_len(dirent, 2);
			ext2fs_dirent_set_file_type(dirent, EXT2_FT_UNKNOWN);
			dirent->name[0] = '.';
			dirent->name[1] = '.';
			dirent->name[2] = '\0';
			return 1;
		}
		return 0;
	}
	if (e2fsck_dir_info_set_dotdot(ctx, ino, dirent->inode)) {
		fix_problem(ctx, PR_2_NO_DIRINFO, pctx);
		return -1;
	}
	return 0;
}
Пример #8
0
errcode_t ext2fs_write_dir_block3(ext2_filsys fs, blk64_t block,
				  void *inbuf, int flags EXT2FS_ATTR((unused)))
{
#ifdef WORDS_BIGENDIAN
	errcode_t	retval;
	char		*p, *end;
	char		*buf = 0;
	unsigned int	rec_len;
	struct ext2_dir_entry *dirent;

	retval = ext2fs_get_mem(fs->blocksize, &buf);
	if (retval)
		return retval;
	memcpy(buf, inbuf, fs->blocksize);
	p = buf;
	end = buf + fs->blocksize;
	while (p < end) {
		dirent = (struct ext2_dir_entry *) p;
		if ((retval = ext2fs_get_rec_len(fs, dirent, &rec_len)) != 0)
			return retval;
		if ((rec_len < 8) ||
		    (rec_len % 4)) {
			ext2fs_free_mem(&buf);
			return (EXT2_ET_DIR_CORRUPTED);
		}
		p += rec_len;
		dirent->inode = ext2fs_swab32(dirent->inode);
		dirent->rec_len = ext2fs_swab16(dirent->rec_len);
		dirent->name_len = ext2fs_swab16(dirent->name_len);

		if (flags & EXT2_DIRBLOCK_V2_STRUCT)
			dirent->name_len = ext2fs_swab16(dirent->name_len);
	}
	retval = io_channel_write_blk64(fs->io, block, 1, buf);
	ext2fs_free_mem(&buf);
	return retval;
#else
	return io_channel_write_blk(fs->io, block, 1, (char *) inbuf);
#endif
}
Пример #9
0
errcode_t ext2fs_read_dir_block3(ext2_filsys fs, blk64_t block,
				 void *buf, int flags EXT2FS_ATTR((unused)))
{
	errcode_t	retval;
	char		*p, *end;
	struct ext2_dir_entry *dirent;
	unsigned int	name_len, rec_len;


	retval = io_channel_read_blk64(fs->io, block, 1, buf);
	if (retval)
		return retval;

	p = (char *) buf;
	end = (char *) buf + fs->blocksize;
	while (p < end-8) {
		dirent = (struct ext2_dir_entry *) p;
#ifdef WORDS_BIGENDIAN
		dirent->inode = ext2fs_swab32(dirent->inode);
		dirent->rec_len = ext2fs_swab16(dirent->rec_len);
		dirent->name_len = ext2fs_swab16(dirent->name_len);
#endif
		name_len = dirent->name_len;
#ifdef WORDS_BIGENDIAN
		if (flags & EXT2_DIRBLOCK_V2_STRUCT)
			dirent->name_len = ext2fs_swab16(dirent->name_len);
#endif
		if ((retval = ext2fs_get_rec_len(fs, dirent, &rec_len)) != 0)
			return retval;
		if ((rec_len < 8) || (rec_len % 4)) {
			rec_len = 8;
			retval = EXT2_ET_DIR_CORRUPTED;
		} else if (((name_len & 0xFF) + 8) > rec_len)
			retval = EXT2_ET_DIR_CORRUPTED;
		p += rec_len;
	}
	return retval;
}
Пример #10
0
static void htree_dump_leaf_node(ext2_filsys fs, ext2_ino_t ino,
				 struct ext2_inode *inode,
				 struct ext2_dx_root_info * rootnode,
				 blk_t blk, char *buf)
{
	errcode_t	errcode;
	struct ext2_dir_entry *dirent;
	int		thislen, col = 0;
	unsigned int	offset = 0;
	char		name[EXT2_NAME_LEN + 1];
	char		tmp[EXT2_NAME_LEN + 16];
	blk_t		pblk;
	ext2_dirhash_t 	hash, minor_hash;
	unsigned int	rec_len;
	int		hash_alg;

	errcode = ext2fs_bmap(fs, ino, inode, buf, 0, blk, &pblk);
	if (errcode) {
		com_err("htree_dump_leaf_node", errcode,
			"while mapping logical block %u\n", blk);
		return;
	}

	printf("Reading directory block %lu, phys %lu\n",
	       (unsigned long) blk, (unsigned long) pblk);
	errcode = ext2fs_read_dir_block2(current_fs, pblk, buf, 0);
	if (errcode) {
		com_err("htree_dump_leaf_node", errcode,
			"while reading block %lu (%lu)\n",
			(unsigned long) blk, (unsigned long) pblk);
		return;
	}
	hash_alg = rootnode->hash_version;
	if ((hash_alg <= EXT2_HASH_TEA) &&
	    (fs->super->s_flags & EXT2_FLAGS_UNSIGNED_HASH))
		hash_alg += 3;

	while (offset < fs->blocksize) {
		dirent = (struct ext2_dir_entry *) (buf + offset);
		errcode = ext2fs_get_rec_len(fs, dirent, &rec_len);
		if (errcode) {
			com_err("htree_dump_leaf_inode", errcode,
				"while getting rec_len for block %lu",
				(unsigned long) blk);
			return;
		}
		if (((offset + rec_len) > fs->blocksize) ||
		    (rec_len < 8) ||
		    ((rec_len % 4) != 0) ||
		    ((((unsigned) dirent->name_len & 0xFF)+8) > rec_len)) {
			fprintf(pager, "Corrupted directory block (%u)!\n", blk);
			break;
		}
		thislen = ((dirent->name_len & 0xFF) < EXT2_NAME_LEN) ?
			(dirent->name_len & 0xFF) : EXT2_NAME_LEN;
		strncpy(name, dirent->name, thislen);
		name[thislen] = '\0';
		errcode = ext2fs_dirhash(hash_alg, name,
					 thislen, fs->super->s_hash_seed,
					 &hash, &minor_hash);
		if (errcode)
			com_err("htree_dump_leaf_node", errcode,
				"while calculating hash");
		sprintf(tmp, "%u 0x%08x-%08x (%d) %s   ", dirent->inode,
			hash, minor_hash, rec_len, name);
		thislen = strlen(tmp);
		if (col + thislen > 80) {
			fprintf(pager, "\n");
			col = 0;
		}
		fprintf(pager, "%s", tmp);
		col += thislen;
		offset += rec_len;
	}
	fprintf(pager, "\n");
}
Пример #11
0
static int link_proc(struct ext2_dir_entry *dirent,
		     int	offset,
		     int	blocksize,
		     char	*buf,
		     void	*priv_data)
{

	struct link_struct *ls = (struct link_struct *) priv_data;
	struct ext2_dir_entry *next;
	unsigned int rec_len, min_rec_len, curr_rec_len;
	int ret = 0;

	printf("Trying to link inode %lu at offset %lu\n", ls->inode, offset);
	rec_len = EXT2_DIR_REC_LEN(ls->namelen);

	ls->err = ext2fs_get_rec_len(ls->fs, dirent, &curr_rec_len);
	if (ls->err) {
		printf("Error: Problem with reading current rec len\n");
		return DIRENT_ABORT;
	}

	printf("Current rec len: %d\n", curr_rec_len);

	/*
	 * See if the following directory entry (if any) is unused;
	 * if so, absorb it into this one.
	 */
	next = (struct ext2_dir_entry *) (buf + offset + curr_rec_len);
	if ((offset + curr_rec_len < blocksize - 8) &&
	    (next->inode == 0) &&
	    (offset + curr_rec_len + next->rec_len <= blocksize)) {
		printf("Absorbing following dirent into this one\n");
		curr_rec_len += next->rec_len;
		ls->err = ext2fs_set_rec_len(ls->fs, curr_rec_len, dirent);
		if (ls->err) {
			printf("Problem with setting rec len for new dirent\n");
			return DIRENT_ABORT;
		}
		ret = DIRENT_CHANGED;
	}

	/*
	 * If the directory entry is used, see if we can split the
	 * directory entry to make room for the new name.  If so,
	 * truncate it and return.
	 */
	if (dirent->inode) {
		printf("Handling used dirent\n");
		min_rec_len = EXT2_DIR_REC_LEN(dirent->name_len & 0xFF);
		if (curr_rec_len < (min_rec_len + rec_len))
			return ret;
		rec_len = curr_rec_len - min_rec_len;
		ls->err = ext2fs_set_rec_len(ls->fs, min_rec_len, dirent);
		if (ls->err)
			return DIRENT_ABORT;
		next = (struct ext2_dir_entry *) (buf + offset +
						  dirent->rec_len);
		next->inode = 0;
		next->name_len = 0;
		ls->err = ext2fs_set_rec_len(ls->fs, rec_len, next);
		if (ls->err)
			return DIRENT_ABORT;
		return DIRENT_CHANGED;
	}

	/*
	 * If we get this far, then the directory entry is not used.
	 * See if we can fit the request entry in.  If so, do it.
	 */
	if (curr_rec_len < rec_len)
		return ret;
	printf("Setting dirent based on ls information\n");
	dirent->inode = ls->inode;
	dirent->name_len = ls->namelen;
	strncpy(dirent->name, ls->name, ls->namelen);
	if (ls->sb->s_feature_incompat & EXT2_FEATURE_INCOMPAT_FILETYPE)
		dirent->name_len |= (ls->flags & 0x7) << 8;

	ls->done++;
	return DIRENT_ABORT|DIRENT_CHANGED;
}
Пример #12
0
static int link_proc(struct ext2_dir_entry *dirent,
		     int	offset,
		     int	blocksize,
		     char	*buf,
		     void	*priv_data)
{
	struct link_struct *ls = (struct link_struct *) priv_data;
	struct ext2_dir_entry *next;
	unsigned int rec_len, min_rec_len, curr_rec_len;
	int ret = 0;

	rec_len = EXT2_DIR_REC_LEN(ls->namelen);

	ls->err = ext2fs_get_rec_len(ls->fs, dirent, &curr_rec_len);
	if (ls->err)
		return DIRENT_ABORT;

	/*
	 * See if the following directory entry (if any) is unused;
	 * if so, absorb it into this one.
	 */
	next = (struct ext2_dir_entry *) (buf + offset + curr_rec_len);
	if ((offset + (int) curr_rec_len < blocksize - 8) &&
	    (next->inode == 0) &&
	    (offset + (int) curr_rec_len + (int) next->rec_len <= blocksize)) {
		curr_rec_len += next->rec_len;
		ls->err = ext2fs_set_rec_len(ls->fs, curr_rec_len, dirent);
		if (ls->err)
			return DIRENT_ABORT;
		ret = DIRENT_CHANGED;
	}

	/*
	 * If the directory entry is used, see if we can split the
	 * directory entry to make room for the new name.  If so,
	 * truncate it and return.
	 */
	if (dirent->inode) {
		min_rec_len = EXT2_DIR_REC_LEN(dirent->name_len & 0xFF);
		if (curr_rec_len < (min_rec_len + rec_len))
			return ret;
		rec_len = curr_rec_len - min_rec_len;
		ls->err = ext2fs_set_rec_len(ls->fs, min_rec_len, dirent);
		if (ls->err)
			return DIRENT_ABORT;
		next = (struct ext2_dir_entry *) (buf + offset +
						  dirent->rec_len);
		next->inode = 0;
		next->name_len = 0;
		ls->err = ext2fs_set_rec_len(ls->fs, rec_len, next);
		if (ls->err)
			return DIRENT_ABORT;
		return DIRENT_CHANGED;
	}

	/*
	 * If we get this far, then the directory entry is not used.
	 * See if we can fit the request entry in.  If so, do it.
	 */
	if (curr_rec_len < rec_len)
		return ret;
	dirent->inode = ls->inode;
	dirent->name_len = ls->namelen;
	strncpy(dirent->name, ls->name, ls->namelen);
	if (ls->sb->s_feature_incompat & EXT2_FEATURE_INCOMPAT_FILETYPE)
		dirent->name_len |= (ls->flags & 0x7) << 8;

	ls->done++;
	return DIRENT_ABORT|DIRENT_CHANGED;
}
Пример #13
0
static int fill_dir_block(ext2_filsys fs,
			  blk64_t *block_nr,
			  e2_blkcnt_t blockcnt,
			  blk64_t ref_block EXT2FS_ATTR((unused)),
			  int ref_offset EXT2FS_ATTR((unused)),
			  void *priv_data)
{
	struct fill_dir_struct	*fd = (struct fill_dir_struct *) priv_data;
	struct hash_entry 	*new_array, *ent;
	struct ext2_dir_entry 	*dirent;
	char			*dir;
	unsigned int		offset, dir_offset, rec_len, name_len;
	int			hash_alg;

	if (blockcnt < 0)
		return 0;

	offset = blockcnt * fs->blocksize;
	if (offset + fs->blocksize > fd->inode->i_size) {
		fd->err = EXT2_ET_DIR_CORRUPTED;
		return BLOCK_ABORT;
	}

	dir = (fd->buf+offset);
	if (*block_nr == 0) {
		memset(dir, 0, fs->blocksize);
		dirent = (struct ext2_dir_entry *) dir;
		(void) ext2fs_set_rec_len(fs, fs->blocksize, dirent);
	} else {
		int flags = fs->flags;
		fs->flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS;
		fd->err = ext2fs_read_dir_block4(fs, *block_nr, dir, 0,
						 fd->dir);
		fs->flags = (flags & EXT2_FLAG_IGNORE_CSUM_ERRORS) |
			    (fs->flags & ~EXT2_FLAG_IGNORE_CSUM_ERRORS);
		if (fd->err)
			return BLOCK_ABORT;
	}
	hash_alg = fs->super->s_def_hash_version;
	if ((hash_alg <= EXT2_HASH_TEA) &&
	    (fs->super->s_flags & EXT2_FLAGS_UNSIGNED_HASH))
		hash_alg += 3;
	/* While the directory block is "hot", index it. */
	dir_offset = 0;
	while (dir_offset < fs->blocksize) {
		dirent = (struct ext2_dir_entry *) (dir + dir_offset);
		(void) ext2fs_get_rec_len(fs, dirent, &rec_len);
		name_len = ext2fs_dirent_name_len(dirent);
		if (((dir_offset + rec_len) > fs->blocksize) ||
		    (rec_len < 8) ||
		    ((rec_len % 4) != 0) ||
		    (name_len + 8 > rec_len)) {
			fd->err = EXT2_ET_DIR_CORRUPTED;
			return BLOCK_ABORT;
		}
		dir_offset += rec_len;
		if (dirent->inode == 0)
			continue;
		if (!fd->compress && (name_len == 1) &&
		    (dirent->name[0] == '.'))
			continue;
		if (!fd->compress && (name_len == 2) &&
		    (dirent->name[0] == '.') && (dirent->name[1] == '.')) {
			fd->parent = dirent->inode;
			continue;
		}
		if (fd->num_array >= fd->max_array) {
			new_array = realloc(fd->harray,
			    sizeof(struct hash_entry) * (fd->max_array+500));
			if (!new_array) {
				fd->err = ENOMEM;
				return BLOCK_ABORT;
			}
			fd->harray = new_array;
			fd->max_array += 500;
		}
		ent = fd->harray + fd->num_array++;
		ent->dir = dirent;
		fd->dir_size += EXT2_DIR_REC_LEN(name_len);
		ent->ino = dirent->inode;
		if (fd->compress)
			ent->hash = ent->minor_hash = 0;
		else {
			fd->err = ext2fs_dirhash(hash_alg, dirent->name,
						 name_len,
						 fs->super->s_hash_seed,
						 &ent->hash, &ent->minor_hash);
			if (fd->err)
				return BLOCK_ABORT;
		}
	}

	return 0;
}
Пример #14
0
int ext2fs_inline_data_dir_iterate(ext2_filsys fs, ext2_ino_t ino,
                                   void *priv_data)
{
    struct dir_context *ctx;
    struct ext2_inode inode;
    struct ext2_dir_entry dirent;
    struct ext2_inline_data data;
    int ret = BLOCK_ABORT;
    e2_blkcnt_t blockcnt = 0;
    char *old_buf;
    unsigned int old_buflen;
    int old_flags;

    ctx = (struct dir_context *)priv_data;
    old_buf = ctx->buf;
    old_buflen = ctx->buflen;
    old_flags = ctx->flags;
    ctx->flags |= DIRENT_FLAG_INCLUDE_INLINE_DATA;

    ctx->errcode = ext2fs_read_inode(fs, ino, &inode);
    if (ctx->errcode)
        goto out;

    if (!(inode.i_flags & EXT4_INLINE_DATA_FL)) {
        ctx->errcode = EXT2_ET_NO_INLINE_DATA;
        goto out;
    }

    if (!LINUX_S_ISDIR(inode.i_mode)) {
        ctx->errcode = EXT2_ET_NO_DIRECTORY;
        goto out;
    }
    ret = 0;

    /* we first check '.' and '..' dir */
    dirent.inode = ino;
    dirent.name_len = 1;
    ext2fs_set_rec_len(fs, EXT2_DIR_REC_LEN(2), &dirent);
    dirent.name[0] = '.';
    dirent.name[1] = '\0';
    ctx->buf = (char *)&dirent;
    ext2fs_get_rec_len(fs, &dirent, &ctx->buflen);
    ret |= ext2fs_process_dir_block(fs, 0, blockcnt++, 0, 0, priv_data);
    if (ret & BLOCK_ABORT)
        goto out;

    dirent.inode = ext2fs_le32_to_cpu(inode.i_block[0]);
    dirent.name_len = 2;
    ext2fs_set_rec_len(fs, EXT2_DIR_REC_LEN(3), &dirent);
    dirent.name[0] = '.';
    dirent.name[1] = '.';
    dirent.name[2] = '\0';
    ctx->buf = (char *)&dirent;
    ext2fs_get_rec_len(fs, &dirent, &ctx->buflen);
    ret |= ext2fs_process_dir_block(fs, 0, blockcnt++, 0, 0, priv_data);
    if (ret & BLOCK_INLINE_DATA_CHANGED) {
        errcode_t err;

        inode.i_block[0] = ext2fs_cpu_to_le32(dirent.inode);
        err = ext2fs_write_inode(fs, ino, &inode);
        if (err)
            goto out;
        ret &= ~BLOCK_INLINE_DATA_CHANGED;
    }
    if (ret & BLOCK_ABORT)
        goto out;

    ctx->buf = (char *)inode.i_block + EXT4_INLINE_DATA_DOTDOT_SIZE;
    ctx->buflen = EXT4_MIN_INLINE_DATA_SIZE - EXT4_INLINE_DATA_DOTDOT_SIZE;
#ifdef WORDS_BIGENDIAN
    ctx->errcode = ext2fs_dirent_swab_in2(fs, ctx->buf, ctx->buflen, 0);
    if (ctx->errcode) {
        ret |= BLOCK_ABORT;
        goto out;
    }
#endif
    ret |= ext2fs_process_dir_block(fs, 0, blockcnt++, 0, 0, priv_data);
    if (ret & BLOCK_INLINE_DATA_CHANGED) {
#ifdef WORDS_BIGENDIAN
        ctx->errcode = ext2fs_dirent_swab_out2(fs, ctx->buf,
                                               ctx->buflen, 0);
        if (ctx->errcode) {
            ret |= BLOCK_ABORT;
            goto out;
        }
#endif
        ctx->errcode = ext2fs_write_inode(fs, ino, &inode);
        if (ctx->errcode)
            ret |= BLOCK_ABORT;
        ret &= ~BLOCK_INLINE_DATA_CHANGED;
    }
    if (ret & BLOCK_ABORT)
        goto out;

    data.fs = fs;
    data.ino = ino;
    ctx->errcode = ext2fs_inline_data_ea_get(&data);
    if (ctx->errcode) {
        ret |= BLOCK_ABORT;
        goto out;
    }
    if (data.ea_size <= 0)
        goto out1;

    ctx->buf = data.ea_data;
    ctx->buflen = data.ea_size;
#ifdef WORDS_BIGENDIAN
    ctx->errcode = ext2fs_dirent_swab_in2(fs, ctx->buf, ctx->buflen, 0);
    if (ctx->errcode) {
        ret |= BLOCK_ABORT;
        goto out1;
    }
#endif

    ret |= ext2fs_process_dir_block(fs, 0, blockcnt++, 0, 0, priv_data);
    if (ret & BLOCK_INLINE_DATA_CHANGED) {
#ifdef WORDS_BIGENDIAN
        ctx->errcode = ext2fs_dirent_swab_out2(fs, ctx->buf,
                                               ctx->buflen, 0);
        if (ctx->errcode) {
            ret |= BLOCK_ABORT;
            goto out1;
        }
#endif
        ctx->errcode = ext2fs_inline_data_ea_set(&data);
        if (ctx->errcode)
            ret |= BLOCK_ABORT;
    }

out1:
    ext2fs_free_mem(&data.ea_data);
out:
    ctx->buf = old_buf;
    ctx->buflen = old_buflen;
    ctx->flags = old_flags;
    ret &= ~(BLOCK_ABORT | BLOCK_INLINE_DATA_CHANGED);
    return ret;
}
Пример #15
0
static errcode_t ext2fs_inline_data_convert_dir(ext2_filsys fs, ext2_ino_t ino,
        char *bbuf, char *ibuf, int size)
{
    struct ext2_dir_entry *dir, *dir2;
    struct ext2_dir_entry_tail *t;
    errcode_t retval;
    unsigned int offset, rec_len;
    int csum_size = 0;
    int filetype = 0;

    if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
                                   EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
        csum_size = sizeof(struct ext2_dir_entry_tail);

    /* Create '.' and '..' */
    if (EXT2_HAS_INCOMPAT_FEATURE(fs->super,
                                  EXT2_FEATURE_INCOMPAT_FILETYPE))
        filetype = EXT2_FT_DIR;

    /*
     * Set up entry for '.'
     */
    dir = (struct ext2_dir_entry *) bbuf;
    dir->inode = ino;
    ext2fs_dirent_set_name_len(dir, 1);
    ext2fs_dirent_set_file_type(dir, filetype);
    dir->name[0] = '.';
    rec_len = (fs->blocksize - csum_size) - EXT2_DIR_REC_LEN(1);
    dir->rec_len = EXT2_DIR_REC_LEN(1);

    /*
     * Set up entry for '..'
     */
    dir = (struct ext2_dir_entry *) (bbuf + dir->rec_len);
    dir->rec_len = EXT2_DIR_REC_LEN(2);
    dir->inode = ext2fs_le32_to_cpu(((__u32 *)ibuf)[0]);
    ext2fs_dirent_set_name_len(dir, 2);
    ext2fs_dirent_set_file_type(dir, filetype);
    dir->name[0] = '.';
    dir->name[1] = '.';

    /*
     * Ajust the last rec_len
     */
    offset = EXT2_DIR_REC_LEN(1) + EXT2_DIR_REC_LEN(2);
    dir = (struct ext2_dir_entry *) (bbuf + offset);
    memcpy(bbuf + offset, ibuf + EXT4_INLINE_DATA_DOTDOT_SIZE,
           size - EXT4_INLINE_DATA_DOTDOT_SIZE);
    size += EXT2_DIR_REC_LEN(1) + EXT2_DIR_REC_LEN(2) -
            EXT4_INLINE_DATA_DOTDOT_SIZE;

    do {
        dir2 = dir;
        retval = ext2fs_get_rec_len(fs, dir, &rec_len);
        if (retval)
            goto err;
        offset += rec_len;
        dir = (struct ext2_dir_entry *) (bbuf + offset);
    } while (offset < size);
    rec_len += fs->blocksize - csum_size - offset;
    retval = ext2fs_set_rec_len(fs, rec_len, dir2);
    if (retval)
        goto err;

    if (csum_size) {
        t = EXT2_DIRENT_TAIL(bbuf, fs->blocksize);
        ext2fs_initialize_dirent_tail(fs, t);
    }

err:
    return retval;
}
Пример #16
0
static int check_dir_block(ext2_filsys fs,
			   struct ext2_db_entry2 *db,
			   void *priv_data)
{
 	struct dx_dir_info	*dx_dir;
#ifdef ENABLE_HTREE
	struct dx_dirblock_info	*dx_db = 0;
#endif /* ENABLE_HTREE */
	struct ext2_dir_entry 	*dirent, *prev;
	ext2_dirhash_t		hash;
	unsigned int		offset = 0;
	int			dir_modified = 0;
	int			dot_state;
	unsigned int		rec_len;
	blk64_t			block_nr = db->blk;
	ext2_ino_t 		ino = db->ino;
	ext2_ino_t 		subdir_parent;
	__u16			links;
	struct check_dir_struct	*cd;
	char 			*buf;
	e2fsck_t		ctx;
	int			problem;
	struct ext2_dx_root_info *root;
	struct ext2_dx_countlimit *limit;
	static dict_t de_dict;
	struct problem_context	pctx;
	int	dups_found = 0;
	int	ret;
	int	dx_csum_size = 0, de_csum_size = 0;
	int	failed_csum = 0;
	int	is_leaf = 1;

	cd = (struct check_dir_struct *) priv_data;
	buf = cd->buf;
	ctx = cd->ctx;

	if (ctx->flags & E2F_FLAG_SIGNAL_MASK || ctx->flags & E2F_FLAG_RESTART)
		return DIRENT_ABORT;

	if (ctx->progress && (ctx->progress)(ctx, 2, cd->count++, cd->max))
		return DIRENT_ABORT;

	if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
				       EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) {
		dx_csum_size = sizeof(struct ext2_dx_tail);
		de_csum_size = sizeof(struct ext2_dir_entry_tail);
	}

	/*
	 * Make sure the inode is still in use (could have been
	 * deleted in the duplicate/bad blocks pass.
	 */
	if (!(ext2fs_test_inode_bitmap2(ctx->inode_used_map, ino)))
		return 0;

	cd->pctx.ino = ino;
	cd->pctx.blk = block_nr;
	cd->pctx.blkcount = db->blockcnt;
	cd->pctx.ino2 = 0;
	cd->pctx.dirent = 0;
	cd->pctx.num = 0;

	if (db->blk == 0) {
		if (allocate_dir_block(ctx, db, buf, &cd->pctx))
			return 0;
		block_nr = db->blk;
	}

	if (db->blockcnt)
		dot_state = 2;
	else
		dot_state = 0;

	if (ctx->dirs_to_hash &&
	    ext2fs_u32_list_test(ctx->dirs_to_hash, ino))
		dups_found++;

#if 0
	printf("In process_dir_block block %lu, #%d, inode %lu\n", block_nr,
	       db->blockcnt, ino);
#endif

	ehandler_operation(_("reading directory block"));
	cd->pctx.errcode = ext2fs_read_dir_block4(fs, block_nr, buf, 0, ino);
	ehandler_operation(0);
	if (cd->pctx.errcode == EXT2_ET_DIR_CORRUPTED)
		cd->pctx.errcode = 0; /* We'll handle this ourselves */
	else if (cd->pctx.errcode == EXT2_ET_DIR_CSUM_INVALID) {
		cd->pctx.errcode = 0; /* We'll handle this ourselves */
		failed_csum = 1;
	}
	if (cd->pctx.errcode) {
		char *buf2;
		if (!fix_problem(ctx, PR_2_READ_DIRBLOCK, &cd->pctx)) {
			ctx->flags |= E2F_FLAG_ABORT;
			return DIRENT_ABORT;
		}
		ext2fs_new_dir_block(fs, db->blockcnt == 0 ? ino : 0,
				     EXT2_ROOT_INO, &buf2);
		memcpy(buf, buf2, fs->blocksize);
		ext2fs_free_mem(&buf2);
	}
#ifdef ENABLE_HTREE
	dx_dir = e2fsck_get_dx_dir_info(ctx, ino);
	if (dx_dir && dx_dir->numblocks) {
		if (db->blockcnt >= dx_dir->numblocks) {
			if (fix_problem(ctx, PR_2_UNEXPECTED_HTREE_BLOCK,
					&pctx)) {
				clear_htree(ctx, ino);
				dx_dir->numblocks = 0;
				dx_db = 0;
				goto out_htree;
			}
			fatal_error(ctx, _("Can not continue."));
		}
		dx_db = &dx_dir->dx_block[db->blockcnt];
		dx_db->type = DX_DIRBLOCK_LEAF;
		dx_db->phys = block_nr;
		dx_db->min_hash = ~0;
		dx_db->max_hash = 0;

		dirent = (struct ext2_dir_entry *) buf;
		(void) ext2fs_get_rec_len(fs, dirent, &rec_len);
		limit = (struct ext2_dx_countlimit *) (buf+8);
		if (db->blockcnt == 0) {
			root = (struct ext2_dx_root_info *) (buf + 24);
			dx_db->type = DX_DIRBLOCK_ROOT;
			dx_db->flags |= DX_FLAG_FIRST | DX_FLAG_LAST;
			if ((root->reserved_zero ||
			     root->info_length < 8 ||
			     root->indirect_levels > 1) &&
			    fix_problem(ctx, PR_2_HTREE_BAD_ROOT, &cd->pctx)) {
				clear_htree(ctx, ino);
				dx_dir->numblocks = 0;
				dx_db = 0;
			}
			dx_dir->hashversion = root->hash_version;
			if ((dx_dir->hashversion <= EXT2_HASH_TEA) &&
			    (fs->super->s_flags & EXT2_FLAGS_UNSIGNED_HASH))
				dx_dir->hashversion += 3;
			dx_dir->depth = root->indirect_levels + 1;
		} else if ((dirent->inode == 0) &&
			   (rec_len == fs->blocksize) &&
			   (dirent->name_len == 0) &&
			   (ext2fs_le16_to_cpu(limit->limit) ==
			    ((fs->blocksize - (8 + dx_csum_size)) /
			     sizeof(struct ext2_dx_entry))))
			dx_db->type = DX_DIRBLOCK_NODE;
		is_leaf = 0;
	}
out_htree:
#endif /* ENABLE_HTREE */

	/* Verify checksum. */
	if (is_leaf && de_csum_size) {
		/* No space for csum?  Rebuild dirs in pass 3A. */
		if (!ext2fs_dirent_has_tail(fs, (struct ext2_dir_entry *)buf)) {
			de_csum_size = 0;
			if (e2fsck_dir_will_be_rehashed(ctx, ino))
				goto skip_checksum;
			if (!fix_problem(cd->ctx, PR_2_LEAF_NODE_MISSING_CSUM,
					 &cd->pctx))
				goto skip_checksum;
			e2fsck_rehash_dir_later(ctx, ino);
			goto skip_checksum;
		}
		if (failed_csum) {
			char *buf2;
			if (!fix_problem(cd->ctx, PR_2_LEAF_NODE_CSUM_INVALID,
					 &cd->pctx))
				goto skip_checksum;
			ext2fs_new_dir_block(fs,
					     db->blockcnt == 0 ? ino : 0,
					     EXT2_ROOT_INO, &buf2);
			memcpy(buf, buf2, fs->blocksize);
			ext2fs_free_mem(&buf2);
			dir_modified++;
			failed_csum = 0;
		}
	}
	/* htree nodes don't use fake dirents to store checksums */
	if (!is_leaf)
		de_csum_size = 0;

skip_checksum:
	dict_init(&de_dict, DICTCOUNT_T_MAX, dict_de_cmp);
	prev = 0;
	do {
		int group;
		ext2_ino_t first_unused_inode;

		problem = 0;
		dirent = (struct ext2_dir_entry *) (buf + offset);
		(void) ext2fs_get_rec_len(fs, dirent, &rec_len);
		cd->pctx.dirent = dirent;
		cd->pctx.num = offset;
		if (((offset + rec_len) > fs->blocksize) ||
		    (rec_len < 12) ||
		    ((rec_len % 4) != 0) ||
		    (((dirent->name_len & (unsigned) 0xFF)+8) > rec_len)) {
			if (fix_problem(ctx, PR_2_DIR_CORRUPTED, &cd->pctx)) {
				salvage_directory(fs, dirent, prev, &offset);
				dir_modified++;
				continue;
			} else
				goto abort_free_dict;
		}

		if (dot_state == 0) {
			if (check_dot(ctx, dirent, ino, &cd->pctx))
				dir_modified++;
		} else if (dot_state == 1) {
			ret = check_dotdot(ctx, dirent, ino, &cd->pctx);
			if (ret < 0)
				goto abort_free_dict;
			if (ret)
				dir_modified++;
		} else if (dirent->inode == ino) {
			problem = PR_2_LINK_DOT;
			if (fix_problem(ctx, PR_2_LINK_DOT, &cd->pctx)) {
				dirent->inode = 0;
				dir_modified++;
				goto next;
			}
		}
		if (!dirent->inode)
			goto next;

		/*
		 * Make sure the inode listed is a legal one.
		 */
		if (((dirent->inode != EXT2_ROOT_INO) &&
		     (dirent->inode < EXT2_FIRST_INODE(fs->super))) ||
		    (dirent->inode > fs->super->s_inodes_count)) {
			problem = PR_2_BAD_INO;
		} else if (ctx->inode_bb_map &&
			   (ext2fs_test_inode_bitmap2(ctx->inode_bb_map,
						     dirent->inode))) {
			/*
			 * If the inode is in a bad block, offer to
			 * clear it.
			 */
			problem = PR_2_BB_INODE;
		} else if ((dot_state > 1) &&
			   ((dirent->name_len & 0xFF) == 1) &&
			   (dirent->name[0] == '.')) {
			/*
			 * If there's a '.' entry in anything other
			 * than the first directory entry, it's a
			 * duplicate entry that should be removed.
			 */
			problem = PR_2_DUP_DOT;
		} else if ((dot_state > 1) &&
			   ((dirent->name_len & 0xFF) == 2) &&
			   (dirent->name[0] == '.') &&
			   (dirent->name[1] == '.')) {
			/*
			 * If there's a '..' entry in anything other
			 * than the second directory entry, it's a
			 * duplicate entry that should be removed.
			 */
			problem = PR_2_DUP_DOT_DOT;
		} else if ((dot_state > 1) &&
			   (dirent->inode == EXT2_ROOT_INO)) {
			/*
			 * Don't allow links to the root directory.
			 * We check this specially to make sure we
			 * catch this error case even if the root
			 * directory hasn't been created yet.
			 */
			problem = PR_2_LINK_ROOT;
		} else if ((dot_state > 1) &&
			   (dirent->name_len & 0xFF) == 0) {
			/*
			 * Don't allow zero-length directory names.
			 */
			problem = PR_2_NULL_NAME;
		}

		if (problem) {
			if (fix_problem(ctx, problem, &cd->pctx)) {
				dirent->inode = 0;
				dir_modified++;
				goto next;
			} else {
				ext2fs_unmark_valid(fs);
				if (problem == PR_2_BAD_INO)
					goto next;
			}
		}

		/*
		 * If the inode was marked as having bad fields in
		 * pass1, process it and offer to fix/clear it.
		 * (We wait until now so that we can display the
		 * pathname to the user.)
		 */
		if (ctx->inode_bad_map &&
		    ext2fs_test_inode_bitmap2(ctx->inode_bad_map,
					     dirent->inode)) {
			if (e2fsck_process_bad_inode(ctx, ino,
						     dirent->inode,
						     buf + fs->blocksize)) {
				dirent->inode = 0;
				dir_modified++;
				goto next;
			}
			if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
				return DIRENT_ABORT;
		}

		group = ext2fs_group_of_ino(fs, dirent->inode);
		first_unused_inode = group * fs->super->s_inodes_per_group +
					1 + fs->super->s_inodes_per_group -
					ext2fs_bg_itable_unused(fs, group);
		cd->pctx.group = group;

		/*
		 * Check if the inode was missed out because
		 * _INODE_UNINIT flag was set or bg_itable_unused was
		 * incorrect.  If so, clear the _INODE_UNINIT flag and
		 * restart e2fsck.  In the future it would be nice if
		 * we could call a function in pass1.c that checks the
		 * newly visible inodes.
		 */
		if (ext2fs_bg_flags_test(fs, group, EXT2_BG_INODE_UNINIT)) {
			pctx.num = dirent->inode;
			if (fix_problem(ctx, PR_2_INOREF_BG_INO_UNINIT,
					&cd->pctx)){
				ext2fs_bg_flags_clear(fs, group,
						      EXT2_BG_INODE_UNINIT);
				ext2fs_mark_super_dirty(fs);
				ctx->flags |= E2F_FLAG_RESTART_LATER;
			} else {
				ext2fs_unmark_valid(fs);
				if (problem == PR_2_BAD_INO)
					goto next;
			}
		} else if (dirent->inode >= first_unused_inode) {
			pctx.num = dirent->inode;
			if (fix_problem(ctx, PR_2_INOREF_IN_UNUSED, &cd->pctx)){
				ext2fs_bg_itable_unused_set(fs, group, 0);
				ext2fs_mark_super_dirty(fs);
				ctx->flags |= E2F_FLAG_RESTART_LATER;
			} else {
				ext2fs_unmark_valid(fs);
				if (problem == PR_2_BAD_INO)
					goto next;
			}
		}

		/* 
		 * Offer to clear unused inodes; if we are going to be
		 * restarting the scan due to bg_itable_unused being
		 * wrong, then don't clear any inodes to avoid zapping
		 * inodes that were skipped during pass1 due to an
		 * incorrect bg_itable_unused; we'll get any real
		 * problems after we restart.
		 */
		if (!(ctx->flags & E2F_FLAG_RESTART_LATER) &&
		    !(ext2fs_test_inode_bitmap2(ctx->inode_used_map,
						dirent->inode)))
			problem = PR_2_UNUSED_INODE;

		if (problem) {
			if (fix_problem(ctx, problem, &cd->pctx)) {
				dirent->inode = 0;
				dir_modified++;
				goto next;
			} else {
				ext2fs_unmark_valid(fs);
				if (problem == PR_2_BAD_INO)
					goto next;
			}
		}

		if (check_name(ctx, dirent, ino, &cd->pctx))
			dir_modified++;

		if (check_filetype(ctx, dirent, ino, &cd->pctx))
			dir_modified++;

#ifdef ENABLE_HTREE
		if (dx_db) {
			ext2fs_dirhash(dx_dir->hashversion, dirent->name,
				       (dirent->name_len & 0xFF),
				       fs->super->s_hash_seed, &hash, 0);
			if (hash < dx_db->min_hash)
				dx_db->min_hash = hash;
			if (hash > dx_db->max_hash)
				dx_db->max_hash = hash;
		}
#endif

		/*
		 * If this is a directory, then mark its parent in its
		 * dir_info structure.  If the parent field is already
		 * filled in, then this directory has more than one
		 * hard link.  We assume the first link is correct,
		 * and ask the user if he/she wants to clear this one.
		 */
		if ((dot_state > 1) &&
		    (ext2fs_test_inode_bitmap2(ctx->inode_dir_map,
					      dirent->inode))) {
			if (e2fsck_dir_info_get_parent(ctx, dirent->inode,
						       &subdir_parent)) {
				cd->pctx.ino = dirent->inode;
				fix_problem(ctx, PR_2_NO_DIRINFO, &cd->pctx);
				goto abort_free_dict;
			}
			if (subdir_parent) {
				cd->pctx.ino2 = subdir_parent;
				if (fix_problem(ctx, PR_2_LINK_DIR,
						&cd->pctx)) {
					dirent->inode = 0;
					dir_modified++;
					goto next;
				}
				cd->pctx.ino2 = 0;
			} else {
				(void) e2fsck_dir_info_set_parent(ctx,
						  dirent->inode, ino);
			}
		}

		if (dups_found) {
			;
		} else if (dict_lookup(&de_dict, dirent)) {
			clear_problem_context(&pctx);
			pctx.ino = ino;
			pctx.dirent = dirent;
			fix_problem(ctx, PR_2_REPORT_DUP_DIRENT, &pctx);
			e2fsck_rehash_dir_later(ctx, ino);
			dups_found++;
		} else
			dict_alloc_insert(&de_dict, dirent, dirent);

		ext2fs_icount_increment(ctx->inode_count, dirent->inode,
					&links);
		if (links > 1)
			ctx->fs_links_count++;
		ctx->fs_total_count++;
	next:
		prev = dirent;
		if (dir_modified)
			(void) ext2fs_get_rec_len(fs, dirent, &rec_len);
		offset += rec_len;
		dot_state++;
	} while (offset < fs->blocksize - de_csum_size);
#if 0
	printf("\n");
#endif
#ifdef ENABLE_HTREE
	if (dx_db) {
#ifdef DX_DEBUG
		printf("db_block %d, type %d, min_hash 0x%0x, max_hash 0x%0x\n",
		       db->blockcnt, dx_db->type,
		       dx_db->min_hash, dx_db->max_hash);
#endif
		cd->pctx.dir = cd->pctx.ino;
		if ((dx_db->type == DX_DIRBLOCK_ROOT) ||
		    (dx_db->type == DX_DIRBLOCK_NODE))
			parse_int_node(fs, db, cd, dx_dir, buf, failed_csum);
	}
#endif /* ENABLE_HTREE */

	if (offset != fs->blocksize - de_csum_size) {
		cd->pctx.num = rec_len - (fs->blocksize - de_csum_size) +
			       offset;
		if (fix_problem(ctx, PR_2_FINAL_RECLEN, &cd->pctx)) {
			dirent->rec_len = cd->pctx.num;
			dir_modified++;
		}
	}
	if (dir_modified) {
		/* leaf block with no tail?  Rehash dirs later. */
		if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
				EXT4_FEATURE_RO_COMPAT_METADATA_CSUM) &&
		    is_leaf &&
		    !ext2fs_dirent_has_tail(fs, (struct ext2_dir_entry *)buf))
			e2fsck_rehash_dir_later(ctx, ino);

write_and_fix:
		if (e2fsck_dir_will_be_rehashed(ctx, ino))
			ctx->fs->flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS;
		cd->pctx.errcode = ext2fs_write_dir_block4(fs, block_nr, buf,
							   0, ino);
		if (e2fsck_dir_will_be_rehashed(ctx, ino))
			ctx->fs->flags &= ~EXT2_FLAG_IGNORE_CSUM_ERRORS;
		if (cd->pctx.errcode) {
			if (!fix_problem(ctx, PR_2_WRITE_DIRBLOCK,
					 &cd->pctx))
				goto abort_free_dict;
		}
		ext2fs_mark_changed(fs);
	} else if (is_leaf && failed_csum && !dir_modified) {
		/*
		 * If a leaf node that fails csum makes it this far without
		 * alteration, ask the user if the checksum should be fixed.
		 */
		if (fix_problem(ctx, PR_2_LEAF_NODE_ONLY_CSUM_INVALID,
				&cd->pctx))
			goto write_and_fix;
	}
	dict_free_nodes(&de_dict);
	return 0;
abort_free_dict:
	ctx->flags |= E2F_FLAG_ABORT;
	dict_free_nodes(&de_dict);
	return DIRENT_ABORT;
}
Пример #17
0
/*
 * Given a busted directory, try to salvage it somehow.
 *
 */
static void salvage_directory(ext2_filsys fs,
			      struct ext2_dir_entry *dirent,
			      struct ext2_dir_entry *prev,
			      unsigned int *offset)
{
	char	*cp = (char *) dirent;
	int left;
	unsigned int rec_len, prev_rec_len;
	unsigned int name_len = dirent->name_len & 0xFF;

	(void) ext2fs_get_rec_len(fs, dirent, &rec_len);
	left = fs->blocksize - *offset - rec_len;

	/*
	 * Special case of directory entry of size 8: copy what's left
	 * of the directory block up to cover up the invalid hole.
	 */
	if ((left >= 12) && (rec_len == 8)) {
		memmove(cp, cp+8, left);
		memset(cp + left, 0, 8);
		return;
	}
	/*
	 * If the directory entry overruns the end of the directory
	 * block, and the name is small enough to fit, then adjust the
	 * record length.
	 */
	if ((left < 0) &&
	    ((int) rec_len + left > 8) &&
	    ((int) name_len + 8 <= (int) rec_len + left) &&
	    dirent->inode <= fs->super->s_inodes_count &&
	    strnlen(dirent->name, name_len) == name_len) {
		(void) ext2fs_set_rec_len(fs, (int) rec_len + left, dirent);
		return;
	}
	/*
	 * If the record length of the directory entry is a multiple
	 * of four, and not too big, such that it is valid, let the
	 * previous directory entry absorb the invalid one.
	 */
	if (prev && rec_len && (rec_len % 4) == 0 &&
	    (*offset + rec_len <= fs->blocksize)) {
		(void) ext2fs_get_rec_len(fs, prev, &prev_rec_len);
		prev_rec_len += rec_len;
		(void) ext2fs_set_rec_len(fs, prev_rec_len, prev);
		*offset += rec_len;
		return;
	}
	/*
	 * Default salvage method --- kill all of the directory
	 * entries for the rest of the block.  We will either try to
	 * absorb it into the previous directory entry, or create a
	 * new empty directory entry the rest of the directory block.
	 */
	if (prev) {
		(void) ext2fs_get_rec_len(fs, prev, &prev_rec_len);
		prev_rec_len += fs->blocksize - *offset;
		(void) ext2fs_set_rec_len(fs, prev_rec_len, prev);
		*offset = fs->blocksize;
	} else {
		rec_len = fs->blocksize - *offset;
		(void) ext2fs_set_rec_len(fs, rec_len, dirent);
		dirent->name_len = 0;
		dirent->inode = 0;
	}
}
Пример #18
0
static int link_proc(struct ext2_dir_entry *dirent,
		     int	offset,
		     int	blocksize,
		     char	*buf,
		     void	*priv_data)
{
	struct link_struct *ls = (struct link_struct *) priv_data;
	struct ext2_dir_entry *next;
	unsigned int rec_len, min_rec_len, curr_rec_len;
	int ret = 0;
	int csum_size = 0;
	struct ext2_dir_entry_tail *t;

	if (ls->done)
		return DIRENT_ABORT;

	rec_len = EXT2_DIR_REC_LEN(ls->namelen);

	ls->err = ext2fs_get_rec_len(ls->fs, dirent, &curr_rec_len);
	if (ls->err)
		return DIRENT_ABORT;

	if (EXT2_HAS_RO_COMPAT_FEATURE(ls->fs->super,
				       EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
		csum_size = sizeof(struct ext2_dir_entry_tail);
	/*
	 * See if the following directory entry (if any) is unused;
	 * if so, absorb it into this one.
	 */
	next = (struct ext2_dir_entry *) (buf + offset + curr_rec_len);
	if ((offset + (int) curr_rec_len < blocksize - (8 + csum_size)) &&
	    (next->inode == 0) &&
	    (offset + (int) curr_rec_len + (int) next->rec_len <= blocksize)) {
		curr_rec_len += next->rec_len;
		ls->err = ext2fs_set_rec_len(ls->fs, curr_rec_len, dirent);
		if (ls->err)
			return DIRENT_ABORT;
		ret = DIRENT_CHANGED;
	}

	/*
	 * Since ext2fs_link blows away htree data, we need to be
	 * careful -- if metadata_csum is enabled and we're passed in
	 * a dirent that contains htree data, we need to create the
	 * fake entry at the end of the block that hides the checksum.
	 */

	/* De-convert a dx_node block */
	if (csum_size &&
	    curr_rec_len == ls->fs->blocksize &&
	    !dirent->inode) {
		curr_rec_len -= csum_size;
		ls->err = ext2fs_set_rec_len(ls->fs, curr_rec_len, dirent);
		if (ls->err)
			return DIRENT_ABORT;
		t = EXT2_DIRENT_TAIL(buf, ls->fs->blocksize);
		ext2fs_initialize_dirent_tail(ls->fs, t);
		ret = DIRENT_CHANGED;
	}

	/* De-convert a dx_root block */
	if (csum_size &&
	    curr_rec_len == ls->fs->blocksize - EXT2_DIR_REC_LEN(1) &&
	    offset == EXT2_DIR_REC_LEN(1) &&
	    dirent->name[0] == '.' && dirent->name[1] == '.') {
		curr_rec_len -= csum_size;
		ls->err = ext2fs_set_rec_len(ls->fs, curr_rec_len, dirent);
		if (ls->err)
			return DIRENT_ABORT;
		t = EXT2_DIRENT_TAIL(buf, ls->fs->blocksize);
		ext2fs_initialize_dirent_tail(ls->fs, t);
		ret = DIRENT_CHANGED;
	}

	/*
	 * If the directory entry is used, see if we can split the
	 * directory entry to make room for the new name.  If so,
	 * truncate it and return.
	 */
	if (dirent->inode) {
		min_rec_len = EXT2_DIR_REC_LEN(ext2fs_dirent_name_len(dirent));
		if (curr_rec_len < (min_rec_len + rec_len))
			return ret;
		rec_len = curr_rec_len - min_rec_len;
		ls->err = ext2fs_set_rec_len(ls->fs, min_rec_len, dirent);
		if (ls->err)
			return DIRENT_ABORT;
		next = (struct ext2_dir_entry *) (buf + offset +
						  dirent->rec_len);
		next->inode = 0;
		ext2fs_dirent_set_name_len(next, 0);
		ext2fs_dirent_set_file_type(next, 0);
		ls->err = ext2fs_set_rec_len(ls->fs, rec_len, next);
		if (ls->err)
			return DIRENT_ABORT;
		return DIRENT_CHANGED;
	}

	/*
	 * If we get this far, then the directory entry is not used.
	 * See if we can fit the request entry in.  If so, do it.
	 */
	if (curr_rec_len < rec_len)
		return ret;
	dirent->inode = ls->inode;
	ext2fs_dirent_set_name_len(dirent, ls->namelen);
	strncpy(dirent->name, ls->name, ls->namelen);
	if (ls->sb->s_feature_incompat & EXT2_FEATURE_INCOMPAT_FILETYPE)
		ext2fs_dirent_set_file_type(dirent, ls->flags & 0x7);

	ls->done++;
	return DIRENT_ABORT|DIRENT_CHANGED;
}
Пример #19
0
static void htree_dump_leaf_node(ext2_filsys fs, ext2_ino_t ino,
				 struct ext2_inode *inode,
				 struct ext2_dx_root_info * rootnode,
				 blk64_t blk, char *buf)
{
	errcode_t	errcode;
	struct ext2_dir_entry *dirent;
	int		thislen, col = 0;
	unsigned int	offset = 0;
	char		name[EXT2_NAME_LEN + 1];
	char		tmp[EXT2_NAME_LEN + 64];
	blk64_t		pblk;
	ext2_dirhash_t 	hash, minor_hash;
	unsigned int	rec_len;
	int		hash_alg;
	int		csum_size = 0;

	if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
				       EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
		csum_size = sizeof(struct ext2_dir_entry_tail);

	errcode = ext2fs_bmap2(fs, ino, inode, buf, 0, blk, 0, &pblk);
	if (errcode) {
		com_err("htree_dump_leaf_node", errcode,
			"while mapping logical block %llu\n", blk);
		return;
	}

	fprintf(pager, "Reading directory block %llu, phys %llu\n", blk, pblk);
	errcode = ext2fs_read_dir_block4(current_fs, pblk, buf, 0, ino);
	if (errcode) {
		com_err("htree_dump_leaf_node", errcode,
			"while reading block %llu (%llu)\n",
			blk, pblk);
		return;
	}
	hash_alg = rootnode->hash_version;
	if ((hash_alg <= EXT2_HASH_TEA) &&
	    (fs->super->s_flags & EXT2_FLAGS_UNSIGNED_HASH))
		hash_alg += 3;

	while (offset < fs->blocksize) {
		dirent = (struct ext2_dir_entry *) (buf + offset);
		errcode = ext2fs_get_rec_len(fs, dirent, &rec_len);
		if (errcode) {
			com_err("htree_dump_leaf_inode", errcode,
				"while getting rec_len for block %lu",
				(unsigned long) blk);
			return;
		}
		if (((offset + rec_len) > fs->blocksize) ||
		    (rec_len < 8) ||
		    ((rec_len % 4) != 0) ||
		    ((((unsigned) dirent->name_len & 0xFF)+8) > rec_len)) {
			fprintf(pager, "Corrupted directory block (%llu)!\n",
				blk);
			break;
		}
		thislen = dirent->name_len & 0xFF;
		strncpy(name, dirent->name, thislen);
		name[thislen] = '\0';
		errcode = ext2fs_dirhash(hash_alg, name,
					 thislen, fs->super->s_hash_seed,
					 &hash, &minor_hash);
		if (errcode)
			com_err("htree_dump_leaf_node", errcode,
				"while calculating hash");
		if ((offset == fs->blocksize - csum_size) &&
		    (dirent->inode == 0) &&
		    (dirent->rec_len == csum_size) &&
		    (dirent->name_len == EXT2_DIR_NAME_LEN_CSUM)) {
			struct ext2_dir_entry_tail *t;

			t = (struct ext2_dir_entry_tail *) dirent;

			snprintf(tmp, EXT2_NAME_LEN + 64,
				 "leaf block checksum: 0x%08x  ",
				 t->det_checksum);
		} else {
			snprintf(tmp, EXT2_NAME_LEN + 64,
				 "%u 0x%08x-%08x (%d) %s   ",
				 dirent->inode, hash, minor_hash,
				 rec_len, name);
		}
		thislen = strlen(tmp);
		if (col + thislen > 80) {
			fprintf(pager, "\n");
			col = 0;
		}
		fprintf(pager, "%s", tmp);
		col += thislen;
		offset += rec_len;
	}
	fprintf(pager, "\n");
}
Пример #20
0
static int check_dir_block(ext2_filsys fs,
			   struct ext2_db_entry *db,
			   void *priv_data)
{
 	struct dx_dir_info	*dx_dir;
#ifdef ENABLE_HTREE
	struct dx_dirblock_info	*dx_db = 0;
#endif 
	struct ext2_dir_entry 	*dirent, *prev;
	ext2_dirhash_t		hash;
	unsigned int		offset = 0;
	const char *		old_op;
	int			dir_modified = 0;
	int			dot_state;
	unsigned int		rec_len;
	blk_t			block_nr = db->blk;
	ext2_ino_t 		ino = db->ino;
	ext2_ino_t 		subdir_parent;
	__u16			links;
	struct check_dir_struct	*cd;
	char 			*buf;
	e2fsck_t		ctx;
	int			problem;
	struct ext2_dx_root_info *root;
	struct ext2_dx_countlimit *limit;
	static dict_t de_dict;
	struct problem_context	pctx;
	int	dups_found = 0;
	int	ret;

	cd = (struct check_dir_struct *) priv_data;
	buf = cd->buf;
	ctx = cd->ctx;

	if (ctx->flags & E2F_FLAG_SIGNAL_MASK || ctx->flags & E2F_FLAG_RESTART)
		return DIRENT_ABORT;

	if (ctx->progress && (ctx->progress)(ctx, 2, cd->count++, cd->max))
		return DIRENT_ABORT;

	if (!(ext2fs_test_inode_bitmap(ctx->inode_used_map, ino)))
		return 0;

	cd->pctx.ino = ino;
	cd->pctx.blk = block_nr;
	cd->pctx.blkcount = db->blockcnt;
	cd->pctx.ino2 = 0;
	cd->pctx.dirent = 0;
	cd->pctx.num = 0;

	if (db->blk == 0) {
		if (allocate_dir_block(ctx, db, buf, &cd->pctx))
			return 0;
		block_nr = db->blk;
	}

	if (db->blockcnt)
		dot_state = 2;
	else
		dot_state = 0;

	if (ctx->dirs_to_hash &&
	    ext2fs_u32_list_test(ctx->dirs_to_hash, ino))
		dups_found++;

#if 0
	printf("In process_dir_block block %lu, #%d, inode %lu\n", block_nr,
	       db->blockcnt, ino);
#endif

	old_op = ehandler_operation(_("reading directory block"));
	cd->pctx.errcode = ext2fs_read_dir_block(fs, block_nr, buf);
	ehandler_operation(0);
	if (cd->pctx.errcode == EXT2_ET_DIR_CORRUPTED)
		cd->pctx.errcode = 0; 
	if (cd->pctx.errcode) {
		if (!fix_problem(ctx, PR_2_READ_DIRBLOCK, &cd->pctx)) {
			ctx->flags |= E2F_FLAG_ABORT;
			return DIRENT_ABORT;
		}
		memset(buf, 0, fs->blocksize);
	}
#ifdef ENABLE_HTREE
	dx_dir = e2fsck_get_dx_dir_info(ctx, ino);
	if (dx_dir && dx_dir->numblocks) {
		if (db->blockcnt >= dx_dir->numblocks) {
			if (fix_problem(ctx, PR_2_UNEXPECTED_HTREE_BLOCK,
					&pctx)) {
				clear_htree(ctx, ino);
				dx_dir->numblocks = 0;
				dx_db = 0;
				goto out_htree;
			}
			fatal_error(ctx, _("Can not continue."));
		}
		dx_db = &dx_dir->dx_block[db->blockcnt];
		dx_db->type = DX_DIRBLOCK_LEAF;
		dx_db->phys = block_nr;
		dx_db->min_hash = ~0;
		dx_db->max_hash = 0;

		dirent = (struct ext2_dir_entry *) buf;
		(void) ext2fs_get_rec_len(fs, dirent, &rec_len);
		limit = (struct ext2_dx_countlimit *) (buf+8);
		if (db->blockcnt == 0) {
			root = (struct ext2_dx_root_info *) (buf + 24);
			dx_db->type = DX_DIRBLOCK_ROOT;
			dx_db->flags |= DX_FLAG_FIRST | DX_FLAG_LAST;
			if ((root->reserved_zero ||
			     root->info_length < 8 ||
			     root->indirect_levels > 1) &&
			    fix_problem(ctx, PR_2_HTREE_BAD_ROOT, &cd->pctx)) {
				clear_htree(ctx, ino);
				dx_dir->numblocks = 0;
				dx_db = 0;
			}
			dx_dir->hashversion = root->hash_version;
			if ((dx_dir->hashversion <= EXT2_HASH_TEA) &&
			    (fs->super->s_flags & EXT2_FLAGS_UNSIGNED_HASH))
				dx_dir->hashversion += 3;
			dx_dir->depth = root->indirect_levels + 1;
		} else if ((dirent->inode == 0) &&
			   (rec_len == fs->blocksize) &&
			   (dirent->name_len == 0) &&
			   (ext2fs_le16_to_cpu(limit->limit) ==
			    ((fs->blocksize-8) /
			     sizeof(struct ext2_dx_entry))))
			dx_db->type = DX_DIRBLOCK_NODE;
	}
out_htree:
#endif 

	dict_init(&de_dict, DICTCOUNT_T_MAX, dict_de_cmp);
	prev = 0;
	do {
		int group;
		ext2_ino_t first_unused_inode;

		problem = 0;
		dirent = (struct ext2_dir_entry *) (buf + offset);
		(void) ext2fs_get_rec_len(fs, dirent, &rec_len);
		cd->pctx.dirent = dirent;
		cd->pctx.num = offset;
		if (((offset + rec_len) > fs->blocksize) ||
		    (rec_len < 12) ||
		    ((rec_len % 4) != 0) ||
		    (((dirent->name_len & (unsigned) 0xFF)+8) > rec_len)) {
			if (fix_problem(ctx, PR_2_DIR_CORRUPTED, &cd->pctx)) {
				salvage_directory(fs, dirent, prev, &offset);
				dir_modified++;
				continue;
			} else
				goto abort_free_dict;
		}
		if ((dirent->name_len & 0xFF) > EXT2_NAME_LEN) {
			if (fix_problem(ctx, PR_2_FILENAME_LONG, &cd->pctx)) {
				dirent->name_len = EXT2_NAME_LEN;
				dir_modified++;
			}
		}

		if (dot_state == 0) {
			if (check_dot(ctx, dirent, ino, &cd->pctx))
				dir_modified++;
		} else if (dot_state == 1) {
			ret = check_dotdot(ctx, dirent, ino, &cd->pctx);
			if (ret < 0)
				goto abort_free_dict;
			if (ret)
				dir_modified++;
		} else if (dirent->inode == ino) {
			problem = PR_2_LINK_DOT;
			if (fix_problem(ctx, PR_2_LINK_DOT, &cd->pctx)) {
				dirent->inode = 0;
				dir_modified++;
				goto next;
			}
		}
		if (!dirent->inode)
			goto next;

		if (((dirent->inode != EXT2_ROOT_INO) &&
		     (dirent->inode < EXT2_FIRST_INODE(fs->super))) ||
		    (dirent->inode > fs->super->s_inodes_count)) {
			problem = PR_2_BAD_INO;
		} else if (ctx->inode_bb_map &&
			   (ext2fs_test_inode_bitmap(ctx->inode_bb_map,
						     dirent->inode))) {
			problem = PR_2_BB_INODE;
		} else if ((dot_state > 1) &&
			   ((dirent->name_len & 0xFF) == 1) &&
			   (dirent->name[0] == '.')) {
			problem = PR_2_DUP_DOT;
		} else if ((dot_state > 1) &&
			   ((dirent->name_len & 0xFF) == 2) &&
			   (dirent->name[0] == '.') &&
			   (dirent->name[1] == '.')) {
			problem = PR_2_DUP_DOT_DOT;
		} else if ((dot_state > 1) &&
			   (dirent->inode == EXT2_ROOT_INO)) {
			problem = PR_2_LINK_ROOT;
		} else if ((dot_state > 1) &&
			   (dirent->name_len & 0xFF) == 0) {
			problem = PR_2_NULL_NAME;
		}

		if (problem) {
			if (fix_problem(ctx, problem, &cd->pctx)) {
				dirent->inode = 0;
				dir_modified++;
				goto next;
			} else {
				ext2fs_unmark_valid(fs);
				if (problem == PR_2_BAD_INO)
					goto next;
			}
		}

		if (ctx->inode_bad_map &&
		    ext2fs_test_inode_bitmap(ctx->inode_bad_map,
					     dirent->inode)) {
			if (e2fsck_process_bad_inode(ctx, ino,
						     dirent->inode,
						     buf + fs->blocksize)) {
				dirent->inode = 0;
				dir_modified++;
				goto next;
			}
			if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
				return DIRENT_ABORT;
		}

		group = ext2fs_group_of_ino(fs, dirent->inode);
		first_unused_inode = group * fs->super->s_inodes_per_group +
					1 + fs->super->s_inodes_per_group -
					fs->group_desc[group].bg_itable_unused;
		cd->pctx.group = group;

		if (fs->group_desc[group].bg_flags & EXT2_BG_INODE_UNINIT) {
			pctx.num = dirent->inode;
			if (fix_problem(ctx, PR_2_INOREF_BG_INO_UNINIT,
					&cd->pctx)){
				fs->group_desc[group].bg_flags &=
					~EXT2_BG_INODE_UNINIT;
				ext2fs_mark_super_dirty(fs);
				ctx->flags |= E2F_FLAG_RESTART_LATER;
			} else {
				ext2fs_unmark_valid(fs);
				if (problem == PR_2_BAD_INO)
					goto next;
			}
		} else if (dirent->inode >= first_unused_inode) {
			pctx.num = dirent->inode;
			if (fix_problem(ctx, PR_2_INOREF_IN_UNUSED, &cd->pctx)){
				fs->group_desc[group].bg_itable_unused = 0;
				ext2fs_mark_super_dirty(fs);
				ctx->flags |= E2F_FLAG_RESTART_LATER;
			} else {
				ext2fs_unmark_valid(fs);
				if (problem == PR_2_BAD_INO)
					goto next;
			}
		}

		if (!(ctx->flags & E2F_FLAG_RESTART_LATER) &&
		    !(ext2fs_test_inode_bitmap(ctx->inode_used_map,
					       dirent->inode)))
			problem = PR_2_UNUSED_INODE;

		if (problem) {
			if (fix_problem(ctx, problem, &cd->pctx)) {
				dirent->inode = 0;
				dir_modified++;
				goto next;
			} else {
				ext2fs_unmark_valid(fs);
				if (problem == PR_2_BAD_INO)
					goto next;
			}
		}

		if (check_name(ctx, dirent, ino, &cd->pctx))
			dir_modified++;

		if (check_filetype(ctx, dirent, ino, &cd->pctx))
			dir_modified++;

#ifdef ENABLE_HTREE
		if (dx_db) {
			ext2fs_dirhash(dx_dir->hashversion, dirent->name,
				       (dirent->name_len & 0xFF),
				       fs->super->s_hash_seed, &hash, 0);
			if (hash < dx_db->min_hash)
				dx_db->min_hash = hash;
			if (hash > dx_db->max_hash)
				dx_db->max_hash = hash;
		}
#endif

		if ((dot_state > 1) &&
		    (ext2fs_test_inode_bitmap(ctx->inode_dir_map,
					      dirent->inode))) {
			if (e2fsck_dir_info_get_parent(ctx, dirent->inode,
						       &subdir_parent)) {
				cd->pctx.ino = dirent->inode;
				fix_problem(ctx, PR_2_NO_DIRINFO, &cd->pctx);
				goto abort_free_dict;
			}
			if (subdir_parent) {
				cd->pctx.ino2 = subdir_parent;
				if (fix_problem(ctx, PR_2_LINK_DIR,
						&cd->pctx)) {
					dirent->inode = 0;
					dir_modified++;
					goto next;
				}
				cd->pctx.ino2 = 0;
			} else {
				(void) e2fsck_dir_info_set_parent(ctx,
						  dirent->inode, ino);
			}
		}

		if (dups_found) {
			;
		} else if (dict_lookup(&de_dict, dirent)) {
			clear_problem_context(&pctx);
			pctx.ino = ino;
			pctx.dirent = dirent;
			fix_problem(ctx, PR_2_REPORT_DUP_DIRENT, &pctx);
			if (!ctx->dirs_to_hash)
				ext2fs_u32_list_create(&ctx->dirs_to_hash, 50);
			if (ctx->dirs_to_hash)
				ext2fs_u32_list_add(ctx->dirs_to_hash, ino);
			dups_found++;
		} else
			dict_alloc_insert(&de_dict, dirent, dirent);

		ext2fs_icount_increment(ctx->inode_count, dirent->inode,
					&links);
		if (links > 1)
			ctx->fs_links_count++;
		ctx->fs_total_count++;
	next:
		prev = dirent;
		if (dir_modified)
			(void) ext2fs_get_rec_len(fs, dirent, &rec_len);
		offset += rec_len;
		dot_state++;
	} while (offset < fs->blocksize);
#if 0
	printf("\n");
#endif
#ifdef ENABLE_HTREE
	if (dx_db) {
#ifdef DX_DEBUG
		printf("db_block %d, type %d, min_hash 0x%0x, max_hash 0x%0x\n",
		       db->blockcnt, dx_db->type,
		       dx_db->min_hash, dx_db->max_hash);
#endif
		cd->pctx.dir = cd->pctx.ino;
		if ((dx_db->type == DX_DIRBLOCK_ROOT) ||
		    (dx_db->type == DX_DIRBLOCK_NODE))
			parse_int_node(fs, db, cd, dx_dir, buf);
	}
#endif 
	if (offset != fs->blocksize) {
		cd->pctx.num = rec_len - fs->blocksize + offset;
		if (fix_problem(ctx, PR_2_FINAL_RECLEN, &cd->pctx)) {
			dirent->rec_len = cd->pctx.num;
			dir_modified++;
		}
	}
	if (dir_modified) {
		cd->pctx.errcode = ext2fs_write_dir_block(fs, block_nr, buf);
		if (cd->pctx.errcode) {
			if (!fix_problem(ctx, PR_2_WRITE_DIRBLOCK,
					 &cd->pctx))
				goto abort_free_dict;
		}
		ext2fs_mark_changed(fs);
	}
	dict_free_nodes(&de_dict);
	return 0;
abort_free_dict:
	ctx->flags |= E2F_FLAG_ABORT;
	dict_free_nodes(&de_dict);
	return DIRENT_ABORT;
}