mirror of
https://git.proxmox.com/git/mirror_zfs.git
synced 2026-04-17 08:54:52 +03:00
zdb: better handling for corrupt block pointers
When dumping indirect blocks, attempt to print corrupt block pointers rather than abort the program. When corruption is detected zdb will exit with an error code of 3. Sponsored by: ConnectWise Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov> Reviewed-by: Alexander Motin <alexander.motin@TrueNAS.com> Reviewed-by: Alek Pinchuk <alek.pinchuk@connectwise.com> Signed-off-by: Alan Somers <asomers@gmail.com> Closes #17166
This commit is contained in:
parent
f8bc01c79f
commit
d3c1d27afd
@ -127,6 +127,7 @@ static zfs_range_tree_t *mos_refd_objs;
|
|||||||
static spa_t *spa;
|
static spa_t *spa;
|
||||||
static objset_t *os;
|
static objset_t *os;
|
||||||
static boolean_t kernel_init_done;
|
static boolean_t kernel_init_done;
|
||||||
|
static boolean_t corruption_found = B_FALSE;
|
||||||
|
|
||||||
static void snprintf_blkptr_compact(char *, size_t, const blkptr_t *,
|
static void snprintf_blkptr_compact(char *, size_t, const blkptr_t *,
|
||||||
boolean_t);
|
boolean_t);
|
||||||
@ -250,6 +251,7 @@ sublivelist_verify_func(void *args, dsl_deadlist_entry_t *dle)
|
|||||||
&e->svbr_blk, B_TRUE);
|
&e->svbr_blk, B_TRUE);
|
||||||
(void) printf("\tERROR: %d unmatched FREE(s): %s\n",
|
(void) printf("\tERROR: %d unmatched FREE(s): %s\n",
|
||||||
e->svbr_refcnt, blkbuf);
|
e->svbr_refcnt, blkbuf);
|
||||||
|
corruption_found = B_TRUE;
|
||||||
}
|
}
|
||||||
zfs_btree_destroy(&sv->sv_pair);
|
zfs_btree_destroy(&sv->sv_pair);
|
||||||
|
|
||||||
@ -405,6 +407,7 @@ verify_livelist_allocs(metaslab_verify_t *mv, uint64_t txg,
|
|||||||
(u_longlong_t)DVA_GET_ASIZE(&found->svb_dva),
|
(u_longlong_t)DVA_GET_ASIZE(&found->svb_dva),
|
||||||
(u_longlong_t)found->svb_allocated_txg,
|
(u_longlong_t)found->svb_allocated_txg,
|
||||||
(u_longlong_t)txg);
|
(u_longlong_t)txg);
|
||||||
|
corruption_found = B_TRUE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -426,6 +429,7 @@ metaslab_spacemap_validation_cb(space_map_entry_t *sme, void *arg)
|
|||||||
(u_longlong_t)txg, (u_longlong_t)offset,
|
(u_longlong_t)txg, (u_longlong_t)offset,
|
||||||
(u_longlong_t)size, (u_longlong_t)mv->mv_vdid,
|
(u_longlong_t)size, (u_longlong_t)mv->mv_vdid,
|
||||||
(u_longlong_t)mv->mv_msid);
|
(u_longlong_t)mv->mv_msid);
|
||||||
|
corruption_found = B_TRUE;
|
||||||
} else {
|
} else {
|
||||||
zfs_range_tree_add(mv->mv_allocated,
|
zfs_range_tree_add(mv->mv_allocated,
|
||||||
offset, size);
|
offset, size);
|
||||||
@ -439,6 +443,7 @@ metaslab_spacemap_validation_cb(space_map_entry_t *sme, void *arg)
|
|||||||
(u_longlong_t)txg, (u_longlong_t)offset,
|
(u_longlong_t)txg, (u_longlong_t)offset,
|
||||||
(u_longlong_t)size, (u_longlong_t)mv->mv_vdid,
|
(u_longlong_t)size, (u_longlong_t)mv->mv_vdid,
|
||||||
(u_longlong_t)mv->mv_msid);
|
(u_longlong_t)mv->mv_msid);
|
||||||
|
corruption_found = B_TRUE;
|
||||||
} else {
|
} else {
|
||||||
zfs_range_tree_remove(mv->mv_allocated,
|
zfs_range_tree_remove(mv->mv_allocated,
|
||||||
offset, size);
|
offset, size);
|
||||||
@ -526,6 +531,7 @@ mv_populate_livelist_allocs(metaslab_verify_t *mv, sublivelist_verify_t *sv)
|
|||||||
(u_longlong_t)DVA_GET_VDEV(&svb->svb_dva),
|
(u_longlong_t)DVA_GET_VDEV(&svb->svb_dva),
|
||||||
(u_longlong_t)DVA_GET_OFFSET(&svb->svb_dva),
|
(u_longlong_t)DVA_GET_OFFSET(&svb->svb_dva),
|
||||||
(u_longlong_t)DVA_GET_ASIZE(&svb->svb_dva));
|
(u_longlong_t)DVA_GET_ASIZE(&svb->svb_dva));
|
||||||
|
corruption_found = B_TRUE;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -542,6 +548,7 @@ mv_populate_livelist_allocs(metaslab_verify_t *mv, sublivelist_verify_t *sv)
|
|||||||
(u_longlong_t)DVA_GET_VDEV(&svb->svb_dva),
|
(u_longlong_t)DVA_GET_VDEV(&svb->svb_dva),
|
||||||
(u_longlong_t)DVA_GET_OFFSET(&svb->svb_dva),
|
(u_longlong_t)DVA_GET_OFFSET(&svb->svb_dva),
|
||||||
(u_longlong_t)DVA_GET_ASIZE(&svb->svb_dva));
|
(u_longlong_t)DVA_GET_ASIZE(&svb->svb_dva));
|
||||||
|
corruption_found = B_TRUE;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -655,6 +662,7 @@ livelist_metaslab_validate(spa_t *spa)
|
|||||||
}
|
}
|
||||||
(void) printf("ERROR: Found livelist blocks marked as allocated "
|
(void) printf("ERROR: Found livelist blocks marked as allocated "
|
||||||
"for indirect vdevs:\n");
|
"for indirect vdevs:\n");
|
||||||
|
corruption_found = B_TRUE;
|
||||||
|
|
||||||
zfs_btree_index_t *where = NULL;
|
zfs_btree_index_t *where = NULL;
|
||||||
sublivelist_verify_block_t *svb;
|
sublivelist_verify_block_t *svb;
|
||||||
@ -827,7 +835,7 @@ usage(void)
|
|||||||
(void) fprintf(stderr, "Specify an option more than once (e.g. -bb) "
|
(void) fprintf(stderr, "Specify an option more than once (e.g. -bb) "
|
||||||
"to make only that option verbose\n");
|
"to make only that option verbose\n");
|
||||||
(void) fprintf(stderr, "Default is to dump everything non-verbosely\n");
|
(void) fprintf(stderr, "Default is to dump everything non-verbosely\n");
|
||||||
zdb_exit(1);
|
zdb_exit(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -2583,19 +2591,17 @@ snprintf_blkptr_compact(char *blkbuf, size_t buflen, const blkptr_t *bp,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static u_longlong_t
|
||||||
print_indirect(spa_t *spa, blkptr_t *bp, const zbookmark_phys_t *zb,
|
print_indirect(spa_t *spa, blkptr_t *bp, const zbookmark_phys_t *zb,
|
||||||
const dnode_phys_t *dnp)
|
const dnode_phys_t *dnp)
|
||||||
{
|
{
|
||||||
char blkbuf[BP_SPRINTF_LEN];
|
char blkbuf[BP_SPRINTF_LEN];
|
||||||
|
u_longlong_t offset;
|
||||||
int l;
|
int l;
|
||||||
|
|
||||||
if (!BP_IS_EMBEDDED(bp)) {
|
offset = (u_longlong_t)blkid2offset(dnp, bp, zb);
|
||||||
ASSERT3U(BP_GET_TYPE(bp), ==, dnp->dn_type);
|
|
||||||
ASSERT3U(BP_GET_LEVEL(bp), ==, zb->zb_level);
|
|
||||||
}
|
|
||||||
|
|
||||||
(void) printf("%16llx ", (u_longlong_t)blkid2offset(dnp, bp, zb));
|
(void) printf("%16llx ", offset);
|
||||||
|
|
||||||
ASSERT(zb->zb_level >= 0);
|
ASSERT(zb->zb_level >= 0);
|
||||||
|
|
||||||
@ -2610,19 +2616,38 @@ print_indirect(spa_t *spa, blkptr_t *bp, const zbookmark_phys_t *zb,
|
|||||||
snprintf_blkptr_compact(blkbuf, sizeof (blkbuf), bp, B_FALSE);
|
snprintf_blkptr_compact(blkbuf, sizeof (blkbuf), bp, B_FALSE);
|
||||||
if (dump_opt['Z'] && BP_GET_COMPRESS(bp) == ZIO_COMPRESS_ZSTD)
|
if (dump_opt['Z'] && BP_GET_COMPRESS(bp) == ZIO_COMPRESS_ZSTD)
|
||||||
snprintf_zstd_header(spa, blkbuf, sizeof (blkbuf), bp);
|
snprintf_zstd_header(spa, blkbuf, sizeof (blkbuf), bp);
|
||||||
(void) printf("%s\n", blkbuf);
|
(void) printf("%s", blkbuf);
|
||||||
|
|
||||||
|
if (!BP_IS_EMBEDDED(bp)) {
|
||||||
|
if (BP_GET_TYPE(bp) != dnp->dn_type) {
|
||||||
|
(void) printf(" (ERROR: Block pointer type "
|
||||||
|
"(%llu) does not match dnode type (%hhu))",
|
||||||
|
BP_GET_TYPE(bp), dnp->dn_type);
|
||||||
|
corruption_found = B_TRUE;
|
||||||
|
}
|
||||||
|
if (BP_GET_LEVEL(bp) != zb->zb_level) {
|
||||||
|
(void) printf(" (ERROR: Block pointer level "
|
||||||
|
"(%llu) does not match bookmark level (%ld))",
|
||||||
|
BP_GET_LEVEL(bp), zb->zb_level);
|
||||||
|
corruption_found = B_TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(void) printf("\n");
|
||||||
|
|
||||||
|
return (offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
visit_indirect(spa_t *spa, const dnode_phys_t *dnp,
|
visit_indirect(spa_t *spa, const dnode_phys_t *dnp,
|
||||||
blkptr_t *bp, const zbookmark_phys_t *zb)
|
blkptr_t *bp, const zbookmark_phys_t *zb)
|
||||||
{
|
{
|
||||||
|
u_longlong_t offset;
|
||||||
int err = 0;
|
int err = 0;
|
||||||
|
|
||||||
if (BP_GET_BIRTH(bp) == 0)
|
if (BP_GET_BIRTH(bp) == 0)
|
||||||
return (0);
|
return (0);
|
||||||
|
|
||||||
print_indirect(spa, bp, zb, dnp);
|
offset = print_indirect(spa, bp, zb, dnp);
|
||||||
|
|
||||||
if (BP_GET_LEVEL(bp) > 0 && !BP_IS_HOLE(bp)) {
|
if (BP_GET_LEVEL(bp) > 0 && !BP_IS_HOLE(bp)) {
|
||||||
arc_flags_t flags = ARC_FLAG_WAIT;
|
arc_flags_t flags = ARC_FLAG_WAIT;
|
||||||
@ -2652,8 +2677,15 @@ visit_indirect(spa_t *spa, const dnode_phys_t *dnp,
|
|||||||
break;
|
break;
|
||||||
fill += BP_GET_FILL(cbp);
|
fill += BP_GET_FILL(cbp);
|
||||||
}
|
}
|
||||||
if (!err)
|
if (!err) {
|
||||||
ASSERT3U(fill, ==, BP_GET_FILL(bp));
|
if (fill != BP_GET_FILL(bp)) {
|
||||||
|
(void) printf("%16llx: Block pointer "
|
||||||
|
"fill (%llu) does not match calculated "
|
||||||
|
"value (%lu)\n", offset, BP_GET_FILL(bp),
|
||||||
|
fill);
|
||||||
|
corruption_found = B_TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
arc_buf_destroy(buf, &buf);
|
arc_buf_destroy(buf, &buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2909,6 +2941,7 @@ dump_full_bpobj(bpobj_t *bpo, const char *name, int indent)
|
|||||||
(void) printf("ERROR %u while trying to open "
|
(void) printf("ERROR %u while trying to open "
|
||||||
"subobj id %llu\n",
|
"subobj id %llu\n",
|
||||||
error, (u_longlong_t)subobj);
|
error, (u_longlong_t)subobj);
|
||||||
|
corruption_found = B_TRUE;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
dump_full_bpobj(&subbpo, "subobj", indent + 1);
|
dump_full_bpobj(&subbpo, "subobj", indent + 1);
|
||||||
@ -3088,6 +3121,7 @@ bpobj_count_refd(bpobj_t *bpo)
|
|||||||
(void) printf("ERROR %u while trying to open "
|
(void) printf("ERROR %u while trying to open "
|
||||||
"subobj id %llu\n",
|
"subobj id %llu\n",
|
||||||
error, (u_longlong_t)subobj);
|
error, (u_longlong_t)subobj);
|
||||||
|
corruption_found = B_TRUE;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
bpobj_count_refd(&subbpo);
|
bpobj_count_refd(&subbpo);
|
||||||
@ -9634,7 +9668,7 @@ main(int argc, char **argv)
|
|||||||
} else if (objset_str && !zdb_numeric(objset_str + 1) &&
|
} else if (objset_str && !zdb_numeric(objset_str + 1) &&
|
||||||
dump_opt['N']) {
|
dump_opt['N']) {
|
||||||
printf("Supply a numeric objset ID with -N\n");
|
printf("Supply a numeric objset ID with -N\n");
|
||||||
error = 1;
|
error = 2;
|
||||||
goto fini;
|
goto fini;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -9936,5 +9970,8 @@ fini:
|
|||||||
if (kernel_init_done)
|
if (kernel_init_done)
|
||||||
kernel_fini();
|
kernel_fini();
|
||||||
|
|
||||||
|
if (corruption_found && error == 0)
|
||||||
|
error = 3;
|
||||||
|
|
||||||
return (error);
|
return (error);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,7 +15,7 @@
|
|||||||
.\" Copyright (c) 2017 Lawrence Livermore National Security, LLC.
|
.\" Copyright (c) 2017 Lawrence Livermore National Security, LLC.
|
||||||
.\" Copyright (c) 2017 Intel Corporation.
|
.\" Copyright (c) 2017 Intel Corporation.
|
||||||
.\"
|
.\"
|
||||||
.Dd October 27, 2024
|
.Dd April 23, 2025
|
||||||
.Dt ZDB 8
|
.Dt ZDB 8
|
||||||
.Os
|
.Os
|
||||||
.
|
.
|
||||||
@ -531,6 +531,18 @@ option, with more occurrences enabling more verbosity.
|
|||||||
If no options are specified, all information about the named pool will be
|
If no options are specified, all information about the named pool will be
|
||||||
displayed at default verbosity.
|
displayed at default verbosity.
|
||||||
.
|
.
|
||||||
|
.Sh EXIT STATUS
|
||||||
|
The
|
||||||
|
.Nm
|
||||||
|
utility exits
|
||||||
|
.Sy 0
|
||||||
|
on success,
|
||||||
|
.Sy 1
|
||||||
|
if a fatal error occurs,
|
||||||
|
.Sy 2
|
||||||
|
if invalid command line options were specified, or
|
||||||
|
.Sy 3
|
||||||
|
if on-disk corruption was detected, but was not fatal.
|
||||||
.Sh EXAMPLES
|
.Sh EXAMPLES
|
||||||
.Ss Example 1 : No Display the configuration of imported pool Ar rpool
|
.Ss Example 1 : No Display the configuration of imported pool Ar rpool
|
||||||
.Bd -literal
|
.Bd -literal
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user