From ca57794a005286948ccc7f9f5ec9bafb3fab8aa7 Mon Sep 17 00:00:00 2001 From: Auke Kok Date: Tue, 26 Mar 2024 18:26:33 -0400 Subject: [PATCH] Generic block header checks: crc, magic. Generally as we call block_get() we should validate that if the block has a hdr, at a minimum the crc is correct and the magic value is the expected value passed, and the fsid matches the superblock. This function implements just that. Returns -EINVAL, up to the caller to report a problem() and handle the outcome. For now the code just hard fails, which incedentally makes it fail the clobber-repair.sh tests I wrote. Signed-off-by: Auke Kok --- utils/src/check/alloc.c | 7 +++++++ utils/src/check/block.c | 42 +++++++++++++++++++++++++++++++++++++++++ utils/src/check/block.h | 2 ++ utils/src/check/btree.c | 8 ++++++++ utils/src/check/super.c | 8 ++++++++ 5 files changed, 67 insertions(+) diff --git a/utils/src/check/alloc.c b/utils/src/check/alloc.c index f67b6660..43d1d125 100644 --- a/utils/src/check/alloc.c +++ b/utils/src/check/alloc.c @@ -50,6 +50,10 @@ int alloc_list_meta_iter(struct scoutfs_alloc_list_head *lhead, extent_cb_t cb, lblk = block_buf(blk); /* XXX verify block */ + ret = block_hdr_valid(blk, blkno, 0, SCOUTFS_BLOCK_MAGIC_ALLOC_LIST); + if (ret < 0) + goto out; + /* XXX sort? maybe */ ref = lblk->next; @@ -89,6 +93,9 @@ int alloc_list_extent_iter(struct scoutfs_alloc_list_head *lhead, extent_cb_t cb lblk = block_buf(blk); /* XXX verify block */ + ret = block_hdr_valid(blk, blkno, 0, SCOUTFS_BLOCK_MAGIC_ALLOC_LIST); + if (ret < 0) + goto out; /* XXX sort? maybe */ ret = 0; diff --git a/utils/src/check/block.c b/utils/src/check/block.c index 53b6eed0..13eeb867 100644 --- a/utils/src/check/block.c +++ b/utils/src/check/block.c @@ -17,7 +17,10 @@ #include "block.h" #include "debug.h" +#include "super.h" #include "eno.h" +#include "crc.h" +#include "sns.h" static struct block_data { struct list_head *hash_lists; @@ -297,6 +300,45 @@ static struct list_head *hash_bucket(struct block_data *bdat, u64 blkno) return &bdat->hash_lists[hash % bdat->hash_nr]; } +int block_hdr_valid(struct block *blk, u64 blkno, int bf, u32 magic) +{ + struct scoutfs_block_header *hdr; + size_t size = (bf & BF_SM) ? SCOUTFS_BLOCK_SM_SIZE : SCOUTFS_BLOCK_LG_SIZE; + int ret; + u32 crc; + + ret = block_get(&blk, blkno, bf); + if (ret < 0) { + fprintf(stderr, "error reading block %llu\n", blkno); + goto out; + } + + hdr = block_buf(blk); + + crc = crc_block(hdr, size); + + if ((le32_to_cpu(hdr->crc) != crc) || + (le32_to_cpu(hdr->magic) != magic)) + ret = -EINVAL; + + /* + * Our first caller fills in global_super. Until this completes, + * we can't do this check. + */ + if ((blkno != SCOUTFS_SUPER_BLKNO) && + (hdr->fsid != global_super->hdr.fsid)) + ret = -EINVAL; + + block_put(&blk); + + debug("%s blk_hdr_valid blkno %llu size %lu crc 0x%08x magic 0x%08x ret %d", + sns_str(), blkno, size, le32_to_cpu(hdr->crc), le32_to_cpu(hdr->magic), + ret); + +out: + return ret; +} + static struct block *get_or_alloc(struct block_data *bdat, u64 blkno, int bf) { struct list_head *bucket = hash_bucket(bdat, blkno); diff --git a/utils/src/check/block.h b/utils/src/check/block.h index ad7195ce..6c13b0cc 100644 --- a/utils/src/check/block.h +++ b/utils/src/check/block.h @@ -29,4 +29,6 @@ int block_try_commit(bool force); int block_setup(int meta_fd, size_t max_cached_bytes, size_t max_dirty_bytes); void block_shutdown(void); +int block_hdr_valid(struct block *blk, u64 blkno, int bf, u32 magic); + #endif diff --git a/utils/src/check/btree.c b/utils/src/check/btree.c index 50bd1fa2..ebf05b8c 100644 --- a/utils/src/check/btree.c +++ b/utils/src/check/btree.c @@ -88,6 +88,10 @@ static int btree_ref_meta_iter(struct scoutfs_block_ref *ref, unsigned level, ex if (ret < 0) return ret; + ret = block_hdr_valid(blk, blkno, 0, SCOUTFS_BLOCK_MAGIC_BTREE); + if (ret < 0) + return ret; + sns_push("btree_parent", blkno, 0); bt = block_buf(blk); @@ -157,6 +161,10 @@ static int btree_ref_item_iter(struct scoutfs_block_ref *ref, unsigned level, else sns_push("btree_leaf", blkno, 0); + ret = block_hdr_valid(blk, blkno, 0, SCOUTFS_BLOCK_MAGIC_BTREE); + if (ret < 0) + return ret; + bt = block_buf(blk); /* XXX integrate verification with block cache */ diff --git a/utils/src/check/super.c b/utils/src/check/super.c index 9c2f078d..40f815ea 100644 --- a/utils/src/check/super.c +++ b/utils/src/check/super.c @@ -28,6 +28,8 @@ int check_supers(void) struct block *blk = NULL; int ret; + sns_push("supers", 0, 0); + global_super = malloc(sizeof(struct scoutfs_super_block)); if (!global_super) { fprintf(stderr, "error allocating super block buffer\n"); @@ -41,6 +43,10 @@ int check_supers(void) goto out; } + ret = block_hdr_valid(blk, SCOUTFS_SUPER_BLKNO, BF_SM, SCOUTFS_BLOCK_MAGIC_SUPER); + if (ret < 0) + return ret; + super = block_buf(blk); memcpy(global_super, super, sizeof(struct scoutfs_super_block)); @@ -48,6 +54,8 @@ int check_supers(void) out: block_put(&blk); + sns_pop(); + return ret; }