diff options
Diffstat (limited to 'fs/xfs/xfs_log_recover.c')
| -rw-r--r-- | fs/xfs/xfs_log_recover.c | 148 | 
1 files changed, 77 insertions, 71 deletions
diff --git a/fs/xfs/xfs_log_recover.c b/fs/xfs/xfs_log_recover.c index c1a514ffff55..99ec3fba4548 100644 --- a/fs/xfs/xfs_log_recover.c +++ b/fs/xfs/xfs_log_recover.c @@ -103,10 +103,9 @@ xlog_alloc_buffer(  	 * Pass log block 0 since we don't have an addr yet, buffer will be  	 * verified on read.  	 */ -	if (!xlog_verify_bno(log, 0, nbblks)) { +	if (XFS_IS_CORRUPT(log->l_mp, !xlog_verify_bno(log, 0, nbblks))) {  		xfs_warn(log->l_mp, "Invalid block length (0x%x) for buffer",  			nbblks); -		XFS_ERROR_REPORT(__func__, XFS_ERRLEVEL_HIGH, log->l_mp);  		return NULL;  	} @@ -152,11 +151,10 @@ xlog_do_io(  {  	int			error; -	if (!xlog_verify_bno(log, blk_no, nbblks)) { +	if (XFS_IS_CORRUPT(log->l_mp, !xlog_verify_bno(log, blk_no, nbblks))) {  		xfs_warn(log->l_mp,  			 "Invalid log block/length (0x%llx, 0x%x) for buffer",  			 blk_no, nbblks); -		XFS_ERROR_REPORT(__func__, XFS_ERRLEVEL_HIGH, log->l_mp);  		return -EFSCORRUPTED;  	} @@ -244,19 +242,17 @@ xlog_header_check_recover(  	 * (XLOG_FMT_UNKNOWN). This stops us from trying to recover  	 * a dirty log created in IRIX.  	 */ -	if (unlikely(head->h_fmt != cpu_to_be32(XLOG_FMT))) { +	if (XFS_IS_CORRUPT(mp, head->h_fmt != cpu_to_be32(XLOG_FMT))) {  		xfs_warn(mp,  	"dirty log written in incompatible format - can't recover");  		xlog_header_check_dump(mp, head); -		XFS_ERROR_REPORT("xlog_header_check_recover(1)", -				 XFS_ERRLEVEL_HIGH, mp);  		return -EFSCORRUPTED; -	} else if (unlikely(!uuid_equal(&mp->m_sb.sb_uuid, &head->h_fs_uuid))) { +	} +	if (XFS_IS_CORRUPT(mp, !uuid_equal(&mp->m_sb.sb_uuid, +					   &head->h_fs_uuid))) {  		xfs_warn(mp,  	"dirty log entry has mismatched uuid - can't recover");  		xlog_header_check_dump(mp, head); -		XFS_ERROR_REPORT("xlog_header_check_recover(2)", -				 XFS_ERRLEVEL_HIGH, mp);  		return -EFSCORRUPTED;  	}  	return 0; @@ -279,11 +275,10 @@ xlog_header_check_mount(  		 * by IRIX and continue.  		 */  		xfs_warn(mp, "null uuid in log - IRIX style log"); -	} else if (unlikely(!uuid_equal(&mp->m_sb.sb_uuid, &head->h_fs_uuid))) { +	} else if (XFS_IS_CORRUPT(mp, !uuid_equal(&mp->m_sb.sb_uuid, +						  &head->h_fs_uuid))) {  		xfs_warn(mp, "log has mismatched uuid - can't recover");  		xlog_header_check_dump(mp, head); -		XFS_ERROR_REPORT("xlog_header_check_mount", -				 XFS_ERRLEVEL_HIGH, mp);  		return -EFSCORRUPTED;  	}  	return 0; @@ -471,7 +466,7 @@ xlog_find_verify_log_record(  			xfs_warn(log->l_mp,  		"Log inconsistent (didn't find previous header)");  			ASSERT(0); -			error = -EIO; +			error = -EFSCORRUPTED;  			goto out;  		} @@ -1347,10 +1342,11 @@ xlog_find_tail(  	error = xlog_rseek_logrec_hdr(log, *head_blk, *head_blk, 1, buffer,  				      &rhead_blk, &rhead, &wrapped);  	if (error < 0) -		return error; +		goto done;  	if (!error) {  		xfs_warn(log->l_mp, "%s: couldn't find sync record", __func__); -		return -EIO; +		error = -EFSCORRUPTED; +		goto done;  	}  	*tail_blk = BLOCK_LSN(be64_to_cpu(rhead->h_tail_lsn)); @@ -1699,11 +1695,10 @@ xlog_clear_stale_blocks(  		 * the distance from the beginning of the log to the  		 * tail.  		 */ -		if (unlikely(head_block < tail_block || head_block >= log->l_logBBsize)) { -			XFS_ERROR_REPORT("xlog_clear_stale_blocks(1)", -					 XFS_ERRLEVEL_LOW, log->l_mp); +		if (XFS_IS_CORRUPT(log->l_mp, +				   head_block < tail_block || +				   head_block >= log->l_logBBsize))  			return -EFSCORRUPTED; -		}  		tail_distance = tail_block + (log->l_logBBsize - head_block);  	} else {  		/* @@ -1711,11 +1706,10 @@ xlog_clear_stale_blocks(  		 * so the distance from the head to the tail is just  		 * the tail block minus the head block.  		 */ -		if (unlikely(head_block >= tail_block || head_cycle != (tail_cycle + 1))){ -			XFS_ERROR_REPORT("xlog_clear_stale_blocks(2)", -					 XFS_ERRLEVEL_LOW, log->l_mp); +		if (XFS_IS_CORRUPT(log->l_mp, +				   head_block >= tail_block || +				   head_cycle != tail_cycle + 1))  			return -EFSCORRUPTED; -		}  		tail_distance = tail_block - head_block;  	} @@ -2135,13 +2129,11 @@ xlog_recover_do_inode_buffer(  		 */  		logged_nextp = item->ri_buf[item_index].i_addr +  				next_unlinked_offset - reg_buf_offset; -		if (unlikely(*logged_nextp == 0)) { +		if (XFS_IS_CORRUPT(mp, *logged_nextp == 0)) {  			xfs_alert(mp,  		"Bad inode buffer log record (ptr = "PTR_FMT", bp = "PTR_FMT"). "  		"Trying to replay bad (0) inode di_next_unlinked field.",  				item, bp); -			XFS_ERROR_REPORT("xlog_recover_do_inode_buf", -					 XFS_ERRLEVEL_LOW, mp);  			return -EFSCORRUPTED;  		} @@ -2576,6 +2568,7 @@ xlog_recover_do_reg_buffer(  	int			bit;  	int			nbits;  	xfs_failaddr_t		fa; +	const size_t		size_disk_dquot = sizeof(struct xfs_disk_dquot);  	trace_xfs_log_recover_buf_reg_buf(mp->m_log, buf_f); @@ -2618,7 +2611,7 @@ xlog_recover_do_reg_buffer(  					"XFS: NULL dquot in %s.", __func__);  				goto next;  			} -			if (item->ri_buf[i].i_len < sizeof(xfs_disk_dquot_t)) { +			if (item->ri_buf[i].i_len < size_disk_dquot) {  				xfs_alert(mp,  					"XFS: dquot too small (%d) in %s.",  					item->ri_buf[i].i_len, __func__); @@ -2969,22 +2962,18 @@ xlog_recover_inode_pass2(  	 * Make sure the place we're flushing out to really looks  	 * like an inode!  	 */ -	if (unlikely(!xfs_verify_magic16(bp, dip->di_magic))) { +	if (XFS_IS_CORRUPT(mp, !xfs_verify_magic16(bp, dip->di_magic))) {  		xfs_alert(mp,  	"%s: Bad inode magic number, dip = "PTR_FMT", dino bp = "PTR_FMT", ino = %Ld",  			__func__, dip, bp, in_f->ilf_ino); -		XFS_ERROR_REPORT("xlog_recover_inode_pass2(1)", -				 XFS_ERRLEVEL_LOW, mp);  		error = -EFSCORRUPTED;  		goto out_release;  	}  	ldip = item->ri_buf[1].i_addr; -	if (unlikely(ldip->di_magic != XFS_DINODE_MAGIC)) { +	if (XFS_IS_CORRUPT(mp, ldip->di_magic != XFS_DINODE_MAGIC)) {  		xfs_alert(mp,  			"%s: Bad inode log record, rec ptr "PTR_FMT", ino %Ld",  			__func__, item, in_f->ilf_ino); -		XFS_ERROR_REPORT("xlog_recover_inode_pass2(2)", -				 XFS_ERRLEVEL_LOW, mp);  		error = -EFSCORRUPTED;  		goto out_release;  	} @@ -3166,7 +3155,7 @@ xlog_recover_inode_pass2(  		default:  			xfs_warn(log->l_mp, "%s: Invalid flag", __func__);  			ASSERT(0); -			error = -EIO; +			error = -EFSCORRUPTED;  			goto out_release;  		}  	} @@ -3247,12 +3236,12 @@ xlog_recover_dquot_pass2(  	recddq = item->ri_buf[1].i_addr;  	if (recddq == NULL) {  		xfs_alert(log->l_mp, "NULL dquot in %s.", __func__); -		return -EIO; +		return -EFSCORRUPTED;  	} -	if (item->ri_buf[1].i_len < sizeof(xfs_disk_dquot_t)) { +	if (item->ri_buf[1].i_len < sizeof(struct xfs_disk_dquot)) {  		xfs_alert(log->l_mp, "dquot too small (%d) in %s.",  			item->ri_buf[1].i_len, __func__); -		return -EIO; +		return -EFSCORRUPTED;  	}  	/* @@ -3279,7 +3268,7 @@ xlog_recover_dquot_pass2(  	if (fa) {  		xfs_alert(mp, "corrupt dquot ID 0x%x in log at %pS",  				dq_f->qlf_id, fa); -		return -EIO; +		return -EFSCORRUPTED;  	}  	ASSERT(dq_f->qlf_len == 1); @@ -3537,6 +3526,7 @@ xfs_cui_copy_format(  		memcpy(dst_cui_fmt, src_cui_fmt, len);  		return 0;  	} +	XFS_ERROR_REPORT(__func__, XFS_ERRLEVEL_LOW, NULL);  	return -EFSCORRUPTED;  } @@ -3601,8 +3591,10 @@ xlog_recover_cud_pass2(  	struct xfs_ail			*ailp = log->l_ailp;  	cud_formatp = item->ri_buf[0].i_addr; -	if (item->ri_buf[0].i_len != sizeof(struct xfs_cud_log_format)) +	if (item->ri_buf[0].i_len != sizeof(struct xfs_cud_log_format)) { +		XFS_ERROR_REPORT(__func__, XFS_ERRLEVEL_LOW, log->l_mp);  		return -EFSCORRUPTED; +	}  	cui_id = cud_formatp->cud_cui_id;  	/* @@ -3654,6 +3646,7 @@ xfs_bui_copy_format(  		memcpy(dst_bui_fmt, src_bui_fmt, len);  		return 0;  	} +	XFS_ERROR_REPORT(__func__, XFS_ERRLEVEL_LOW, NULL);  	return -EFSCORRUPTED;  } @@ -3677,8 +3670,10 @@ xlog_recover_bui_pass2(  	bui_formatp = item->ri_buf[0].i_addr; -	if (bui_formatp->bui_nextents != XFS_BUI_MAX_FAST_EXTENTS) +	if (bui_formatp->bui_nextents != XFS_BUI_MAX_FAST_EXTENTS) { +		XFS_ERROR_REPORT(__func__, XFS_ERRLEVEL_LOW, log->l_mp);  		return -EFSCORRUPTED; +	}  	buip = xfs_bui_init(mp);  	error = xfs_bui_copy_format(&item->ri_buf[0], &buip->bui_format);  	if (error) { @@ -3720,8 +3715,10 @@ xlog_recover_bud_pass2(  	struct xfs_ail			*ailp = log->l_ailp;  	bud_formatp = item->ri_buf[0].i_addr; -	if (item->ri_buf[0].i_len != sizeof(struct xfs_bud_log_format)) +	if (item->ri_buf[0].i_len != sizeof(struct xfs_bud_log_format)) { +		XFS_ERROR_REPORT(__func__, XFS_ERRLEVEL_LOW, log->l_mp);  		return -EFSCORRUPTED; +	}  	bui_id = bud_formatp->bud_bui_id;  	/* @@ -4018,7 +4015,7 @@ xlog_recover_commit_pass1(  		xfs_warn(log->l_mp, "%s: invalid item type (%d)",  			__func__, ITEM_TYPE(item));  		ASSERT(0); -		return -EIO; +		return -EFSCORRUPTED;  	}  } @@ -4066,7 +4063,7 @@ xlog_recover_commit_pass2(  		xfs_warn(log->l_mp, "%s: invalid item type (%d)",  			__func__, ITEM_TYPE(item));  		ASSERT(0); -		return -EIO; +		return -EFSCORRUPTED;  	}  } @@ -4187,7 +4184,7 @@ xlog_recover_add_to_cont_trans(  		ASSERT(len <= sizeof(struct xfs_trans_header));  		if (len > sizeof(struct xfs_trans_header)) {  			xfs_warn(log->l_mp, "%s: bad header length", __func__); -			return -EIO; +			return -EFSCORRUPTED;  		}  		xlog_recover_add_item(&trans->r_itemq); @@ -4243,13 +4240,13 @@ xlog_recover_add_to_trans(  			xfs_warn(log->l_mp, "%s: bad header magic number",  				__func__);  			ASSERT(0); -			return -EIO; +			return -EFSCORRUPTED;  		}  		if (len > sizeof(struct xfs_trans_header)) {  			xfs_warn(log->l_mp, "%s: bad header length", __func__);  			ASSERT(0); -			return -EIO; +			return -EFSCORRUPTED;  		}  		/* @@ -4285,7 +4282,7 @@ xlog_recover_add_to_trans(  				  in_f->ilf_size);  			ASSERT(0);  			kmem_free(ptr); -			return -EIO; +			return -EFSCORRUPTED;  		}  		item->ri_total = in_f->ilf_size; @@ -4293,7 +4290,16 @@ xlog_recover_add_to_trans(  			kmem_zalloc(item->ri_total * sizeof(xfs_log_iovec_t),  				    0);  	} -	ASSERT(item->ri_total > item->ri_cnt); + +	if (item->ri_total <= item->ri_cnt) { +		xfs_warn(log->l_mp, +	"log item region count (%d) overflowed size (%d)", +				item->ri_cnt, item->ri_total); +		ASSERT(0); +		kmem_free(ptr); +		return -EFSCORRUPTED; +	} +  	/* Description region is ri_buf[0] */  	item->ri_buf[item->ri_cnt].i_addr = ptr;  	item->ri_buf[item->ri_cnt].i_len  = len; @@ -4380,7 +4386,7 @@ xlog_recovery_process_trans(  	default:  		xfs_warn(log->l_mp, "%s: bad flag 0x%x", __func__, flags);  		ASSERT(0); -		error = -EIO; +		error = -EFSCORRUPTED;  		break;  	}  	if (error || freeit) @@ -4460,7 +4466,7 @@ xlog_recover_process_ophdr(  		xfs_warn(log->l_mp, "%s: bad clientid 0x%x",  			__func__, ohead->oh_clientid);  		ASSERT(0); -		return -EIO; +		return -EFSCORRUPTED;  	}  	/* @@ -4470,7 +4476,7 @@ xlog_recover_process_ophdr(  	if (dp + len > end) {  		xfs_warn(log->l_mp, "%s: bad length 0x%x", __func__, len);  		WARN_ON(1); -		return -EIO; +		return -EFSCORRUPTED;  	}  	trans = xlog_recover_ophdr_to_trans(rhash, rhead, ohead); @@ -5172,8 +5178,10 @@ xlog_recover_process(  		 * If the filesystem is CRC enabled, this mismatch becomes a  		 * fatal log corruption failure.  		 */ -		if (xfs_sb_version_hascrc(&log->l_mp->m_sb)) +		if (xfs_sb_version_hascrc(&log->l_mp->m_sb)) { +			XFS_ERROR_REPORT(__func__, XFS_ERRLEVEL_LOW, log->l_mp);  			return -EFSCORRUPTED; +		}  	}  	xlog_unpack_data(rhead, dp, log); @@ -5190,31 +5198,25 @@ xlog_valid_rec_header(  {  	int			hlen; -	if (unlikely(rhead->h_magicno != cpu_to_be32(XLOG_HEADER_MAGIC_NUM))) { -		XFS_ERROR_REPORT("xlog_valid_rec_header(1)", -				XFS_ERRLEVEL_LOW, log->l_mp); +	if (XFS_IS_CORRUPT(log->l_mp, +			   rhead->h_magicno != cpu_to_be32(XLOG_HEADER_MAGIC_NUM)))  		return -EFSCORRUPTED; -	} -	if (unlikely( -	    (!rhead->h_version || -	    (be32_to_cpu(rhead->h_version) & (~XLOG_VERSION_OKBITS))))) { +	if (XFS_IS_CORRUPT(log->l_mp, +			   (!rhead->h_version || +			   (be32_to_cpu(rhead->h_version) & +			    (~XLOG_VERSION_OKBITS))))) {  		xfs_warn(log->l_mp, "%s: unrecognised log version (%d).",  			__func__, be32_to_cpu(rhead->h_version)); -		return -EIO; +		return -EFSCORRUPTED;  	}  	/* LR body must have data or it wouldn't have been written */  	hlen = be32_to_cpu(rhead->h_len); -	if (unlikely( hlen <= 0 || hlen > INT_MAX )) { -		XFS_ERROR_REPORT("xlog_valid_rec_header(2)", -				XFS_ERRLEVEL_LOW, log->l_mp); +	if (XFS_IS_CORRUPT(log->l_mp, hlen <= 0 || hlen > INT_MAX))  		return -EFSCORRUPTED; -	} -	if (unlikely( blkno > log->l_logBBsize || blkno > INT_MAX )) { -		XFS_ERROR_REPORT("xlog_valid_rec_header(3)", -				XFS_ERRLEVEL_LOW, log->l_mp); +	if (XFS_IS_CORRUPT(log->l_mp, +			   blkno > log->l_logBBsize || blkno > INT_MAX))  		return -EFSCORRUPTED; -	}  	return 0;  } @@ -5296,8 +5298,12 @@ xlog_do_recovery_pass(  		"invalid iclog size (%d bytes), using lsunit (%d bytes)",  					 h_size, log->l_mp->m_logbsize);  				h_size = log->l_mp->m_logbsize; -			} else -				return -EFSCORRUPTED; +			} else { +				XFS_ERROR_REPORT(__func__, XFS_ERRLEVEL_LOW, +						log->l_mp); +				error = -EFSCORRUPTED; +				goto bread_err1; +			}  		}  		if ((be32_to_cpu(rhead->h_version) & XLOG_VERSION_2) &&  |