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