diff mbox series

[30/36] libxfs: retain ifork_ops when flushing inode

Message ID 155259761740.31886.12775146464846669280.stgit@magnolia (mailing list archive)
State Superseded
Headers show
Series xfsprogs-5.0: fix various problems | expand

Commit Message

Darrick J. Wong March 14, 2019, 9:06 p.m. UTC
From: Darrick J. Wong <darrick.wong@oracle.com>

Retain the ifork ops used to validate the inode so that we can use the
same one to iflush it.  xfs_repair phase 6 can use multiple transactions
to fix various inode problems, which means that the inode might not be
fully fixed when each transaction commits.

This can be a particular problem if there's a shortform directory with
both invalid directory entries and incorrect i8count.  Phase 3 will set
the parent inode to "0" to signal to phase 6 that it needs to reset the
parent and i8count, but phase 6 starts a transaction to junk the bad
entries which fail to commit because the parent is invalid:

fixing i8count in inode 69022994673
Invalid inode number 0x0
xfs_dir_ino_validate: XFS_ERROR_REPORT
Metadata corruption detected at 0x464eb0, inode 0x10121750f1 data fork
xfs_repair: warning - iflush_int failed (-117)

And thus the inode fixes never get written out.

Reported-by: Arkadiusz Miskiewicz <arekm@maven.pl>
Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
 include/xfs_inode.h |    1 +
 libxfs/rdwr.c       |    1 +
 libxfs/util.c       |    2 +-
 3 files changed, 3 insertions(+), 1 deletion(-)

Comments

Eric Sandeen April 5, 2019, 6:17 p.m. UTC | #1
On 3/14/19 4:06 PM, Darrick J. Wong wrote:
> From: Darrick J. Wong <darrick.wong@oracle.com>
> 
> Retain the ifork ops used to validate the inode so that we can use the
> same one to iflush it.  xfs_repair phase 6 can use multiple transactions
> to fix various inode problems, which means that the inode might not be
> fully fixed when each transaction commits.
> 
> This can be a particular problem if there's a shortform directory with
> both invalid directory entries and incorrect i8count.  Phase 3 will set
> the parent inode to "0" to signal to phase 6 that it needs to reset the
> parent and i8count, but phase 6 starts a transaction to junk the bad
> entries which fail to commit because the parent is invalid:
> 
> fixing i8count in inode 69022994673
> Invalid inode number 0x0
> xfs_dir_ino_validate: XFS_ERROR_REPORT
> Metadata corruption detected at 0x464eb0, inode 0x10121750f1 data fork
> xfs_repair: warning - iflush_int failed (-117)
> 
> And thus the inode fixes never get written out.

This feels like it would be better to use consistently by dropping the ops
arg to libxfs_inode_verify_forks, using ->i_fork_ops inside it, and setting
it prior to the call.  Er, same for libxfs_iget?

A mishmash of grabbing what was last set in the inode vs. explicitly pointing
at a global ops structure seems a bit ad hoc.  Thoughts?


> 
> Reported-by: Arkadiusz Miskiewicz <arekm@maven.pl>
> Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
> ---
>  include/xfs_inode.h |    1 +
>  libxfs/rdwr.c       |    1 +
>  libxfs/util.c       |    2 +-
>  3 files changed, 3 insertions(+), 1 deletion(-)
> 
> 
> diff --git a/include/xfs_inode.h b/include/xfs_inode.h
> index 79ec3a2d..e1e8b430 100644
> --- a/include/xfs_inode.h
> +++ b/include/xfs_inode.h
> @@ -51,6 +51,7 @@ typedef struct xfs_inode {
>  
>  	xfs_fsize_t		i_size;		/* in-memory size */
>  	const struct xfs_dir_ops *d_ops;	/* directory ops vector */
> +	struct xfs_ifork_ops	*i_fork_ops;	/* fork verifiers */
>  	struct inode		i_vnode;
>  } xfs_inode_t;
>  
> diff --git a/libxfs/rdwr.c b/libxfs/rdwr.c
> index a00360e7..69d5abb2 100644
> --- a/libxfs/rdwr.c
> +++ b/libxfs/rdwr.c
> @@ -1391,6 +1391,7 @@ libxfs_iget(
>  		return error;
>  	}
>  
> +	ip->i_fork_ops = ifork_ops;
>  	if (!libxfs_inode_verify_forks(ip, ifork_ops)) {
>  		libxfs_irele(ip);
>  		return -EFSCORRUPTED;
> diff --git a/libxfs/util.c b/libxfs/util.c
> index bd414043..0799f965 100644
> --- a/libxfs/util.c
> +++ b/libxfs/util.c
> @@ -420,7 +420,7 @@ libxfs_iflush_int(xfs_inode_t *ip, xfs_buf_t *bp)
>  		VFS_I(ip)->i_version++;
>  
>  	/* Check the inline fork data before we write out. */
> -	if (!libxfs_inode_verify_forks(ip, &xfs_default_ifork_ops))
> +	if (!libxfs_inode_verify_forks(ip, ip->i_fork_ops))
>  		return -EFSCORRUPTED;
>  
>  	/*
>
Eric Sandeen April 5, 2019, 6:19 p.m. UTC | #2
On 4/5/19 1:17 PM, Eric Sandeen wrote:
> On 3/14/19 4:06 PM, Darrick J. Wong wrote:
>> From: Darrick J. Wong <darrick.wong@oracle.com>
>>
>> Retain the ifork ops used to validate the inode so that we can use the
>> same one to iflush it.  xfs_repair phase 6 can use multiple transactions
>> to fix various inode problems, which means that the inode might not be
>> fully fixed when each transaction commits.
>>
>> This can be a particular problem if there's a shortform directory with
>> both invalid directory entries and incorrect i8count.  Phase 3 will set
>> the parent inode to "0" to signal to phase 6 that it needs to reset the
>> parent and i8count, but phase 6 starts a transaction to junk the bad
>> entries which fail to commit because the parent is invalid:
>>
>> fixing i8count in inode 69022994673
>> Invalid inode number 0x0
>> xfs_dir_ino_validate: XFS_ERROR_REPORT
>> Metadata corruption detected at 0x464eb0, inode 0x10121750f1 data fork
>> xfs_repair: warning - iflush_int failed (-117)
>>
>> And thus the inode fixes never get written out.
> 
> This feels like it would be better to use consistently by dropping the ops
> arg to libxfs_inode_verify_forks, using ->i_fork_ops inside it, and setting
> it prior to the call.  Er, same for libxfs_iget?

er, no not for libxfs_iget, sorry.  But libxfs_iget can set the ops on
the inode if found and from then on stuff like libxfs_inode_verify_forks
uses what's currently set in the inode ....)

> A mishmash of grabbing what was last set in the inode vs. explicitly pointing
> at a global ops structure seems a bit ad hoc.  Thoughts?
Darrick J. Wong April 5, 2019, 8:06 p.m. UTC | #3
On Fri, Apr 05, 2019 at 01:19:52PM -0500, Eric Sandeen wrote:
> 
> 
> On 4/5/19 1:17 PM, Eric Sandeen wrote:
> > On 3/14/19 4:06 PM, Darrick J. Wong wrote:
> >> From: Darrick J. Wong <darrick.wong@oracle.com>
> >>
> >> Retain the ifork ops used to validate the inode so that we can use the
> >> same one to iflush it.  xfs_repair phase 6 can use multiple transactions
> >> to fix various inode problems, which means that the inode might not be
> >> fully fixed when each transaction commits.
> >>
> >> This can be a particular problem if there's a shortform directory with
> >> both invalid directory entries and incorrect i8count.  Phase 3 will set
> >> the parent inode to "0" to signal to phase 6 that it needs to reset the
> >> parent and i8count, but phase 6 starts a transaction to junk the bad
> >> entries which fail to commit because the parent is invalid:
> >>
> >> fixing i8count in inode 69022994673
> >> Invalid inode number 0x0
> >> xfs_dir_ino_validate: XFS_ERROR_REPORT
> >> Metadata corruption detected at 0x464eb0, inode 0x10121750f1 data fork
> >> xfs_repair: warning - iflush_int failed (-117)
> >>
> >> And thus the inode fixes never get written out.
> > 
> > This feels like it would be better to use consistently by dropping the ops
> > arg to libxfs_inode_verify_forks, using ->i_fork_ops inside it, and setting
> > it prior to the call.  Er, same for libxfs_iget?
> 
> er, no not for libxfs_iget, sorry.  But libxfs_iget can set the ops on
> the inode if found and from then on stuff like libxfs_inode_verify_forks
> uses what's currently set in the inode ....)
> 
> > A mishmash of grabbing what was last set in the inode vs. explicitly pointing
> > at a global ops structure seems a bit ad hoc.  Thoughts?

Yeah, there's no reason to have the second parameter when we've already
got it in @ip, so I'll make a new patch to refactor it out.

--D
diff mbox series

Patch

diff --git a/include/xfs_inode.h b/include/xfs_inode.h
index 79ec3a2d..e1e8b430 100644
--- a/include/xfs_inode.h
+++ b/include/xfs_inode.h
@@ -51,6 +51,7 @@  typedef struct xfs_inode {
 
 	xfs_fsize_t		i_size;		/* in-memory size */
 	const struct xfs_dir_ops *d_ops;	/* directory ops vector */
+	struct xfs_ifork_ops	*i_fork_ops;	/* fork verifiers */
 	struct inode		i_vnode;
 } xfs_inode_t;
 
diff --git a/libxfs/rdwr.c b/libxfs/rdwr.c
index a00360e7..69d5abb2 100644
--- a/libxfs/rdwr.c
+++ b/libxfs/rdwr.c
@@ -1391,6 +1391,7 @@  libxfs_iget(
 		return error;
 	}
 
+	ip->i_fork_ops = ifork_ops;
 	if (!libxfs_inode_verify_forks(ip, ifork_ops)) {
 		libxfs_irele(ip);
 		return -EFSCORRUPTED;
diff --git a/libxfs/util.c b/libxfs/util.c
index bd414043..0799f965 100644
--- a/libxfs/util.c
+++ b/libxfs/util.c
@@ -420,7 +420,7 @@  libxfs_iflush_int(xfs_inode_t *ip, xfs_buf_t *bp)
 		VFS_I(ip)->i_version++;
 
 	/* Check the inline fork data before we write out. */
-	if (!libxfs_inode_verify_forks(ip, &xfs_default_ifork_ops))
+	if (!libxfs_inode_verify_forks(ip, ip->i_fork_ops))
 		return -EFSCORRUPTED;
 
 	/*