1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Copyright (c) 2000-2006 Silicon Graphics, Inc.
4 * All Rights Reserved.
5 */
6#include "xfs.h"
7#include "xfs_fs.h"
8#include "xfs_shared.h"
9#include "xfs_format.h"
10#include "xfs_log_format.h"
11#include "xfs_trans_resv.h"
12#include "xfs_mount.h"
13#include "xfs_inode.h"
14#include "xfs_quota.h"
15#include "xfs_trans.h"
16#include "xfs_buf_item.h"
17#include "xfs_trans_priv.h"
18#include "xfs_qm.h"
19#include "xfs_log.h"
20#include "xfs_log_priv.h"
21#include "xfs_log_recover.h"
22#include "xfs_error.h"
23
24STATIC void
25xlog_recover_dquot_ra_pass2(
26	struct xlog			*log,
27	struct xlog_recover_item	*item)
28{
29	struct xfs_mount	*mp = log->l_mp;
30	struct xfs_disk_dquot	*recddq;
31	struct xfs_dq_logformat	*dq_f;
32	uint			type;
33
34	if (mp->m_qflags == 0)
35		return;
36
37	recddq = item->ri_buf[1].i_addr;
38	if (recddq == NULL)
39		return;
40	if (item->ri_buf[1].i_len < sizeof(struct xfs_disk_dquot))
41		return;
42
43	type = recddq->d_type & XFS_DQTYPE_REC_MASK;
44	ASSERT(type);
45	if (log->l_quotaoffs_flag & type)
46		return;
47
48	dq_f = item->ri_buf[0].i_addr;
49	ASSERT(dq_f);
50	ASSERT(dq_f->qlf_len == 1);
51
52	xlog_buf_readahead(log, dq_f->qlf_blkno,
53			XFS_FSB_TO_BB(mp, dq_f->qlf_len),
54			&xfs_dquot_buf_ra_ops);
55}
56
57/*
58 * Recover a dquot record
59 */
60STATIC int
61xlog_recover_dquot_commit_pass2(
62	struct xlog			*log,
63	struct list_head		*buffer_list,
64	struct xlog_recover_item	*item,
65	xfs_lsn_t			current_lsn)
66{
67	struct xfs_mount		*mp = log->l_mp;
68	struct xfs_buf			*bp;
69	struct xfs_dqblk		*dqb;
70	struct xfs_disk_dquot		*ddq, *recddq;
71	struct xfs_dq_logformat		*dq_f;
72	xfs_failaddr_t			fa;
73	int				error;
74	uint				type;
75
76	/*
77	 * Filesystems are required to send in quota flags at mount time.
78	 */
79	if (mp->m_qflags == 0)
80		return 0;
81
82	recddq = item->ri_buf[1].i_addr;
83	if (recddq == NULL) {
84		xfs_alert(log->l_mp, "NULL dquot in %s.", __func__);
85		return -EFSCORRUPTED;
86	}
87	if (item->ri_buf[1].i_len < sizeof(struct xfs_disk_dquot)) {
88		xfs_alert(log->l_mp, "dquot too small (%d) in %s.",
89			item->ri_buf[1].i_len, __func__);
90		return -EFSCORRUPTED;
91	}
92
93	/*
94	 * This type of quotas was turned off, so ignore this record.
95	 */
96	type = recddq->d_type & XFS_DQTYPE_REC_MASK;
97	ASSERT(type);
98	if (log->l_quotaoffs_flag & type)
99		return 0;
100
101	/*
102	 * At this point we know that quota was _not_ turned off.
103	 * Since the mount flags are not indicating to us otherwise, this
104	 * must mean that quota is on, and the dquot needs to be replayed.
105	 * Remember that we may not have fully recovered the superblock yet,
106	 * so we can't do the usual trick of looking at the SB quota bits.
107	 *
108	 * The other possibility, of course, is that the quota subsystem was
109	 * removed since the last mount - ENOSYS.
110	 */
111	dq_f = item->ri_buf[0].i_addr;
112	ASSERT(dq_f);
113	fa = xfs_dquot_verify(mp, recddq, dq_f->qlf_id);
114	if (fa) {
115		xfs_alert(mp, "corrupt dquot ID 0x%x in log at %pS",
116				dq_f->qlf_id, fa);
117		return -EFSCORRUPTED;
118	}
119	ASSERT(dq_f->qlf_len == 1);
120
121	/*
122	 * At this point we are assuming that the dquots have been allocated
123	 * and hence the buffer has valid dquots stamped in it. It should,
124	 * therefore, pass verifier validation. If the dquot is bad, then the
125	 * we'll return an error here, so we don't need to specifically check
126	 * the dquot in the buffer after the verifier has run.
127	 */
128	error = xfs_trans_read_buf(mp, NULL, mp->m_ddev_targp, dq_f->qlf_blkno,
129				   XFS_FSB_TO_BB(mp, dq_f->qlf_len), 0, &bp,
130				   &xfs_dquot_buf_ops);
131	if (error)
132		return error;
133
134	ASSERT(bp);
135	dqb = xfs_buf_offset(bp, dq_f->qlf_boffset);
136	ddq = &dqb->dd_diskdq;
137
138	/*
139	 * If the dquot has an LSN in it, recover the dquot only if it's less
140	 * than the lsn of the transaction we are replaying.
141	 */
142	if (xfs_has_crc(mp)) {
143		xfs_lsn_t	lsn = be64_to_cpu(dqb->dd_lsn);
144
145		if (lsn && lsn != -1 && XFS_LSN_CMP(lsn, current_lsn) >= 0) {
146			goto out_release;
147		}
148	}
149
150	memcpy(ddq, recddq, item->ri_buf[1].i_len);
151	if (xfs_has_crc(mp)) {
152		xfs_update_cksum((char *)dqb, sizeof(struct xfs_dqblk),
153				 XFS_DQUOT_CRC_OFF);
154	}
155
156	/* Validate the recovered dquot. */
157	fa = xfs_dqblk_verify(log->l_mp, dqb, dq_f->qlf_id);
158	if (fa) {
159		XFS_CORRUPTION_ERROR("Bad dquot after recovery",
160				XFS_ERRLEVEL_LOW, mp, dqb,
161				sizeof(struct xfs_dqblk));
162		xfs_alert(mp,
163 "Metadata corruption detected at %pS, dquot 0x%x",
164				fa, dq_f->qlf_id);
165		error = -EFSCORRUPTED;
166		goto out_release;
167	}
168
169	ASSERT(dq_f->qlf_size == 2);
170	ASSERT(bp->b_mount == mp);
171	bp->b_flags |= _XBF_LOGRECOVERY;
172	xfs_buf_delwri_queue(bp, buffer_list);
173
174out_release:
175	xfs_buf_relse(bp);
176	return 0;
177}
178
179const struct xlog_recover_item_ops xlog_dquot_item_ops = {
180	.item_type		= XFS_LI_DQUOT,
181	.ra_pass2		= xlog_recover_dquot_ra_pass2,
182	.commit_pass2		= xlog_recover_dquot_commit_pass2,
183};
184
185/*
186 * Recover QUOTAOFF records. We simply make a note of it in the xlog
187 * structure, so that we know not to do any dquot item or dquot buffer recovery,
188 * of that type.
189 */
190STATIC int
191xlog_recover_quotaoff_commit_pass1(
192	struct xlog			*log,
193	struct xlog_recover_item	*item)
194{
195	struct xfs_qoff_logformat	*qoff_f = item->ri_buf[0].i_addr;
196	ASSERT(qoff_f);
197
198	/*
199	 * The logitem format's flag tells us if this was user quotaoff,
200	 * group/project quotaoff or both.
201	 */
202	if (qoff_f->qf_flags & XFS_UQUOTA_ACCT)
203		log->l_quotaoffs_flag |= XFS_DQTYPE_USER;
204	if (qoff_f->qf_flags & XFS_PQUOTA_ACCT)
205		log->l_quotaoffs_flag |= XFS_DQTYPE_PROJ;
206	if (qoff_f->qf_flags & XFS_GQUOTA_ACCT)
207		log->l_quotaoffs_flag |= XFS_DQTYPE_GROUP;
208
209	return 0;
210}
211
212const struct xlog_recover_item_ops xlog_quotaoff_item_ops = {
213	.item_type		= XFS_LI_QUOTAOFF,
214	.commit_pass1		= xlog_recover_quotaoff_commit_pass1,
215	/* nothing to commit in pass2 */
216};
217