Commit e744cef2 authored by Darrick J. Wong's avatar Darrick J. Wong

xfs: zap broken inode forks

Determine if inode fork damage is responsible for the inode being unable
to pass the ifork verifiers in xfs_iget and zap the fork contents if
this is true.  Once this is done the fork will be empty but we'll be
able to construct an in-core inode, and a subsequent call to the inode
fork repair ioctl will search the rmapbt to rebuild the records that
were in the fork.
Signed-off-by: default avatarDarrick J. Wong <djwong@kernel.org>
Reviewed-by: default avatarChristoph Hellwig <hch@lst.de>
parent 2d295fe6
...@@ -1040,23 +1040,16 @@ xfs_attr_shortform_allfit( ...@@ -1040,23 +1040,16 @@ xfs_attr_shortform_allfit(
return xfs_attr_shortform_bytesfit(dp, bytes); return xfs_attr_shortform_bytesfit(dp, bytes);
} }
/* Verify the consistency of an inline attribute fork. */ /* Verify the consistency of a raw inline attribute fork. */
xfs_failaddr_t xfs_failaddr_t
xfs_attr_shortform_verify( xfs_attr_shortform_verify(
struct xfs_inode *ip) struct xfs_attr_shortform *sfp,
size_t size)
{ {
struct xfs_attr_shortform *sfp;
struct xfs_attr_sf_entry *sfep; struct xfs_attr_sf_entry *sfep;
struct xfs_attr_sf_entry *next_sfep; struct xfs_attr_sf_entry *next_sfep;
char *endp; char *endp;
struct xfs_ifork *ifp;
int i; int i;
int64_t size;
ASSERT(ip->i_af.if_format == XFS_DINODE_FMT_LOCAL);
ifp = xfs_ifork_ptr(ip, XFS_ATTR_FORK);
sfp = (struct xfs_attr_shortform *)ifp->if_u1.if_data;
size = ifp->if_bytes;
/* /*
* Give up if the attribute is way too short. * Give up if the attribute is way too short.
......
...@@ -56,7 +56,8 @@ int xfs_attr_sf_findname(struct xfs_da_args *args, ...@@ -56,7 +56,8 @@ int xfs_attr_sf_findname(struct xfs_da_args *args,
unsigned int *basep); unsigned int *basep);
int xfs_attr_shortform_allfit(struct xfs_buf *bp, struct xfs_inode *dp); int xfs_attr_shortform_allfit(struct xfs_buf *bp, struct xfs_inode *dp);
int xfs_attr_shortform_bytesfit(struct xfs_inode *dp, int bytes); int xfs_attr_shortform_bytesfit(struct xfs_inode *dp, int bytes);
xfs_failaddr_t xfs_attr_shortform_verify(struct xfs_inode *ip); xfs_failaddr_t xfs_attr_shortform_verify(struct xfs_attr_shortform *sfp,
size_t size);
void xfs_attr_fork_remove(struct xfs_inode *ip, struct xfs_trans *tp); void xfs_attr_fork_remove(struct xfs_inode *ip, struct xfs_trans *tp);
/* /*
......
...@@ -6168,19 +6168,18 @@ xfs_bmap_finish_one( ...@@ -6168,19 +6168,18 @@ xfs_bmap_finish_one(
return error; return error;
} }
/* Check that an inode's extent does not have invalid flags or bad ranges. */ /* Check that an extent does not have invalid flags or bad ranges. */
xfs_failaddr_t xfs_failaddr_t
xfs_bmap_validate_extent( xfs_bmap_validate_extent_raw(
struct xfs_inode *ip, struct xfs_mount *mp,
bool rtfile,
int whichfork, int whichfork,
struct xfs_bmbt_irec *irec) struct xfs_bmbt_irec *irec)
{ {
struct xfs_mount *mp = ip->i_mount;
if (!xfs_verify_fileext(mp, irec->br_startoff, irec->br_blockcount)) if (!xfs_verify_fileext(mp, irec->br_startoff, irec->br_blockcount))
return __this_address; return __this_address;
if (XFS_IS_REALTIME_INODE(ip) && whichfork == XFS_DATA_FORK) { if (rtfile && whichfork == XFS_DATA_FORK) {
if (!xfs_verify_rtbext(mp, irec->br_startblock, if (!xfs_verify_rtbext(mp, irec->br_startblock,
irec->br_blockcount)) irec->br_blockcount))
return __this_address; return __this_address;
...@@ -6210,3 +6209,14 @@ xfs_bmap_intent_destroy_cache(void) ...@@ -6210,3 +6209,14 @@ xfs_bmap_intent_destroy_cache(void)
kmem_cache_destroy(xfs_bmap_intent_cache); kmem_cache_destroy(xfs_bmap_intent_cache);
xfs_bmap_intent_cache = NULL; xfs_bmap_intent_cache = NULL;
} }
/* Check that an inode's extent does not have invalid flags or bad ranges. */
xfs_failaddr_t
xfs_bmap_validate_extent(
struct xfs_inode *ip,
int whichfork,
struct xfs_bmbt_irec *irec)
{
return xfs_bmap_validate_extent_raw(ip->i_mount,
XFS_IS_REALTIME_INODE(ip), whichfork, irec);
}
...@@ -263,6 +263,8 @@ static inline uint32_t xfs_bmap_fork_to_state(int whichfork) ...@@ -263,6 +263,8 @@ static inline uint32_t xfs_bmap_fork_to_state(int whichfork)
} }
} }
xfs_failaddr_t xfs_bmap_validate_extent_raw(struct xfs_mount *mp, bool rtfile,
int whichfork, struct xfs_bmbt_irec *irec);
xfs_failaddr_t xfs_bmap_validate_extent(struct xfs_inode *ip, int whichfork, xfs_failaddr_t xfs_bmap_validate_extent(struct xfs_inode *ip, int whichfork,
struct xfs_bmbt_irec *irec); struct xfs_bmbt_irec *irec);
int xfs_bmap_complain_bad_rec(struct xfs_inode *ip, int whichfork, int xfs_bmap_complain_bad_rec(struct xfs_inode *ip, int whichfork,
......
...@@ -175,7 +175,8 @@ extern int xfs_dir2_sf_create(struct xfs_da_args *args, xfs_ino_t pino); ...@@ -175,7 +175,8 @@ extern int xfs_dir2_sf_create(struct xfs_da_args *args, xfs_ino_t pino);
extern int xfs_dir2_sf_lookup(struct xfs_da_args *args); extern int xfs_dir2_sf_lookup(struct xfs_da_args *args);
extern int xfs_dir2_sf_removename(struct xfs_da_args *args); extern int xfs_dir2_sf_removename(struct xfs_da_args *args);
extern int xfs_dir2_sf_replace(struct xfs_da_args *args); extern int xfs_dir2_sf_replace(struct xfs_da_args *args);
extern xfs_failaddr_t xfs_dir2_sf_verify(struct xfs_inode *ip); xfs_failaddr_t xfs_dir2_sf_verify(struct xfs_mount *mp,
struct xfs_dir2_sf_hdr *sfp, int64_t size);
int xfs_dir2_sf_entsize(struct xfs_mount *mp, int xfs_dir2_sf_entsize(struct xfs_mount *mp,
struct xfs_dir2_sf_hdr *hdr, int len); struct xfs_dir2_sf_hdr *hdr, int len);
void xfs_dir2_sf_put_ino(struct xfs_mount *mp, struct xfs_dir2_sf_hdr *hdr, void xfs_dir2_sf_put_ino(struct xfs_mount *mp, struct xfs_dir2_sf_hdr *hdr,
......
...@@ -707,11 +707,10 @@ xfs_dir2_sf_check( ...@@ -707,11 +707,10 @@ xfs_dir2_sf_check(
/* Verify the consistency of an inline directory. */ /* Verify the consistency of an inline directory. */
xfs_failaddr_t xfs_failaddr_t
xfs_dir2_sf_verify( xfs_dir2_sf_verify(
struct xfs_inode *ip) struct xfs_mount *mp,
struct xfs_dir2_sf_hdr *sfp,
int64_t size)
{ {
struct xfs_mount *mp = ip->i_mount;
struct xfs_ifork *ifp = xfs_ifork_ptr(ip, XFS_DATA_FORK);
struct xfs_dir2_sf_hdr *sfp;
struct xfs_dir2_sf_entry *sfep; struct xfs_dir2_sf_entry *sfep;
struct xfs_dir2_sf_entry *next_sfep; struct xfs_dir2_sf_entry *next_sfep;
char *endp; char *endp;
...@@ -719,15 +718,9 @@ xfs_dir2_sf_verify( ...@@ -719,15 +718,9 @@ xfs_dir2_sf_verify(
int i; int i;
int i8count; int i8count;
int offset; int offset;
int64_t size;
int error; int error;
uint8_t filetype; uint8_t filetype;
ASSERT(ifp->if_format == XFS_DINODE_FMT_LOCAL);
sfp = (struct xfs_dir2_sf_hdr *)ifp->if_u1.if_data;
size = ifp->if_bytes;
/* /*
* Give up if the directory is way too short. * Give up if the directory is way too short.
*/ */
......
...@@ -702,12 +702,22 @@ xfs_ifork_verify_local_data( ...@@ -702,12 +702,22 @@ xfs_ifork_verify_local_data(
xfs_failaddr_t fa = NULL; xfs_failaddr_t fa = NULL;
switch (VFS_I(ip)->i_mode & S_IFMT) { switch (VFS_I(ip)->i_mode & S_IFMT) {
case S_IFDIR: case S_IFDIR: {
fa = xfs_dir2_sf_verify(ip); struct xfs_mount *mp = ip->i_mount;
struct xfs_ifork *ifp = xfs_ifork_ptr(ip, XFS_DATA_FORK);
struct xfs_dir2_sf_hdr *sfp;
sfp = (struct xfs_dir2_sf_hdr *)ifp->if_u1.if_data;
fa = xfs_dir2_sf_verify(mp, sfp, ifp->if_bytes);
break; break;
case S_IFLNK: }
fa = xfs_symlink_shortform_verify(ip); case S_IFLNK: {
struct xfs_ifork *ifp = xfs_ifork_ptr(ip, XFS_DATA_FORK);
fa = xfs_symlink_shortform_verify(ifp->if_u1.if_data,
ifp->if_bytes);
break; break;
}
default: default:
break; break;
} }
...@@ -729,11 +739,20 @@ xfs_ifork_verify_local_attr( ...@@ -729,11 +739,20 @@ xfs_ifork_verify_local_attr(
struct xfs_ifork *ifp = &ip->i_af; struct xfs_ifork *ifp = &ip->i_af;
xfs_failaddr_t fa; xfs_failaddr_t fa;
if (!xfs_inode_has_attr_fork(ip)) if (!xfs_inode_has_attr_fork(ip)) {
fa = __this_address; fa = __this_address;
else } else {
fa = xfs_attr_shortform_verify(ip); struct xfs_attr_shortform *sfp;
struct xfs_ifork *ifp;
int64_t size;
ASSERT(ip->i_af.if_format == XFS_DINODE_FMT_LOCAL);
ifp = xfs_ifork_ptr(ip, XFS_ATTR_FORK);
sfp = (struct xfs_attr_shortform *)ifp->if_u1.if_data;
size = ifp->if_bytes;
fa = xfs_attr_shortform_verify(sfp, size);
}
if (fa) { if (fa) {
xfs_inode_verifier_error(ip, -EFSCORRUPTED, "attr fork", xfs_inode_verifier_error(ip, -EFSCORRUPTED, "attr fork",
ifp->if_u1.if_data, ifp->if_bytes, fa); ifp->if_u1.if_data, ifp->if_bytes, fa);
......
...@@ -139,7 +139,7 @@ bool xfs_symlink_hdr_ok(xfs_ino_t ino, uint32_t offset, ...@@ -139,7 +139,7 @@ bool xfs_symlink_hdr_ok(xfs_ino_t ino, uint32_t offset,
uint32_t size, struct xfs_buf *bp); uint32_t size, struct xfs_buf *bp);
void xfs_symlink_local_to_remote(struct xfs_trans *tp, struct xfs_buf *bp, void xfs_symlink_local_to_remote(struct xfs_trans *tp, struct xfs_buf *bp,
struct xfs_inode *ip, struct xfs_ifork *ifp); struct xfs_inode *ip, struct xfs_ifork *ifp);
xfs_failaddr_t xfs_symlink_shortform_verify(struct xfs_inode *ip); xfs_failaddr_t xfs_symlink_shortform_verify(void *sfp, int64_t size);
/* Computed inode geometry for the filesystem. */ /* Computed inode geometry for the filesystem. */
struct xfs_ino_geometry { struct xfs_ino_geometry {
......
...@@ -202,15 +202,11 @@ xfs_symlink_local_to_remote( ...@@ -202,15 +202,11 @@ xfs_symlink_local_to_remote(
*/ */
xfs_failaddr_t xfs_failaddr_t
xfs_symlink_shortform_verify( xfs_symlink_shortform_verify(
struct xfs_inode *ip) void *sfp,
int64_t size)
{ {
struct xfs_ifork *ifp = xfs_ifork_ptr(ip, XFS_DATA_FORK);
char *sfp = (char *)ifp->if_u1.if_data;
int size = ifp->if_bytes;
char *endp = sfp + size; char *endp = sfp + size;
ASSERT(ifp->if_format == XFS_DINODE_FMT_LOCAL);
/* /*
* Zero length symlinks should never occur in memory as they are * Zero length symlinks should never occur in memory as they are
* never allowed to exist on disk. * never allowed to exist on disk.
......
This diff is collapsed.
...@@ -1469,6 +1469,10 @@ DEFINE_REPAIR_DINODE_EVENT(xrep_dinode_extsize_hints); ...@@ -1469,6 +1469,10 @@ DEFINE_REPAIR_DINODE_EVENT(xrep_dinode_extsize_hints);
DEFINE_REPAIR_DINODE_EVENT(xrep_dinode_zap_symlink); DEFINE_REPAIR_DINODE_EVENT(xrep_dinode_zap_symlink);
DEFINE_REPAIR_DINODE_EVENT(xrep_dinode_zap_dir); DEFINE_REPAIR_DINODE_EVENT(xrep_dinode_zap_dir);
DEFINE_REPAIR_DINODE_EVENT(xrep_dinode_fixed); DEFINE_REPAIR_DINODE_EVENT(xrep_dinode_fixed);
DEFINE_REPAIR_DINODE_EVENT(xrep_dinode_zap_forks);
DEFINE_REPAIR_DINODE_EVENT(xrep_dinode_zap_dfork);
DEFINE_REPAIR_DINODE_EVENT(xrep_dinode_zap_afork);
DEFINE_REPAIR_DINODE_EVENT(xrep_dinode_ensure_forkoff);
DECLARE_EVENT_CLASS(xrep_inode_class, DECLARE_EVENT_CLASS(xrep_inode_class,
TP_PROTO(struct xfs_scrub *sc), TP_PROTO(struct xfs_scrub *sc),
...@@ -1522,6 +1526,44 @@ DEFINE_REPAIR_INODE_EVENT(xrep_inode_sfdir_size); ...@@ -1522,6 +1526,44 @@ DEFINE_REPAIR_INODE_EVENT(xrep_inode_sfdir_size);
DEFINE_REPAIR_INODE_EVENT(xrep_inode_dir_size); DEFINE_REPAIR_INODE_EVENT(xrep_inode_dir_size);
DEFINE_REPAIR_INODE_EVENT(xrep_inode_fixed); DEFINE_REPAIR_INODE_EVENT(xrep_inode_fixed);
TRACE_EVENT(xrep_dinode_count_rmaps,
TP_PROTO(struct xfs_scrub *sc, xfs_rfsblock_t data_blocks,
xfs_rfsblock_t rt_blocks, xfs_rfsblock_t attr_blocks,
xfs_extnum_t data_extents, xfs_extnum_t rt_extents,
xfs_aextnum_t attr_extents),
TP_ARGS(sc, data_blocks, rt_blocks, attr_blocks, data_extents,
rt_extents, attr_extents),
TP_STRUCT__entry(
__field(dev_t, dev)
__field(xfs_ino_t, ino)
__field(xfs_rfsblock_t, data_blocks)
__field(xfs_rfsblock_t, rt_blocks)
__field(xfs_rfsblock_t, attr_blocks)
__field(xfs_extnum_t, data_extents)
__field(xfs_extnum_t, rt_extents)
__field(xfs_aextnum_t, attr_extents)
),
TP_fast_assign(
__entry->dev = sc->mp->m_super->s_dev;
__entry->ino = sc->sm->sm_ino;
__entry->data_blocks = data_blocks;
__entry->rt_blocks = rt_blocks;
__entry->attr_blocks = attr_blocks;
__entry->data_extents = data_extents;
__entry->rt_extents = rt_extents;
__entry->attr_extents = attr_extents;
),
TP_printk("dev %d:%d ino 0x%llx dblocks 0x%llx rtblocks 0x%llx ablocks 0x%llx dextents %llu rtextents %llu aextents %u",
MAJOR(__entry->dev), MINOR(__entry->dev),
__entry->ino,
__entry->data_blocks,
__entry->rt_blocks,
__entry->attr_blocks,
__entry->data_extents,
__entry->rt_extents,
__entry->attr_extents)
);
#endif /* IS_ENABLED(CONFIG_XFS_ONLINE_REPAIR) */ #endif /* IS_ENABLED(CONFIG_XFS_ONLINE_REPAIR) */
#endif /* _TRACE_XFS_SCRUB_TRACE_H */ #endif /* _TRACE_XFS_SCRUB_TRACE_H */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment