Avoid core dump on invalid redaction bookmark
libzfs aborts and dumps core on EINVAL from the kernel when trying to do a redacted send with a bookmark that is not a redaction bookmark. Move redacted bookmark validation into libzfs. Check if the bookmark given for redactions is actually a redaction bookmark. Print an error message and exit gracefully if it is not. Don't abort on EINVAL in zfs_send_one. Reviewed-by: Paul Dagnelie <pcd@delphix.com> Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov> Signed-off-by: Ryan Moeller <ryan@iXsystems.com> Closes #10138
This commit is contained in:
parent
4df8b2c373
commit
22df2457a7
|
@ -4408,24 +4408,6 @@ zfs_do_send(int argc, char **argv)
|
||||||
if (!(flags.replicate || flags.doall)) {
|
if (!(flags.replicate || flags.doall)) {
|
||||||
char frombuf[ZFS_MAX_DATASET_NAME_LEN];
|
char frombuf[ZFS_MAX_DATASET_NAME_LEN];
|
||||||
|
|
||||||
if (redactbook != NULL) {
|
|
||||||
if (strchr(argv[0], '@') == NULL) {
|
|
||||||
(void) fprintf(stderr, gettext("Error: Cannot "
|
|
||||||
"do a redacted send to a filesystem.\n"));
|
|
||||||
return (1);
|
|
||||||
}
|
|
||||||
if (strchr(redactbook, '#') != NULL) {
|
|
||||||
(void) fprintf(stderr, gettext("Error: "
|
|
||||||
"redaction bookmark argument must "
|
|
||||||
"not contain '#'\n"));
|
|
||||||
return (1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_DATASET);
|
|
||||||
if (zhp == NULL)
|
|
||||||
return (1);
|
|
||||||
|
|
||||||
if (fromname != NULL && (strchr(fromname, '#') == NULL &&
|
if (fromname != NULL && (strchr(fromname, '#') == NULL &&
|
||||||
strchr(fromname, '@') == NULL)) {
|
strchr(fromname, '@') == NULL)) {
|
||||||
/*
|
/*
|
||||||
|
@ -4454,6 +4436,10 @@ zfs_do_send(int argc, char **argv)
|
||||||
(void) strlcat(frombuf, tmpbuf, sizeof (frombuf));
|
(void) strlcat(frombuf, tmpbuf, sizeof (frombuf));
|
||||||
fromname = frombuf;
|
fromname = frombuf;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_DATASET);
|
||||||
|
if (zhp == NULL)
|
||||||
|
return (1);
|
||||||
err = zfs_send_one(zhp, fromname, STDOUT_FILENO, &flags,
|
err = zfs_send_one(zhp, fromname, STDOUT_FILENO, &flags,
|
||||||
redactbook);
|
redactbook);
|
||||||
zfs_close(zhp);
|
zfs_close(zhp);
|
||||||
|
|
|
@ -2816,6 +2816,7 @@ zfs_send_one(zfs_handle_t *zhp, const char *from, int fd, sendflags_t *flags,
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
libzfs_handle_t *hdl = zhp->zfs_hdl;
|
libzfs_handle_t *hdl = zhp->zfs_hdl;
|
||||||
|
char *name = zhp->zfs_name;
|
||||||
int orig_fd = fd;
|
int orig_fd = fd;
|
||||||
pthread_t ddtid, ptid;
|
pthread_t ddtid, ptid;
|
||||||
progress_arg_t pa = { 0 };
|
progress_arg_t pa = { 0 };
|
||||||
|
@ -2823,7 +2824,7 @@ zfs_send_one(zfs_handle_t *zhp, const char *from, int fd, sendflags_t *flags,
|
||||||
|
|
||||||
char errbuf[1024];
|
char errbuf[1024];
|
||||||
(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
|
(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
|
||||||
"warning: cannot send '%s'"), zhp->zfs_name);
|
"warning: cannot send '%s'"), name);
|
||||||
|
|
||||||
if (from != NULL && strchr(from, '@')) {
|
if (from != NULL && strchr(from, '@')) {
|
||||||
zfs_handle_t *from_zhp = zfs_open(hdl, from,
|
zfs_handle_t *from_zhp = zfs_open(hdl, from,
|
||||||
|
@ -2839,6 +2840,44 @@ zfs_send_one(zfs_handle_t *zhp, const char *from, int fd, sendflags_t *flags,
|
||||||
zfs_close(from_zhp);
|
zfs_close(from_zhp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (redactbook != NULL) {
|
||||||
|
char bookname[ZFS_MAX_DATASET_NAME_LEN];
|
||||||
|
nvlist_t *redact_snaps;
|
||||||
|
zfs_handle_t *book_zhp;
|
||||||
|
char *at, *pound;
|
||||||
|
int dsnamelen;
|
||||||
|
|
||||||
|
pound = strchr(redactbook, '#');
|
||||||
|
if (pound != NULL)
|
||||||
|
redactbook = pound + 1;
|
||||||
|
at = strchr(name, '@');
|
||||||
|
if (at == NULL) {
|
||||||
|
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
||||||
|
"cannot do a redacted send to a filesystem"));
|
||||||
|
return (zfs_error(hdl, EZFS_BADTYPE, errbuf));
|
||||||
|
}
|
||||||
|
dsnamelen = at - name;
|
||||||
|
if (snprintf(bookname, sizeof (bookname), "%.*s#%s",
|
||||||
|
dsnamelen, name, redactbook)
|
||||||
|
>= sizeof (bookname)) {
|
||||||
|
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
||||||
|
"invalid bookmark name"));
|
||||||
|
return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
|
||||||
|
}
|
||||||
|
book_zhp = zfs_open(hdl, bookname, ZFS_TYPE_BOOKMARK);
|
||||||
|
if (book_zhp == NULL)
|
||||||
|
return (-1);
|
||||||
|
if (nvlist_lookup_nvlist(book_zhp->zfs_props,
|
||||||
|
zfs_prop_to_name(ZFS_PROP_REDACT_SNAPS),
|
||||||
|
&redact_snaps) != 0 || redact_snaps == NULL) {
|
||||||
|
zfs_close(book_zhp);
|
||||||
|
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
||||||
|
"not a redaction bookmark"));
|
||||||
|
return (zfs_error(hdl, EZFS_BADTYPE, errbuf));
|
||||||
|
}
|
||||||
|
zfs_close(book_zhp);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Send fs properties
|
* Send fs properties
|
||||||
*/
|
*/
|
||||||
|
@ -2909,7 +2948,7 @@ zfs_send_one(zfs_handle_t *zhp, const char *from, int fd, sendflags_t *flags,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
err = lzc_send_redacted(zhp->zfs_name, from, fd,
|
err = lzc_send_redacted(name, from, fd,
|
||||||
lzc_flags_from_sendflags(flags), redactbook);
|
lzc_flags_from_sendflags(flags), redactbook);
|
||||||
|
|
||||||
if (flags->progress) {
|
if (flags->progress) {
|
||||||
|
@ -2948,7 +2987,7 @@ zfs_send_one(zfs_handle_t *zhp, const char *from, int fd, sendflags_t *flags,
|
||||||
|
|
||||||
case ENOENT:
|
case ENOENT:
|
||||||
case ESRCH:
|
case ESRCH:
|
||||||
if (lzc_exists(zhp->zfs_name)) {
|
if (lzc_exists(name)) {
|
||||||
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
||||||
"incremental source (%s) does not exist"),
|
"incremental source (%s) does not exist"),
|
||||||
from);
|
from);
|
||||||
|
@ -2967,7 +3006,9 @@ zfs_send_one(zfs_handle_t *zhp, const char *from, int fd, sendflags_t *flags,
|
||||||
return (zfs_error(hdl, EZFS_BUSY, errbuf));
|
return (zfs_error(hdl, EZFS_BUSY, errbuf));
|
||||||
|
|
||||||
case EDQUOT:
|
case EDQUOT:
|
||||||
|
case EFAULT:
|
||||||
case EFBIG:
|
case EFBIG:
|
||||||
|
case EINVAL:
|
||||||
case EIO:
|
case EIO:
|
||||||
case ENOLINK:
|
case ENOLINK:
|
||||||
case ENOSPC:
|
case ENOSPC:
|
||||||
|
@ -2975,7 +3016,6 @@ zfs_send_one(zfs_handle_t *zhp, const char *from, int fd, sendflags_t *flags,
|
||||||
case ENXIO:
|
case ENXIO:
|
||||||
case EPIPE:
|
case EPIPE:
|
||||||
case ERANGE:
|
case ERANGE:
|
||||||
case EFAULT:
|
|
||||||
case EROFS:
|
case EROFS:
|
||||||
zfs_error_aux(hdl, strerror(errno));
|
zfs_error_aux(hdl, strerror(errno));
|
||||||
return (zfs_error(hdl, EZFS_BADBACKUP, errbuf));
|
return (zfs_error(hdl, EZFS_BADBACKUP, errbuf));
|
||||||
|
|
|
@ -222,12 +222,12 @@ log_must zfs redact "$DATASET@$TESTSNAP" "$TESTBM" "$DATASET@$TESTSNAP2"
|
||||||
log_must eval "zfs get all $DATASET#$TESTBM | grep redact_snaps"
|
log_must eval "zfs get all $DATASET#$TESTBM | grep redact_snaps"
|
||||||
## copy the redaction bookmark
|
## copy the redaction bookmark
|
||||||
log_must zfs bookmark "$DATASET#$TESTBM" "#$TESTBMCOPY"
|
log_must zfs bookmark "$DATASET#$TESTBM" "#$TESTBMCOPY"
|
||||||
log_must eval "zfs send --redact "$TESTBMCOPY" -i $DATASET@$TESTSNAP $DATASET@$TESTSNAP2 2>&1 | head -n 100 | grep 'internal error: Invalid argument'"
|
|
||||||
log_mustnot eval "zfs get all $DATASET#$TESTBMCOPY | grep redact_snaps"
|
log_mustnot eval "zfs get all $DATASET#$TESTBMCOPY | grep redact_snaps"
|
||||||
|
log_must eval "zfs send --redact "$TESTBMCOPY" -i $DATASET@$TESTSNAP $DATASET@$TESTSNAP2 2>&1 | head -n 100 | grep 'not a redaction bookmark'"
|
||||||
# try the above again after destroying the source bookmark, preventive measure for future work
|
# try the above again after destroying the source bookmark, preventive measure for future work
|
||||||
log_must zfs destroy "$DATASET#$TESTBM"
|
log_must zfs destroy "$DATASET#$TESTBM"
|
||||||
log_must eval "zfs send --redact "$TESTBMCOPY" -i $DATASET@$TESTSNAP $DATASET@$TESTSNAP2 2>&1 | head -n 100 | grep 'internal error: Invalid argument'"
|
|
||||||
log_mustnot eval "zfs get all $DATASET#$TESTBMCOPY | grep redact_snaps"
|
log_mustnot eval "zfs get all $DATASET#$TESTBMCOPY | grep redact_snaps"
|
||||||
|
log_must eval "zfs send --redact "$TESTBMCOPY" -i $DATASET@$TESTSNAP $DATASET@$TESTSNAP2 2>&1 | head -n 100 | grep 'not a redaction bookmark'"
|
||||||
## cleanup
|
## cleanup
|
||||||
log_must eval "destroy_dataset $DATASET@$TESTSNAP2"
|
log_must eval "destroy_dataset $DATASET@$TESTSNAP2"
|
||||||
log_must zfs destroy "$DATASET#$TESTBMCOPY"
|
log_must zfs destroy "$DATASET#$TESTBMCOPY"
|
||||||
|
|
|
@ -79,7 +79,7 @@ log_mustnot zfs redact testpool2/recvfs@snap2 book7 testpool2/recvfs@snap
|
||||||
|
|
||||||
# Non-redaction bookmark cannot be sent and produces invalid argument error
|
# Non-redaction bookmark cannot be sent and produces invalid argument error
|
||||||
log_must zfs bookmark "$sendfs@snap1" "$sendfs#book8"
|
log_must zfs bookmark "$sendfs@snap1" "$sendfs#book8"
|
||||||
log_must eval "zfs send --redact book8 -i $sendfs@snap1 $sendfs@snap2 2>&1 | head -n 100 | grep 'internal error: Invalid argument'"
|
log_must eval "zfs send --redact book8 -i $sendfs@snap1 $sendfs@snap2 2>&1 | head -n 100 | grep 'not a redaction bookmark'"
|
||||||
|
|
||||||
# Error messages for common usage errors
|
# Error messages for common usage errors
|
||||||
log_mustnot_expect "not contain '#'" zfs redact $sendfs@snap1 \#book $sendfs@snap2
|
log_mustnot_expect "not contain '#'" zfs redact $sendfs@snap1 \#book $sendfs@snap2
|
||||||
|
|
Loading…
Reference in New Issue