162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (c) 2000-2006 Silicon Graphics, 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_quota.h"
1562306a36Sopenharmony_ci#include "xfs_trans.h"
1662306a36Sopenharmony_ci#include "xfs_buf_item.h"
1762306a36Sopenharmony_ci#include "xfs_trans_priv.h"
1862306a36Sopenharmony_ci#include "xfs_qm.h"
1962306a36Sopenharmony_ci#include "xfs_log.h"
2062306a36Sopenharmony_ci#include "xfs_log_priv.h"
2162306a36Sopenharmony_ci#include "xfs_log_recover.h"
2262306a36Sopenharmony_ci#include "xfs_error.h"
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ciSTATIC void
2562306a36Sopenharmony_cixlog_recover_dquot_ra_pass2(
2662306a36Sopenharmony_ci	struct xlog			*log,
2762306a36Sopenharmony_ci	struct xlog_recover_item	*item)
2862306a36Sopenharmony_ci{
2962306a36Sopenharmony_ci	struct xfs_mount	*mp = log->l_mp;
3062306a36Sopenharmony_ci	struct xfs_disk_dquot	*recddq;
3162306a36Sopenharmony_ci	struct xfs_dq_logformat	*dq_f;
3262306a36Sopenharmony_ci	uint			type;
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci	if (mp->m_qflags == 0)
3562306a36Sopenharmony_ci		return;
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci	recddq = item->ri_buf[1].i_addr;
3862306a36Sopenharmony_ci	if (recddq == NULL)
3962306a36Sopenharmony_ci		return;
4062306a36Sopenharmony_ci	if (item->ri_buf[1].i_len < sizeof(struct xfs_disk_dquot))
4162306a36Sopenharmony_ci		return;
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci	type = recddq->d_type & XFS_DQTYPE_REC_MASK;
4462306a36Sopenharmony_ci	ASSERT(type);
4562306a36Sopenharmony_ci	if (log->l_quotaoffs_flag & type)
4662306a36Sopenharmony_ci		return;
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci	dq_f = item->ri_buf[0].i_addr;
4962306a36Sopenharmony_ci	ASSERT(dq_f);
5062306a36Sopenharmony_ci	ASSERT(dq_f->qlf_len == 1);
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	xlog_buf_readahead(log, dq_f->qlf_blkno,
5362306a36Sopenharmony_ci			XFS_FSB_TO_BB(mp, dq_f->qlf_len),
5462306a36Sopenharmony_ci			&xfs_dquot_buf_ra_ops);
5562306a36Sopenharmony_ci}
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci/*
5862306a36Sopenharmony_ci * Recover a dquot record
5962306a36Sopenharmony_ci */
6062306a36Sopenharmony_ciSTATIC int
6162306a36Sopenharmony_cixlog_recover_dquot_commit_pass2(
6262306a36Sopenharmony_ci	struct xlog			*log,
6362306a36Sopenharmony_ci	struct list_head		*buffer_list,
6462306a36Sopenharmony_ci	struct xlog_recover_item	*item,
6562306a36Sopenharmony_ci	xfs_lsn_t			current_lsn)
6662306a36Sopenharmony_ci{
6762306a36Sopenharmony_ci	struct xfs_mount		*mp = log->l_mp;
6862306a36Sopenharmony_ci	struct xfs_buf			*bp;
6962306a36Sopenharmony_ci	struct xfs_dqblk		*dqb;
7062306a36Sopenharmony_ci	struct xfs_disk_dquot		*ddq, *recddq;
7162306a36Sopenharmony_ci	struct xfs_dq_logformat		*dq_f;
7262306a36Sopenharmony_ci	xfs_failaddr_t			fa;
7362306a36Sopenharmony_ci	int				error;
7462306a36Sopenharmony_ci	uint				type;
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	/*
7762306a36Sopenharmony_ci	 * Filesystems are required to send in quota flags at mount time.
7862306a36Sopenharmony_ci	 */
7962306a36Sopenharmony_ci	if (mp->m_qflags == 0)
8062306a36Sopenharmony_ci		return 0;
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	recddq = item->ri_buf[1].i_addr;
8362306a36Sopenharmony_ci	if (recddq == NULL) {
8462306a36Sopenharmony_ci		xfs_alert(log->l_mp, "NULL dquot in %s.", __func__);
8562306a36Sopenharmony_ci		return -EFSCORRUPTED;
8662306a36Sopenharmony_ci	}
8762306a36Sopenharmony_ci	if (item->ri_buf[1].i_len < sizeof(struct xfs_disk_dquot)) {
8862306a36Sopenharmony_ci		xfs_alert(log->l_mp, "dquot too small (%d) in %s.",
8962306a36Sopenharmony_ci			item->ri_buf[1].i_len, __func__);
9062306a36Sopenharmony_ci		return -EFSCORRUPTED;
9162306a36Sopenharmony_ci	}
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	/*
9462306a36Sopenharmony_ci	 * This type of quotas was turned off, so ignore this record.
9562306a36Sopenharmony_ci	 */
9662306a36Sopenharmony_ci	type = recddq->d_type & XFS_DQTYPE_REC_MASK;
9762306a36Sopenharmony_ci	ASSERT(type);
9862306a36Sopenharmony_ci	if (log->l_quotaoffs_flag & type)
9962306a36Sopenharmony_ci		return 0;
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	/*
10262306a36Sopenharmony_ci	 * At this point we know that quota was _not_ turned off.
10362306a36Sopenharmony_ci	 * Since the mount flags are not indicating to us otherwise, this
10462306a36Sopenharmony_ci	 * must mean that quota is on, and the dquot needs to be replayed.
10562306a36Sopenharmony_ci	 * Remember that we may not have fully recovered the superblock yet,
10662306a36Sopenharmony_ci	 * so we can't do the usual trick of looking at the SB quota bits.
10762306a36Sopenharmony_ci	 *
10862306a36Sopenharmony_ci	 * The other possibility, of course, is that the quota subsystem was
10962306a36Sopenharmony_ci	 * removed since the last mount - ENOSYS.
11062306a36Sopenharmony_ci	 */
11162306a36Sopenharmony_ci	dq_f = item->ri_buf[0].i_addr;
11262306a36Sopenharmony_ci	ASSERT(dq_f);
11362306a36Sopenharmony_ci	fa = xfs_dquot_verify(mp, recddq, dq_f->qlf_id);
11462306a36Sopenharmony_ci	if (fa) {
11562306a36Sopenharmony_ci		xfs_alert(mp, "corrupt dquot ID 0x%x in log at %pS",
11662306a36Sopenharmony_ci				dq_f->qlf_id, fa);
11762306a36Sopenharmony_ci		return -EFSCORRUPTED;
11862306a36Sopenharmony_ci	}
11962306a36Sopenharmony_ci	ASSERT(dq_f->qlf_len == 1);
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	/*
12262306a36Sopenharmony_ci	 * At this point we are assuming that the dquots have been allocated
12362306a36Sopenharmony_ci	 * and hence the buffer has valid dquots stamped in it. It should,
12462306a36Sopenharmony_ci	 * therefore, pass verifier validation. If the dquot is bad, then the
12562306a36Sopenharmony_ci	 * we'll return an error here, so we don't need to specifically check
12662306a36Sopenharmony_ci	 * the dquot in the buffer after the verifier has run.
12762306a36Sopenharmony_ci	 */
12862306a36Sopenharmony_ci	error = xfs_trans_read_buf(mp, NULL, mp->m_ddev_targp, dq_f->qlf_blkno,
12962306a36Sopenharmony_ci				   XFS_FSB_TO_BB(mp, dq_f->qlf_len), 0, &bp,
13062306a36Sopenharmony_ci				   &xfs_dquot_buf_ops);
13162306a36Sopenharmony_ci	if (error)
13262306a36Sopenharmony_ci		return error;
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	ASSERT(bp);
13562306a36Sopenharmony_ci	dqb = xfs_buf_offset(bp, dq_f->qlf_boffset);
13662306a36Sopenharmony_ci	ddq = &dqb->dd_diskdq;
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	/*
13962306a36Sopenharmony_ci	 * If the dquot has an LSN in it, recover the dquot only if it's less
14062306a36Sopenharmony_ci	 * than the lsn of the transaction we are replaying.
14162306a36Sopenharmony_ci	 */
14262306a36Sopenharmony_ci	if (xfs_has_crc(mp)) {
14362306a36Sopenharmony_ci		xfs_lsn_t	lsn = be64_to_cpu(dqb->dd_lsn);
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci		if (lsn && lsn != -1 && XFS_LSN_CMP(lsn, current_lsn) >= 0) {
14662306a36Sopenharmony_ci			goto out_release;
14762306a36Sopenharmony_ci		}
14862306a36Sopenharmony_ci	}
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	memcpy(ddq, recddq, item->ri_buf[1].i_len);
15162306a36Sopenharmony_ci	if (xfs_has_crc(mp)) {
15262306a36Sopenharmony_ci		xfs_update_cksum((char *)dqb, sizeof(struct xfs_dqblk),
15362306a36Sopenharmony_ci				 XFS_DQUOT_CRC_OFF);
15462306a36Sopenharmony_ci	}
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	/* Validate the recovered dquot. */
15762306a36Sopenharmony_ci	fa = xfs_dqblk_verify(log->l_mp, dqb, dq_f->qlf_id);
15862306a36Sopenharmony_ci	if (fa) {
15962306a36Sopenharmony_ci		XFS_CORRUPTION_ERROR("Bad dquot after recovery",
16062306a36Sopenharmony_ci				XFS_ERRLEVEL_LOW, mp, dqb,
16162306a36Sopenharmony_ci				sizeof(struct xfs_dqblk));
16262306a36Sopenharmony_ci		xfs_alert(mp,
16362306a36Sopenharmony_ci "Metadata corruption detected at %pS, dquot 0x%x",
16462306a36Sopenharmony_ci				fa, dq_f->qlf_id);
16562306a36Sopenharmony_ci		error = -EFSCORRUPTED;
16662306a36Sopenharmony_ci		goto out_release;
16762306a36Sopenharmony_ci	}
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	ASSERT(dq_f->qlf_size == 2);
17062306a36Sopenharmony_ci	ASSERT(bp->b_mount == mp);
17162306a36Sopenharmony_ci	bp->b_flags |= _XBF_LOGRECOVERY;
17262306a36Sopenharmony_ci	xfs_buf_delwri_queue(bp, buffer_list);
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ciout_release:
17562306a36Sopenharmony_ci	xfs_buf_relse(bp);
17662306a36Sopenharmony_ci	return 0;
17762306a36Sopenharmony_ci}
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ciconst struct xlog_recover_item_ops xlog_dquot_item_ops = {
18062306a36Sopenharmony_ci	.item_type		= XFS_LI_DQUOT,
18162306a36Sopenharmony_ci	.ra_pass2		= xlog_recover_dquot_ra_pass2,
18262306a36Sopenharmony_ci	.commit_pass2		= xlog_recover_dquot_commit_pass2,
18362306a36Sopenharmony_ci};
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci/*
18662306a36Sopenharmony_ci * Recover QUOTAOFF records. We simply make a note of it in the xlog
18762306a36Sopenharmony_ci * structure, so that we know not to do any dquot item or dquot buffer recovery,
18862306a36Sopenharmony_ci * of that type.
18962306a36Sopenharmony_ci */
19062306a36Sopenharmony_ciSTATIC int
19162306a36Sopenharmony_cixlog_recover_quotaoff_commit_pass1(
19262306a36Sopenharmony_ci	struct xlog			*log,
19362306a36Sopenharmony_ci	struct xlog_recover_item	*item)
19462306a36Sopenharmony_ci{
19562306a36Sopenharmony_ci	struct xfs_qoff_logformat	*qoff_f = item->ri_buf[0].i_addr;
19662306a36Sopenharmony_ci	ASSERT(qoff_f);
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	/*
19962306a36Sopenharmony_ci	 * The logitem format's flag tells us if this was user quotaoff,
20062306a36Sopenharmony_ci	 * group/project quotaoff or both.
20162306a36Sopenharmony_ci	 */
20262306a36Sopenharmony_ci	if (qoff_f->qf_flags & XFS_UQUOTA_ACCT)
20362306a36Sopenharmony_ci		log->l_quotaoffs_flag |= XFS_DQTYPE_USER;
20462306a36Sopenharmony_ci	if (qoff_f->qf_flags & XFS_PQUOTA_ACCT)
20562306a36Sopenharmony_ci		log->l_quotaoffs_flag |= XFS_DQTYPE_PROJ;
20662306a36Sopenharmony_ci	if (qoff_f->qf_flags & XFS_GQUOTA_ACCT)
20762306a36Sopenharmony_ci		log->l_quotaoffs_flag |= XFS_DQTYPE_GROUP;
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	return 0;
21062306a36Sopenharmony_ci}
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ciconst struct xlog_recover_item_ops xlog_quotaoff_item_ops = {
21362306a36Sopenharmony_ci	.item_type		= XFS_LI_QUOTAOFF,
21462306a36Sopenharmony_ci	.commit_pass1		= xlog_recover_quotaoff_commit_pass1,
21562306a36Sopenharmony_ci	/* nothing to commit in pass2 */
21662306a36Sopenharmony_ci};
217