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