| From foo@baz Mon Sep 18 10:16:36 CEST 2017 |
| From: Christoph Hellwig <hch@lst.de> |
| Date: Sun, 17 Sep 2017 14:06:54 -0700 |
| Subject: xfs: handle -EFSCORRUPTED during head/tail verification |
| To: stable@vger.kernel.org |
| Cc: linux-xfs@vger.kernel.org, Brian Foster <bfoster@redhat.com>, "Darrick J . Wong" <darrick.wong@oracle.com> |
| Message-ID: <20170917210712.10804-30-hch@lst.de> |
| |
| From: Brian Foster <bfoster@redhat.com> |
| |
| commit a4c9b34d6a17081005ec459b57b8effc08f4c731 upstream. |
| |
| Torn write and tail overwrite detection both trigger only on |
| -EFSBADCRC errors. While this is the most likely failure scenario |
| for each condition, -EFSCORRUPTED is still possible in certain cases |
| depending on what ends up on disk when a torn write or partial tail |
| overwrite occurs. For example, an invalid log record h_len can lead |
| to an -EFSCORRUPTED error when running the log recovery CRC pass. |
| |
| Therefore, update log head and tail verification to trigger the |
| associated head/tail fixups in the event of -EFSCORRUPTED errors |
| along with -EFSBADCRC. Also, -EFSCORRUPTED can currently be returned |
| from xlog_do_recovery_pass() before rhead_blk is initialized if the |
| first record encountered happens to be corrupted. This leads to an |
| incorrect 'first_bad' return value. Initialize rhead_blk earlier in |
| the function to address that problem as well. |
| |
| Signed-off-by: Brian Foster <bfoster@redhat.com> |
| Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com> |
| Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| fs/xfs/xfs_log_recover.c | 7 +++---- |
| 1 file changed, 3 insertions(+), 4 deletions(-) |
| |
| --- a/fs/xfs/xfs_log_recover.c |
| +++ b/fs/xfs/xfs_log_recover.c |
| @@ -1102,7 +1102,7 @@ xlog_verify_tail( |
| first_bad = 0; |
| error = xlog_do_recovery_pass(log, head_blk, *tail_blk, |
| XLOG_RECOVER_CRCPASS, &first_bad); |
| - while (error == -EFSBADCRC && first_bad) { |
| + while ((error == -EFSBADCRC || error == -EFSCORRUPTED) && first_bad) { |
| int tail_distance; |
| |
| /* |
| @@ -1188,7 +1188,7 @@ xlog_verify_head( |
| */ |
| error = xlog_do_recovery_pass(log, *head_blk, tmp_rhead_blk, |
| XLOG_RECOVER_CRCPASS, &first_bad); |
| - if (error == -EFSBADCRC) { |
| + if ((error == -EFSBADCRC || error == -EFSCORRUPTED) && first_bad) { |
| /* |
| * We've hit a potential torn write. Reset the error and warn |
| * about it. |
| @@ -5255,7 +5255,7 @@ xlog_do_recovery_pass( |
| LIST_HEAD (buffer_list); |
| |
| ASSERT(head_blk != tail_blk); |
| - rhead_blk = 0; |
| + blk_no = rhead_blk = tail_blk; |
| |
| for (i = 0; i < XLOG_RHASH_SIZE; i++) |
| INIT_HLIST_HEAD(&rhash[i]); |
| @@ -5333,7 +5333,6 @@ xlog_do_recovery_pass( |
| } |
| |
| memset(rhash, 0, sizeof(rhash)); |
| - blk_no = rhead_blk = tail_blk; |
| if (tail_blk > head_blk) { |
| /* |
| * Perform recovery around the end of the physical log. |