mirror of
https://github.com/torvalds/linux.git
synced 2026-09-14 08:01:12 +02:00
xfs: advance the findparent inode scan cursor while holding ILOCK
LOLLM pointed out a race condition in xrep_findparent_scan -- the
directory live update hook holds the directory ILOCK when it calls the
xchk_iscan_want_live_update predicate to figure out if it needs to
remember the live update, but xrep_findparent_scan drops the directory
ILOCK before advancing the cursor. Therefore, it's possible for a live
update to check the scan cursor after the scan drops the ILOCK but
before the scan updates its cursor. If this happens, we'll fail to
record the live update. Fix this by moving the cursor update logic
inside xrep_findparent_walk_directory.
Note that for non-directories it's ok to advance the cursor without
holding any ILOCK because the findparent scan only cares about directory
parents, not the children.
Cc: stable@vger.kernel.org # v6.10
Fixes: a07b455762 ("xfs: scan the filesystem to repair a directory dotdot entry")
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Assisted-by: LOLLM # finding obvious bugs
Reviewed-by: Carlos Maiolino <cmaiolino@redhat.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Carlos Maiolino <cem@kernel.org>
This commit is contained in:
parent
69e10c2b4a
commit
ad4497a92c
|
|
@ -139,12 +139,40 @@ xrep_findparent_dirent(
|
|||
return 0;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
xrep_findparent_want_scan_file(
|
||||
const struct xrep_findparent_info *fpi)
|
||||
{
|
||||
const struct xfs_scrub *sc = fpi->sc;
|
||||
const struct xfs_inode *dp = fpi->dp;
|
||||
|
||||
/* Only directories can be parents */
|
||||
if (!S_ISDIR(VFS_IC(dp)->i_mode))
|
||||
return false;
|
||||
|
||||
/*
|
||||
* The inode being scanned cannot be its own parent, nor can any
|
||||
* temporary directory we created to stage this repair.
|
||||
*/
|
||||
if (dp == sc->ip || dp == sc->tempip)
|
||||
return false;
|
||||
|
||||
/*
|
||||
* Similarly, temporary files created to stage a repair cannot be the
|
||||
* parent of this inode.
|
||||
*/
|
||||
if (xrep_is_tempfile(dp))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* If this is a directory, walk the dirents looking for any that point to the
|
||||
* scrub target inode.
|
||||
*/
|
||||
STATIC int
|
||||
xrep_findparent_walk_directory(
|
||||
xrep_findparent_walk_file(
|
||||
struct xrep_findparent_info *fpi)
|
||||
{
|
||||
struct xfs_scrub *sc = fpi->sc;
|
||||
|
|
@ -152,19 +180,11 @@ xrep_findparent_walk_directory(
|
|||
unsigned int lock_mode;
|
||||
int error = 0;
|
||||
|
||||
/*
|
||||
* The inode being scanned cannot be its own parent, nor can any
|
||||
* temporary directory we created to stage this repair.
|
||||
*/
|
||||
if (dp == sc->ip || dp == sc->tempip)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Similarly, temporary files created to stage a repair cannot be the
|
||||
* parent of this inode.
|
||||
*/
|
||||
if (xrep_is_tempfile(dp))
|
||||
if (!xrep_findparent_want_scan_file(fpi)) {
|
||||
if (fpi->parent_scan)
|
||||
xchk_iscan_mark_visited(&fpi->parent_scan->iscan, dp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Scan the directory to see if there it contains an entry pointing to
|
||||
|
|
@ -201,6 +221,8 @@ xrep_findparent_walk_directory(
|
|||
goto out_unlock;
|
||||
|
||||
out_unlock:
|
||||
if (fpi->parent_scan)
|
||||
xchk_iscan_mark_visited(&fpi->parent_scan->iscan, dp);
|
||||
xfs_iunlock(dp, lock_mode);
|
||||
return error;
|
||||
}
|
||||
|
|
@ -308,11 +330,7 @@ xrep_findparent_scan(
|
|||
ASSERT(S_ISDIR(VFS_IC(sc->ip)->i_mode));
|
||||
|
||||
while ((ret = xchk_iscan_iter(&pscan->iscan, &fpi.dp)) == 1) {
|
||||
if (S_ISDIR(VFS_I(fpi.dp)->i_mode))
|
||||
ret = xrep_findparent_walk_directory(&fpi);
|
||||
else
|
||||
ret = 0;
|
||||
xchk_iscan_mark_visited(&pscan->iscan, fpi.dp);
|
||||
ret = xrep_findparent_walk_file(&fpi);
|
||||
xchk_irele(sc, fpi.dp);
|
||||
if (ret)
|
||||
break;
|
||||
|
|
@ -401,7 +419,7 @@ xrep_findparent_confirm(
|
|||
goto out_rele;
|
||||
}
|
||||
|
||||
error = xrep_findparent_walk_directory(&fpi);
|
||||
error = xrep_findparent_walk_file(&fpi);
|
||||
if (error)
|
||||
goto out_rele;
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user