1 /*
2  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
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 #include <stdio.h>
40 #include <limits.h>
41 #include <time.h>
42 #include <sys/stat.h>
43 #include <unistd.h>
44 #include <dirent.h>
45 #include <pthread.h>
46 #include <archives.h>
47 #include <tlm.h>
48 #include <sys/fs/zfs.h>
49 #include <libzfs.h>
50 #include <libcmdutils.h>
51 #include <pwd.h>
52 #include <grp.h>
53 #include "tlm_proto.h"
54 
55 
56 static char *get_write_buffer(long size,
57     long *actual_size,
58     boolean_t zero,
59     tlm_cmd_t *);
60 static int output_acl_header(sec_attr_t *,
61     tlm_cmd_t *);
62 static int output_file_header(char *name,
63     char *link,
64     tlm_acls_t *,
65     int section,
66     tlm_cmd_t *);
67 static int output_xattr_header(char *fname,
68     char *aname,
69     int fd,
70     tlm_acls_t *,
71     int section,
72     tlm_cmd_t *);
73 
74 extern  libzfs_handle_t *zlibh;
75 extern  mutex_t zlib_mtx;
76 
77 
78 /*
79  * output_mem
80  *
81  * Gets a IO write buffer and copies memory to the that.
82  */
83 static void
84 output_mem(tlm_cmd_t *local_commands, char *mem,
85     int len)
86 {
87 	long actual_size, rec_size;
88 	char *rec;
89 
90 	while (len > 0) {
91 		rec = get_write_buffer(len, &actual_size,
92 		    FALSE, local_commands);
93 		rec_size = min(actual_size, len);
94 		(void) memcpy(rec, mem, rec_size);
95 		mem += rec_size;
96 		len -= rec_size;
97 	}
98 }
99 
100 /*
101  * tlm_output_dir
102  *
103  * Put the directory information into the output buffers.
104  */
105 int
106 tlm_output_dir(char *name, tlm_acls_t *tlm_acls,
107     tlm_cmd_t *local_commands, tlm_job_stats_t *job_stats)
108 {
109 	u_longlong_t pos;
110 
111 	/*
112 	 * Send the node or path history of the directory itself.
113 	 */
114 	pos = tlm_get_data_offset(local_commands);
115 	NDMP_LOG(LOG_DEBUG, "pos: %10lld  [%s]", pos, name);
116 	(void) tlm_log_fhnode(job_stats, name, "", &tlm_acls->acl_attr, pos);
117 	(void) tlm_log_fhpath_name(job_stats, name, &tlm_acls->acl_attr, pos);
118 	/* fhdir_cb is handled in ndmpd_tar3.c */
119 
120 	(void) output_acl_header(&tlm_acls->acl_info,
121 	    local_commands);
122 	(void) output_file_header(name, "", tlm_acls, 0,
123 	    local_commands);
124 
125 	return (0);
126 }
127 
128 /*
129  * tar_putdir
130  *
131  * Main dir backup function for tar
132  */
133 int
134 tar_putdir(char *name, tlm_acls_t *tlm_acls,
135     tlm_cmd_t *local_commands, tlm_job_stats_t *job_stats)
136 {
137 	int rv;
138 
139 	rv = tlm_output_dir(name, tlm_acls, local_commands, job_stats);
140 	return (rv < 0 ? rv : 0);
141 }
142 
143 /*
144  * output_acl_header
145  *
146  * output the ACL header record and data
147  */
148 static int
149 output_acl_header(sec_attr_t *acl_info,
150     tlm_cmd_t *local_commands)
151 {
152 	long	actual_size;
153 	tlm_tar_hdr_t *tar_hdr;
154 	long	acl_size;
155 
156 	if ((acl_info == NULL) || (*acl_info->attr_info == '\0'))
157 		return (0);
158 
159 	tar_hdr = (tlm_tar_hdr_t *)get_write_buffer(RECORDSIZE,
160 	    &actual_size, TRUE, local_commands);
161 	if (!tar_hdr)
162 		return (0);
163 
164 	tar_hdr->th_linkflag = LF_ACL;
165 	acl_info->attr_type = UFSD_ACL;
166 	(void) snprintf(acl_info->attr_len, sizeof (acl_info->attr_len),
167 	    "%06o", strlen(acl_info->attr_info));
168 
169 	acl_size = sizeof (*acl_info);
170 	(void) strlcpy(tar_hdr->th_name, "UFSACL", TLM_NAME_SIZE);
171 	(void) snprintf(tar_hdr->th_size, sizeof (tar_hdr->th_size), "%011o ",
172 	    acl_size);
173 	(void) snprintf(tar_hdr->th_mode, sizeof (tar_hdr->th_mode), "%06o ",
174 	    0444);
175 	(void) snprintf(tar_hdr->th_uid, sizeof (tar_hdr->th_uid), "%06o ", 0);
176 	(void) snprintf(tar_hdr->th_gid, sizeof (tar_hdr->th_gid), "%06o ", 0);
177 	(void) snprintf(tar_hdr->th_mtime, sizeof (tar_hdr->th_mtime),
178 	    "%011o ", 0);
179 	(void) strlcpy(tar_hdr->th_magic, TLM_MAGIC,
180 	    sizeof (tar_hdr->th_magic));
181 
182 	tlm_build_header_checksum(tar_hdr);
183 
184 	(void) output_mem(local_commands, (void *)acl_info, acl_size);
185 	return (0);
186 }
187 
188 /*
189  * output_humongus_header
190  *
191  * output a special header record for HUGE files
192  * output is:	1) a TAR "HUGE" header redord
193  * 		2) a "file" of size, name
194  */
195 static int
196 output_humongus_header(char *fullname, longlong_t file_size,
197     tlm_cmd_t *local_commands)
198 {
199 	char	*buf;
200 	int	len;
201 	long	actual_size;
202 	tlm_tar_hdr_t *tar_hdr;
203 
204 	/*
205 	 * buf will contain: "%llu %s":
206 	 * - 20 is the maximum length of 'ulong_tlong' decimal notation.
207 	 * - The first '1' is for the ' ' between the "%llu" and the fullname.
208 	 * - The last '1' is for the null-terminator of fullname.
209 	 */
210 	len = 20 + 1 + strlen(fullname) + 1;
211 
212 	if ((buf = ndmp_malloc(sizeof (char) * len)) == NULL)
213 		return (-1);
214 
215 	tar_hdr = (tlm_tar_hdr_t *)get_write_buffer(RECORDSIZE,
216 	    &actual_size, TRUE, local_commands);
217 	if (!tar_hdr) {
218 		free(buf);
219 		return (0);
220 	}
221 
222 	tar_hdr->th_linkflag = LF_HUMONGUS;
223 	(void) snprintf(tar_hdr->th_size, sizeof (tar_hdr->th_size), "%011o ",
224 	    len);
225 	tlm_build_header_checksum(tar_hdr);
226 	(void) snprintf(buf, len, "%lld %s", file_size, fullname);
227 	(void) output_mem(local_commands, buf, len);
228 
229 	free(buf);
230 	return (0);
231 }
232 
233 
234 /*
235  * output_xattr_header
236  *
237  * output the TAR header record for extended attributes
238  */
239 static int
240 output_xattr_header(char *fname, char *aname, int fd,
241     tlm_acls_t *tlm_acls, int section, tlm_cmd_t *local_commands)
242 {
243 	struct stat64 *attr = &tlm_acls->acl_attr;
244 	struct xattr_hdr *xhdr;
245 	struct xattr_buf *xbuf;
246 	tlm_tar_hdr_t *tar_hdr;
247 	long	actual_size;
248 	char	*section_name = ndmp_malloc(TLM_MAX_PATH_NAME);
249 	int	hsize;
250 	int	comlen;
251 	int	namesz;
252 
253 	if (section_name == NULL)
254 		return (-TLM_NO_SCRATCH_SPACE);
255 
256 	if (fstat64(fd, attr) == -1) {
257 		NDMP_LOG(LOG_DEBUG, "output_file_header stat failed.");
258 		free(section_name);
259 		return (-TLM_OPEN_ERR);
260 	}
261 
262 	/*
263 	 * if the file has to go out in sections,
264 	 * we must mung the name.
265 	 */
266 	if (section == 0) {
267 		(void) snprintf(section_name, TLM_MAX_PATH_NAME,
268 		    "/dev/null/%s.hdr", aname);
269 	} else {
270 		(void) snprintf(section_name,
271 		    TLM_MAX_PATH_NAME, "%s.%03d", aname, section);
272 	}
273 	namesz = strlen(section_name) + strlen(fname) + 2; /* 2 nulls */
274 	hsize = namesz + sizeof (struct xattr_hdr) + sizeof (struct xattr_buf);
275 	comlen = namesz + sizeof (struct xattr_buf);
276 
277 	tar_hdr = (tlm_tar_hdr_t *)get_write_buffer(RECORDSIZE,
278 	    &actual_size, TRUE, local_commands);
279 	if (!tar_hdr) {
280 		free(section_name);
281 		return (0);
282 	}
283 
284 	(void) strlcpy(tar_hdr->th_name, section_name, TLM_NAME_SIZE);
285 
286 	tar_hdr->th_linkflag = LF_XATTR;
287 	(void) snprintf(tar_hdr->th_size, sizeof (tar_hdr->th_size), "%011o ",
288 	    hsize);
289 	(void) snprintf(tar_hdr->th_mode, sizeof (tar_hdr->th_mode), "%06o ",
290 	    attr->st_mode & 07777);
291 	(void) snprintf(tar_hdr->th_uid, sizeof (tar_hdr->th_uid), "%06o ",
292 	    attr->st_uid);
293 	(void) snprintf(tar_hdr->th_gid, sizeof (tar_hdr->th_gid), "%06o ",
294 	    attr->st_gid);
295 	(void) snprintf(tar_hdr->th_mtime, sizeof (tar_hdr->th_mtime), "%011o ",
296 	    attr->st_mtime);
297 	(void) strlcpy(tar_hdr->th_magic, TLM_MAGIC,
298 	    sizeof (tar_hdr->th_magic));
299 
300 	NDMP_LOG(LOG_DEBUG, "xattr_hdr: %s size %d mode %06o uid %d gid %d",
301 	    aname, hsize, attr->st_mode & 07777, attr->st_uid, attr->st_gid);
302 
303 	tlm_build_header_checksum(tar_hdr);
304 
305 	xhdr = (struct xattr_hdr *)get_write_buffer(RECORDSIZE,
306 	    &actual_size, TRUE, local_commands);
307 	if (!xhdr) {
308 		free(section_name);
309 		return (0);
310 	}
311 
312 	(void) snprintf(xhdr->h_version, sizeof (xhdr->h_version), "%s",
313 	    XATTR_ARCH_VERS);
314 	(void) snprintf(xhdr->h_size, sizeof (xhdr->h_size), "%0*d",
315 	    sizeof (xhdr->h_size) - 1, hsize);
316 	(void) snprintf(xhdr->h_component_len, sizeof (xhdr->h_component_len),
317 	    "%0*d", sizeof (xhdr->h_component_len) - 1, comlen);
318 	(void) snprintf(xhdr->h_link_component_len,
319 	    sizeof (xhdr->h_link_component_len), "%0*d",
320 	    sizeof (xhdr->h_link_component_len) - 1, 0);
321 
322 	xbuf = (struct xattr_buf *)(((caddr_t)xhdr) +
323 	    sizeof (struct xattr_hdr));
324 	(void) snprintf(xbuf->h_namesz, sizeof (xbuf->h_namesz), "%0*d",
325 	    sizeof (xbuf->h_namesz) - 1, namesz);
326 
327 	/* No support for links in extended attributes */
328 	xbuf->h_typeflag = LF_NORMAL;
329 
330 	(void) strlcpy(xbuf->h_names, fname, TLM_NAME_SIZE);
331 	(void) strlcpy(&xbuf->h_names[strlen(fname) + 1], aname,
332 	    TLM_NAME_SIZE);
333 
334 	free(section_name);
335 	return (0);
336 }
337 
338 
339 /*
340  * output_file_header
341  *
342  * output the TAR header record
343  */
344 static int
345 output_file_header(char *name, char *link,
346     tlm_acls_t *tlm_acls, int section, tlm_cmd_t *local_commands)
347 {
348 	static	longlong_t file_count = 0;
349 	struct stat64 *attr = &tlm_acls->acl_attr;
350 	tlm_tar_hdr_t *tar_hdr;
351 	long	actual_size;
352 	boolean_t long_name = FALSE;
353 	boolean_t long_link = FALSE;
354 	char	*section_name = ndmp_malloc(TLM_MAX_PATH_NAME);
355 	int	nmlen, lnklen;
356 	uid_t uid;
357 	gid_t gid;
358 	char *uname = "";
359 	char *gname = "";
360 	struct passwd *pwd;
361 	struct group *grp;
362 
363 	if (section_name == NULL)
364 		return (-TLM_NO_SCRATCH_SPACE);
365 
366 	/*
367 	 * if the file has to go out in sections,
368 	 * we must mung the name.
369 	 */
370 	if (section == 0) {
371 		(void) strlcpy(section_name, name, TLM_MAX_PATH_NAME);
372 	} else {
373 		(void) snprintf(section_name,
374 		    TLM_MAX_PATH_NAME, "%s.%03d", name, section);
375 	}
376 
377 	if ((pwd = getpwuid(attr->st_uid)) != NULL)
378 		uname = pwd->pw_name;
379 	if ((grp = getgrgid(attr->st_gid)) != NULL)
380 		gname = grp->gr_name;
381 
382 	if ((ulong_t)(uid = attr->st_uid) > (ulong_t)OCTAL7CHAR)
383 		uid = UID_NOBODY;
384 	if ((ulong_t)(gid = attr->st_gid) > (ulong_t)OCTAL7CHAR)
385 		gid = GID_NOBODY;
386 
387 	nmlen = strlen(section_name);
388 	if (nmlen >= NAMSIZ) {
389 		/*
390 		 * file name is too big, it must go out
391 		 * in its own data file
392 		 */
393 		tar_hdr = (tlm_tar_hdr_t *)get_write_buffer(RECORDSIZE,
394 		    &actual_size, TRUE, local_commands);
395 		if (!tar_hdr) {
396 			free(section_name);
397 			return (0);
398 		}
399 		(void) snprintf(tar_hdr->th_name,
400 		    sizeof (tar_hdr->th_name),
401 		    "%s%08qd.fil",
402 		    LONGNAME_PREFIX,
403 		    file_count++);
404 
405 		tar_hdr->th_linkflag = LF_LONGNAME;
406 		(void) snprintf(tar_hdr->th_size, sizeof (tar_hdr->th_size),
407 		    "%011o ", nmlen);
408 		(void) snprintf(tar_hdr->th_mode, sizeof (tar_hdr->th_mode),
409 		    "%06o ", attr->st_mode & 07777);
410 		(void) snprintf(tar_hdr->th_uid, sizeof (tar_hdr->th_uid),
411 		    "%06o ", uid);
412 		(void) snprintf(tar_hdr->th_gid, sizeof (tar_hdr->th_gid),
413 		    "%06o ", gid);
414 		(void) snprintf(tar_hdr->th_uname, sizeof (tar_hdr->th_uname),
415 		    "%.31s", uname);
416 		(void) snprintf(tar_hdr->th_gname, sizeof (tar_hdr->th_gname),
417 		    "%.31s", gname);
418 		(void) snprintf(tar_hdr->th_mtime, sizeof (tar_hdr->th_mtime),
419 		    "%011o ", attr->st_mtime);
420 		(void) strlcpy(tar_hdr->th_magic, TLM_MAGIC,
421 		    sizeof (tar_hdr->th_magic));
422 
423 		tlm_build_header_checksum(tar_hdr);
424 
425 		(void) output_mem(local_commands,
426 		    (void *)section_name, nmlen);
427 		long_name = TRUE;
428 	}
429 
430 	lnklen = strlen(link);
431 	if (lnklen >= NAMSIZ) {
432 		/*
433 		 * link name is too big, it must go out
434 		 * in its own data file
435 		 */
436 		tar_hdr = (tlm_tar_hdr_t *)get_write_buffer(RECORDSIZE,
437 		    &actual_size, TRUE, local_commands);
438 		if (!tar_hdr) {
439 			free(section_name);
440 			return (0);
441 		}
442 		(void) snprintf(tar_hdr->th_linkname,
443 		    sizeof (tar_hdr->th_name),
444 		    "%s%08qd.slk",
445 		    LONGNAME_PREFIX,
446 		    file_count++);
447 
448 		tar_hdr->th_linkflag = LF_LONGLINK;
449 		(void) snprintf(tar_hdr->th_size, sizeof (tar_hdr->th_size),
450 		    "%011o ", lnklen);
451 		(void) snprintf(tar_hdr->th_mode, sizeof (tar_hdr->th_mode),
452 		    "%06o ", attr->st_mode & 07777);
453 		(void) snprintf(tar_hdr->th_uid, sizeof (tar_hdr->th_uid),
454 		    "%06o ", uid);
455 		(void) snprintf(tar_hdr->th_gid, sizeof (tar_hdr->th_gid),
456 		    "%06o ", gid);
457 		(void) snprintf(tar_hdr->th_uname, sizeof (tar_hdr->th_uname),
458 		    "%.31s", uname);
459 		(void) snprintf(tar_hdr->th_gname, sizeof (tar_hdr->th_gname),
460 		    "%.31s", gname);
461 		(void) snprintf(tar_hdr->th_mtime, sizeof (tar_hdr->th_mtime),
462 		    "%011o ", attr->st_mtime);
463 		(void) strlcpy(tar_hdr->th_magic, TLM_MAGIC,
464 		    sizeof (tar_hdr->th_magic));
465 
466 		tlm_build_header_checksum(tar_hdr);
467 
468 		(void) output_mem(local_commands, (void *)link,
469 		    lnklen);
470 		long_link = TRUE;
471 	}
472 	tar_hdr = (tlm_tar_hdr_t *)get_write_buffer(RECORDSIZE,
473 	    &actual_size, TRUE, local_commands);
474 	if (!tar_hdr) {
475 		free(section_name);
476 		return (0);
477 	}
478 	if (long_name) {
479 		(void) snprintf(tar_hdr->th_name,
480 		    sizeof (tar_hdr->th_name),
481 		    "%s%08qd.fil",
482 		    LONGNAME_PREFIX,
483 		    file_count++);
484 	} else {
485 		(void) strlcpy(tar_hdr->th_name, section_name, TLM_NAME_SIZE);
486 	}
487 
488 	NDMP_LOG(LOG_DEBUG, "long_link: %s [%s]", long_link ? "TRUE" : "FALSE",
489 	    link);
490 
491 	if (long_link) {
492 		(void) snprintf(tar_hdr->th_linkname,
493 		    sizeof (tar_hdr->th_name),
494 		    "%s%08qd.slk",
495 		    LONGNAME_PREFIX,
496 		    file_count++);
497 	} else {
498 		(void) strlcpy(tar_hdr->th_linkname, link, TLM_NAME_SIZE);
499 	}
500 	if (S_ISDIR(attr->st_mode)) {
501 		tar_hdr->th_linkflag = LF_DIR;
502 	} else if (S_ISFIFO(attr->st_mode)) {
503 		tar_hdr->th_linkflag = LF_FIFO;
504 	} else if (attr->st_nlink > 1) {
505 		/* mark file with hardlink LF_LINK */
506 		tar_hdr->th_linkflag = LF_LINK;
507 		(void) snprintf(tar_hdr->th_shared.th_hlink_ino,
508 		    sizeof (tar_hdr->th_shared.th_hlink_ino),
509 		    "%011llo ", attr->st_ino);
510 	} else {
511 		tar_hdr->th_linkflag = *link == 0 ? LF_NORMAL : LF_SYMLINK;
512 		NDMP_LOG(LOG_DEBUG, "linkflag: '%c'", tar_hdr->th_linkflag);
513 	}
514 	(void) snprintf(tar_hdr->th_size, sizeof (tar_hdr->th_size), "%011o ",
515 	    (long)attr->st_size);
516 	(void) snprintf(tar_hdr->th_mode, sizeof (tar_hdr->th_mode), "%06o ",
517 	    attr->st_mode & 07777);
518 	(void) snprintf(tar_hdr->th_uid, sizeof (tar_hdr->th_uid), "%06o ",
519 	    uid);
520 	(void) snprintf(tar_hdr->th_gid, sizeof (tar_hdr->th_gid), "%06o ",
521 	    gid);
522 	(void) snprintf(tar_hdr->th_uname, sizeof (tar_hdr->th_uname), "%.31s",
523 	    uname);
524 	(void) snprintf(tar_hdr->th_gname, sizeof (tar_hdr->th_gname), "%.31s",
525 	    gname);
526 	(void) snprintf(tar_hdr->th_mtime, sizeof (tar_hdr->th_mtime), "%011o ",
527 	    attr->st_mtime);
528 	(void) strlcpy(tar_hdr->th_magic, TLM_MAGIC,
529 	    sizeof (tar_hdr->th_magic));
530 
531 	tlm_build_header_checksum(tar_hdr);
532 	if (long_name || long_link) {
533 		if (file_count > 99999990) {
534 			file_count = 0;
535 		}
536 	}
537 	free(section_name);
538 	return (0);
539 }
540 
541 
542 /*
543  * tlm_readlink
544  *
545  * Read where the softlink points to.  Read the link in the checkpointed
546  * path if the backup is being done on a checkpointed file system.
547  */
548 static int
549 tlm_readlink(char *nm, char *snap, char *buf, int bufsize)
550 {
551 	int len;
552 
553 	if ((len = readlink(snap, buf, bufsize)) >= 0) {
554 		/*
555 		 * realink(2) doesn't null terminate the link name.  We must
556 		 * do it here.
557 		 */
558 		buf[len] = '\0';
559 	} else {
560 		NDMP_LOG(LOG_DEBUG, "Error %d reading softlink of [%s]",
561 		    errno, nm);
562 		buf[0] = '\0';
563 
564 		/* Backup the link if the destination missing */
565 		if (errno == ENOENT)
566 			return (0);
567 
568 	}
569 
570 	return (len);
571 }
572 
573 /*
574  * Read the system attribute file in a single buffer to write
575  * it as a single write. A partial write to system attribute would
576  * cause an EINVAL on write.
577  */
578 static char *
579 get_write_one_buf(char *buf, char *rec, int buf_size, int rec_size,
580     tlm_cmd_t *lc)
581 {
582 	int len;
583 	long write_size;
584 
585 	if (rec_size > buf_size)
586 		return (rec);
587 
588 	len = rec_size;
589 	(void) memcpy(rec, buf, len);
590 	buf += len;
591 	while (rec_size < buf_size) {
592 		rec = get_write_buffer(buf_size - rec_size,
593 		    &write_size, FALSE, lc);
594 		if (!rec)
595 			return (0);
596 
597 		len = min(buf_size - rec_size, write_size);
598 		(void) memcpy(rec, buf, len);
599 		rec_size += len;
600 		buf += len;
601 	}
602 	return (rec);
603 }
604 
605 
606 /*
607  * tlm_output_xattr
608  *
609  * Put this file into the output buffers.
610  */
611 /*ARGSUSED*/
612 longlong_t
613 tlm_output_xattr(char  *dir, char *name, char *chkdir,
614     tlm_acls_t *tlm_acls, tlm_commands_t *commands,
615     tlm_cmd_t *local_commands, tlm_job_stats_t *job_stats)
616 {
617 	char	*fullname;		/* directory + name */
618 	char	*snapname;		/* snapshot name */
619 	int	section;		/* section of a huge file */
620 	int	fd;
621 	int	afd = 0;
622 	longlong_t seek_spot = 0;	/* location in the file */
623 					/* for Multi Volume record */
624 	u_longlong_t pos;
625 	DIR *dp;
626 	struct dirent *dtp;
627 	char *attrname;
628 	char *fnamep;
629 	int rv = 0;
630 
631 	if (S_ISLNK(tlm_acls->acl_attr.st_mode) ||
632 	    S_ISFIFO(tlm_acls->acl_attr.st_mode)) {
633 		return (TLM_NO_SOURCE_FILE);
634 	}
635 
636 	fullname = ndmp_malloc(TLM_MAX_PATH_NAME);
637 	if (fullname == NULL) {
638 		free(fullname);
639 		return (-TLM_NO_SCRATCH_SPACE);
640 	}
641 
642 	if (!tlm_cat_path(fullname, dir, name)) {
643 		NDMP_LOG(LOG_DEBUG, "Path too long.");
644 		free(fullname);
645 		return (-TLM_NO_SCRATCH_SPACE);
646 	}
647 
648 	if (pathconf(fullname, _PC_XATTR_EXISTS) != 1 &&
649 	    sysattr_support(fullname, _PC_SATTR_EXISTS) != 1) {
650 		free(fullname);
651 		return (0);
652 	}
653 
654 	attrname = ndmp_malloc(TLM_MAX_PATH_NAME);
655 	snapname = ndmp_malloc(TLM_MAX_PATH_NAME);
656 	if (attrname == NULL || snapname == NULL) {
657 		rv = -TLM_NO_SCRATCH_SPACE;
658 		goto err_out;
659 	}
660 
661 	if (!tlm_cat_path(snapname, chkdir, name)) {
662 		NDMP_LOG(LOG_DEBUG, "Path too long.");
663 		rv = -TLM_NO_SCRATCH_SPACE;
664 		goto err_out;
665 	}
666 
667 	fnamep = (tlm_acls->acl_checkpointed) ? snapname : fullname;
668 
669 	/*
670 	 * Open the file for reading.
671 	 */
672 	fd = attropen(fnamep, ".", O_RDONLY);
673 	if (fd == -1) {
674 		NDMP_LOG(LOG_DEBUG, "BACKUP> Can't open file [%s][%s]",
675 		    fullname, fnamep);
676 		rv = TLM_NO_SOURCE_FILE;
677 		goto err_out;
678 	}
679 
680 	pos = tlm_get_data_offset(local_commands);
681 	NDMP_LOG(LOG_DEBUG, "pos: %10lld  [%s]", pos, name);
682 
683 	section = 0;
684 
685 	dp = (DIR *)fdopendir(fd);
686 	if (dp == NULL) {
687 		NDMP_LOG(LOG_DEBUG, "BACKUP> Can't open file [%s]", fullname);
688 		(void) close(fd);
689 		rv = TLM_NO_SOURCE_FILE;
690 		goto err_out;
691 	}
692 
693 	while ((dtp = readdir(dp)) != NULL) {
694 		int section_size;
695 
696 		if (*dtp->d_name == '.')
697 			continue;
698 
699 		if (sysattr_rdonly(dtp->d_name))
700 			continue;
701 
702 		afd = attropen(fnamep, dtp->d_name, O_RDONLY);
703 		if (afd == -1) {
704 			NDMP_LOG(LOG_DEBUG,
705 			    "problem(%d) opening xattr file [%s][%s]", errno,
706 			    fullname, fnamep);
707 			goto tear_down;
708 		}
709 
710 		(void) output_xattr_header(fullname, dtp->d_name, afd,
711 		    tlm_acls, section, local_commands);
712 		(void) snprintf(attrname, TLM_MAX_PATH_NAME, "/dev/null/%s",
713 		    dtp->d_name);
714 		(void) output_file_header(attrname, "", tlm_acls, 0,
715 		    local_commands);
716 
717 		section_size = (long)llmin(tlm_acls->acl_attr.st_size,
718 		    (longlong_t)TLM_MAX_TAR_IMAGE);
719 
720 		/* We only can read upto one section extended attribute */
721 		while (section_size > 0) {
722 			char	*buf;
723 			long	actual_size;
724 			int	read_size;
725 			int sysattr_read = 0;
726 			char *rec;
727 			int size;
728 
729 			/*
730 			 * check for Abort commands
731 			 */
732 			if (commands->tcs_reader != TLM_BACKUP_RUN) {
733 				local_commands->tc_writer = TLM_ABORT;
734 				goto tear_down;
735 			}
736 
737 			local_commands->tc_buffers->tbs_buffer[
738 			    local_commands->tc_buffers->tbs_buffer_in].
739 			    tb_file_size = section_size;
740 			local_commands->tc_buffers->tbs_buffer[
741 			    local_commands->tc_buffers->tbs_buffer_in].
742 			    tb_seek_spot = seek_spot;
743 
744 			buf = get_write_buffer(section_size,
745 			    &actual_size, FALSE, local_commands);
746 			if (!buf)
747 				goto tear_down;
748 
749 			if ((actual_size < section_size) &&
750 			    sysattr_rw(dtp->d_name)) {
751 				rec = buf;
752 				buf = ndmp_malloc(section_size);
753 				if (!buf)
754 					goto tear_down;
755 				size = actual_size;
756 				actual_size = section_size;
757 				sysattr_read = 1;
758 			}
759 
760 			/*
761 			 * check for Abort commands
762 			 */
763 			if (commands->tcs_reader != TLM_BACKUP_RUN) {
764 				local_commands->tc_writer = TLM_ABORT;
765 				goto tear_down;
766 			}
767 
768 			read_size = min(section_size, actual_size);
769 			if ((actual_size = read(afd, buf, read_size)) < 0)
770 				break;
771 
772 			if (sysattr_read) {
773 				if (get_write_one_buf(buf, rec, read_size,
774 				    size, local_commands) == 0) {
775 					free(buf);
776 					goto tear_down;
777 				}
778 				free(buf);
779 			}
780 
781 
782 			NS_ADD(rdisk, actual_size);
783 			NS_INC(rfile);
784 
785 			if (actual_size == -1) {
786 				NDMP_LOG(LOG_DEBUG,
787 				    "problem(%d) reading file [%s][%s]",
788 				    errno, fullname, snapname);
789 				goto tear_down;
790 			}
791 			seek_spot += actual_size;
792 			section_size -= actual_size;
793 		}
794 		(void) close(afd);
795 		afd = -1;
796 	}
797 
798 tear_down:
799 	local_commands->tc_buffers->tbs_buffer[
800 	    local_commands->tc_buffers->tbs_buffer_in].tb_seek_spot = 0;
801 
802 	if (afd > 0)
803 		(void) close(afd);
804 
805 	/* closedir closes fd too */
806 	(void) closedir(dp);
807 
808 err_out:
809 	free(fullname);
810 	free(attrname);
811 	free(snapname);
812 	return (rv);
813 }
814 
815 
816 /*
817  * tlm_output_file
818  *
819  * Put this file into the output buffers.
820  */
821 longlong_t
822 tlm_output_file(char *dir, char *name, char *chkdir,
823     tlm_acls_t *tlm_acls, tlm_commands_t *commands, tlm_cmd_t *local_commands,
824     tlm_job_stats_t *job_stats, struct hardlink_q *hardlink_q)
825 {
826 	char	*fullname;		/* directory + name */
827 	char	*snapname;		/* snapshot name */
828 	char	*linkname;		/* where this file points */
829 	int	section = 0;		/* section of a huge file */
830 	int	fd;
831 	longlong_t real_size;		/* the origional file size */
832 	longlong_t file_size;		/* real size of this file */
833 	longlong_t seek_spot = 0;	/* location in the file */
834 					/* for Multi Volume record */
835 	u_longlong_t pos;
836 	char *fnamep;
837 
838 	/* Indicate whether a file with the same inode has been backed up. */
839 	int hardlink_done = 0;
840 
841 	/*
842 	 * If a file with the same inode has been backed up, hardlink_pos holds
843 	 * the tape offset of the data record.
844 	 */
845 	u_longlong_t hardlink_pos = 0;
846 
847 	if (tlm_is_too_long(tlm_acls->acl_checkpointed, dir, name)) {
848 		NDMP_LOG(LOG_DEBUG, "Path too long [%s][%s]", dir, name);
849 		return (-TLM_NO_SCRATCH_SPACE);
850 	}
851 
852 	fullname = ndmp_malloc(TLM_MAX_PATH_NAME);
853 	linkname = ndmp_malloc(TLM_MAX_PATH_NAME);
854 	snapname = ndmp_malloc(TLM_MAX_PATH_NAME);
855 	if (fullname == NULL || linkname == NULL || snapname == NULL) {
856 		real_size = -TLM_NO_SCRATCH_SPACE;
857 		goto err_out;
858 	}
859 	if (!tlm_cat_path(fullname, dir, name) ||
860 	    !tlm_cat_path(snapname, chkdir, name)) {
861 		NDMP_LOG(LOG_DEBUG, "Path too long.");
862 		real_size = -TLM_NO_SCRATCH_SPACE;
863 		goto err_out;
864 	}
865 
866 	pos = tlm_get_data_offset(local_commands);
867 	NDMP_LOG(LOG_DEBUG, "pos: %10lld  [%s]", pos, name);
868 
869 	if (S_ISLNK(tlm_acls->acl_attr.st_mode) ||
870 	    S_ISFIFO(tlm_acls->acl_attr.st_mode)) {
871 		if (S_ISLNK(tlm_acls->acl_attr.st_mode)) {
872 			file_size = tlm_readlink(fullname, snapname, linkname,
873 			    TLM_MAX_PATH_NAME-1);
874 			if (file_size < 0) {
875 				real_size = -ENOENT;
876 				goto err_out;
877 			}
878 		}
879 
880 		/*
881 		 * Since soft links can not be read(2), we should only
882 		 * backup the file header.
883 		 */
884 		(void) output_file_header(fullname,
885 		    linkname,
886 		    tlm_acls,
887 		    section,
888 		    local_commands);
889 
890 		(void) tlm_log_fhnode(job_stats, dir, name,
891 		    &tlm_acls->acl_attr, pos);
892 		(void) tlm_log_fhpath_name(job_stats, fullname,
893 		    &tlm_acls->acl_attr, pos);
894 
895 		free(fullname);
896 		free(linkname);
897 		free(snapname);
898 		return (0);
899 	}
900 
901 	fnamep = (tlm_acls->acl_checkpointed) ? snapname : fullname;
902 
903 	/*
904 	 * For hardlink, only read the data if no other link
905 	 * belonging to the same inode has been backed up.
906 	 */
907 	if (tlm_acls->acl_attr.st_nlink > 1) {
908 		hardlink_done = !hardlink_q_get(hardlink_q,
909 		    tlm_acls->acl_attr.st_ino, &hardlink_pos, NULL);
910 	}
911 
912 	if (!hardlink_done) {
913 		/*
914 		 * Open the file for reading.
915 		 */
916 		fd = open(fnamep, O_RDONLY);
917 		if (fd == -1) {
918 			NDMP_LOG(LOG_DEBUG,
919 			    "BACKUP> Can't open file [%s][%s] err(%d)",
920 			    fullname, fnamep, errno);
921 			real_size = -TLM_NO_SOURCE_FILE;
922 			goto err_out;
923 		}
924 	} else {
925 		NDMP_LOG(LOG_DEBUG, "found hardlink, inode = %llu, pos = %llu ",
926 		    tlm_acls->acl_attr.st_ino, hardlink_pos);
927 
928 		fd = -1;
929 	}
930 
931 	linkname[0] = 0;
932 
933 	real_size = tlm_acls->acl_attr.st_size;
934 	(void) output_acl_header(&tlm_acls->acl_info,
935 	    local_commands);
936 
937 	/*
938 	 * section = 0: file is small enough for TAR
939 	 * section > 0: file goes out in TLM_MAX_TAR_IMAGE sized chunks
940 	 * 		and the file name gets munged
941 	 */
942 	file_size = real_size;
943 	if (file_size > TLM_MAX_TAR_IMAGE) {
944 		if (output_humongus_header(fullname, file_size,
945 		    local_commands) < 0) {
946 			(void) close(fd);
947 			real_size = -TLM_NO_SCRATCH_SPACE;
948 			goto err_out;
949 		}
950 		section = 1;
951 	} else {
952 		section = 0;
953 	}
954 
955 	/*
956 	 * For hardlink, if other link belonging to the same inode
957 	 * has been backed up, only backup an empty record.
958 	 */
959 	if (hardlink_done)
960 		file_size = 0;
961 
962 	/*
963 	 * work
964 	 */
965 	if (file_size == 0) {
966 		(void) output_file_header(fullname,
967 		    linkname,
968 		    tlm_acls,
969 		    section,
970 		    local_commands);
971 		/*
972 		 * this can fall right through since zero size files
973 		 * will be skipped by the WHILE loop anyway
974 		 */
975 	}
976 
977 	while (file_size > 0) {
978 		int section_size = llmin(file_size,
979 		    (longlong_t)TLM_MAX_TAR_IMAGE);
980 
981 		tlm_acls->acl_attr.st_size = (longlong_t)section_size;
982 		(void) output_file_header(fullname,
983 		    linkname,
984 		    tlm_acls,
985 		    section,
986 		    local_commands);
987 		while (section_size > 0) {
988 			char	*buf;
989 			long	actual_size;
990 			int	read_size;
991 
992 			/*
993 			 * check for Abort commands
994 			 */
995 			if (commands->tcs_reader != TLM_BACKUP_RUN) {
996 				local_commands->tc_writer = TLM_ABORT;
997 				goto tear_down;
998 			}
999 
1000 			local_commands->tc_buffers->tbs_buffer[
1001 			    local_commands->tc_buffers->tbs_buffer_in].
1002 			    tb_file_size = section_size;
1003 			local_commands->tc_buffers->tbs_buffer[
1004 			    local_commands->tc_buffers->tbs_buffer_in].
1005 			    tb_seek_spot = seek_spot;
1006 
1007 			buf = get_write_buffer(section_size,
1008 			    &actual_size, FALSE, local_commands);
1009 			if (!buf)
1010 				goto tear_down;
1011 
1012 			/*
1013 			 * check for Abort commands
1014 			 */
1015 			if (commands->tcs_reader != TLM_BACKUP_RUN) {
1016 				local_commands->tc_writer = TLM_ABORT;
1017 				goto tear_down;
1018 			}
1019 
1020 			read_size = min(section_size, actual_size);
1021 			actual_size = read(fd, buf, read_size);
1022 			NS_ADD(rdisk, actual_size);
1023 			NS_INC(rfile);
1024 
1025 			if (actual_size == 0)
1026 				break;
1027 
1028 			if (actual_size == -1) {
1029 				NDMP_LOG(LOG_DEBUG,
1030 				    "problem(%d) reading file [%s][%s]",
1031 				    errno, fullname, snapname);
1032 				goto tear_down;
1033 			}
1034 			seek_spot += actual_size;
1035 			file_size -= actual_size;
1036 			section_size -= actual_size;
1037 		}
1038 		section++;
1039 	}
1040 
1041 	/*
1042 	 * If data belonging to this hardlink has been backed up, add the link
1043 	 * to hardlink queue.
1044 	 */
1045 	if (tlm_acls->acl_attr.st_nlink > 1 && !hardlink_done) {
1046 		(void) hardlink_q_add(hardlink_q, tlm_acls->acl_attr.st_ino,
1047 		    pos, NULL, 0);
1048 		NDMP_LOG(LOG_DEBUG,
1049 		    "backed up hardlink file %s, inode = %llu, pos = %llu ",
1050 		    fullname, tlm_acls->acl_attr.st_ino, pos);
1051 	}
1052 
1053 	/*
1054 	 * For hardlink, if other link belonging to the same inode has been
1055 	 * backed up, no add_node entry should be sent for this link.
1056 	 */
1057 	if (hardlink_done) {
1058 		NDMP_LOG(LOG_DEBUG,
1059 		    "backed up hardlink link %s, inode = %llu, pos = %llu ",
1060 		    fullname, tlm_acls->acl_attr.st_ino, hardlink_pos);
1061 	} else {
1062 		(void) tlm_log_fhnode(job_stats, dir, name,
1063 		    &tlm_acls->acl_attr, pos);
1064 	}
1065 
1066 	(void) tlm_log_fhpath_name(job_stats, fullname, &tlm_acls->acl_attr,
1067 	    pos);
1068 
1069 tear_down:
1070 	local_commands->tc_buffers->tbs_buffer[
1071 	    local_commands->tc_buffers->tbs_buffer_in].tb_seek_spot = 0;
1072 
1073 	(void) close(fd);
1074 
1075 err_out:
1076 	free(fullname);
1077 	free(linkname);
1078 	free(snapname);
1079 	return (real_size);
1080 }
1081 
1082 /*
1083  * tar_putfile
1084  *
1085  * Main file backup function for tar
1086  */
1087 int
1088 tar_putfile(char *dir, char *name, char *chkdir,
1089     tlm_acls_t *tlm_acls, tlm_commands_t *commands,
1090     tlm_cmd_t *local_commands, tlm_job_stats_t *job_stats,
1091     struct hardlink_q *hardlink_q)
1092 {
1093 	int rv;
1094 
1095 	rv = tlm_output_file(dir, name, chkdir, tlm_acls, commands,
1096 	    local_commands, job_stats, hardlink_q);
1097 	if (rv < 0)
1098 		return (rv);
1099 
1100 	rv = tlm_output_xattr(dir, name, chkdir, tlm_acls, commands,
1101 	    local_commands, job_stats);
1102 
1103 	return (rv < 0 ? rv : 0);
1104 }
1105 
1106 /*
1107  * get_write_buffer
1108  *
1109  * a wrapper to tlm_get_write_buffer so that
1110  * we can cleanly detect ABORT commands
1111  * without involving the TLM library with
1112  * our problems.
1113  */
1114 static char *
1115 get_write_buffer(long size, long *actual_size,
1116     boolean_t zero, tlm_cmd_t *local_commands)
1117 {
1118 	while (local_commands->tc_reader == TLM_BACKUP_RUN) {
1119 		char *rec = tlm_get_write_buffer(size, actual_size,
1120 		    local_commands->tc_buffers, zero);
1121 		if (rec != 0) {
1122 			return (rec);
1123 		}
1124 	}
1125 	return (NULL);
1126 }
1127 
1128 #define	NDMP_MORE_RECORDS	2
1129 
1130 /*
1131  * write_tar_eof
1132  *
1133  * This function is initially written for NDMP support.  It appends
1134  * two tar headers to the tar file, and also N more empty buffers
1135  * to make sure that the two tar headers will be read as a part of
1136  * a mover record and don't get locked because of EOM on the mover
1137  * side.
1138  */
1139 void
1140 write_tar_eof(tlm_cmd_t *local_commands)
1141 {
1142 	int i;
1143 	long actual_size;
1144 	tlm_buffers_t *bufs;
1145 
1146 	/*
1147 	 * output 2 zero filled records,
1148 	 * TAR wants this.
1149 	 */
1150 	(void) get_write_buffer(sizeof (tlm_tar_hdr_t),
1151 	    &actual_size, TRUE, local_commands);
1152 	(void) get_write_buffer(sizeof (tlm_tar_hdr_t),
1153 	    &actual_size, TRUE, local_commands);
1154 
1155 	/*
1156 	 * NDMP: Clear the rest of the buffer and write two more buffers
1157 	 * to the tape.
1158 	 */
1159 	bufs = local_commands->tc_buffers;
1160 	(void) get_write_buffer(bufs->tbs_data_transfer_size,
1161 	    &actual_size, TRUE, local_commands);
1162 
1163 	for (i = 0; i < NDMP_MORE_RECORDS &&
1164 	    local_commands->tc_reader == TLM_BACKUP_RUN; i++) {
1165 		/*
1166 		 * We don't need the return value of get_write_buffer(),
1167 		 * since it's already zeroed out if the buffer is returned.
1168 		 */
1169 		(void) get_write_buffer(bufs->tbs_data_transfer_size,
1170 		    &actual_size, TRUE, local_commands);
1171 	}
1172 
1173 	bufs->tbs_buffer[bufs->tbs_buffer_in].tb_full = TRUE;
1174 	tlm_buffer_release_in_buf(bufs);
1175 }
1176 
1177 /*
1178  * Callback to backup each ZFS property
1179  */
1180 static int
1181 zfs_put_prop_cb(int prop, void *pp)
1182 {
1183 	ndmp_metadata_header_t *mhp;
1184 	ndmp_metadata_property_t *mpp;
1185 	char buf[ZFS_MAXNAMELEN];
1186 	char sbuf[ZFS_MAXNAMELEN];
1187 	zprop_source_t stype;
1188 	char *sourcestr;
1189 
1190 	if (pp == NULL)
1191 		return (ZPROP_INVAL);
1192 
1193 	mhp = (ndmp_metadata_header_t *)pp;
1194 
1195 	if (zfs_prop_get(mhp->nh_handle, prop, buf, sizeof (buf),
1196 	    &stype, sbuf, sizeof (sbuf), B_TRUE) != 0)
1197 		return (ZPROP_CONT);
1198 
1199 	mpp = &mhp->nh_property[mhp->nh_count++];
1200 	(void) strlcpy(mpp->mp_name, zfs_prop_to_name(prop), NAME_MAX);
1201 	(void) strlcpy(mpp->mp_value, buf, NAME_MAX);
1202 
1203 	switch (stype) {
1204 	case ZPROP_SRC_NONE:
1205 		sourcestr = "none";
1206 		break;
1207 	case ZPROP_SRC_LOCAL:
1208 		sourcestr = mhp->nh_dataset;
1209 		break;
1210 	case ZPROP_SRC_TEMPORARY:
1211 		sourcestr = "temporary";
1212 		break;
1213 	case ZPROP_SRC_DEFAULT:
1214 		sourcestr = "default";
1215 		break;
1216 	default:
1217 		sourcestr = sbuf;
1218 		break;
1219 	}
1220 	(void) strlcpy(mpp->mp_source, sourcestr, NAME_MAX);
1221 
1222 	return (ZPROP_CONT);
1223 }
1224 
1225 
1226 /*
1227  * Notifies ndmpd that the metadata associated with the given ZFS dataset
1228  * should be backed up.
1229  */
1230 int
1231 ndmp_include_zfs(ndmp_context_t *nctx, const char *dataset)
1232 {
1233 	tlm_commands_t *cmds;
1234 	ndmp_metadata_header_t *mhp;
1235 	ndmp_metadata_property_t *mpp;
1236 	tlm_cmd_t *lcmd;
1237 	long actual_size;
1238 	nvlist_t *uprops, *ulist;
1239 	const char *pname;
1240 	nvpair_t *elp;
1241 	char *sval, *ssrc;
1242 	char *wbuf, *pp, *tp;
1243 	long size, lsize, sz;
1244 	int align = RECORDSIZE - 1;
1245 
1246 	if (nctx == NULL || (cmds = (tlm_commands_t *)nctx->nc_cmds) == NULL)
1247 		return (-1);
1248 
1249 	if ((lcmd = cmds->tcs_command) == NULL ||
1250 	    lcmd->tc_buffers == NULL)
1251 		return (-1);
1252 
1253 	size = sizeof (ndmp_metadata_header_t) +
1254 	    ZFS_MAX_PROPS * sizeof (ndmp_metadata_property_t);
1255 	size += align;
1256 	size &= ~align;
1257 
1258 	if ((mhp = malloc(size)) == NULL)
1259 		return (-1);
1260 	(void) memset(mhp, 0, size);
1261 
1262 	mhp->nh_plversion = nctx->nc_plversion;
1263 	(void) strlcpy(mhp->nh_plname, nctx->nc_plname,
1264 	    sizeof (mhp->nh_plname));
1265 	(void) strlcpy(mhp->nh_magic, ZFS_META_MAGIC, sizeof (mhp->nh_magic));
1266 	(void) strlcpy(mhp->nh_dataset, dataset, sizeof (mhp->nh_dataset));
1267 
1268 	(void) mutex_lock(&zlib_mtx);
1269 	if ((mhp->nh_handle = zfs_open(zlibh, dataset,
1270 	    ZFS_TYPE_DATASET)) == NULL) {
1271 		(void) mutex_unlock(&zlib_mtx);
1272 		free(mhp);
1273 		return (ZPROP_INVAL);
1274 	}
1275 
1276 	/* Get all the ZFS properties */
1277 	(void) zprop_iter(zfs_put_prop_cb, mhp, TRUE, TRUE,
1278 	    ZFS_TYPE_VOLUME | ZFS_TYPE_DATASET);
1279 
1280 	/* Get user properties */
1281 	uprops = zfs_get_user_props(mhp->nh_handle);
1282 
1283 	elp = nvlist_next_nvpair(uprops, NULL);
1284 
1285 	while (elp != NULL) {
1286 		mpp = &mhp->nh_property[mhp->nh_count];
1287 		if (nvpair_value_nvlist(elp, &ulist) != 0 ||
1288 		    nvlist_lookup_string(ulist, ZPROP_VALUE, &sval) != 0 ||
1289 		    nvlist_lookup_string(ulist, ZPROP_SOURCE, &ssrc) != 0) {
1290 			zfs_close(mhp->nh_handle);
1291 			(void) mutex_unlock(&zlib_mtx);
1292 			free(mhp);
1293 			return (-1);
1294 		}
1295 		if ((pname = nvpair_name(elp)) != NULL)
1296 			(void) strlcpy(mpp->mp_name, pname, NAME_MAX);
1297 
1298 		(void) strlcpy(mpp->mp_value, sval, NAME_MAX);
1299 		(void) strlcpy(mpp->mp_source, ssrc, NAME_MAX);
1300 		mhp->nh_count++;
1301 		elp = nvlist_next_nvpair(uprops, elp);
1302 	}
1303 
1304 	zfs_close(mhp->nh_handle);
1305 	(void) mutex_unlock(&zlib_mtx);
1306 
1307 	if ((wbuf = get_write_buffer(size, &actual_size, TRUE,
1308 	    lcmd)) != NULL) {
1309 		pp = (char *)mhp;
1310 
1311 		(void) memcpy(wbuf, pp, (actual_size < size) ?
1312 		    actual_size : size);
1313 		pp += (actual_size < size) ? actual_size : size;
1314 
1315 		sz = actual_size;
1316 		while (sz < size &&
1317 		    ((tp = get_write_buffer(size - sz, &lsize,
1318 		    TRUE, lcmd))) != NULL) {
1319 			(void) memcpy(tp, pp, size - sz);
1320 			sz += lsize;
1321 			pp += lsize;
1322 		}
1323 		if (sz > size) {
1324 			tlm_unget_write_buffer(lcmd->tc_buffers, sz - size);
1325 		}
1326 	}
1327 
1328 	free(mhp);
1329 	return (0);
1330 }
1331