aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDarrick J. Wong <darrick.wong@oracle.com>2018-06-03 19:10:13 -0400
committerDarrick J. Wong <darrick.wong@oracle.com>2018-06-04 21:25:04 -0400
commite4f45eff86fdbafd8e0ba072fd52422e32e5b5ac (patch)
tree1377fb33c226a3c230e1f51e638fcf6edb8c089c
parent924cade4df49e7c9fddcbae678dbd9dee3b0aeb6 (diff)
xfs: check directory bestfree information in the verifier
Create a variant of xfs_dir2_data_freefind that is suitable for use in a verifier. Because _freefind is called by the verifier, we simply duplicate the _freefind function, convert the ASSERTs to return __this_address, and modify the verifier to call our new function. Once we've made it impossible for directory blocks with bad bestfree data to make it into the filesystem we can remove the DEBUG code from the regular _freefind function. Underlying argument: corruption of on-disk metadata should return -EFSCORRUPTED instead of blowing ASSERTs. Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com> Reviewed-by: Dave Chinner <dchinner@redhat.com>
-rw-r--r--fs/xfs/libxfs/xfs_dir2_data.c103
1 files changed, 68 insertions, 35 deletions
diff --git a/fs/xfs/libxfs/xfs_dir2_data.c b/fs/xfs/libxfs/xfs_dir2_data.c
index cb67ec730b9b..2c16bb4f2155 100644
--- a/fs/xfs/libxfs/xfs_dir2_data.c
+++ b/fs/xfs/libxfs/xfs_dir2_data.c
@@ -33,6 +33,11 @@
33#include "xfs_cksum.h" 33#include "xfs_cksum.h"
34#include "xfs_log.h" 34#include "xfs_log.h"
35 35
36static xfs_failaddr_t xfs_dir2_data_freefind_verify(
37 struct xfs_dir2_data_hdr *hdr, struct xfs_dir2_data_free *bf,
38 struct xfs_dir2_data_unused *dup,
39 struct xfs_dir2_data_free **bf_ent);
40
36/* 41/*
37 * Check the consistency of the data block. 42 * Check the consistency of the data block.
38 * The input can also be a block-format directory. 43 * The input can also be a block-format directory.
@@ -147,6 +152,8 @@ __xfs_dir3_data_check(
147 * doesn't need to be there. 152 * doesn't need to be there.
148 */ 153 */
149 if (be16_to_cpu(dup->freetag) == XFS_DIR2_DATA_FREE_TAG) { 154 if (be16_to_cpu(dup->freetag) == XFS_DIR2_DATA_FREE_TAG) {
155 xfs_failaddr_t fa;
156
150 if (lastfree != 0) 157 if (lastfree != 0)
151 return __this_address; 158 return __this_address;
152 if (endp < p + be16_to_cpu(dup->length)) 159 if (endp < p + be16_to_cpu(dup->length))
@@ -154,7 +161,9 @@ __xfs_dir3_data_check(
154 if (be16_to_cpu(*xfs_dir2_data_unused_tag_p(dup)) != 161 if (be16_to_cpu(*xfs_dir2_data_unused_tag_p(dup)) !=
155 (char *)dup - (char *)hdr) 162 (char *)dup - (char *)hdr)
156 return __this_address; 163 return __this_address;
157 dfp = xfs_dir2_data_freefind(hdr, bf, dup); 164 fa = xfs_dir2_data_freefind_verify(hdr, bf, dup, &dfp);
165 if (fa)
166 return fa;
158 if (dfp) { 167 if (dfp) {
159 i = (int)(dfp - bf); 168 i = (int)(dfp - bf);
160 if ((freeseen & (1 << i)) != 0) 169 if ((freeseen & (1 << i)) != 0)
@@ -381,55 +390,79 @@ xfs_dir3_data_readahead(
381} 390}
382 391
383/* 392/*
384 * Given a data block and an unused entry from that block, 393 * Find the bestfree entry that exactly coincides with unused directory space
385 * return the bestfree entry if any that corresponds to it. 394 * or a verifier error because the bestfree data are bad.
386 */ 395 */
387xfs_dir2_data_free_t * 396static xfs_failaddr_t
388xfs_dir2_data_freefind( 397xfs_dir2_data_freefind_verify(
389 struct xfs_dir2_data_hdr *hdr, /* data block header */ 398 struct xfs_dir2_data_hdr *hdr,
390 struct xfs_dir2_data_free *bf, /* bestfree table pointer */ 399 struct xfs_dir2_data_free *bf,
391 struct xfs_dir2_data_unused *dup) /* unused space */ 400 struct xfs_dir2_data_unused *dup,
401 struct xfs_dir2_data_free **bf_ent)
392{ 402{
393 xfs_dir2_data_free_t *dfp; /* bestfree entry */ 403 struct xfs_dir2_data_free *dfp;
394 xfs_dir2_data_aoff_t off; /* offset value needed */ 404 xfs_dir2_data_aoff_t off;
395#ifdef DEBUG 405 bool matched = false;
396 int matched; /* matched the value */ 406 bool seenzero = false;
397 int seenzero; /* saw a 0 bestfree entry */
398#endif
399 407
408 *bf_ent = NULL;
400 off = (xfs_dir2_data_aoff_t)((char *)dup - (char *)hdr); 409 off = (xfs_dir2_data_aoff_t)((char *)dup - (char *)hdr);
401 410
402#ifdef DEBUG
403 /* 411 /*
404 * Validate some consistency in the bestfree table. 412 * Validate some consistency in the bestfree table.
405 * Check order, non-overlapping entries, and if we find the 413 * Check order, non-overlapping entries, and if we find the
406 * one we're looking for it has to be exact. 414 * one we're looking for it has to be exact.
407 */ 415 */
408 ASSERT(hdr->magic == cpu_to_be32(XFS_DIR2_DATA_MAGIC) || 416 for (dfp = &bf[0]; dfp < &bf[XFS_DIR2_DATA_FD_COUNT]; dfp++) {
409 hdr->magic == cpu_to_be32(XFS_DIR3_DATA_MAGIC) ||
410 hdr->magic == cpu_to_be32(XFS_DIR2_BLOCK_MAGIC) ||
411 hdr->magic == cpu_to_be32(XFS_DIR3_BLOCK_MAGIC));
412 for (dfp = &bf[0], seenzero = matched = 0;
413 dfp < &bf[XFS_DIR2_DATA_FD_COUNT];
414 dfp++) {
415 if (!dfp->offset) { 417 if (!dfp->offset) {
416 ASSERT(!dfp->length); 418 if (dfp->length)
417 seenzero = 1; 419 return __this_address;
420 seenzero = true;
418 continue; 421 continue;
419 } 422 }
420 ASSERT(seenzero == 0); 423 if (seenzero)
424 return __this_address;
421 if (be16_to_cpu(dfp->offset) == off) { 425 if (be16_to_cpu(dfp->offset) == off) {
422 matched = 1; 426 matched = true;
423 ASSERT(dfp->length == dup->length); 427 if (dfp->length != dup->length)
424 } else if (off < be16_to_cpu(dfp->offset)) 428 return __this_address;
425 ASSERT(off + be16_to_cpu(dup->length) <= be16_to_cpu(dfp->offset)); 429 } else if (be16_to_cpu(dfp->offset) > off) {
426 else 430 if (off + be16_to_cpu(dup->length) >
427 ASSERT(be16_to_cpu(dfp->offset) + be16_to_cpu(dfp->length) <= off); 431 be16_to_cpu(dfp->offset))
428 ASSERT(matched || be16_to_cpu(dfp->length) >= be16_to_cpu(dup->length)); 432 return __this_address;
429 if (dfp > &bf[0]) 433 } else {
430 ASSERT(be16_to_cpu(dfp[-1].length) >= be16_to_cpu(dfp[0].length)); 434 if (be16_to_cpu(dfp->offset) +
435 be16_to_cpu(dfp->length) > off)
436 return __this_address;
437 }
438 if (!matched &&
439 be16_to_cpu(dfp->length) < be16_to_cpu(dup->length))
440 return __this_address;
441 if (dfp > &bf[0] &&
442 be16_to_cpu(dfp[-1].length) < be16_to_cpu(dfp[0].length))
443 return __this_address;
431 } 444 }
432#endif 445
446 /* Looks ok so far; now try to match up with a bestfree entry. */
447 *bf_ent = xfs_dir2_data_freefind(hdr, bf, dup);
448 return NULL;
449}
450
451/*
452 * Given a data block and an unused entry from that block,
453 * return the bestfree entry if any that corresponds to it.
454 */
455xfs_dir2_data_free_t *
456xfs_dir2_data_freefind(
457 struct xfs_dir2_data_hdr *hdr, /* data block header */
458 struct xfs_dir2_data_free *bf, /* bestfree table pointer */
459 struct xfs_dir2_data_unused *dup) /* unused space */
460{
461 xfs_dir2_data_free_t *dfp; /* bestfree entry */
462 xfs_dir2_data_aoff_t off; /* offset value needed */
463
464 off = (xfs_dir2_data_aoff_t)((char *)dup - (char *)hdr);
465
433 /* 466 /*
434 * If this is smaller than the smallest bestfree entry, 467 * If this is smaller than the smallest bestfree entry,
435 * it can't be there since they're sorted. 468 * it can't be there since they're sorted.