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