예제 #1
0
/*
 * Helper function which writes out a directory block.
 */
static int write_dir_block(ext2_filsys fs,
			   blk_t	*block_nr,
			   e2_blkcnt_t blockcnt,
			   blk_t ref_block EXT2FS_ATTR((unused)),
			   int ref_offset EXT2FS_ATTR((unused)),
			   void *priv_data)
{
	struct write_dir_struct	*wd = (struct write_dir_struct *) priv_data;
	blk_t	blk;
	char	*dir;

	if (*block_nr == 0)
		return 0;
	if (blockcnt >= wd->outdir->num) {
		e2fsck_read_bitmaps(wd->ctx);
		blk = *block_nr;
		ext2fs_unmark_block_bitmap2(wd->ctx->block_found_map, blk);
		ext2fs_block_alloc_stats(fs, blk, -1);
		*block_nr = 0;
		wd->cleared++;
		return BLOCK_CHANGED;
	}
	if (blockcnt < 0)
		return 0;

	dir = wd->outdir->buf + (blockcnt * fs->blocksize);
	wd->err = ext2fs_write_dir_block(fs, *block_nr, dir);
	if (wd->err)
		return BLOCK_ABORT;
	return 0;
}
예제 #2
0
static int expand_dir_proc(ext2_filsys	fs,
			   blk_t	*blocknr,
			   e2_blkcnt_t	blockcnt,
			   blk_t	ref_block EXT2FS_ATTR((unused)),
			   int		ref_offset EXT2FS_ATTR((unused)),
			   void		*priv_data)
{
	struct expand_dir_struct *es = (struct expand_dir_struct *) priv_data;
	blk_t	new_blk;
	static blk_t	last_blk = 0;
	char		*block;
	errcode_t	retval;

	if (*blocknr) {
		last_blk = *blocknr;
		return 0;
	}
	retval = ext2fs_new_block(fs, last_blk, 0, &new_blk);
	if (retval) {
		es->err = retval;
		return BLOCK_ABORT;
	}
	if (blockcnt > 0) {
		retval = ext2fs_new_dir_block(fs, 0, 0, &block);
		if (retval) {
			es->err = retval;
			return BLOCK_ABORT;
		}
		es->done = 1;
		retval = ext2fs_write_dir_block(fs, new_blk, block);
	} else {
		retval = ext2fs_get_mem(fs->blocksize, &block);
		if (retval) {
			es->err = retval;
			return BLOCK_ABORT;
		}
		memset(block, 0, fs->blocksize);
		retval = io_channel_write_blk(fs->io, new_blk, 1, block);
	}
	if (retval) {
		es->err = retval;
		return BLOCK_ABORT;
	}
	ext2fs_free_mem(&block);
	*blocknr = new_blk;
	ext2fs_block_alloc_stats(fs, new_blk, +1);
	es->newblocks++;

	if (es->done)
		return (BLOCK_CHANGED | BLOCK_ABORT);
	else
		return BLOCK_CHANGED;
}
예제 #3
0
static int swap_block(ext2_filsys fs, blk_t *block_nr, int blockcnt,
		      void *priv_data)
{
	errcode_t	retval;
	
	struct swap_block_struct *sb = (struct swap_block_struct *) priv_data;

	if (sb->isdir && (blockcnt >= 0) && *block_nr) {
		retval = ext2fs_read_dir_block(fs, *block_nr, sb->dir_buf);
		if (retval) {
			sb->errcode = retval;
			return BLOCK_ABORT;
		}
		retval = ext2fs_write_dir_block(fs, *block_nr, sb->dir_buf);
		if (retval) {
			sb->errcode = retval;
			return BLOCK_ABORT;
		}
	}
	if (blockcnt >= 0) {
		if (blockcnt < EXT2_NDIR_BLOCKS)
			return 0;
		return BLOCK_CHANGED;
	}
	if (blockcnt == BLOCK_COUNT_IND) {
		if (*block_nr == sb->inode->i_block[EXT2_IND_BLOCK])
			return 0;
		return BLOCK_CHANGED;
	}
	if (blockcnt == BLOCK_COUNT_DIND) {
		if (*block_nr == sb->inode->i_block[EXT2_DIND_BLOCK])
			return 0;
		return BLOCK_CHANGED;
	}
	if (blockcnt == BLOCK_COUNT_TIND) {
		if (*block_nr == sb->inode->i_block[EXT2_TIND_BLOCK])
			return 0;
		return BLOCK_CHANGED;
	}
	return BLOCK_CHANGED;
}
예제 #4
0
/*
 * Helper function which is private to this module.  Used by
 * ext2fs_dir_iterate() and ext2fs_dblist_dir_iterate()
 */
int ext2fs_process_dir_block(ext2_filsys  	fs,
                             blk_t		*blocknr,
                             e2_blkcnt_t		blockcnt,
                             blk_t		ref_block,
                             int			ref_offset,
                             void		*priv_data)
{
    struct dir_context *ctx = (struct dir_context *) priv_data;
    int		offset = 0;
    int		next_real_entry = 0;
    int		ret = 0;
    int		changed = 0;
    int		do_abort = 0;
    int		entry, size;
    struct ext2_dir_entry *dirent;

    if (blockcnt < 0)
        return 0;

    entry = blockcnt ? DIRENT_OTHER_FILE : DIRENT_DOT_FILE;

    ctx->errcode = ext2fs_read_dir_block(fs, *blocknr, ctx->buf);
    if (ctx->errcode)
        return BLOCK_ABORT;

    while (offset < fs->blocksize) {
        dirent = (struct ext2_dir_entry *) (ctx->buf + offset);
        if (((offset + dirent->rec_len) > fs->blocksize) ||
                (dirent->rec_len < 8) ||
                ((dirent->rec_len % 4) != 0) ||
                (((dirent->name_len & 0xFF)+8) > dirent->rec_len)) {
            ctx->errcode = EXT2_ET_DIR_CORRUPTED;
            return BLOCK_ABORT;
        }
        if (!dirent->inode &&
                !(ctx->flags & DIRENT_FLAG_INCLUDE_EMPTY))
            goto next;

        ret = (ctx->func)(ctx->dir,
                          (next_real_entry > offset) ?
                          DIRENT_DELETED_FILE : entry,
                          dirent, offset,
                          fs->blocksize, ctx->buf,
                          ctx->priv_data);
        if (entry < DIRENT_OTHER_FILE)
            entry++;

        if (ret & DIRENT_CHANGED)
            changed++;
        if (ret & DIRENT_ABORT) {
            do_abort++;
            break;
        }
next:
        if (next_real_entry == offset)
            next_real_entry += dirent->rec_len;

        if (ctx->flags & DIRENT_FLAG_INCLUDE_REMOVED) {
            size = ((dirent->name_len & 0xFF) + 11) & ~3;

            if (dirent->rec_len != size)  {
                int final_offset = offset + dirent->rec_len;

                offset += size;
                while (offset < final_offset &&
                        !ext2fs_validate_entry(ctx->buf,
                                               offset,
                                               final_offset))
                    offset += 4;
                continue;
            }
        }
        offset += dirent->rec_len;
    }

    if (changed) {
        ctx->errcode = ext2fs_write_dir_block(fs, *blocknr, ctx->buf);
        if (ctx->errcode)
            return BLOCK_ABORT;
    }
    if (do_abort)
        return BLOCK_ABORT;
    return 0;
}
예제 #5
0
파일: mkdir.c 프로젝트: haie1011/nofs
errcode_t ext2fs_mkdir(ext2_filsys fs, ext2_ino_t parent, ext2_ino_t inum,
		       const char *name)
{
	errcode_t		retval;
	struct ext2_inode	parent_inode, inode;
	ext2_ino_t		ino = inum;
	ext2_ino_t		scratch_ino;
	blk_t			blk;
	char			*block = 0;
	char 			*ext2bp_block = 0;

	EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);

	/*
	 * Allocate an inode, if necessary
	 */
	if (!ino) {
		retval = ext2fs_new_inode(fs, parent, LINUX_S_IFDIR | 0755,
					  0, &ino);
		if (retval)
			goto cleanup;
	}


	/*
	 * Allocate a data block for the directory
	 */
	retval = ext2fs_new_block(fs, 0, 0, &blk);
	printf("Using block %lu for the dir\n", blk);
	if (retval)
		goto cleanup;


	printf("Writing the dir info into the buffer\n");
	/*
	 * Create a scratch template for the directory
	 */
	/* Vijay: Offset the block address by the backpointer information */
	retval = ext2fs_new_dir_block(fs, ino, parent, &block);
	//ext2bp_block = block + EXT2BP_HEADER_SIZE;
	//retval = ext2fs_new_dir_block(fs, ino, parent, &ext2bp_block);
	if (retval)
		goto cleanup;

	/*
	 * Get the parent's inode, if necessary
	 */
	if (parent != ino) {
		retval = ext2fs_read_inode(fs, parent, &parent_inode);
		if (retval)
			goto cleanup;
	} else
		memset(&parent_inode, 0, sizeof(parent_inode));

	/*
	 * Create the inode structure....
	 */
	memset(&inode, 0, sizeof(struct ext2_inode));
	inode.i_mode = LINUX_S_IFDIR | (0777 & ~fs->umask);
	inode.i_uid = inode.i_gid = 0;
	ext2fs_iblk_set(fs, &inode, 1);
	inode.i_block[0] = blk;
	inode.i_links_count = 2;
	inode.i_size = fs->blocksize;

	/* Vijay: Adding backlink to the inode - Modifying position 0
	 * since this is a new inode - It will not have any other links
	 * pointing to it. First, we need to reset all the backlinks.
	 */
	printf("Vijay: Adding the backlink to %s\n", name);

	int i;
	for(i=0; i < EXT2_N_LINKS; i++)
		inode.i_backlinks[i] = 0;
	inode.i_backlinks[0] = parent;

	printf("Going to write the dir block\n");
	/*
	 * Write out the inode and inode data block
	 */
	retval = ext2fs_write_dir_block(fs, blk, block);
	if (retval)
		goto cleanup;
	retval = ext2fs_write_new_inode(fs, ino, &inode);
	if (retval)
		goto cleanup;


	printf("Link the directory into the filesystem hierarchy\n");
	/*
	 * Link the directory into the filesystem hierarchy
	 */
	if (name) {
		retval = ext2fs_lookup(fs, parent, name, strlen(name), 0,
				       &scratch_ino);
		if (!retval) {
			retval = EXT2_ET_DIR_EXISTS;
			name = 0;
			goto cleanup;
		}
		if (retval != EXT2_ET_FILE_NOT_FOUND)
			goto cleanup;
		retval = ext2fs_link(fs, parent, name, ino, EXT2_FT_DIR);
		printf("Linking done: %lu\n", retval);
		if (retval)
			goto cleanup;
	}

	/*
	 * Update parent inode's counts
	 */
	if (parent != ino) {
		parent_inode.i_links_count++;
		retval = ext2fs_write_inode(fs, parent, &parent_inode);
		if (retval)
			goto cleanup;
	}

	/*
	 * Update accounting....
	 */
	ext2fs_block_alloc_stats(fs, blk, +1);
	ext2fs_inode_alloc_stats2(fs, ino, +1, 1);

cleanup:
	if (block)
		ext2fs_free_mem(&block);
	return retval;

}
예제 #6
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;
}
예제 #7
0
static int expand_dir_proc(ext2_filsys	fs,
			   blk64_t	*blocknr,
			   e2_blkcnt_t	blockcnt,
			   blk64_t	ref_block EXT2FS_ATTR((unused)),
			   int		ref_offset EXT2FS_ATTR((unused)),
			   void		*priv_data)
{
	struct expand_dir_struct *es = (struct expand_dir_struct *) priv_data;
	blk64_t	new_blk;
	char		*block;
	errcode_t	retval;

	if (*blocknr) {
		if (blockcnt >= 0)
			es->goal = *blocknr;
		return 0;
	}
	if (blockcnt &&
	    (EXT2FS_B2C(fs, es->goal) == EXT2FS_B2C(fs, es->goal+1)))
		new_blk = es->goal+1;
	else {
		es->goal &= ~EXT2FS_CLUSTER_MASK(fs);
		retval = ext2fs_new_block2(fs, es->goal, 0, &new_blk);
		if (retval) {
			es->err = retval;
			return BLOCK_ABORT;
		}
		es->newblocks++;
	}
	if (blockcnt > 0) {
		retval = ext2fs_new_dir_block(fs, 0, 0, &block);
		if (retval) {
			es->err = retval;
			return BLOCK_ABORT;
		}
		es->done = 1;
		retval = ext2fs_write_dir_block(fs, new_blk, block);
	} else {
		retval = ext2fs_get_mem(fs->blocksize, &block);
		if (retval) {
			es->err = retval;
			return BLOCK_ABORT;
		}
		memset(block, 0, fs->blocksize);
		retval = io_channel_write_blk64(fs->io, new_blk, 1, block);
	}
	if (blockcnt >= 0)
		es->goal = new_blk;
	if (retval) {
		es->err = retval;
		return BLOCK_ABORT;
	}
	ext2fs_free_mem(&block);
	*blocknr = new_blk;
	ext2fs_block_alloc_stats2(fs, new_blk, +1);

	if (es->done)
		return (BLOCK_CHANGED | BLOCK_ABORT);
	else
		return BLOCK_CHANGED;
}
예제 #8
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;

	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;

	/*
	 * 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_block3(fs, block_nr, buf, 0);
	ehandler_operation(0);
	if (cd->pctx.errcode == EXT2_ET_DIR_CORRUPTED)
		cd->pctx.errcode = 0; /* We'll handle this ourselves */
	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 /* ENABLE_HTREE */

	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);
			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 /* ENABLE_HTREE */
	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;
}
예제 #9
0
/*
 * This routine gets the lost_and_found inode, making it a directory
 * if necessary
 */
ext2_ino_t e2fsck_get_lost_and_found(e2fsck_t ctx, int fix)
{
    ext2_filsys fs = ctx->fs;
    ext2_ino_t			ino;
    blk64_t			blk;
    errcode_t		retval;
    struct ext2_inode	inode;
    char *			block;
    static const char	name[] = "lost+found";
    struct 	problem_context	pctx;

    if (ctx->lost_and_found)
        return ctx->lost_and_found;

    clear_problem_context(&pctx);

    retval = ext2fs_lookup(fs, EXT2_ROOT_INO, name,
                           sizeof(name)-1, 0, &ino);
    if (retval && !fix)
        return 0;
    if (!retval) {
        if (ext2fs_test_inode_bitmap2(ctx->inode_dir_map, ino)) {
            ctx->lost_and_found = ino;
            return ino;
        }

        /* Lost+found isn't a directory! */
        if (!fix)
            return 0;
        pctx.ino = ino;
        if (!fix_problem(ctx, PR_3_LPF_NOTDIR, &pctx))
            return 0;

        /* OK, unlink the old /lost+found file. */
        pctx.errcode = ext2fs_unlink(fs, EXT2_ROOT_INO, name, ino, 0);
        if (pctx.errcode) {
            pctx.str = "ext2fs_unlink";
            fix_problem(ctx, PR_3_CREATE_LPF_ERROR, &pctx);
            return 0;
        }
        (void) e2fsck_dir_info_set_parent(ctx, ino, 0);
        e2fsck_adjust_inode_count(ctx, ino, -1);
    } else if (retval != EXT2_ET_FILE_NOT_FOUND) {
        pctx.errcode = retval;
        fix_problem(ctx, PR_3_ERR_FIND_LPF, &pctx);
    }
    if (!fix_problem(ctx, PR_3_NO_LF_DIR, 0))
        return 0;

    /*
     * Read the inode and block bitmaps in; we'll be messing with
     * them.
     */
    e2fsck_read_bitmaps(ctx);

    /*
     * First, find a free block
     */
    retval = ext2fs_new_block2(fs, 0, ctx->block_found_map, &blk);
    if (retval) {
        pctx.errcode = retval;
        fix_problem(ctx, PR_3_ERR_LPF_NEW_BLOCK, &pctx);
        return 0;
    }
    ext2fs_mark_block_bitmap2(ctx->block_found_map, blk);
    ext2fs_block_alloc_stats2(fs, blk, +1);

    /*
     * Next find a free inode.
     */
    retval = ext2fs_new_inode(fs, EXT2_ROOT_INO, 040700,
                              ctx->inode_used_map, &ino);
    if (retval) {
        pctx.errcode = retval;
        fix_problem(ctx, PR_3_ERR_LPF_NEW_INODE, &pctx);
        return 0;
    }
    ext2fs_mark_inode_bitmap2(ctx->inode_used_map, ino);
    ext2fs_mark_inode_bitmap2(ctx->inode_dir_map, ino);
    ext2fs_inode_alloc_stats2(fs, ino, +1, 1);

    /*
     * Now let's create the actual data block for the inode
     */
    retval = ext2fs_new_dir_block(fs, ino, EXT2_ROOT_INO, &block);
    if (retval) {
        pctx.errcode = retval;
        fix_problem(ctx, PR_3_ERR_LPF_NEW_DIR_BLOCK, &pctx);
        return 0;
    }

    retval = ext2fs_write_dir_block(fs, blk, block);
    ext2fs_free_mem(&block);
    if (retval) {
        pctx.errcode = retval;
        fix_problem(ctx, PR_3_ERR_LPF_WRITE_BLOCK, &pctx);
        return 0;
    }

    /*
     * Set up the inode structure
     */
    memset(&inode, 0, sizeof(inode));
    inode.i_mode = 040700;
    inode.i_size = fs->blocksize;
    inode.i_atime = inode.i_ctime = inode.i_mtime = ctx->now;
    inode.i_links_count = 2;
    ext2fs_iblk_set(fs, &inode, 1);
    inode.i_block[0] = blk;

    /*
     * Next, write out the inode.
     */
    pctx.errcode = ext2fs_write_new_inode(fs, ino, &inode);
    if (pctx.errcode) {
        pctx.str = "ext2fs_write_inode";
        fix_problem(ctx, PR_3_CREATE_LPF_ERROR, &pctx);
        return 0;
    }
    /*
     * Finally, create the directory link
     */
    pctx.errcode = ext2fs_link(fs, EXT2_ROOT_INO, name, ino, EXT2_FT_DIR);
    if (pctx.errcode) {
        pctx.str = "ext2fs_link";
        fix_problem(ctx, PR_3_CREATE_LPF_ERROR, &pctx);
        return 0;
    }

    /*
     * Miscellaneous bookkeeping that needs to be kept straight.
     */
    e2fsck_add_dir_info(ctx, ino, EXT2_ROOT_INO);
    e2fsck_adjust_inode_count(ctx, EXT2_ROOT_INO, 1);
    ext2fs_icount_store(ctx->inode_count, ino, 2);
    ext2fs_icount_store(ctx->inode_link_info, ino, 2);
    ctx->lost_and_found = ino;
    quota_data_add(ctx->qctx, &inode, ino, fs->blocksize);
    quota_data_inodes(ctx->qctx, &inode, ino, +1);
#if 0
    printf("/lost+found created; inode #%lu\n", ino);
#endif
    return ino;
}
예제 #10
0
/*
 * This makes sure the root inode is present; if not, we ask if the
 * user wants us to create it.  Not creating it is a fatal error.
 */
static void check_root(e2fsck_t ctx)
{
    ext2_filsys fs = ctx->fs;
    blk64_t			blk;
    struct ext2_inode	inode;
    char *			block;
    struct problem_context	pctx;

    clear_problem_context(&pctx);

    if (ext2fs_test_inode_bitmap2(ctx->inode_used_map, EXT2_ROOT_INO)) {
        /*
         * If the root inode is not a directory, die here.  The
         * user must have answered 'no' in pass1 when we
         * offered to clear it.
         */
        if (!(ext2fs_test_inode_bitmap2(ctx->inode_dir_map,
                                        EXT2_ROOT_INO))) {
            fix_problem(ctx, PR_3_ROOT_NOT_DIR_ABORT, &pctx);
            ctx->flags |= E2F_FLAG_ABORT;
        }
        return;
    }

    if (!fix_problem(ctx, PR_3_NO_ROOT_INODE, &pctx)) {
        fix_problem(ctx, PR_3_NO_ROOT_INODE_ABORT, &pctx);
        ctx->flags |= E2F_FLAG_ABORT;
        return;
    }

    e2fsck_read_bitmaps(ctx);

    /*
     * First, find a free block
     */
    pctx.errcode = ext2fs_new_block2(fs, 0, ctx->block_found_map, &blk);
    if (pctx.errcode) {
        pctx.str = "ext2fs_new_block";
        fix_problem(ctx, PR_3_CREATE_ROOT_ERROR, &pctx);
        ctx->flags |= E2F_FLAG_ABORT;
        return;
    }
    ext2fs_mark_block_bitmap2(ctx->block_found_map, blk);
    ext2fs_mark_block_bitmap2(fs->block_map, blk);
    ext2fs_mark_bb_dirty(fs);

    /*
     * Now let's create the actual data block for the inode
     */
    pctx.errcode = ext2fs_new_dir_block(fs, EXT2_ROOT_INO, EXT2_ROOT_INO,
                                        &block);
    if (pctx.errcode) {
        pctx.str = "ext2fs_new_dir_block";
        fix_problem(ctx, PR_3_CREATE_ROOT_ERROR, &pctx);
        ctx->flags |= E2F_FLAG_ABORT;
        return;
    }

    pctx.errcode = ext2fs_write_dir_block(fs, blk, block);
    if (pctx.errcode) {
        pctx.str = "ext2fs_write_dir_block";
        fix_problem(ctx, PR_3_CREATE_ROOT_ERROR, &pctx);
        ctx->flags |= E2F_FLAG_ABORT;
        return;
    }
    ext2fs_free_mem(&block);

    /*
     * Set up the inode structure
     */
    memset(&inode, 0, sizeof(inode));
    inode.i_mode = 040755;
    inode.i_size = fs->blocksize;
    inode.i_atime = inode.i_ctime = inode.i_mtime = ctx->now;
    inode.i_links_count = 2;
    ext2fs_iblk_set(fs, &inode, 1);
    inode.i_block[0] = blk;

    /*
     * Write out the inode.
     */
    pctx.errcode = ext2fs_write_new_inode(fs, EXT2_ROOT_INO, &inode);
    if (pctx.errcode) {
        pctx.str = "ext2fs_write_inode";
        fix_problem(ctx, PR_3_CREATE_ROOT_ERROR, &pctx);
        ctx->flags |= E2F_FLAG_ABORT;
        return;
    }

    /*
     * Miscellaneous bookkeeping...
     */
    e2fsck_add_dir_info(ctx, EXT2_ROOT_INO, EXT2_ROOT_INO);
    ext2fs_icount_store(ctx->inode_count, EXT2_ROOT_INO, 2);
    ext2fs_icount_store(ctx->inode_link_info, EXT2_ROOT_INO, 2);

    ext2fs_mark_inode_bitmap2(ctx->inode_used_map, EXT2_ROOT_INO);
    ext2fs_mark_inode_bitmap2(ctx->inode_dir_map, EXT2_ROOT_INO);
    ext2fs_mark_inode_bitmap2(fs->inode_map, EXT2_ROOT_INO);
    ext2fs_mark_ib_dirty(fs);
}
예제 #11
0
errcode_t ext2fs_mkdir(ext2_filsys fs, ext2_ino_t parent, ext2_ino_t inum,
		       const char *name)
{
	errcode_t		retval;
	struct ext2_inode	parent_inode, inode;
	ext2_ino_t		ino = inum;
	ext2_ino_t		scratch_ino;
	blk_t			blk;
	char			*block = 0;

	EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);

	/*
	 * Allocate an inode, if necessary
	 */
	if (!ino) {
		retval = ext2fs_new_inode(fs, parent, LINUX_S_IFDIR | 0755,
					  0, &ino);
		if (retval)
			goto cleanup;
	}

	/*
	 * Allocate a data block for the directory
	 */
	retval = ext2fs_new_block(fs, 0, 0, &blk);
	if (retval)
		goto cleanup;

	/*
	 * Create a scratch template for the directory
	 */
	retval = ext2fs_new_dir_block(fs, ino, parent, &block);
	if (retval)
		goto cleanup;

	/*
	 * Get the parent's inode, if necessary
	 */
	if (parent != ino) {
		retval = ext2fs_read_inode(fs, parent, &parent_inode);
		if (retval)
			goto cleanup;
	} else
		memset(&parent_inode, 0, sizeof(parent_inode));

	/*
	 * Create the inode structure....
	 */
	memset(&inode, 0, sizeof(struct ext2_inode));
	inode.i_mode = LINUX_S_IFDIR | (0777 & ~fs->umask);
	inode.i_uid = inode.i_gid = 0;
	inode.i_blocks = fs->blocksize / 512;
	inode.i_block[0] = blk;
	inode.i_links_count = 2;
	inode.i_ctime = inode.i_atime = inode.i_mtime = fs->now ? fs->now : time(NULL);
	inode.i_size = fs->blocksize;

	/*
	 * Write out the inode and inode data block
	 */
	retval = ext2fs_write_dir_block(fs, blk, block);
	if (retval)
		goto cleanup;
	retval = ext2fs_write_new_inode(fs, ino, &inode); 
	if (retval)
		goto cleanup;

	/*
	 * Link the directory into the filesystem hierarchy
	 */
	if (name) {
		retval = ext2fs_lookup(fs, parent, name, strlen(name), 0,
				       &scratch_ino);
		if (!retval) {
			retval = EXT2_ET_DIR_EXISTS;
			name = 0;
			goto cleanup;
		}
		if (retval != EXT2_ET_FILE_NOT_FOUND)
			goto cleanup;
		retval = ext2fs_link(fs, parent, name, ino, EXT2_FT_DIR);
		if (retval)
			goto cleanup;
	}

	/*
	 * Update parent inode's counts
	 */
	if (parent != ino) {
		parent_inode.i_links_count++;
		retval = ext2fs_write_inode(fs, parent, &parent_inode);
		if (retval)
			goto cleanup;
	}
	
	/*
	 * Update accounting....
	 */
	ext2fs_block_alloc_stats(fs, blk, +1);
	ext2fs_inode_alloc_stats2(fs, ino, +1, 1);

cleanup:
	if (block)
		ext2fs_free_mem(&block);
	return retval;

}
예제 #12
0
/*
 * allocate_dir_block --- this function allocates a new directory
 * 	block for a particular inode; this is done if a directory has
 * 	a "hole" in it, or if a directory has a illegal block number
 * 	that was zeroed out and now needs to be replaced.
 */
static int allocate_dir_block(e2fsck_t ctx,
                              struct ext2_db_entry *db,
                              char *buf, struct problem_context *pctx)
{
    ext2_filsys fs = ctx->fs;
    blk_t			blk;
    char			*block;
    struct ext2_inode	inode;

    if (fix_problem(ctx, PR_2_DIRECTORY_HOLE, pctx) == 0)
        return 1;

    /*
     * Read the inode and block bitmaps in; we'll be messing with
     * them.
     */
    e2fsck_read_bitmaps(ctx);

    /*
     * First, find a free block
     */
    pctx->errcode = ext2fs_new_block(fs, 0, ctx->block_found_map, &blk);
    if (pctx->errcode) {
        pctx->str = "ext2fs_new_block";
        fix_problem(ctx, PR_2_ALLOC_DIRBOCK, pctx);
        return 1;
    }
    ext2fs_mark_block_bitmap(ctx->block_found_map, blk);
    ext2fs_mark_block_bitmap(fs->block_map, blk);
    ext2fs_mark_bb_dirty(fs);

    /*
     * Now let's create the actual data block for the inode
     */
    if (db->blockcnt)
        pctx->errcode = ext2fs_new_dir_block(fs, 0, 0, &block);
    else
        pctx->errcode = ext2fs_new_dir_block(fs, db->ino,
                                             EXT2_ROOT_INO, &block);

    if (pctx->errcode) {
        pctx->str = "ext2fs_new_dir_block";
        fix_problem(ctx, PR_2_ALLOC_DIRBOCK, pctx);
        return 1;
    }

    pctx->errcode = ext2fs_write_dir_block(fs, blk, block);
    ext2fs_free_mem((void **) &block);
    if (pctx->errcode) {
        pctx->str = "ext2fs_write_dir_block";
        fix_problem(ctx, PR_2_ALLOC_DIRBOCK, pctx);
        return 1;
    }

    /*
     * Update the inode block count
     */
    e2fsck_read_inode(ctx, db->ino, &inode, "allocate_dir_block");
    inode.i_blocks += fs->blocksize / 512;
    if (inode.i_size < (db->blockcnt+1) * fs->blocksize)
        inode.i_size = (db->blockcnt+1) * fs->blocksize;
    e2fsck_write_inode(ctx, db->ino, &inode, "allocate_dir_block");

    /*
     * Finally, update the block pointers for the inode
     */
    db->blk = blk;
    pctx->errcode = ext2fs_block_iterate2(fs, db->ino, BLOCK_FLAG_HOLE,
                                          0, update_dir_block, db);
    if (pctx->errcode) {
        pctx->str = "ext2fs_block_iterate";
        fix_problem(ctx, PR_2_ALLOC_DIRBOCK, pctx);
        return 1;
    }

    return 0;
}
예제 #13
0
static int check_dir_block(ext2_filsys fs,
                           struct ext2_db_entry *db,
                           void *priv_data)
{
    struct dir_info		*subdir, *dir;
    struct ext2_dir_entry 	*dirent;
    int			offset = 0;
    int			dir_modified = 0;
    int			dot_state;
    blk_t			block_nr = db->blk;
    ext2_ino_t 		ino = db->ino;
    __u16			links;
    struct check_dir_struct	*cd;
    char 			*buf;
    e2fsck_t		ctx;
    int			problem;

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

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

    /*
     * Make sure the inode is still in use (could have been
     * deleted in the duplicate/bad blocks pass.
     */
    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 0
    printf("In process_dir_block block %lu, #%d, inode %lu\n", block_nr,
           db->blockcnt, ino);
#endif

    cd->pctx.errcode = ext2fs_read_dir_block(fs, block_nr, buf);
    if (cd->pctx.errcode == EXT2_ET_DIR_CORRUPTED)
        cd->pctx.errcode = 0; /* We'll handle this ourselves */
    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);
    }

    do {
        dot_state++;
        problem = 0;
        dirent = (struct ext2_dir_entry *) (buf + offset);
        cd->pctx.dirent = dirent;
        cd->pctx.num = offset;
        if (((offset + dirent->rec_len) > fs->blocksize) ||
                (dirent->rec_len < 12) ||
                ((dirent->rec_len % 4) != 0) ||
                (((dirent->name_len & 0xFF)+8) > dirent->rec_len)) {
            if (fix_problem(ctx, PR_2_DIR_CORRUPTED, &cd->pctx)) {
                dirent->rec_len = fs->blocksize - offset;
                dirent->name_len = 0;
                dirent->inode = 0;
                dir_modified++;
            } else
                return DIRENT_ABORT;
        }
        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 == 1) {
            if (check_dot(ctx, dirent, ino, &cd->pctx))
                dir_modified++;
        } else if (dot_state == 2) {
            dir = e2fsck_get_dir_info(ctx, ino);
            if (!dir) {
                fix_problem(ctx, PR_2_NO_DIRINFO, &cd->pctx);
                ctx->flags |= E2F_FLAG_ABORT;
                return DIRENT_ABORT;
            }
            if (check_dotdot(ctx, dirent, dir, &cd->pctx))
                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 (!(ext2fs_test_inode_bitmap(ctx->inode_used_map,
                                              dirent->inode))) {
            /*
             * If the inode is unused, offer to clear it.
             */
            problem = PR_2_UNUSED_INODE;
        } else if (ctx->inode_bb_map &&
                   (ext2fs_test_inode_bitmap(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 > 2) &&
                   ((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 > 2) &&
                   ((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 > 2) &&
                   (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 > 2) &&
                   (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_bitmap(ctx->inode_bad_map,
                                         dirent->inode)) {
            if (e2fsck_process_bad_inode(ctx, ino,
                                         dirent->inode)) {
                dirent->inode = 0;
                dir_modified++;
                goto next;
            }
            if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
                return DIRENT_ABORT;
        }

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

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

        /*
         * 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 > 2) &&
                (ext2fs_test_inode_bitmap(ctx->inode_dir_map,
                                          dirent->inode))) {
            subdir = e2fsck_get_dir_info(ctx, dirent->inode);
            if (!subdir) {
                cd->pctx.ino = dirent->inode;
                fix_problem(ctx, PR_2_NO_DIRINFO, &cd->pctx);
                ctx->flags |= E2F_FLAG_ABORT;
                return DIRENT_ABORT;
            }
            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
                subdir->parent = ino;
        }

        ext2fs_icount_increment(ctx->inode_count, dirent->inode,
                                &links);
        if (links > 1)
            ctx->fs_links_count++;
        ctx->fs_total_count++;
next:
        offset += dirent->rec_len;
    } while (offset < fs->blocksize);
#if 0
    printf("\n");
#endif
    if (offset != fs->blocksize) {
        cd->pctx.num = dirent->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)) {
                ctx->flags |= E2F_FLAG_ABORT;
                return DIRENT_ABORT;
            }
        }
        ext2fs_mark_changed(fs);
    }
    return 0;
}
예제 #14
0
errcode_t ext2fs_mkdir(ext2_filsys fs, ext2_ino_t parent, ext2_ino_t inum,
		       const char *name)
{
	ext2_extent_handle_t	handle;
	errcode_t		retval;
	struct ext2_inode	parent_inode, inode;
	ext2_ino_t		ino = inum;
	ext2_ino_t		scratch_ino;
	blk64_t			blk;
	char			*block = 0;

	EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);

	/*
	 * Allocate an inode, if necessary
	 */
	if (!ino) {
		retval = ext2fs_new_inode(fs, parent, LINUX_S_IFDIR | 0755,
					  0, &ino);
		if (retval)
			goto cleanup;
	}

	/*
	 * Allocate a data block for the directory
	 */
	retval = ext2fs_new_block2(fs, 0, 0, &blk);
	if (retval)
		goto cleanup;

	/*
	 * Create a scratch template for the directory
	 */
	retval = ext2fs_new_dir_block(fs, ino, parent, &block);
	if (retval)
		goto cleanup;

	/*
	 * Get the parent's inode, if necessary
	 */
	if (parent != ino) {
		retval = ext2fs_read_inode(fs, parent, &parent_inode);
		if (retval)
			goto cleanup;
	} else
		memset(&parent_inode, 0, sizeof(parent_inode));

	/*
	 * Create the inode structure....
	 */
	memset(&inode, 0, sizeof(struct ext2_inode));
	inode.i_mode = LINUX_S_IFDIR | (0777 & ~fs->umask);
	inode.i_uid = inode.i_gid = 0;
	ext2fs_iblk_set(fs, &inode, 1);
	if (fs->super->s_feature_incompat & EXT3_FEATURE_INCOMPAT_EXTENTS)
		inode.i_flags |= EXT4_EXTENTS_FL;
	else
		inode.i_block[0] = blk;
	inode.i_links_count = 2;
	inode.i_size = fs->blocksize;

	/*
	 * Write out the inode and inode data block
	 */
	retval = ext2fs_write_dir_block(fs, blk, block);
	if (retval)
		goto cleanup;
	retval = ext2fs_write_new_inode(fs, ino, &inode);
	if (retval)
		goto cleanup;

	if (fs->super->s_feature_incompat & EXT3_FEATURE_INCOMPAT_EXTENTS) {
		retval = ext2fs_extent_open2(fs, ino, &inode, &handle);
		if (retval)
			goto cleanup;
		retval = ext2fs_extent_set_bmap(handle, 0, blk, 0);
		ext2fs_extent_free(handle);
		if (retval)
			goto cleanup;
	}

	/*
	 * Link the directory into the filesystem hierarchy
	 */
	if (name) {
		retval = ext2fs_lookup(fs, parent, name, strlen(name), 0,
				       &scratch_ino);
		if (!retval) {
			retval = EXT2_ET_DIR_EXISTS;
			name = 0;
			goto cleanup;
		}
		if (retval != EXT2_ET_FILE_NOT_FOUND)
			goto cleanup;
		retval = ext2fs_link(fs, parent, name, ino, EXT2_FT_DIR);
		if (retval)
			goto cleanup;
	}

	/*
	 * Update parent inode's counts
	 */
	if (parent != ino) {
		parent_inode.i_links_count++;
		retval = ext2fs_write_inode(fs, parent, &parent_inode);
		if (retval)
			goto cleanup;
	}

	/*
	 * Update accounting....
	 */
	ext2fs_block_alloc_stats2(fs, blk, +1);
	ext2fs_inode_alloc_stats2(fs, ino, +1, 1);

cleanup:
	if (block)
		ext2fs_free_mem(&block);
	return retval;

}