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