diff --git a/contrib/pyzfs/libzfs_core/_constants.py b/contrib/pyzfs/libzfs_core/_constants.py index e1364aa33f..5c285164b9 100644 --- a/contrib/pyzfs/libzfs_core/_constants.py +++ b/contrib/pyzfs/libzfs_core/_constants.py @@ -94,6 +94,7 @@ zfs_errno = enum_with_offset(1024, [ 'ZFS_ERR_UNKNOWN_SEND_STREAM_FEATURE', 'ZFS_ERR_EXPORT_IN_PROGRESS', 'ZFS_ERR_BOOKMARK_SOURCE_NOT_ANCESTOR', + 'ZFS_ERR_STREAM_TRUNCATED', ], {} ) diff --git a/contrib/pyzfs/libzfs_core/_error_translation.py b/contrib/pyzfs/libzfs_core/_error_translation.py index 7881715c9b..5a063c714a 100644 --- a/contrib/pyzfs/libzfs_core/_error_translation.py +++ b/contrib/pyzfs/libzfs_core/_error_translation.py @@ -469,6 +469,8 @@ def lzc_receive_translate_errors( raise lzc_exc.BadStream() if ret == ZFS_ERR_WRONG_PARENT: raise lzc_exc.WrongParent(_fs_name(snapname)) + if ret == zfs_errno.ZFS_ERR_STREAM_TRUNCATED: + raise lzc_exc.StreamTruncated() raise lzc_exc.StreamIOError(ret) diff --git a/contrib/pyzfs/libzfs_core/exceptions.py b/contrib/pyzfs/libzfs_core/exceptions.py index 9eeab1d7c9..6c571b7e2c 100644 --- a/contrib/pyzfs/libzfs_core/exceptions.py +++ b/contrib/pyzfs/libzfs_core/exceptions.py @@ -29,7 +29,8 @@ from ._constants import ( ZFS_ERR_NO_CHECKPOINT, ZFS_ERR_DEVRM_IN_PROGRESS, ZFS_ERR_VDEV_TOO_BIG, - ZFS_ERR_WRONG_PARENT + ZFS_ERR_WRONG_PARENT, + zfs_errno ) @@ -351,6 +352,11 @@ class StreamFeatureIncompatible(ZFSError): message = "Incompatible embedded feature with encrypted receive" +class StreamTruncated(ZFSError): + errno = zfs_errno.ZFS_ERR_STREAM_TRUNCATED + message = "incomplete stream" + + class ReceivePropertyFailure(MultipleOperationsFailure): message = "Receiving of properties failed for one or more reasons" diff --git a/contrib/pyzfs/libzfs_core/test/test_libzfs_core.py b/contrib/pyzfs/libzfs_core/test/test_libzfs_core.py index f47583b831..a841f96af3 100644 --- a/contrib/pyzfs/libzfs_core/test/test_libzfs_core.py +++ b/contrib/pyzfs/libzfs_core/test/test_libzfs_core.py @@ -2714,7 +2714,7 @@ class ZFSTest(unittest.TestCase): lzc.lzc_send(src, None, stream.fileno()) stream.seek(0) stream.truncate(1024 * 3) - with self.assertRaises(lzc_exc.BadStream): + with self.assertRaises(lzc_exc.StreamTruncated): lzc.lzc_receive_resumable(dst, stream.fileno()) # Resume token code from zfs_send_resume_token_to_nvlist() # XXX: if used more than twice move this code into an external func @@ -2771,7 +2771,7 @@ class ZFSTest(unittest.TestCase): lzc.lzc_send(snap2, snap1, stream.fileno()) stream.seek(0) stream.truncate(1024 * 3) - with self.assertRaises(lzc_exc.BadStream): + with self.assertRaises(lzc_exc.StreamTruncated): lzc.lzc_receive_resumable(dst2, stream.fileno()) # Resume token code from zfs_send_resume_token_to_nvlist() # format: --- diff --git a/include/sys/fs/zfs.h b/include/sys/fs/zfs.h index 2c7fd81626..3484b13e37 100644 --- a/include/sys/fs/zfs.h +++ b/include/sys/fs/zfs.h @@ -1330,6 +1330,7 @@ typedef enum { ZFS_ERR_UNKNOWN_SEND_STREAM_FEATURE, ZFS_ERR_EXPORT_IN_PROGRESS, ZFS_ERR_BOOKMARK_SOURCE_NOT_ANCESTOR, + ZFS_ERR_STREAM_TRUNCATED, } zfs_errno_t; /* diff --git a/lib/libzfs/libzfs_sendrecv.c b/lib/libzfs/libzfs_sendrecv.c index 39bad750ac..4571541716 100644 --- a/lib/libzfs/libzfs_sendrecv.c +++ b/lib/libzfs/libzfs_sendrecv.c @@ -4253,12 +4253,12 @@ recv_skip(libzfs_handle_t *hdl, int fd, boolean_t byteswap) static void recv_ecksum_set_aux(libzfs_handle_t *hdl, const char *target_snap, - boolean_t resumable) + boolean_t resumable, boolean_t checksum) { char target_fs[ZFS_MAX_DATASET_NAME_LEN]; - zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, - "checksum mismatch or incomplete stream")); + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, (checksum ? + "checksum mismatch" : "incomplete stream"))); if (!resumable) return; @@ -5206,7 +5206,9 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap, (void) zfs_error(hdl, EZFS_BADSTREAM, errbuf); break; case ECKSUM: - recv_ecksum_set_aux(hdl, destsnap, flags->resumable); + case ZFS_ERR_STREAM_TRUNCATED: + recv_ecksum_set_aux(hdl, destsnap, flags->resumable, + ioctl_err == ECKSUM); (void) zfs_error(hdl, EZFS_BADSTREAM, errbuf); break; case ENOTSUP: diff --git a/module/zfs/dmu_recv.c b/module/zfs/dmu_recv.c index 62881753f9..9c82cecc6a 100644 --- a/module/zfs/dmu_recv.c +++ b/module/zfs/dmu_recv.c @@ -1265,10 +1265,11 @@ receive_read(dmu_recv_cookie_t *drc, int len, void *buf) len - done, &resid); if (resid == len - done) { /* - * Note: ECKSUM indicates that the receive - * was interrupted and can potentially be resumed. + * Note: ECKSUM or ZFS_ERR_STREAM_TRUNCATED indicates + * that the receive was interrupted and can + * potentially be resumed. */ - drc->drc_err = SET_ERROR(ECKSUM); + drc->drc_err = SET_ERROR(ZFS_ERR_STREAM_TRUNCATED); } drc->drc_voff += len - done - resid; done = len - resid; diff --git a/tests/zfs-tests/cmd/libzfs_input_check/libzfs_input_check.c b/tests/zfs-tests/cmd/libzfs_input_check/libzfs_input_check.c index f0c548ee43..cb73df59bf 100644 --- a/tests/zfs-tests/cmd/libzfs_input_check/libzfs_input_check.c +++ b/tests/zfs-tests/cmd/libzfs_input_check/libzfs_input_check.c @@ -556,7 +556,7 @@ test_recv_new(const char *dataset, int fd) fnvlist_add_boolean(optional, "resumable"); fnvlist_add_uint64(optional, "action_handle", *action_handle); #endif - IOC_INPUT_TEST(ZFS_IOC_RECV_NEW, dataset, required, optional, ECKSUM); + IOC_INPUT_TEST(ZFS_IOC_RECV_NEW, dataset, required, optional, ZFS_ERR_STREAM_TRUNCATED); nvlist_free(props); nvlist_free(optional);