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 | |