diff --git a/cmd/zfs/zfs_main.c b/cmd/zfs/zfs_main.c index be8389518..4331df403 100644 --- a/cmd/zfs/zfs_main.c +++ b/cmd/zfs/zfs_main.c @@ -3995,13 +3995,11 @@ zfs_do_send(int argc, char **argv) if (strchr(argv[0], '@') == NULL || (fromname && strchr(fromname, '#') != NULL)) { char frombuf[ZFS_MAX_DATASET_NAME_LEN]; - enum lzc_send_flags lzc_flags = 0; if (flags.replicate || flags.doall || flags.props || - flags.dedup || flags.dryrun || flags.verbose || - flags.progress) { - (void) fprintf(stderr, - gettext("Error: " + flags.dedup || (strchr(argv[0], '@') == NULL && + (flags.dryrun || flags.verbose || flags.progress))) { + (void) fprintf(stderr, gettext("Error: " "Unsupported flag with filesystem or bookmark.\n")); return (1); } @@ -4010,15 +4008,6 @@ zfs_do_send(int argc, char **argv) if (zhp == NULL) return (1); - if (flags.largeblock) - lzc_flags |= LZC_SEND_FLAG_LARGE_BLOCK; - if (flags.embed_data) - lzc_flags |= LZC_SEND_FLAG_EMBED_DATA; - if (flags.compress) - lzc_flags |= LZC_SEND_FLAG_COMPRESS; - if (flags.raw) - lzc_flags |= LZC_SEND_FLAG_RAW; - if (fromname != NULL && (fromname[0] == '#' || fromname[0] == '@')) { /* @@ -4032,7 +4021,7 @@ zfs_do_send(int argc, char **argv) (void) strlcat(frombuf, fromname, sizeof (frombuf)); fromname = frombuf; } - err = zfs_send_one(zhp, fromname, STDOUT_FILENO, lzc_flags); + err = zfs_send_one(zhp, fromname, STDOUT_FILENO, flags); zfs_close(zhp); return (err != 0); } diff --git a/include/libzfs.h b/include/libzfs.h index b5c35c491..df8d738b7 100644 --- a/include/libzfs.h +++ b/include/libzfs.h @@ -677,7 +677,7 @@ typedef boolean_t (snapfilter_cb_t)(zfs_handle_t *, void *); extern int zfs_send(zfs_handle_t *, const char *, const char *, sendflags_t *, int, snapfilter_cb_t, void *, nvlist_t **); -extern int zfs_send_one(zfs_handle_t *, const char *, int, enum lzc_send_flags); +extern int zfs_send_one(zfs_handle_t *, const char *, int, sendflags_t flags); extern int zfs_send_resume(libzfs_handle_t *, sendflags_t *, int outfd, const char *); extern nvlist_t *zfs_send_resume_token_to_nvlist(libzfs_handle_t *hdl, diff --git a/lib/libzfs/libzfs_sendrecv.c b/lib/libzfs/libzfs_sendrecv.c index fddcd9c02..857abda3d 100644 --- a/lib/libzfs/libzfs_sendrecv.c +++ b/lib/libzfs/libzfs_sendrecv.c @@ -1242,16 +1242,14 @@ send_print_verbose(FILE *fout, const char *tosnap, const char *fromsnap, } } - if (size != 0) { - if (parsable) { - (void) fprintf(fout, "\t%llu", - (longlong_t)size); - } else { - char buf[16]; - zfs_nicebytes(size, buf, sizeof (buf)); - (void) fprintf(fout, dgettext(TEXT_DOMAIN, - " estimated size is %s"), buf); - } + if (parsable) { + (void) fprintf(fout, "\t%llu", + (longlong_t)size); + } else if (size != 0) { + char buf[16]; + zfs_nicebytes(size, buf, sizeof (buf)); + (void) fprintf(fout, dgettext(TEXT_DOMAIN, + " estimated size is %s"), buf); } (void) fprintf(fout, "\n"); } @@ -2113,17 +2111,42 @@ err_out: } int -zfs_send_one(zfs_handle_t *zhp, const char *from, int fd, - enum lzc_send_flags flags) +zfs_send_one(zfs_handle_t *zhp, const char *from, int fd, sendflags_t flags) { - int err; + int err = 0; libzfs_handle_t *hdl = zhp->zfs_hdl; - + enum lzc_send_flags lzc_flags = 0; + FILE *fout = (flags.verbose && flags.dryrun) ? stdout : stderr; char errbuf[1024]; + + if (flags.largeblock) + lzc_flags |= LZC_SEND_FLAG_LARGE_BLOCK; + if (flags.embed_data) + lzc_flags |= LZC_SEND_FLAG_EMBED_DATA; + if (flags.compress) + lzc_flags |= LZC_SEND_FLAG_COMPRESS; + if (flags.raw) + lzc_flags |= LZC_SEND_FLAG_RAW; + + if (flags.verbose) { + uint64_t size = 0; + err = lzc_send_space(zhp->zfs_name, from, lzc_flags, &size); + if (err == 0) { + send_print_verbose(fout, zhp->zfs_name, from, size, + flags.parsable); + } else { + (void) fprintf(stderr, "Cannot estimate send size: " + "%s\n", strerror(errno)); + } + } + + if (flags.dryrun) + return (err); + (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, "warning: cannot send '%s'"), zhp->zfs_name); - err = lzc_send(zhp->zfs_name, from, fd, flags); + err = lzc_send(zhp->zfs_name, from, fd, lzc_flags); if (err != 0) { switch (errno) { case EXDEV: diff --git a/man/man8/zfs.8 b/man/man8/zfs.8 index 89541eed9..eb5b6ffb9 100644 --- a/man/man8/zfs.8 +++ b/man/man8/zfs.8 @@ -171,7 +171,7 @@ .Ar snapshot .Nm .Cm send -.Op Fl Lcew +.Op Fl LPcenvw .Op Fl i Ar snapshot Ns | Ns Ar bookmark .Ar filesystem Ns | Ns Ar volume Ns | Ns Ar snapshot .Nm @@ -3253,14 +3253,14 @@ The receiving system must also support this feature. .It Fl v, -verbose Print verbose information about the stream package generated. This information includes a per-second report of how much data has been sent. -.El .Pp The format of the stream is committed. You will be able to receive your streams on future versions of ZFS. +.El .It Xo .Nm .Cm send -.Op Fl Lce +.Op Fl LPcenvw .Op Fl i Ar snapshot Ns | Ns Ar bookmark .Ar filesystem Ns | Ns Ar volume Ns | Ns Ar snapshot .Xc @@ -3287,6 +3287,8 @@ See for details on ZFS feature flags and the .Sy large_blocks feature. +.It Fl P, -parsable +Print machine-parsable verbose information about the stream package generated. .It Fl c, -compressed Generate a more compact stream by using compressed WRITE records for blocks which are compressed on disk and in memory @@ -3360,6 +3362,23 @@ character and following If the incremental target is a clone, the incremental source can be the origin snapshot, or an earlier snapshot in the origin's filesystem, or the origin's origin, etc. +.It Fl n, -dryrun +Do a dry-run +.Pq Qq No-op +send. +Do not generate any actual send data. +This is useful in conjunction with the +.Fl v +or +.Fl P +flags to determine what data will be sent. +In this case, the verbose output will be written to standard output +.Po contrast with a non-dry-run, where the stream is written to standard output +and the verbose output goes to standard error +.Pc . +.It Fl v, -verbose +Print verbose information about the stream package generated. +This information includes a per-second report of how much data has been sent. .El .It Xo .Nm diff --git a/tests/zfs-tests/tests/functional/cli_root/zfs_send/zfs_send_006_pos.ksh b/tests/zfs-tests/tests/functional/cli_root/zfs_send/zfs_send_006_pos.ksh index 801633e1f..1fe10a760 100755 --- a/tests/zfs-tests/tests/functional/cli_root/zfs_send/zfs_send_006_pos.ksh +++ b/tests/zfs-tests/tests/functional/cli_root/zfs_send/zfs_send_006_pos.ksh @@ -60,10 +60,8 @@ function get_estimate_size typeset total_size=$(zfs send $option $base_snapshot $snapshot \ 2>&1 | tail -1) fi - if [[ $options == *"P"* ]]; then - total_size=$(echo "$total_size" | awk '{print $2}') - else - total_size=$(echo "$total_size" | awk '{print $5}') + total_size=$(echo "$total_size" | awk '{print $NF}') + if [[ $options != *"P"* ]]; then total_size=${total_size%M} total_size=$(echo "$total_size * $block_count" | bc) fi @@ -106,14 +104,18 @@ for block_size in 64 128 256; do log_must dd if=/dev/urandom of=/$TESTPOOL/$TESTFS1/file$block_size \ bs=1M count=$block_size log_must zfs snapshot $TESTPOOL/$TESTFS1@snap$block_size + log_must zfs bookmark $TESTPOOL/$TESTFS1@snap$block_size \ + "$TESTPOOL/$TESTFS1#bmark$block_size" done full_snapshot="$TESTPOOL/$TESTFS1@snap64" -increamental_snapshot="$TESTPOOL/$TESTFS1@snap256" +incremental_snapshot="$TESTPOOL/$TESTFS1@snap256" +full_bookmark="$TESTPOOL/$TESTFS1#bmark64" +incremental_bookmark="$TESTPOOL/$TESTFS1#bmark256" full_size=$(zfs send $full_snapshot 2>&1 | wc -c) -increamental_size=$(zfs send $increamental_snapshot 2>&1 | wc -c) -increamental_send=$(zfs send -i $full_snapshot $increamental_snapshot 2>&1 | wc -c) +incremental_size=$(zfs send $incremental_snapshot 2>&1 | wc -c) +incremental_send=$(zfs send -i $full_snapshot $incremental_snapshot 2>&1 | wc -c) log_note "verify zfs send -nv" options="-nv" @@ -129,31 +131,35 @@ log_must verify_size_estimates $options $full_size log_note "verify zfs send -nv for multiple snapshot send" options="-nv" -refer_size=$(get_prop refer $increamental_snapshot) +refer_size=$(get_prop refer $incremental_snapshot) -estimate_size=$(get_estimate_size $increamental_snapshot $options) -log_must verify_size_estimates $options $increamental_size +estimate_size=$(get_estimate_size $incremental_snapshot $options) +log_must verify_size_estimates $options $incremental_size log_note "verify zfs send -vPn for multiple snapshot send" options="-vPn" -estimate_size=$(get_estimate_size $increamental_snapshot $options) -log_must verify_size_estimates $options $increamental_size +estimate_size=$(get_estimate_size $incremental_snapshot $options) +log_must verify_size_estimates $options $incremental_size -log_note "verify zfs send -inv for increamental send" +log_note "verify zfs send -inv for incremental send" options="-nvi" -refer_size=$(get_prop refer $increamental_snapshot) +refer_size=$(get_prop refer $incremental_snapshot) deduct_size=$(get_prop refer $full_snapshot) refer_size=$(echo "$refer_size - $deduct_size" | bc) -estimate_size=$(get_estimate_size $increamental_snapshot $options $full_snapshot) -log_must verify_size_estimates $options $increamental_send +estimate_size=$(get_estimate_size $incremental_snapshot $options $full_snapshot) +log_must verify_size_estimates $options $incremental_send +estimate_size=$(get_estimate_size $incremental_snapshot $options $full_bookmark) +log_must verify_size_estimates $options $incremental_send -log_note "verify zfs send -ivPn for increamental send" +log_note "verify zfs send -ivPn for incremental send" options="-vPni" -estimate_size=$(get_estimate_size $increamental_snapshot $options $full_snapshot) -log_must verify_size_estimates $options $increamental_send +estimate_size=$(get_estimate_size $incremental_snapshot $options $full_snapshot) +log_must verify_size_estimates $options $incremental_send +estimate_size=$(get_estimate_size $incremental_snapshot $options $full_bookmark) +log_must verify_size_estimates $options $incremental_send log_must zfs destroy -r $TESTPOOL/$TESTFS1