]>
Commit | Line | Data |
---|---|---|
86ffa471 DW |
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" | |
9c235dfc | 22 | #include "xfs_error.h" |
86ffa471 | 23 | |
8ea5682d DW |
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 | ||
d8c1af0d | 43 | type = recddq->d_type & XFS_DQTYPE_REC_MASK; |
8ea5682d DW |
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 | ||
fcbdf91e DW |
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; | |
ed17f7da | 69 | struct xfs_dqblk *dqb; |
fcbdf91e DW |
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 | */ | |
d8c1af0d | 96 | type = recddq->d_type & XFS_DQTYPE_REC_MASK; |
fcbdf91e DW |
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); | |
f9751c4a | 113 | fa = xfs_dquot_verify(mp, recddq, dq_f->qlf_id); |
fcbdf91e DW |
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); | |
ed17f7da DW |
135 | dqb = xfs_buf_offset(bp, dq_f->qlf_boffset); |
136 | ddq = &dqb->dd_diskdq; | |
fcbdf91e DW |
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 | */ | |
38c26bfd | 142 | if (xfs_has_crc(mp)) { |
fcbdf91e DW |
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); | |
38c26bfd | 151 | if (xfs_has_crc(mp)) { |
ed17f7da | 152 | xfs_update_cksum((char *)dqb, sizeof(struct xfs_dqblk), |
fcbdf91e DW |
153 | XFS_DQUOT_CRC_OFF); |
154 | } | |
155 | ||
9c235dfc DW |
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 | ||
fcbdf91e DW |
169 | ASSERT(dq_f->qlf_size == 2); |
170 | ASSERT(bp->b_mount == mp); | |
9fe5c77c | 171 | bp->b_flags |= _XBF_LOGRECOVERY; |
fcbdf91e DW |
172 | xfs_buf_delwri_queue(bp, buffer_list); |
173 | ||
174 | out_release: | |
175 | xfs_buf_relse(bp); | |
176 | return 0; | |
177 | } | |
178 | ||
86ffa471 DW |
179 | const struct xlog_recover_item_ops xlog_dquot_item_ops = { |
180 | .item_type = XFS_LI_DQUOT, | |
8ea5682d | 181 | .ra_pass2 = xlog_recover_dquot_ra_pass2, |
fcbdf91e | 182 | .commit_pass2 = xlog_recover_dquot_commit_pass2, |
86ffa471 DW |
183 | }; |
184 | ||
3304a4fa DW |
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) | |
8cd4901d | 203 | log->l_quotaoffs_flag |= XFS_DQTYPE_USER; |
3304a4fa | 204 | if (qoff_f->qf_flags & XFS_PQUOTA_ACCT) |
8cd4901d | 205 | log->l_quotaoffs_flag |= XFS_DQTYPE_PROJ; |
3304a4fa | 206 | if (qoff_f->qf_flags & XFS_GQUOTA_ACCT) |
8cd4901d | 207 | log->l_quotaoffs_flag |= XFS_DQTYPE_GROUP; |
3304a4fa DW |
208 | |
209 | return 0; | |
210 | } | |
211 | ||
86ffa471 DW |
212 | const struct xlog_recover_item_ops xlog_quotaoff_item_ops = { |
213 | .item_type = XFS_LI_QUOTAOFF, | |
3304a4fa | 214 | .commit_pass1 = xlog_recover_quotaoff_commit_pass1, |
2565a11b | 215 | /* nothing to commit in pass2 */ |
86ffa471 | 216 | }; |