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