Commit 20e41d9b authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge tag 'ext4_for_linus_stable' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4

Pull ext4 fixes from Ted Ts'o:
 "Miscellaneous ext4 bug fixes"

* tag 'ext4_for_linus_stable' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4:
  ext4: Only advertise encrypted_casefold when encryption and unicode are enabled
  ext4: fix no-key deletion for encrypt+casefold
  ext4: fix memory leak in ext4_fill_super
  ext4: fix fast commit alignment issues
  ext4: fix bug on in ext4_es_cache_extent as ext4_split_extent_at failed
  ext4: fix accessing uninit percpu counter variable with fast_commit
  ext4: fix memory leak in ext4_mb_init_backend on error path.
parents decad3e1 e71f99f2
Loading
Loading
Loading
Loading
+23 −20
Original line number Diff line number Diff line
@@ -3206,7 +3206,10 @@ static int ext4_split_extent_at(handle_t *handle,
		ext4_ext_mark_unwritten(ex2);

	err = ext4_ext_insert_extent(handle, inode, ppath, &newex, flags);
	if (err == -ENOSPC && (EXT4_EXT_MAY_ZEROOUT & split_flag)) {
	if (err != -ENOSPC && err != -EDQUOT)
		goto out;

	if (EXT4_EXT_MAY_ZEROOUT & split_flag) {
		if (split_flag & (EXT4_EXT_DATA_VALID1|EXT4_EXT_DATA_VALID2)) {
			if (split_flag & EXT4_EXT_DATA_VALID1) {
				err = ext4_ext_zeroout(inode, ex2);
@@ -3232,25 +3235,22 @@ static int ext4_split_extent_at(handle_t *handle,
					      ext4_ext_pblock(&orig_ex));
		}

		if (err)
			goto fix_extent_len;
		if (!err) {
			/* update the extent length and mark as initialized */
			ex->ee_len = cpu_to_le16(ee_len);
			ext4_ext_try_to_merge(handle, inode, path, ex);
			err = ext4_ext_dirty(handle, inode, path + path->p_depth);
		if (err)
			goto fix_extent_len;

			if (!err)
				/* update extent status tree */
				err = ext4_zeroout_es(inode, &zero_ex);

			/* If we failed at this point, we don't know in which
			 * state the extent tree exactly is so don't try to fix
			 * length of the original extent as it may do even more
			 * damage.
			 */
			goto out;
	} else if (err)
		goto fix_extent_len;

out:
	ext4_ext_show_leaf(inode, path);
	return err;
		}
	}

fix_extent_len:
	ex->ee_len = orig_ex.ee_len;
@@ -3260,6 +3260,9 @@ static int ext4_split_extent_at(handle_t *handle,
	 */
	ext4_ext_dirty(handle, inode, path + path->p_depth);
	return err;
out:
	ext4_ext_show_leaf(inode, path);
	return err;
}

/*
+90 −80
Original line number Diff line number Diff line
@@ -1288,28 +1288,29 @@ struct dentry_info_args {
};

static inline void tl_to_darg(struct dentry_info_args *darg,
				struct  ext4_fc_tl *tl)
			      struct  ext4_fc_tl *tl, u8 *val)
{
	struct ext4_fc_dentry_info *fcd;
	struct ext4_fc_dentry_info fcd;

	fcd = (struct ext4_fc_dentry_info *)ext4_fc_tag_val(tl);
	memcpy(&fcd, val, sizeof(fcd));

	darg->parent_ino = le32_to_cpu(fcd->fc_parent_ino);
	darg->ino = le32_to_cpu(fcd->fc_ino);
	darg->dname = fcd->fc_dname;
	darg->dname_len = ext4_fc_tag_len(tl) -
	darg->parent_ino = le32_to_cpu(fcd.fc_parent_ino);
	darg->ino = le32_to_cpu(fcd.fc_ino);
	darg->dname = val + offsetof(struct ext4_fc_dentry_info, fc_dname);
	darg->dname_len = le16_to_cpu(tl->fc_len) -
		sizeof(struct ext4_fc_dentry_info);
}

/* Unlink replay function */
static int ext4_fc_replay_unlink(struct super_block *sb, struct ext4_fc_tl *tl)
static int ext4_fc_replay_unlink(struct super_block *sb, struct ext4_fc_tl *tl,
				 u8 *val)
{
	struct inode *inode, *old_parent;
	struct qstr entry;
	struct dentry_info_args darg;
	int ret = 0;

	tl_to_darg(&darg, tl);
	tl_to_darg(&darg, tl, val);

	trace_ext4_fc_replay(sb, EXT4_FC_TAG_UNLINK, darg.ino,
			darg.parent_ino, darg.dname_len);
@@ -1399,13 +1400,14 @@ static int ext4_fc_replay_link_internal(struct super_block *sb,
}

/* Link replay function */
static int ext4_fc_replay_link(struct super_block *sb, struct ext4_fc_tl *tl)
static int ext4_fc_replay_link(struct super_block *sb, struct ext4_fc_tl *tl,
			       u8 *val)
{
	struct inode *inode;
	struct dentry_info_args darg;
	int ret = 0;

	tl_to_darg(&darg, tl);
	tl_to_darg(&darg, tl, val);
	trace_ext4_fc_replay(sb, EXT4_FC_TAG_LINK, darg.ino,
			darg.parent_ino, darg.dname_len);

@@ -1450,9 +1452,10 @@ static int ext4_fc_record_modified_inode(struct super_block *sb, int ino)
/*
 * Inode replay function
 */
static int ext4_fc_replay_inode(struct super_block *sb, struct ext4_fc_tl *tl)
static int ext4_fc_replay_inode(struct super_block *sb, struct ext4_fc_tl *tl,
				u8 *val)
{
	struct ext4_fc_inode *fc_inode;
	struct ext4_fc_inode fc_inode;
	struct ext4_inode *raw_inode;
	struct ext4_inode *raw_fc_inode;
	struct inode *inode = NULL;
@@ -1460,9 +1463,9 @@ static int ext4_fc_replay_inode(struct super_block *sb, struct ext4_fc_tl *tl)
	int inode_len, ino, ret, tag = le16_to_cpu(tl->fc_tag);
	struct ext4_extent_header *eh;

	fc_inode = (struct ext4_fc_inode *)ext4_fc_tag_val(tl);
	memcpy(&fc_inode, val, sizeof(fc_inode));

	ino = le32_to_cpu(fc_inode->fc_ino);
	ino = le32_to_cpu(fc_inode.fc_ino);
	trace_ext4_fc_replay(sb, tag, ino, 0, 0);

	inode = ext4_iget(sb, ino, EXT4_IGET_NORMAL);
@@ -1474,12 +1477,13 @@ static int ext4_fc_replay_inode(struct super_block *sb, struct ext4_fc_tl *tl)

	ext4_fc_record_modified_inode(sb, ino);

	raw_fc_inode = (struct ext4_inode *)fc_inode->fc_raw_inode;
	raw_fc_inode = (struct ext4_inode *)
		(val + offsetof(struct ext4_fc_inode, fc_raw_inode));
	ret = ext4_get_fc_inode_loc(sb, ino, &iloc);
	if (ret)
		goto out;

	inode_len = ext4_fc_tag_len(tl) - sizeof(struct ext4_fc_inode);
	inode_len = le16_to_cpu(tl->fc_len) - sizeof(struct ext4_fc_inode);
	raw_inode = ext4_raw_inode(&iloc);

	memcpy(raw_inode, raw_fc_inode, offsetof(struct ext4_inode, i_block));
@@ -1547,14 +1551,15 @@ static int ext4_fc_replay_inode(struct super_block *sb, struct ext4_fc_tl *tl)
 * inode for which we are trying to create a dentry here, should already have
 * been replayed before we start here.
 */
static int ext4_fc_replay_create(struct super_block *sb, struct ext4_fc_tl *tl)
static int ext4_fc_replay_create(struct super_block *sb, struct ext4_fc_tl *tl,
				 u8 *val)
{
	int ret = 0;
	struct inode *inode = NULL;
	struct inode *dir = NULL;
	struct dentry_info_args darg;

	tl_to_darg(&darg, tl);
	tl_to_darg(&darg, tl, val);

	trace_ext4_fc_replay(sb, EXT4_FC_TAG_CREAT, darg.ino,
			darg.parent_ino, darg.dname_len);
@@ -1633,9 +1638,9 @@ static int ext4_fc_record_regions(struct super_block *sb, int ino,

/* Replay add range tag */
static int ext4_fc_replay_add_range(struct super_block *sb,
				struct ext4_fc_tl *tl)
				    struct ext4_fc_tl *tl, u8 *val)
{
	struct ext4_fc_add_range *fc_add_ex;
	struct ext4_fc_add_range fc_add_ex;
	struct ext4_extent newex, *ex;
	struct inode *inode;
	ext4_lblk_t start, cur;
@@ -1645,15 +1650,14 @@ static int ext4_fc_replay_add_range(struct super_block *sb,
	struct ext4_ext_path *path = NULL;
	int ret;

	fc_add_ex = (struct ext4_fc_add_range *)ext4_fc_tag_val(tl);
	ex = (struct ext4_extent *)&fc_add_ex->fc_ex;
	memcpy(&fc_add_ex, val, sizeof(fc_add_ex));
	ex = (struct ext4_extent *)&fc_add_ex.fc_ex;

	trace_ext4_fc_replay(sb, EXT4_FC_TAG_ADD_RANGE,
		le32_to_cpu(fc_add_ex->fc_ino), le32_to_cpu(ex->ee_block),
		le32_to_cpu(fc_add_ex.fc_ino), le32_to_cpu(ex->ee_block),
		ext4_ext_get_actual_len(ex));

	inode = ext4_iget(sb, le32_to_cpu(fc_add_ex->fc_ino),
				EXT4_IGET_NORMAL);
	inode = ext4_iget(sb, le32_to_cpu(fc_add_ex.fc_ino), EXT4_IGET_NORMAL);
	if (IS_ERR(inode)) {
		jbd_debug(1, "Inode not found.");
		return 0;
@@ -1762,32 +1766,33 @@ static int ext4_fc_replay_add_range(struct super_block *sb,

/* Replay DEL_RANGE tag */
static int
ext4_fc_replay_del_range(struct super_block *sb, struct ext4_fc_tl *tl)
ext4_fc_replay_del_range(struct super_block *sb, struct ext4_fc_tl *tl,
			 u8 *val)
{
	struct inode *inode;
	struct ext4_fc_del_range *lrange;
	struct ext4_fc_del_range lrange;
	struct ext4_map_blocks map;
	ext4_lblk_t cur, remaining;
	int ret;

	lrange = (struct ext4_fc_del_range *)ext4_fc_tag_val(tl);
	cur = le32_to_cpu(lrange->fc_lblk);
	remaining = le32_to_cpu(lrange->fc_len);
	memcpy(&lrange, val, sizeof(lrange));
	cur = le32_to_cpu(lrange.fc_lblk);
	remaining = le32_to_cpu(lrange.fc_len);

	trace_ext4_fc_replay(sb, EXT4_FC_TAG_DEL_RANGE,
		le32_to_cpu(lrange->fc_ino), cur, remaining);
		le32_to_cpu(lrange.fc_ino), cur, remaining);

	inode = ext4_iget(sb, le32_to_cpu(lrange->fc_ino), EXT4_IGET_NORMAL);
	inode = ext4_iget(sb, le32_to_cpu(lrange.fc_ino), EXT4_IGET_NORMAL);
	if (IS_ERR(inode)) {
		jbd_debug(1, "Inode %d not found", le32_to_cpu(lrange->fc_ino));
		jbd_debug(1, "Inode %d not found", le32_to_cpu(lrange.fc_ino));
		return 0;
	}

	ret = ext4_fc_record_modified_inode(sb, inode->i_ino);

	jbd_debug(1, "DEL_RANGE, inode %ld, lblk %d, len %d\n",
			inode->i_ino, le32_to_cpu(lrange->fc_lblk),
			le32_to_cpu(lrange->fc_len));
			inode->i_ino, le32_to_cpu(lrange.fc_lblk),
			le32_to_cpu(lrange.fc_len));
	while (remaining > 0) {
		map.m_lblk = cur;
		map.m_len = remaining;
@@ -1808,8 +1813,8 @@ ext4_fc_replay_del_range(struct super_block *sb, struct ext4_fc_tl *tl)
	}

	ret = ext4_punch_hole(inode,
		le32_to_cpu(lrange->fc_lblk) << sb->s_blocksize_bits,
		le32_to_cpu(lrange->fc_len) <<  sb->s_blocksize_bits);
		le32_to_cpu(lrange.fc_lblk) << sb->s_blocksize_bits,
		le32_to_cpu(lrange.fc_len) <<  sb->s_blocksize_bits);
	if (ret)
		jbd_debug(1, "ext4_punch_hole returned %d", ret);
	ext4_ext_replay_shrink_inode(inode,
@@ -1925,11 +1930,11 @@ static int ext4_fc_replay_scan(journal_t *journal,
	struct ext4_sb_info *sbi = EXT4_SB(sb);
	struct ext4_fc_replay_state *state;
	int ret = JBD2_FC_REPLAY_CONTINUE;
	struct ext4_fc_add_range *ext;
	struct ext4_fc_tl *tl;
	struct ext4_fc_tail *tail;
	__u8 *start, *end;
	struct ext4_fc_head *head;
	struct ext4_fc_add_range ext;
	struct ext4_fc_tl tl;
	struct ext4_fc_tail tail;
	__u8 *start, *end, *cur, *val;
	struct ext4_fc_head head;
	struct ext4_extent *ex;

	state = &sbi->s_fc_replay_state;
@@ -1956,15 +1961,17 @@ static int ext4_fc_replay_scan(journal_t *journal,
	}

	state->fc_replay_expected_off++;
	fc_for_each_tl(start, end, tl) {
	for (cur = start; cur < end; cur = cur + sizeof(tl) + le16_to_cpu(tl.fc_len)) {
		memcpy(&tl, cur, sizeof(tl));
		val = cur + sizeof(tl);
		jbd_debug(3, "Scan phase, tag:%s, blk %lld\n",
			  tag2str(le16_to_cpu(tl->fc_tag)), bh->b_blocknr);
		switch (le16_to_cpu(tl->fc_tag)) {
			  tag2str(le16_to_cpu(tl.fc_tag)), bh->b_blocknr);
		switch (le16_to_cpu(tl.fc_tag)) {
		case EXT4_FC_TAG_ADD_RANGE:
			ext = (struct ext4_fc_add_range *)ext4_fc_tag_val(tl);
			ex = (struct ext4_extent *)&ext->fc_ex;
			memcpy(&ext, val, sizeof(ext));
			ex = (struct ext4_extent *)&ext.fc_ex;
			ret = ext4_fc_record_regions(sb,
				le32_to_cpu(ext->fc_ino),
				le32_to_cpu(ext.fc_ino),
				le32_to_cpu(ex->ee_block), ext4_ext_pblock(ex),
				ext4_ext_get_actual_len(ex));
			if (ret < 0)
@@ -1978,18 +1985,18 @@ static int ext4_fc_replay_scan(journal_t *journal,
		case EXT4_FC_TAG_INODE:
		case EXT4_FC_TAG_PAD:
			state->fc_cur_tag++;
			state->fc_crc = ext4_chksum(sbi, state->fc_crc, tl,
					sizeof(*tl) + ext4_fc_tag_len(tl));
			state->fc_crc = ext4_chksum(sbi, state->fc_crc, cur,
					sizeof(tl) + le16_to_cpu(tl.fc_len));
			break;
		case EXT4_FC_TAG_TAIL:
			state->fc_cur_tag++;
			tail = (struct ext4_fc_tail *)ext4_fc_tag_val(tl);
			state->fc_crc = ext4_chksum(sbi, state->fc_crc, tl,
						sizeof(*tl) +
			memcpy(&tail, val, sizeof(tail));
			state->fc_crc = ext4_chksum(sbi, state->fc_crc, cur,
						sizeof(tl) +
						offsetof(struct ext4_fc_tail,
						fc_crc));
			if (le32_to_cpu(tail->fc_tid) == expected_tid &&
				le32_to_cpu(tail->fc_crc) == state->fc_crc) {
			if (le32_to_cpu(tail.fc_tid) == expected_tid &&
				le32_to_cpu(tail.fc_crc) == state->fc_crc) {
				state->fc_replay_num_tags = state->fc_cur_tag;
				state->fc_regions_valid =
					state->fc_regions_used;
@@ -2000,19 +2007,19 @@ static int ext4_fc_replay_scan(journal_t *journal,
			state->fc_crc = 0;
			break;
		case EXT4_FC_TAG_HEAD:
			head = (struct ext4_fc_head *)ext4_fc_tag_val(tl);
			if (le32_to_cpu(head->fc_features) &
			memcpy(&head, val, sizeof(head));
			if (le32_to_cpu(head.fc_features) &
				~EXT4_FC_SUPPORTED_FEATURES) {
				ret = -EOPNOTSUPP;
				break;
			}
			if (le32_to_cpu(head->fc_tid) != expected_tid) {
			if (le32_to_cpu(head.fc_tid) != expected_tid) {
				ret = JBD2_FC_REPLAY_STOP;
				break;
			}
			state->fc_cur_tag++;
			state->fc_crc = ext4_chksum(sbi, state->fc_crc, tl,
					sizeof(*tl) + ext4_fc_tag_len(tl));
			state->fc_crc = ext4_chksum(sbi, state->fc_crc, cur,
					    sizeof(tl) + le16_to_cpu(tl.fc_len));
			break;
		default:
			ret = state->fc_replay_num_tags ?
@@ -2036,11 +2043,11 @@ static int ext4_fc_replay(journal_t *journal, struct buffer_head *bh,
{
	struct super_block *sb = journal->j_private;
	struct ext4_sb_info *sbi = EXT4_SB(sb);
	struct ext4_fc_tl *tl;
	__u8 *start, *end;
	struct ext4_fc_tl tl;
	__u8 *start, *end, *cur, *val;
	int ret = JBD2_FC_REPLAY_CONTINUE;
	struct ext4_fc_replay_state *state = &sbi->s_fc_replay_state;
	struct ext4_fc_tail *tail;
	struct ext4_fc_tail tail;

	if (pass == PASS_SCAN) {
		state->fc_current_pass = PASS_SCAN;
@@ -2067,49 +2074,52 @@ static int ext4_fc_replay(journal_t *journal, struct buffer_head *bh,
	start = (u8 *)bh->b_data;
	end = (__u8 *)bh->b_data + journal->j_blocksize - 1;

	fc_for_each_tl(start, end, tl) {
	for (cur = start; cur < end; cur = cur + sizeof(tl) + le16_to_cpu(tl.fc_len)) {
		memcpy(&tl, cur, sizeof(tl));
		val = cur + sizeof(tl);

		if (state->fc_replay_num_tags == 0) {
			ret = JBD2_FC_REPLAY_STOP;
			ext4_fc_set_bitmaps_and_counters(sb);
			break;
		}
		jbd_debug(3, "Replay phase, tag:%s\n",
				tag2str(le16_to_cpu(tl->fc_tag)));
				tag2str(le16_to_cpu(tl.fc_tag)));
		state->fc_replay_num_tags--;
		switch (le16_to_cpu(tl->fc_tag)) {
		switch (le16_to_cpu(tl.fc_tag)) {
		case EXT4_FC_TAG_LINK:
			ret = ext4_fc_replay_link(sb, tl);
			ret = ext4_fc_replay_link(sb, &tl, val);
			break;
		case EXT4_FC_TAG_UNLINK:
			ret = ext4_fc_replay_unlink(sb, tl);
			ret = ext4_fc_replay_unlink(sb, &tl, val);
			break;
		case EXT4_FC_TAG_ADD_RANGE:
			ret = ext4_fc_replay_add_range(sb, tl);
			ret = ext4_fc_replay_add_range(sb, &tl, val);
			break;
		case EXT4_FC_TAG_CREAT:
			ret = ext4_fc_replay_create(sb, tl);
			ret = ext4_fc_replay_create(sb, &tl, val);
			break;
		case EXT4_FC_TAG_DEL_RANGE:
			ret = ext4_fc_replay_del_range(sb, tl);
			ret = ext4_fc_replay_del_range(sb, &tl, val);
			break;
		case EXT4_FC_TAG_INODE:
			ret = ext4_fc_replay_inode(sb, tl);
			ret = ext4_fc_replay_inode(sb, &tl, val);
			break;
		case EXT4_FC_TAG_PAD:
			trace_ext4_fc_replay(sb, EXT4_FC_TAG_PAD, 0,
				ext4_fc_tag_len(tl), 0);
					     le16_to_cpu(tl.fc_len), 0);
			break;
		case EXT4_FC_TAG_TAIL:
			trace_ext4_fc_replay(sb, EXT4_FC_TAG_TAIL, 0,
				ext4_fc_tag_len(tl), 0);
			tail = (struct ext4_fc_tail *)ext4_fc_tag_val(tl);
			WARN_ON(le32_to_cpu(tail->fc_tid) != expected_tid);
					     le16_to_cpu(tl.fc_len), 0);
			memcpy(&tail, val, sizeof(tail));
			WARN_ON(le32_to_cpu(tail.fc_tid) != expected_tid);
			break;
		case EXT4_FC_TAG_HEAD:
			break;
		default:
			trace_ext4_fc_replay(sb, le16_to_cpu(tl->fc_tag), 0,
				ext4_fc_tag_len(tl), 0);
			trace_ext4_fc_replay(sb, le16_to_cpu(tl.fc_tag), 0,
					     le16_to_cpu(tl.fc_len), 0);
			ret = -ECANCELED;
			break;
		}
+0 −19
Original line number Diff line number Diff line
@@ -153,13 +153,6 @@ struct ext4_fc_replay_state {
#define region_last(__region) (((__region)->lblk) + ((__region)->len) - 1)
#endif

#define fc_for_each_tl(__start, __end, __tl)				\
	for (tl = (struct ext4_fc_tl *)(__start);			\
	     (__u8 *)tl < (__u8 *)(__end);				\
		tl = (struct ext4_fc_tl *)((__u8 *)tl +			\
					sizeof(struct ext4_fc_tl) +	\
					+ le16_to_cpu(tl->fc_len)))

static inline const char *tag2str(__u16 tag)
{
	switch (tag) {
@@ -186,16 +179,4 @@ static inline const char *tag2str(__u16 tag)
	}
}

/* Get length of a particular tlv */
static inline int ext4_fc_tag_len(struct ext4_fc_tl *tl)
{
	return le16_to_cpu(tl->fc_len);
}

/* Get a pointer to "value" of a tlv */
static inline __u8 *ext4_fc_tag_val(struct ext4_fc_tl *tl)
{
	return (__u8 *)tl + sizeof(*tl);
}

#endif /* __FAST_COMMIT_H__ */
+4 −2
Original line number Diff line number Diff line
@@ -322,6 +322,7 @@ void ext4_free_inode(handle_t *handle, struct inode *inode)
	if (is_directory) {
		count = ext4_used_dirs_count(sb, gdp) - 1;
		ext4_used_dirs_set(sb, gdp, count);
		if (percpu_counter_initialized(&sbi->s_dirs_counter))
			percpu_counter_dec(&sbi->s_dirs_counter);
	}
	ext4_inode_bitmap_csum_set(sb, block_group, gdp, bitmap_bh,
@@ -329,6 +330,7 @@ void ext4_free_inode(handle_t *handle, struct inode *inode)
	ext4_group_desc_csum_set(sb, block_group, gdp);
	ext4_unlock_group(sb, block_group);

	if (percpu_counter_initialized(&sbi->s_freeinodes_counter))
		percpu_counter_inc(&sbi->s_freeinodes_counter);
	if (sbi->s_log_groups_per_flex) {
		struct flex_groups *fg;
+1 −1
Original line number Diff line number Diff line
@@ -3217,7 +3217,7 @@ static int ext4_mb_init_backend(struct super_block *sb)
		 */
		if (sbi->s_es->s_log_groups_per_flex >= 32) {
			ext4_msg(sb, KERN_ERR, "too many log groups per flexible block group");
			goto err_freesgi;
			goto err_freebuddy;
		}
		sbi->s_mb_prefetch = min_t(uint, 1 << sbi->s_es->s_log_groups_per_flex,
			BLK_MAX_SEGMENT_SIZE >> (sb->s_blocksize_bits - 9));
Loading