diff options
author | pjd <pjd@FreeBSD.org> | 2008-11-17 20:49:29 +0000 |
---|---|---|
committer | pjd <pjd@FreeBSD.org> | 2008-11-17 20:49:29 +0000 |
commit | bbe899b96e388a8b82439f81ed3707e0d9c6070d (patch) | |
tree | 81b89fa4ac6467771d5aa291a97f4665981a6108 /cddl/contrib/opensolaris/cmd/zdb/zdb.c | |
parent | d2f579595c362ce27b4d87e2c40e1c4e09b929e3 (diff) | |
download | FreeBSD-src-bbe899b96e388a8b82439f81ed3707e0d9c6070d.zip FreeBSD-src-bbe899b96e388a8b82439f81ed3707e0d9c6070d.tar.gz |
Update ZFS from version 6 to 13 and bring some FreeBSD-specific changes.
This bring huge amount of changes, I'll enumerate only user-visible changes:
- Delegated Administration
Allows regular users to perform ZFS operations, like file system
creation, snapshot creation, etc.
- L2ARC
Level 2 cache for ZFS - allows to use additional disks for cache.
Huge performance improvements mostly for random read of mostly
static content.
- slog
Allow to use additional disks for ZFS Intent Log to speed up
operations like fsync(2).
- vfs.zfs.super_owner
Allows regular users to perform privileged operations on files stored
on ZFS file systems owned by him. Very careful with this one.
- chflags(2)
Not all the flags are supported. This still needs work.
- ZFSBoot
Support to boot off of ZFS pool. Not finished, AFAIK.
Submitted by: dfr
- Snapshot properties
- New failure modes
Before if write requested failed, system paniced. Now one
can select from one of three failure modes:
- panic - panic on write error
- wait - wait for disk to reappear
- continue - serve read requests if possible, block write requests
- Refquota, refreservation properties
Just quota and reservation properties, but don't count space consumed
by children file systems, clones and snapshots.
- Sparse volumes
ZVOLs that don't reserve space in the pool.
- External attributes
Compatible with extattr(2).
- NFSv4-ACLs
Not sure about the status, might not be complete yet.
Submitted by: trasz
- Creation-time properties
- Regression tests for zpool(8) command.
Obtained from: OpenSolaris
Diffstat (limited to 'cddl/contrib/opensolaris/cmd/zdb/zdb.c')
-rw-r--r-- | cddl/contrib/opensolaris/cmd/zdb/zdb.c | 829 |
1 files changed, 576 insertions, 253 deletions
diff --git a/cddl/contrib/opensolaris/cmd/zdb/zdb.c b/cddl/contrib/opensolaris/cmd/zdb/zdb.c index 2dc459d..16b2787 100644 --- a/cddl/contrib/opensolaris/cmd/zdb/zdb.c +++ b/cddl/contrib/opensolaris/cmd/zdb/zdb.c @@ -19,12 +19,10 @@ * CDDL HEADER END */ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ -#pragma ident "%Z%%M% %I% %E% SMI" - #include <stdio.h> #include <stdio_ext.h> #include <stdlib.h> @@ -51,6 +49,10 @@ #include <sys/dmu_traverse.h> #include <sys/zio_checksum.h> #include <sys/zio_compress.h> +#include <sys/zfs_fuid.h> +#undef ZFS_MAXNAMELEN +#undef verify +#include <libzfs.h> const char cmdname[] = "zdb"; uint8_t dump_opt[256]; @@ -62,6 +64,9 @@ uint64_t *zopt_object = NULL; int zopt_objects = 0; int zdb_advance = ADVANCE_PRE; zbookmark_t zdb_noread = { 0, 0, ZB_NO_LEVEL, 0 }; +libzfs_handle_t *g_zfs; +boolean_t zdb_sig_user_data = B_TRUE; +int zdb_sig_cksumalg = ZIO_CHECKSUM_SHA256; /* * These libumem hooks provide a reasonable set of defaults for the allocator's @@ -83,12 +88,15 @@ static void usage(void) { (void) fprintf(stderr, - "Usage: %s [-udibcsvLU] [-O order] [-B os:obj:level:blkid] " + "Usage: %s [-udibcsvL] [-U cachefile_path] [-O order] " + "[-B os:obj:level:blkid] [-S user:cksumalg] " "dataset [object...]\n" " %s -C [pool]\n" " %s -l dev\n" - " %s -R vdev:offset:size:flags\n", - cmdname, cmdname, cmdname, cmdname); + " %s -R pool:vdev:offset:size:flags\n" + " %s [-p path_to_vdev_dir]\n" + " %s -e pool | GUID | devid ...\n", + cmdname, cmdname, cmdname, cmdname, cmdname, cmdname); (void) fprintf(stderr, " -u uberblock\n"); (void) fprintf(stderr, " -d datasets\n"); @@ -97,16 +105,22 @@ usage(void) (void) fprintf(stderr, " -b block statistics\n"); (void) fprintf(stderr, " -c checksum all data blocks\n"); (void) fprintf(stderr, " -s report stats on zdb's I/O\n"); + (void) fprintf(stderr, " -S <user|all>:<cksum_alg|all> -- " + "dump blkptr signatures\n"); (void) fprintf(stderr, " -v verbose (applies to all others)\n"); (void) fprintf(stderr, " -l dump label contents\n"); (void) fprintf(stderr, " -L live pool (allows some errors)\n"); (void) fprintf(stderr, " -O [!]<pre|post|prune|data|holes> " "visitation order\n"); - (void) fprintf(stderr, " -U use zpool.cache in /tmp\n"); + (void) fprintf(stderr, " -U cachefile_path -- use alternate " + "cachefile\n"); (void) fprintf(stderr, " -B objset:object:level:blkid -- " "simulate bad block\n"); - (void) fprintf(stderr, " -R read and display block from a" + (void) fprintf(stderr, " -R read and display block from a " "device\n"); + (void) fprintf(stderr, " -e Pool is exported/destroyed/" + "has altroot\n"); + (void) fprintf(stderr, " -p <Path to vdev dir> (use with -e)\n"); (void) fprintf(stderr, "Specify an option more than once (e.g. -bb) " "to make only that option verbose\n"); (void) fprintf(stderr, "Default is to dump everything non-verbosely\n"); @@ -367,6 +381,44 @@ dump_zap(objset_t *os, uint64_t object, void *data, size_t size) zap_cursor_fini(&zc); } +/*ARGSUSED*/ +static void +dump_zpldir(objset_t *os, uint64_t object, void *data, size_t size) +{ + zap_cursor_t zc; + zap_attribute_t attr; + const char *typenames[] = { + /* 0 */ "not specified", + /* 1 */ "FIFO", + /* 2 */ "Character Device", + /* 3 */ "3 (invalid)", + /* 4 */ "Directory", + /* 5 */ "5 (invalid)", + /* 6 */ "Block Device", + /* 7 */ "7 (invalid)", + /* 8 */ "Regular File", + /* 9 */ "9 (invalid)", + /* 10 */ "Symbolic Link", + /* 11 */ "11 (invalid)", + /* 12 */ "Socket", + /* 13 */ "Door", + /* 14 */ "Event Port", + /* 15 */ "15 (invalid)", + }; + + dump_zap_stats(os, object); + (void) printf("\n"); + + for (zap_cursor_init(&zc, os, object); + zap_cursor_retrieve(&zc, &attr) == 0; + zap_cursor_advance(&zc)) { + (void) printf("\t\t%s = %lld (type: %s)\n", + attr.za_name, ZFS_DIRENT_OBJ(attr.za_first_integer), + typenames[ZFS_DIRENT_TYPE(attr.za_first_integer)]); + } + zap_cursor_fini(&zc); +} + static void dump_spacemap(objset_t *os, space_map_obj_t *smo, space_map_t *sm) { @@ -456,10 +508,7 @@ dump_metaslabs(spa_t *spa) for (c = 0; c < rvd->vdev_children; c++) { vd = rvd->vdev_child[c]; - spa_config_enter(spa, RW_READER, FTAG); - (void) printf("\n vdev %llu = %s\n\n", - (u_longlong_t)vd->vdev_id, vdev_description(vd)); - spa_config_exit(spa, FTAG); + (void) printf("\n vdev %llu\n\n", (u_longlong_t)vd->vdev_id); if (dump_opt['d'] <= 5) { (void) printf("\t%10s %10s %5s\n", @@ -477,7 +526,6 @@ static void dump_dtl(vdev_t *vd, int indent) { avl_tree_t *t = &vd->vdev_dtl_map.sm_root; - spa_t *spa = vd->vdev_spa; space_seg_t *ss; vdev_t *pvd; int c; @@ -485,9 +533,10 @@ dump_dtl(vdev_t *vd, int indent) if (indent == 0) (void) printf("\nDirty time logs:\n\n"); - spa_config_enter(spa, RW_READER, FTAG); - (void) printf("\t%*s%s\n", indent, "", vdev_description(vd)); - spa_config_exit(spa, FTAG); + (void) printf("\t%*s%s\n", indent, "", + vd->vdev_path ? vd->vdev_path : + vd->vdev_parent ? vd->vdev_ops->vdev_op_type : + spa_name(vd->vdev_spa)); for (ss = avl_first(t); ss; ss = AVL_NEXT(t, ss)) { /* @@ -670,36 +719,49 @@ dump_dsl_dir(objset_t *os, uint64_t object, void *data, size_t size) { dsl_dir_phys_t *dd = data; time_t crtime; - char used[6], compressed[6], uncompressed[6], quota[6], resv[6]; + char nice[6]; if (dd == NULL) return; - ASSERT(size == sizeof (*dd)); + ASSERT3U(size, >=, sizeof (dsl_dir_phys_t)); crtime = dd->dd_creation_time; - nicenum(dd->dd_used_bytes, used); - nicenum(dd->dd_compressed_bytes, compressed); - nicenum(dd->dd_uncompressed_bytes, uncompressed); - nicenum(dd->dd_quota, quota); - nicenum(dd->dd_reserved, resv); - (void) printf("\t\tcreation_time = %s", ctime(&crtime)); (void) printf("\t\thead_dataset_obj = %llu\n", (u_longlong_t)dd->dd_head_dataset_obj); (void) printf("\t\tparent_dir_obj = %llu\n", (u_longlong_t)dd->dd_parent_obj); - (void) printf("\t\tclone_parent_obj = %llu\n", - (u_longlong_t)dd->dd_clone_parent_obj); + (void) printf("\t\torigin_obj = %llu\n", + (u_longlong_t)dd->dd_origin_obj); (void) printf("\t\tchild_dir_zapobj = %llu\n", (u_longlong_t)dd->dd_child_dir_zapobj); - (void) printf("\t\tused_bytes = %s\n", used); - (void) printf("\t\tcompressed_bytes = %s\n", compressed); - (void) printf("\t\tuncompressed_bytes = %s\n", uncompressed); - (void) printf("\t\tquota = %s\n", quota); - (void) printf("\t\treserved = %s\n", resv); + nicenum(dd->dd_used_bytes, nice); + (void) printf("\t\tused_bytes = %s\n", nice); + nicenum(dd->dd_compressed_bytes, nice); + (void) printf("\t\tcompressed_bytes = %s\n", nice); + nicenum(dd->dd_uncompressed_bytes, nice); + (void) printf("\t\tuncompressed_bytes = %s\n", nice); + nicenum(dd->dd_quota, nice); + (void) printf("\t\tquota = %s\n", nice); + nicenum(dd->dd_reserved, nice); + (void) printf("\t\treserved = %s\n", nice); (void) printf("\t\tprops_zapobj = %llu\n", (u_longlong_t)dd->dd_props_zapobj); + (void) printf("\t\tdeleg_zapobj = %llu\n", + (u_longlong_t)dd->dd_deleg_zapobj); + (void) printf("\t\tflags = %llx\n", + (u_longlong_t)dd->dd_flags); + +#define DO(which) \ + nicenum(dd->dd_used_breakdown[DD_USED_ ## which], nice); \ + (void) printf("\t\tused_breakdown[" #which "] = %s\n", nice) + DO(HEAD); + DO(SNAP); + DO(CHILD); + DO(CHILD_RSRV); + DO(REFRSRV); +#undef DO } /*ARGSUSED*/ @@ -722,7 +784,7 @@ dump_dsl_dataset(objset_t *os, uint64_t object, void *data, size_t size) nicenum(ds->ds_unique_bytes, unique); sprintf_blkptr(blkbuf, BP_SPRINTF_LEN, &ds->ds_bp); - (void) printf("\t\tdataset_obj = %llu\n", + (void) printf("\t\tdir_obj = %llu\n", (u_longlong_t)ds->ds_dir_obj); (void) printf("\t\tprev_snap_obj = %llu\n", (u_longlong_t)ds->ds_prev_snap_obj); @@ -749,6 +811,10 @@ dump_dsl_dataset(objset_t *os, uint64_t object, void *data, size_t size) (u_longlong_t)ds->ds_guid); (void) printf("\t\tflags = %llx\n", (u_longlong_t)ds->ds_flags); + (void) printf("\t\tnext_clones_obj = %llu\n", + (u_longlong_t)ds->ds_next_clones_obj); + (void) printf("\t\tprops_obj = %llu\n", + (u_longlong_t)ds->ds_props_obj); (void) printf("\t\tbp = %s\n", blkbuf); } @@ -765,9 +831,11 @@ dump_bplist(objset_t *mos, uint64_t object, char *name) if (dump_opt['d'] < 3) return; + mutex_init(&bpl.bpl_lock, NULL, MUTEX_DEFAULT, NULL); VERIFY(0 == bplist_open(&bpl, mos, object)); if (bplist_empty(&bpl)) { bplist_close(&bpl); + mutex_destroy(&bpl.bpl_lock); return; } @@ -785,6 +853,7 @@ dump_bplist(objset_t *mos, uint64_t object, char *name) if (dump_opt['d'] < 5) { bplist_close(&bpl); + mutex_destroy(&bpl.bpl_lock); return; } @@ -800,6 +869,65 @@ dump_bplist(objset_t *mos, uint64_t object, char *name) } bplist_close(&bpl); + mutex_destroy(&bpl.bpl_lock); +} + +static avl_tree_t idx_tree; +static avl_tree_t domain_tree; +static boolean_t fuid_table_loaded; + +static void +fuid_table_destroy() +{ + if (fuid_table_loaded) { + zfs_fuid_table_destroy(&idx_tree, &domain_tree); + fuid_table_loaded = B_FALSE; + } +} + +/* + * print uid or gid information. + * For normal POSIX id just the id is printed in decimal format. + * For CIFS files with FUID the fuid is printed in hex followed by + * the doman-rid string. + */ +static void +print_idstr(uint64_t id, const char *id_type) +{ + if (FUID_INDEX(id)) { + char *domain; + + domain = zfs_fuid_idx_domain(&idx_tree, FUID_INDEX(id)); + (void) printf("\t%s %llx [%s-%d]\n", id_type, + (u_longlong_t)id, domain, (int)FUID_RID(id)); + } else { + (void) printf("\t%s %llu\n", id_type, (u_longlong_t)id); + } + +} + +static void +dump_uidgid(objset_t *os, znode_phys_t *zp) +{ + uint32_t uid_idx, gid_idx; + + uid_idx = FUID_INDEX(zp->zp_uid); + gid_idx = FUID_INDEX(zp->zp_gid); + + /* Load domain table, if not already loaded */ + if (!fuid_table_loaded && (uid_idx || gid_idx)) { + uint64_t fuid_obj; + + /* first find the fuid object. It lives in the master node */ + VERIFY(zap_lookup(os, MASTER_NODE_OBJ, ZFS_FUID_TABLES, + 8, 1, &fuid_obj) == 0); + (void) zfs_fuid_table_load(os, fuid_obj, + &idx_tree, &domain_tree); + fuid_table_loaded = B_TRUE; + } + + print_idstr(zp->zp_uid, "uid"); + print_idstr(zp->zp_gid, "gid"); } /*ARGSUSED*/ @@ -830,6 +958,7 @@ dump_znode(objset_t *os, uint64_t object, void *data, size_t size) z_ctime = (time_t)zp->zp_ctime[0]; (void) printf("\tpath %s\n", path); + dump_uidgid(os, zp); (void) printf("\tatime %s", ctime(&z_atime)); (void) printf("\tmtime %s", ctime(&z_mtime)); (void) printf("\tctime %s", ctime(&z_ctime)); @@ -874,9 +1003,9 @@ static object_viewer_t *object_viewer[DMU_OT_NUMTYPES] = { dump_zap, /* DSL props */ dump_dsl_dataset, /* DSL dataset */ dump_znode, /* ZFS znode */ - dump_acl, /* ZFS ACL */ + dump_acl, /* ZFS V0 ACL */ dump_uint8, /* ZFS plain file */ - dump_zap, /* ZFS directory */ + dump_zpldir, /* ZFS directory */ dump_zap, /* ZFS master node */ dump_zap, /* ZFS delete queue */ dump_uint8, /* zvol object */ @@ -888,6 +1017,13 @@ static object_viewer_t *object_viewer[DMU_OT_NUMTYPES] = { dump_uint8, /* SPA history */ dump_uint64, /* SPA history offsets */ dump_zap, /* Pool properties */ + dump_zap, /* DSL permissions */ + dump_acl, /* ZFS ACL */ + dump_uint8, /* ZFS SYSACL */ + dump_none, /* FUID nvlist */ + dump_packed_nvlist, /* FUID nvlist size */ + dump_zap, /* DSL dataset next clones */ + dump_zap, /* DSL scrub queue */ }; static void @@ -930,13 +1066,15 @@ dump_object(objset_t *os, uint64_t object, int verbosity, int *print_header) aux[0] = '\0'; - if (doi.doi_checksum != ZIO_CHECKSUM_INHERIT || verbosity >= 6) + if (doi.doi_checksum != ZIO_CHECKSUM_INHERIT || verbosity >= 6) { (void) snprintf(aux + strlen(aux), sizeof (aux), " (K=%s)", - zio_checksum_table[doi.doi_checksum].ci_name); + zio_checksum_table[doi.doi_checksum].ci_name); + } - if (doi.doi_compress != ZIO_COMPRESS_INHERIT || verbosity >= 6) + if (doi.doi_compress != ZIO_COMPRESS_INHERIT || verbosity >= 6) { (void) snprintf(aux + strlen(aux), sizeof (aux), " (Z=%s)", - zio_compress_table[doi.doi_compress].ci_name); + zio_compress_table[doi.doi_compress].ci_name); + } (void) printf("%10lld %3u %5s %5s %5s %5s %s%s\n", (u_longlong_t)object, doi.doi_indirection, iblk, dblk, lsize, @@ -972,13 +1110,13 @@ dump_object(objset_t *os, uint64_t object, int verbosity, int *print_header) } for (;;) { - error = dnode_next_offset(dn, B_FALSE, &start, minlvl, - blkfill, 0); + error = dnode_next_offset(dn, + 0, &start, minlvl, blkfill, 0); if (error) break; end = start; - error = dnode_next_offset(dn, B_TRUE, &end, minlvl, - blkfill, 0); + error = dnode_next_offset(dn, + DNODE_FIND_HOLE, &end, minlvl, blkfill, 0); nicenum(end - start, segsize); (void) printf("\t\tsegment [%016llx, %016llx)" " size %5s\n", (u_longlong_t)start, @@ -996,7 +1134,6 @@ dump_object(objset_t *os, uint64_t object, int verbosity, int *print_header) static char *objset_types[DMU_OST_NUMTYPES] = { "NONE", "META", "ZPL", "ZVOL", "OTHER", "ANY" }; -/*ARGSUSED*/ static void dump_dir(objset_t *os) { @@ -1019,8 +1156,8 @@ dump_dir(objset_t *os) if (dds.dds_type == DMU_OST_META) { dds.dds_creation_txg = TXG_INITIAL; usedobjs = os->os->os_rootbp->blk_fill; - refdbytes = - os->os->os_spa->spa_dsl_pool->dp_mos_dir->dd_used_bytes; + refdbytes = os->os->os_spa->spa_dsl_pool-> + dp_mos_dir->dd_phys->dd_used_bytes; } else { dmu_objset_space(os, &refdbytes, &scratch, &usedobjs, &scratch); } @@ -1054,6 +1191,9 @@ dump_dir(objset_t *os) if (verbosity < 2) return; + if (os->os->os_rootbp->blk_birth == 0) + return; + if (zopt_objects != 0) { for (i = 0; i < zopt_objects; i++) dump_object(os, zopt_object[i], verbosity, @@ -1115,6 +1255,52 @@ dump_config(const char *pool) } static void +dump_cachefile(const char *cachefile) +{ + int fd; + struct stat64 statbuf; + char *buf; + nvlist_t *config; + + if ((fd = open64(cachefile, O_RDONLY)) < 0) { + (void) printf("cannot open '%s': %s\n", cachefile, + strerror(errno)); + exit(1); + } + + if (fstat64(fd, &statbuf) != 0) { + (void) printf("failed to stat '%s': %s\n", cachefile, + strerror(errno)); + exit(1); + } + + if ((buf = malloc(statbuf.st_size)) == NULL) { + (void) fprintf(stderr, "failed to allocate %llu bytes\n", + (u_longlong_t)statbuf.st_size); + exit(1); + } + + if (read(fd, buf, statbuf.st_size) != statbuf.st_size) { + (void) fprintf(stderr, "failed to read %llu bytes\n", + (u_longlong_t)statbuf.st_size); + exit(1); + } + + (void) close(fd); + + if (nvlist_unpack(buf, statbuf.st_size, &config, 0) != 0) { + (void) fprintf(stderr, "failed to unpack nvlist\n"); + exit(1); + } + + free(buf); + + dump_nvlist(config, 0); + + nvlist_free(config); +} + +static void dump_label(const char *dev) { int fd; @@ -1136,15 +1322,7 @@ dump_label(const char *dev) exit(1); } - if (S_ISCHR(statbuf.st_mode)) { - if (ioctl(fd, DIOCGMEDIASIZE, &psize) != 0) { - (void) printf("failed to get size '%s': %s\n", dev, - strerror(errno)); - exit(1); - } - } else - psize = statbuf.st_size; - + psize = statbuf.st_size; psize = P2ALIGN(psize, (uint64_t)sizeof (vdev_label_t)); for (l = 0; l < VDEV_LABELS; l++) { @@ -1178,170 +1356,84 @@ dump_one_dir(char *dsname, void *arg) objset_t *os; error = dmu_objset_open(dsname, DMU_OST_ANY, - DS_MODE_STANDARD | DS_MODE_READONLY, &os); + DS_MODE_USER | DS_MODE_READONLY, &os); if (error) { (void) printf("Could not open %s\n", dsname); return (0); } dump_dir(os); dmu_objset_close(os); + fuid_table_destroy(); return (0); } static void -zdb_space_map_load(spa_t *spa) +zdb_leak(space_map_t *sm, uint64_t start, uint64_t size) { - vdev_t *rvd = spa->spa_root_vdev; - vdev_t *vd; - int c, m, error; + vdev_t *vd = sm->sm_ppd; - for (c = 0; c < rvd->vdev_children; c++) { - vd = rvd->vdev_child[c]; - for (m = 0; m < vd->vdev_ms_count; m++) { - metaslab_t *msp = vd->vdev_ms[m]; - mutex_enter(&msp->ms_lock); - error = space_map_load(&msp->ms_allocmap[0], NULL, - SM_ALLOC, &msp->ms_smo, spa->spa_meta_objset); - mutex_exit(&msp->ms_lock); - if (error) - fatal("%s bad space map #%d, error %d", - spa->spa_name, c, error); - } - } + (void) printf("leaked space: vdev %llu, offset 0x%llx, size %llu\n", + (u_longlong_t)vd->vdev_id, (u_longlong_t)start, (u_longlong_t)size); } -static int -zdb_space_map_claim(spa_t *spa, blkptr_t *bp, zbookmark_t *zb) +/* ARGSUSED */ +static void +zdb_space_map_load(space_map_t *sm) { - dva_t *dva = bp->blk_dva; - vdev_t *vd; - metaslab_t *msp; - space_map_t *allocmap, *freemap; - int error; - int d; - blkptr_t blk = *bp; - - for (d = 0; d < BP_GET_NDVAS(bp); d++) { - uint64_t vdev = DVA_GET_VDEV(&dva[d]); - uint64_t offset = DVA_GET_OFFSET(&dva[d]); - uint64_t size = DVA_GET_ASIZE(&dva[d]); - - if ((vd = vdev_lookup_top(spa, vdev)) == NULL) - return (ENXIO); - - if ((offset >> vd->vdev_ms_shift) >= vd->vdev_ms_count) - return (ENXIO); - - msp = vd->vdev_ms[offset >> vd->vdev_ms_shift]; - allocmap = &msp->ms_allocmap[0]; - freemap = &msp->ms_freemap[0]; - - /* Prepare our copy of the bp in case we need to read GBHs */ - if (DVA_GET_GANG(&dva[d])) { - size = vdev_psize_to_asize(vd, SPA_GANGBLOCKSIZE); - DVA_SET_ASIZE(&blk.blk_dva[d], size); - DVA_SET_GANG(&blk.blk_dva[d], 0); - } - - mutex_enter(&msp->ms_lock); - if (space_map_contains(freemap, offset, size)) { - mutex_exit(&msp->ms_lock); - return (EAGAIN); /* allocated more than once */ - } - - if (!space_map_contains(allocmap, offset, size)) { - mutex_exit(&msp->ms_lock); - return (ESTALE); /* not allocated at all */ - } - - space_map_remove(allocmap, offset, size); - space_map_add(freemap, offset, size); - - mutex_exit(&msp->ms_lock); - } - - if (BP_IS_GANG(bp)) { - zio_gbh_phys_t gbh; - int g; - - /* LINTED - compile time assert */ - ASSERT(sizeof (zio_gbh_phys_t) == SPA_GANGBLOCKSIZE); - - BP_SET_CHECKSUM(&blk, ZIO_CHECKSUM_GANG_HEADER); - BP_SET_PSIZE(&blk, SPA_GANGBLOCKSIZE); - BP_SET_LSIZE(&blk, SPA_GANGBLOCKSIZE); - BP_SET_COMPRESS(&blk, ZIO_COMPRESS_OFF); - error = zio_wait(zio_read(NULL, spa, &blk, &gbh, - SPA_GANGBLOCKSIZE, NULL, NULL, ZIO_PRIORITY_SYNC_READ, - ZIO_FLAG_CANFAIL | ZIO_FLAG_CONFIG_HELD, zb)); - if (error) - return (error); - if (BP_SHOULD_BYTESWAP(&blk)) - byteswap_uint64_array(&gbh, SPA_GANGBLOCKSIZE); - for (g = 0; g < SPA_GBH_NBLKPTRS; g++) { - if (BP_IS_HOLE(&gbh.zg_blkptr[g])) - break; - error = zdb_space_map_claim(spa, &gbh.zg_blkptr[g], zb); - if (error) - return (error); - } - } - - return (0); } static void -zdb_leak(space_map_t *sm, uint64_t start, uint64_t size) +zdb_space_map_unload(space_map_t *sm) { - metaslab_t *msp; - - /* LINTED */ - msp = (metaslab_t *)((char *)sm - offsetof(metaslab_t, ms_allocmap[0])); + space_map_vacate(sm, zdb_leak, sm); +} - (void) printf("leaked space: vdev %llu, offset 0x%llx, size %llu\n", - (u_longlong_t)msp->ms_group->mg_vd->vdev_id, - (u_longlong_t)start, - (u_longlong_t)size); +/* ARGSUSED */ +static void +zdb_space_map_claim(space_map_t *sm, uint64_t start, uint64_t size) +{ } +static space_map_ops_t zdb_space_map_ops = { + zdb_space_map_load, + zdb_space_map_unload, + NULL, /* alloc */ + zdb_space_map_claim, + NULL /* free */ +}; + static void -zdb_space_map_unload(spa_t *spa) +zdb_leak_init(spa_t *spa) { vdev_t *rvd = spa->spa_root_vdev; - vdev_t *vd; - int c, m; - for (c = 0; c < rvd->vdev_children; c++) { - vd = rvd->vdev_child[c]; - for (m = 0; m < vd->vdev_ms_count; m++) { + for (int c = 0; c < rvd->vdev_children; c++) { + vdev_t *vd = rvd->vdev_child[c]; + for (int m = 0; m < vd->vdev_ms_count; m++) { metaslab_t *msp = vd->vdev_ms[m]; mutex_enter(&msp->ms_lock); - space_map_vacate(&msp->ms_allocmap[0], zdb_leak, - &msp->ms_allocmap[0]); - space_map_unload(&msp->ms_allocmap[0]); - space_map_vacate(&msp->ms_freemap[0], NULL, NULL); + VERIFY(space_map_load(&msp->ms_map, &zdb_space_map_ops, + SM_ALLOC, &msp->ms_smo, spa->spa_meta_objset) == 0); + msp->ms_map.sm_ppd = vd; mutex_exit(&msp->ms_lock); } } } static void -zdb_refresh_ubsync(spa_t *spa) +zdb_leak_fini(spa_t *spa) { - uberblock_t ub = { 0 }; vdev_t *rvd = spa->spa_root_vdev; - zio_t *zio; - /* - * Reload the uberblock. - */ - zio = zio_root(spa, NULL, NULL, - ZIO_FLAG_CANFAIL | ZIO_FLAG_SPECULATIVE); - vdev_uberblock_load(zio, rvd, &ub); - (void) zio_wait(zio); - - if (ub.ub_txg != 0) - spa->spa_ubsync = ub; + for (int c = 0; c < rvd->vdev_children; c++) { + vdev_t *vd = rvd->vdev_child[c]; + for (int m = 0; m < vd->vdev_ms_count; m++) { + metaslab_t *msp = vd->vdev_ms[m]; + mutex_enter(&msp->ms_lock); + space_map_unload(&msp->ms_map); + mutex_exit(&msp->ms_lock); + } + } } /* @@ -1371,9 +1463,7 @@ typedef struct zdb_cb { static void zdb_count_block(spa_t *spa, zdb_cb_t *zcb, blkptr_t *bp, int type) { - int i, error; - - for (i = 0; i < 4; i++) { + for (int i = 0; i < 4; i++) { int l = (i < 2) ? BP_GET_LEVEL(bp) : ZB_TOTAL; int t = (i & 1) ? type : DMU_OT_TOTAL; zdb_blkstats_t *zb = &zcb->zcb_type[l][t]; @@ -1384,21 +1474,34 @@ zdb_count_block(spa_t *spa, zdb_cb_t *zcb, blkptr_t *bp, int type) zb->zb_count++; } - if (dump_opt['L']) - return; - - error = zdb_space_map_claim(spa, bp, &zcb->zcb_cache->bc_bookmark); - - if (error == 0) - return; - - if (error == EAGAIN) - (void) fatal("double-allocation, bp=%p", bp); - - if (error == ESTALE) - (void) fatal("reference to freed block, bp=%p", bp); + if (dump_opt['S']) { + boolean_t print_sig; + + print_sig = !zdb_sig_user_data || (BP_GET_LEVEL(bp) == 0 && + BP_GET_TYPE(bp) == DMU_OT_PLAIN_FILE_CONTENTS); + + if (BP_GET_CHECKSUM(bp) < zdb_sig_cksumalg) + print_sig = B_FALSE; + + if (print_sig) { + (void) printf("%llu\t%lld\t%lld\t%s\t%s\t%s\t" + "%llx:%llx:%llx:%llx\n", + (u_longlong_t)BP_GET_LEVEL(bp), + (longlong_t)BP_GET_PSIZE(bp), + (longlong_t)BP_GET_NDVAS(bp), + dmu_ot[BP_GET_TYPE(bp)].ot_name, + zio_checksum_table[BP_GET_CHECKSUM(bp)].ci_name, + zio_compress_table[BP_GET_COMPRESS(bp)].ci_name, + (u_longlong_t)bp->blk_cksum.zc_word[0], + (u_longlong_t)bp->blk_cksum.zc_word[1], + (u_longlong_t)bp->blk_cksum.zc_word[2], + (u_longlong_t)bp->blk_cksum.zc_word[3]); + } + } - (void) fatal("fatal error %d in bp %p", error, bp); + if (!dump_opt['L']) + VERIFY(zio_wait(zio_claim(NULL, spa, spa_first_txg(spa), bp, + NULL, NULL, ZIO_FLAG_MUSTSUCCEED)) == 0); } static int @@ -1411,9 +1514,16 @@ zdb_blkptr_cb(traverse_blk_cache_t *bc, spa_t *spa, void *arg) char blkbuf[BP_SPRINTF_LEN]; int error = 0; + ASSERT(!BP_IS_HOLE(bp)); + + zdb_count_block(spa, zcb, bp, type); + if (bc->bc_errno) { if (zcb->zcb_readfails++ < 10 && dump_opt['L']) { - zdb_refresh_ubsync(spa); + uberblock_t ub; + vdev_uberblock_load(NULL, spa->spa_root_vdev, &ub); + if (ub.ub_txg != 0) + spa->spa_ubsync = ub; error = EAGAIN; } else { zcb->zcb_haderrors = 1; @@ -1426,35 +1536,32 @@ zdb_blkptr_cb(traverse_blk_cache_t *bc, spa_t *spa, void *arg) else blkbuf[0] = '\0'; - (void) printf("zdb_blkptr_cb: Got error %d reading " - "<%llu, %llu, %lld, %llx> %s -- %s\n", - bc->bc_errno, - (u_longlong_t)zb->zb_objset, - (u_longlong_t)zb->zb_object, - (u_longlong_t)zb->zb_level, - (u_longlong_t)zb->zb_blkid, - blkbuf, - error == EAGAIN ? "retrying" : "skipping"); + if (!dump_opt['S']) { + (void) printf("zdb_blkptr_cb: Got error %d reading " + "<%llu, %llu, %lld, %llx> %s -- %s\n", + bc->bc_errno, + (u_longlong_t)zb->zb_objset, + (u_longlong_t)zb->zb_object, + (u_longlong_t)zb->zb_level, + (u_longlong_t)zb->zb_blkid, + blkbuf, + error == EAGAIN ? "retrying" : "skipping"); + } return (error); } zcb->zcb_readfails = 0; - ASSERT(!BP_IS_HOLE(bp)); - if (dump_opt['b'] >= 4) { sprintf_blkptr(blkbuf, BP_SPRINTF_LEN, bp); (void) printf("objset %llu object %llu offset 0x%llx %s\n", (u_longlong_t)zb->zb_objset, (u_longlong_t)zb->zb_object, (u_longlong_t)blkid2offset(bc->bc_dnode, - zb->zb_level, zb->zb_blkid), - blkbuf); + zb->zb_level, zb->zb_blkid), blkbuf); } - zdb_count_block(spa, zcb, bp, type); - return (0); } @@ -1465,32 +1572,35 @@ dump_block_stats(spa_t *spa) zdb_cb_t zcb = { 0 }; traverse_blk_cache_t dummy_cache = { 0 }; zdb_blkstats_t *zb, *tzb; - uint64_t alloc, space; + uint64_t alloc, space, logalloc; + vdev_t *rvd = spa->spa_root_vdev; int leaks = 0; int advance = zdb_advance; - int flags; - int e; + int c, e, flags; zcb.zcb_cache = &dummy_cache; - if (dump_opt['c']) + if (dump_opt['c'] || dump_opt['S']) advance |= ADVANCE_DATA; advance |= ADVANCE_PRUNE | ADVANCE_ZIL; - (void) printf("\nTraversing all blocks to %sverify" - " nothing leaked ...\n", - dump_opt['c'] ? "verify checksums and " : ""); + if (!dump_opt['S']) { + (void) printf("\nTraversing all blocks to %sverify" + " nothing leaked ...\n", + dump_opt['c'] ? "verify checksums and " : ""); + } /* - * Load all space maps. As we traverse the pool, if we find a block - * that's not in its space map, that indicates a double-allocation, - * reference to a freed block, or an unclaimed block. Otherwise we - * remove the block from the space map. If the space maps are not - * empty when we're done, that indicates leaked blocks. + * Load all space maps as SM_ALLOC maps, then traverse the pool + * claiming each block we discover. If the pool is perfectly + * consistent, the space maps will be empty when we're done. + * Anything left over is a leak; any block we can't claim (because + * it's not part of any space map) is a double allocation, + * reference to a freed block, or an unclaimed log block. */ if (!dump_opt['L']) - zdb_space_map_load(spa); + zdb_leak_init(spa); /* * If there's a deferred-free bplist, process that first. @@ -1533,7 +1643,7 @@ dump_block_stats(spa_t *spa) traverse_fini(th); - if (zcb.zcb_haderrors) { + if (zcb.zcb_haderrors && !dump_opt['S']) { (void) printf("\nError counts:\n\n"); (void) printf("\t%5s %s\n", "errno", "count"); for (e = 0; e < 256; e++) { @@ -1548,7 +1658,15 @@ dump_block_stats(spa_t *spa) * Report any leaked segments. */ if (!dump_opt['L']) - zdb_space_map_unload(spa); + zdb_leak_fini(spa); + + /* + * If we're interested in printing out the blkptr signatures, + * return now as we don't print out anything else (including + * errors and leaks). + */ + if (dump_opt['S']) + return (zcb.zcb_haderrors ? 3 : 0); if (dump_opt['L']) (void) printf("\n\n *** Live pool traversal; " @@ -1557,17 +1675,27 @@ dump_block_stats(spa_t *spa) alloc = spa_get_alloc(spa); space = spa_get_space(spa); + /* + * Log blocks allocated from a separate log device don't count + * as part of the normal pool space; factor them in here. + */ + logalloc = 0; + + for (c = 0; c < rvd->vdev_children; c++) + if (rvd->vdev_child[c]->vdev_islog) + logalloc += rvd->vdev_child[c]->vdev_stat.vs_alloc; + tzb = &zcb.zcb_type[ZB_TOTAL][DMU_OT_TOTAL]; - if (tzb->zb_asize == alloc) { + if (tzb->zb_asize == alloc + logalloc) { (void) printf("\n\tNo leaks (block sum matches space" " maps exactly)\n"); } else { (void) printf("block traversal size %llu != alloc %llu " "(leaked %lld)\n", (u_longlong_t)tzb->zb_asize, - (u_longlong_t)alloc, - (u_longlong_t)(alloc - tzb->zb_asize)); + (u_longlong_t)alloc + logalloc, + (u_longlong_t)(alloc + logalloc - tzb->zb_asize)); leaks = 1; } @@ -1682,11 +1810,11 @@ dump_zpool(spa_t *spa) dump_dtl(spa->spa_root_vdev, 0); dump_metaslabs(spa); } - (void) dmu_objset_find(spa->spa_name, dump_one_dir, NULL, + (void) dmu_objset_find(spa_name(spa), dump_one_dir, NULL, DS_FIND_SNAPSHOTS | DS_FIND_CHILDREN); } - if (dump_opt['b'] || dump_opt['c']) + if (dump_opt['b'] || dump_opt['c'] || dump_opt['S']) rc = dump_block_stats(spa); if (dump_opt['s']) @@ -1898,12 +2026,12 @@ zdb_read_block(char *thing, spa_t **spap) zio_t *zio; vdev_t *vd; void *buf; - char *s, *p, *dup, *spa_name, *vdev, *flagstr; + char *s, *p, *dup, *pool, *vdev, *flagstr; int i, error, zio_flags; dup = strdup(thing); s = strtok(dup, ":"); - spa_name = s ? s : ""; + pool = s ? s : ""; s = strtok(NULL, ":"); vdev = s ? s : ""; s = strtok(NULL, ":"); @@ -1953,14 +2081,13 @@ zdb_read_block(char *thing, spa_t **spap) } } - if (spa == NULL || spa->spa_name == NULL || - strcmp(spa->spa_name, spa_name)) { - if (spa && spa->spa_name) + if (spa == NULL || strcmp(spa_name(spa), pool) != 0) { + if (spa) spa_close(spa, (void *)zdb_read_block); - error = spa_open(spa_name, spap, (void *)zdb_read_block); + error = spa_open(pool, spap, (void *)zdb_read_block); if (error) fatal("Failed to open pool '%s': %s", - spa_name, strerror(error)); + pool, strerror(error)); spa = *spap; } @@ -1980,16 +2107,15 @@ zdb_read_block(char *thing, spa_t **spap) buf = umem_alloc(size, UMEM_NOFAIL); zio_flags = ZIO_FLAG_DONT_CACHE | ZIO_FLAG_DONT_QUEUE | - ZIO_FLAG_DONT_PROPAGATE | ZIO_FLAG_DONT_RETRY | ZIO_FLAG_NOBOOKMARK; - - if (flags & ZDB_FLAG_PHYS) - zio_flags |= ZIO_FLAG_PHYSICAL; + ZIO_FLAG_DONT_PROPAGATE | ZIO_FLAG_DONT_RETRY; + spa_config_enter(spa, SCL_STATE, FTAG, RW_READER); zio = zio_root(spa, NULL, NULL, 0); /* XXX todo - cons up a BP so RAID-Z will be happy */ zio_nowait(zio_vdev_child_io(zio, NULL, vd, offset, buf, size, ZIO_TYPE_READ, ZIO_PRIORITY_SYNC_READ, zio_flags, NULL, NULL)); error = zio_wait(zio); + spa_config_exit(spa, SCL_STATE, FTAG); if (error) { (void) printf("Read of %s failed, error: %d\n", thing, error); @@ -2014,6 +2140,132 @@ out: free(dup); } +static boolean_t +nvlist_string_match(nvlist_t *config, char *name, char *tgt) +{ + char *s; + + if (nvlist_lookup_string(config, name, &s) != 0) + return (B_FALSE); + + return (strcmp(s, tgt) == 0); +} + +static boolean_t +nvlist_uint64_match(nvlist_t *config, char *name, uint64_t tgt) +{ + uint64_t val; + + if (nvlist_lookup_uint64(config, name, &val) != 0) + return (B_FALSE); + + return (val == tgt); +} + +static boolean_t +vdev_child_guid_match(nvlist_t *vdev, uint64_t guid) +{ + nvlist_t **child; + uint_t c, children; + + verify(nvlist_lookup_nvlist_array(vdev, ZPOOL_CONFIG_CHILDREN, + &child, &children) == 0); + for (c = 0; c < children; ++c) + if (nvlist_uint64_match(child[c], ZPOOL_CONFIG_GUID, guid)) + return (B_TRUE); + return (B_FALSE); +} + +static boolean_t +vdev_child_string_match(nvlist_t *vdev, char *tgt) +{ + nvlist_t **child; + uint_t c, children; + + verify(nvlist_lookup_nvlist_array(vdev, ZPOOL_CONFIG_CHILDREN, + &child, &children) == 0); + for (c = 0; c < children; ++c) { + if (nvlist_string_match(child[c], ZPOOL_CONFIG_PATH, tgt) || + nvlist_string_match(child[c], ZPOOL_CONFIG_DEVID, tgt)) + return (B_TRUE); + } + return (B_FALSE); +} + +static boolean_t +vdev_guid_match(nvlist_t *config, uint64_t guid) +{ + nvlist_t *nvroot; + + verify(nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE, + &nvroot) == 0); + + return (nvlist_uint64_match(nvroot, ZPOOL_CONFIG_GUID, guid) || + vdev_child_guid_match(nvroot, guid)); +} + +static boolean_t +vdev_string_match(nvlist_t *config, char *tgt) +{ + nvlist_t *nvroot; + + verify(nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE, + &nvroot) == 0); + + return (vdev_child_string_match(nvroot, tgt)); +} + +static boolean_t +pool_match(nvlist_t *config, char *tgt) +{ + uint64_t guid = strtoull(tgt, NULL, 0); + + if (guid != 0) { + return ( + nvlist_uint64_match(config, ZPOOL_CONFIG_POOL_GUID, guid) || + vdev_guid_match(config, guid)); + } else { + return ( + nvlist_string_match(config, ZPOOL_CONFIG_POOL_NAME, tgt) || + vdev_string_match(config, tgt)); + } +} + +static int +find_exported_zpool(char *pool_id, nvlist_t **configp, char *vdev_dir) +{ + nvlist_t *pools; + int error = ENOENT; + nvlist_t *match = NULL; + + if (vdev_dir != NULL) + pools = zpool_find_import_activeok(g_zfs, 1, &vdev_dir); + else + pools = zpool_find_import_activeok(g_zfs, 0, NULL); + + if (pools != NULL) { + nvpair_t *elem = NULL; + + while ((elem = nvlist_next_nvpair(pools, elem)) != NULL) { + verify(nvpair_value_nvlist(elem, configp) == 0); + if (pool_match(*configp, pool_id)) { + if (match != NULL) { + (void) fatal( + "More than one matching pool - " + "specify guid/devid/device path."); + } else { + match = *configp; + error = 0; + } + } + } + } + + *configp = error ? NULL : match; + + return (error); +} + int main(int argc, char **argv) { @@ -2026,13 +2278,15 @@ main(int argc, char **argv) int verbose = 0; int error; int flag, set; + int exported = 0; + char *vdev_dir = NULL; (void) setrlimit(RLIMIT_NOFILE, &rl); (void) enable_extended_FILE_stdio(-1, -1); dprintf_setup(&argc, argv); - while ((c = getopt(argc, argv, "udibcsvCLO:B:UlR")) != -1) { + while ((c = getopt(argc, argv, "udibcsvCLO:B:S:U:lRep:")) != -1) { switch (c) { case 'u': case 'd': @@ -2093,7 +2347,31 @@ main(int argc, char **argv) verbose++; break; case 'U': - spa_config_dir = "/tmp"; + spa_config_path = optarg; + break; + case 'e': + exported = 1; + break; + case 'p': + vdev_dir = optarg; + break; + case 'S': + dump_opt[c]++; + dump_all = 0; + zdb_sig_user_data = (strncmp(optarg, "user:", 5) == 0); + if (!zdb_sig_user_data && strncmp(optarg, "all:", 4)) + usage(); + endstr = strchr(optarg, ':') + 1; + if (strcmp(endstr, "fletcher2") == 0) + zdb_sig_cksumalg = ZIO_CHECKSUM_FLETCHER_2; + else if (strcmp(endstr, "fletcher4") == 0) + zdb_sig_cksumalg = ZIO_CHECKSUM_FLETCHER_4; + else if (strcmp(endstr, "sha256") == 0) + zdb_sig_cksumalg = ZIO_CHECKSUM_SHA256; + else if (strcmp(endstr, "all") == 0) + zdb_sig_cksumalg = ZIO_CHECKSUM_FLETCHER_2; + else + usage(); break; default: usage(); @@ -2101,7 +2379,12 @@ main(int argc, char **argv) } } + if (vdev_dir != NULL && exported == 0) + (void) fatal("-p option requires use of -e\n"); + kernel_init(FREAD); + g_zfs = libzfs_init(); + ASSERT(g_zfs != NULL); /* * Disable vdev caching. If we don't do this, live pool traversal @@ -2121,7 +2404,7 @@ main(int argc, char **argv) if (argc < 1) { if (dump_opt['C']) { - dump_config(NULL); + dump_cachefile(spa_config_path); return (0); } usage(); @@ -2156,11 +2439,48 @@ main(int argc, char **argv) if (dump_opt['C']) dump_config(argv[0]); - if (strchr(argv[0], '/') != NULL) { - error = dmu_objset_open(argv[0], DMU_OST_ANY, - DS_MODE_STANDARD | DS_MODE_READONLY, &os); - } else { - error = spa_open(argv[0], &spa, FTAG); + error = 0; + if (exported) { + /* + * Check to see if the name refers to an exported zpool + */ + char *slash; + nvlist_t *exported_conf = NULL; + + if ((slash = strchr(argv[0], '/')) != NULL) + *slash = '\0'; + + error = find_exported_zpool(argv[0], &exported_conf, vdev_dir); + if (error == 0) { + nvlist_t *nvl = NULL; + + if (vdev_dir != NULL) { + if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0) + error = ENOMEM; + else if (nvlist_add_string(nvl, + zpool_prop_to_name(ZPOOL_PROP_ALTROOT), + vdev_dir) != 0) + error = ENOMEM; + } + + if (error == 0) + error = spa_import_faulted(argv[0], + exported_conf, nvl); + + nvlist_free(nvl); + } + + if (slash != NULL) + *slash = '/'; + } + + if (error == 0) { + if (strchr(argv[0], '/') != NULL) { + error = dmu_objset_open(argv[0], DMU_OST_ANY, + DS_MODE_USER | DS_MODE_READONLY, &os); + } else { + error = spa_open(argv[0], &spa, FTAG); + } } if (error) @@ -2187,6 +2507,9 @@ main(int argc, char **argv) spa_close(spa, FTAG); } + fuid_table_destroy(); + + libzfs_fini(g_zfs); kernel_fini(); return (0); |