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