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