162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (c) 2020-2022, Red Hat, Inc.
462306a36Sopenharmony_ci * All Rights Reserved.
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci#include "xfs.h"
762306a36Sopenharmony_ci#include "xfs_fs.h"
862306a36Sopenharmony_ci#include "xfs_shared.h"
962306a36Sopenharmony_ci#include "xfs_format.h"
1062306a36Sopenharmony_ci#include "xfs_log_format.h"
1162306a36Sopenharmony_ci#include "xfs_trans_resv.h"
1262306a36Sopenharmony_ci#include "xfs_mount.h"
1362306a36Sopenharmony_ci#include "xfs_inode.h"
1462306a36Sopenharmony_ci#include "xfs_trans.h"
1562306a36Sopenharmony_ci#include "xfs_trans_priv.h"
1662306a36Sopenharmony_ci#include "xfs_ag.h"
1762306a36Sopenharmony_ci#include "xfs_iunlink_item.h"
1862306a36Sopenharmony_ci#include "xfs_trace.h"
1962306a36Sopenharmony_ci#include "xfs_error.h"
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_cistruct kmem_cache	*xfs_iunlink_cache;
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_cistatic inline struct xfs_iunlink_item *IUL_ITEM(struct xfs_log_item *lip)
2462306a36Sopenharmony_ci{
2562306a36Sopenharmony_ci	return container_of(lip, struct xfs_iunlink_item, item);
2662306a36Sopenharmony_ci}
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_cistatic void
2962306a36Sopenharmony_cixfs_iunlink_item_release(
3062306a36Sopenharmony_ci	struct xfs_log_item	*lip)
3162306a36Sopenharmony_ci{
3262306a36Sopenharmony_ci	struct xfs_iunlink_item	*iup = IUL_ITEM(lip);
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci	xfs_perag_put(iup->pag);
3562306a36Sopenharmony_ci	kmem_cache_free(xfs_iunlink_cache, IUL_ITEM(lip));
3662306a36Sopenharmony_ci}
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_cistatic uint64_t
4062306a36Sopenharmony_cixfs_iunlink_item_sort(
4162306a36Sopenharmony_ci	struct xfs_log_item	*lip)
4262306a36Sopenharmony_ci{
4362306a36Sopenharmony_ci	return IUL_ITEM(lip)->ip->i_ino;
4462306a36Sopenharmony_ci}
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci/*
4762306a36Sopenharmony_ci * Look up the inode cluster buffer and log the on-disk unlinked inode change
4862306a36Sopenharmony_ci * we need to make.
4962306a36Sopenharmony_ci */
5062306a36Sopenharmony_cistatic int
5162306a36Sopenharmony_cixfs_iunlink_log_dinode(
5262306a36Sopenharmony_ci	struct xfs_trans	*tp,
5362306a36Sopenharmony_ci	struct xfs_iunlink_item	*iup)
5462306a36Sopenharmony_ci{
5562306a36Sopenharmony_ci	struct xfs_mount	*mp = tp->t_mountp;
5662306a36Sopenharmony_ci	struct xfs_inode	*ip = iup->ip;
5762306a36Sopenharmony_ci	struct xfs_dinode	*dip;
5862306a36Sopenharmony_ci	struct xfs_buf		*ibp;
5962306a36Sopenharmony_ci	int			offset;
6062306a36Sopenharmony_ci	int			error;
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	error = xfs_imap_to_bp(mp, tp, &ip->i_imap, &ibp);
6362306a36Sopenharmony_ci	if (error)
6462306a36Sopenharmony_ci		return error;
6562306a36Sopenharmony_ci	/*
6662306a36Sopenharmony_ci	 * Don't log the unlinked field on stale buffers as this may be the
6762306a36Sopenharmony_ci	 * transaction that frees the inode cluster and relogging the buffer
6862306a36Sopenharmony_ci	 * here will incorrectly remove the stale state.
6962306a36Sopenharmony_ci	 */
7062306a36Sopenharmony_ci	if (ibp->b_flags & XBF_STALE)
7162306a36Sopenharmony_ci		goto out;
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	dip = xfs_buf_offset(ibp, ip->i_imap.im_boffset);
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	/* Make sure the old pointer isn't garbage. */
7662306a36Sopenharmony_ci	if (be32_to_cpu(dip->di_next_unlinked) != iup->old_agino) {
7762306a36Sopenharmony_ci		xfs_inode_verifier_error(ip, -EFSCORRUPTED, __func__, dip,
7862306a36Sopenharmony_ci				sizeof(*dip), __this_address);
7962306a36Sopenharmony_ci		error = -EFSCORRUPTED;
8062306a36Sopenharmony_ci		goto out;
8162306a36Sopenharmony_ci	}
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	trace_xfs_iunlink_update_dinode(mp, iup->pag->pag_agno,
8462306a36Sopenharmony_ci			XFS_INO_TO_AGINO(mp, ip->i_ino),
8562306a36Sopenharmony_ci			be32_to_cpu(dip->di_next_unlinked), iup->next_agino);
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	dip->di_next_unlinked = cpu_to_be32(iup->next_agino);
8862306a36Sopenharmony_ci	offset = ip->i_imap.im_boffset +
8962306a36Sopenharmony_ci			offsetof(struct xfs_dinode, di_next_unlinked);
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	xfs_dinode_calc_crc(mp, dip);
9262306a36Sopenharmony_ci	xfs_trans_inode_buf(tp, ibp);
9362306a36Sopenharmony_ci	xfs_trans_log_buf(tp, ibp, offset, offset + sizeof(xfs_agino_t) - 1);
9462306a36Sopenharmony_ci	return 0;
9562306a36Sopenharmony_ciout:
9662306a36Sopenharmony_ci	xfs_trans_brelse(tp, ibp);
9762306a36Sopenharmony_ci	return error;
9862306a36Sopenharmony_ci}
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci/*
10162306a36Sopenharmony_ci * On precommit, we grab the inode cluster buffer for the inode number we were
10262306a36Sopenharmony_ci * passed, then update the next unlinked field for that inode in the buffer and
10362306a36Sopenharmony_ci * log the buffer. This ensures that the inode cluster buffer was logged in the
10462306a36Sopenharmony_ci * correct order w.r.t. other inode cluster buffers. We can then remove the
10562306a36Sopenharmony_ci * iunlink item from the transaction and release it as it is has now served it's
10662306a36Sopenharmony_ci * purpose.
10762306a36Sopenharmony_ci */
10862306a36Sopenharmony_cistatic int
10962306a36Sopenharmony_cixfs_iunlink_item_precommit(
11062306a36Sopenharmony_ci	struct xfs_trans	*tp,
11162306a36Sopenharmony_ci	struct xfs_log_item	*lip)
11262306a36Sopenharmony_ci{
11362306a36Sopenharmony_ci	struct xfs_iunlink_item	*iup = IUL_ITEM(lip);
11462306a36Sopenharmony_ci	int			error;
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	error = xfs_iunlink_log_dinode(tp, iup);
11762306a36Sopenharmony_ci	list_del(&lip->li_trans);
11862306a36Sopenharmony_ci	xfs_iunlink_item_release(lip);
11962306a36Sopenharmony_ci	return error;
12062306a36Sopenharmony_ci}
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_cistatic const struct xfs_item_ops xfs_iunlink_item_ops = {
12362306a36Sopenharmony_ci	.iop_release	= xfs_iunlink_item_release,
12462306a36Sopenharmony_ci	.iop_sort	= xfs_iunlink_item_sort,
12562306a36Sopenharmony_ci	.iop_precommit	= xfs_iunlink_item_precommit,
12662306a36Sopenharmony_ci};
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci/*
13062306a36Sopenharmony_ci * Initialize the inode log item for a newly allocated (in-core) inode.
13162306a36Sopenharmony_ci *
13262306a36Sopenharmony_ci * Inode extents can only reside within an AG. Hence specify the starting
13362306a36Sopenharmony_ci * block for the inode chunk by offset within an AG as well as the
13462306a36Sopenharmony_ci * length of the allocated extent.
13562306a36Sopenharmony_ci *
13662306a36Sopenharmony_ci * This joins the item to the transaction and marks it dirty so
13762306a36Sopenharmony_ci * that we don't need a separate call to do this, nor does the
13862306a36Sopenharmony_ci * caller need to know anything about the iunlink item.
13962306a36Sopenharmony_ci */
14062306a36Sopenharmony_ciint
14162306a36Sopenharmony_cixfs_iunlink_log_inode(
14262306a36Sopenharmony_ci	struct xfs_trans	*tp,
14362306a36Sopenharmony_ci	struct xfs_inode	*ip,
14462306a36Sopenharmony_ci	struct xfs_perag	*pag,
14562306a36Sopenharmony_ci	xfs_agino_t		next_agino)
14662306a36Sopenharmony_ci{
14762306a36Sopenharmony_ci	struct xfs_mount	*mp = tp->t_mountp;
14862306a36Sopenharmony_ci	struct xfs_iunlink_item	*iup;
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	ASSERT(xfs_verify_agino_or_null(pag, next_agino));
15162306a36Sopenharmony_ci	ASSERT(xfs_verify_agino_or_null(pag, ip->i_next_unlinked));
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	/*
15462306a36Sopenharmony_ci	 * Since we're updating a linked list, we should never find that the
15562306a36Sopenharmony_ci	 * current pointer is the same as the new value, unless we're
15662306a36Sopenharmony_ci	 * terminating the list.
15762306a36Sopenharmony_ci	 */
15862306a36Sopenharmony_ci	if (ip->i_next_unlinked == next_agino) {
15962306a36Sopenharmony_ci		if (next_agino != NULLAGINO)
16062306a36Sopenharmony_ci			return -EFSCORRUPTED;
16162306a36Sopenharmony_ci		return 0;
16262306a36Sopenharmony_ci	}
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	iup = kmem_cache_zalloc(xfs_iunlink_cache, GFP_KERNEL | __GFP_NOFAIL);
16562306a36Sopenharmony_ci	xfs_log_item_init(mp, &iup->item, XFS_LI_IUNLINK,
16662306a36Sopenharmony_ci			  &xfs_iunlink_item_ops);
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	iup->ip = ip;
16962306a36Sopenharmony_ci	iup->next_agino = next_agino;
17062306a36Sopenharmony_ci	iup->old_agino = ip->i_next_unlinked;
17162306a36Sopenharmony_ci	iup->pag = xfs_perag_hold(pag);
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	xfs_trans_add_item(tp, &iup->item);
17462306a36Sopenharmony_ci	tp->t_flags |= XFS_TRANS_DIRTY;
17562306a36Sopenharmony_ci	set_bit(XFS_LI_DIRTY, &iup->item.li_flags);
17662306a36Sopenharmony_ci	return 0;
17762306a36Sopenharmony_ci}
17862306a36Sopenharmony_ci
179