mirror of
				https://git.proxmox.com/git/mirror_zfs.git
				synced 2025-10-26 18:05:04 +03:00 
			
		
		
		
	Implement zfs_ioc_recv_new() for OpenZFS 2605
Adds ZFS_IOC_RECV_NEW for resumable streams and preserves the legacy ZFS_IOC_RECV user/kernel interface. The new interface supports all stream options but is currently only used for resumable streams. This way updated user space utilities will interoperate with older kernel modules. ZFS_IOC_RECV_NEW is modeled after the existing ZFS_IOC_SEND_NEW handler. Non-Linux OpenZFS platforms have opted to change the legacy interface in an incompatible fashion instead of adding a new ioctl. Signed-off-by: Brian Behlendorf <behlendorf1@llnl.gov>
This commit is contained in:
		
							parent
							
								
									8c62a0d0f3
								
							
						
					
					
						commit
						43e52eddb1
					
				@ -69,6 +69,9 @@ int lzc_receive_resumable(const char *, nvlist_t *, const char *,
 | 
			
		||||
    boolean_t, int);
 | 
			
		||||
int lzc_receive_with_header(const char *, nvlist_t *, const char *, boolean_t,
 | 
			
		||||
    boolean_t, int, const struct dmu_replay_record *);
 | 
			
		||||
int lzc_receive_one(const char *, nvlist_t *, const char *, boolean_t,
 | 
			
		||||
    boolean_t, int, const struct dmu_replay_record *, int, uint64_t *,
 | 
			
		||||
    uint64_t *, uint64_t *, nvlist_t **);
 | 
			
		||||
 | 
			
		||||
boolean_t lzc_exists(const char *);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -232,6 +232,7 @@ typedef enum {
 | 
			
		||||
 | 
			
		||||
#define	ZPROP_SOURCE_VAL_RECVD	"$recvd"
 | 
			
		||||
#define	ZPROP_N_MORE_ERRORS	"N_MORE_ERRORS"
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Dataset flag implemented as a special entry in the props zap object
 | 
			
		||||
 * indicating that the dataset has received properties on or after
 | 
			
		||||
@ -923,7 +924,7 @@ typedef struct ddt_histogram {
 | 
			
		||||
 */
 | 
			
		||||
typedef enum zfs_ioc {
 | 
			
		||||
	/*
 | 
			
		||||
	 * Illumos - 70/128 numbers reserved.
 | 
			
		||||
	 * Illumos - 71/128 numbers reserved.
 | 
			
		||||
	 */
 | 
			
		||||
	ZFS_IOC_FIRST =	('Z' << 8),
 | 
			
		||||
	ZFS_IOC = ZFS_IOC_FIRST,
 | 
			
		||||
@ -997,6 +998,7 @@ typedef enum zfs_ioc {
 | 
			
		||||
	ZFS_IOC_BOOKMARK,
 | 
			
		||||
	ZFS_IOC_GET_BOOKMARKS,
 | 
			
		||||
	ZFS_IOC_DESTROY_BOOKMARKS,
 | 
			
		||||
	ZFS_IOC_RECV_NEW,
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Linux - 3/64 numbers reserved.
 | 
			
		||||
 | 
			
		||||
@ -389,15 +389,14 @@ typedef struct zfs_cmd {
 | 
			
		||||
	uint64_t	zc_iflags;		/* internal to zfs(7fs) */
 | 
			
		||||
	zfs_share_t	zc_share;
 | 
			
		||||
	dmu_objset_stats_t zc_objset_stats;
 | 
			
		||||
	dmu_replay_record_t zc_begin_record;
 | 
			
		||||
	struct drr_begin zc_begin_record;
 | 
			
		||||
	zinject_record_t zc_inject_record;
 | 
			
		||||
	uint32_t	zc_defer_destroy;
 | 
			
		||||
	uint32_t	zc_flags;
 | 
			
		||||
	uint64_t	zc_action_handle;
 | 
			
		||||
	int		zc_cleanup_fd;
 | 
			
		||||
	uint8_t		zc_simple;
 | 
			
		||||
	boolean_t	zc_resumable;
 | 
			
		||||
	uint8_t		zc_pad[2];		/* alignment */
 | 
			
		||||
	uint8_t		zc_pad[3];		/* alignment */
 | 
			
		||||
	uint64_t	zc_sendobj;
 | 
			
		||||
	uint64_t	zc_fromobj;
 | 
			
		||||
	uint64_t	zc_createtxg;
 | 
			
		||||
 | 
			
		||||
@ -2960,24 +2960,31 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
 | 
			
		||||
    avl_tree_t *stream_avl, char **top_zfs, int cleanup_fd,
 | 
			
		||||
    uint64_t *action_handlep, const char *finalsnap)
 | 
			
		||||
{
 | 
			
		||||
	zfs_cmd_t zc = {"\0"};
 | 
			
		||||
	time_t begin_time;
 | 
			
		||||
	int ioctl_err, ioctl_errno, err;
 | 
			
		||||
	char *cp;
 | 
			
		||||
	struct drr_begin *drrb = &drr->drr_u.drr_begin;
 | 
			
		||||
	char errbuf[1024];
 | 
			
		||||
	char prop_errbuf[1024];
 | 
			
		||||
	const char *chopprefix;
 | 
			
		||||
	boolean_t newfs = B_FALSE;
 | 
			
		||||
	boolean_t stream_wantsnewfs;
 | 
			
		||||
	boolean_t newprops = B_FALSE;
 | 
			
		||||
	uint64_t read_bytes = 0;
 | 
			
		||||
	uint64_t errflags = 0;
 | 
			
		||||
	uint64_t parent_snapguid = 0;
 | 
			
		||||
	prop_changelist_t *clp = NULL;
 | 
			
		||||
	nvlist_t *snapprops_nvlist = NULL;
 | 
			
		||||
	zprop_errflags_t prop_errflags;
 | 
			
		||||
	nvlist_t *prop_errors = NULL;
 | 
			
		||||
	boolean_t recursive;
 | 
			
		||||
	char *snapname = NULL;
 | 
			
		||||
	char destsnap[MAXPATHLEN * 2];
 | 
			
		||||
	char origin[MAXNAMELEN];
 | 
			
		||||
	char name[MAXPATHLEN];
 | 
			
		||||
	nvlist_t *props = NULL;
 | 
			
		||||
 | 
			
		||||
	begin_time = time(NULL);
 | 
			
		||||
	bzero(origin, MAXNAMELEN);
 | 
			
		||||
 | 
			
		||||
	(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
 | 
			
		||||
	    "cannot receive"));
 | 
			
		||||
@ -2988,24 +2995,19 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
 | 
			
		||||
	if (stream_avl != NULL) {
 | 
			
		||||
		nvlist_t *fs = fsavl_find(stream_avl, drrb->drr_toguid,
 | 
			
		||||
		    &snapname);
 | 
			
		||||
		nvlist_t *props;
 | 
			
		||||
		int ret;
 | 
			
		||||
 | 
			
		||||
		(void) nvlist_lookup_uint64(fs, "parentfromsnap",
 | 
			
		||||
		    &parent_snapguid);
 | 
			
		||||
		err = nvlist_lookup_nvlist(fs, "props", &props);
 | 
			
		||||
		if (err)
 | 
			
		||||
		if (err) {
 | 
			
		||||
			VERIFY(0 == nvlist_alloc(&props, NV_UNIQUE_NAME, 0));
 | 
			
		||||
			newprops = B_TRUE;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (flags->canmountoff) {
 | 
			
		||||
			VERIFY(0 == nvlist_add_uint64(props,
 | 
			
		||||
			    zfs_prop_to_name(ZFS_PROP_CANMOUNT), 0));
 | 
			
		||||
		}
 | 
			
		||||
		ret = zcmd_write_src_nvlist(hdl, &zc, props);
 | 
			
		||||
		if (err)
 | 
			
		||||
			nvlist_free(props);
 | 
			
		||||
		if (ret != 0)
 | 
			
		||||
			return (-1);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cp = NULL;
 | 
			
		||||
@ -3026,7 +3028,8 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
 | 
			
		||||
		if (strchr(tosnap, '@')) {
 | 
			
		||||
			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "invalid "
 | 
			
		||||
			    "argument - snapshot not allowed with -e"));
 | 
			
		||||
			return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
 | 
			
		||||
			err = zfs_error(hdl, EZFS_INVALIDNAME, errbuf);
 | 
			
		||||
			goto out;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		chopprefix = strrchr(sendfs, '/');
 | 
			
		||||
@ -3053,7 +3056,8 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
 | 
			
		||||
		if (strchr(tosnap, '@')) {
 | 
			
		||||
			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "invalid "
 | 
			
		||||
			    "argument - snapshot not allowed with -d"));
 | 
			
		||||
			return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
 | 
			
		||||
			err = zfs_error(hdl, EZFS_INVALIDNAME, errbuf);
 | 
			
		||||
			goto out;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		chopprefix = strchr(drrb->drr_toname, '/');
 | 
			
		||||
@ -3071,7 +3075,8 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
 | 
			
		||||
			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 | 
			
		||||
			    "cannot specify snapshot name for multi-snapshot "
 | 
			
		||||
			    "stream"));
 | 
			
		||||
			return (zfs_error(hdl, EZFS_BADSTREAM, errbuf));
 | 
			
		||||
			err = zfs_error(hdl, EZFS_BADSTREAM, errbuf);
 | 
			
		||||
			goto out;
 | 
			
		||||
		}
 | 
			
		||||
		chopprefix = drrb->drr_toname + strlen(drrb->drr_toname);
 | 
			
		||||
	}
 | 
			
		||||
@ -3083,35 +3088,35 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
 | 
			
		||||
	    chopprefix[0] == '\0');
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Determine name of destination snapshot, store in zc_value.
 | 
			
		||||
	 * Determine name of destination snapshot.
 | 
			
		||||
	 */
 | 
			
		||||
	(void) strcpy(zc.zc_value, tosnap);
 | 
			
		||||
	(void) strlcat(zc.zc_value, chopprefix, sizeof (zc.zc_value));
 | 
			
		||||
	(void) strcpy(destsnap, tosnap);
 | 
			
		||||
	(void) strlcat(destsnap, chopprefix, sizeof (destsnap));
 | 
			
		||||
	free(cp);
 | 
			
		||||
	if (!zfs_name_valid(zc.zc_value, ZFS_TYPE_SNAPSHOT)) {
 | 
			
		||||
		zcmd_free_nvlists(&zc);
 | 
			
		||||
		return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
 | 
			
		||||
	if (!zfs_name_valid(destsnap, ZFS_TYPE_SNAPSHOT)) {
 | 
			
		||||
		err = zfs_error(hdl, EZFS_INVALIDNAME, errbuf);
 | 
			
		||||
		goto out;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Determine the name of the origin snapshot, store in zc_string.
 | 
			
		||||
	 * Determine the name of the origin snapshot.
 | 
			
		||||
	 */
 | 
			
		||||
	if (drrb->drr_flags & DRR_FLAG_CLONE) {
 | 
			
		||||
		if (guid_to_name(hdl, zc.zc_value,
 | 
			
		||||
		    drrb->drr_fromguid, B_FALSE, zc.zc_string) != 0) {
 | 
			
		||||
			zcmd_free_nvlists(&zc);
 | 
			
		||||
		if (guid_to_name(hdl, destsnap,
 | 
			
		||||
		    drrb->drr_fromguid, B_FALSE, origin) != 0) {
 | 
			
		||||
			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 | 
			
		||||
			    "local origin for clone %s does not exist"),
 | 
			
		||||
			    zc.zc_value);
 | 
			
		||||
			return (zfs_error(hdl, EZFS_NOENT, errbuf));
 | 
			
		||||
			    destsnap);
 | 
			
		||||
			err = zfs_error(hdl, EZFS_NOENT, errbuf);
 | 
			
		||||
			goto out;
 | 
			
		||||
		}
 | 
			
		||||
		if (flags->verbose)
 | 
			
		||||
			(void) printf("found clone origin %s\n", zc.zc_string);
 | 
			
		||||
			(void) printf("found clone origin %s\n", origin);
 | 
			
		||||
	} else if (originsnap) {
 | 
			
		||||
		(void) strncpy(zc.zc_string, originsnap, ZFS_MAXNAMELEN);
 | 
			
		||||
		(void) strncpy(origin, originsnap, ZFS_MAXNAMELEN);
 | 
			
		||||
		if (flags->verbose)
 | 
			
		||||
			(void) printf("using provided clone origin %s\n",
 | 
			
		||||
			    zc.zc_string);
 | 
			
		||||
			    origin);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	boolean_t resuming = DMU_GET_FEATUREFLAGS(drrb->drr_versioninfo) &
 | 
			
		||||
@ -3127,18 +3132,18 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
 | 
			
		||||
		(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
 | 
			
		||||
		    "cannot receive new filesystem stream"));
 | 
			
		||||
 | 
			
		||||
		(void) strcpy(zc.zc_name, zc.zc_value);
 | 
			
		||||
		cp = strrchr(zc.zc_name, '/');
 | 
			
		||||
		(void) strcpy(name, destsnap);
 | 
			
		||||
		cp = strrchr(name, '/');
 | 
			
		||||
		if (cp)
 | 
			
		||||
			*cp = '\0';
 | 
			
		||||
		if (cp &&
 | 
			
		||||
		    !zfs_dataset_exists(hdl, zc.zc_name, ZFS_TYPE_DATASET)) {
 | 
			
		||||
		    !zfs_dataset_exists(hdl, name, ZFS_TYPE_DATASET)) {
 | 
			
		||||
			char suffix[ZFS_MAXNAMELEN];
 | 
			
		||||
			(void) strcpy(suffix, strrchr(zc.zc_value, '/'));
 | 
			
		||||
			if (guid_to_name(hdl, zc.zc_name, parent_snapguid,
 | 
			
		||||
			    B_FALSE, zc.zc_value) == 0) {
 | 
			
		||||
				*strchr(zc.zc_value, '@') = '\0';
 | 
			
		||||
				(void) strcat(zc.zc_value, suffix);
 | 
			
		||||
			(void) strcpy(suffix, strrchr(destsnap, '/'));
 | 
			
		||||
			if (guid_to_name(hdl, name, parent_snapguid,
 | 
			
		||||
			    B_FALSE, destsnap) == 0) {
 | 
			
		||||
				*strchr(destsnap, '@') = '\0';
 | 
			
		||||
				(void) strcat(destsnap, suffix);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
@ -3149,8 +3154,8 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
 | 
			
		||||
		(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
 | 
			
		||||
		    "cannot receive incremental stream"));
 | 
			
		||||
 | 
			
		||||
		(void) strcpy(zc.zc_name, zc.zc_value);
 | 
			
		||||
		*strchr(zc.zc_name, '@') = '\0';
 | 
			
		||||
		(void) strcpy(name, destsnap);
 | 
			
		||||
		*strchr(name, '@') = '\0';
 | 
			
		||||
 | 
			
		||||
		/*
 | 
			
		||||
		 * If the exact receive path was specified and this is the
 | 
			
		||||
@ -3159,23 +3164,26 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
 | 
			
		||||
		 */
 | 
			
		||||
		if ((flags->isprefix || (*(chopprefix = drrb->drr_toname +
 | 
			
		||||
		    strlen(sendfs)) != '\0' && *chopprefix != '@')) &&
 | 
			
		||||
		    !zfs_dataset_exists(hdl, zc.zc_name, ZFS_TYPE_DATASET)) {
 | 
			
		||||
		    !zfs_dataset_exists(hdl, name, ZFS_TYPE_DATASET)) {
 | 
			
		||||
			char snap[ZFS_MAXNAMELEN];
 | 
			
		||||
			(void) strcpy(snap, strchr(zc.zc_value, '@'));
 | 
			
		||||
			if (guid_to_name(hdl, zc.zc_name, drrb->drr_fromguid,
 | 
			
		||||
			    B_FALSE, zc.zc_value) == 0) {
 | 
			
		||||
				*strchr(zc.zc_value, '@') = '\0';
 | 
			
		||||
				(void) strcat(zc.zc_value, snap);
 | 
			
		||||
			(void) strcpy(snap, strchr(destsnap, '@'));
 | 
			
		||||
			if (guid_to_name(hdl, name, drrb->drr_fromguid,
 | 
			
		||||
			    B_FALSE, destsnap) == 0) {
 | 
			
		||||
				*strchr(destsnap, '@') = '\0';
 | 
			
		||||
				(void) strcat(destsnap, snap);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	(void) strcpy(zc.zc_name, zc.zc_value);
 | 
			
		||||
	*strchr(zc.zc_name, '@') = '\0';
 | 
			
		||||
	(void) strcpy(name, destsnap);
 | 
			
		||||
	*strchr(name, '@') = '\0';
 | 
			
		||||
 | 
			
		||||
	if (zfs_dataset_exists(hdl, zc.zc_name, ZFS_TYPE_DATASET)) {
 | 
			
		||||
	if (zfs_dataset_exists(hdl, name, ZFS_TYPE_DATASET)) {
 | 
			
		||||
		zfs_cmd_t zc = {"\0"};
 | 
			
		||||
		zfs_handle_t *zhp;
 | 
			
		||||
 | 
			
		||||
		(void) strcpy(zc.zc_name, name);
 | 
			
		||||
 | 
			
		||||
		/*
 | 
			
		||||
		 * Destination fs exists.  It must be one of these cases:
 | 
			
		||||
		 *  - an incremental send stream
 | 
			
		||||
@ -3186,39 +3194,37 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
 | 
			
		||||
		 */
 | 
			
		||||
		if (stream_wantsnewfs) {
 | 
			
		||||
			if (!flags->force) {
 | 
			
		||||
				zcmd_free_nvlists(&zc);
 | 
			
		||||
				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 | 
			
		||||
				    "destination '%s' exists\n"
 | 
			
		||||
				    "must specify -F to overwrite it"),
 | 
			
		||||
				    zc.zc_name);
 | 
			
		||||
				return (zfs_error(hdl, EZFS_EXISTS, errbuf));
 | 
			
		||||
				    "must specify -F to overwrite it"), name);
 | 
			
		||||
				err = zfs_error(hdl, EZFS_EXISTS, errbuf);
 | 
			
		||||
				goto out;
 | 
			
		||||
			}
 | 
			
		||||
			if (ioctl(hdl->libzfs_fd, ZFS_IOC_SNAPSHOT_LIST_NEXT,
 | 
			
		||||
			    &zc) == 0) {
 | 
			
		||||
				zcmd_free_nvlists(&zc);
 | 
			
		||||
				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 | 
			
		||||
				    "destination has snapshots (eg. %s)\n"
 | 
			
		||||
				    "must destroy them to overwrite it"),
 | 
			
		||||
				    zc.zc_name);
 | 
			
		||||
				return (zfs_error(hdl, EZFS_EXISTS, errbuf));
 | 
			
		||||
				    name);
 | 
			
		||||
				err = zfs_error(hdl, EZFS_EXISTS, errbuf);
 | 
			
		||||
				goto out;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if ((zhp = zfs_open(hdl, zc.zc_name,
 | 
			
		||||
		if ((zhp = zfs_open(hdl, name,
 | 
			
		||||
		    ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME)) == NULL) {
 | 
			
		||||
			zcmd_free_nvlists(&zc);
 | 
			
		||||
			return (-1);
 | 
			
		||||
			err = -1;
 | 
			
		||||
			goto out;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (stream_wantsnewfs &&
 | 
			
		||||
		    zhp->zfs_dmustats.dds_origin[0]) {
 | 
			
		||||
			zcmd_free_nvlists(&zc);
 | 
			
		||||
			zfs_close(zhp);
 | 
			
		||||
			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 | 
			
		||||
			    "destination '%s' is a clone\n"
 | 
			
		||||
			    "must destroy it to overwrite it"),
 | 
			
		||||
			    zc.zc_name);
 | 
			
		||||
			return (zfs_error(hdl, EZFS_EXISTS, errbuf));
 | 
			
		||||
			    "must destroy it to overwrite it"), name);
 | 
			
		||||
			err = zfs_error(hdl, EZFS_EXISTS, errbuf);
 | 
			
		||||
			goto out;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (!flags->dryrun && zhp->zfs_type == ZFS_TYPE_FILESYSTEM &&
 | 
			
		||||
@ -3227,14 +3233,14 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
 | 
			
		||||
			clp = changelist_gather(zhp, ZFS_PROP_NAME, 0, 0);
 | 
			
		||||
			if (clp == NULL) {
 | 
			
		||||
				zfs_close(zhp);
 | 
			
		||||
				zcmd_free_nvlists(&zc);
 | 
			
		||||
				return (-1);
 | 
			
		||||
				err = -1;
 | 
			
		||||
				goto out;
 | 
			
		||||
			}
 | 
			
		||||
			if (changelist_prefix(clp) != 0) {
 | 
			
		||||
				changelist_free(clp);
 | 
			
		||||
				zfs_close(zhp);
 | 
			
		||||
				zcmd_free_nvlists(&zc);
 | 
			
		||||
				return (-1);
 | 
			
		||||
				err = -1;
 | 
			
		||||
				goto out;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
@ -3259,11 +3265,11 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
 | 
			
		||||
		 * contained no slash character).
 | 
			
		||||
		 */
 | 
			
		||||
		if (!stream_wantsnewfs ||
 | 
			
		||||
		    (cp = strrchr(zc.zc_name, '/')) == NULL) {
 | 
			
		||||
			zcmd_free_nvlists(&zc);
 | 
			
		||||
		    (cp = strrchr(name, '/')) == NULL) {
 | 
			
		||||
			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 | 
			
		||||
			    "destination '%s' does not exist"), zc.zc_name);
 | 
			
		||||
			return (zfs_error(hdl, EZFS_NOENT, errbuf));
 | 
			
		||||
			    "destination '%s' does not exist"), name);
 | 
			
		||||
			err = zfs_error(hdl, EZFS_NOENT, errbuf);
 | 
			
		||||
			goto out;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/*
 | 
			
		||||
@ -3273,45 +3279,34 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
 | 
			
		||||
		*cp = '\0';
 | 
			
		||||
 | 
			
		||||
		if (flags->isprefix && !flags->istail && !flags->dryrun &&
 | 
			
		||||
		    create_parents(hdl, zc.zc_value, strlen(tosnap)) != 0) {
 | 
			
		||||
			zcmd_free_nvlists(&zc);
 | 
			
		||||
			return (zfs_error(hdl, EZFS_BADRESTORE, errbuf));
 | 
			
		||||
		    create_parents(hdl, destsnap, strlen(tosnap)) != 0) {
 | 
			
		||||
			err = zfs_error(hdl, EZFS_BADRESTORE, errbuf);
 | 
			
		||||
			goto out;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		newfs = B_TRUE;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	zc.zc_begin_record = *drr_noswap;
 | 
			
		||||
	zc.zc_cookie = infd;
 | 
			
		||||
	zc.zc_guid = flags->force;
 | 
			
		||||
	zc.zc_resumable = flags->resumable;
 | 
			
		||||
	if (flags->verbose) {
 | 
			
		||||
		(void) printf("%s %s stream of %s into %s\n",
 | 
			
		||||
		    flags->dryrun ? "would receive" : "receiving",
 | 
			
		||||
		    drrb->drr_fromguid ? "incremental" : "full",
 | 
			
		||||
		    drrb->drr_toname, zc.zc_value);
 | 
			
		||||
		    drrb->drr_toname, destsnap);
 | 
			
		||||
		(void) fflush(stdout);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (flags->dryrun) {
 | 
			
		||||
		zcmd_free_nvlists(&zc);
 | 
			
		||||
		return (recv_skip(hdl, infd, flags->byteswap));
 | 
			
		||||
		err = recv_skip(hdl, infd, flags->byteswap);
 | 
			
		||||
		goto out;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	zc.zc_nvlist_dst = (uint64_t)(uintptr_t)prop_errbuf;
 | 
			
		||||
	zc.zc_nvlist_dst_size = sizeof (prop_errbuf);
 | 
			
		||||
	zc.zc_cleanup_fd = cleanup_fd;
 | 
			
		||||
	zc.zc_action_handle = *action_handlep;
 | 
			
		||||
 | 
			
		||||
	err = ioctl_err = zfs_ioctl(hdl, ZFS_IOC_RECV, &zc);
 | 
			
		||||
	ioctl_errno = errno;
 | 
			
		||||
	prop_errflags = (zprop_errflags_t)zc.zc_obj;
 | 
			
		||||
	err = ioctl_err = lzc_receive_one(destsnap, props, origin,
 | 
			
		||||
	    flags->force, flags->resumable, infd, drr_noswap, cleanup_fd,
 | 
			
		||||
	    &read_bytes, &errflags, action_handlep, &prop_errors);
 | 
			
		||||
	ioctl_errno = ioctl_err;
 | 
			
		||||
	prop_errflags = errflags;
 | 
			
		||||
 | 
			
		||||
	if (err == 0) {
 | 
			
		||||
		nvlist_t *prop_errors;
 | 
			
		||||
		VERIFY(0 == nvlist_unpack((void *)(uintptr_t)zc.zc_nvlist_dst,
 | 
			
		||||
		    zc.zc_nvlist_dst_size, &prop_errors, 0));
 | 
			
		||||
 | 
			
		||||
		nvpair_t *prop_err = NULL;
 | 
			
		||||
 | 
			
		||||
		while ((prop_err = nvlist_next_nvpair(prop_errors,
 | 
			
		||||
@ -3344,25 +3339,20 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
 | 
			
		||||
				(void) snprintf(tbuf, sizeof (tbuf),
 | 
			
		||||
				    dgettext(TEXT_DOMAIN,
 | 
			
		||||
				    "cannot receive %s property on %s"),
 | 
			
		||||
				    nvpair_name(prop_err), zc.zc_name);
 | 
			
		||||
				    nvpair_name(prop_err), name);
 | 
			
		||||
				zfs_setprop_error(hdl, prop, intval, tbuf);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		nvlist_free(prop_errors);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	zc.zc_nvlist_dst = 0;
 | 
			
		||||
	zc.zc_nvlist_dst_size = 0;
 | 
			
		||||
	zcmd_free_nvlists(&zc);
 | 
			
		||||
 | 
			
		||||
	if (err == 0 && snapprops_nvlist) {
 | 
			
		||||
		zfs_cmd_t zc2 = {"\0"};
 | 
			
		||||
		zfs_cmd_t zc = {"\0"};
 | 
			
		||||
 | 
			
		||||
		(void) strcpy(zc2.zc_name, zc.zc_value);
 | 
			
		||||
		zc2.zc_cookie = B_TRUE; /* received */
 | 
			
		||||
		if (zcmd_write_src_nvlist(hdl, &zc2, snapprops_nvlist) == 0) {
 | 
			
		||||
			(void) zfs_ioctl(hdl, ZFS_IOC_SET_PROP, &zc2);
 | 
			
		||||
			zcmd_free_nvlists(&zc2);
 | 
			
		||||
		(void) strcpy(zc.zc_name, destsnap);
 | 
			
		||||
		zc.zc_cookie = B_TRUE; /* received */
 | 
			
		||||
		if (zcmd_write_src_nvlist(hdl, &zc, snapprops_nvlist) == 0) {
 | 
			
		||||
			(void) zfs_ioctl(hdl, ZFS_IOC_SET_PROP, &zc);
 | 
			
		||||
			zcmd_free_nvlists(&zc);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@ -3374,7 +3364,7 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
 | 
			
		||||
		 */
 | 
			
		||||
		avl_tree_t *local_avl;
 | 
			
		||||
		nvlist_t *local_nv, *fs;
 | 
			
		||||
		cp = strchr(zc.zc_value, '@');
 | 
			
		||||
		cp = strchr(destsnap, '@');
 | 
			
		||||
 | 
			
		||||
		/*
 | 
			
		||||
		 * XXX Do this faster by just iterating over snaps in
 | 
			
		||||
@ -3382,7 +3372,7 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
 | 
			
		||||
		 * get a strange "does not exist" error message.
 | 
			
		||||
		 */
 | 
			
		||||
		*cp = '\0';
 | 
			
		||||
		if (gather_nvlist(hdl, zc.zc_value, NULL, NULL, B_FALSE,
 | 
			
		||||
		if (gather_nvlist(hdl, destsnap, NULL, NULL, B_FALSE,
 | 
			
		||||
		    &local_nv, &local_avl) == 0) {
 | 
			
		||||
			*cp = '@';
 | 
			
		||||
			fs = fsavl_find(local_avl, drrb->drr_toguid, NULL);
 | 
			
		||||
@ -3392,7 +3382,7 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
 | 
			
		||||
			if (fs != NULL) {
 | 
			
		||||
				if (flags->verbose) {
 | 
			
		||||
					(void) printf("snap %s already exists; "
 | 
			
		||||
					    "ignoring\n", zc.zc_value);
 | 
			
		||||
					    "ignoring\n", destsnap);
 | 
			
		||||
				}
 | 
			
		||||
				err = ioctl_err = recv_skip(hdl, infd,
 | 
			
		||||
				    flags->byteswap);
 | 
			
		||||
@ -3404,22 +3394,22 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
 | 
			
		||||
	if (ioctl_err != 0) {
 | 
			
		||||
		switch (ioctl_errno) {
 | 
			
		||||
		case ENODEV:
 | 
			
		||||
			cp = strchr(zc.zc_value, '@');
 | 
			
		||||
			cp = strchr(destsnap, '@');
 | 
			
		||||
			*cp = '\0';
 | 
			
		||||
			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 | 
			
		||||
			    "most recent snapshot of %s does not\n"
 | 
			
		||||
			    "match incremental source"), zc.zc_value);
 | 
			
		||||
			    "match incremental source"), destsnap);
 | 
			
		||||
			(void) zfs_error(hdl, EZFS_BADRESTORE, errbuf);
 | 
			
		||||
			*cp = '@';
 | 
			
		||||
			break;
 | 
			
		||||
		case ETXTBSY:
 | 
			
		||||
			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 | 
			
		||||
			    "destination %s has been modified\n"
 | 
			
		||||
			    "since most recent snapshot"), zc.zc_name);
 | 
			
		||||
			    "since most recent snapshot"), name);
 | 
			
		||||
			(void) zfs_error(hdl, EZFS_BADRESTORE, errbuf);
 | 
			
		||||
			break;
 | 
			
		||||
		case EEXIST:
 | 
			
		||||
			cp = strchr(zc.zc_value, '@');
 | 
			
		||||
			cp = strchr(destsnap, '@');
 | 
			
		||||
			if (newfs) {
 | 
			
		||||
				/* it's the containing fs that exists */
 | 
			
		||||
				*cp = '\0';
 | 
			
		||||
@ -3428,14 +3418,18 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
 | 
			
		||||
			    "destination already exists"));
 | 
			
		||||
			(void) zfs_error_fmt(hdl, EZFS_EXISTS,
 | 
			
		||||
			    dgettext(TEXT_DOMAIN, "cannot restore to %s"),
 | 
			
		||||
			    zc.zc_value);
 | 
			
		||||
			    destsnap);
 | 
			
		||||
			*cp = '@';
 | 
			
		||||
			break;
 | 
			
		||||
		case EINVAL:
 | 
			
		||||
			if (flags->resumable)
 | 
			
		||||
				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 | 
			
		||||
				    "kernel modules must be upgraded to "
 | 
			
		||||
				    "receive this stream."));
 | 
			
		||||
			(void) zfs_error(hdl, EZFS_BADSTREAM, errbuf);
 | 
			
		||||
			break;
 | 
			
		||||
		case ECKSUM:
 | 
			
		||||
			recv_ecksum_set_aux(hdl, zc.zc_value, flags->resumable);
 | 
			
		||||
			recv_ecksum_set_aux(hdl, destsnap, flags->resumable);
 | 
			
		||||
			(void) zfs_error(hdl, EZFS_BADSTREAM, errbuf);
 | 
			
		||||
			break;
 | 
			
		||||
		case ENOTSUP:
 | 
			
		||||
@ -3445,7 +3439,7 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
 | 
			
		||||
			break;
 | 
			
		||||
		case EDQUOT:
 | 
			
		||||
			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 | 
			
		||||
			    "destination %s space quota exceeded"), zc.zc_name);
 | 
			
		||||
			    "destination %s space quota exceeded"), name);
 | 
			
		||||
			(void) zfs_error(hdl, EZFS_NOSPC, errbuf);
 | 
			
		||||
			break;
 | 
			
		||||
		default:
 | 
			
		||||
@ -3458,12 +3452,12 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
 | 
			
		||||
	 * children of the target filesystem if we did a replication
 | 
			
		||||
	 * receive (indicated by stream_avl being non-NULL).
 | 
			
		||||
	 */
 | 
			
		||||
	cp = strchr(zc.zc_value, '@');
 | 
			
		||||
	cp = strchr(destsnap, '@');
 | 
			
		||||
	if (cp && (ioctl_err == 0 || !newfs)) {
 | 
			
		||||
		zfs_handle_t *h;
 | 
			
		||||
 | 
			
		||||
		*cp = '\0';
 | 
			
		||||
		h = zfs_open(hdl, zc.zc_value,
 | 
			
		||||
		h = zfs_open(hdl, destsnap,
 | 
			
		||||
		    ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME);
 | 
			
		||||
		if (h != NULL) {
 | 
			
		||||
			if (h->zfs_type == ZFS_TYPE_VOLUME) {
 | 
			
		||||
@ -3474,7 +3468,7 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
 | 
			
		||||
				 * for mounting and sharing later.
 | 
			
		||||
				 */
 | 
			
		||||
				if (top_zfs && *top_zfs == NULL)
 | 
			
		||||
					*top_zfs = zfs_strdup(hdl, zc.zc_value);
 | 
			
		||||
					*top_zfs = zfs_strdup(hdl, destsnap);
 | 
			
		||||
			}
 | 
			
		||||
			zfs_close(h);
 | 
			
		||||
		}
 | 
			
		||||
@ -3488,26 +3482,24 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
 | 
			
		||||
 | 
			
		||||
	if (prop_errflags & ZPROP_ERR_NOCLEAR) {
 | 
			
		||||
		(void) fprintf(stderr, dgettext(TEXT_DOMAIN, "Warning: "
 | 
			
		||||
		    "failed to clear unreceived properties on %s"),
 | 
			
		||||
		    zc.zc_name);
 | 
			
		||||
		    "failed to clear unreceived properties on %s"), name);
 | 
			
		||||
		(void) fprintf(stderr, "\n");
 | 
			
		||||
	}
 | 
			
		||||
	if (prop_errflags & ZPROP_ERR_NORESTORE) {
 | 
			
		||||
		(void) fprintf(stderr, dgettext(TEXT_DOMAIN, "Warning: "
 | 
			
		||||
		    "failed to restore original properties on %s"),
 | 
			
		||||
		    zc.zc_name);
 | 
			
		||||
		    "failed to restore original properties on %s"), name);
 | 
			
		||||
		(void) fprintf(stderr, "\n");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (err || ioctl_err)
 | 
			
		||||
		return (-1);
 | 
			
		||||
 | 
			
		||||
	*action_handlep = zc.zc_action_handle;
 | 
			
		||||
	if (err || ioctl_err) {
 | 
			
		||||
		err = -1;
 | 
			
		||||
		goto out;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (flags->verbose) {
 | 
			
		||||
		char buf1[64];
 | 
			
		||||
		char buf2[64];
 | 
			
		||||
		uint64_t bytes = zc.zc_cookie;
 | 
			
		||||
		uint64_t bytes = read_bytes;
 | 
			
		||||
		time_t delta = time(NULL) - begin_time;
 | 
			
		||||
		if (delta == 0)
 | 
			
		||||
			delta = 1;
 | 
			
		||||
@ -3518,7 +3510,15 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
 | 
			
		||||
		    buf1, delta, buf2);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return (0);
 | 
			
		||||
	err = 0;
 | 
			
		||||
out:
 | 
			
		||||
	if (prop_errors != NULL)
 | 
			
		||||
		nvlist_free(prop_errors);
 | 
			
		||||
 | 
			
		||||
	if (newprops)
 | 
			
		||||
		nvlist_free(props);
 | 
			
		||||
 | 
			
		||||
	return (err);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
 | 
			
		||||
@ -547,82 +547,168 @@ recv_read(int fd, void *buf, int ilen)
 | 
			
		||||
	return (0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Linux adds ZFS_IOC_RECV_NEW for resumable streams and preserves the legacy
 | 
			
		||||
 * ZFS_IOC_RECV user/kernel interface.  The new interface supports all stream
 | 
			
		||||
 * options but is currently only used for resumable streams.  This way updated
 | 
			
		||||
 * user space utilities will interoperate with older kernel modules.
 | 
			
		||||
 *
 | 
			
		||||
 * Non-Linux OpenZFS platforms have opted to modify the legacy interface.
 | 
			
		||||
 */
 | 
			
		||||
static int
 | 
			
		||||
recv_impl(const char *snapname, nvlist_t *props, const char *origin,
 | 
			
		||||
    boolean_t force, boolean_t resumable, int fd,
 | 
			
		||||
    const dmu_replay_record_t *begin_record)
 | 
			
		||||
    boolean_t force, boolean_t resumable, int input_fd,
 | 
			
		||||
    const dmu_replay_record_t *begin_record, int cleanup_fd,
 | 
			
		||||
    uint64_t *read_bytes, uint64_t *errflags, uint64_t *action_handle,
 | 
			
		||||
    nvlist_t **errors)
 | 
			
		||||
{
 | 
			
		||||
	/*
 | 
			
		||||
	 * The receive ioctl is still legacy, so we need to construct our own
 | 
			
		||||
	 * zfs_cmd_t rather than using zfsc_ioctl().
 | 
			
		||||
	 */
 | 
			
		||||
	zfs_cmd_t zc = {"\0"};
 | 
			
		||||
	dmu_replay_record_t drr;
 | 
			
		||||
	char fsname[MAXPATHLEN];
 | 
			
		||||
	char *atp;
 | 
			
		||||
	char *packed = NULL;
 | 
			
		||||
	size_t size;
 | 
			
		||||
	int error;
 | 
			
		||||
 | 
			
		||||
	ASSERT3S(g_refcount, >, 0);
 | 
			
		||||
 | 
			
		||||
	/* zc_name is name of containing filesystem */
 | 
			
		||||
	(void) strlcpy(zc.zc_name, snapname, sizeof (zc.zc_name));
 | 
			
		||||
	atp = strchr(zc.zc_name, '@');
 | 
			
		||||
	/* Set 'fsname' to the name of containing filesystem */
 | 
			
		||||
	(void) strlcpy(fsname, snapname, sizeof (fsname));
 | 
			
		||||
	atp = strchr(fsname, '@');
 | 
			
		||||
	if (atp == NULL)
 | 
			
		||||
		return (EINVAL);
 | 
			
		||||
	*atp = '\0';
 | 
			
		||||
 | 
			
		||||
	/* if the fs does not exist, try its parent. */
 | 
			
		||||
	if (!lzc_exists(zc.zc_name)) {
 | 
			
		||||
		char *slashp = strrchr(zc.zc_name, '/');
 | 
			
		||||
	/* If the fs does not exist, try its parent. */
 | 
			
		||||
	if (!lzc_exists(fsname)) {
 | 
			
		||||
		char *slashp = strrchr(fsname, '/');
 | 
			
		||||
		if (slashp == NULL)
 | 
			
		||||
			return (ENOENT);
 | 
			
		||||
		*slashp = '\0';
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* zc_value is full name of the snapshot to create */
 | 
			
		||||
	(void) strlcpy(zc.zc_value, snapname, sizeof (zc.zc_value));
 | 
			
		||||
 | 
			
		||||
	if (props != NULL) {
 | 
			
		||||
		/* zc_nvlist_src is props to set */
 | 
			
		||||
		packed = fnvlist_pack(props, &size);
 | 
			
		||||
		zc.zc_nvlist_src = (uint64_t)(uintptr_t)packed;
 | 
			
		||||
		zc.zc_nvlist_src_size = size;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* zc_string is name of clone origin (if DRR_FLAG_CLONE) */
 | 
			
		||||
	if (origin != NULL)
 | 
			
		||||
		(void) strlcpy(zc.zc_string, origin, sizeof (zc.zc_string));
 | 
			
		||||
 | 
			
		||||
	/* zc_begin_record is non-byteswapped BEGIN record */
 | 
			
		||||
	/*
 | 
			
		||||
	 * The begin_record is normally a non-byteswapped BEGIN record.
 | 
			
		||||
	 * For resumable streams it may be set to any non-byteswapped
 | 
			
		||||
	 * dmu_replay_record_t.
 | 
			
		||||
	 */
 | 
			
		||||
	if (begin_record == NULL) {
 | 
			
		||||
		error = recv_read(fd, &zc.zc_begin_record,
 | 
			
		||||
		    sizeof (zc.zc_begin_record));
 | 
			
		||||
		error = recv_read(input_fd, &drr, sizeof (drr));
 | 
			
		||||
		if (error != 0)
 | 
			
		||||
			goto out;
 | 
			
		||||
			return (error);
 | 
			
		||||
	} else {
 | 
			
		||||
		zc.zc_begin_record = *begin_record;
 | 
			
		||||
		drr = *begin_record;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* zc_cookie is fd to read from */
 | 
			
		||||
	zc.zc_cookie = fd;
 | 
			
		||||
	if (resumable) {
 | 
			
		||||
		nvlist_t *outnvl = NULL;
 | 
			
		||||
		nvlist_t *innvl = fnvlist_alloc();
 | 
			
		||||
 | 
			
		||||
	/* zc guid is force flag */
 | 
			
		||||
	zc.zc_guid = force;
 | 
			
		||||
		fnvlist_add_string(innvl, "snapname", snapname);
 | 
			
		||||
 | 
			
		||||
	zc.zc_resumable = resumable;
 | 
			
		||||
		if (props != NULL)
 | 
			
		||||
			fnvlist_add_nvlist(innvl, "props", props);
 | 
			
		||||
 | 
			
		||||
	/* zc_cleanup_fd is unused */
 | 
			
		||||
	zc.zc_cleanup_fd = -1;
 | 
			
		||||
		if (origin != NULL && strlen(origin))
 | 
			
		||||
			fnvlist_add_string(innvl, "origin", origin);
 | 
			
		||||
 | 
			
		||||
	error = ioctl(g_fd, ZFS_IOC_RECV, &zc);
 | 
			
		||||
	if (error != 0)
 | 
			
		||||
		error = errno;
 | 
			
		||||
		fnvlist_add_byte_array(innvl, "begin_record",
 | 
			
		||||
		    (uchar_t *) &drr, sizeof (drr));
 | 
			
		||||
 | 
			
		||||
		fnvlist_add_int32(innvl, "input_fd", input_fd);
 | 
			
		||||
 | 
			
		||||
		if (force)
 | 
			
		||||
			fnvlist_add_boolean(innvl, "force");
 | 
			
		||||
 | 
			
		||||
		if (resumable)
 | 
			
		||||
			fnvlist_add_boolean(innvl, "resumable");
 | 
			
		||||
 | 
			
		||||
		if (cleanup_fd >= 0)
 | 
			
		||||
			fnvlist_add_int32(innvl, "cleanup_fd", cleanup_fd);
 | 
			
		||||
 | 
			
		||||
		if (action_handle != NULL)
 | 
			
		||||
			fnvlist_add_uint64(innvl, "action_handle",
 | 
			
		||||
			    *action_handle);
 | 
			
		||||
 | 
			
		||||
		error = lzc_ioctl(ZFS_IOC_RECV_NEW, fsname, innvl, &outnvl);
 | 
			
		||||
 | 
			
		||||
		if (error == 0 && read_bytes != NULL)
 | 
			
		||||
			error = nvlist_lookup_uint64(outnvl, "read_bytes",
 | 
			
		||||
			    read_bytes);
 | 
			
		||||
 | 
			
		||||
		if (error == 0 && errflags != NULL)
 | 
			
		||||
			error = nvlist_lookup_uint64(outnvl, "error_flags",
 | 
			
		||||
			    errflags);
 | 
			
		||||
 | 
			
		||||
		if (error == 0 && action_handle != NULL)
 | 
			
		||||
			error = nvlist_lookup_uint64(outnvl, "action_handle",
 | 
			
		||||
			    action_handle);
 | 
			
		||||
 | 
			
		||||
		if (error == 0 && errors != NULL) {
 | 
			
		||||
			nvlist_t *nvl;
 | 
			
		||||
			error = nvlist_lookup_nvlist(outnvl, "errors", &nvl);
 | 
			
		||||
			if (error == 0)
 | 
			
		||||
				*errors = fnvlist_dup(nvl);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		fnvlist_free(innvl);
 | 
			
		||||
		fnvlist_free(outnvl);
 | 
			
		||||
	} else {
 | 
			
		||||
		zfs_cmd_t zc = {"\0"};
 | 
			
		||||
		char *packed = NULL;
 | 
			
		||||
		size_t size;
 | 
			
		||||
 | 
			
		||||
		ASSERT3S(g_refcount, >, 0);
 | 
			
		||||
 | 
			
		||||
		(void) strlcpy(zc.zc_name, fsname, sizeof (zc.zc_value));
 | 
			
		||||
		(void) strlcpy(zc.zc_value, snapname, sizeof (zc.zc_value));
 | 
			
		||||
 | 
			
		||||
		if (props != NULL) {
 | 
			
		||||
			packed = fnvlist_pack(props, &size);
 | 
			
		||||
			zc.zc_nvlist_src = (uint64_t)(uintptr_t)packed;
 | 
			
		||||
			zc.zc_nvlist_src_size = size;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (origin != NULL)
 | 
			
		||||
			(void) strlcpy(zc.zc_string, origin,
 | 
			
		||||
			    sizeof (zc.zc_string));
 | 
			
		||||
 | 
			
		||||
		ASSERT3S(drr.drr_type, ==, DRR_BEGIN);
 | 
			
		||||
		zc.zc_begin_record = drr.drr_u.drr_begin;
 | 
			
		||||
		zc.zc_guid = force;
 | 
			
		||||
		zc.zc_cookie = input_fd;
 | 
			
		||||
		zc.zc_cleanup_fd = -1;
 | 
			
		||||
		zc.zc_action_handle = 0;
 | 
			
		||||
 | 
			
		||||
		if (cleanup_fd >= 0)
 | 
			
		||||
			zc.zc_cleanup_fd = cleanup_fd;
 | 
			
		||||
 | 
			
		||||
		if (action_handle != NULL)
 | 
			
		||||
			zc.zc_action_handle = *action_handle;
 | 
			
		||||
 | 
			
		||||
		zc.zc_nvlist_dst_size = 128 * 1024;
 | 
			
		||||
		zc.zc_nvlist_dst = (uint64_t)(uintptr_t)
 | 
			
		||||
		    malloc(zc.zc_nvlist_dst_size);
 | 
			
		||||
 | 
			
		||||
		error = ioctl(g_fd, ZFS_IOC_RECV, &zc);
 | 
			
		||||
		if (error != 0) {
 | 
			
		||||
			error = errno;
 | 
			
		||||
		} else {
 | 
			
		||||
			if (read_bytes != NULL)
 | 
			
		||||
				*read_bytes = zc.zc_cookie;
 | 
			
		||||
 | 
			
		||||
			if (errflags != NULL)
 | 
			
		||||
				*errflags = zc.zc_obj;
 | 
			
		||||
 | 
			
		||||
			if (action_handle != NULL)
 | 
			
		||||
				*action_handle = zc.zc_action_handle;
 | 
			
		||||
 | 
			
		||||
			if (errors != NULL)
 | 
			
		||||
				VERIFY0(nvlist_unpack(
 | 
			
		||||
				    (void *)(uintptr_t)zc.zc_nvlist_dst,
 | 
			
		||||
				    zc.zc_nvlist_dst_size, errors, KM_SLEEP));
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (packed != NULL)
 | 
			
		||||
			fnvlist_pack_free(packed, size);
 | 
			
		||||
		free((void *)(uintptr_t)zc.zc_nvlist_dst);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
out:
 | 
			
		||||
	if (packed != NULL)
 | 
			
		||||
		fnvlist_pack_free(packed, size);
 | 
			
		||||
	free((void*)(uintptr_t)zc.zc_nvlist_dst);
 | 
			
		||||
	return (error);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -643,7 +729,8 @@ int
 | 
			
		||||
lzc_receive(const char *snapname, nvlist_t *props, const char *origin,
 | 
			
		||||
    boolean_t force, int fd)
 | 
			
		||||
{
 | 
			
		||||
	return (recv_impl(snapname, props, origin, force, B_FALSE, fd, NULL));
 | 
			
		||||
	return (recv_impl(snapname, props, origin, force, B_FALSE, fd,
 | 
			
		||||
	    NULL, -1, NULL, NULL, NULL, NULL));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
@ -656,7 +743,8 @@ int
 | 
			
		||||
lzc_receive_resumable(const char *snapname, nvlist_t *props, const char *origin,
 | 
			
		||||
    boolean_t force, int fd)
 | 
			
		||||
{
 | 
			
		||||
	return (recv_impl(snapname, props, origin, force, B_TRUE, fd, NULL));
 | 
			
		||||
	return (recv_impl(snapname, props, origin, force, B_TRUE, fd,
 | 
			
		||||
	    NULL, -1, NULL, NULL, NULL, NULL));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
@ -678,7 +766,38 @@ lzc_receive_with_header(const char *snapname, nvlist_t *props,
 | 
			
		||||
	if (begin_record == NULL)
 | 
			
		||||
		return (EINVAL);
 | 
			
		||||
	return (recv_impl(snapname, props, origin, force, resumable, fd,
 | 
			
		||||
	    begin_record));
 | 
			
		||||
	    begin_record, -1, NULL, NULL, NULL, NULL));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Like lzc_receive, but allows the caller to pass all supported arguments
 | 
			
		||||
 * and retrieve all values returned.  The only additional input parameter
 | 
			
		||||
 * is 'cleanup_fd' which is used to set a cleanup-on-exit file descriptor.
 | 
			
		||||
 *
 | 
			
		||||
 * The following parameters all provide return values.  Several may be set
 | 
			
		||||
 * in the failure case and will contain additional information.
 | 
			
		||||
 *
 | 
			
		||||
 * The 'read_bytes' value will be set to the total number of bytes read.
 | 
			
		||||
 *
 | 
			
		||||
 * The 'errflags' value will contain zprop_errflags_t flags which are
 | 
			
		||||
 * used to describe any failures.
 | 
			
		||||
 *
 | 
			
		||||
 * The 'action_handle' is used to pass the handle for this guid/ds mapping.
 | 
			
		||||
 * It should be set to zero on first call and will contain an updated handle
 | 
			
		||||
 * on success, it should be passed in subsequent calls.
 | 
			
		||||
 *
 | 
			
		||||
 * The 'errors' nvlist contains an entry for each unapplied received
 | 
			
		||||
 * property.  Callers are responsible for freeing this nvlist.
 | 
			
		||||
 */
 | 
			
		||||
int lzc_receive_one(const char *snapname, nvlist_t *props,
 | 
			
		||||
    const char *origin, boolean_t force, boolean_t resumable, int input_fd,
 | 
			
		||||
    const dmu_replay_record_t *begin_record, int cleanup_fd,
 | 
			
		||||
    uint64_t *read_bytes, uint64_t *errflags, uint64_t *action_handle,
 | 
			
		||||
    nvlist_t **errors)
 | 
			
		||||
{
 | 
			
		||||
	return (recv_impl(snapname, props, origin, force, resumable,
 | 
			
		||||
	    input_fd, begin_record, cleanup_fd, read_bytes, errflags,
 | 
			
		||||
	    action_handle, errors));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 | 
			
		||||
@ -945,6 +945,13 @@ zfs_secpolicy_recv(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr)
 | 
			
		||||
	    ZFS_DELEG_PERM_CREATE, cr));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* ARGSUSED */
 | 
			
		||||
static int
 | 
			
		||||
zfs_secpolicy_recv_new(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr)
 | 
			
		||||
{
 | 
			
		||||
	return (zfs_secpolicy_recv(zc, innvl, cr));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
zfs_secpolicy_snapshot_perms(const char *name, cred_t *cr)
 | 
			
		||||
{
 | 
			
		||||
@ -4046,74 +4053,38 @@ static boolean_t zfs_ioc_recv_inject_err;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * inputs:
 | 
			
		||||
 * zc_name		name of containing filesystem
 | 
			
		||||
 * zc_nvlist_src{_size}	nvlist of properties to apply
 | 
			
		||||
 * zc_value		name of snapshot to create
 | 
			
		||||
 * zc_string		name of clone origin (if DRR_FLAG_CLONE)
 | 
			
		||||
 * zc_cookie		file descriptor to recv from
 | 
			
		||||
 * zc_begin_record	the BEGIN record of the stream (not byteswapped)
 | 
			
		||||
 * zc_guid		force flag
 | 
			
		||||
 * zc_cleanup_fd	cleanup-on-exit file descriptor
 | 
			
		||||
 * zc_action_handle	handle for this guid/ds mapping (or zero on first call)
 | 
			
		||||
 * zc_resumable		if data is incomplete assume sender will resume
 | 
			
		||||
 *
 | 
			
		||||
 * outputs:
 | 
			
		||||
 * zc_cookie		number of bytes read
 | 
			
		||||
 * zc_nvlist_dst{_size} error for each unapplied received property
 | 
			
		||||
 * zc_obj		zprop_errflags_t
 | 
			
		||||
 * zc_action_handle	handle for this guid/ds mapping
 | 
			
		||||
 * On failure the 'errors' nvlist may be allocated and will contain a
 | 
			
		||||
 * descriptions of the failures.  It's the callers responsibilty to free.
 | 
			
		||||
 */
 | 
			
		||||
static int
 | 
			
		||||
zfs_ioc_recv(zfs_cmd_t *zc)
 | 
			
		||||
zfs_ioc_recv_impl(char *tofs, char *tosnap, char *origin,
 | 
			
		||||
    nvlist_t *props, boolean_t force, boolean_t resumable, int input_fd,
 | 
			
		||||
    dmu_replay_record_t *begin_record, int cleanup_fd, uint64_t *read_bytes,
 | 
			
		||||
    uint64_t *errflags, uint64_t *action_handle, nvlist_t **errors)
 | 
			
		||||
{
 | 
			
		||||
	file_t *fp;
 | 
			
		||||
	dmu_recv_cookie_t drc;
 | 
			
		||||
	boolean_t force = (boolean_t)zc->zc_guid;
 | 
			
		||||
	int fd;
 | 
			
		||||
	int error = 0;
 | 
			
		||||
	int props_error = 0;
 | 
			
		||||
	nvlist_t *errors;
 | 
			
		||||
	offset_t off;
 | 
			
		||||
	nvlist_t *props = NULL; /* sent properties */
 | 
			
		||||
	nvlist_t *origprops = NULL; /* existing properties */
 | 
			
		||||
	nvlist_t *delayprops = NULL; /* sent properties applied post-receive */
 | 
			
		||||
	char *origin = NULL;
 | 
			
		||||
	char *tosnap;
 | 
			
		||||
	char tofs[ZFS_MAXNAMELEN];
 | 
			
		||||
	nvlist_t *origprops = NULL; /* existing properties */
 | 
			
		||||
	boolean_t first_recvd_props = B_FALSE;
 | 
			
		||||
	file_t *input_fp;
 | 
			
		||||
 | 
			
		||||
	if (dataset_namecheck(zc->zc_value, NULL, NULL) != 0 ||
 | 
			
		||||
	    strchr(zc->zc_value, '@') == NULL ||
 | 
			
		||||
	    strchr(zc->zc_value, '%'))
 | 
			
		||||
		return (SET_ERROR(EINVAL));
 | 
			
		||||
 | 
			
		||||
	(void) strcpy(tofs, zc->zc_value);
 | 
			
		||||
	tosnap = strchr(tofs, '@');
 | 
			
		||||
	*tosnap++ = '\0';
 | 
			
		||||
 | 
			
		||||
	if (zc->zc_nvlist_src != 0 &&
 | 
			
		||||
	    (error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size,
 | 
			
		||||
	    zc->zc_iflags, &props)) != 0)
 | 
			
		||||
		return (error);
 | 
			
		||||
 | 
			
		||||
	fd = zc->zc_cookie;
 | 
			
		||||
	fp = getf(fd);
 | 
			
		||||
	if (fp == NULL) {
 | 
			
		||||
		nvlist_free(props);
 | 
			
		||||
	*errors = NULL;
 | 
			
		||||
	input_fp = getf(input_fd);
 | 
			
		||||
	if (input_fp == NULL)
 | 
			
		||||
		return (SET_ERROR(EBADF));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	errors = fnvlist_alloc();
 | 
			
		||||
 | 
			
		||||
	if (zc->zc_string[0])
 | 
			
		||||
		origin = zc->zc_string;
 | 
			
		||||
 | 
			
		||||
	error = dmu_recv_begin(tofs, tosnap,
 | 
			
		||||
	    &zc->zc_begin_record, force, zc->zc_resumable, origin, &drc);
 | 
			
		||||
	    begin_record, force, resumable, origin, &drc);
 | 
			
		||||
	if (error != 0)
 | 
			
		||||
		goto out;
 | 
			
		||||
 | 
			
		||||
	*read_bytes = 0;
 | 
			
		||||
	*errflags = 0;
 | 
			
		||||
	*errors = fnvlist_alloc();
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Set properties before we receive the stream so that they are applied
 | 
			
		||||
	 * to the new data. Note that we must call dmu_recv_stream() if
 | 
			
		||||
@ -4143,14 +4114,14 @@ zfs_ioc_recv(zfs_cmd_t *zc)
 | 
			
		||||
			if (!first_recvd_props)
 | 
			
		||||
				props_reduce(props, origprops);
 | 
			
		||||
			if (zfs_check_clearable(tofs, origprops, &errlist) != 0)
 | 
			
		||||
				(void) nvlist_merge(errors, errlist, 0);
 | 
			
		||||
				(void) nvlist_merge(*errors, errlist, 0);
 | 
			
		||||
			nvlist_free(errlist);
 | 
			
		||||
 | 
			
		||||
			if (clear_received_props(tofs, origprops,
 | 
			
		||||
			    first_recvd_props ? NULL : props) != 0)
 | 
			
		||||
				zc->zc_obj |= ZPROP_ERR_NOCLEAR;
 | 
			
		||||
				*errflags |= ZPROP_ERR_NOCLEAR;
 | 
			
		||||
		} else {
 | 
			
		||||
			zc->zc_obj |= ZPROP_ERR_NOCLEAR;
 | 
			
		||||
			*errflags |= ZPROP_ERR_NOCLEAR;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@ -4160,13 +4131,13 @@ zfs_ioc_recv(zfs_cmd_t *zc)
 | 
			
		||||
		if (props_error == 0) {
 | 
			
		||||
			delayprops = extract_delay_props(props);
 | 
			
		||||
			(void) zfs_set_prop_nvlist(tofs, ZPROP_SRC_RECEIVED,
 | 
			
		||||
			    props, errors);
 | 
			
		||||
			    props, *errors);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	off = fp->f_offset;
 | 
			
		||||
	error = dmu_recv_stream(&drc, fp->f_vnode, &off, zc->zc_cleanup_fd,
 | 
			
		||||
	    &zc->zc_action_handle);
 | 
			
		||||
	off = input_fp->f_offset;
 | 
			
		||||
	error = dmu_recv_stream(&drc, input_fp->f_vnode, &off, cleanup_fd,
 | 
			
		||||
	    action_handle);
 | 
			
		||||
 | 
			
		||||
	if (error == 0) {
 | 
			
		||||
		zfs_sb_t *zsb = NULL;
 | 
			
		||||
@ -4192,7 +4163,7 @@ zfs_ioc_recv(zfs_cmd_t *zc)
 | 
			
		||||
		/* Set delayed properties now, after we're done receiving. */
 | 
			
		||||
		if (delayprops != NULL && error == 0) {
 | 
			
		||||
			(void) zfs_set_prop_nvlist(tofs, ZPROP_SRC_RECEIVED,
 | 
			
		||||
			    delayprops, errors);
 | 
			
		||||
			    delayprops, *errors);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@ -4210,23 +4181,10 @@ zfs_ioc_recv(zfs_cmd_t *zc)
 | 
			
		||||
		nvlist_free(delayprops);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Now that all props, initial and delayed, are set, report the prop
 | 
			
		||||
	 * errors to the caller.
 | 
			
		||||
	 */
 | 
			
		||||
	if (zc->zc_nvlist_dst_size != 0 &&
 | 
			
		||||
	    (nvlist_smush(errors, zc->zc_nvlist_dst_size) != 0 ||
 | 
			
		||||
	    put_nvlist(zc, errors) != 0)) {
 | 
			
		||||
		/*
 | 
			
		||||
		 * Caller made zc->zc_nvlist_dst less than the minimum expected
 | 
			
		||||
		 * size or supplied an invalid address.
 | 
			
		||||
		 */
 | 
			
		||||
		props_error = SET_ERROR(EINVAL);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	zc->zc_cookie = off - fp->f_offset;
 | 
			
		||||
	if (VOP_SEEK(fp->f_vnode, fp->f_offset, &off, NULL) == 0)
 | 
			
		||||
		fp->f_offset = off;
 | 
			
		||||
	*read_bytes = off - input_fp->f_offset;
 | 
			
		||||
	if (VOP_SEEK(input_fp->f_vnode, input_fp->f_offset, &off, NULL) == 0)
 | 
			
		||||
	    input_fp->f_offset = off;
 | 
			
		||||
 | 
			
		||||
#ifdef	DEBUG
 | 
			
		||||
	if (zfs_ioc_recv_inject_err) {
 | 
			
		||||
@ -4245,14 +4203,14 @@ zfs_ioc_recv(zfs_cmd_t *zc)
 | 
			
		||||
			 * Since we may have left a $recvd value on the
 | 
			
		||||
			 * system, we can't clear the $hasrecvd flag.
 | 
			
		||||
			 */
 | 
			
		||||
			zc->zc_obj |= ZPROP_ERR_NORESTORE;
 | 
			
		||||
			*errflags |= ZPROP_ERR_NORESTORE;
 | 
			
		||||
		} else if (first_recvd_props) {
 | 
			
		||||
			dsl_prop_unset_hasrecvd(tofs);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (origprops == NULL && !drc.drc_newfs) {
 | 
			
		||||
			/* We failed to stash the original properties. */
 | 
			
		||||
			zc->zc_obj |= ZPROP_ERR_NORESTORE;
 | 
			
		||||
			*errflags |= ZPROP_ERR_NORESTORE;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/*
 | 
			
		||||
@ -4269,14 +4227,12 @@ zfs_ioc_recv(zfs_cmd_t *zc)
 | 
			
		||||
			 * We stashed the original properties but failed to
 | 
			
		||||
			 * restore them.
 | 
			
		||||
			 */
 | 
			
		||||
			zc->zc_obj |= ZPROP_ERR_NORESTORE;
 | 
			
		||||
			*errflags |= ZPROP_ERR_NORESTORE;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
out:
 | 
			
		||||
	nvlist_free(props);
 | 
			
		||||
	releasef(input_fd);
 | 
			
		||||
	nvlist_free(origprops);
 | 
			
		||||
	nvlist_free(errors);
 | 
			
		||||
	releasef(fd);
 | 
			
		||||
 | 
			
		||||
	if (error == 0)
 | 
			
		||||
		error = props_error;
 | 
			
		||||
@ -4284,6 +4240,176 @@ out:
 | 
			
		||||
	return (error);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * inputs:
 | 
			
		||||
 * zc_name		name of containing filesystem (unused)
 | 
			
		||||
 * zc_nvlist_src{_size}	nvlist of properties to apply
 | 
			
		||||
 * zc_value		name of snapshot to create
 | 
			
		||||
 * zc_string		name of clone origin (if DRR_FLAG_CLONE)
 | 
			
		||||
 * zc_cookie		file descriptor to recv from
 | 
			
		||||
 * zc_begin_record	the BEGIN record of the stream (not byteswapped)
 | 
			
		||||
 * zc_guid		force flag
 | 
			
		||||
 * zc_cleanup_fd	cleanup-on-exit file descriptor
 | 
			
		||||
 * zc_action_handle	handle for this guid/ds mapping (or zero on first call)
 | 
			
		||||
 *
 | 
			
		||||
 * outputs:
 | 
			
		||||
 * zc_cookie		number of bytes read
 | 
			
		||||
 * zc_obj		zprop_errflags_t
 | 
			
		||||
 * zc_action_handle	handle for this guid/ds mapping
 | 
			
		||||
 * zc_nvlist_dst{_size} error for each unapplied received property
 | 
			
		||||
 */
 | 
			
		||||
static int
 | 
			
		||||
zfs_ioc_recv(zfs_cmd_t *zc)
 | 
			
		||||
{
 | 
			
		||||
	dmu_replay_record_t begin_record;
 | 
			
		||||
	nvlist_t *errors = NULL;
 | 
			
		||||
	nvlist_t *props = NULL;
 | 
			
		||||
	char *origin = NULL;
 | 
			
		||||
	char *tosnap;
 | 
			
		||||
	char tofs[ZFS_MAXNAMELEN];
 | 
			
		||||
	int error = 0;
 | 
			
		||||
 | 
			
		||||
	if (dataset_namecheck(zc->zc_value, NULL, NULL) != 0 ||
 | 
			
		||||
	    strchr(zc->zc_value, '@') == NULL ||
 | 
			
		||||
	    strchr(zc->zc_value, '%'))
 | 
			
		||||
		return (SET_ERROR(EINVAL));
 | 
			
		||||
 | 
			
		||||
	(void) strcpy(tofs, zc->zc_value);
 | 
			
		||||
	tosnap = strchr(tofs, '@');
 | 
			
		||||
	*tosnap++ = '\0';
 | 
			
		||||
 | 
			
		||||
	if (zc->zc_nvlist_src != 0 &&
 | 
			
		||||
	    (error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size,
 | 
			
		||||
	    zc->zc_iflags, &props)) != 0)
 | 
			
		||||
		return (error);
 | 
			
		||||
 | 
			
		||||
	if (zc->zc_string[0])
 | 
			
		||||
		origin = zc->zc_string;
 | 
			
		||||
 | 
			
		||||
	begin_record.drr_type = DRR_BEGIN;
 | 
			
		||||
	begin_record.drr_payloadlen = 0;
 | 
			
		||||
	begin_record.drr_u.drr_begin = zc->zc_begin_record;
 | 
			
		||||
 | 
			
		||||
	error = zfs_ioc_recv_impl(tofs, tosnap, origin, props, zc->zc_guid,
 | 
			
		||||
	    B_FALSE, zc->zc_cookie, &begin_record, zc->zc_cleanup_fd,
 | 
			
		||||
	    &zc->zc_cookie, &zc->zc_obj, &zc->zc_action_handle, &errors);
 | 
			
		||||
	nvlist_free(props);
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Now that all props, initial and delayed, are set, report the prop
 | 
			
		||||
	 * errors to the caller.
 | 
			
		||||
	 */
 | 
			
		||||
	if (zc->zc_nvlist_dst_size != 0 && errors != NULL &&
 | 
			
		||||
	    (nvlist_smush(errors, zc->zc_nvlist_dst_size) != 0 ||
 | 
			
		||||
	    put_nvlist(zc, errors) != 0)) {
 | 
			
		||||
		/*
 | 
			
		||||
		 * Caller made zc->zc_nvlist_dst less than the minimum expected
 | 
			
		||||
		 * size or supplied an invalid address.
 | 
			
		||||
		 */
 | 
			
		||||
		error = SET_ERROR(EINVAL);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	nvlist_free(errors);
 | 
			
		||||
 | 
			
		||||
	return (error);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * innvl: {
 | 
			
		||||
 *     "snapname" -> full name of the snapshot to create
 | 
			
		||||
 *     (optional) "props" -> properties to set (nvlist)
 | 
			
		||||
 *     (optional) "origin" -> name of clone origin (DRR_FLAG_CLONE)
 | 
			
		||||
 *     "begin_record" -> non-byteswapped dmu_replay_record_t
 | 
			
		||||
 *     "input_fd" -> file descriptor to read stream from (int32)
 | 
			
		||||
 *     (optional) "force" -> force flag (value ignored)
 | 
			
		||||
 *     (optional) "resumable" -> resumable flag (value ignored)
 | 
			
		||||
 *     (optional) "cleanup_fd" -> cleanup-on-exit file descriptor
 | 
			
		||||
 *     (optional) "action_handle" -> handle for this guid/ds mapping
 | 
			
		||||
 * }
 | 
			
		||||
 *
 | 
			
		||||
 * outnvl: {
 | 
			
		||||
 *     "read_bytes" -> number of bytes read
 | 
			
		||||
 *     "error_flags" -> zprop_errflags_t
 | 
			
		||||
 *     "action_handle" -> handle for this guid/ds mapping
 | 
			
		||||
 *     "errors" -> error for each unapplied received property (nvlist)
 | 
			
		||||
 * }
 | 
			
		||||
 */
 | 
			
		||||
static int
 | 
			
		||||
zfs_ioc_recv_new(const char *fsname, nvlist_t *innvl, nvlist_t *outnvl)
 | 
			
		||||
{
 | 
			
		||||
	dmu_replay_record_t *begin_record;
 | 
			
		||||
	uint_t begin_record_size;
 | 
			
		||||
	nvlist_t *errors = NULL;
 | 
			
		||||
	nvlist_t *props = NULL;
 | 
			
		||||
	char *snapname = NULL;
 | 
			
		||||
	char *origin = NULL;
 | 
			
		||||
	char *tosnap;
 | 
			
		||||
	char tofs[ZFS_MAXNAMELEN];
 | 
			
		||||
	boolean_t force;
 | 
			
		||||
	boolean_t resumable;
 | 
			
		||||
	uint64_t action_handle = 0;
 | 
			
		||||
	uint64_t read_bytes = 0;
 | 
			
		||||
	uint64_t errflags = 0;
 | 
			
		||||
	int input_fd = -1;
 | 
			
		||||
	int cleanup_fd = -1;
 | 
			
		||||
	int error;
 | 
			
		||||
 | 
			
		||||
	error = nvlist_lookup_string(innvl, "snapname", &snapname);
 | 
			
		||||
	if (error != 0)
 | 
			
		||||
		return (SET_ERROR(EINVAL));
 | 
			
		||||
 | 
			
		||||
	if (dataset_namecheck(snapname, NULL, NULL) != 0 ||
 | 
			
		||||
	    strchr(snapname, '@') == NULL ||
 | 
			
		||||
	    strchr(snapname, '%'))
 | 
			
		||||
		return (SET_ERROR(EINVAL));
 | 
			
		||||
 | 
			
		||||
	(void) strcpy(tofs, snapname);
 | 
			
		||||
	tosnap = strchr(tofs, '@');
 | 
			
		||||
	*tosnap++ = '\0';
 | 
			
		||||
 | 
			
		||||
	error = nvlist_lookup_string(innvl, "origin", &origin);
 | 
			
		||||
	if (error && error != ENOENT)
 | 
			
		||||
		return (error);
 | 
			
		||||
 | 
			
		||||
	error = nvlist_lookup_byte_array(innvl, "begin_record",
 | 
			
		||||
	    (uchar_t **) &begin_record, &begin_record_size);
 | 
			
		||||
	if (error != 0 || begin_record_size != sizeof (*begin_record))
 | 
			
		||||
		return (SET_ERROR(EINVAL));
 | 
			
		||||
 | 
			
		||||
	error = nvlist_lookup_int32(innvl, "input_fd", &input_fd);
 | 
			
		||||
	if (error != 0)
 | 
			
		||||
		return (SET_ERROR(EINVAL));
 | 
			
		||||
 | 
			
		||||
	force = nvlist_exists(innvl, "force");
 | 
			
		||||
	resumable = nvlist_exists(innvl, "resumable");
 | 
			
		||||
 | 
			
		||||
	error = nvlist_lookup_int32(innvl, "cleanup_fd", &cleanup_fd);
 | 
			
		||||
	if (error && error != ENOENT)
 | 
			
		||||
		return (error);
 | 
			
		||||
 | 
			
		||||
	error = nvlist_lookup_uint64(innvl, "action_handle", &action_handle);
 | 
			
		||||
	if (error && error != ENOENT)
 | 
			
		||||
		return (error);
 | 
			
		||||
 | 
			
		||||
	error = nvlist_lookup_nvlist(innvl, "props", &props);
 | 
			
		||||
	if (error && error != ENOENT)
 | 
			
		||||
		return (error);
 | 
			
		||||
 | 
			
		||||
	error = zfs_ioc_recv_impl(tofs, tosnap, origin, props, force,
 | 
			
		||||
	    resumable, input_fd, begin_record, cleanup_fd, &read_bytes,
 | 
			
		||||
	    &errflags, &action_handle, &errors);
 | 
			
		||||
 | 
			
		||||
	fnvlist_add_uint64(outnvl, "read_bytes", read_bytes);
 | 
			
		||||
	fnvlist_add_uint64(outnvl, "error_flags", errflags);
 | 
			
		||||
	fnvlist_add_uint64(outnvl, "action_handle", action_handle);
 | 
			
		||||
	fnvlist_add_nvlist(outnvl, "errors", errors);
 | 
			
		||||
 | 
			
		||||
	nvlist_free(errors);
 | 
			
		||||
	nvlist_free(props);
 | 
			
		||||
 | 
			
		||||
	return (error);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * inputs:
 | 
			
		||||
 * zc_name	name of snapshot to send
 | 
			
		||||
@ -5555,6 +5681,10 @@ zfs_ioctl_init(void)
 | 
			
		||||
	    POOL_NAME,
 | 
			
		||||
	    POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_TRUE);
 | 
			
		||||
 | 
			
		||||
	zfs_ioctl_register("receive", ZFS_IOC_RECV_NEW,
 | 
			
		||||
	    zfs_ioc_recv_new, zfs_secpolicy_recv_new, DATASET_NAME,
 | 
			
		||||
	    POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_TRUE);
 | 
			
		||||
 | 
			
		||||
	/* IOCTLS that use the legacy function signature */
 | 
			
		||||
 | 
			
		||||
	zfs_ioctl_register_legacy(ZFS_IOC_POOL_FREEZE, zfs_ioc_pool_freeze,
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user