xref: /openbmc/linux/fs/xfs/xfs_dquot_item_recover.c (revision 695c312ec5a68e4373d063ee649c7b925ffb5da7)
186ffa471SDarrick J. Wong // SPDX-License-Identifier: GPL-2.0
286ffa471SDarrick J. Wong /*
386ffa471SDarrick J. Wong  * Copyright (c) 2000-2006 Silicon Graphics, Inc.
486ffa471SDarrick J. Wong  * All Rights Reserved.
586ffa471SDarrick J. Wong  */
686ffa471SDarrick J. Wong #include "xfs.h"
786ffa471SDarrick J. Wong #include "xfs_fs.h"
886ffa471SDarrick J. Wong #include "xfs_shared.h"
986ffa471SDarrick J. Wong #include "xfs_format.h"
1086ffa471SDarrick J. Wong #include "xfs_log_format.h"
1186ffa471SDarrick J. Wong #include "xfs_trans_resv.h"
1286ffa471SDarrick J. Wong #include "xfs_mount.h"
1386ffa471SDarrick J. Wong #include "xfs_inode.h"
1486ffa471SDarrick J. Wong #include "xfs_quota.h"
1586ffa471SDarrick J. Wong #include "xfs_trans.h"
1686ffa471SDarrick J. Wong #include "xfs_buf_item.h"
1786ffa471SDarrick J. Wong #include "xfs_trans_priv.h"
1886ffa471SDarrick J. Wong #include "xfs_qm.h"
1986ffa471SDarrick J. Wong #include "xfs_log.h"
2086ffa471SDarrick J. Wong #include "xfs_log_priv.h"
2186ffa471SDarrick J. Wong #include "xfs_log_recover.h"
22*3581868fSDarrick J. Wong #include "xfs_error.h"
2386ffa471SDarrick J. Wong 
248ea5682dSDarrick J. Wong STATIC void
xlog_recover_dquot_ra_pass2(struct xlog * log,struct xlog_recover_item * item)258ea5682dSDarrick J. Wong xlog_recover_dquot_ra_pass2(
268ea5682dSDarrick J. Wong 	struct xlog			*log,
278ea5682dSDarrick J. Wong 	struct xlog_recover_item	*item)
288ea5682dSDarrick J. Wong {
298ea5682dSDarrick J. Wong 	struct xfs_mount	*mp = log->l_mp;
308ea5682dSDarrick J. Wong 	struct xfs_disk_dquot	*recddq;
318ea5682dSDarrick J. Wong 	struct xfs_dq_logformat	*dq_f;
328ea5682dSDarrick J. Wong 	uint			type;
338ea5682dSDarrick J. Wong 
348ea5682dSDarrick J. Wong 	if (mp->m_qflags == 0)
358ea5682dSDarrick J. Wong 		return;
368ea5682dSDarrick J. Wong 
378ea5682dSDarrick J. Wong 	recddq = item->ri_buf[1].i_addr;
388ea5682dSDarrick J. Wong 	if (recddq == NULL)
398ea5682dSDarrick J. Wong 		return;
408ea5682dSDarrick J. Wong 	if (item->ri_buf[1].i_len < sizeof(struct xfs_disk_dquot))
418ea5682dSDarrick J. Wong 		return;
428ea5682dSDarrick J. Wong 
43d8c1af0dSDarrick J. Wong 	type = recddq->d_type & XFS_DQTYPE_REC_MASK;
448ea5682dSDarrick J. Wong 	ASSERT(type);
458ea5682dSDarrick J. Wong 	if (log->l_quotaoffs_flag & type)
468ea5682dSDarrick J. Wong 		return;
478ea5682dSDarrick J. Wong 
488ea5682dSDarrick J. Wong 	dq_f = item->ri_buf[0].i_addr;
498ea5682dSDarrick J. Wong 	ASSERT(dq_f);
508ea5682dSDarrick J. Wong 	ASSERT(dq_f->qlf_len == 1);
518ea5682dSDarrick J. Wong 
528ea5682dSDarrick J. Wong 	xlog_buf_readahead(log, dq_f->qlf_blkno,
538ea5682dSDarrick J. Wong 			XFS_FSB_TO_BB(mp, dq_f->qlf_len),
548ea5682dSDarrick J. Wong 			&xfs_dquot_buf_ra_ops);
558ea5682dSDarrick J. Wong }
568ea5682dSDarrick J. Wong 
57fcbdf91eSDarrick J. Wong /*
58fcbdf91eSDarrick J. Wong  * Recover a dquot record
59fcbdf91eSDarrick J. Wong  */
60fcbdf91eSDarrick J. Wong STATIC int
xlog_recover_dquot_commit_pass2(struct xlog * log,struct list_head * buffer_list,struct xlog_recover_item * item,xfs_lsn_t current_lsn)61fcbdf91eSDarrick J. Wong xlog_recover_dquot_commit_pass2(
62fcbdf91eSDarrick J. Wong 	struct xlog			*log,
63fcbdf91eSDarrick J. Wong 	struct list_head		*buffer_list,
64fcbdf91eSDarrick J. Wong 	struct xlog_recover_item	*item,
65fcbdf91eSDarrick J. Wong 	xfs_lsn_t			current_lsn)
66fcbdf91eSDarrick J. Wong {
67fcbdf91eSDarrick J. Wong 	struct xfs_mount		*mp = log->l_mp;
68fcbdf91eSDarrick J. Wong 	struct xfs_buf			*bp;
69d744e578SDarrick J. Wong 	struct xfs_dqblk		*dqb;
70fcbdf91eSDarrick J. Wong 	struct xfs_disk_dquot		*ddq, *recddq;
71fcbdf91eSDarrick J. Wong 	struct xfs_dq_logformat		*dq_f;
72fcbdf91eSDarrick J. Wong 	xfs_failaddr_t			fa;
73fcbdf91eSDarrick J. Wong 	int				error;
74fcbdf91eSDarrick J. Wong 	uint				type;
75fcbdf91eSDarrick J. Wong 
76fcbdf91eSDarrick J. Wong 	/*
77fcbdf91eSDarrick J. Wong 	 * Filesystems are required to send in quota flags at mount time.
78fcbdf91eSDarrick J. Wong 	 */
79fcbdf91eSDarrick J. Wong 	if (mp->m_qflags == 0)
80fcbdf91eSDarrick J. Wong 		return 0;
81fcbdf91eSDarrick J. Wong 
82fcbdf91eSDarrick J. Wong 	recddq = item->ri_buf[1].i_addr;
83fcbdf91eSDarrick J. Wong 	if (recddq == NULL) {
84fcbdf91eSDarrick J. Wong 		xfs_alert(log->l_mp, "NULL dquot in %s.", __func__);
85fcbdf91eSDarrick J. Wong 		return -EFSCORRUPTED;
86fcbdf91eSDarrick J. Wong 	}
87fcbdf91eSDarrick J. Wong 	if (item->ri_buf[1].i_len < sizeof(struct xfs_disk_dquot)) {
88fcbdf91eSDarrick J. Wong 		xfs_alert(log->l_mp, "dquot too small (%d) in %s.",
89fcbdf91eSDarrick J. Wong 			item->ri_buf[1].i_len, __func__);
90fcbdf91eSDarrick J. Wong 		return -EFSCORRUPTED;
91fcbdf91eSDarrick J. Wong 	}
92fcbdf91eSDarrick J. Wong 
93fcbdf91eSDarrick J. Wong 	/*
94fcbdf91eSDarrick J. Wong 	 * This type of quotas was turned off, so ignore this record.
95fcbdf91eSDarrick J. Wong 	 */
96d8c1af0dSDarrick J. Wong 	type = recddq->d_type & XFS_DQTYPE_REC_MASK;
97fcbdf91eSDarrick J. Wong 	ASSERT(type);
98fcbdf91eSDarrick J. Wong 	if (log->l_quotaoffs_flag & type)
99fcbdf91eSDarrick J. Wong 		return 0;
100fcbdf91eSDarrick J. Wong 
101fcbdf91eSDarrick J. Wong 	/*
102fcbdf91eSDarrick J. Wong 	 * At this point we know that quota was _not_ turned off.
103fcbdf91eSDarrick J. Wong 	 * Since the mount flags are not indicating to us otherwise, this
104fcbdf91eSDarrick J. Wong 	 * must mean that quota is on, and the dquot needs to be replayed.
105fcbdf91eSDarrick J. Wong 	 * Remember that we may not have fully recovered the superblock yet,
106fcbdf91eSDarrick J. Wong 	 * so we can't do the usual trick of looking at the SB quota bits.
107fcbdf91eSDarrick J. Wong 	 *
108fcbdf91eSDarrick J. Wong 	 * The other possibility, of course, is that the quota subsystem was
109fcbdf91eSDarrick J. Wong 	 * removed since the last mount - ENOSYS.
110fcbdf91eSDarrick J. Wong 	 */
111fcbdf91eSDarrick J. Wong 	dq_f = item->ri_buf[0].i_addr;
112fcbdf91eSDarrick J. Wong 	ASSERT(dq_f);
113f9751c4aSDarrick J. Wong 	fa = xfs_dquot_verify(mp, recddq, dq_f->qlf_id);
114fcbdf91eSDarrick J. Wong 	if (fa) {
115fcbdf91eSDarrick J. Wong 		xfs_alert(mp, "corrupt dquot ID 0x%x in log at %pS",
116fcbdf91eSDarrick J. Wong 				dq_f->qlf_id, fa);
117fcbdf91eSDarrick J. Wong 		return -EFSCORRUPTED;
118fcbdf91eSDarrick J. Wong 	}
119fcbdf91eSDarrick J. Wong 	ASSERT(dq_f->qlf_len == 1);
120fcbdf91eSDarrick J. Wong 
121fcbdf91eSDarrick J. Wong 	/*
122fcbdf91eSDarrick J. Wong 	 * At this point we are assuming that the dquots have been allocated
123fcbdf91eSDarrick J. Wong 	 * and hence the buffer has valid dquots stamped in it. It should,
124fcbdf91eSDarrick J. Wong 	 * therefore, pass verifier validation. If the dquot is bad, then the
125fcbdf91eSDarrick J. Wong 	 * we'll return an error here, so we don't need to specifically check
126fcbdf91eSDarrick J. Wong 	 * the dquot in the buffer after the verifier has run.
127fcbdf91eSDarrick J. Wong 	 */
128fcbdf91eSDarrick J. Wong 	error = xfs_trans_read_buf(mp, NULL, mp->m_ddev_targp, dq_f->qlf_blkno,
129fcbdf91eSDarrick J. Wong 				   XFS_FSB_TO_BB(mp, dq_f->qlf_len), 0, &bp,
130fcbdf91eSDarrick J. Wong 				   &xfs_dquot_buf_ops);
131fcbdf91eSDarrick J. Wong 	if (error)
132fcbdf91eSDarrick J. Wong 		return error;
133fcbdf91eSDarrick J. Wong 
134fcbdf91eSDarrick J. Wong 	ASSERT(bp);
135d744e578SDarrick J. Wong 	dqb = xfs_buf_offset(bp, dq_f->qlf_boffset);
136d744e578SDarrick J. Wong 	ddq = &dqb->dd_diskdq;
137fcbdf91eSDarrick J. Wong 
138fcbdf91eSDarrick J. Wong 	/*
139fcbdf91eSDarrick J. Wong 	 * If the dquot has an LSN in it, recover the dquot only if it's less
140fcbdf91eSDarrick J. Wong 	 * than the lsn of the transaction we are replaying.
141fcbdf91eSDarrick J. Wong 	 */
14238c26bfdSDave Chinner 	if (xfs_has_crc(mp)) {
143fcbdf91eSDarrick J. Wong 		xfs_lsn_t	lsn = be64_to_cpu(dqb->dd_lsn);
144fcbdf91eSDarrick J. Wong 
145fcbdf91eSDarrick J. Wong 		if (lsn && lsn != -1 && XFS_LSN_CMP(lsn, current_lsn) >= 0) {
146fcbdf91eSDarrick J. Wong 			goto out_release;
147fcbdf91eSDarrick J. Wong 		}
148fcbdf91eSDarrick J. Wong 	}
149fcbdf91eSDarrick J. Wong 
150fcbdf91eSDarrick J. Wong 	memcpy(ddq, recddq, item->ri_buf[1].i_len);
15138c26bfdSDave Chinner 	if (xfs_has_crc(mp)) {
152d744e578SDarrick J. Wong 		xfs_update_cksum((char *)dqb, sizeof(struct xfs_dqblk),
153fcbdf91eSDarrick J. Wong 				 XFS_DQUOT_CRC_OFF);
154fcbdf91eSDarrick J. Wong 	}
155fcbdf91eSDarrick J. Wong 
156*3581868fSDarrick J. Wong 	/* Validate the recovered dquot. */
157*3581868fSDarrick J. Wong 	fa = xfs_dqblk_verify(log->l_mp, dqb, dq_f->qlf_id);
158*3581868fSDarrick J. Wong 	if (fa) {
159*3581868fSDarrick J. Wong 		XFS_CORRUPTION_ERROR("Bad dquot after recovery",
160*3581868fSDarrick J. Wong 				XFS_ERRLEVEL_LOW, mp, dqb,
161*3581868fSDarrick J. Wong 				sizeof(struct xfs_dqblk));
162*3581868fSDarrick J. Wong 		xfs_alert(mp,
163*3581868fSDarrick J. Wong  "Metadata corruption detected at %pS, dquot 0x%x",
164*3581868fSDarrick J. Wong 				fa, dq_f->qlf_id);
165*3581868fSDarrick J. Wong 		error = -EFSCORRUPTED;
166*3581868fSDarrick J. Wong 		goto out_release;
167*3581868fSDarrick J. Wong 	}
168*3581868fSDarrick J. Wong 
169fcbdf91eSDarrick J. Wong 	ASSERT(dq_f->qlf_size == 2);
170fcbdf91eSDarrick J. Wong 	ASSERT(bp->b_mount == mp);
1719fe5c77cSDave Chinner 	bp->b_flags |= _XBF_LOGRECOVERY;
172fcbdf91eSDarrick J. Wong 	xfs_buf_delwri_queue(bp, buffer_list);
173fcbdf91eSDarrick J. Wong 
174fcbdf91eSDarrick J. Wong out_release:
175fcbdf91eSDarrick J. Wong 	xfs_buf_relse(bp);
176fcbdf91eSDarrick J. Wong 	return 0;
177fcbdf91eSDarrick J. Wong }
178fcbdf91eSDarrick J. Wong 
17986ffa471SDarrick J. Wong const struct xlog_recover_item_ops xlog_dquot_item_ops = {
18086ffa471SDarrick J. Wong 	.item_type		= XFS_LI_DQUOT,
1818ea5682dSDarrick J. Wong 	.ra_pass2		= xlog_recover_dquot_ra_pass2,
182fcbdf91eSDarrick J. Wong 	.commit_pass2		= xlog_recover_dquot_commit_pass2,
18386ffa471SDarrick J. Wong };
18486ffa471SDarrick J. Wong 
1853304a4faSDarrick J. Wong /*
1863304a4faSDarrick J. Wong  * Recover QUOTAOFF records. We simply make a note of it in the xlog
1873304a4faSDarrick J. Wong  * structure, so that we know not to do any dquot item or dquot buffer recovery,
1883304a4faSDarrick J. Wong  * of that type.
1893304a4faSDarrick J. Wong  */
1903304a4faSDarrick J. Wong STATIC int
xlog_recover_quotaoff_commit_pass1(struct xlog * log,struct xlog_recover_item * item)1913304a4faSDarrick J. Wong xlog_recover_quotaoff_commit_pass1(
1923304a4faSDarrick J. Wong 	struct xlog			*log,
1933304a4faSDarrick J. Wong 	struct xlog_recover_item	*item)
1943304a4faSDarrick J. Wong {
1953304a4faSDarrick J. Wong 	struct xfs_qoff_logformat	*qoff_f = item->ri_buf[0].i_addr;
1963304a4faSDarrick J. Wong 	ASSERT(qoff_f);
1973304a4faSDarrick J. Wong 
1983304a4faSDarrick J. Wong 	/*
1993304a4faSDarrick J. Wong 	 * The logitem format's flag tells us if this was user quotaoff,
2003304a4faSDarrick J. Wong 	 * group/project quotaoff or both.
2013304a4faSDarrick J. Wong 	 */
2023304a4faSDarrick J. Wong 	if (qoff_f->qf_flags & XFS_UQUOTA_ACCT)
2038cd4901dSDarrick J. Wong 		log->l_quotaoffs_flag |= XFS_DQTYPE_USER;
2043304a4faSDarrick J. Wong 	if (qoff_f->qf_flags & XFS_PQUOTA_ACCT)
2058cd4901dSDarrick J. Wong 		log->l_quotaoffs_flag |= XFS_DQTYPE_PROJ;
2063304a4faSDarrick J. Wong 	if (qoff_f->qf_flags & XFS_GQUOTA_ACCT)
2078cd4901dSDarrick J. Wong 		log->l_quotaoffs_flag |= XFS_DQTYPE_GROUP;
2083304a4faSDarrick J. Wong 
2093304a4faSDarrick J. Wong 	return 0;
2103304a4faSDarrick J. Wong }
2113304a4faSDarrick J. Wong 
21286ffa471SDarrick J. Wong const struct xlog_recover_item_ops xlog_quotaoff_item_ops = {
21386ffa471SDarrick J. Wong 	.item_type		= XFS_LI_QUOTAOFF,
2143304a4faSDarrick J. Wong 	.commit_pass1		= xlog_recover_quotaoff_commit_pass1,
2152565a11bSDarrick J. Wong 	/* nothing to commit in pass2 */
21686ffa471SDarrick J. Wong };
217