xref: /titanic_52/usr/src/cmd/ndmpd/tlm/tlm_backup_reader.c (revision 5dfd244acc8f144280c5bc8f69ed941185fc3ccc)
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 		return (TLM_NO_SOURCE_FILE);
632 
633 	fullname = ndmp_malloc(TLM_MAX_PATH_NAME);
634 	if (fullname == NULL) {
635 		free(fullname);
636 		return (-TLM_NO_SCRATCH_SPACE);
637 	}
638 
639 	if (!tlm_cat_path(fullname, dir, name)) {
640 		NDMP_LOG(LOG_DEBUG, "Path too long.");
641 		free(fullname);
642 		return (-TLM_NO_SCRATCH_SPACE);
643 	}
644 
645 	if (pathconf(fullname, _PC_XATTR_EXISTS) != 1 &&
646 	    sysattr_support(fullname, _PC_SATTR_EXISTS) != 1) {
647 		free(fullname);
648 		return (0);
649 	}
650 
651 	attrname = ndmp_malloc(TLM_MAX_PATH_NAME);
652 	snapname = ndmp_malloc(TLM_MAX_PATH_NAME);
653 	if (attrname == NULL || snapname == NULL) {
654 		rv = -TLM_NO_SCRATCH_SPACE;
655 		goto err_out;
656 	}
657 
658 	if (!tlm_cat_path(snapname, chkdir, name)) {
659 		NDMP_LOG(LOG_DEBUG, "Path too long.");
660 		rv = -TLM_NO_SCRATCH_SPACE;
661 		goto err_out;
662 	}
663 
664 	fnamep = (tlm_acls->acl_checkpointed) ? snapname : fullname;
665 
666 	/*
667 	 * Open the file for reading.
668 	 */
669 	fd = attropen(fnamep, ".", O_RDONLY);
670 	if (fd == -1) {
671 		NDMP_LOG(LOG_DEBUG, "BACKUP> Can't open file [%s][%s]",
672 		    fullname, fnamep);
673 		rv = TLM_NO_SOURCE_FILE;
674 		goto err_out;
675 	}
676 
677 	pos = tlm_get_data_offset(local_commands);
678 	NDMP_LOG(LOG_DEBUG, "pos: %10lld  [%s]", pos, name);
679 
680 	section = 0;
681 
682 	dp = (DIR *)fdopendir(fd);
683 	if (dp == NULL) {
684 		NDMP_LOG(LOG_DEBUG, "BACKUP> Can't open file [%s]", fullname);
685 		(void) close(fd);
686 		rv = TLM_NO_SOURCE_FILE;
687 		goto err_out;
688 	}
689 
690 	while ((dtp = readdir(dp)) != NULL) {
691 		int section_size;
692 
693 		if (*dtp->d_name == '.')
694 			continue;
695 
696 		if (sysattr_rdonly(dtp->d_name))
697 			continue;
698 
699 		afd = attropen(fnamep, dtp->d_name, O_RDONLY);
700 		if (afd == -1) {
701 			NDMP_LOG(LOG_DEBUG,
702 			    "problem(%d) opening xattr file [%s][%s]", errno,
703 			    fullname, fnamep);
704 			goto tear_down;
705 		}
706 
707 		(void) output_xattr_header(fullname, dtp->d_name, afd,
708 		    tlm_acls, section, local_commands);
709 		(void) snprintf(attrname, TLM_MAX_PATH_NAME, "/dev/null/%s",
710 		    dtp->d_name);
711 		(void) output_file_header(attrname, "", tlm_acls, 0,
712 		    local_commands);
713 
714 		section_size = (long)llmin(tlm_acls->acl_attr.st_size,
715 		    (longlong_t)TLM_MAX_TAR_IMAGE);
716 
717 		/* We only can read upto one section extended attribute */
718 		while (section_size > 0) {
719 			char	*buf;
720 			long	actual_size;
721 			int	read_size;
722 			int sysattr_read = 0;
723 			char *rec;
724 			int size;
725 
726 			/*
727 			 * check for Abort commands
728 			 */
729 			if (commands->tcs_reader != TLM_BACKUP_RUN) {
730 				local_commands->tc_writer = TLM_ABORT;
731 				goto tear_down;
732 			}
733 
734 			local_commands->tc_buffers->tbs_buffer[
735 			    local_commands->tc_buffers->tbs_buffer_in].
736 			    tb_file_size = section_size;
737 			local_commands->tc_buffers->tbs_buffer[
738 			    local_commands->tc_buffers->tbs_buffer_in].
739 			    tb_seek_spot = seek_spot;
740 
741 			buf = get_write_buffer(section_size,
742 			    &actual_size, FALSE, local_commands);
743 			if (!buf)
744 				goto tear_down;
745 
746 			if ((actual_size < section_size) &&
747 			    sysattr_rw(dtp->d_name)) {
748 				rec = buf;
749 				buf = ndmp_malloc(section_size);
750 				if (!buf)
751 					goto tear_down;
752 				size = actual_size;
753 				actual_size = section_size;
754 				sysattr_read = 1;
755 			}
756 
757 			/*
758 			 * check for Abort commands
759 			 */
760 			if (commands->tcs_reader != TLM_BACKUP_RUN) {
761 				local_commands->tc_writer = TLM_ABORT;
762 				goto tear_down;
763 			}
764 
765 			read_size = min(section_size, actual_size);
766 			if ((actual_size = read(afd, buf, read_size)) < 0)
767 				break;
768 
769 			if (sysattr_read) {
770 				if (get_write_one_buf(buf, rec, read_size,
771 				    size, local_commands) == 0) {
772 					free(buf);
773 					goto tear_down;
774 				}
775 				free(buf);
776 			}
777 
778 
779 			NS_ADD(rdisk, actual_size);
780 			NS_INC(rfile);
781 
782 			if (actual_size == -1) {
783 				NDMP_LOG(LOG_DEBUG,
784 				    "problem(%d) reading file [%s][%s]",
785 				    errno, fullname, snapname);
786 				goto tear_down;
787 			}
788 			seek_spot += actual_size;
789 			section_size -= actual_size;
790 		}
791 		(void) close(afd);
792 		afd = -1;
793 	}
794 
795 tear_down:
796 	local_commands->tc_buffers->tbs_buffer[
797 	    local_commands->tc_buffers->tbs_buffer_in].tb_seek_spot = 0;
798 
799 	if (afd > 0)
800 		(void) close(afd);
801 
802 	/* closedir closes fd too */
803 	(void) closedir(dp);
804 
805 err_out:
806 	free(fullname);
807 	free(attrname);
808 	free(snapname);
809 	return (rv);
810 }
811 
812 
813 /*
814  * tlm_output_file
815  *
816  * Put this file into the output buffers.
817  */
818 longlong_t
819 tlm_output_file(char *dir, char *name, char *chkdir,
820     tlm_acls_t *tlm_acls, tlm_commands_t *commands, tlm_cmd_t *local_commands,
821     tlm_job_stats_t *job_stats, struct hardlink_q *hardlink_q)
822 {
823 	char	*fullname;		/* directory + name */
824 	char	*snapname;		/* snapshot name */
825 	char	*linkname;		/* where this file points */
826 	int	section = 0;		/* section of a huge file */
827 	int	fd;
828 	longlong_t real_size;		/* the origional file size */
829 	longlong_t file_size;		/* real size of this file */
830 	longlong_t seek_spot = 0;	/* location in the file */
831 					/* for Multi Volume record */
832 	u_longlong_t pos;
833 	char *fnamep;
834 
835 	/* Indicate whether a file with the same inode has been backed up. */
836 	int hardlink_done = 0;
837 
838 	/*
839 	 * If a file with the same inode has been backed up, hardlink_pos holds
840 	 * the tape offset of the data record.
841 	 */
842 	u_longlong_t hardlink_pos = 0;
843 
844 	if (tlm_is_too_long(tlm_acls->acl_checkpointed, dir, name)) {
845 		NDMP_LOG(LOG_DEBUG, "Path too long [%s][%s]", dir, name);
846 		return (-TLM_NO_SCRATCH_SPACE);
847 	}
848 
849 	fullname = ndmp_malloc(TLM_MAX_PATH_NAME);
850 	linkname = ndmp_malloc(TLM_MAX_PATH_NAME);
851 	snapname = ndmp_malloc(TLM_MAX_PATH_NAME);
852 	if (fullname == NULL || linkname == NULL || snapname == NULL) {
853 		real_size = -TLM_NO_SCRATCH_SPACE;
854 		goto err_out;
855 	}
856 	if (!tlm_cat_path(fullname, dir, name) ||
857 	    !tlm_cat_path(snapname, chkdir, name)) {
858 		NDMP_LOG(LOG_DEBUG, "Path too long.");
859 		real_size = -TLM_NO_SCRATCH_SPACE;
860 		goto err_out;
861 	}
862 
863 	pos = tlm_get_data_offset(local_commands);
864 	NDMP_LOG(LOG_DEBUG, "pos: %10lld  [%s]", pos, name);
865 
866 	if (S_ISLNK(tlm_acls->acl_attr.st_mode)) {
867 		file_size = tlm_readlink(fullname, snapname, linkname,
868 		    TLM_MAX_PATH_NAME-1);
869 		if (file_size < 0) {
870 			real_size = -ENOENT;
871 			goto err_out;
872 		}
873 
874 		/*
875 		 * Since soft links can not be read(2), we should only
876 		 * backup the file header.
877 		 */
878 		(void) output_file_header(fullname,
879 		    linkname,
880 		    tlm_acls,
881 		    section,
882 		    local_commands);
883 
884 		(void) tlm_log_fhnode(job_stats, dir, name,
885 		    &tlm_acls->acl_attr, pos);
886 		(void) tlm_log_fhpath_name(job_stats, fullname,
887 		    &tlm_acls->acl_attr, pos);
888 
889 		free(fullname);
890 		free(linkname);
891 		free(snapname);
892 		return (0);
893 	}
894 
895 	fnamep = (tlm_acls->acl_checkpointed) ? snapname : fullname;
896 
897 	/*
898 	 * For hardlink, only read the data if no other link
899 	 * belonging to the same inode has been backed up.
900 	 */
901 	if (tlm_acls->acl_attr.st_nlink > 1) {
902 		hardlink_done = !hardlink_q_get(hardlink_q,
903 		    tlm_acls->acl_attr.st_ino, &hardlink_pos, NULL);
904 	}
905 
906 	if (!hardlink_done) {
907 		/*
908 		 * Open the file for reading.
909 		 */
910 		fd = open(fnamep, O_RDONLY);
911 		if (fd == -1) {
912 			NDMP_LOG(LOG_DEBUG,
913 			    "BACKUP> Can't open file [%s][%s] err(%d)",
914 			    fullname, fnamep, errno);
915 			real_size = -TLM_NO_SOURCE_FILE;
916 			goto err_out;
917 		}
918 	} else {
919 		NDMP_LOG(LOG_DEBUG, "found hardlink, inode = %llu, pos = %llu ",
920 		    tlm_acls->acl_attr.st_ino, hardlink_pos);
921 
922 		fd = -1;
923 	}
924 
925 	linkname[0] = 0;
926 
927 	real_size = tlm_acls->acl_attr.st_size;
928 	(void) output_acl_header(&tlm_acls->acl_info,
929 	    local_commands);
930 
931 	/*
932 	 * section = 0: file is small enough for TAR
933 	 * section > 0: file goes out in TLM_MAX_TAR_IMAGE sized chunks
934 	 * 		and the file name gets munged
935 	 */
936 	file_size = real_size;
937 	if (file_size > TLM_MAX_TAR_IMAGE) {
938 		if (output_humongus_header(fullname, file_size,
939 		    local_commands) < 0) {
940 			(void) close(fd);
941 			real_size = -TLM_NO_SCRATCH_SPACE;
942 			goto err_out;
943 		}
944 		section = 1;
945 	} else {
946 		section = 0;
947 	}
948 
949 	/*
950 	 * For hardlink, if other link belonging to the same inode
951 	 * has been backed up, only backup an empty record.
952 	 */
953 	if (hardlink_done)
954 		file_size = 0;
955 
956 	/*
957 	 * work
958 	 */
959 	if (file_size == 0) {
960 		(void) output_file_header(fullname,
961 		    linkname,
962 		    tlm_acls,
963 		    section,
964 		    local_commands);
965 		/*
966 		 * this can fall right through since zero size files
967 		 * will be skipped by the WHILE loop anyway
968 		 */
969 	}
970 
971 	while (file_size > 0) {
972 		int section_size = llmin(file_size,
973 		    (longlong_t)TLM_MAX_TAR_IMAGE);
974 
975 		tlm_acls->acl_attr.st_size = (longlong_t)section_size;
976 		(void) output_file_header(fullname,
977 		    linkname,
978 		    tlm_acls,
979 		    section,
980 		    local_commands);
981 		while (section_size > 0) {
982 			char	*buf;
983 			long	actual_size;
984 			int	read_size;
985 
986 			/*
987 			 * check for Abort commands
988 			 */
989 			if (commands->tcs_reader != TLM_BACKUP_RUN) {
990 				local_commands->tc_writer = TLM_ABORT;
991 				goto tear_down;
992 			}
993 
994 			local_commands->tc_buffers->tbs_buffer[
995 			    local_commands->tc_buffers->tbs_buffer_in].
996 			    tb_file_size = section_size;
997 			local_commands->tc_buffers->tbs_buffer[
998 			    local_commands->tc_buffers->tbs_buffer_in].
999 			    tb_seek_spot = seek_spot;
1000 
1001 			buf = get_write_buffer(section_size,
1002 			    &actual_size, FALSE, local_commands);
1003 			if (!buf)
1004 				goto tear_down;
1005 
1006 			/*
1007 			 * check for Abort commands
1008 			 */
1009 			if (commands->tcs_reader != TLM_BACKUP_RUN) {
1010 				local_commands->tc_writer = TLM_ABORT;
1011 				goto tear_down;
1012 			}
1013 
1014 			read_size = min(section_size, actual_size);
1015 			actual_size = read(fd, buf, read_size);
1016 			NS_ADD(rdisk, actual_size);
1017 			NS_INC(rfile);
1018 
1019 			if (actual_size == 0)
1020 				break;
1021 
1022 			if (actual_size == -1) {
1023 				NDMP_LOG(LOG_DEBUG,
1024 				    "problem(%d) reading file [%s][%s]",
1025 				    errno, fullname, snapname);
1026 				goto tear_down;
1027 			}
1028 			seek_spot += actual_size;
1029 			file_size -= actual_size;
1030 			section_size -= actual_size;
1031 		}
1032 		section++;
1033 	}
1034 
1035 	/*
1036 	 * If data belonging to this hardlink has been backed up, add the link
1037 	 * to hardlink queue.
1038 	 */
1039 	if (tlm_acls->acl_attr.st_nlink > 1 && !hardlink_done) {
1040 		(void) hardlink_q_add(hardlink_q, tlm_acls->acl_attr.st_ino,
1041 		    pos, NULL, 0);
1042 		NDMP_LOG(LOG_DEBUG,
1043 		    "backed up hardlink file %s, inode = %llu, pos = %llu ",
1044 		    fullname, tlm_acls->acl_attr.st_ino, pos);
1045 	}
1046 
1047 	/*
1048 	 * For hardlink, if other link belonging to the same inode has been
1049 	 * backed up, no add_node entry should be sent for this link.
1050 	 */
1051 	if (hardlink_done) {
1052 		NDMP_LOG(LOG_DEBUG,
1053 		    "backed up hardlink link %s, inode = %llu, pos = %llu ",
1054 		    fullname, tlm_acls->acl_attr.st_ino, hardlink_pos);
1055 	} else {
1056 		(void) tlm_log_fhnode(job_stats, dir, name,
1057 		    &tlm_acls->acl_attr, pos);
1058 	}
1059 
1060 	(void) tlm_log_fhpath_name(job_stats, fullname, &tlm_acls->acl_attr,
1061 	    pos);
1062 
1063 tear_down:
1064 	local_commands->tc_buffers->tbs_buffer[
1065 	    local_commands->tc_buffers->tbs_buffer_in].tb_seek_spot = 0;
1066 
1067 	(void) close(fd);
1068 
1069 err_out:
1070 	free(fullname);
1071 	free(linkname);
1072 	free(snapname);
1073 	return (real_size);
1074 }
1075 
1076 /*
1077  * tar_putfile
1078  *
1079  * Main file backup function for tar
1080  */
1081 int
1082 tar_putfile(char *dir, char *name, char *chkdir,
1083     tlm_acls_t *tlm_acls, tlm_commands_t *commands,
1084     tlm_cmd_t *local_commands, tlm_job_stats_t *job_stats,
1085     struct hardlink_q *hardlink_q)
1086 {
1087 	int rv;
1088 
1089 	rv = tlm_output_file(dir, name, chkdir, tlm_acls, commands,
1090 	    local_commands, job_stats, hardlink_q);
1091 	if (rv < 0)
1092 		return (rv);
1093 
1094 	rv = tlm_output_xattr(dir, name, chkdir, tlm_acls, commands,
1095 	    local_commands, job_stats);
1096 
1097 	return (rv < 0 ? rv : 0);
1098 }
1099 
1100 /*
1101  * get_write_buffer
1102  *
1103  * a wrapper to tlm_get_write_buffer so that
1104  * we can cleanly detect ABORT commands
1105  * without involving the TLM library with
1106  * our problems.
1107  */
1108 static char *
1109 get_write_buffer(long size, long *actual_size,
1110     boolean_t zero, tlm_cmd_t *local_commands)
1111 {
1112 	while (local_commands->tc_reader == TLM_BACKUP_RUN) {
1113 		char *rec = tlm_get_write_buffer(size, actual_size,
1114 		    local_commands->tc_buffers, zero);
1115 		if (rec != 0) {
1116 			return (rec);
1117 		}
1118 	}
1119 	return (NULL);
1120 }
1121 
1122 #define	NDMP_MORE_RECORDS	2
1123 
1124 /*
1125  * write_tar_eof
1126  *
1127  * This function is initially written for NDMP support.  It appends
1128  * two tar headers to the tar file, and also N more empty buffers
1129  * to make sure that the two tar headers will be read as a part of
1130  * a mover record and don't get locked because of EOM on the mover
1131  * side.
1132  */
1133 void
1134 write_tar_eof(tlm_cmd_t *local_commands)
1135 {
1136 	int i;
1137 	long actual_size;
1138 	tlm_buffers_t *bufs;
1139 
1140 	/*
1141 	 * output 2 zero filled records,
1142 	 * TAR wants this.
1143 	 */
1144 	(void) get_write_buffer(sizeof (tlm_tar_hdr_t),
1145 	    &actual_size, TRUE, local_commands);
1146 	(void) get_write_buffer(sizeof (tlm_tar_hdr_t),
1147 	    &actual_size, TRUE, local_commands);
1148 
1149 	/*
1150 	 * NDMP: Clear the rest of the buffer and write two more buffers
1151 	 * to the tape.
1152 	 */
1153 	bufs = local_commands->tc_buffers;
1154 	(void) get_write_buffer(bufs->tbs_data_transfer_size,
1155 	    &actual_size, TRUE, local_commands);
1156 
1157 	for (i = 0; i < NDMP_MORE_RECORDS &&
1158 	    local_commands->tc_reader == TLM_BACKUP_RUN; i++) {
1159 		/*
1160 		 * We don't need the return value of get_write_buffer(),
1161 		 * since it's already zeroed out if the buffer is returned.
1162 		 */
1163 		(void) get_write_buffer(bufs->tbs_data_transfer_size,
1164 		    &actual_size, TRUE, local_commands);
1165 	}
1166 
1167 	bufs->tbs_buffer[bufs->tbs_buffer_in].tb_full = TRUE;
1168 	tlm_buffer_release_in_buf(bufs);
1169 }
1170 
1171 /*
1172  * Callback to backup each ZFS property
1173  */
1174 static int
1175 zfs_put_prop_cb(int prop, void *pp)
1176 {
1177 	ndmp_metadata_header_t *mhp;
1178 	ndmp_metadata_property_t *mpp;
1179 	char buf[ZFS_MAXNAMELEN];
1180 	char sbuf[ZFS_MAXNAMELEN];
1181 	zprop_source_t stype;
1182 	char *sourcestr;
1183 
1184 	if (pp == NULL)
1185 		return (ZPROP_INVAL);
1186 
1187 	mhp = (ndmp_metadata_header_t *)pp;
1188 	mpp = &mhp->nh_property[mhp->nh_count++];
1189 
1190 	(void) strlcpy(mpp->mp_name, zfs_prop_to_name(prop), NAME_MAX);
1191 	(void) zfs_prop_get(mhp->nh_handle,
1192 	    prop, buf, sizeof (buf), &stype, sbuf, sizeof (sbuf), B_TRUE);
1193 	(void) strlcpy(mpp->mp_value, buf, NAME_MAX);
1194 
1195 	switch (stype) {
1196 	case ZPROP_SRC_NONE:
1197 		sourcestr = "none";
1198 		break;
1199 	case ZPROP_SRC_LOCAL:
1200 		sourcestr = mhp->nh_dataset;
1201 		break;
1202 	case ZPROP_SRC_TEMPORARY:
1203 		sourcestr = "temporary";
1204 		break;
1205 	case ZPROP_SRC_DEFAULT:
1206 		sourcestr = "default";
1207 		break;
1208 	default:
1209 		sourcestr = sbuf;
1210 		break;
1211 	}
1212 	(void) strlcpy(mpp->mp_source, sourcestr, NAME_MAX);
1213 
1214 	return (ZPROP_CONT);
1215 }
1216 
1217 
1218 /*
1219  * Notifies ndmpd that the metadata associated with the given ZFS dataset
1220  * should be backed up.
1221  */
1222 int
1223 ndmp_include_zfs(ndmp_context_t *nctx, const char *dataset)
1224 {
1225 	tlm_commands_t *cmds;
1226 	ndmp_metadata_header_t *mhp;
1227 	ndmp_metadata_property_t *mpp;
1228 	tlm_cmd_t *lcmd;
1229 	long actual_size;
1230 	nvlist_t *uprops, *ulist;
1231 	const char *pname;
1232 	nvpair_t *elp;
1233 	char *sval, *ssrc;
1234 	char *wbuf, *pp, *tp;
1235 	long size, lsize, sz;
1236 	int align = RECORDSIZE - 1;
1237 
1238 	if (nctx == NULL || (cmds = (tlm_commands_t *)nctx->nc_cmds) == NULL)
1239 		return (-1);
1240 
1241 	if ((lcmd = cmds->tcs_command) == NULL ||
1242 	    lcmd->tc_buffers == NULL)
1243 		return (-1);
1244 
1245 	size = sizeof (ndmp_metadata_header_t) +
1246 	    ZFS_MAX_PROPS * sizeof (ndmp_metadata_property_t);
1247 	size += align;
1248 	size &= ~align;
1249 
1250 	if ((mhp = malloc(size)) == NULL)
1251 		return (-1);
1252 	(void) memset(mhp, 0, size);
1253 
1254 	mhp->nh_plversion = nctx->nc_plversion;
1255 	(void) strlcpy(mhp->nh_plname, nctx->nc_plname,
1256 	    sizeof (mhp->nh_plname));
1257 	(void) strlcpy(mhp->nh_magic, ZFS_META_MAGIC, sizeof (mhp->nh_magic));
1258 	(void) strlcpy(mhp->nh_dataset, dataset, sizeof (mhp->nh_dataset));
1259 
1260 	if ((mhp->nh_handle = zfs_open(zlibh, dataset,
1261 	    ZFS_TYPE_DATASET)) == NULL) {
1262 		free(mhp);
1263 		return (ZPROP_INVAL);
1264 	}
1265 
1266 	/* Get all the ZFS properties */
1267 	(void) zprop_iter(zfs_put_prop_cb, mhp, TRUE, TRUE,
1268 	    ZFS_TYPE_VOLUME | ZFS_TYPE_DATASET);
1269 
1270 	/* Get user properties */
1271 	uprops = zfs_get_user_props(mhp->nh_handle);
1272 
1273 	elp = nvlist_next_nvpair(uprops, NULL);
1274 
1275 	while (elp != NULL) {
1276 		mpp = &mhp->nh_property[mhp->nh_count];
1277 		if (nvpair_value_nvlist(elp, &ulist) != 0 ||
1278 		    nvlist_lookup_string(ulist, ZPROP_VALUE, &sval) != 0 ||
1279 		    nvlist_lookup_string(ulist, ZPROP_SOURCE, &ssrc) != 0) {
1280 			zfs_close(mhp->nh_handle);
1281 			free(mhp);
1282 			return (-1);
1283 		}
1284 		if ((pname = nvpair_name(elp)) != NULL)
1285 			(void) strlcpy(mpp->mp_name, pname, NAME_MAX);
1286 
1287 		(void) strlcpy(mpp->mp_value, sval, NAME_MAX);
1288 		(void) strlcpy(mpp->mp_source, ssrc, NAME_MAX);
1289 		mhp->nh_count++;
1290 		elp = nvlist_next_nvpair(uprops, elp);
1291 	}
1292 
1293 	zfs_close(mhp->nh_handle);
1294 
1295 	if ((wbuf = get_write_buffer(size, &actual_size, TRUE,
1296 	    lcmd)) != NULL) {
1297 		pp = (char *)mhp;
1298 
1299 		(void) memcpy(wbuf, pp, (actual_size < size) ?
1300 		    actual_size : size);
1301 		pp += (actual_size < size) ? actual_size : size;
1302 
1303 		sz = actual_size;
1304 		while (sz < size &&
1305 		    ((tp = get_write_buffer(size - sz, &lsize,
1306 		    TRUE, lcmd))) != NULL) {
1307 			(void) memcpy(tp, pp, size - sz);
1308 			sz += lsize;
1309 			pp += lsize;
1310 		}
1311 		if (sz > size) {
1312 			tlm_unget_write_buffer(lcmd->tc_buffers, sz - size);
1313 		}
1314 	}
1315 
1316 	free(mhp);
1317 	return (0);
1318 }
1319