Commit 3fe58f30b4fc3f8a9084b035a02bc0c67bee8d00
Committed by
Ben Myers
1 parent
983d09ffe3
Exists in
smarc-l5.0.0_1.0.0-ga
and in
5 other branches
xfs: add CRC checks for quota blocks
Use the reserved space in struct xfs_dqblk to store a UUID and a crc for the quota blocks. [dchinner@redhat.com] Add a LSN field and update for current verifier infrastructure. Signed-off-by: Christoph Hellwig <hch@lst.de> Signed-off-by: Dave Chinner <dchinner@redhat.com> Reviewed-by: Ben Myers <bpm@sgi.com> Signed-off-by: Ben Myers <bpm@sgi.com>
Showing 5 changed files with 141 additions and 17 deletions Side-by-side Diff
fs/xfs/xfs_dquot.c
... | ... | @@ -36,6 +36,7 @@ |
36 | 36 | #include "xfs_trans_space.h" |
37 | 37 | #include "xfs_trans_priv.h" |
38 | 38 | #include "xfs_qm.h" |
39 | +#include "xfs_cksum.h" | |
39 | 40 | #include "xfs_trace.h" |
40 | 41 | |
41 | 42 | /* |
... | ... | @@ -248,6 +249,8 @@ |
248 | 249 | d->dd_diskdq.d_version = XFS_DQUOT_VERSION; |
249 | 250 | d->dd_diskdq.d_id = cpu_to_be32(curid); |
250 | 251 | d->dd_diskdq.d_flags = type; |
252 | + if (xfs_sb_version_hascrc(&mp->m_sb)) | |
253 | + uuid_copy(&d->dd_uuid, &mp->m_sb.sb_uuid); | |
251 | 254 | } |
252 | 255 | |
253 | 256 | xfs_trans_dquot_buf(tp, bp, |
254 | 257 | |
255 | 258 | |
256 | 259 | |
257 | 260 | |
258 | 261 | |
259 | 262 | |
... | ... | @@ -283,25 +286,87 @@ |
283 | 286 | dqp->q_low_space[XFS_QLOWSP_5_PCNT] = space * 5; |
284 | 287 | } |
285 | 288 | |
286 | -static void | |
289 | +STATIC void | |
290 | +xfs_dquot_buf_calc_crc( | |
291 | + struct xfs_mount *mp, | |
292 | + struct xfs_buf *bp) | |
293 | +{ | |
294 | + struct xfs_dqblk *d = (struct xfs_dqblk *)bp->b_addr; | |
295 | + int i; | |
296 | + | |
297 | + if (!xfs_sb_version_hascrc(&mp->m_sb)) | |
298 | + return; | |
299 | + | |
300 | + for (i = 0; i < mp->m_quotainfo->qi_dqperchunk; i++, d++) { | |
301 | + xfs_update_cksum((char *)d, sizeof(struct xfs_dqblk), | |
302 | + offsetof(struct xfs_dqblk, dd_crc)); | |
303 | + } | |
304 | +} | |
305 | + | |
306 | +STATIC bool | |
307 | +xfs_dquot_buf_verify_crc( | |
308 | + struct xfs_mount *mp, | |
309 | + struct xfs_buf *bp) | |
310 | +{ | |
311 | + struct xfs_dqblk *d = (struct xfs_dqblk *)bp->b_addr; | |
312 | + int ndquots; | |
313 | + int i; | |
314 | + | |
315 | + if (!xfs_sb_version_hascrc(&mp->m_sb)) | |
316 | + return true; | |
317 | + | |
318 | + /* | |
319 | + * if we are in log recovery, the quota subsystem has not been | |
320 | + * initialised so we have no quotainfo structure. In that case, we need | |
321 | + * to manually calculate the number of dquots in the buffer. | |
322 | + */ | |
323 | + if (mp->m_quotainfo) | |
324 | + ndquots = mp->m_quotainfo->qi_dqperchunk; | |
325 | + else | |
326 | + ndquots = xfs_qm_calc_dquots_per_chunk(mp, | |
327 | + XFS_BB_TO_FSB(mp, bp->b_length)); | |
328 | + | |
329 | + for (i = 0; i < ndquots; i++, d++) { | |
330 | + if (!xfs_verify_cksum((char *)d, sizeof(struct xfs_dqblk), | |
331 | + offsetof(struct xfs_dqblk, dd_crc))) | |
332 | + return false; | |
333 | + if (!uuid_equal(&d->dd_uuid, &mp->m_sb.sb_uuid)) | |
334 | + return false; | |
335 | + } | |
336 | + | |
337 | + return true; | |
338 | +} | |
339 | + | |
340 | +STATIC bool | |
287 | 341 | xfs_dquot_buf_verify( |
342 | + struct xfs_mount *mp, | |
288 | 343 | struct xfs_buf *bp) |
289 | 344 | { |
290 | - struct xfs_mount *mp = bp->b_target->bt_mount; | |
291 | 345 | struct xfs_dqblk *d = (struct xfs_dqblk *)bp->b_addr; |
292 | - struct xfs_disk_dquot *ddq; | |
293 | 346 | xfs_dqid_t id = 0; |
347 | + int ndquots; | |
294 | 348 | int i; |
295 | 349 | |
296 | 350 | /* |
351 | + * if we are in log recovery, the quota subsystem has not been | |
352 | + * initialised so we have no quotainfo structure. In that case, we need | |
353 | + * to manually calculate the number of dquots in the buffer. | |
354 | + */ | |
355 | + if (mp->m_quotainfo) | |
356 | + ndquots = mp->m_quotainfo->qi_dqperchunk; | |
357 | + else | |
358 | + ndquots = xfs_qm_calc_dquots_per_chunk(mp, bp->b_length); | |
359 | + | |
360 | + /* | |
297 | 361 | * On the first read of the buffer, verify that each dquot is valid. |
298 | 362 | * We don't know what the id of the dquot is supposed to be, just that |
299 | 363 | * they should be increasing monotonically within the buffer. If the |
300 | 364 | * first id is corrupt, then it will fail on the second dquot in the |
301 | 365 | * buffer so corruptions could point to the wrong dquot in this case. |
302 | 366 | */ |
303 | - for (i = 0; i < mp->m_quotainfo->qi_dqperchunk; i++) { | |
304 | - int error; | |
367 | + for (i = 0; i < ndquots; i++) { | |
368 | + struct xfs_disk_dquot *ddq; | |
369 | + int error; | |
305 | 370 | |
306 | 371 | ddq = &d[i].dd_diskdq; |
307 | 372 | |
308 | 373 | |
309 | 374 | |
310 | 375 | |
... | ... | @@ -309,27 +374,37 @@ |
309 | 374 | id = be32_to_cpu(ddq->d_id); |
310 | 375 | |
311 | 376 | error = xfs_qm_dqcheck(mp, ddq, id + i, 0, XFS_QMOPT_DOWARN, |
312 | - "xfs_dquot_read_verify"); | |
313 | - if (error) { | |
314 | - XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp, d); | |
315 | - xfs_buf_ioerror(bp, EFSCORRUPTED); | |
316 | - break; | |
317 | - } | |
377 | + "xfs_dquot_buf_verify"); | |
378 | + if (error) | |
379 | + return false; | |
318 | 380 | } |
381 | + return true; | |
319 | 382 | } |
320 | 383 | |
321 | 384 | static void |
322 | 385 | xfs_dquot_buf_read_verify( |
323 | 386 | struct xfs_buf *bp) |
324 | 387 | { |
325 | - xfs_dquot_buf_verify(bp); | |
388 | + struct xfs_mount *mp = bp->b_target->bt_mount; | |
389 | + | |
390 | + if (!xfs_dquot_buf_verify_crc(mp, bp) || !xfs_dquot_buf_verify(mp, bp)) { | |
391 | + XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp, bp->b_addr); | |
392 | + xfs_buf_ioerror(bp, EFSCORRUPTED); | |
393 | + } | |
326 | 394 | } |
327 | 395 | |
328 | 396 | void |
329 | 397 | xfs_dquot_buf_write_verify( |
330 | 398 | struct xfs_buf *bp) |
331 | 399 | { |
332 | - xfs_dquot_buf_verify(bp); | |
400 | + struct xfs_mount *mp = bp->b_target->bt_mount; | |
401 | + | |
402 | + if (!xfs_dquot_buf_verify(mp, bp)) { | |
403 | + XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp, bp->b_addr); | |
404 | + xfs_buf_ioerror(bp, EFSCORRUPTED); | |
405 | + return; | |
406 | + } | |
407 | + xfs_dquot_buf_calc_crc(mp, bp); | |
333 | 408 | } |
334 | 409 | |
335 | 410 | const struct xfs_buf_ops xfs_dquot_buf_ops = { |
... | ... | @@ -1071,6 +1146,17 @@ |
1071 | 1146 | |
1072 | 1147 | xfs_trans_ail_copy_lsn(mp->m_ail, &dqp->q_logitem.qli_flush_lsn, |
1073 | 1148 | &dqp->q_logitem.qli_item.li_lsn); |
1149 | + | |
1150 | + /* | |
1151 | + * copy the lsn into the on-disk dquot now while we have the in memory | |
1152 | + * dquot here. This can't be done later in the write verifier as we | |
1153 | + * can't get access to the log item at that point in time. | |
1154 | + */ | |
1155 | + if (xfs_sb_version_hascrc(&mp->m_sb)) { | |
1156 | + struct xfs_dqblk *dqb = (struct xfs_dqblk *)ddqp; | |
1157 | + | |
1158 | + dqb->dd_lsn = cpu_to_be64(dqp->q_logitem.qli_item.li_lsn); | |
1159 | + } | |
1074 | 1160 | |
1075 | 1161 | /* |
1076 | 1162 | * Attach an iodone routine so that we can remove this dquot from the |
fs/xfs/xfs_log_recover.c
... | ... | @@ -1979,6 +1979,16 @@ |
1979 | 1979 | } |
1980 | 1980 | bp->b_ops = &xfs_agi_buf_ops; |
1981 | 1981 | break; |
1982 | + case XFS_BLF_UDQUOT_BUF: | |
1983 | + case XFS_BLF_PDQUOT_BUF: | |
1984 | + case XFS_BLF_GDQUOT_BUF: | |
1985 | + if (*(__be16 *)bp->b_addr != cpu_to_be16(XFS_DQUOT_MAGIC)) { | |
1986 | + xfs_warn(mp, "Bad DQUOT block magic!"); | |
1987 | + ASSERT(0); | |
1988 | + break; | |
1989 | + } | |
1990 | + bp->b_ops = &xfs_dquot_buf_ops; | |
1991 | + break; | |
1982 | 1992 | default: |
1983 | 1993 | break; |
1984 | 1994 | } |
fs/xfs/xfs_qm.c
... | ... | @@ -617,6 +617,20 @@ |
617 | 617 | } |
618 | 618 | } |
619 | 619 | |
620 | +int | |
621 | +xfs_qm_calc_dquots_per_chunk( | |
622 | + struct xfs_mount *mp, | |
623 | + unsigned int nbblks) /* basic block units */ | |
624 | +{ | |
625 | + unsigned int ndquots; | |
626 | + | |
627 | + ASSERT(nbblks > 0); | |
628 | + ndquots = BBTOB(nbblks); | |
629 | + do_div(ndquots, sizeof(xfs_dqblk_t)); | |
630 | + | |
631 | + return ndquots; | |
632 | +} | |
633 | + | |
620 | 634 | /* |
621 | 635 | * This initializes all the quota information that's kept in the |
622 | 636 | * mount structure |
... | ... | @@ -656,9 +670,8 @@ |
656 | 670 | |
657 | 671 | /* Precalc some constants */ |
658 | 672 | qinf->qi_dqchunklen = XFS_FSB_TO_BB(mp, XFS_DQUOT_CLUSTER_SIZE_FSB); |
659 | - ASSERT(qinf->qi_dqchunklen); | |
660 | - qinf->qi_dqperchunk = BBTOB(qinf->qi_dqchunklen); | |
661 | - do_div(qinf->qi_dqperchunk, sizeof(xfs_dqblk_t)); | |
673 | + qinf->qi_dqperchunk = xfs_qm_calc_dquots_per_chunk(mp, | |
674 | + qinf->qi_dqchunklen); | |
662 | 675 | |
663 | 676 | mp->m_qflags |= (mp->m_sb.sb_qflags & XFS_ALL_QUOTA_CHKD); |
664 | 677 | |
... | ... | @@ -897,6 +910,10 @@ |
897 | 910 | if (error) |
898 | 911 | break; |
899 | 912 | |
913 | + /* | |
914 | + * XXX(hch): need to figure out if it makes sense to validate | |
915 | + * the CRC here. | |
916 | + */ | |
900 | 917 | xfs_qm_reset_dqcounts(mp, bp, firstid, type); |
901 | 918 | xfs_buf_delwri_queue(bp, buffer_list); |
902 | 919 | xfs_buf_relse(bp); |
fs/xfs/xfs_qm.h
... | ... | @@ -75,6 +75,8 @@ |
75 | 75 | &((qi)->qi_gquota_tree)) |
76 | 76 | |
77 | 77 | |
78 | +extern int xfs_qm_calc_dquots_per_chunk(struct xfs_mount *mp, | |
79 | + unsigned int nbblks); | |
78 | 80 | extern void xfs_trans_mod_dquot(xfs_trans_t *, xfs_dquot_t *, uint, long); |
79 | 81 | extern int xfs_trans_reserve_quota_bydquots(xfs_trans_t *, xfs_mount_t *, |
80 | 82 | xfs_dquot_t *, xfs_dquot_t *, long, long, uint); |
fs/xfs/xfs_quota.h
... | ... | @@ -77,7 +77,14 @@ |
77 | 77 | */ |
78 | 78 | typedef struct xfs_dqblk { |
79 | 79 | xfs_disk_dquot_t dd_diskdq; /* portion that lives incore as well */ |
80 | - char dd_fill[32]; /* filling for posterity */ | |
80 | + char dd_fill[4]; /* filling for posterity */ | |
81 | + | |
82 | + /* | |
83 | + * These two are only present on filesystems with the CRC bits set. | |
84 | + */ | |
85 | + __be32 dd_crc; /* checksum */ | |
86 | + __be64 dd_lsn; /* last modification in log */ | |
87 | + uuid_t dd_uuid; /* location information */ | |
81 | 88 | } xfs_dqblk_t; |
82 | 89 | |
83 | 90 | /* |
... | ... | @@ -379,6 +386,8 @@ |
379 | 386 | extern int xfs_qm_dqcheck(struct xfs_mount *, xfs_disk_dquot_t *, |
380 | 387 | xfs_dqid_t, uint, uint, char *); |
381 | 388 | extern int xfs_mount_reset_sbqflags(struct xfs_mount *); |
389 | + | |
390 | +extern const struct xfs_buf_ops xfs_dquot_buf_ops; | |
382 | 391 | |
383 | 392 | #endif /* __KERNEL__ */ |
384 | 393 | #endif /* __XFS_QUOTA_H__ */ |