xfs: update the unlinked list when repairing link counts

When we're repairing the link counts of a file, we must ensure either
that the file has zero link count and is on the unlinked list; or that
it has nonzero link count and is not on the unlinked list.

Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
This commit is contained in:
Darrick J. Wong 2024-04-15 14:54:49 -07:00
parent e921533ef1
commit 669dfe883c

View File

@ -17,6 +17,7 @@
#include "xfs_iwalk.h"
#include "xfs_ialloc.h"
#include "xfs_sb.h"
#include "xfs_ag.h"
#include "scrub/scrub.h"
#include "scrub/common.h"
#include "scrub/repair.h"
@ -36,6 +37,20 @@
* inode is locked.
*/
/* Remove an inode from the unlinked list. */
STATIC int
xrep_nlinks_iunlink_remove(
struct xfs_scrub *sc)
{
struct xfs_perag *pag;
int error;
pag = xfs_perag_get(sc->mp, XFS_INO_TO_AGNO(sc->mp, sc->ip->i_ino));
error = xfs_iunlink_remove(sc->tp, pag, sc->ip);
xfs_perag_put(pag);
return error;
}
/*
* Correct the link count of the given inode. Because we have to grab locks
* and resources in a certain order, it's possible that this will be a no-op.
@ -99,16 +114,25 @@ xrep_nlinks_repair_inode(
}
/*
* We did not find any links to this inode. If the inode agrees, we
* have nothing further to do. If not, the inode has a nonzero link
* count and we don't have anywhere to graft the child onto. Dropping
* a live inode's link count to zero can cause unexpected shutdowns in
* inactivation, so leave it alone.
* If this inode is linked from the directory tree and on the unlinked
* list, remove it from the unlinked list.
*/
if (total_links == 0) {
if (actual_nlink != 0)
trace_xrep_nlinks_unfixable_inode(mp, ip, &obs);
goto out_trans;
if (total_links > 0 && xfs_inode_on_unlinked_list(ip)) {
error = xrep_nlinks_iunlink_remove(sc);
if (error)
goto out_trans;
dirty = true;
}
/*
* If this inode is not linked from the directory tree yet not on the
* unlinked list, put it on the unlinked list.
*/
if (total_links == 0 && !xfs_inode_on_unlinked_list(ip)) {
error = xfs_iunlink(sc->tp, ip);
if (error)
goto out_trans;
dirty = true;
}
/* Commit the new link count if it changed. */