From 307ae950647d7db413c8e05dc17eb6aca9ef35d2 Mon Sep 17 00:00:00 2001 From: Roger Willcocks Date: Tue, 18 Aug 2015 17:53:18 +1000 Subject: [PATCH] libxfs: v3 inodes are only valid on crc-enabled filesystems xfs_repair was not detecting that version 3 inodes are invalid for for non-CRC filesystems. The result is specific inode corruptions go undetected and hence aren't repaired if only the version number is out of range. The core of the problem is that the XFS_DINODE_GOOD_VERSION() macro doesn't know that valid inode versions are dependent on a superblock version number. Fix this in libxfs, and propagate the new function out into the rest of xfsprogs to fix the issue. [dchinner: forward port from 3.2.4 to 4.2.0-rc1, move xfs_dinode_good_version() to libxfs/xfs_inode-buf.c with all the other dinode validation functions. ] Reported-by: Leslie Rhorer Signed-off-by: Roger Willcocks Reviewed-by: Dave Chinner Signed-off-by: Dave Chinner --- db/check.c | 2 +- libxfs/xfs_format.h | 1 - libxfs/xfs_inode_buf.c | 13 ++++++++++++- libxfs/xfs_inode_buf.h | 2 ++ repair/dinode.c | 5 ++--- repair/prefetch.c | 2 +- 6 files changed, 18 insertions(+), 7 deletions(-) diff --git a/db/check.c b/db/check.c index afeea32dc..d28199dda 100644 --- a/db/check.c +++ b/db/check.c @@ -2630,7 +2630,7 @@ process_inode( error++; return; } - if (!XFS_DINODE_GOOD_VERSION(idic.di_version)) { + if (!xfs_dinode_good_version(mp, idic.di_version)) { if (isfree || v) dbprintf(_("bad version number %#x for inode %lld\n"), idic.di_version, ino); diff --git a/libxfs/xfs_format.h b/libxfs/xfs_format.h index 282926da3..bb7cc043d 100644 --- a/libxfs/xfs_format.h +++ b/libxfs/xfs_format.h @@ -828,7 +828,6 @@ typedef struct xfs_timestamp { * padding field for v3 inodes. */ #define XFS_DINODE_MAGIC 0x494e /* 'IN' */ -#define XFS_DINODE_GOOD_VERSION(v) ((v) >= 1 && (v) <= 3) typedef struct xfs_dinode { __be16 di_magic; /* inode magic # = XFS_DINODE_MAGIC */ __be16 di_mode; /* mode and type of file */ diff --git a/libxfs/xfs_inode_buf.c b/libxfs/xfs_inode_buf.c index be9d1662a..747a87968 100644 --- a/libxfs/xfs_inode_buf.c +++ b/libxfs/xfs_inode_buf.c @@ -54,6 +54,17 @@ xfs_inobp_check( } #endif +bool +xfs_dinode_good_version( + struct xfs_mount *mp, + __u8 version) +{ + if (xfs_sb_version_hascrc(&mp->m_sb)) + return version == 3; + + return version == 1 || version == 2; +} + /* * If we are doing readahead on an inode buffer, we might be in log recovery * reading an inode allocation buffer that hasn't yet been replayed, and hence @@ -85,7 +96,7 @@ xfs_inode_buf_verify( dip = xfs_buf_offset(bp, (i << mp->m_sb.sb_inodelog)); di_ok = dip->di_magic == cpu_to_be16(XFS_DINODE_MAGIC) && - XFS_DINODE_GOOD_VERSION(dip->di_version); + xfs_dinode_good_version(mp, dip->di_version); if (unlikely(XFS_TEST_ERROR(!di_ok, mp, XFS_ERRTAG_ITOBP_INOTOBP, XFS_RANDOM_ITOBP_INOTOBP))) { diff --git a/libxfs/xfs_inode_buf.h b/libxfs/xfs_inode_buf.h index 56bc65e44..03f2a906c 100644 --- a/libxfs/xfs_inode_buf.h +++ b/libxfs/xfs_inode_buf.h @@ -43,6 +43,8 @@ void xfs_dinode_from_disk(struct xfs_icdinode *to, struct xfs_dinode *from); bool xfs_dinode_verify(struct xfs_mount *mp, xfs_ino_t ino, struct xfs_dinode *dip); +bool xfs_dinode_good_version(struct xfs_mount *mp, __u8 version); + #if defined(DEBUG) void xfs_inobp_check(struct xfs_mount *, struct xfs_buf *); #else diff --git a/repair/dinode.c b/repair/dinode.c index 7e07ca701..f78f90788 100644 --- a/repair/dinode.c +++ b/repair/dinode.c @@ -129,7 +129,7 @@ clear_dinode_core(struct xfs_mount *mp, xfs_dinode_t *dinoc, xfs_ino_t ino_num) dinoc->di_magic = cpu_to_be16(XFS_DINODE_MAGIC); } - if (!XFS_DINODE_GOOD_VERSION(dinoc->di_version)) { + if (!xfs_dinode_good_version(mp, dinoc->di_version)) { __dirty_no_modify_ret(dirty); if (xfs_sb_version_hascrc(&mp->m_sb)) dinoc->di_version = 3; @@ -2254,8 +2254,7 @@ process_dinode_int(xfs_mount_t *mp, } } - if (!XFS_DINODE_GOOD_VERSION(dino->di_version) || - (xfs_sb_version_hascrc(&mp->m_sb) && dino->di_version < 3) ) { + if (!xfs_dinode_good_version(mp, dino->di_version)) { retval = 1; if (!uncertain) do_warn(_("bad version number 0x%x on inode %" PRIu64 "%c"), diff --git a/repair/prefetch.c b/repair/prefetch.c index 8b261ae6b..1de3ec0a5 100644 --- a/repair/prefetch.c +++ b/repair/prefetch.c @@ -419,7 +419,7 @@ pf_read_inode_dirs( if (be16_to_cpu(dino->di_magic) != XFS_DINODE_MAGIC) continue; - if (!XFS_DINODE_GOOD_VERSION(dino->di_version)) + if (!xfs_dinode_good_version(mp, dino->di_version)) continue; if (be64_to_cpu(dino->di_size) <= XFS_DFORK_DSIZE(dino, mp)) -- 2.47.2