diff --git a/cmd/zdb/zdb.c b/cmd/zdb/zdb.c index c76076a392..8259665551 100644 --- a/cmd/zdb/zdb.c +++ b/cmd/zdb/zdb.c @@ -26,7 +26,7 @@ * Copyright 2016 Nexenta Systems, Inc. * Copyright (c) 2017, 2018 Lawrence Livermore National Security, LLC. * Copyright (c) 2015, 2017, Intel Corporation. - * Copyright (c) 2019 Datto Inc. + * Copyright (c) 2020 Datto Inc. */ #include @@ -144,9 +144,9 @@ usage(void) "Usage:\t%s [-AbcdDFGhikLMPsvX] [-e [-V] [-p ...]] " "[-I ]\n" "\t\t[-o =]... [-t ] [-U ] [-x ]\n" - "\t\t[ [ ...]]\n" - "\t%s [-AdiPv] [-e [-V] [-p ...]] [-U ] \n" - "\t\t[ ...]\n" + "\t\t[[/] [ ...]]\n" + "\t%s [-AdiPv] [-e [-V] [-p ...]] [-U ]\n" + "\t\t[[/] [ ...]\n" "\t%s [-v] \n" "\t%s -C [-A] [-U ]\n" "\t%s -l [-Aqu] \n" @@ -6335,6 +6335,26 @@ name: return (NULL); } +static int +name_from_objset_id(spa_t *spa, uint64_t objset_id, char *outstr) +{ + dsl_dataset_t *ds; + + dsl_pool_config_enter(spa->spa_dsl_pool, FTAG); + int error = dsl_dataset_hold_obj(spa->spa_dsl_pool, objset_id, + NULL, &ds); + if (error != 0) { + (void) fprintf(stderr, "failed to hold objset %llu: %s\n", + (u_longlong_t)objset_id, strerror(error)); + dsl_pool_config_exit(spa->spa_dsl_pool, FTAG); + return (error); + } + dsl_dataset_name(ds, outstr); + dsl_dataset_rele(ds, NULL); + dsl_pool_config_exit(spa->spa_dsl_pool, FTAG); + return (0); +} + static boolean_t zdb_parse_block_sizes(char *sizes, uint64_t *lsize, uint64_t *psize) { @@ -6713,13 +6733,14 @@ main(int argc, char **argv) int error = 0; char **searchdirs = NULL; int nsearch = 0; - char *target, *target_pool; + char *target, *target_pool, dsname[ZFS_MAX_DATASET_NAME_LEN]; nvlist_t *policy = NULL; uint64_t max_txg = UINT64_MAX; + int64_t objset_id = -1; int flags = ZFS_IMPORT_MISSING_LOG; int rewind = ZPOOL_NEVER_REWIND; - char *spa_config_path_env; - boolean_t target_is_spa = B_TRUE; + char *spa_config_path_env, *objset_str; + boolean_t target_is_spa = B_TRUE, dataset_lookup = B_FALSE; nvlist_t *cfg = NULL; (void) setrlimit(RLIMIT_NOFILE, &rl); @@ -6846,6 +6867,31 @@ main(int argc, char **argv) (void) fprintf(stderr, "-p option requires use of -e\n"); usage(); } + if (dump_opt['d']) { + /* [/ is accepted */ + if (argv[2] && (objset_str = strchr(argv[2], '/')) != NULL && + objset_str++ != NULL) { + char *endptr; + errno = 0; + objset_id = strtoull(objset_str, &endptr, 0); + /* dataset 0 is the same as opening the pool */ + if (errno == 0 && endptr != objset_str && + objset_id != 0) { + target_is_spa = B_FALSE; + dataset_lookup = B_TRUE; + } else if (objset_id != 0) { + printf("failed to open objset %s " + "%llu %s", objset_str, + (u_longlong_t)objset_id, + strerror(errno)); + exit(1); + } + /* normal dataset name not an objset ID */ + if (endptr == objset_str) { + objset_id = -1; + } + } + } #if defined(_LP64) /* @@ -6890,7 +6936,6 @@ main(int argc, char **argv) argc -= optind; argv += optind; - if (argc < 2 && dump_opt['R']) usage(); @@ -7010,7 +7055,7 @@ main(int argc, char **argv) checkpoint_pool, error); } - } else if (target_is_spa || dump_opt['R']) { + } else if (target_is_spa || dump_opt['R'] || objset_id == 0) { zdb_set_skip_mmp(target); error = spa_open_rewind(target, &spa, FTAG, policy, NULL); @@ -7049,7 +7094,22 @@ main(int argc, char **argv) return (error); } else { zdb_set_skip_mmp(target); - error = open_objset(target, FTAG, &os); + if (dataset_lookup == B_TRUE) { + /* + * Use the supplied id to get the name + * for open_objset. + */ + error = spa_open(target, &spa, FTAG); + if (error == 0) { + error = name_from_objset_id(spa, + objset_id, dsname); + spa_close(spa, FTAG); + if (error == 0) + target = dsname; + } + } + if (error == 0) + error = open_objset(target, FTAG, &os); if (error == 0) spa = dmu_objset_spa(os); } diff --git a/man/man8/zdb.8 b/man/man8/zdb.8 index 7c50d95d91..87d3ad8cc5 100644 --- a/man/man8/zdb.8 +++ b/man/man8/zdb.8 @@ -30,12 +30,13 @@ .Op Fl t Ar txg .Op Fl U Ar cache .Op Fl x Ar dumpdir -.Op Ar poolname Op Ar object ... +.Op Ar poolname[/dataset | objset ID] +.Op Ar object ... .Nm .Op Fl AdiPv .Op Fl e Oo Fl V Oc Op Fl p Ar path ... .Op Fl U Ar cache -.Ar dataset Op Ar object ... +.Ar poolname[/dataset | objset ID] Op Ar object ... .Nm .Fl C .Op Fl A diff --git a/tests/runfiles/common.run b/tests/runfiles/common.run index 50330884de..4342662e7d 100644 --- a/tests/runfiles/common.run +++ b/tests/runfiles/common.run @@ -100,7 +100,8 @@ tags = ['functional', 'clean_mirror'] [tests/functional/cli_root/zdb] tests = ['zdb_001_neg', 'zdb_002_pos', 'zdb_003_pos', 'zdb_004_pos', - 'zdb_005_pos', 'zdb_006_pos', 'zdb_checksum', 'zdb_decompress'] + 'zdb_005_pos', 'zdb_006_pos', 'zdb_checksum', 'zdb_decompress', + 'zdb_objset_id'] pre = post = tags = ['functional', 'cli_root', 'zdb'] diff --git a/tests/zfs-tests/tests/functional/cli_root/zdb/Makefile.am b/tests/zfs-tests/tests/functional/cli_root/zdb/Makefile.am index 9f143078f1..edbebb0208 100644 --- a/tests/zfs-tests/tests/functional/cli_root/zdb/Makefile.am +++ b/tests/zfs-tests/tests/functional/cli_root/zdb/Makefile.am @@ -7,4 +7,5 @@ dist_pkgdata_SCRIPTS = \ zdb_005_pos.ksh \ zdb_006_pos.ksh \ zdb_checksum.ksh \ - zdb_decompress.ksh + zdb_decompress.ksh \ + zdb_objset_id.ksh diff --git a/tests/zfs-tests/tests/functional/cli_root/zdb/zdb_objset_id.ksh b/tests/zfs-tests/tests/functional/cli_root/zdb/zdb_objset_id.ksh new file mode 100755 index 0000000000..f0c1076c97 --- /dev/null +++ b/tests/zfs-tests/tests/functional/cli_root/zdb/zdb_objset_id.ksh @@ -0,0 +1,96 @@ +#!/bin/ksh + +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2020 by Datto, Inc. All rights reserved. +# + +. $STF_SUITE/include/libtest.shlib + +# +# Description: +# zdb -d pool/ will display the dataset +# +# Strategy: +# 1. Create a pool +# 2. Write some data to a file +# 3. Get the inode number (object number) of the file +# 4. Run zdb -d to get the objset ID of the dataset +# 5. Run zdb -dddddd pool/objsetID objectID (decimal) +# 6. Confirm names +# 7. Run zdb -dddddd pool/objsetID objectID (hex) +# 8. Confirm names +# 9. Obtain objsetID from /proc/spl/kstat/zfs/testpool/obset-0x +# (linux only) +# 10. Run zdb -dddddd pool/objsetID (hex) +# 11. Match name from zdb against proc entry +# + +function cleanup +{ + datasetexists $TESTPOOL && destroy_pool $TESTPOOL +} + +log_assert "Verify zdb -d / generates the correct names." +log_onexit cleanup +init_data=$TESTDIR/file1 +write_count=8 +blksize=131072 +verify_runnable "global" +verify_disk_count "$DISKS" 2 + +default_mirror_setup_noexit $DISKS +file_write -o create -w -f $init_data -b $blksize -c $write_count + +# get object number of file +listing=$(ls -i $init_data) +set -A array $listing +obj=${array[0]} +log_note "file $init_data has object number $obj" + +output=$(zdb -d $TESTPOOL/$TESTFS) +objset_id=$(echo $output | awk '{split($0,array,",")} END{print array[2]}' | + awk '{split($0,array," ")} END{print array[2]}') +objset_hex=$(printf "0x%X" $objset_id) +log_note "objset $TESTPOOL/$TESTFS has objset ID $objset_id ($objset_hex)" + +for id in "$objset_id" "$objset_hex" +do + log_note "zdb -dddddd $TESTPOOL/$id $obj" + output=$(zdb -dddddd $TESTPOOL/$id $obj) + reason="($TESTPOOL/$TESTFS not in zdb output)" + echo $output |grep "$TESTPOOL/$TESTFS" > /dev/null + (( $? != 0 )) && log_fail \ + "zdb -dddddd $TESTPOOL/$id $obj failed $reason" + reason="(file1 not in zdb output)" + echo $output |grep "file1" > /dev/null + (( $? != 0 )) && log_fail \ + "zdb -dddddd $TESTPOOL/$id $obj failed $reason" + obj=$(printf "0x%X" $obj) +done + +if is_linux; then + output=$(ls -1 /proc/spl/kstat/zfs/$TESTPOOL |grep objset- |tail -1) + objset_hex=${output#*-} + name_from_proc=$(cat /proc/spl/kstat/zfs/$TESTPOOL/$output | + grep dataset_name | awk '{split($0,array," ")} END{print array[3]}') + log_note "checking zdb output for $name_from_proc" + reason="(name $name_from_proc from proc not in zdb output)" + log_note "zdb -dddddd $TESTPOOL/$objset_hex" + output=$(zdb -dddddd $TESTPOOL/$objset_hex) + echo $output |grep "$name_from_proc" > /dev/null + (( $? != 0 )) && log_fail \ + "zdb -dddddd $TESTPOOL/$objset_hex failed $reason" +fi + +log_pass "zdb -d / generates the correct names."