forked from openzfs/zfs
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
zdb: add -B option to generate backup stream
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
1 parent
2b9f8ba
commit 8653f1d
Showing
6 changed files
with
174 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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> | ||
|
@@ -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" | ||
|
@@ -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"); | ||
|
@@ -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 " | ||
|
@@ -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) | ||
{ | ||
|
@@ -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'}, | ||
|
@@ -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': | ||
|
@@ -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; | ||
|
@@ -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); | ||
|
@@ -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); | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
55 changes: 55 additions & 0 deletions
55
tests/zfs-tests/tests/functional/cli_root/zdb/zdb_backup.ksh
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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" |