aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorChristoph Hellwig <hch@lst.de>2013-04-03 01:11:16 -0400
committerBen Myers <bpm@sgi.com>2013-04-21 15:58:22 -0400
commit3fe58f30b4fc3f8a9084b035a02bc0c67bee8d00 (patch)
tree4b46222d26f3e74d65c6741ddcaed7eaa48948eb /fs
parent983d09ffe396ed5d5339a1b9ff994dd0b0f2069f (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.c112
-rw-r--r--fs/xfs/xfs_log_recover.c10
-rw-r--r--fs/xfs/xfs_qm.c23
-rw-r--r--fs/xfs/xfs_qm.h2
-rw-r--r--fs/xfs/xfs_quota.h11
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
286static void 289STATIC void
290xfs_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
306STATIC bool
307xfs_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
340STATIC bool
287xfs_dquot_buf_verify( 341xfs_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
321static void 384static void
322xfs_dquot_buf_read_verify( 385xfs_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
328void 396void
329xfs_dquot_buf_write_verify( 397xfs_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
335const struct xfs_buf_ops xfs_dquot_buf_ops = { 410const 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
620int
621xfs_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
78extern int xfs_qm_calc_dquots_per_chunk(struct xfs_mount *mp,
79 unsigned int nbblks);
78extern void xfs_trans_mod_dquot(xfs_trans_t *, xfs_dquot_t *, uint, long); 80extern void xfs_trans_mod_dquot(xfs_trans_t *, xfs_dquot_t *, uint, long);
79extern int xfs_trans_reserve_quota_bydquots(xfs_trans_t *, xfs_mount_t *, 81extern 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 */
78typedef struct xfs_dqblk { 78typedef 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 *);
381extern int xfs_mount_reset_sbqflags(struct xfs_mount *); 388extern int xfs_mount_reset_sbqflags(struct xfs_mount *);
382 389
390extern 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__ */