Skip to content

Commit

Permalink
zdb: add -B option to generate backup stream
Browse files Browse the repository at this point in the history
This is more-or-less like `zfs send`, but specifying the snapshot by its
objset id for situations where it can't be referenced any other way.

Sponsored-By: Klara, Inc.
Reviewed-by: Tino Reichardt <[email protected]>
Reviewed-by: WHR <[email protected]>
Signed-off-by: Rob Norris <[email protected]>
Closes openzfs#14642
  • Loading branch information
robn authored and behlendorf committed Jun 5, 2023
1 parent 2b9f8ba commit 8653f1d
Show file tree
Hide file tree
Showing 6 changed files with 174 additions and 9 deletions.
97 changes: 92 additions & 5 deletions cmd/zdb/zdb.c
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
* under sponsorship from the FreeBSD Foundation.
* Copyright (c) 2021 Allan Jude
* Copyright (c) 2021 Toomas Soome <[email protected]>
* Copyright (c) 2023, Klara Inc.
*/

#include <stdio.h>
Expand Down Expand Up @@ -789,6 +790,9 @@ usage(void)
"\t\t[<poolname>[/<dataset | objset id>] [<object | range> ...]]\n"
"\t%s [-AdiPv] [-e [-V] [-p <path> ...]] [-U <cache>] [-K <key>]\n"
"\t\t[<poolname>[/<dataset | objset id>] [<object | range> ...]\n"
"\t%s -B [-e [-V] [-p <path> ...]] [-I <inflight I/Os>]\n"
"\t\t[-o <var>=<value>]... [-t <txg>] [-U <cache>] [-x <dumpdir>]\n"
"\t\t[-K <key>] <poolname>/<objset id> [<backupflags>]\n"
"\t%s [-v] <bookmark>\n"
"\t%s -C [-A] [-U <cache>]\n"
"\t%s -l [-Aqu] <device>\n"
Expand All @@ -802,7 +806,7 @@ usage(void)
"\t%s -S [-AP] [-e [-V] [-p <path> ...]] [-U <cache>] "
"<poolname>\n\n",
cmdname, cmdname, cmdname, cmdname, cmdname, cmdname, cmdname,
cmdname, cmdname, cmdname, cmdname);
cmdname, cmdname, cmdname, cmdname, cmdname);

(void) fprintf(stderr, " Dataset name must include at least one "
"separator character '/' or '@'\n");
Expand All @@ -825,6 +829,8 @@ usage(void)
(void) fprintf(stderr, " Options to control amount of output:\n");
(void) fprintf(stderr, " -b --block-stats "
"block statistics\n");
(void) fprintf(stderr, " -B --backup "
"backup stream\n");
(void) fprintf(stderr, " -c --checksum "
"checksum all metadata (twice for all data) blocks\n");
(void) fprintf(stderr, " -C --config "
Expand Down Expand Up @@ -4875,6 +4881,81 @@ dump_path(char *ds, char *path, uint64_t *retobj)
return (err);
}

static int
dump_backup_bytes(objset_t *os, void *buf, int len, void *arg)
{
const char *p = (const char *)buf;
ssize_t nwritten;

(void) os;
(void) arg;

/* Write the data out, handling short writes and signals. */
while ((nwritten = write(STDOUT_FILENO, p, len)) < len) {
if (nwritten < 0) {
if (errno == EINTR)
continue;
return (errno);
}
p += nwritten;
len -= nwritten;
}

return (0);
}

static void
dump_backup(const char *pool, uint64_t objset_id, const char *flagstr)
{
boolean_t embed = B_FALSE;
boolean_t large_block = B_FALSE;
boolean_t compress = B_FALSE;
boolean_t raw = B_FALSE;

const char *c;
for (c = flagstr; c != NULL && *c != '\0'; c++) {
switch (*c) {
case 'e':
embed = B_TRUE;
break;
case 'L':
large_block = B_TRUE;
break;
case 'c':
compress = B_TRUE;
break;
case 'w':
raw = B_TRUE;
break;
default:
fprintf(stderr, "dump_backup: invalid flag "
"'%c'\n", *c);
return;
}
}

if (isatty(STDOUT_FILENO)) {
fprintf(stderr, "dump_backup: stream cannot be written "
"to a terminal\n");
return;
}

offset_t off = 0;
dmu_send_outparams_t out = {
.dso_outfunc = dump_backup_bytes,
.dso_dryrun = B_FALSE,
};

int err = dmu_send_obj(pool, objset_id, /* fromsnap */0, embed,
large_block, compress, raw, /* saved */ B_FALSE, STDOUT_FILENO,
&off, &out);
if (err != 0) {
fprintf(stderr, "dump_backup: dmu_send_obj: %s\n",
strerror(err));
return;
}
}

static int
zdb_copy_object(objset_t *os, uint64_t srcobj, char *destfile)
{
Expand Down Expand Up @@ -8695,6 +8776,7 @@ main(int argc, char **argv)
struct option long_options[] = {
{"ignore-assertions", no_argument, NULL, 'A'},
{"block-stats", no_argument, NULL, 'b'},
{"backup", no_argument, NULL, 'B'},
{"checksum", no_argument, NULL, 'c'},
{"config", no_argument, NULL, 'C'},
{"datasets", no_argument, NULL, 'd'},
Expand Down Expand Up @@ -8736,10 +8818,11 @@ main(int argc, char **argv)
};

while ((c = getopt_long(argc, argv,
"AbcCdDeEFGhiI:kK:lLmMNo:Op:PqrRsSt:uU:vVx:XYyZ",
"AbBcCdDeEFGhiI:kK:lLmMNo:Op:PqrRsSt:uU:vVx:XYyZ",
long_options, NULL)) != -1) {
switch (c) {
case 'b':
case 'B':
case 'c':
case 'C':
case 'd':
Expand Down Expand Up @@ -8887,7 +8970,7 @@ main(int argc, char **argv)
verbose = MAX(verbose, 1);

for (c = 0; c < 256; c++) {
if (dump_all && strchr("AeEFkKlLNOPrRSXy", c) == NULL)
if (dump_all && strchr("ABeEFkKlLNOPrRSXy", c) == NULL)
dump_opt[c] = 1;
if (dump_opt[c])
dump_opt[c] += verbose;
Expand Down Expand Up @@ -9073,7 +9156,8 @@ main(int argc, char **argv)
checkpoint_pool, error);
}

} else if (target_is_spa || dump_opt['R'] || objset_id == 0) {
} else if (target_is_spa || dump_opt['R'] || dump_opt['B'] ||
objset_id == 0) {
zdb_set_skip_mmp(target);
error = spa_open_rewind(target, &spa, FTAG, policy,
NULL);
Expand Down Expand Up @@ -9209,7 +9293,10 @@ main(int argc, char **argv)
strerror(errno));
}
}
if (os != NULL) {
if (dump_opt['B']) {
dump_backup(target, objset_id,
argc > 0 ? argv[0] : NULL);
} else if (os != NULL) {
dump_objset(os);
} else if (zopt_object_args > 0 && !dump_opt['m']) {
dump_objset(spa->spa_meta_objset);
Expand Down
25 changes: 24 additions & 1 deletion man/man8/zdb.8
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
.\" Copyright (c) 2017 Lawrence Livermore National Security, LLC.
.\" Copyright (c) 2017 Intel Corporation.
.\"
.Dd October 7, 2020
.Dd June 4, 2023
.Dt ZDB 8
.Os
.
Expand All @@ -41,6 +41,13 @@
.Ar poolname Ns Op Ar / Ns Ar dataset Ns | Ns Ar objset-ID
.Op Ar object Ns | Ns Ar range Ns
.Nm
.Fl B
.Op Fl e Oo Fl V Oc Oo Fl p Ar path Oc Ns
.Op Fl U Ar cache
.Op Fl K Ar key
.Ar poolname Ns Ar / Ns Ar objset-ID
.Op Ar backup-flags
.Nm
.Fl C
.Op Fl A
.Op Fl U Ar cache
Expand Down Expand Up @@ -123,6 +130,22 @@ Display options:
Display statistics regarding the number, size
.Pq logical, physical and allocated
and deduplication of blocks.
.It Fl B , -backup
Generate a backup stream, similar to
.Nm zfs Cm send ,
but for the numeric objset ID, and without opening the dataset.
This can be useful in recovery scenarios if dataset metadata has become
corrupted but the dataset itself is readable.
The optional
.Ar flags
argument is a string of one or more of the letters
.Sy e ,
.Sy L ,
.Sy c ,
and
.Sy w ,
which correspond to the same flags in
.Xr zfs-send 8 .
.It Fl c , -checksum
Verify the checksum of all metadata blocks while printing block statistics
.Po see
Expand Down
3 changes: 1 addition & 2 deletions module/zfs/dmu_send.c
Original file line number Diff line number Diff line change
Expand Up @@ -1955,7 +1955,7 @@ setup_featureflags(struct dmu_send_params *dspp, objset_t *os,
{
dsl_dataset_t *to_ds = dspp->to_ds;
dsl_pool_t *dp = dspp->dp;
#ifdef _KERNEL

if (dmu_objset_type(os) == DMU_OST_ZFS) {
uint64_t version;
if (zfs_get_zplprop(os, ZFS_PROP_VERSION, &version) != 0)
Expand All @@ -1964,7 +1964,6 @@ setup_featureflags(struct dmu_send_params *dspp, objset_t *os,
if (version >= ZPL_VERSION_SA)
*featureflags |= DMU_BACKUP_FEATURE_SA_SPILL;
}
#endif

/* raw sends imply large_block_ok */
if ((dspp->rawok || dspp->large_block_ok) &&
Expand Down
2 changes: 1 addition & 1 deletion tests/runfiles/common.run
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ tests = ['zdb_002_pos', 'zdb_003_pos', 'zdb_004_pos', 'zdb_005_pos',
'zdb_block_size_histogram', 'zdb_checksum', 'zdb_decompress',
'zdb_display_block', 'zdb_encrypted', 'zdb_label_checksum',
'zdb_object_range_neg', 'zdb_object_range_pos', 'zdb_objset_id',
'zdb_decompress_zstd', 'zdb_recover', 'zdb_recover_2']
'zdb_decompress_zstd', 'zdb_recover', 'zdb_recover_2', 'zdb_backup']
pre =
post =
tags = ['functional', 'cli_root', 'zdb']
Expand Down
1 change: 1 addition & 0 deletions tests/zfs-tests/tests/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -572,6 +572,7 @@ nobase_dist_datadir_zfs_tests_tests_SCRIPTS += \
functional/cli_root/zdb/zdb_006_pos.ksh \
functional/cli_root/zdb/zdb_args_neg.ksh \
functional/cli_root/zdb/zdb_args_pos.ksh \
functional/cli_root/zdb/zdb_backup.ksh \
functional/cli_root/zdb/zdb_block_size_histogram.ksh \
functional/cli_root/zdb/zdb_checksum.ksh \
functional/cli_root/zdb/zdb_decompress.ksh \
Expand Down
55 changes: 55 additions & 0 deletions tests/zfs-tests/tests/functional/cli_root/zdb/zdb_backup.ksh
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
#!/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) 2023, Klara Inc.
#

. $STF_SUITE/include/libtest.shlib

write_count=8
blksize=131072

tmpfile=$TEST_BASE_DIR/tmpfile

function cleanup
{
datasetexists $TESTPOOL && destroy_pool $TESTPOOL
rm $tmpfile.1 $tmpfile.2
}

log_onexit cleanup

log_assert "Verify that zfs send and zdb -B produce the same stream"

verify_runnable "global"
verify_disk_count "$DISKS" 2

default_mirror_setup_noexit $DISKS
file_write -o create -w -f $TESTDIR/file -b $blksize -c $write_count

snap=$TESTPOOL/$TESTFS@snap
log_must zfs snapshot $snap
typeset -i objsetid=$(zfs get -Ho value objsetid $snap)

sync_pool $TESTPOOL

log_must eval "zfs send -ecL $snap > $tmpfile.1"
log_must eval "zdb -B $TESTPOOL/$objsetid ecL > $tmpfile.2"

typeset sum1=$(cat $tmpfile.1 | md5sum)
typeset sum2=$(cat $tmpfile.2 | md5sum)

log_must test "$sum1" = "$sum2"

log_pass "zfs send and zdb -B produce the same stream"

0 comments on commit 8653f1d

Please sign in to comment.