diff options
author | Christoph Hellwig <hch@lst.de> | 2013-04-03 01:11:16 -0400 |
---|---|---|
committer | Ben Myers <bpm@sgi.com> | 2013-04-21 15:58:22 -0400 |
commit | 3fe58f30b4fc3f8a9084b035a02bc0c67bee8d00 (patch) | |
tree | 4b46222d26f3e74d65c6741ddcaed7eaa48948eb /fs | |
parent | 983d09ffe396ed5d5339a1b9ff994dd0b0f2069f (diff) |
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>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/xfs/xfs_dquot.c | 112 | ||||
-rw-r--r-- | fs/xfs/xfs_log_recover.c | 10 | ||||
-rw-r--r-- | fs/xfs/xfs_qm.c | 23 | ||||
-rw-r--r-- | fs/xfs/xfs_qm.h | 2 | ||||
-rw-r--r-- | fs/xfs/xfs_quota.h | 11 |
5 files changed, 141 insertions, 17 deletions
diff --git a/fs/xfs/xfs_dquot.c b/fs/xfs/xfs_dquot.c index 45bb08f6ba17..a41f8bf1da37 100644 --- a/fs/xfs/xfs_dquot.c +++ b/fs/xfs/xfs_dquot.c | |||
@@ -36,6 +36,7 @@ | |||
36 | #include "xfs_trans_space.h" | 36 | #include "xfs_trans_space.h" |
37 | #include "xfs_trans_priv.h" | 37 | #include "xfs_trans_priv.h" |
38 | #include "xfs_qm.h" | 38 | #include "xfs_qm.h" |
39 | #include "xfs_cksum.h" | ||
39 | #include "xfs_trace.h" | 40 | #include "xfs_trace.h" |
40 | 41 | ||
41 | /* | 42 | /* |
@@ -248,6 +249,8 @@ xfs_qm_init_dquot_blk( | |||
248 | d->dd_diskdq.d_version = XFS_DQUOT_VERSION; | 249 | d->dd_diskdq.d_version = XFS_DQUOT_VERSION; |
249 | d->dd_diskdq.d_id = cpu_to_be32(curid); | 250 | d->dd_diskdq.d_id = cpu_to_be32(curid); |
250 | d->dd_diskdq.d_flags = type; | 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 | xfs_trans_dquot_buf(tp, bp, | 256 | xfs_trans_dquot_buf(tp, bp, |
@@ -283,25 +286,87 @@ xfs_dquot_set_prealloc_limits(struct xfs_dquot *dqp) | |||
283 | dqp->q_low_space[XFS_QLOWSP_5_PCNT] = space * 5; | 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 | xfs_dquot_buf_verify( | 341 | xfs_dquot_buf_verify( |
342 | struct xfs_mount *mp, | ||
288 | struct xfs_buf *bp) | 343 | struct xfs_buf *bp) |
289 | { | 344 | { |
290 | struct xfs_mount *mp = bp->b_target->bt_mount; | ||
291 | struct xfs_dqblk *d = (struct xfs_dqblk *)bp->b_addr; | 345 | struct xfs_dqblk *d = (struct xfs_dqblk *)bp->b_addr; |
292 | struct xfs_disk_dquot *ddq; | ||
293 | xfs_dqid_t id = 0; | 346 | xfs_dqid_t id = 0; |
347 | int ndquots; | ||
294 | int i; | 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 | * On the first read of the buffer, verify that each dquot is valid. | 361 | * On the first read of the buffer, verify that each dquot is valid. |
298 | * We don't know what the id of the dquot is supposed to be, just that | 362 | * We don't know what the id of the dquot is supposed to be, just that |
299 | * they should be increasing monotonically within the buffer. If the | 363 | * they should be increasing monotonically within the buffer. If the |
300 | * first id is corrupt, then it will fail on the second dquot in the | 364 | * first id is corrupt, then it will fail on the second dquot in the |
301 | * buffer so corruptions could point to the wrong dquot in this case. | 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++) { | 367 | for (i = 0; i < ndquots; i++) { |
304 | int error; | 368 | struct xfs_disk_dquot *ddq; |
369 | int error; | ||
305 | 370 | ||
306 | ddq = &d[i].dd_diskdq; | 371 | ddq = &d[i].dd_diskdq; |
307 | 372 | ||
@@ -309,27 +374,37 @@ xfs_dquot_buf_verify( | |||
309 | id = be32_to_cpu(ddq->d_id); | 374 | id = be32_to_cpu(ddq->d_id); |
310 | 375 | ||
311 | error = xfs_qm_dqcheck(mp, ddq, id + i, 0, XFS_QMOPT_DOWARN, | 376 | error = xfs_qm_dqcheck(mp, ddq, id + i, 0, XFS_QMOPT_DOWARN, |
312 | "xfs_dquot_read_verify"); | 377 | "xfs_dquot_buf_verify"); |
313 | if (error) { | 378 | if (error) |
314 | XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp, d); | 379 | return false; |
315 | xfs_buf_ioerror(bp, EFSCORRUPTED); | ||
316 | break; | ||
317 | } | ||
318 | } | 380 | } |
381 | return true; | ||
319 | } | 382 | } |
320 | 383 | ||
321 | static void | 384 | static void |
322 | xfs_dquot_buf_read_verify( | 385 | xfs_dquot_buf_read_verify( |
323 | struct xfs_buf *bp) | 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 | void | 396 | void |
329 | xfs_dquot_buf_write_verify( | 397 | xfs_dquot_buf_write_verify( |
330 | struct xfs_buf *bp) | 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 | const struct xfs_buf_ops xfs_dquot_buf_ops = { | 410 | const struct xfs_buf_ops xfs_dquot_buf_ops = { |
@@ -1073,6 +1148,17 @@ xfs_qm_dqflush( | |||
1073 | &dqp->q_logitem.qli_item.li_lsn); | 1148 | &dqp->q_logitem.qli_item.li_lsn); |
1074 | 1149 | ||
1075 | /* | 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 | } | ||
1160 | |||
1161 | /* | ||
1076 | * Attach an iodone routine so that we can remove this dquot from the | 1162 | * Attach an iodone routine so that we can remove this dquot from the |
1077 | * AIL and release the flush lock once the dquot is synced to disk. | 1163 | * AIL and release the flush lock once the dquot is synced to disk. |
1078 | */ | 1164 | */ |
diff --git a/fs/xfs/xfs_log_recover.c b/fs/xfs/xfs_log_recover.c index 6778a7943db4..27b3ec214a67 100644 --- a/fs/xfs/xfs_log_recover.c +++ b/fs/xfs/xfs_log_recover.c | |||
@@ -1979,6 +1979,16 @@ xlog_recover_do_reg_buffer( | |||
1979 | } | 1979 | } |
1980 | bp->b_ops = &xfs_agi_buf_ops; | 1980 | bp->b_ops = &xfs_agi_buf_ops; |
1981 | break; | 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 | default: | 1992 | default: |
1983 | break; | 1993 | break; |
1984 | } | 1994 | } |
diff --git a/fs/xfs/xfs_qm.c b/fs/xfs/xfs_qm.c index d0acb4e40a6f..f41702b43003 100644 --- a/fs/xfs/xfs_qm.c +++ b/fs/xfs/xfs_qm.c | |||
@@ -617,6 +617,20 @@ xfs_qm_dqdetach( | |||
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 | * This initializes all the quota information that's kept in the | 635 | * This initializes all the quota information that's kept in the |
622 | * mount structure | 636 | * mount structure |
@@ -656,9 +670,8 @@ xfs_qm_init_quotainfo( | |||
656 | 670 | ||
657 | /* Precalc some constants */ | 671 | /* Precalc some constants */ |
658 | qinf->qi_dqchunklen = XFS_FSB_TO_BB(mp, XFS_DQUOT_CLUSTER_SIZE_FSB); | 672 | qinf->qi_dqchunklen = XFS_FSB_TO_BB(mp, XFS_DQUOT_CLUSTER_SIZE_FSB); |
659 | ASSERT(qinf->qi_dqchunklen); | 673 | qinf->qi_dqperchunk = xfs_qm_calc_dquots_per_chunk(mp, |
660 | qinf->qi_dqperchunk = BBTOB(qinf->qi_dqchunklen); | 674 | qinf->qi_dqchunklen); |
661 | do_div(qinf->qi_dqperchunk, sizeof(xfs_dqblk_t)); | ||
662 | 675 | ||
663 | mp->m_qflags |= (mp->m_sb.sb_qflags & XFS_ALL_QUOTA_CHKD); | 676 | mp->m_qflags |= (mp->m_sb.sb_qflags & XFS_ALL_QUOTA_CHKD); |
664 | 677 | ||
@@ -897,6 +910,10 @@ xfs_qm_dqiter_bufs( | |||
897 | if (error) | 910 | if (error) |
898 | break; | 911 | break; |
899 | 912 | ||
913 | /* | ||
914 | * XXX(hch): need to figure out if it makes sense to validate | ||
915 | * the CRC here. | ||
916 | */ | ||
900 | xfs_qm_reset_dqcounts(mp, bp, firstid, type); | 917 | xfs_qm_reset_dqcounts(mp, bp, firstid, type); |
901 | xfs_buf_delwri_queue(bp, buffer_list); | 918 | xfs_buf_delwri_queue(bp, buffer_list); |
902 | xfs_buf_relse(bp); | 919 | xfs_buf_relse(bp); |
diff --git a/fs/xfs/xfs_qm.h b/fs/xfs/xfs_qm.h index 82c21084587b..5d16a6e6900f 100644 --- a/fs/xfs/xfs_qm.h +++ b/fs/xfs/xfs_qm.h | |||
@@ -75,6 +75,8 @@ typedef struct xfs_quotainfo { | |||
75 | &((qi)->qi_gquota_tree)) | 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 | extern void xfs_trans_mod_dquot(xfs_trans_t *, xfs_dquot_t *, uint, long); | 80 | extern void xfs_trans_mod_dquot(xfs_trans_t *, xfs_dquot_t *, uint, long); |
79 | extern int xfs_trans_reserve_quota_bydquots(xfs_trans_t *, xfs_mount_t *, | 81 | extern int xfs_trans_reserve_quota_bydquots(xfs_trans_t *, xfs_mount_t *, |
80 | xfs_dquot_t *, xfs_dquot_t *, long, long, uint); | 82 | xfs_dquot_t *, xfs_dquot_t *, long, long, uint); |
diff --git a/fs/xfs/xfs_quota.h b/fs/xfs/xfs_quota.h index b50ec5b95d5a..c61e31c7d997 100644 --- a/fs/xfs/xfs_quota.h +++ b/fs/xfs/xfs_quota.h | |||
@@ -77,7 +77,14 @@ typedef struct xfs_disk_dquot { | |||
77 | */ | 77 | */ |
78 | typedef struct xfs_dqblk { | 78 | typedef struct xfs_dqblk { |
79 | xfs_disk_dquot_t dd_diskdq; /* portion that lives incore as well */ | 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 | } xfs_dqblk_t; | 88 | } xfs_dqblk_t; |
82 | 89 | ||
83 | /* | 90 | /* |
@@ -380,5 +387,7 @@ extern int xfs_qm_dqcheck(struct xfs_mount *, xfs_disk_dquot_t *, | |||
380 | xfs_dqid_t, uint, uint, char *); | 387 | xfs_dqid_t, uint, uint, char *); |
381 | extern int xfs_mount_reset_sbqflags(struct xfs_mount *); | 388 | extern int xfs_mount_reset_sbqflags(struct xfs_mount *); |
382 | 389 | ||
390 | extern const struct xfs_buf_ops xfs_dquot_buf_ops; | ||
391 | |||
383 | #endif /* __KERNEL__ */ | 392 | #endif /* __KERNEL__ */ |
384 | #endif /* __XFS_QUOTA_H__ */ | 393 | #endif /* __XFS_QUOTA_H__ */ |