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