xref: /illumos-gate/usr/src/cmd/ndmpd/ndmp/ndmpd_zfs.c (revision f970887035084ffc66d633763104257174c8c502)
1 /*
2  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
3  */
4 
5 /*
6  * BSD 3 Clause License
7  *
8  * Copyright (c) 2007, The Storage Networking Industry Association.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 	- Redistributions of source code must retain the above copyright
14  *	  notice, this list of conditions and the following disclaimer.
15  *
16  * 	- Redistributions in binary form must reproduce the above copyright
17  *	  notice, this list of conditions and the following disclaimer in
18  *	  the documentation and/or other materials provided with the
19  *	  distribution.
20  *
21  *	- Neither the name of The Storage Networking Industry Association (SNIA)
22  *	  nor the names of its contributors may be used to endorse or promote
23  *	  products derived from this software without specific prior written
24  *	  permission.
25  *
26  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
27  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
30  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36  * POSSIBILITY OF SUCH DAMAGE.
37  */
38 /* Copyright (c) 2007, The Storage Networking Industry Association. */
39 /* Copyright (c) 1996, 1997 PDC, Network Appliance. All Rights Reserved */
40 
41 #include <sys/types.h>
42 #include <sys/param.h>
43 #include <sys/lwp.h>
44 #include <sys/fs/zfs.h>
45 #include <sys/mtio.h>
46 #include <sys/time.h>
47 #include <unistd.h>
48 #include <errno.h>
49 #include <stdlib.h>
50 #include <string.h>
51 #include <ctype.h>
52 #include <libzfs.h>
53 #include <stdio.h>
54 #include "ndmpd_common.h"
55 #include "ndmpd.h"
56 
57 typedef struct {
58 	char nzs_findprop[ZFS_MAXPROPLEN]; /* prop substring to find */
59 	char nzs_snapname[ZFS_MAXNAMELEN]; /* snap's name */
60 	char nzs_snapprop[ZFS_MAXPROPLEN]; /* snap's prop value */
61 	char nzs_snapskip[ZFS_MAXPROPLEN]; /* snap to skip */
62 	uint32_t nzs_prop_major;	   /* property major version */
63 	uint32_t nzs_prop_minor;	   /* property minor version */
64 } ndmpd_zfs_snapfind_t;
65 
66 mutex_t ndmpd_zfs_fd_lock;
67 
68 static int ndmpd_zfs_open_fds(ndmpd_zfs_args_t *);
69 static void ndmpd_zfs_close_fds(ndmpd_zfs_args_t *);
70 
71 static void ndmpd_zfs_close_one_fd(ndmpd_zfs_args_t *, int);
72 
73 static int ndmpd_zfs_header_write(ndmpd_session_t *);
74 static int ndmpd_zfs_header_read(ndmpd_zfs_args_t *);
75 
76 static int ndmpd_zfs_backup_send_read(ndmpd_zfs_args_t *);
77 static int ndmpd_zfs_backup_tape_write(ndmpd_zfs_args_t *);
78 
79 static int ndmpd_zfs_restore(ndmpd_zfs_args_t *);
80 static int ndmpd_zfs_restore_tape_read(ndmpd_zfs_args_t *);
81 static int ndmpd_zfs_restore_recv_write(ndmpd_zfs_args_t *);
82 
83 static int ndmpd_zfs_reader_writer(ndmpd_zfs_args_t *, int **, int **);
84 
85 static int ndmpd_zfs_is_spanning(ndmpd_zfs_args_t *);
86 
87 static boolean_t ndmpd_zfs_backup_pathvalid(ndmpd_zfs_args_t *);
88 static int ndmpd_zfs_backup_getpath(ndmpd_zfs_args_t *, char *, int);
89 static int ndmpd_zfs_backup_getenv(ndmpd_zfs_args_t *);
90 
91 static boolean_t ndmpd_zfs_restore_pathvalid(ndmpd_zfs_args_t *);
92 static int ndmpd_zfs_restore_getpath(ndmpd_zfs_args_t *);
93 static int ndmpd_zfs_restore_getenv(ndmpd_zfs_args_t *);
94 
95 static int ndmpd_zfs_getenv(ndmpd_zfs_args_t *);
96 static int ndmpd_zfs_getenv_zfs_mode(ndmpd_zfs_args_t *);
97 static int ndmpd_zfs_getenv_zfs_force(ndmpd_zfs_args_t *);
98 static int ndmpd_zfs_getenv_level(ndmpd_zfs_args_t *);
99 static int ndmpd_zfs_getenv_update(ndmpd_zfs_args_t *);
100 static int ndmpd_zfs_getenv_dmp_name(ndmpd_zfs_args_t *);
101 static boolean_t ndmpd_zfs_dmp_name_valid(ndmpd_zfs_args_t *, char *);
102 static boolean_t ndmpd_zfs_is_incremental(ndmpd_zfs_args_t *);
103 static int ndmpd_zfs_send_fhist(ndmpd_zfs_args_t *);
104 
105 static int ndmpd_zfs_snapshot_prepare(ndmpd_zfs_args_t *);
106 static int ndmpd_zfs_snapshot_cleanup(ndmpd_zfs_args_t *, int);
107 static int ndmpd_zfs_snapshot_create(ndmpd_zfs_args_t *);
108 static int ndmpd_zfs_snapshot_unuse(ndmpd_zfs_args_t *,
109     boolean_t, ndmpd_zfs_snapfind_t *);
110 static boolean_t ndmpd_zfs_snapshot_ndmpd_generated(char *);
111 static int ndmpd_zfs_snapshot_find(ndmpd_zfs_args_t *, ndmpd_zfs_snapfind_t *);
112 
113 static int ndmpd_zfs_snapshot_prop_find(zfs_handle_t *, void *);
114 static int ndmpd_zfs_snapshot_prop_get(zfs_handle_t *, char *);
115 static int ndmpd_zfs_snapshot_prop_add(ndmpd_zfs_args_t *);
116 static int ndmpd_zfs_snapshot_prop_create(ndmpd_zfs_args_t *, char *,
117     boolean_t *);
118 static int ndmpd_zfs_prop_create_subprop(ndmpd_zfs_args_t *, char *, int,
119     boolean_t);
120 static int ndmpd_zfs_snapshot_prop_remove(ndmpd_zfs_args_t *,
121     ndmpd_zfs_snapfind_t *);
122 static boolean_t ndmpd_zfs_prop_version_check(char *, uint32_t *, uint32_t *);
123 
124 static int ndmpd_zfs_snapname_create(ndmpd_zfs_args_t *, char *, int);
125 
126 static void ndmpd_zfs_zerr_dma_log(ndmpd_zfs_args_t *);
127 
128 static int ndmpd_zfs_backup(ndmpd_zfs_args_t *);
129 
130 #define	snapshot_create chkpnt_backup_prepare
131 #define	snapshot_destroy chkpnt_backup_successful
132 
133 /*
134  * Syntax for com.sun.ndmp:incr property value:
135  *	#.#.n|u/$LEVEL.$DMP_NAME.$ZFS_MODE(/ ...)
136  *
137  * where
138  *	#.# is the version number
139  *	'n' means ndmp-generated; 'u' means user-supplied
140  *	$LEVEL: backup (incremental) level [0-9]
141  *	$DMP_NAME: set name [default: "level"]
142  *	$ZFS_MODE: d | r | p [for dataset, recursive, or package]
143  *
144  * Examples:
145  *
146  * 	0.0.n/0.bob.p
147  * 	0.0.u/1.bob.p/0.jane.d
148  *
149  * Note: NDMPD_ZFS_SUBPROP_MAX is calculated based on ZFS_MAXPROPLEN
150  */
151 
152 #define	NDMPD_ZFS_PROP_INCR "com.sun.ndmp:incr"
153 #define	NDMPD_ZFS_SUBPROP_MAX	28
154 
155 /*
156  * NDMPD_ZFS_LOG_ZERR
157  *
158  * As coded, there should be no races in the retrieval of the ZFS errno
159  * from the ndmpd_zfs_args->nz_zlibh.  I.e., for a given ndmpd_zfs backup
160  * or restore, there should only ever be one ZFS library call taking place
161  * at any one moment in time.
162  */
163 
164 #define	NDMPD_ZFS_LOG_ZERR(ndmpd_zfs_args, ...) {			\
165 	NDMP_LOG(LOG_ERR, __VA_ARGS__);					\
166 	NDMP_LOG(LOG_ERR, "%s--%s",					\
167 	    libzfs_error_action((ndmpd_zfs_args)->nz_zlibh),           	\
168 	    libzfs_error_description((ndmpd_zfs_args)->nz_zlibh));     	\
169 	ndmpd_zfs_zerr_dma_log((ndmpd_zfs_args));			\
170 }
171 
172 int
173 ndmpd_zfs_init(ndmpd_session_t *session)
174 {
175 	ndmpd_zfs_args_t *ndmpd_zfs_args = &session->ns_ndmpd_zfs_args;
176 	int version = session->ns_protocol_version;
177 
178 	bzero(ndmpd_zfs_args, sizeof (*ndmpd_zfs_args));
179 
180 	if ((version < NDMPV3) || (version > NDMPV4)) {
181 		NDMP_LOG(LOG_ERR, "Unknown or unsupported version %d", version);
182 		return (-1);
183 	}
184 
185 	if ((ndmpd_zfs_args->nz_zlibh = libzfs_init()) == NULL) {
186 		NDMP_LOG(LOG_ERR, "libzfs init error [%d]", errno);
187 		return (-1);
188 	}
189 
190 	if (ndmpd_zfs_open_fds(ndmpd_zfs_args) < 0) {
191 		NDMP_LOG(LOG_ERR, "open_fds() failure(): %d\n", errno);
192 		return (-1);
193 	}
194 
195 	ndmpd_zfs_args->nz_bufsize = ndmp_buffer_get_size(session);
196 	ndmpd_zfs_args->nz_window_len = session->ns_mover.md_window_length;
197 
198 	ndmpd_zfs_args->nz_nlp = ndmp_get_nlp(session);
199 
200 	assert(ndmpd_zfs_args->nz_nlp != NULL);
201 
202 	ndmpd_zfs_args->nz_nlp->nlp_bytes_total = 0;
203 
204 	session->ns_data.dd_module.dm_module_cookie = ndmpd_zfs_args;
205 	session->ns_data.dd_data_size = 0;
206 	session->ns_data.dd_module.dm_stats.ms_est_bytes_remaining = 0;
207 	session->ns_data.dd_module.dm_stats.ms_est_time_remaining  = 0;
208 
209 	session->ns_data.dd_bytes_left_to_read = 0;
210 	session->ns_data.dd_position = 0;
211 	session->ns_data.dd_discard_length = 0;
212 	session->ns_data.dd_read_offset = 0;
213 	session->ns_data.dd_read_length = 0;
214 
215 	ndmpd_zfs_params->mp_get_env_func = ndmpd_api_get_env;
216 	ndmpd_zfs_params->mp_add_env_func = ndmpd_api_add_env;
217 	ndmpd_zfs_params->mp_set_env_func = ndmpd_api_set_env;
218 	ndmpd_zfs_params->mp_dispatch_func = ndmpd_api_dispatch;
219 	ndmpd_zfs_params->mp_daemon_cookie = (void *)session;
220 	ndmpd_zfs_params->mp_protocol_version = session->ns_protocol_version;
221 	ndmpd_zfs_params->mp_stats = &session->ns_data.dd_module.dm_stats;
222 	ndmpd_zfs_params->mp_add_file_handler_func =
223 	    ndmpd_api_add_file_handler;
224 	ndmpd_zfs_params->mp_remove_file_handler_func =
225 	    ndmpd_api_remove_file_handler;
226 	ndmpd_zfs_params->mp_seek_func = 0;
227 
228 	switch (version) {
229 	case NDMPV3:
230 		ndmpd_zfs_params->mp_write_func = ndmpd_api_write_v3;
231 		ndmpd_zfs_params->mp_read_func = ndmpd_api_read_v3;
232 		ndmpd_zfs_params->mp_get_name_func = ndmpd_api_get_name_v3;
233 		ndmpd_zfs_params->mp_done_func = ndmpd_api_done_v3;
234 		ndmpd_zfs_params->mp_log_func_v3 = ndmpd_api_log_v3;
235 		ndmpd_zfs_params->mp_file_recovered_func =
236 		    ndmpd_api_file_recovered_v3;
237 		break;
238 	case NDMPV4:
239 		ndmpd_zfs_params->mp_write_func = ndmpd_api_write_v3;
240 		ndmpd_zfs_params->mp_read_func = ndmpd_api_read_v3;
241 		ndmpd_zfs_params->mp_get_name_func = ndmpd_api_get_name_v3;
242 		ndmpd_zfs_params->mp_done_func = ndmpd_api_done_v3;
243 		ndmpd_zfs_params->mp_log_func_v3 = ndmpd_api_log_v4;
244 		ndmpd_zfs_params->mp_file_recovered_func =
245 		    ndmpd_api_file_recovered_v4;
246 		break;
247 	default:
248 		/* error already returned above for this case */
249 		break;
250 	}
251 
252 	return (0);
253 }
254 
255 void
256 ndmpd_zfs_fini(ndmpd_zfs_args_t *ndmpd_zfs_args)
257 {
258 	libzfs_fini(ndmpd_zfs_args->nz_zlibh);
259 
260 	ndmpd_zfs_close_fds(ndmpd_zfs_args);
261 }
262 
263 static int
264 ndmpd_zfs_open_fds(ndmpd_zfs_args_t *ndmpd_zfs_args)
265 {
266 	int err;
267 
268 	err = pipe(ndmpd_zfs_args->nz_pipe_fd);
269 	if (err)
270 		NDMP_LOG(LOG_ERR, "pipe(2) failed: %s:\n", strerror(errno));
271 
272 	return (err);
273 }
274 
275 /*
276  * ndmpd_zfs_close_fds()
277  *
278  * In the abort case, use dup2() to redirect the end of the pipe that is
279  * being written to (to a new pipe).  Close the ends of the new pipe to cause
280  * EPIPE to be returned to the writing thread.  This will cause the writer
281  * and reader to terminate without having any of the writer's data erroneously
282  * go to any reopened descriptor.
283  */
284 
285 static void
286 ndmpd_zfs_close_fds(ndmpd_zfs_args_t *ndmpd_zfs_args)
287 {
288 	ndmpd_session_t *session = (ndmpd_session_t *)
289 	    (ndmpd_zfs_params->mp_daemon_cookie);
290 	int pipe_end;
291 	int fds[2];
292 
293 	if (session->ns_data.dd_state != NDMP_DATA_STATE_ACTIVE) {
294 		ndmpd_zfs_close_one_fd(ndmpd_zfs_args, PIPE_ZFS);
295 		ndmpd_zfs_close_one_fd(ndmpd_zfs_args, PIPE_TAPE);
296 		return;
297 	}
298 
299 	(void) mutex_lock(&ndmpd_zfs_fd_lock);
300 
301 	if (ndmpd_zfs_params->mp_operation == NDMP_DATA_OP_BACKUP) {
302 		pipe_end = PIPE_ZFS;
303 	} else {
304 		pipe_end = PIPE_TAPE;
305 	}
306 
307 	if (ndmpd_zfs_args->nz_pipe_fd[pipe_end] != -1) {
308 		if (pipe(fds) != 0) {
309 			(void) mutex_unlock(&ndmpd_zfs_fd_lock);
310 			NDMP_LOG(LOG_ERR, "pipe(2) failed: %s:\n",
311 			    strerror(errno));
312 			return;
313 		}
314 
315 		(void) dup2(fds[0], ndmpd_zfs_args->nz_pipe_fd[pipe_end]);
316 		(void) close(fds[0]);
317 		(void) close(fds[1]);
318 
319 		ndmpd_zfs_args->nz_pipe_fd[pipe_end] = -1;
320 	}
321 
322 	(void) mutex_unlock(&ndmpd_zfs_fd_lock);
323 }
324 
325 static void
326 ndmpd_zfs_close_one_fd(ndmpd_zfs_args_t *ndmpd_zfs_args, int pipe_end)
327 {
328 	(void) mutex_lock(&ndmpd_zfs_fd_lock);
329 	(void) close(ndmpd_zfs_args->nz_pipe_fd[pipe_end]);
330 	ndmpd_zfs_args->nz_pipe_fd[pipe_end] = -1;
331 	(void) mutex_unlock(&ndmpd_zfs_fd_lock);
332 }
333 
334 static int
335 ndmpd_zfs_header_write(ndmpd_session_t *session)
336 {
337 	ndmpd_zfs_args_t *ndmpd_zfs_args = &session->ns_ndmpd_zfs_args;
338 	int32_t bufsize = ndmpd_zfs_args->nz_bufsize;
339 	ndmpd_zfs_header_t *tape_header = &ndmpd_zfs_args->nz_tape_header;
340 	char *buf;
341 
342 	buf = ndmp_malloc(bufsize);
343 	if (buf == NULL) {
344 		NDMP_LOG(LOG_DEBUG, "buf NULL");
345 		return (-1);
346 	}
347 
348 	(void) strlcpy(tape_header->nzh_magic, NDMPUTF8MAGIC,
349 	    sizeof (NDMPUTF8MAGIC));
350 	tape_header->nzh_major = LE_32(NDMPD_ZFS_MAJOR_VERSION);
351 	tape_header->nzh_minor = LE_32(NDMPD_ZFS_MINOR_VERSION);
352 	tape_header->nzh_hdrlen = LE_32(bufsize);
353 
354 	bzero(buf, bufsize);
355 	(void) memcpy(buf, tape_header, sizeof (ndmpd_zfs_header_t));
356 
357 	NDMP_LOG(LOG_DEBUG, "header (major, minor, length): %u %u %u",
358 	    NDMPD_ZFS_MAJOR_VERSION,
359 	    NDMPD_ZFS_MINOR_VERSION,
360 	    bufsize);
361 
362 	if (MOD_WRITE(ndmpd_zfs_params, buf, bufsize) != 0) {
363 		free(buf);
364 		NDMP_LOG(LOG_ERR, "MOD_WRITE error");
365 		return (-1);
366 	}
367 
368 	free(buf);
369 
370 	session->ns_data.dd_module.dm_stats.ms_bytes_processed = bufsize;
371 
372 	return (0);
373 }
374 
375 static int
376 ndmpd_zfs_header_read(ndmpd_zfs_args_t *ndmpd_zfs_args)
377 {
378 	int32_t bufsize = ndmpd_zfs_args->nz_bufsize;
379 	ndmpd_zfs_header_t *tape_header = &ndmpd_zfs_args->nz_tape_header;
380 	uint32_t hdrlen;
381 	int32_t header_left;
382 	int err;
383 	char *buf;
384 
385 	buf = ndmp_malloc(bufsize);
386 	if (buf == NULL) {
387 		NDMP_LOG(LOG_DEBUG, "buf NULL");
388 		return (-1);
389 	}
390 
391 	bzero(buf, bufsize);
392 
393 	/*
394 	 * Read nz_bufsize worth of bytes first (the size of a mover record).
395 	 */
396 
397 	err = MOD_READ(ndmpd_zfs_params, buf, bufsize);
398 
399 	if (err != 0) {
400 		NDMP_LOG(LOG_ERR, "MOD_READ error: %d", err);
401 		free(buf);
402 		return (-1);
403 	}
404 
405 	(void) memcpy(tape_header, buf, sizeof (ndmpd_zfs_header_t));
406 
407 
408 	if (strcmp(tape_header->nzh_magic, NDMPUTF8MAGIC) != 0) {
409 		ndmpd_zfs_dma_log(ndmpd_zfs_args, NDMP_LOG_ERROR,
410 		    "bad magic string\n");
411 		goto _err;
412 	}
413 
414 	if (tape_header->nzh_major > LE_32(NDMPD_ZFS_MAJOR_VERSION)) {
415 		ndmpd_zfs_dma_log(ndmpd_zfs_args, NDMP_LOG_ERROR,
416 		    "major number larger than supported: (%d %d)\n",
417 		    LE_32(tape_header->nzh_major), NDMPD_ZFS_MAJOR_VERSION);
418 		goto _err;
419 	}
420 
421 	/*
422 	 * Major version 0 (regardless of minor version):
423 	 * Header must be a multiple of the mover record size.
424 	 */
425 
426 	hdrlen = LE_32(tape_header->nzh_hdrlen);
427 	if (hdrlen > bufsize) {
428 		header_left = hdrlen - bufsize;
429 		while (header_left > 0) {
430 			err = MOD_READ(ndmpd_zfs_params, buf, bufsize);
431 			if (err == -1) {
432 				ndmpd_zfs_dma_log(ndmpd_zfs_args,
433 				    NDMP_LOG_ERROR, "bad header\n");
434 				goto _err;
435 			}
436 			header_left -= bufsize;
437 		}
438 	}
439 
440 	NDMP_LOG(LOG_DEBUG, "tape header: %s; %u %u; %u ",
441 	    tape_header->nzh_magic,
442 	    LE_32(tape_header->nzh_major),
443 	    LE_32(tape_header->nzh_minor),
444 	    LE_32(tape_header->nzh_hdrlen));
445 
446 	ndmpd_zfs_args->nz_nlp->nlp_bytes_total = hdrlen;
447 
448 	free(buf);
449 	return (0);
450 
451 _err:
452 
453 	NDMP_LOG(LOG_ERR, "tape header: %s; %u %u; %u ",
454 	    tape_header->nzh_magic,
455 	    LE_32(tape_header->nzh_major),
456 	    LE_32(tape_header->nzh_minor),
457 	    LE_32(tape_header->nzh_hdrlen));
458 
459 	free(buf);
460 	return (-1);
461 }
462 
463 int
464 ndmpd_zfs_backup_starter(void *arg)
465 {
466 	ndmpd_zfs_args_t *ndmpd_zfs_args = arg;
467 	ndmpd_session_t *session = (ndmpd_session_t *)
468 	    (ndmpd_zfs_params->mp_daemon_cookie);
469 	int cleanup_err = 0;
470 	int err = 0;
471 
472 	ndmp_session_ref(session);
473 
474 	if (ndmpd_zfs_snapshot_prepare(ndmpd_zfs_args) != 0) {
475 		err = -1;
476 		goto _done;
477 	}
478 
479 	err = ndmpd_zfs_backup(ndmpd_zfs_args);
480 
481 	cleanup_err = ndmpd_zfs_snapshot_cleanup(ndmpd_zfs_args, err);
482 
483 	NDMP_LOG(LOG_DEBUG,
484 	    "data bytes_total(including header):%llu",
485 	    session->ns_data.dd_module.dm_stats.ms_bytes_processed);
486 
487 	if (err == 0)
488 		err = ndmpd_zfs_send_fhist(ndmpd_zfs_args);
489 
490 _done:
491 	NS_DEC(nbk);
492 	MOD_DONE(ndmpd_zfs_params, err ? err : cleanup_err);
493 	ndmp_session_unref(session);
494 	ndmpd_zfs_fini(ndmpd_zfs_args);
495 
496 	return (err);
497 }
498 
499 static int
500 ndmpd_zfs_send_fhist(ndmpd_zfs_args_t *ndmpd_zfs_args)
501 {
502 	ndmpd_session_t *session = (ndmpd_session_t *)
503 	    (ndmpd_zfs_params->mp_daemon_cookie);
504 	struct stat64 st;
505 	char *envp;
506 	zfs_handle_t *zhp;
507 	char mountpoint[PATH_MAX];
508 
509 	envp = MOD_GETENV(ndmpd_zfs_params, "HIST");
510 	if (!envp)
511 		return (0);
512 
513 	if (!(strchr("YT", toupper(*envp)))) {
514 		ndmpd_zfs_dma_log(ndmpd_zfs_args, NDMP_LOG_WARNING,
515 		    "HIST is not set.  No file history will be "
516 		    "generated.\n");
517 		return (0);
518 	}
519 
520 	if ((zhp = zfs_open(ndmpd_zfs_args->nz_zlibh,
521 	    ndmpd_zfs_args->nz_dataset, ndmpd_zfs_args->nz_type)) == NULL ||
522 	    zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint, PATH_MAX, NULL,
523 	    NULL, 0, B_FALSE) != 0 ||
524 	    stat64(mountpoint, &st) != 0)
525 		(void) memset(&st, 0, sizeof (struct stat64));
526 	if (zhp)
527 		zfs_close(zhp);
528 
529 	if (ndmpd_api_file_history_dir_v3(session, ".", ROOT_INODE,
530 	    ROOT_INODE) != 0)
531 		return (-1);
532 	if (ndmpd_api_file_history_dir_v3(session, "..", ROOT_INODE,
533 	    ROOT_INODE) != 0)
534 		return (-1);
535 	if (ndmpd_api_file_history_node_v3(session, ROOT_INODE, &st, 0) != 0)
536 		return (-1);
537 
538 	ndmpd_file_history_cleanup(session, TRUE);
539 	return (0);
540 }
541 
542 static int
543 ndmpd_zfs_backup(ndmpd_zfs_args_t *ndmpd_zfs_args)
544 {
545 	ndmpd_session_t *session = (ndmpd_session_t *)
546 	    (ndmpd_zfs_params->mp_daemon_cookie);
547 	int *read_err = NULL;
548 	int *write_err = NULL;
549 	int result = 0;
550 	int err;
551 
552 	if (session->ns_eof)
553 		return (-1);
554 
555 	if (!session->ns_data.dd_abort) {
556 		if (ndmpd_zfs_header_write(session)) {
557 			ndmpd_zfs_dma_log(ndmpd_zfs_args, NDMP_LOG_ERROR,
558 			    "ndmpd_zfs header write error\n");
559 			return (-1);
560 		}
561 
562 		err = ndmpd_zfs_reader_writer(ndmpd_zfs_args,
563 		    &read_err, &write_err);
564 
565 		if (err || read_err || write_err || session->ns_eof)
566 			result = EPIPE;
567 	}
568 
569 	if (session->ns_data.dd_abort) {
570 		ndmpd_audit_backup(session->ns_connection,
571 		    ndmpd_zfs_args->nz_dataset,
572 		    session->ns_data.dd_data_addr.addr_type,
573 		    ndmpd_zfs_args->nz_dataset, EINTR);
574 		NDMP_LOG(LOG_DEBUG, "Backing up \"%s\" aborted.",
575 		    ndmpd_zfs_args->nz_dataset);
576 
577 		(void) ndmpd_zfs_post_backup(ndmpd_zfs_args);
578 		err = -1;
579 	} else {
580 		ndmpd_audit_backup(session->ns_connection,
581 		    ndmpd_zfs_args->nz_dataset,
582 		    session->ns_data.dd_data_addr.addr_type,
583 		    ndmpd_zfs_args->nz_dataset, result);
584 
585 		err = ndmpd_zfs_post_backup(ndmpd_zfs_args);
586 		if (err || result)
587 			err = -1;
588 
589 		if (err == 0)  {
590 			NDMP_LOG(LOG_DEBUG, "Backing up \"%s\" finished.",
591 			    ndmpd_zfs_args->nz_dataset);
592 		} else {
593 			NDMP_LOG(LOG_DEBUG, "An error occurred while backing up"
594 			    " \"%s\"", ndmpd_zfs_args->nz_dataset);
595 		}
596 	}
597 
598 	return (err);
599 }
600 
601 /*
602  * ndmpd_zfs_backup_send_read()
603  *
604  * This routine executes zfs_send() to create the backup data stream.
605  * The value of ZFS_MODE determines the type of zfs_send():
606  * 	dataset ('d'): Only the dataset specified (i.e., top level) is backed up
607  * 	recursive ('r'): The dataset and its child file systems are backed up
608  * 	package ('p'): Same as 'r', except all intermediate snapshots are also
609  *			backed up
610  *
611  * Volumes do not have descednants, so 'd' and 'r' produce equivalent results.
612  */
613 
614 static int
615 ndmpd_zfs_backup_send_read(ndmpd_zfs_args_t *ndmpd_zfs_args)
616 {
617 	ndmpd_session_t *session = (ndmpd_session_t *)
618 	    (ndmpd_zfs_params->mp_daemon_cookie);
619 	sendflags_t flags = { 0 };
620 	char *fromsnap = NULL;
621 	zfs_handle_t *zhp;
622 	int err;
623 
624 	zhp = zfs_open(ndmpd_zfs_args->nz_zlibh,
625 	    ndmpd_zfs_args->nz_dataset, ndmpd_zfs_args->nz_type);
626 
627 	if (!zhp) {
628 		if (!session->ns_data.dd_abort)
629 			NDMPD_ZFS_LOG_ZERR(ndmpd_zfs_args, "zfs_open");
630 		return (-1);
631 	}
632 
633 	switch (ndmpd_zfs_args->nz_zfs_mode) {
634 	case ('d'):
635 		flags.props = B_TRUE;
636 		break;
637 	case ('r'):
638 		flags.replicate = B_TRUE;
639 		break;
640 	case ('p'):
641 		flags.doall = B_TRUE;
642 		flags.replicate = B_TRUE;
643 		break;
644 	default:
645 		NDMP_LOG(LOG_ERR, "unknown zfs_mode: %c",
646 		    ndmpd_zfs_args->nz_zfs_mode);
647 		zfs_close(zhp);
648 		return (-1);
649 	}
650 
651 	if (ndmpd_zfs_is_incremental(ndmpd_zfs_args)) {
652 		if (ndmpd_zfs_args->nz_fromsnap[0] == '\0') {
653 			NDMP_LOG(LOG_ERR, "no fromsnap");
654 			zfs_close(zhp);
655 			return (-1);
656 		}
657 		fromsnap = ndmpd_zfs_args->nz_fromsnap;
658 	}
659 
660 	err = zfs_send(zhp, fromsnap, ndmpd_zfs_args->nz_snapname, flags,
661 	    ndmpd_zfs_args->nz_pipe_fd[PIPE_ZFS], NULL, NULL, NULL);
662 
663 	if (err && !session->ns_data.dd_abort)
664 		NDMPD_ZFS_LOG_ZERR(ndmpd_zfs_args, "zfs_send: %d", err);
665 
666 	zfs_close(zhp);
667 
668 	return (err);
669 }
670 
671 /*
672  * ndmpd_zfs_backup_tape_write()
673  *
674  * The data begins on a mover record boundary (because
675  * the header is the size of a mover record--i.e.
676  * ndmpd_zfs_args->nz_bufsize).
677  */
678 
679 static int
680 ndmpd_zfs_backup_tape_write(ndmpd_zfs_args_t *ndmpd_zfs_args)
681 {
682 	ndmpd_session_t *session = (ndmpd_session_t *)
683 	    (ndmpd_zfs_params->mp_daemon_cookie);
684 	int bufsize = ndmpd_zfs_args->nz_bufsize;
685 	u_longlong_t *bytes_totalp;
686 	int count;
687 	char *buf;
688 
689 	buf = ndmp_malloc(bufsize);
690 	if (buf == NULL) {
691 		NDMP_LOG(LOG_DEBUG, "buf NULL");
692 		return (-1);
693 	}
694 
695 	bytes_totalp =
696 	    &(session->ns_data.dd_module.dm_stats.ms_bytes_processed);
697 
698 	for (;;) {
699 		bzero(buf, bufsize);
700 
701 		count = read(ndmpd_zfs_args->nz_pipe_fd[PIPE_TAPE], buf,
702 		    bufsize);
703 
704 		if (count == 0) /* EOF */ {
705 			NDMP_LOG(LOG_DEBUG,
706 			    "zfs_send stream size: %llu bytes (sans header)",
707 			    *bytes_totalp - bufsize);
708 			free(buf);
709 			return (0);
710 		}
711 
712 		if (count == -1) {
713 			NDMP_LOG(LOG_DEBUG, "pipe read error (errno %d)",
714 			    errno);
715 			free(buf);
716 			return (-1);
717 		}
718 		NS_ADD(rdisk, count);
719 
720 		if (MOD_WRITE(ndmpd_zfs_params, buf, count) != 0) {
721 			(void) ndmpd_zfs_abort((void *) ndmpd_zfs_args);
722 			NDMP_LOG(LOG_ERR, "MOD_WRITE error");
723 			free(buf);
724 			return (-1);
725 		}
726 
727 		*bytes_totalp += count;
728 	}
729 	/* NOTREACHED */
730 }
731 
732 int
733 ndmpd_zfs_restore_starter(void *arg)
734 {
735 	ndmpd_zfs_args_t *ndmpd_zfs_args = arg;
736 	ndmpd_session_t *session = (ndmpd_session_t *)
737 	    (ndmpd_zfs_params->mp_daemon_cookie);
738 	int err;
739 
740 	ndmp_session_ref(session);
741 
742 	err = ndmpd_zfs_restore(ndmpd_zfs_args);
743 
744 	MOD_DONE(ndmpd_zfs_params, err);
745 
746 	NS_DEC(nrs);
747 
748 	ndmp_session_unref(session);
749 
750 	ndmpd_zfs_fini(ndmpd_zfs_args);
751 
752 	return (err);
753 }
754 
755 static int
756 ndmpd_zfs_restore(ndmpd_zfs_args_t *ndmpd_zfs_args)
757 {
758 	ndmpd_session_t *session = (ndmpd_session_t *)
759 	    (ndmpd_zfs_params->mp_daemon_cookie);
760 	int *read_err = NULL;
761 	int *write_err = NULL;
762 	int result = 0;
763 	int err;
764 
765 	if (!session->ns_data.dd_abort) {
766 		if (ndmpd_zfs_header_read(ndmpd_zfs_args)) {
767 			ndmpd_zfs_dma_log(ndmpd_zfs_args, NDMP_LOG_ERROR,
768 			    "ndmpd_zfs header read error\n");
769 			return (-1);
770 		}
771 
772 		err = ndmpd_zfs_reader_writer(ndmpd_zfs_args,
773 		    &write_err, &read_err);
774 
775 		if (err || read_err || write_err || session->ns_eof)
776 			result = EIO;
777 	}
778 
779 	if (session->ns_data.dd_abort) {
780 		NDMP_LOG(LOG_DEBUG, "Restoring to \"%s\" aborted.",
781 		    ndmpd_zfs_args->nz_dataset);
782 		ndmpd_audit_restore(session->ns_connection,
783 		    ndmpd_zfs_args->nz_dataset,
784 		    session->ns_data.dd_data_addr.addr_type,
785 		    ndmpd_zfs_args->nz_dataset, EINTR);
786 		(void) ndmpd_zfs_post_restore(ndmpd_zfs_args);
787 		err = -1;
788 	} else {
789 		ndmpd_audit_restore(session->ns_connection,
790 		    ndmpd_zfs_args->nz_dataset,
791 		    session->ns_data.dd_data_addr.addr_type,
792 		    ndmpd_zfs_args->nz_dataset, result);
793 		err = ndmpd_zfs_post_restore(ndmpd_zfs_args);
794 		if (err || result)
795 			err = -1;
796 
797 		if (err == 0) {
798 			NDMP_LOG(LOG_DEBUG, "Restoring to \"%s\" finished",
799 			    ndmpd_zfs_args->nz_dataset);
800 		} else {
801 			NDMP_LOG(LOG_DEBUG, "An error occurred while restoring"
802 			    " to \"%s\"", ndmpd_zfs_args->nz_dataset);
803 		}
804 	}
805 
806 	return (err);
807 }
808 
809 static int
810 ndmpd_zfs_restore_tape_read(ndmpd_zfs_args_t *ndmpd_zfs_args)
811 {
812 	ndmpd_session_t *session = (ndmpd_session_t *)
813 	    (ndmpd_zfs_params->mp_daemon_cookie);
814 	int bufsize = ndmpd_zfs_args->nz_bufsize;
815 	u_longlong_t *bytes_totalp;
816 	char *buf;
817 	int count;
818 	int err;
819 
820 	buf = ndmp_malloc(bufsize);
821 	if (buf == NULL) {
822 		NDMP_LOG(LOG_DEBUG, "buf NULL");
823 		return (-1);
824 	}
825 
826 	bytes_totalp = &ndmpd_zfs_args->nz_nlp->nlp_bytes_total;
827 
828 	for (;;) {
829 		err = MOD_READ(ndmpd_zfs_params, buf, bufsize);
830 
831 		if (err == -1) {
832 			NDMP_LOG(LOG_DEBUG, "end of data (%llu) bytes",
833 			    *bytes_totalp);
834 			(void) write(ndmpd_zfs_args->nz_pipe_fd[PIPE_TAPE],
835 			    buf, bufsize);
836 			NS_ADD(wdisk, bufsize);
837 			break;
838 		}
839 
840 		count = write(ndmpd_zfs_args->nz_pipe_fd[PIPE_TAPE], buf,
841 		    bufsize);
842 
843 		if (count == -1) {
844 			free(buf);
845 
846 			if ((errno == EPIPE) && !session->ns_data.dd_abort) {
847 				NDMP_LOG(LOG_DEBUG, "EPIPE; count == -1; "
848 				    "[%llu bytes] returning success",
849 				    *bytes_totalp);
850 				return (0);
851 			}
852 
853 			if (session->ns_data.dd_abort)
854 				NDMP_LOG(LOG_DEBUG, "abort set");
855 
856 			NDMP_LOG(LOG_DEBUG, "pipe write error:"
857 			    "errno: %d", errno);
858 
859 			return (-1);
860 		}
861 		NS_ADD(wdisk, count);
862 
863 		*bytes_totalp += count;
864 
865 		/*
866 		 * A short write to the pipe indicates the
867 		 * other peer is terminated so we should exit
868 		 */
869 
870 		if (count != bufsize) {
871 			NDMP_LOG(LOG_DEBUG, "count != bufsize:"
872 			    "count: %d; bufsize: %d",
873 			    count, bufsize);
874 			free(buf);
875 			return (0);
876 		}
877 
878 		/* Checks for local mover */
879 		if (*bytes_totalp == ndmpd_zfs_args->nz_window_len) {
880 			NDMP_LOG(LOG_DEBUG, "Reached EOW at %lld",
881 			    *bytes_totalp);
882 			if (ndmpd_zfs_is_spanning(ndmpd_zfs_args) == 0) {
883 				NDMP_LOG(LOG_DEBUG, "Exit");
884 				break;
885 			}
886 			NDMP_LOG(LOG_DEBUG, "Continue through spanning");
887 		}
888 	}
889 
890 	free(buf);
891 	return (0);
892 }
893 
894 /*
895  * ndmpd_zfs_restore_recv_write()
896  *
897  * This routine executes zfs_receive() to restore the backup.
898  */
899 
900 static int
901 ndmpd_zfs_restore_recv_write(ndmpd_zfs_args_t *ndmpd_zfs_args)
902 {
903 	ndmpd_session_t *session = (ndmpd_session_t *)
904 	    (ndmpd_zfs_params->mp_daemon_cookie);
905 	recvflags_t flags;
906 	int err;
907 
908 	bzero(&flags, sizeof (recvflags_t));
909 
910 	flags.nomount = B_TRUE;
911 
912 	NDMP_LOG(LOG_DEBUG, "nz_zfs_force: %d\n", ndmpd_zfs_args->nz_zfs_force);
913 
914 	if (ndmpd_zfs_args->nz_zfs_force)
915 		flags.force = B_TRUE;
916 
917 	err = zfs_receive(ndmpd_zfs_args->nz_zlibh, ndmpd_zfs_args->nz_dataset,
918 	    flags, ndmpd_zfs_args->nz_pipe_fd[PIPE_ZFS], NULL);
919 
920 	if (err && !session->ns_data.dd_abort)
921 		NDMPD_ZFS_LOG_ZERR(ndmpd_zfs_args, "zfs_receive: %d", err);
922 
923 	return (err);
924 }
925 
926 /*
927  * ndmpd_zfs_is_spanning()
928  *
929  * Check to see if the tape is at spanning point
930  */
931 
932 static int
933 ndmpd_zfs_is_spanning(ndmpd_zfs_args_t *ndmpd_zfs_args)
934 {
935 	ndmpd_session_t *session = (ndmpd_session_t *)
936 	    (ndmpd_zfs_params->mp_daemon_cookie);
937 	int count = session->ns_mover.md_record_size;
938 	char *buf;
939 
940 	if ((buf = ndmp_malloc(count)) == NULL)
941 		return (0);
942 
943 	if (read(session->ns_tape.td_fd, buf, count) == 0) {
944 		free(buf);
945 		return (1);
946 	}
947 
948 	(void) ndmp_mtioctl(session->ns_tape.td_fd, MTBSR, 1);
949 	free(buf);
950 	return (0);
951 }
952 
953 /*
954  * ndmpd_zfs_reader_writer()
955  *
956  * Two separate threads are used for actual backup or restore.
957  */
958 
959 static int
960 ndmpd_zfs_reader_writer(ndmpd_zfs_args_t *ndmpd_zfs_args,
961     int **sendrecv_errp, int **tape_errp)
962 {
963 	funct_t sendrecv_func;
964 	funct_t tape_func;
965 	int sendrecv_err;
966 	int tape_err;
967 
968 	switch (ndmpd_zfs_params->mp_operation) {
969 	case NDMP_DATA_OP_BACKUP:
970 		sendrecv_func = (funct_t)ndmpd_zfs_backup_send_read;
971 		tape_func = (funct_t)ndmpd_zfs_backup_tape_write;
972 		break;
973 	case NDMP_DATA_OP_RECOVER:
974 		sendrecv_func = (funct_t)ndmpd_zfs_restore_recv_write;
975 		tape_func = (funct_t)ndmpd_zfs_restore_tape_read;
976 		break;
977 	}
978 
979 	sendrecv_err = pthread_create(&ndmpd_zfs_args->nz_sendrecv_thread,
980 	    NULL, sendrecv_func, ndmpd_zfs_args);
981 
982 	if (sendrecv_err == 0) {
983 		tape_err = pthread_create(&ndmpd_zfs_args->nz_tape_thread,
984 		    NULL, tape_func, ndmpd_zfs_args);
985 
986 		if (tape_err) {
987 			/*
988 			 * The close of the tape side of the pipe will cause
989 			 * nz_sendrecv_thread to error in the zfs_send/recv()
990 			 * call and to return.  Hence we do not need
991 			 * to explicitly cancel the sendrecv_thread here
992 			 * (the pthread_join() below is sufficient).
993 			 */
994 
995 			(void) close(ndmpd_zfs_args->nz_pipe_fd[PIPE_TAPE]);
996 			NDMP_LOG(LOG_ERR, "Could not start tape thread; "
997 			    "aborting z-op");
998 		}
999 
1000 		(void) pthread_join(ndmpd_zfs_args->nz_sendrecv_thread,
1001 		    (void **) sendrecv_errp);
1002 	}
1003 
1004 	if ((tape_err == 0) &&
1005 	    (ndmpd_zfs_params->mp_operation == NDMP_DATA_OP_RECOVER))
1006 		ndmpd_zfs_close_one_fd(ndmpd_zfs_args, PIPE_TAPE);
1007 
1008 	ndmpd_zfs_close_one_fd(ndmpd_zfs_args, PIPE_ZFS);
1009 
1010 	if ((sendrecv_err == 0) && (tape_err == 0)) {
1011 		(void) pthread_join(ndmpd_zfs_args->nz_tape_thread,
1012 		    (void **) tape_errp);
1013 	}
1014 
1015 	if ((tape_err == 0) &&
1016 	    (ndmpd_zfs_params->mp_operation == NDMP_DATA_OP_BACKUP))
1017 		ndmpd_zfs_close_one_fd(ndmpd_zfs_args, PIPE_TAPE);
1018 
1019 	return (sendrecv_err ? sendrecv_err : tape_err);
1020 }
1021 
1022 int
1023 ndmpd_zfs_abort(void *arg)
1024 {
1025 	ndmpd_zfs_args_t *ndmpd_zfs_args = arg;
1026 	char str[8];
1027 
1028 	if (ndmpd_zfs_params->mp_operation == NDMP_DATA_OP_BACKUP)
1029 		(void) strlcpy(str, "backup", 8);
1030 	else
1031 		(void) strlcpy(str, "recover", 8);
1032 
1033 	NDMP_LOG(LOG_ERR, "ndmpd_zfs_abort() called...aborting %s operation",
1034 	    str);
1035 
1036 	ndmpd_zfs_close_fds(ndmpd_zfs_args);
1037 
1038 	return (0);
1039 }
1040 
1041 /*
1042  * ndmpd_zfs_pre_backup()
1043  *
1044  * Note: The memset to 0 of nctxp ensures that nctx->nc_cmds == NULL.
1045  * This ensures that ndmp_include_zfs() will fail, which is
1046  * a requirement for "zfs"-type backup.
1047  */
1048 
1049 int
1050 ndmpd_zfs_pre_backup(ndmpd_zfs_args_t *ndmpd_zfs_args)
1051 {
1052 	ndmpd_session_t *session = (ndmpd_session_t *)
1053 	    (ndmpd_zfs_params->mp_daemon_cookie);
1054 	ndmp_context_t *nctxp = &ndmpd_zfs_args->nz_nctx;
1055 	int err;
1056 
1057 	if (ndmp_pl == NULL || ndmp_pl->np_pre_backup == NULL)
1058 		return (0);
1059 
1060 	(void) memset(nctxp, 0, sizeof (ndmp_context_t));
1061 	nctxp->nc_plversion = ndmp_pl->np_plversion;
1062 	nctxp->nc_plname = ndmpd_get_prop(NDMP_PLUGIN_PATH);
1063 	nctxp->nc_ddata = (void *) session;
1064 
1065 	err = ndmp_pl->np_pre_backup(ndmp_pl, nctxp,
1066 	    ndmpd_zfs_args->nz_dataset);
1067 
1068 	if (err != 0) {
1069 		NDMP_LOG(LOG_DEBUG, "Pre-backup plug-in: %m");
1070 		(void) ndmpd_zfs_post_backup(ndmpd_zfs_args);
1071 	}
1072 
1073 	return (err);
1074 }
1075 
1076 int
1077 ndmpd_zfs_post_backup(ndmpd_zfs_args_t *ndmpd_zfs_args)
1078 {
1079 	ndmp_context_t *nctxp = &ndmpd_zfs_args->nz_nctx;
1080 	int err = 0;
1081 
1082 	if (ndmp_pl == NULL || ndmp_pl->np_post_backup == NULL)
1083 		return (0);
1084 
1085 	err = ndmp_pl->np_post_backup(ndmp_pl, nctxp, err);
1086 
1087 	if (err == -1)
1088 		NDMP_LOG(LOG_DEBUG, "Post-backup plug-in: %m");
1089 
1090 	return (err);
1091 }
1092 
1093 int
1094 ndmpd_zfs_pre_restore(ndmpd_zfs_args_t *ndmpd_zfs_args)
1095 {
1096 	ndmpd_session_t *session = (ndmpd_session_t *)
1097 	    (ndmpd_zfs_params->mp_daemon_cookie);
1098 	ndmp_context_t *nctxp = &ndmpd_zfs_args->nz_nctx;
1099 	char bkpath[ZFS_MAXNAMELEN];
1100 	int err;
1101 
1102 	if (ndmp_pl == NULL || ndmp_pl->np_pre_restore == NULL)
1103 		return (0);
1104 
1105 	err = ndmpd_zfs_backup_getpath(ndmpd_zfs_args, bkpath, ZFS_MAXNAMELEN);
1106 
1107 	if (err != 0) {
1108 		NDMP_LOG(LOG_DEBUG, "error getting bkup path: %d", err);
1109 		return (-1);
1110 	}
1111 
1112 	err = ndmpd_zfs_restore_getpath(ndmpd_zfs_args);
1113 
1114 	if (err != 0) {
1115 		NDMP_LOG(LOG_DEBUG, "error getting restore path: %d", err);
1116 		return (-1);
1117 	}
1118 
1119 	(void) memset(nctxp, 0, sizeof (ndmp_context_t));
1120 	nctxp->nc_ddata = (void *) session;
1121 
1122 	err = ndmp_pl->np_pre_restore(ndmp_pl, nctxp, bkpath,
1123 	    ndmpd_zfs_args->nz_dataset);
1124 
1125 	if (err != 0) {
1126 		NDMP_LOG(LOG_DEBUG, "Pre-restore plug-in: %m");
1127 		return (-1);
1128 	}
1129 
1130 	return (0);
1131 }
1132 
1133 int
1134 ndmpd_zfs_post_restore(ndmpd_zfs_args_t *ndmpd_zfs_args)
1135 {
1136 	ndmp_context_t *nctxp = &ndmpd_zfs_args->nz_nctx;
1137 	int err = 0;
1138 
1139 	if (ndmp_pl == NULL || ndmp_pl->np_post_restore == NULL)
1140 		return (0);
1141 
1142 	err = ndmp_pl->np_post_restore(ndmp_pl, nctxp, err);
1143 
1144 	if (err == -1)
1145 		NDMP_LOG(LOG_DEBUG, "Post-restore plug-in: %m");
1146 
1147 	return (err);
1148 }
1149 
1150 boolean_t
1151 ndmpd_zfs_backup_parms_valid(ndmpd_zfs_args_t *ndmpd_zfs_args)
1152 {
1153 	ndmpd_zfs_snapfind_t snapdata;
1154 
1155 	if (ndmpd_zfs_backup_getenv(ndmpd_zfs_args) != 0)
1156 		return (B_FALSE);
1157 
1158 	if (!ndmpd_zfs_backup_pathvalid(ndmpd_zfs_args))
1159 		return (B_FALSE);
1160 
1161 	if (ndmpd_zfs_is_incremental(ndmpd_zfs_args)) {
1162 		(void) ndmpd_zfs_prop_create_subprop(ndmpd_zfs_args,
1163 		    snapdata.nzs_findprop, ZFS_MAXPROPLEN, B_TRUE);
1164 
1165 		snapdata.nzs_snapname[0] = '\0';
1166 		snapdata.nzs_snapprop[0] = '\0';
1167 
1168 		if (ndmpd_zfs_snapshot_find(ndmpd_zfs_args, &snapdata))
1169 			return (B_FALSE);
1170 
1171 		if (snapdata.nzs_snapname[0] == '\0') { /* not found */
1172 			ndmpd_zfs_dma_log(ndmpd_zfs_args, NDMP_LOG_ERROR,
1173 			    "Snapshot for level %d does not exist\n",
1174 			    ndmpd_zfs_args->nz_level-1);
1175 			return (B_FALSE);
1176 		}
1177 
1178 		(void) strlcpy(ndmpd_zfs_args->nz_fromsnap,
1179 		    snapdata.nzs_snapname, ZFS_MAXNAMELEN);
1180 	}
1181 
1182 	return (B_TRUE);
1183 }
1184 
1185 /*
1186  * ndmpd_zfs_backup_pathvalid()
1187  *
1188  * Make sure the path is of an existing dataset
1189  */
1190 
1191 static boolean_t
1192 ndmpd_zfs_backup_pathvalid(ndmpd_zfs_args_t *ndmpd_zfs_args)
1193 {
1194 	char zpath[ZFS_MAXNAMELEN];
1195 	char propstr[ZFS_MAXPROPLEN];
1196 	zfs_handle_t *zhp;
1197 	zfs_type_t ztype = 0;
1198 	int err;
1199 
1200 	if (ndmpd_zfs_backup_getpath(ndmpd_zfs_args, zpath, ZFS_MAXNAMELEN)
1201 	    != 0)
1202 		return (B_FALSE);
1203 
1204 	if (ndmpd_zfs_args->nz_snapname[0] != '\0') {
1205 		zhp = zfs_open(ndmpd_zfs_args->nz_zlibh, zpath,
1206 		    ZFS_TYPE_SNAPSHOT);
1207 
1208 		if (!zhp) {
1209 			NDMPD_ZFS_LOG_ZERR(ndmpd_zfs_args,
1210 			    "zfs_open (snap)");
1211 			ndmpd_zfs_args->nz_snapname[0] = '\0';
1212 			ndmpd_zfs_args->nz_dataset[0] = '\0';
1213 			return (B_FALSE);
1214 		}
1215 
1216 		err = ndmpd_zfs_snapshot_prop_get(zhp, propstr);
1217 
1218 		zfs_close(zhp);
1219 
1220 		if (err) {
1221 			NDMP_LOG(LOG_DEBUG,
1222 			    "ndmpd_zfs_snapshot_prop_get failed");
1223 			return (-1);
1224 		}
1225 
1226 		if (propstr && ndmpd_zfs_snapshot_ndmpd_generated(propstr)) {
1227 			ndmpd_zfs_dma_log(ndmpd_zfs_args, NDMP_LOG_ERROR,
1228 			    "cannot use an ndmpd-generated snapshot\n");
1229 			return (B_FALSE);
1230 		}
1231 	}
1232 
1233 	zhp = zfs_open(ndmpd_zfs_args->nz_zlibh,
1234 	    ndmpd_zfs_args->nz_dataset, ZFS_TYPE_DATASET);
1235 
1236 	if (zhp) {
1237 		ztype = zfs_get_type(zhp);
1238 		zfs_close(zhp);
1239 	}
1240 
1241 	if ((ztype == ZFS_TYPE_VOLUME) ||
1242 	    (ztype == ZFS_TYPE_FILESYSTEM)) {
1243 		ndmpd_zfs_args->nz_type = ztype;
1244 		return (B_TRUE);
1245 	}
1246 
1247 	ndmpd_zfs_dma_log(ndmpd_zfs_args, NDMP_LOG_ERROR,
1248 	    "Invalid file system or volume.\n");
1249 
1250 	return (B_FALSE);
1251 }
1252 
1253 /*
1254  * ndmpd_zfs_backup_getpath()
1255  *
1256  * Retrieve the backup path from the environment, which should
1257  * be of the form "/dataset[@snap]".  The leading slash is required
1258  * by certain DMA's but can otherwise be ignored.
1259  *
1260  * (Note: "dataset" can consist of more than one component,
1261  * e.g. "pool", "pool/volume", "pool/fs/fs2".)
1262  *
1263  * The dataset name and the snapshot name (if any) will be
1264  * stored in ndmpd_zfs_args.
1265  */
1266 
1267 static int
1268 ndmpd_zfs_backup_getpath(ndmpd_zfs_args_t *ndmpd_zfs_args, char *zpath,
1269     int zlen)
1270 {
1271 	char *env_path;
1272 	char *at;
1273 
1274 	env_path = get_backup_path_v3(ndmpd_zfs_params);
1275 	if (env_path == NULL)
1276 		return (-1);
1277 
1278 	if (env_path[0] != '/') {
1279 		ndmpd_zfs_dma_log(ndmpd_zfs_args, NDMP_LOG_ERROR,
1280 		    "Invalid path: %s (leading slash required)\n", env_path);
1281 		return (-1);
1282 	}
1283 
1284 	(void) strlcpy(zpath, &env_path[1], zlen);
1285 	(void) strlcpy(ndmpd_zfs_args->nz_dataset, &env_path[1],
1286 	    ZFS_MAXNAMELEN);
1287 
1288 	at = strchr(ndmpd_zfs_args->nz_dataset, '@');
1289 	if (at) {
1290 		*at = '\0';
1291 		(void) strlcpy(ndmpd_zfs_args->nz_snapname, ++at,
1292 		    ZFS_MAXNAMELEN);
1293 	} else {
1294 		ndmpd_zfs_args->nz_snapname[0] = '\0';
1295 	}
1296 
1297 	(void) trim_whitespace(ndmpd_zfs_args->nz_dataset);
1298 
1299 	return (0);
1300 }
1301 
1302 static int
1303 ndmpd_zfs_backup_getenv(ndmpd_zfs_args_t *ndmpd_zfs_args)
1304 {
1305 	return (ndmpd_zfs_getenv(ndmpd_zfs_args));
1306 }
1307 
1308 boolean_t
1309 ndmpd_zfs_restore_parms_valid(ndmpd_zfs_args_t *ndmpd_zfs_args)
1310 {
1311 	if (ndmpd_zfs_restore_getenv(ndmpd_zfs_args) != 0)
1312 		return (B_FALSE);
1313 
1314 	if (!ndmpd_zfs_restore_pathvalid(ndmpd_zfs_args))
1315 		return (B_FALSE);
1316 
1317 	return (B_TRUE);
1318 }
1319 
1320 static boolean_t
1321 ndmpd_zfs_restore_pathvalid(ndmpd_zfs_args_t *ndmpd_zfs_args)
1322 {
1323 	zfs_handle_t *zhp;
1324 	char *at;
1325 
1326 	if (ndmpd_zfs_restore_getpath(ndmpd_zfs_args) != 0)
1327 		return (B_FALSE);
1328 
1329 	at = strchr(ndmpd_zfs_args->nz_dataset, '@');
1330 
1331 	if (at) {
1332 		ndmpd_zfs_dma_log(ndmpd_zfs_args, NDMP_LOG_WARNING,
1333 		    "%s ignored in restore path\n", at);
1334 		*at = '\0';
1335 	}
1336 
1337 	ndmpd_zfs_args->nz_type = ZFS_TYPE_VOLUME | ZFS_TYPE_FILESYSTEM;
1338 
1339 	zhp = zfs_open(ndmpd_zfs_args->nz_zlibh,
1340 	    ndmpd_zfs_args->nz_dataset, ndmpd_zfs_args->nz_type);
1341 
1342 	if (zhp) {
1343 		zfs_close(zhp);
1344 
1345 		if (!ndmpd_zfs_is_incremental(ndmpd_zfs_args)) {
1346 			ndmpd_zfs_dma_log(ndmpd_zfs_args, NDMP_LOG_ERROR,
1347 			    "Restore dataset exists.\n"
1348 			    "A nonexistent dataset must be specified "
1349 			    "for 'zfs' non-incremental restore.\n");
1350 			return (B_FALSE);
1351 		}
1352 	}
1353 
1354 	NDMP_LOG(LOG_DEBUG, "restore path: %s\n", ndmpd_zfs_args->nz_dataset);
1355 
1356 	return (B_TRUE);
1357 }
1358 
1359 /*
1360  * ndmpd_zfs_restore_getpath()
1361  *
1362  * Be sure to not include the leading slash, which is required for
1363  * compatibility with backup applications (NBU) but which is not part
1364  * of the ZFS syntax.  (Note that this done explicitly in all paths
1365  * below except those calling ndmpd_zfs_backup_getpath(), because it is
1366  * already stripped in that function.)
1367  *
1368  * In addition, the DMA might add a trailing slash to the path.
1369  * Strip all such slashes.
1370  */
1371 
1372 static int
1373 ndmpd_zfs_restore_getpath(ndmpd_zfs_args_t *ndmpd_zfs_args)
1374 {
1375 	int version = ndmpd_zfs_params->mp_protocol_version;
1376 	char zpath[ZFS_MAXNAMELEN];
1377 	mem_ndmp_name_v3_t *namep_v3;
1378 	char *dataset = ndmpd_zfs_args->nz_dataset;
1379 	char *nm;
1380 	char *p;
1381 	int len;
1382 	int err;
1383 
1384 	dataset = ndmpd_zfs_args->nz_dataset;
1385 
1386 	namep_v3 = (mem_ndmp_name_v3_t *)MOD_GETNAME(ndmpd_zfs_params, 0);
1387 
1388 	if (namep_v3 == NULL) {
1389 		NDMP_LOG(LOG_DEBUG, "Can't get Nlist[0]");
1390 		return (-1);
1391 	}
1392 
1393 	if (namep_v3->nm3_dpath) {
1394 		if (namep_v3->nm3_dpath[0] != '/') {
1395 			ndmpd_zfs_dma_log(ndmpd_zfs_args, NDMP_LOG_ERROR,
1396 			    "Invalid path: %s (leading slash required)\n",
1397 			    namep_v3->nm3_dpath);
1398 			return (-1);
1399 		}
1400 
1401 		(void) strlcpy(dataset, &(namep_v3->nm3_dpath[1]),
1402 		    ZFS_MAXNAMELEN);
1403 
1404 		if (namep_v3->nm3_newnm) {
1405 			(void) strlcat(dataset, "/", ZFS_MAXNAMELEN);
1406 			(void) strlcat(dataset, namep_v3->nm3_newnm,
1407 			    ZFS_MAXNAMELEN);
1408 
1409 		} else {
1410 			if (version == NDMPV3) {
1411 				/*
1412 				 * The following does not apply for V4.
1413 				 *
1414 				 * Find the last component of nm3_opath.
1415 				 * nm3_opath has no trailing '/'.
1416 				 */
1417 				p = strrchr(namep_v3->nm3_opath, '/');
1418 				nm = p? p : namep_v3->nm3_opath;
1419 				(void) strlcat(dataset, "/", ZFS_MAXNAMELEN);
1420 				(void) strlcat(dataset, nm, ZFS_MAXNAMELEN);
1421 			}
1422 		}
1423 	} else {
1424 		err = ndmpd_zfs_backup_getpath(ndmpd_zfs_args, zpath,
1425 		    ZFS_MAXNAMELEN);
1426 		if (err)
1427 			return (err);
1428 	}
1429 
1430 	len = strlen(dataset);
1431 	while (dataset[len-1] == '/') {
1432 		dataset[len-1] = '\0';
1433 		len--;
1434 	}
1435 
1436 	return (0);
1437 }
1438 
1439 static int
1440 ndmpd_zfs_restore_getenv(ndmpd_zfs_args_t *ndmpd_zfs_args)
1441 {
1442 	return (ndmpd_zfs_getenv(ndmpd_zfs_args));
1443 }
1444 
1445 static int
1446 ndmpd_zfs_getenv(ndmpd_zfs_args_t *ndmpd_zfs_args)
1447 {
1448 
1449 	if (ndmpd_zfs_getenv_level(ndmpd_zfs_args) != 0)
1450 		return (-1);
1451 
1452 	if (ndmpd_zfs_getenv_zfs_mode(ndmpd_zfs_args) != 0)
1453 		return (-1);
1454 
1455 	if (ndmpd_zfs_getenv_zfs_force(ndmpd_zfs_args) != 0)
1456 		return (-1);
1457 
1458 	if (ndmpd_zfs_getenv_update(ndmpd_zfs_args) != 0)
1459 		return (-1);
1460 
1461 	if (ndmpd_zfs_getenv_dmp_name(ndmpd_zfs_args) != 0)
1462 		return (-1);
1463 
1464 	return (0);
1465 }
1466 
1467 static int
1468 ndmpd_zfs_getenv_zfs_mode(ndmpd_zfs_args_t *ndmpd_zfs_args)
1469 {
1470 	char *envp;
1471 
1472 	envp = MOD_GETENV(ndmpd_zfs_params, "ZFS_MODE");
1473 
1474 	if (envp == NULL) {
1475 		NDMP_LOG(LOG_DEBUG, "env(ZFS_MODE) not specified, "
1476 		    "defaulting to recursive");
1477 		ndmpd_zfs_args->nz_zfs_mode = 'r';
1478 		return (0);
1479 	}
1480 
1481 	if ((strcmp(envp, "dataset") == 0) || (strcmp(envp, "d") == 0)) {
1482 		ndmpd_zfs_args->nz_zfs_mode = 'd';
1483 	} else if ((strcmp(envp, "recursive") == 0) ||
1484 	    (strcmp(envp, "r") == 0)) {
1485 		ndmpd_zfs_args->nz_zfs_mode = 'r';
1486 	} else if ((strcmp(envp, "package") == 0) || (strcmp(envp, "p") == 0)) {
1487 		ndmpd_zfs_args->nz_zfs_mode = 'p';
1488 	} else {
1489 		ndmpd_zfs_dma_log(ndmpd_zfs_args, NDMP_LOG_ERROR,
1490 		    "Invalid ZFS_MODE value \"%s\".\n", envp);
1491 		return (-1);
1492 	}
1493 
1494 	NDMP_LOG(LOG_DEBUG, "env(ZFS_MODE): \"%c\"",
1495 	    ndmpd_zfs_args->nz_zfs_mode);
1496 
1497 	return (0);
1498 }
1499 
1500 /*
1501  * ndmpd_zfs_getenv_zfs_force()
1502  *
1503  * If SMF property zfs-force-override is set to "yes" or "no", this
1504  * value will override any value of NDMP environment variable ZFS_FORCE
1505  * as set by the DMA admin (or override the default of 'n', if ZFS_FORCE
1506  * is not set).  By default, zfs-force-override is "off", which means it
1507  * will not override ZFS_FORCE.
1508  */
1509 
1510 static int
1511 ndmpd_zfs_getenv_zfs_force(ndmpd_zfs_args_t *ndmpd_zfs_args)
1512 {
1513 	char *envp_force;
1514 	char *override;
1515 
1516 	override = ndmpd_get_prop(NDMP_ZFS_FORCE_OVERRIDE);
1517 
1518 	if (strcasecmp(override, "yes") == 0) {
1519 		ndmpd_zfs_args->nz_zfs_force = B_TRUE;
1520 		NDMP_LOG(LOG_NOTICE,
1521 		    "SMF property zfs-force-override set to 'yes', "
1522 		    "overriding ZFS_FORCE");
1523 		return (0);
1524 	}
1525 
1526 	if (strcasecmp(override, "no") == 0) {
1527 		ndmpd_zfs_args->nz_zfs_force = B_FALSE;
1528 		NDMP_LOG(LOG_NOTICE,
1529 		    "SMF property zfs-force-override set to 'no', "
1530 		    "overriding ZFS_FORCE");
1531 		return (0);
1532 	}
1533 
1534 	if (strcasecmp(override, "off") != 0) {
1535 		ndmpd_zfs_dma_log(ndmpd_zfs_args, NDMP_LOG_ERROR,
1536 		    "SMF property zfs-force-override set to invalid value of "
1537 		    "'%s'; treating it as 'off'.", override);
1538 	}
1539 
1540 	envp_force = MOD_GETENV(ndmpd_zfs_params, "ZFS_FORCE");
1541 
1542 	if (envp_force == NULL) {
1543 		NDMP_LOG(LOG_DEBUG,
1544 		    "env(ZFS_FORCE) not specified, defaulting to FALSE");
1545 		ndmpd_zfs_args->nz_zfs_force = B_FALSE;
1546 		return (0);
1547 	}
1548 
1549 	/*
1550 	 * The value can be either 't' ("true" for v3) or 'y' ("yes" for v4).
1551 	 */
1552 
1553 	if (strchr("tTyY", *envp_force))
1554 		ndmpd_zfs_args->nz_zfs_force = B_TRUE;
1555 
1556 	NDMP_LOG(LOG_DEBUG, "env(ZFS_FORCE): \"%s\"", envp_force);
1557 
1558 	return (0);
1559 }
1560 
1561 static int
1562 ndmpd_zfs_getenv_level(ndmpd_zfs_args_t *ndmpd_zfs_args)
1563 {
1564 	char *envp;
1565 
1566 	envp = MOD_GETENV(ndmpd_zfs_params, "LEVEL");
1567 
1568 	if (envp == NULL) {
1569 		NDMP_LOG(LOG_DEBUG, "env(LEVEL) not specified, "
1570 		    "defaulting to 0");
1571 		ndmpd_zfs_args->nz_level = 0;
1572 		return (0);
1573 	}
1574 
1575 	if (envp[1] != '\0') {
1576 		ndmpd_zfs_dma_log(ndmpd_zfs_args, NDMP_LOG_ERROR,
1577 		    "Invalid backup level \"%s\".\n", envp);
1578 		return (-1);
1579 	}
1580 
1581 	if (!isdigit(*envp)) {
1582 		ndmpd_zfs_dma_log(ndmpd_zfs_args, NDMP_LOG_ERROR,
1583 		    "Invalid backup level \"%s\".\n", envp);
1584 		return (-1);
1585 	}
1586 
1587 	ndmpd_zfs_args->nz_level = atoi(envp);
1588 
1589 	NDMP_LOG(LOG_DEBUG, "env(LEVEL): \"%d\"",
1590 	    ndmpd_zfs_args->nz_level);
1591 
1592 	return (0);
1593 }
1594 
1595 static int
1596 ndmpd_zfs_getenv_update(ndmpd_zfs_args_t *ndmpd_zfs_args)
1597 {
1598 	char *envp_update;
1599 
1600 	envp_update = MOD_GETENV(ndmpd_zfs_params, "UPDATE");
1601 
1602 	if (envp_update == NULL) {
1603 		NDMP_LOG(LOG_DEBUG,
1604 		    "env(UPDATE) not specified, defaulting to TRUE");
1605 		ndmpd_zfs_args->nz_update = B_TRUE;
1606 		return (0);
1607 	}
1608 
1609 	/*
1610 	 * The value can be either 't' ("true" for v3) or 'y' ("yes" for v4).
1611 	 */
1612 
1613 	if (strchr("tTyY", *envp_update))
1614 		ndmpd_zfs_args->nz_update = B_TRUE;
1615 
1616 	NDMP_LOG(LOG_DEBUG, "env(UPDATE): \"%s\"", envp_update);
1617 
1618 	return (0);
1619 }
1620 
1621 static int
1622 ndmpd_zfs_getenv_dmp_name(ndmpd_zfs_args_t *ndmpd_zfs_args)
1623 {
1624 	char *envp;
1625 
1626 	envp = MOD_GETENV(ndmpd_zfs_params, "DMP_NAME");
1627 
1628 	if (envp == NULL) {
1629 		NDMP_LOG(LOG_DEBUG,
1630 		    "env(DMP_NAME) not specified, defaulting to 'level'");
1631 		(void) strlcpy(ndmpd_zfs_args->nz_dmp_name, "level",
1632 		    NDMPD_ZFS_DMP_NAME_MAX);
1633 		return (0);
1634 	}
1635 
1636 	if (!ndmpd_zfs_dmp_name_valid(ndmpd_zfs_args, envp))
1637 		return (-1);
1638 
1639 	(void) strlcpy(ndmpd_zfs_args->nz_dmp_name, envp,
1640 	    NDMPD_ZFS_DMP_NAME_MAX);
1641 
1642 	NDMP_LOG(LOG_DEBUG, "env(DMP_NAME): \"%s\"", envp);
1643 
1644 	return (0);
1645 }
1646 
1647 /*
1648  * ndmpd_zfs_dmp_name_valid()
1649  *
1650  * This function verifies that the dmp_name is valid.
1651  *
1652  * The dmp_name is restricted to alphanumeric characters plus
1653  * the underscore and hyphen, and must be 31 characters or less.
1654  * This is due to its use in the NDMPD_ZFS_PROP_INCR property
1655  * and in the ZFS snapshot name (if an ndmpd-generated snapshot
1656  * is required).
1657  */
1658 
1659 static boolean_t
1660 ndmpd_zfs_dmp_name_valid(ndmpd_zfs_args_t *ndmpd_zfs_args, char *dmp_name)
1661 {
1662 	char *c;
1663 
1664 	if (strlen(dmp_name) >= NDMPD_ZFS_DMP_NAME_MAX) {
1665 		ndmpd_zfs_dma_log(ndmpd_zfs_args, NDMP_LOG_ERROR,
1666 		    "DMP_NAME %s is longer than %d\n",
1667 		    dmp_name, NDMPD_ZFS_DMP_NAME_MAX-1);
1668 		return (B_FALSE);
1669 	}
1670 
1671 	for (c = dmp_name; *c != '\0'; c++) {
1672 		if (!isalpha(*c) && !isdigit(*c) &&
1673 		    (*c != '_') && (*c != '-')) {
1674 			ndmpd_zfs_dma_log(ndmpd_zfs_args, NDMP_LOG_ERROR,
1675 			    "DMP_NAME %s contains illegal character %c\n",
1676 			    dmp_name, *c);
1677 			return (B_FALSE);
1678 		}
1679 	}
1680 
1681 	NDMP_LOG(LOG_DEBUG, "DMP_NAME is valid: %s\n", dmp_name);
1682 	return (B_TRUE);
1683 }
1684 
1685 /*
1686  * ndmpd_zfs_is_incremental()
1687  *
1688  * This can only be called after ndmpd_zfs_getenv_level()
1689  * has been called.
1690  */
1691 
1692 static boolean_t
1693 ndmpd_zfs_is_incremental(ndmpd_zfs_args_t *ndmpd_zfs_args)
1694 {
1695 	return (ndmpd_zfs_args->nz_level != 0);
1696 }
1697 
1698 /*
1699  * ndmpd_zfs_snapshot_prepare()
1700  *
1701  * If no snapshot was supplied by the user, create a snapshot
1702  * for use by ndmpd.
1703  */
1704 
1705 static int
1706 ndmpd_zfs_snapshot_prepare(ndmpd_zfs_args_t *ndmpd_zfs_args)
1707 {
1708 	ndmpd_session_t *session = (ndmpd_session_t *)
1709 	    (ndmpd_zfs_params->mp_daemon_cookie);
1710 	boolean_t recursive = B_FALSE;
1711 	int zfs_err = 0;
1712 
1713 	if (session->ns_data.dd_abort) {
1714 		NDMP_LOG(LOG_DEBUG, "Backing up \"%s\" aborted.",
1715 		    ndmpd_zfs_args->nz_dataset);
1716 		return (-1);
1717 	}
1718 
1719 	if (ndmpd_zfs_args->nz_snapname[0] == '\0') {
1720 		ndmpd_zfs_args->nz_ndmpd_snap = B_TRUE;
1721 
1722 		if (ndmpd_zfs_snapshot_create(ndmpd_zfs_args) != 0) {
1723 			ndmpd_zfs_args->nz_snapname[0] = '\0';
1724 
1725 			ndmpd_zfs_dma_log(ndmpd_zfs_args, NDMP_LOG_ERROR,
1726 			    "Error creating snapshot for %s\n",
1727 			    ndmpd_zfs_args->nz_dataset);
1728 
1729 			return (-1);
1730 		}
1731 	}
1732 
1733 	if (ndmpd_zfs_snapshot_prop_add(ndmpd_zfs_args)) {
1734 		NDMP_LOG(LOG_DEBUG,
1735 		    "ndmpd_zfs_snapshot_prop_add error\n");
1736 
1737 		if (ndmpd_zfs_args->nz_ndmpd_snap) {
1738 
1739 			if (ndmpd_zfs_args->nz_zfs_mode != 'd')
1740 				recursive = B_TRUE;
1741 
1742 			(void) snapshot_destroy(ndmpd_zfs_args->nz_dataset,
1743 			    ndmpd_zfs_args->nz_snapname, recursive, &zfs_err);
1744 		}
1745 
1746 		return (-1);
1747 	}
1748 
1749 	return (0);
1750 }
1751 
1752 /*
1753  * ndmpd_zfs_snapshot_cleanup()
1754  *
1755  * If UPDATE = y, find the old snapshot (if any) corresponding to
1756  * {LEVEL, DMP_NAME, ZFS_MODE}. If it was ndmpd-generated,
1757  * remove the snapshot.  Otherwise, update its NDMPD_ZFS_PROP_INCR
1758  * property to remove {L, D, Z}.
1759  *
1760  * If UPDATE = n, if an ndmpd-generated snapshot was used for backup,
1761  * remove the snapshot.  Otherwise, update its NDMPD_ZFS_PROP_INCR
1762  * property to remove {L, D, Z}.
1763  */
1764 
1765 static int
1766 ndmpd_zfs_snapshot_cleanup(ndmpd_zfs_args_t *ndmpd_zfs_args, int err)
1767 {
1768 	ndmpd_session_t *session = (ndmpd_session_t *)
1769 	    (ndmpd_zfs_params->mp_daemon_cookie);
1770 	ndmpd_zfs_snapfind_t snapdata;
1771 	boolean_t ndmpd_generated = B_FALSE;
1772 
1773 	bzero(&snapdata, sizeof (ndmpd_zfs_snapfind_t));
1774 
1775 	(void) ndmpd_zfs_prop_create_subprop(ndmpd_zfs_args,
1776 	    snapdata.nzs_findprop, ZFS_MAXPROPLEN, B_FALSE);
1777 
1778 	if (ndmpd_zfs_args->nz_update && !session->ns_data.dd_abort && !err) {
1779 		/*
1780 		 * Find the existing snapshot, if any, to "unuse."
1781 		 * Indicate that the current snapshot used for backup
1782 		 * should be skipped in the search.  (The search is
1783 		 * sorted by creation time but this cannot be relied
1784 		 * upon for user-supplied snapshots.)
1785 		 */
1786 
1787 		(void) snprintf(snapdata.nzs_snapskip, ZFS_MAXNAMELEN, "%s",
1788 		    ndmpd_zfs_args->nz_snapname);
1789 
1790 		if (ndmpd_zfs_snapshot_find(ndmpd_zfs_args, &snapdata)) {
1791 			NDMP_LOG(LOG_DEBUG, "ndmpd_zfs_snapshot_find error\n");
1792 			goto _remove_tmp_snap;
1793 		}
1794 
1795 		if (snapdata.nzs_snapname[0] != '\0') { /* snapshot found */
1796 			ndmpd_generated = ndmpd_zfs_snapshot_ndmpd_generated
1797 			    (snapdata.nzs_snapprop);
1798 
1799 			if (ndmpd_zfs_snapshot_unuse(ndmpd_zfs_args,
1800 			    ndmpd_generated, &snapdata) != 0) {
1801 				NDMP_LOG(LOG_DEBUG,
1802 				    "ndmpd_zfs_snapshot_unuse error\n");
1803 				goto _remove_tmp_snap;
1804 			}
1805 		}
1806 
1807 		if (session->ns_data.dd_abort)
1808 			goto _remove_tmp_snap;
1809 
1810 		return (0);
1811 	}
1812 
1813 _remove_tmp_snap:
1814 
1815 	(void) snprintf(snapdata.nzs_snapname, ZFS_MAXNAMELEN, "%s",
1816 	    ndmpd_zfs_args->nz_snapname);
1817 
1818 	(void) strlcpy(snapdata.nzs_snapprop, ndmpd_zfs_args->nz_snapprop,
1819 	    ZFS_MAXPROPLEN);
1820 
1821 	snapdata.nzs_snapskip[0] = '\0';
1822 
1823 	if (ndmpd_zfs_snapshot_unuse(ndmpd_zfs_args,
1824 	    ndmpd_zfs_args->nz_ndmpd_snap, &snapdata) != 0) {
1825 		NDMP_LOG(LOG_DEBUG, "ndmpd_zfs_snapshot_unuse error\n");
1826 		return (-1);
1827 	}
1828 
1829 	if (!ndmpd_zfs_args->nz_update)
1830 		return (0);
1831 
1832 	return (-1);
1833 }
1834 
1835 static int
1836 ndmpd_zfs_snapshot_create(ndmpd_zfs_args_t *ndmpd_zfs_args)
1837 {
1838 	boolean_t recursive = B_FALSE;
1839 
1840 	if (ndmpd_zfs_snapname_create(ndmpd_zfs_args,
1841 	    ndmpd_zfs_args->nz_snapname, ZFS_MAXNAMELEN -1) < 0) {
1842 		NDMP_LOG(LOG_ERR, "Error (%d) creating snapshot name for %s",
1843 		    errno, ndmpd_zfs_args->nz_dataset);
1844 		return (-1);
1845 	}
1846 
1847 	if (ndmpd_zfs_args->nz_zfs_mode != 'd')
1848 		recursive = B_TRUE;
1849 
1850 	if (snapshot_create(ndmpd_zfs_args->nz_dataset,
1851 	    ndmpd_zfs_args->nz_snapname, recursive) != 0) {
1852 		NDMP_LOG(LOG_ERR, "could not create snapshot %s@%s",
1853 		    ndmpd_zfs_args->nz_dataset, ndmpd_zfs_args->nz_snapname);
1854 		return (-1);
1855 	}
1856 
1857 	NDMP_LOG(LOG_DEBUG, "created snapshot %s@%s",
1858 	    ndmpd_zfs_args->nz_dataset, ndmpd_zfs_args->nz_snapname);
1859 
1860 	return (0);
1861 }
1862 
1863 /*
1864  * ndmpd_zfs_snapshot_unuse()
1865  *
1866  * Given a pre-existing snapshot of the given {L, D, Z}:
1867  * If snapshot is ndmpd-generated, remove snapshot.
1868  * If not ndmpd-generated, or if the ndmpd-generated snapshot
1869  * cannot be destroyed, remove the {L, D, Z} substring from the
1870  * snapshot's NDMPD_ZFS_PROP_INCR property.
1871  *
1872  * In the event of a failure, it may be that two snapshots will
1873  * have the {L, D, Z} property set on them.  This is not desirable,
1874  * so return an error and log the failure.
1875  */
1876 
1877 static int
1878 ndmpd_zfs_snapshot_unuse(ndmpd_zfs_args_t *ndmpd_zfs_args,
1879     boolean_t ndmpd_generated, ndmpd_zfs_snapfind_t *snapdata_p)
1880 {
1881 	boolean_t recursive = B_FALSE;
1882 	int zfs_err = 0;
1883 	int err = 0;
1884 
1885 	if (ndmpd_generated) {
1886 		if (ndmpd_zfs_args->nz_zfs_mode != 'd')
1887 			recursive = B_TRUE;
1888 
1889 		err = snapshot_destroy(ndmpd_zfs_args->nz_dataset,
1890 		    snapdata_p->nzs_snapname, recursive, &zfs_err);
1891 
1892 		if (err) {
1893 			NDMP_LOG(LOG_ERR, "snapshot_destroy: %s@%s;"
1894 			    " err: %d; zfs_err: %d",
1895 			    ndmpd_zfs_args->nz_dataset,
1896 			    snapdata_p->nzs_snapname, err, zfs_err);
1897 			return (-1);
1898 		}
1899 	}
1900 
1901 	if (!ndmpd_generated || zfs_err) {
1902 		if (ndmpd_zfs_snapshot_prop_remove(ndmpd_zfs_args, snapdata_p))
1903 			return (-1);
1904 	}
1905 
1906 	return (0);
1907 }
1908 
1909 static boolean_t
1910 ndmpd_zfs_snapshot_ndmpd_generated(char *snapprop)
1911 {
1912 	char origin;
1913 
1914 	(void) sscanf(snapprop, "%*u.%*u.%c%*s", &origin);
1915 
1916 	return (origin == 'n');
1917 }
1918 
1919 /*
1920  * ndmpd_zfs_snapshot_find()
1921  *
1922  * Find a snapshot with a particular value for
1923  * the NDMPD_ZFS_PROP_INCR property.
1924  */
1925 
1926 static int
1927 ndmpd_zfs_snapshot_find(ndmpd_zfs_args_t *ndmpd_zfs_args,
1928     ndmpd_zfs_snapfind_t *snapdata)
1929 {
1930 	zfs_handle_t *zhp;
1931 	int err;
1932 
1933 	zhp = zfs_open(ndmpd_zfs_args->nz_zlibh, ndmpd_zfs_args->nz_dataset,
1934 	    ndmpd_zfs_args->nz_type);
1935 
1936 	if (!zhp) {
1937 		NDMPD_ZFS_LOG_ZERR(ndmpd_zfs_args, "zfs_open");
1938 		return (-1);
1939 	}
1940 
1941 	err = zfs_iter_snapshots_sorted(zhp, ndmpd_zfs_snapshot_prop_find,
1942 	    snapdata);
1943 
1944 	zfs_close(zhp);
1945 
1946 	if (err) {
1947 		NDMPD_ZFS_LOG_ZERR(ndmpd_zfs_args, "zfs_iter_snapshots: %d",
1948 		    err);
1949 		ndmpd_zfs_dma_log(ndmpd_zfs_args, NDMP_LOG_ERROR,
1950 		    "Error iterating snapshots\n");
1951 		return (-1);
1952 	}
1953 
1954 	return (0);
1955 }
1956 
1957 /*
1958  * ndmpd_zfs_snapshot_prop_find()
1959  *
1960  * Find a snapshot with a particular value for
1961  * NDMPD_ZFS_PROP_INCR.  Fill in data for the first one
1962  * found (sorted by creation time).  However, skip the
1963  * the snapshot indicated in nzs_snapskip, if any.
1964  */
1965 
1966 static int
1967 ndmpd_zfs_snapshot_prop_find(zfs_handle_t *zhp, void *arg)
1968 {
1969 	ndmpd_zfs_snapfind_t *snapdata_p = (ndmpd_zfs_snapfind_t *)arg;
1970 	char propstr[ZFS_MAXPROPLEN];
1971 	char findprop_plus_slash[ZFS_MAXPROPLEN];
1972 	char *justsnap;
1973 	int err = 0;
1974 
1975 	if (snapdata_p->nzs_snapname[0] != '\0') {
1976 		NDMP_LOG(LOG_DEBUG, "no need to get prop for snapshot %s",
1977 		    (char *)zfs_get_name(zhp));
1978 		goto _done;
1979 	}
1980 
1981 	err = ndmpd_zfs_snapshot_prop_get(zhp, propstr);
1982 
1983 	if (err) {
1984 		NDMP_LOG(LOG_DEBUG, "ndmpd_zfs_snapshot_prop_get failed");
1985 		goto _done;
1986 	}
1987 
1988 	if (propstr[0] == '\0') {
1989 		NDMP_LOG(LOG_DEBUG, "snapshot %s: propr not set",
1990 		    (char *)zfs_get_name(zhp));
1991 		goto _done;
1992 	}
1993 
1994 	(void) snprintf(findprop_plus_slash, ZFS_MAXPROPLEN, "/%s",
1995 	    snapdata_p->nzs_findprop);
1996 
1997 	if (!strstr((const char *)propstr,
1998 	    (const char *)findprop_plus_slash)) {
1999 		NDMP_LOG(LOG_DEBUG, "snapshot %s: property %s (%s not found)",
2000 		    (char *)zfs_get_name(zhp), propstr,
2001 		    snapdata_p->nzs_findprop);
2002 		goto _done;
2003 	}
2004 
2005 	if (!ndmpd_zfs_prop_version_check(propstr,
2006 	    &snapdata_p->nzs_prop_major, &snapdata_p->nzs_prop_minor)) {
2007 		NDMP_LOG(LOG_DEBUG, "snapshot %s: property %s (%s found)",
2008 		    (char *)zfs_get_name(zhp),  propstr,
2009 		    snapdata_p->nzs_findprop);
2010 		NDMP_LOG(LOG_DEBUG, "did not pass version check");
2011 		goto _done;
2012 	}
2013 
2014 	justsnap = strchr(zfs_get_name(zhp), '@') + 1;
2015 
2016 	if (strcmp(justsnap, snapdata_p->nzs_snapskip) != 0) {
2017 		(void) strlcpy(snapdata_p->nzs_snapname, justsnap,
2018 		    ZFS_MAXNAMELEN);
2019 
2020 		(void) strlcpy(snapdata_p->nzs_snapprop, propstr,
2021 		    ZFS_MAXPROPLEN);
2022 
2023 		NDMP_LOG(LOG_DEBUG, "found match: %s [%s]\n",
2024 		    snapdata_p->nzs_snapname, snapdata_p->nzs_snapprop);
2025 	}
2026 
2027 _done:
2028 	zfs_close(zhp);
2029 	return (err);
2030 }
2031 
2032 /*
2033  * ndmpd_zfs_snapshot_prop_get()
2034  *
2035  * Retrieve NDMPD_ZFS_PROP_INCR property from snapshot
2036  */
2037 
2038 static int
2039 ndmpd_zfs_snapshot_prop_get(zfs_handle_t *zhp, char *propstr)
2040 {
2041 	nvlist_t *userprop;
2042 	nvlist_t *propval;
2043 	char *strval;
2044 	int err;
2045 
2046 	propstr[0] = '\0';
2047 
2048 	userprop = zfs_get_user_props(zhp);
2049 
2050 	if (userprop == NULL)
2051 		return (0);
2052 
2053 	err = nvlist_lookup_nvlist(userprop, NDMPD_ZFS_PROP_INCR, &propval);
2054 
2055 	if (err != 0) {
2056 		if (err == ENOENT)
2057 			return (0);
2058 
2059 		NDMP_LOG(LOG_DEBUG, "nvlist_lookup_nvlist error: %d\n", err);
2060 
2061 		return (-1);
2062 	}
2063 
2064 	err = nvlist_lookup_string(propval, ZPROP_VALUE, &strval);
2065 
2066 	if (err != 0) {
2067 		if (err == ENOENT)
2068 			return (0);
2069 
2070 		NDMP_LOG(LOG_DEBUG, "nvlist_lookup_string error: %d\n", err);
2071 
2072 		return (-1);
2073 	}
2074 
2075 	(void) strlcpy(propstr, strval, ZFS_MAXPROPLEN);
2076 
2077 	return (0);
2078 }
2079 
2080 /*
2081  * ndmpd_zfs_snapshot_prop_add()
2082  *
2083  * Update snapshot's NDMPD_ZFS_PROP_INCR property with
2084  * the current LEVEL, DMP_NAME, and ZFS_MODE values
2085  * (add property if it doesn't exist)
2086  */
2087 
2088 static int
2089 ndmpd_zfs_snapshot_prop_add(ndmpd_zfs_args_t *ndmpd_zfs_args)
2090 {
2091 	char fullname[ZFS_MAXNAMELEN];
2092 	char propstr[ZFS_MAXPROPLEN];
2093 	zfs_handle_t *zhp;
2094 	boolean_t set;
2095 	int err;
2096 
2097 	(void) snprintf(fullname, ZFS_MAXNAMELEN, "%s@%s",
2098 	    ndmpd_zfs_args->nz_dataset,
2099 	    ndmpd_zfs_args->nz_snapname);
2100 
2101 	zhp = zfs_open(ndmpd_zfs_args->nz_zlibh, fullname, ZFS_TYPE_SNAPSHOT);
2102 
2103 	if (!zhp) {
2104 		NDMPD_ZFS_LOG_ZERR(ndmpd_zfs_args, "zfs_open (snap)");
2105 		return (-1);
2106 	}
2107 
2108 	bzero(propstr, ZFS_MAXPROPLEN);
2109 
2110 	if (ndmpd_zfs_snapshot_prop_get(zhp, propstr)) {
2111 		NDMP_LOG(LOG_DEBUG, "error getting property");
2112 		zfs_close(zhp);
2113 		return (-1);
2114 	}
2115 
2116 	if (ndmpd_zfs_snapshot_prop_create(ndmpd_zfs_args, propstr, &set)) {
2117 		zfs_close(zhp);
2118 		return (-1);
2119 	}
2120 
2121 	if (set) {
2122 		err = zfs_prop_set(zhp, NDMPD_ZFS_PROP_INCR, propstr);
2123 		if (err) {
2124 			NDMPD_ZFS_LOG_ZERR(ndmpd_zfs_args, "zfs_prop_set: %d",
2125 			    err);
2126 			NDMP_LOG(LOG_ERR, "error setting property %s",
2127 			    propstr);
2128 			zfs_close(zhp);
2129 			return (-1);
2130 		}
2131 	}
2132 
2133 	zfs_close(zhp);
2134 
2135 	(void) strlcpy(ndmpd_zfs_args->nz_snapprop, propstr, ZFS_MAXPROPLEN);
2136 
2137 	return (0);
2138 }
2139 
2140 static int
2141 ndmpd_zfs_snapshot_prop_create(ndmpd_zfs_args_t *ndmpd_zfs_args,
2142     char *propstr, boolean_t *set)
2143 {
2144 	char subprop[ZFS_MAXPROPLEN];
2145 	char *p = propstr;
2146 	int slash_count = 0;
2147 
2148 	*set = B_TRUE;
2149 
2150 	(void) ndmpd_zfs_prop_create_subprop(ndmpd_zfs_args,
2151 	    subprop, ZFS_MAXPROPLEN, B_FALSE);
2152 
2153 	if (propstr[0] == '\0') {
2154 		(void) snprintf(propstr, ZFS_MAXPROPLEN, "%u.%u.%c/%s",
2155 		    NDMPD_ZFS_PROP_MAJOR_VERSION,
2156 		    NDMPD_ZFS_PROP_MINOR_VERSION,
2157 		    (ndmpd_zfs_args->nz_ndmpd_snap) ? 'n' : 'u',
2158 		    subprop);
2159 		return (0);
2160 	}
2161 
2162 	if (strstr((const char *)propstr, (const char *)subprop)) {
2163 		NDMP_LOG(LOG_DEBUG, "Did not add entry %s as it already exists",
2164 		    subprop);
2165 		*set = B_FALSE;
2166 		return (0);
2167 	}
2168 
2169 	while (*p) {
2170 		if (*(p++) == '/')
2171 			slash_count++;
2172 	}
2173 
2174 	if (slash_count >= NDMPD_ZFS_SUBPROP_MAX) {
2175 		ndmpd_zfs_dma_log(ndmpd_zfs_args, NDMP_LOG_ERROR,
2176 		    "snapshot %s: user property %s limit of %d subprops "
2177 		    "reached; cannot complete operation",
2178 		    ndmpd_zfs_args->nz_snapname,
2179 		    NDMPD_ZFS_PROP_INCR,
2180 		    NDMPD_ZFS_SUBPROP_MAX);
2181 		return (-1);
2182 	}
2183 
2184 	assert((strlen(propstr) + strlen(subprop) + 2) < ZFS_MAXPROPLEN);
2185 
2186 	(void) strlcat(propstr, "/", ZFS_MAXPROPLEN);
2187 	(void) strlcat(propstr, subprop, ZFS_MAXPROPLEN);
2188 
2189 	return (0);
2190 }
2191 
2192 static int
2193 ndmpd_zfs_prop_create_subprop(ndmpd_zfs_args_t *ndmpd_zfs_args,
2194     char *subprop, int len, boolean_t prev_level)
2195 {
2196 	return (snprintf(subprop, len, "%d.%s.%c",
2197 	    prev_level ? ndmpd_zfs_args->nz_level-1 : ndmpd_zfs_args->nz_level,
2198 	    ndmpd_zfs_args->nz_dmp_name,
2199 	    ndmpd_zfs_args->nz_zfs_mode));
2200 }
2201 
2202 /*
2203  * ndmpd_zfs_snapshot_prop_remove()
2204  *
2205  * Remove specified substring from the snapshot's
2206  * NDMPD_ZFS_PROP_INCR property
2207  */
2208 
2209 static int
2210 ndmpd_zfs_snapshot_prop_remove(ndmpd_zfs_args_t *ndmpd_zfs_args,
2211     ndmpd_zfs_snapfind_t *snapdata_p)
2212 {
2213 	char findprop_plus_slash[ZFS_MAXPROPLEN];
2214 	char fullname[ZFS_MAXNAMELEN];
2215 	char newprop[ZFS_MAXPROPLEN];
2216 	char tmpstr[ZFS_MAXPROPLEN];
2217 	zfs_handle_t *zhp;
2218 	char *ptr;
2219 	int err;
2220 
2221 	(void) snprintf(fullname, ZFS_MAXNAMELEN, "%s@%s",
2222 	    ndmpd_zfs_args->nz_dataset,
2223 	    snapdata_p->nzs_snapname);
2224 
2225 	zhp = zfs_open(ndmpd_zfs_args->nz_zlibh, fullname, ZFS_TYPE_SNAPSHOT);
2226 
2227 	if (!zhp) {
2228 		NDMPD_ZFS_LOG_ZERR(ndmpd_zfs_args, "zfs_open");
2229 		return (-1);
2230 	}
2231 
2232 	bzero(newprop, ZFS_MAXPROPLEN);
2233 
2234 	/*
2235 	 * If the substring to be removed is the only {L, D, Z}
2236 	 * in the property, remove the entire property
2237 	 */
2238 
2239 	tmpstr[0] = '\0';
2240 
2241 	(void) sscanf(snapdata_p->nzs_snapprop, "%*u.%*u.%*c/%1023s", tmpstr);
2242 
2243 	if (strcmp(tmpstr, snapdata_p->nzs_findprop) == 0) {
2244 
2245 		err = zfs_prop_set(zhp, NDMPD_ZFS_PROP_INCR, newprop);
2246 
2247 		zfs_close(zhp);
2248 
2249 		if (err) {
2250 			NDMPD_ZFS_LOG_ZERR(ndmpd_zfs_args, "zfs_prop_set: %d",
2251 			    err);
2252 			NDMP_LOG(LOG_ERR, "error setting property %s", newprop);
2253 			return (-1);
2254 		}
2255 
2256 		return (0);
2257 	}
2258 
2259 	(void) snprintf(findprop_plus_slash, ZFS_MAXPROPLEN, "/%s",
2260 	    snapdata_p->nzs_findprop);
2261 
2262 	ptr = strstr((const char *)snapdata_p->nzs_snapprop,
2263 	    (const char *)findprop_plus_slash);
2264 
2265 	if (ptr == NULL) {
2266 		/*
2267 		 * This shouldn't happen.  Just return success.
2268 		 */
2269 		zfs_close(zhp);
2270 
2271 		return (0);
2272 	}
2273 
2274 	/*
2275 	 * Remove "nzs_findprop" substring from property
2276 	 *
2277 	 * Example property:
2278 	 *	0.0.u/1.bob.p/0.jane.d
2279 	 *
2280 	 * Note that there will always be a prefix to the
2281 	 * strstr() result.  Hence the below code works for
2282 	 * all cases.
2283 	 */
2284 
2285 	ptr--;
2286 	(void) strncpy(newprop, snapdata_p->nzs_snapprop,
2287 	    (char *)ptr - snapdata_p->nzs_snapprop);
2288 	ptr += strlen(snapdata_p->nzs_findprop) + 1;
2289 	(void) strlcat(newprop, ptr, ZFS_MAXPROPLEN);
2290 
2291 	err = zfs_prop_set(zhp, NDMPD_ZFS_PROP_INCR, newprop);
2292 
2293 	zfs_close(zhp);
2294 
2295 	if (err) {
2296 		NDMPD_ZFS_LOG_ZERR(ndmpd_zfs_args, "zfs_prop_set: %d", err);
2297 		NDMP_LOG(LOG_ERR, "error modifying property %s", newprop);
2298 		return (-1);
2299 	}
2300 
2301 	return (0);
2302 }
2303 
2304 static boolean_t
2305 ndmpd_zfs_prop_version_check(char *propstr, uint32_t *major, uint32_t *minor)
2306 {
2307 	(void) sscanf(propstr, "%u.%u.%*c%*s", major, minor);
2308 
2309 	if (*major > NDMPD_ZFS_PROP_MAJOR_VERSION) {
2310 		NDMP_LOG(LOG_ERR, "unsupported prop major (%u > %u)",
2311 		    *major, NDMPD_ZFS_PROP_MAJOR_VERSION);
2312 		return (B_FALSE);
2313 	}
2314 
2315 	if (*minor > NDMPD_ZFS_PROP_MINOR_VERSION) {
2316 		NDMP_LOG(LOG_ERR, "later prop minor (%u > %u)",
2317 		    *minor, NDMPD_ZFS_PROP_MINOR_VERSION);
2318 	}
2319 
2320 	NDMP_LOG(LOG_DEBUG, "passed version check: "
2321 	    "supported prop major (%u <= %u); (snapprop minor: %u [%u])",
2322 	    *major, NDMPD_ZFS_PROP_MAJOR_VERSION,
2323 	    *minor, NDMPD_ZFS_PROP_MINOR_VERSION);
2324 
2325 	return (B_TRUE);
2326 }
2327 
2328 static int
2329 ndmpd_zfs_snapname_create(ndmpd_zfs_args_t *ndmpd_zfs_args,
2330     char *snapname, int namelen)
2331 {
2332 	char subprop[ZFS_MAXPROPLEN];
2333 	struct timeval tp;
2334 	int err = 0;
2335 
2336 	(void) ndmpd_zfs_prop_create_subprop(ndmpd_zfs_args,
2337 	    subprop, ZFS_MAXPROPLEN, B_FALSE);
2338 
2339 	(void) gettimeofday(&tp, NULL);
2340 
2341 	err = snprintf(snapname, namelen,
2342 	    "ndmp.%s.%ld.%ld",
2343 	    subprop,
2344 	    tp.tv_sec,
2345 	    tp.tv_usec);
2346 
2347 	if (err > namelen) {
2348 		NDMP_LOG(LOG_ERR, "name of snapshot [%s...] would exceed %d",
2349 		    snapname, namelen);
2350 		return (-1);
2351 	}
2352 
2353 	return (0);
2354 }
2355 
2356 static void
2357 ndmpd_zfs_zerr_dma_log(ndmpd_zfs_args_t *ndmpd_zfs_args)
2358 {
2359 	switch (libzfs_errno(ndmpd_zfs_args->nz_zlibh)) {
2360 	case EZFS_EXISTS:
2361 	case EZFS_BUSY:
2362 	case EZFS_NOENT:
2363 	case EZFS_INVALIDNAME:
2364 	case EZFS_MOUNTFAILED:
2365 	case EZFS_UMOUNTFAILED:
2366 	case EZFS_NAMETOOLONG:
2367 	case EZFS_BADRESTORE:
2368 
2369 		/* use existing error text */
2370 
2371 		ndmpd_zfs_dma_log(ndmpd_zfs_args, NDMP_LOG_ERROR,
2372 		    "%s: %s: %s\n",
2373 		    ndmpd_zfs_args->nz_dataset,
2374 		    libzfs_error_action(ndmpd_zfs_args->nz_zlibh),
2375 		    libzfs_error_description(ndmpd_zfs_args->nz_zlibh));
2376 
2377 		break;
2378 
2379 	case EZFS_NOMEM:
2380 		ndmpd_zfs_dma_log(ndmpd_zfs_args, NDMP_LOG_ERROR,
2381 		    "Unable to obtain memory for operation\n");
2382 		break;
2383 
2384 	case EZFS_PROPSPACE:
2385 		ndmpd_zfs_dma_log(ndmpd_zfs_args, NDMP_LOG_ERROR,
2386 		    "A bad ZFS quota or reservation was encountered.\n");
2387 		break;
2388 
2389 	case EZFS_BADSTREAM:
2390 		ndmpd_zfs_dma_log(ndmpd_zfs_args, NDMP_LOG_ERROR,
2391 		    "The backup stream is invalid.\n");
2392 		break;
2393 
2394 	case EZFS_ZONED:
2395 		ndmpd_zfs_dma_log(ndmpd_zfs_args, NDMP_LOG_ERROR,
2396 		    "An error related to the local zone occurred.\n");
2397 		break;
2398 
2399 	case EZFS_NOSPC:
2400 		ndmpd_zfs_dma_log(ndmpd_zfs_args, NDMP_LOG_ERROR,
2401 		    "No more space is available\n");
2402 		break;
2403 
2404 	case EZFS_IO:
2405 		ndmpd_zfs_dma_log(ndmpd_zfs_args, NDMP_LOG_ERROR,
2406 		    "An I/O error occurred.\n");
2407 		break;
2408 
2409 	default:
2410 		ndmpd_zfs_dma_log(ndmpd_zfs_args, NDMP_LOG_ERROR,
2411 		    "An internal ndmpd error occurred.  "
2412 		    "Please contact support\n");
2413 		break;
2414 	}
2415 }
2416 
2417 void
2418 ndmpd_zfs_dma_log(ndmpd_zfs_args_t *ndmpd_zfs_args, ndmp_log_type log_type,
2419     char *format, ...)
2420 {
2421 	static char buf[1024];
2422 	va_list ap;
2423 
2424 	va_start(ap, format);
2425 
2426 	/*LINTED variable format specifier */
2427 	(void) vsnprintf(buf, sizeof (buf), format, ap);
2428 	va_end(ap);
2429 
2430 	MOD_LOGV3(ndmpd_zfs_params, log_type, buf);
2431 
2432 	if ((log_type) == NDMP_LOG_ERROR) {
2433 		NDMP_LOG(LOG_ERR, buf);
2434 	} else {
2435 		NDMP_LOG(LOG_NOTICE, buf);
2436 	}
2437 }
2438