xref: /linux/fs/xfs/xfs_dquot_item_recover.c (revision 0a149ab78ee220c75eef797abea7a29f4490e226)
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 
24 STATIC void
25 xlog_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  */
60 STATIC int
61 xlog_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 
174 out_release:
175 	xfs_buf_relse(bp);
176 	return 0;
177 }
178 
179 const 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  */
190 STATIC int
191 xlog_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 
212 const 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