xref: /netbsd-src/external/cddl/osnet/dist/lib/libzfs/common/libzfs_diff.c (revision 3227e6cf668bd374971740bd6660f43cee4417ac)
1*3227e6cfSchs /*
2*3227e6cfSchs  * CDDL HEADER START
3*3227e6cfSchs  *
4*3227e6cfSchs  * The contents of this file are subject to the terms of the
5*3227e6cfSchs  * Common Development and Distribution License (the "License").
6*3227e6cfSchs  * You may not use this file except in compliance with the License.
7*3227e6cfSchs  *
8*3227e6cfSchs  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*3227e6cfSchs  * or http://www.opensolaris.org/os/licensing.
10*3227e6cfSchs  * See the License for the specific language governing permissions
11*3227e6cfSchs  * and limitations under the License.
12*3227e6cfSchs  *
13*3227e6cfSchs  * When distributing Covered Code, include this CDDL HEADER in each
14*3227e6cfSchs  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*3227e6cfSchs  * If applicable, add the following below this CDDL HEADER, with the
16*3227e6cfSchs  * fields enclosed by brackets "[]" replaced with your own identifying
17*3227e6cfSchs  * information: Portions Copyright [yyyy] [name of copyright owner]
18*3227e6cfSchs  *
19*3227e6cfSchs  * CDDL HEADER END
20*3227e6cfSchs  */
21*3227e6cfSchs 
22*3227e6cfSchs /*
23*3227e6cfSchs  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
24*3227e6cfSchs  * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
25*3227e6cfSchs  * Copyright (c) 2015 by Delphix. All rights reserved.
26*3227e6cfSchs  * Copyright 2016 Joyent, Inc.
27*3227e6cfSchs  * Copyright 2016 Igor Kozhukhov <ikozhukhov@gmail.com>
28*3227e6cfSchs  */
29*3227e6cfSchs 
30*3227e6cfSchs /*
31*3227e6cfSchs  * zfs diff support
32*3227e6cfSchs  */
33*3227e6cfSchs #include <ctype.h>
34*3227e6cfSchs #include <errno.h>
35*3227e6cfSchs #include <libintl.h>
36*3227e6cfSchs #include <string.h>
37*3227e6cfSchs #include <sys/types.h>
38*3227e6cfSchs #include <sys/stat.h>
39*3227e6cfSchs #include <fcntl.h>
40*3227e6cfSchs #include <stddef.h>
41*3227e6cfSchs #include <unistd.h>
42*3227e6cfSchs #include <stdio.h>
43*3227e6cfSchs #include <stdlib.h>
44*3227e6cfSchs #include <pthread.h>
45*3227e6cfSchs #include <sys/zfs_ioctl.h>
46*3227e6cfSchs #include <libzfs.h>
47*3227e6cfSchs #include "libzfs_impl.h"
48*3227e6cfSchs 
49*3227e6cfSchs #define	ZDIFF_SNAPDIR		"/.zfs/snapshot/"
50*3227e6cfSchs #define	ZDIFF_SHARESDIR 	"/.zfs/shares/"
51*3227e6cfSchs #define	ZDIFF_PREFIX		"zfs-diff-%d"
52*3227e6cfSchs 
53*3227e6cfSchs #define	ZDIFF_ADDED	'+'
54*3227e6cfSchs #define	ZDIFF_MODIFIED	'M'
55*3227e6cfSchs #define	ZDIFF_REMOVED	'-'
56*3227e6cfSchs #define	ZDIFF_RENAMED	'R'
57*3227e6cfSchs 
58*3227e6cfSchs static boolean_t
do_name_cmp(const char * fpath,const char * tpath)59*3227e6cfSchs do_name_cmp(const char *fpath, const char *tpath)
60*3227e6cfSchs {
61*3227e6cfSchs 	char *fname, *tname;
62*3227e6cfSchs 	fname = strrchr(fpath, '/') + 1;
63*3227e6cfSchs 	tname = strrchr(tpath, '/') + 1;
64*3227e6cfSchs 	return (strcmp(fname, tname) == 0);
65*3227e6cfSchs }
66*3227e6cfSchs 
67*3227e6cfSchs typedef struct differ_info {
68*3227e6cfSchs 	zfs_handle_t *zhp;
69*3227e6cfSchs 	char *fromsnap;
70*3227e6cfSchs 	char *frommnt;
71*3227e6cfSchs 	char *tosnap;
72*3227e6cfSchs 	char *tomnt;
73*3227e6cfSchs 	char *ds;
74*3227e6cfSchs 	char *dsmnt;
75*3227e6cfSchs 	char *tmpsnap;
76*3227e6cfSchs 	char errbuf[1024];
77*3227e6cfSchs 	boolean_t isclone;
78*3227e6cfSchs 	boolean_t scripted;
79*3227e6cfSchs 	boolean_t classify;
80*3227e6cfSchs 	boolean_t timestamped;
81*3227e6cfSchs 	uint64_t shares;
82*3227e6cfSchs 	int zerr;
83*3227e6cfSchs 	int cleanupfd;
84*3227e6cfSchs 	int outputfd;
85*3227e6cfSchs 	int datafd;
86*3227e6cfSchs } differ_info_t;
87*3227e6cfSchs 
88*3227e6cfSchs /*
89*3227e6cfSchs  * Given a {dsname, object id}, get the object path
90*3227e6cfSchs  */
91*3227e6cfSchs static int
get_stats_for_obj(differ_info_t * di,const char * dsname,uint64_t obj,char * pn,int maxlen,zfs_stat_t * sb)92*3227e6cfSchs get_stats_for_obj(differ_info_t *di, const char *dsname, uint64_t obj,
93*3227e6cfSchs     char *pn, int maxlen, zfs_stat_t *sb)
94*3227e6cfSchs {
95*3227e6cfSchs 	zfs_cmd_t zc = { 0 };
96*3227e6cfSchs 	int error;
97*3227e6cfSchs 
98*3227e6cfSchs 	(void) strlcpy(zc.zc_name, dsname, sizeof (zc.zc_name));
99*3227e6cfSchs 	zc.zc_obj = obj;
100*3227e6cfSchs 
101*3227e6cfSchs 	errno = 0;
102*3227e6cfSchs 	error = ioctl(di->zhp->zfs_hdl->libzfs_fd, ZFS_IOC_OBJ_TO_STATS, &zc);
103*3227e6cfSchs 	di->zerr = errno;
104*3227e6cfSchs 
105*3227e6cfSchs 	/* we can get stats even if we failed to get a path */
106*3227e6cfSchs 	(void) memcpy(sb, &zc.zc_stat, sizeof (zfs_stat_t));
107*3227e6cfSchs 	if (error == 0) {
108*3227e6cfSchs 		ASSERT(di->zerr == 0);
109*3227e6cfSchs 		(void) strlcpy(pn, zc.zc_value, maxlen);
110*3227e6cfSchs 		return (0);
111*3227e6cfSchs 	}
112*3227e6cfSchs 
113*3227e6cfSchs 	if (di->zerr == EPERM) {
114*3227e6cfSchs 		(void) snprintf(di->errbuf, sizeof (di->errbuf),
115*3227e6cfSchs 		    dgettext(TEXT_DOMAIN,
116*3227e6cfSchs 		    "The sys_config privilege or diff delegated permission "
117*3227e6cfSchs 		    "is needed\nto discover path names"));
118*3227e6cfSchs 		return (-1);
119*3227e6cfSchs 	} else {
120*3227e6cfSchs 		(void) snprintf(di->errbuf, sizeof (di->errbuf),
121*3227e6cfSchs 		    dgettext(TEXT_DOMAIN,
122*3227e6cfSchs 		    "Unable to determine path or stats for "
123*3227e6cfSchs 		    "object %lld in %s"), obj, dsname);
124*3227e6cfSchs 		return (-1);
125*3227e6cfSchs 	}
126*3227e6cfSchs }
127*3227e6cfSchs 
128*3227e6cfSchs /*
129*3227e6cfSchs  * stream_bytes
130*3227e6cfSchs  *
131*3227e6cfSchs  * Prints a file name out a character at a time.  If the character is
132*3227e6cfSchs  * not in the range of what we consider "printable" ASCII, display it
133*3227e6cfSchs  * as an escaped 3-digit octal value.  ASCII values less than a space
134*3227e6cfSchs  * are all control characters and we declare the upper end as the
135*3227e6cfSchs  * DELete character.  This also is the last 7-bit ASCII character.
136*3227e6cfSchs  * We choose to treat all 8-bit ASCII as not printable for this
137*3227e6cfSchs  * application.
138*3227e6cfSchs  */
139*3227e6cfSchs static void
stream_bytes(FILE * fp,const char * string)140*3227e6cfSchs stream_bytes(FILE *fp, const char *string)
141*3227e6cfSchs {
142*3227e6cfSchs 	char c;
143*3227e6cfSchs 
144*3227e6cfSchs 	while ((c = *string++) != '\0') {
145*3227e6cfSchs 		if (c > ' ' && c != '\\' && c < '\177') {
146*3227e6cfSchs 			(void) fprintf(fp, "%c", c);
147*3227e6cfSchs 		} else {
148*3227e6cfSchs 			(void) fprintf(fp, "\\%03o", (uint8_t)c);
149*3227e6cfSchs 		}
150*3227e6cfSchs 	}
151*3227e6cfSchs }
152*3227e6cfSchs 
153*3227e6cfSchs static void
print_what(FILE * fp,mode_t what)154*3227e6cfSchs print_what(FILE *fp, mode_t what)
155*3227e6cfSchs {
156*3227e6cfSchs 	char symbol;
157*3227e6cfSchs 
158*3227e6cfSchs 	switch (what & S_IFMT) {
159*3227e6cfSchs 	case S_IFBLK:
160*3227e6cfSchs 		symbol = 'B';
161*3227e6cfSchs 		break;
162*3227e6cfSchs 	case S_IFCHR:
163*3227e6cfSchs 		symbol = 'C';
164*3227e6cfSchs 		break;
165*3227e6cfSchs 	case S_IFDIR:
166*3227e6cfSchs 		symbol = '/';
167*3227e6cfSchs 		break;
168*3227e6cfSchs #ifdef S_IFDOOR
169*3227e6cfSchs 	case S_IFDOOR:
170*3227e6cfSchs 		symbol = '>';
171*3227e6cfSchs 		break;
172*3227e6cfSchs #endif
173*3227e6cfSchs 	case S_IFIFO:
174*3227e6cfSchs 		symbol = '|';
175*3227e6cfSchs 		break;
176*3227e6cfSchs 	case S_IFLNK:
177*3227e6cfSchs 		symbol = '@';
178*3227e6cfSchs 		break;
179*3227e6cfSchs #ifdef S_IFPORT
180*3227e6cfSchs 	case S_IFPORT:
181*3227e6cfSchs 		symbol = 'P';
182*3227e6cfSchs 		break;
183*3227e6cfSchs #endif
184*3227e6cfSchs 	case S_IFSOCK:
185*3227e6cfSchs 		symbol = '=';
186*3227e6cfSchs 		break;
187*3227e6cfSchs 	case S_IFREG:
188*3227e6cfSchs 		symbol = 'F';
189*3227e6cfSchs 		break;
190*3227e6cfSchs 	default:
191*3227e6cfSchs 		symbol = '?';
192*3227e6cfSchs 		break;
193*3227e6cfSchs 	}
194*3227e6cfSchs 	(void) fprintf(fp, "%c", symbol);
195*3227e6cfSchs }
196*3227e6cfSchs 
197*3227e6cfSchs static void
print_cmn(FILE * fp,differ_info_t * di,const char * file)198*3227e6cfSchs print_cmn(FILE *fp, differ_info_t *di, const char *file)
199*3227e6cfSchs {
200*3227e6cfSchs 	stream_bytes(fp, di->dsmnt);
201*3227e6cfSchs 	stream_bytes(fp, file);
202*3227e6cfSchs }
203*3227e6cfSchs 
204*3227e6cfSchs static void
print_rename(FILE * fp,differ_info_t * di,const char * old,const char * new,zfs_stat_t * isb)205*3227e6cfSchs print_rename(FILE *fp, differ_info_t *di, const char *old, const char *new,
206*3227e6cfSchs     zfs_stat_t *isb)
207*3227e6cfSchs {
208*3227e6cfSchs 	if (di->timestamped)
209*3227e6cfSchs 		(void) fprintf(fp, "%10lld.%09lld\t",
210*3227e6cfSchs 		    (longlong_t)isb->zs_ctime[0],
211*3227e6cfSchs 		    (longlong_t)isb->zs_ctime[1]);
212*3227e6cfSchs 	(void) fprintf(fp, "%c\t", ZDIFF_RENAMED);
213*3227e6cfSchs 	if (di->classify) {
214*3227e6cfSchs 		print_what(fp, isb->zs_mode);
215*3227e6cfSchs 		(void) fprintf(fp, "\t");
216*3227e6cfSchs 	}
217*3227e6cfSchs 	print_cmn(fp, di, old);
218*3227e6cfSchs 	if (di->scripted)
219*3227e6cfSchs 		(void) fprintf(fp, "\t");
220*3227e6cfSchs 	else
221*3227e6cfSchs 		(void) fprintf(fp, " -> ");
222*3227e6cfSchs 	print_cmn(fp, di, new);
223*3227e6cfSchs 	(void) fprintf(fp, "\n");
224*3227e6cfSchs }
225*3227e6cfSchs 
226*3227e6cfSchs static void
print_link_change(FILE * fp,differ_info_t * di,int delta,const char * file,zfs_stat_t * isb)227*3227e6cfSchs print_link_change(FILE *fp, differ_info_t *di, int delta, const char *file,
228*3227e6cfSchs     zfs_stat_t *isb)
229*3227e6cfSchs {
230*3227e6cfSchs 	if (di->timestamped)
231*3227e6cfSchs 		(void) fprintf(fp, "%10lld.%09lld\t",
232*3227e6cfSchs 		    (longlong_t)isb->zs_ctime[0],
233*3227e6cfSchs 		    (longlong_t)isb->zs_ctime[1]);
234*3227e6cfSchs 	(void) fprintf(fp, "%c\t", ZDIFF_MODIFIED);
235*3227e6cfSchs 	if (di->classify) {
236*3227e6cfSchs 		print_what(fp, isb->zs_mode);
237*3227e6cfSchs 		(void) fprintf(fp, "\t");
238*3227e6cfSchs 	}
239*3227e6cfSchs 	print_cmn(fp, di, file);
240*3227e6cfSchs 	(void) fprintf(fp, "\t(%+d)", delta);
241*3227e6cfSchs 	(void) fprintf(fp, "\n");
242*3227e6cfSchs }
243*3227e6cfSchs 
244*3227e6cfSchs static void
print_file(FILE * fp,differ_info_t * di,char type,const char * file,zfs_stat_t * isb)245*3227e6cfSchs print_file(FILE *fp, differ_info_t *di, char type, const char *file,
246*3227e6cfSchs     zfs_stat_t *isb)
247*3227e6cfSchs {
248*3227e6cfSchs 	if (di->timestamped)
249*3227e6cfSchs 		(void) fprintf(fp, "%10lld.%09lld\t",
250*3227e6cfSchs 		    (longlong_t)isb->zs_ctime[0],
251*3227e6cfSchs 		    (longlong_t)isb->zs_ctime[1]);
252*3227e6cfSchs 	(void) fprintf(fp, "%c\t", type);
253*3227e6cfSchs 	if (di->classify) {
254*3227e6cfSchs 		print_what(fp, isb->zs_mode);
255*3227e6cfSchs 		(void) fprintf(fp, "\t");
256*3227e6cfSchs 	}
257*3227e6cfSchs 	print_cmn(fp, di, file);
258*3227e6cfSchs 	(void) fprintf(fp, "\n");
259*3227e6cfSchs }
260*3227e6cfSchs 
261*3227e6cfSchs static int
write_inuse_diffs_one(FILE * fp,differ_info_t * di,uint64_t dobj)262*3227e6cfSchs write_inuse_diffs_one(FILE *fp, differ_info_t *di, uint64_t dobj)
263*3227e6cfSchs {
264*3227e6cfSchs 	struct zfs_stat fsb, tsb;
265*3227e6cfSchs 	boolean_t same_name;
266*3227e6cfSchs 	mode_t fmode, tmode;
267*3227e6cfSchs 	char fobjname[MAXPATHLEN], tobjname[MAXPATHLEN];
268*3227e6cfSchs 	int fobjerr, tobjerr;
269*3227e6cfSchs 	int change;
270*3227e6cfSchs 
271*3227e6cfSchs 	if (dobj == di->shares)
272*3227e6cfSchs 		return (0);
273*3227e6cfSchs 
274*3227e6cfSchs 	/*
275*3227e6cfSchs 	 * Check the from and to snapshots for info on the object. If
276*3227e6cfSchs 	 * we get ENOENT, then the object just didn't exist in that
277*3227e6cfSchs 	 * snapshot.  If we get ENOTSUP, then we tried to get
278*3227e6cfSchs 	 * info on a non-ZPL object, which we don't care about anyway.
279*3227e6cfSchs 	 */
280*3227e6cfSchs 	fobjerr = get_stats_for_obj(di, di->fromsnap, dobj, fobjname,
281*3227e6cfSchs 	    MAXPATHLEN, &fsb);
282*3227e6cfSchs 	if (fobjerr && di->zerr != ENOENT && di->zerr != ENOTSUP)
283*3227e6cfSchs 		return (-1);
284*3227e6cfSchs 
285*3227e6cfSchs 	tobjerr = get_stats_for_obj(di, di->tosnap, dobj, tobjname,
286*3227e6cfSchs 	    MAXPATHLEN, &tsb);
287*3227e6cfSchs 	if (tobjerr && di->zerr != ENOENT && di->zerr != ENOTSUP)
288*3227e6cfSchs 		return (-1);
289*3227e6cfSchs 
290*3227e6cfSchs 	/*
291*3227e6cfSchs 	 * Unallocated object sharing the same meta dnode block
292*3227e6cfSchs 	 */
293*3227e6cfSchs 	if (fobjerr && tobjerr) {
294*3227e6cfSchs 		ASSERT(di->zerr == ENOENT || di->zerr == ENOTSUP);
295*3227e6cfSchs 		di->zerr = 0;
296*3227e6cfSchs 		return (0);
297*3227e6cfSchs 	}
298*3227e6cfSchs 
299*3227e6cfSchs 	di->zerr = 0; /* negate get_stats_for_obj() from side that failed */
300*3227e6cfSchs 	fmode = fsb.zs_mode & S_IFMT;
301*3227e6cfSchs 	tmode = tsb.zs_mode & S_IFMT;
302*3227e6cfSchs 	if (fmode == S_IFDIR || tmode == S_IFDIR || fsb.zs_links == 0 ||
303*3227e6cfSchs 	    tsb.zs_links == 0)
304*3227e6cfSchs 		change = 0;
305*3227e6cfSchs 	else
306*3227e6cfSchs 		change = tsb.zs_links - fsb.zs_links;
307*3227e6cfSchs 
308*3227e6cfSchs 	if (fobjerr) {
309*3227e6cfSchs 		if (change) {
310*3227e6cfSchs 			print_link_change(fp, di, change, tobjname, &tsb);
311*3227e6cfSchs 			return (0);
312*3227e6cfSchs 		}
313*3227e6cfSchs 		print_file(fp, di, ZDIFF_ADDED, tobjname, &tsb);
314*3227e6cfSchs 		return (0);
315*3227e6cfSchs 	} else if (tobjerr) {
316*3227e6cfSchs 		if (change) {
317*3227e6cfSchs 			print_link_change(fp, di, change, fobjname, &fsb);
318*3227e6cfSchs 			return (0);
319*3227e6cfSchs 		}
320*3227e6cfSchs 		print_file(fp, di, ZDIFF_REMOVED, fobjname, &fsb);
321*3227e6cfSchs 		return (0);
322*3227e6cfSchs 	}
323*3227e6cfSchs 
324*3227e6cfSchs 	if (fmode != tmode && fsb.zs_gen == tsb.zs_gen)
325*3227e6cfSchs 		tsb.zs_gen++;	/* Force a generational difference */
326*3227e6cfSchs 	same_name = do_name_cmp(fobjname, tobjname);
327*3227e6cfSchs 
328*3227e6cfSchs 	/* Simple modification or no change */
329*3227e6cfSchs 	if (fsb.zs_gen == tsb.zs_gen) {
330*3227e6cfSchs 		/* No apparent changes.  Could we assert !this?  */
331*3227e6cfSchs 		if (fsb.zs_ctime[0] == tsb.zs_ctime[0] &&
332*3227e6cfSchs 		    fsb.zs_ctime[1] == tsb.zs_ctime[1])
333*3227e6cfSchs 			return (0);
334*3227e6cfSchs 		if (change) {
335*3227e6cfSchs 			print_link_change(fp, di, change,
336*3227e6cfSchs 			    change > 0 ? fobjname : tobjname, &tsb);
337*3227e6cfSchs 		} else if (same_name) {
338*3227e6cfSchs 			print_file(fp, di, ZDIFF_MODIFIED, fobjname, &tsb);
339*3227e6cfSchs 		} else {
340*3227e6cfSchs 			print_rename(fp, di, fobjname, tobjname, &tsb);
341*3227e6cfSchs 		}
342*3227e6cfSchs 		return (0);
343*3227e6cfSchs 	} else {
344*3227e6cfSchs 		/* file re-created or object re-used */
345*3227e6cfSchs 		print_file(fp, di, ZDIFF_REMOVED, fobjname, &fsb);
346*3227e6cfSchs 		print_file(fp, di, ZDIFF_ADDED, tobjname, &tsb);
347*3227e6cfSchs 		return (0);
348*3227e6cfSchs 	}
349*3227e6cfSchs }
350*3227e6cfSchs 
351*3227e6cfSchs static int
write_inuse_diffs(FILE * fp,differ_info_t * di,dmu_diff_record_t * dr)352*3227e6cfSchs write_inuse_diffs(FILE *fp, differ_info_t *di, dmu_diff_record_t *dr)
353*3227e6cfSchs {
354*3227e6cfSchs 	uint64_t o;
355*3227e6cfSchs 	int err;
356*3227e6cfSchs 
357*3227e6cfSchs 	for (o = dr->ddr_first; o <= dr->ddr_last; o++) {
358*3227e6cfSchs 		if ((err = write_inuse_diffs_one(fp, di, o)) != 0)
359*3227e6cfSchs 			return (err);
360*3227e6cfSchs 	}
361*3227e6cfSchs 	return (0);
362*3227e6cfSchs }
363*3227e6cfSchs 
364*3227e6cfSchs static int
describe_free(FILE * fp,differ_info_t * di,uint64_t object,char * namebuf,int maxlen)365*3227e6cfSchs describe_free(FILE *fp, differ_info_t *di, uint64_t object, char *namebuf,
366*3227e6cfSchs     int maxlen)
367*3227e6cfSchs {
368*3227e6cfSchs 	struct zfs_stat sb;
369*3227e6cfSchs 
370*3227e6cfSchs 	if (get_stats_for_obj(di, di->fromsnap, object, namebuf,
371*3227e6cfSchs 	    maxlen, &sb) != 0) {
372*3227e6cfSchs 		/* Let it slide, if in the delete queue on from side */
373*3227e6cfSchs 		if (di->zerr == ENOENT && sb.zs_links == 0) {
374*3227e6cfSchs 			di->zerr = 0;
375*3227e6cfSchs 			return (0);
376*3227e6cfSchs 		}
377*3227e6cfSchs 		return (-1);
378*3227e6cfSchs 	}
379*3227e6cfSchs 
380*3227e6cfSchs 	print_file(fp, di, ZDIFF_REMOVED, namebuf, &sb);
381*3227e6cfSchs 	return (0);
382*3227e6cfSchs }
383*3227e6cfSchs 
384*3227e6cfSchs static int
write_free_diffs(FILE * fp,differ_info_t * di,dmu_diff_record_t * dr)385*3227e6cfSchs write_free_diffs(FILE *fp, differ_info_t *di, dmu_diff_record_t *dr)
386*3227e6cfSchs {
387*3227e6cfSchs 	zfs_cmd_t zc = { 0 };
388*3227e6cfSchs 	libzfs_handle_t *lhdl = di->zhp->zfs_hdl;
389*3227e6cfSchs 	char fobjname[MAXPATHLEN];
390*3227e6cfSchs 
391*3227e6cfSchs 	(void) strlcpy(zc.zc_name, di->fromsnap, sizeof (zc.zc_name));
392*3227e6cfSchs 	zc.zc_obj = dr->ddr_first - 1;
393*3227e6cfSchs 
394*3227e6cfSchs 	ASSERT(di->zerr == 0);
395*3227e6cfSchs 
396*3227e6cfSchs 	while (zc.zc_obj < dr->ddr_last) {
397*3227e6cfSchs 		int err;
398*3227e6cfSchs 
399*3227e6cfSchs 		err = ioctl(lhdl->libzfs_fd, ZFS_IOC_NEXT_OBJ, &zc);
400*3227e6cfSchs 		if (err == 0) {
401*3227e6cfSchs 			if (zc.zc_obj == di->shares) {
402*3227e6cfSchs 				zc.zc_obj++;
403*3227e6cfSchs 				continue;
404*3227e6cfSchs 			}
405*3227e6cfSchs 			if (zc.zc_obj > dr->ddr_last) {
406*3227e6cfSchs 				break;
407*3227e6cfSchs 			}
408*3227e6cfSchs 			err = describe_free(fp, di, zc.zc_obj, fobjname,
409*3227e6cfSchs 			    MAXPATHLEN);
410*3227e6cfSchs 			if (err)
411*3227e6cfSchs 				break;
412*3227e6cfSchs 		} else if (errno == ESRCH) {
413*3227e6cfSchs 			break;
414*3227e6cfSchs 		} else {
415*3227e6cfSchs 			(void) snprintf(di->errbuf, sizeof (di->errbuf),
416*3227e6cfSchs 			    dgettext(TEXT_DOMAIN,
417*3227e6cfSchs 			    "next allocated object (> %lld) find failure"),
418*3227e6cfSchs 			    zc.zc_obj);
419*3227e6cfSchs 			di->zerr = errno;
420*3227e6cfSchs 			break;
421*3227e6cfSchs 		}
422*3227e6cfSchs 	}
423*3227e6cfSchs 	if (di->zerr)
424*3227e6cfSchs 		return (-1);
425*3227e6cfSchs 	return (0);
426*3227e6cfSchs }
427*3227e6cfSchs 
428*3227e6cfSchs static void *
differ(void * arg)429*3227e6cfSchs differ(void *arg)
430*3227e6cfSchs {
431*3227e6cfSchs 	differ_info_t *di = arg;
432*3227e6cfSchs 	dmu_diff_record_t dr;
433*3227e6cfSchs 	FILE *ofp;
434*3227e6cfSchs 	int err = 0;
435*3227e6cfSchs 
436*3227e6cfSchs 	if ((ofp = fdopen(di->outputfd, "w")) == NULL) {
437*3227e6cfSchs 		di->zerr = errno;
438*3227e6cfSchs 		(void) strerror_r(errno, di->errbuf, sizeof (di->errbuf));
439*3227e6cfSchs 		(void) close(di->datafd);
440*3227e6cfSchs 		return ((void *)-1);
441*3227e6cfSchs 	}
442*3227e6cfSchs 
443*3227e6cfSchs 	for (;;) {
444*3227e6cfSchs 		char *cp = (char *)&dr;
445*3227e6cfSchs 		int len = sizeof (dr);
446*3227e6cfSchs 		int rv;
447*3227e6cfSchs 
448*3227e6cfSchs 		do {
449*3227e6cfSchs 			rv = read(di->datafd, cp, len);
450*3227e6cfSchs 			cp += rv;
451*3227e6cfSchs 			len -= rv;
452*3227e6cfSchs 		} while (len > 0 && rv > 0);
453*3227e6cfSchs 
454*3227e6cfSchs 		if (rv < 0 || (rv == 0 && len != sizeof (dr))) {
455*3227e6cfSchs 			di->zerr = EPIPE;
456*3227e6cfSchs 			break;
457*3227e6cfSchs 		} else if (rv == 0) {
458*3227e6cfSchs 			/* end of file at a natural breaking point */
459*3227e6cfSchs 			break;
460*3227e6cfSchs 		}
461*3227e6cfSchs 
462*3227e6cfSchs 		switch (dr.ddr_type) {
463*3227e6cfSchs 		case DDR_FREE:
464*3227e6cfSchs 			err = write_free_diffs(ofp, di, &dr);
465*3227e6cfSchs 			break;
466*3227e6cfSchs 		case DDR_INUSE:
467*3227e6cfSchs 			err = write_inuse_diffs(ofp, di, &dr);
468*3227e6cfSchs 			break;
469*3227e6cfSchs 		default:
470*3227e6cfSchs 			di->zerr = EPIPE;
471*3227e6cfSchs 			break;
472*3227e6cfSchs 		}
473*3227e6cfSchs 
474*3227e6cfSchs 		if (err || di->zerr)
475*3227e6cfSchs 			break;
476*3227e6cfSchs 	}
477*3227e6cfSchs 
478*3227e6cfSchs 	(void) fclose(ofp);
479*3227e6cfSchs 	(void) close(di->datafd);
480*3227e6cfSchs 	if (err)
481*3227e6cfSchs 		return ((void *)-1);
482*3227e6cfSchs 	if (di->zerr) {
483*3227e6cfSchs 		ASSERT(di->zerr == EINVAL);
484*3227e6cfSchs 		(void) snprintf(di->errbuf, sizeof (di->errbuf),
485*3227e6cfSchs 		    dgettext(TEXT_DOMAIN,
486*3227e6cfSchs 		    "Internal error: bad data from diff IOCTL"));
487*3227e6cfSchs 		return ((void *)-1);
488*3227e6cfSchs 	}
489*3227e6cfSchs 	return ((void *)0);
490*3227e6cfSchs }
491*3227e6cfSchs 
492*3227e6cfSchs static int
find_shares_object(differ_info_t * di)493*3227e6cfSchs find_shares_object(differ_info_t *di)
494*3227e6cfSchs {
495*3227e6cfSchs 	char fullpath[MAXPATHLEN];
496*3227e6cfSchs 	struct stat64 sb = { 0 };
497*3227e6cfSchs 
498*3227e6cfSchs 	(void) strlcpy(fullpath, di->dsmnt, MAXPATHLEN);
499*3227e6cfSchs 	(void) strlcat(fullpath, ZDIFF_SHARESDIR, MAXPATHLEN);
500*3227e6cfSchs 
501*3227e6cfSchs 	if (stat64(fullpath, &sb) != 0) {
502*3227e6cfSchs #ifdef illumos
503*3227e6cfSchs 		(void) snprintf(di->errbuf, sizeof (di->errbuf),
504*3227e6cfSchs 		    dgettext(TEXT_DOMAIN, "Cannot stat %s"), fullpath);
505*3227e6cfSchs 		return (zfs_error(di->zhp->zfs_hdl, EZFS_DIFF, di->errbuf));
506*3227e6cfSchs #else
507*3227e6cfSchs 		return (0);
508*3227e6cfSchs #endif
509*3227e6cfSchs 	}
510*3227e6cfSchs 
511*3227e6cfSchs 	di->shares = (uint64_t)sb.st_ino;
512*3227e6cfSchs 	return (0);
513*3227e6cfSchs }
514*3227e6cfSchs 
515*3227e6cfSchs static int
make_temp_snapshot(differ_info_t * di)516*3227e6cfSchs make_temp_snapshot(differ_info_t *di)
517*3227e6cfSchs {
518*3227e6cfSchs 	libzfs_handle_t *hdl = di->zhp->zfs_hdl;
519*3227e6cfSchs 	zfs_cmd_t zc = { 0 };
520*3227e6cfSchs 
521*3227e6cfSchs 	(void) snprintf(zc.zc_value, sizeof (zc.zc_value),
522*3227e6cfSchs 	    ZDIFF_PREFIX, getpid());
523*3227e6cfSchs 	(void) strlcpy(zc.zc_name, di->ds, sizeof (zc.zc_name));
524*3227e6cfSchs 	zc.zc_cleanup_fd = di->cleanupfd;
525*3227e6cfSchs 
526*3227e6cfSchs 	if (ioctl(hdl->libzfs_fd, ZFS_IOC_TMP_SNAPSHOT, &zc) != 0) {
527*3227e6cfSchs 		int err = errno;
528*3227e6cfSchs 		if (err == EPERM) {
529*3227e6cfSchs 			(void) snprintf(di->errbuf, sizeof (di->errbuf),
530*3227e6cfSchs 			    dgettext(TEXT_DOMAIN, "The diff delegated "
531*3227e6cfSchs 			    "permission is needed in order\nto create a "
532*3227e6cfSchs 			    "just-in-time snapshot for diffing\n"));
533*3227e6cfSchs 			return (zfs_error(hdl, EZFS_DIFF, di->errbuf));
534*3227e6cfSchs 		} else {
535*3227e6cfSchs 			(void) snprintf(di->errbuf, sizeof (di->errbuf),
536*3227e6cfSchs 			    dgettext(TEXT_DOMAIN, "Cannot create just-in-time "
537*3227e6cfSchs 			    "snapshot of '%s'"), zc.zc_name);
538*3227e6cfSchs 			return (zfs_standard_error(hdl, err, di->errbuf));
539*3227e6cfSchs 		}
540*3227e6cfSchs 	}
541*3227e6cfSchs 
542*3227e6cfSchs 	di->tmpsnap = zfs_strdup(hdl, zc.zc_value);
543*3227e6cfSchs 	di->tosnap = zfs_asprintf(hdl, "%s@%s", di->ds, di->tmpsnap);
544*3227e6cfSchs 	return (0);
545*3227e6cfSchs }
546*3227e6cfSchs 
547*3227e6cfSchs static void
teardown_differ_info(differ_info_t * di)548*3227e6cfSchs teardown_differ_info(differ_info_t *di)
549*3227e6cfSchs {
550*3227e6cfSchs 	free(di->ds);
551*3227e6cfSchs 	free(di->dsmnt);
552*3227e6cfSchs 	free(di->fromsnap);
553*3227e6cfSchs 	free(di->frommnt);
554*3227e6cfSchs 	free(di->tosnap);
555*3227e6cfSchs 	free(di->tmpsnap);
556*3227e6cfSchs 	free(di->tomnt);
557*3227e6cfSchs 	(void) close(di->cleanupfd);
558*3227e6cfSchs }
559*3227e6cfSchs 
560*3227e6cfSchs static int
get_snapshot_names(differ_info_t * di,const char * fromsnap,const char * tosnap)561*3227e6cfSchs get_snapshot_names(differ_info_t *di, const char *fromsnap,
562*3227e6cfSchs     const char *tosnap)
563*3227e6cfSchs {
564*3227e6cfSchs 	libzfs_handle_t *hdl = di->zhp->zfs_hdl;
565*3227e6cfSchs 	char *atptrf = NULL;
566*3227e6cfSchs 	char *atptrt = NULL;
567*3227e6cfSchs 	int fdslen, fsnlen;
568*3227e6cfSchs 	int tdslen, tsnlen;
569*3227e6cfSchs 
570*3227e6cfSchs 	/*
571*3227e6cfSchs 	 * Can accept
572*3227e6cfSchs 	 *    dataset@snap1
573*3227e6cfSchs 	 *    dataset@snap1 dataset@snap2
574*3227e6cfSchs 	 *    dataset@snap1 @snap2
575*3227e6cfSchs 	 *    dataset@snap1 dataset
576*3227e6cfSchs 	 *    @snap1 dataset@snap2
577*3227e6cfSchs 	 */
578*3227e6cfSchs 	if (tosnap == NULL) {
579*3227e6cfSchs 		/* only a from snapshot given, must be valid */
580*3227e6cfSchs 		(void) snprintf(di->errbuf, sizeof (di->errbuf),
581*3227e6cfSchs 		    dgettext(TEXT_DOMAIN,
582*3227e6cfSchs 		    "Badly formed snapshot name %s"), fromsnap);
583*3227e6cfSchs 
584*3227e6cfSchs 		if (!zfs_validate_name(hdl, fromsnap, ZFS_TYPE_SNAPSHOT,
585*3227e6cfSchs 		    B_FALSE)) {
586*3227e6cfSchs 			return (zfs_error(hdl, EZFS_INVALIDNAME,
587*3227e6cfSchs 			    di->errbuf));
588*3227e6cfSchs 		}
589*3227e6cfSchs 
590*3227e6cfSchs 		atptrf = strchr(fromsnap, '@');
591*3227e6cfSchs 		ASSERT(atptrf != NULL);
592*3227e6cfSchs 		fdslen = atptrf - fromsnap;
593*3227e6cfSchs 
594*3227e6cfSchs 		di->fromsnap = zfs_strdup(hdl, fromsnap);
595*3227e6cfSchs 		di->ds = zfs_strdup(hdl, fromsnap);
596*3227e6cfSchs 		di->ds[fdslen] = '\0';
597*3227e6cfSchs 
598*3227e6cfSchs 		/* the to snap will be a just-in-time snap of the head */
599*3227e6cfSchs 		return (make_temp_snapshot(di));
600*3227e6cfSchs 	}
601*3227e6cfSchs 
602*3227e6cfSchs 	(void) snprintf(di->errbuf, sizeof (di->errbuf),
603*3227e6cfSchs 	    dgettext(TEXT_DOMAIN,
604*3227e6cfSchs 	    "Unable to determine which snapshots to compare"));
605*3227e6cfSchs 
606*3227e6cfSchs 	atptrf = strchr(fromsnap, '@');
607*3227e6cfSchs 	atptrt = strchr(tosnap, '@');
608*3227e6cfSchs 	fdslen = atptrf ? atptrf - fromsnap : strlen(fromsnap);
609*3227e6cfSchs 	tdslen = atptrt ? atptrt - tosnap : strlen(tosnap);
610*3227e6cfSchs 	fsnlen = strlen(fromsnap) - fdslen;	/* includes @ sign */
611*3227e6cfSchs 	tsnlen = strlen(tosnap) - tdslen;	/* includes @ sign */
612*3227e6cfSchs 
613*3227e6cfSchs 	if (fsnlen <= 1 || tsnlen == 1 || (fdslen == 0 && tdslen == 0) ||
614*3227e6cfSchs 	    (fsnlen == 0 && tsnlen == 0)) {
615*3227e6cfSchs 		return (zfs_error(hdl, EZFS_INVALIDNAME, di->errbuf));
616*3227e6cfSchs 	} else if ((fdslen > 0 && tdslen > 0) &&
617*3227e6cfSchs 	    ((tdslen != fdslen || strncmp(fromsnap, tosnap, fdslen) != 0))) {
618*3227e6cfSchs 		/*
619*3227e6cfSchs 		 * not the same dataset name, might be okay if
620*3227e6cfSchs 		 * tosnap is a clone of a fromsnap descendant.
621*3227e6cfSchs 		 */
622*3227e6cfSchs 		char origin[ZFS_MAX_DATASET_NAME_LEN];
623*3227e6cfSchs 		zprop_source_t src;
624*3227e6cfSchs 		zfs_handle_t *zhp;
625*3227e6cfSchs 
626*3227e6cfSchs 		di->ds = zfs_alloc(di->zhp->zfs_hdl, tdslen + 1);
627*3227e6cfSchs 		(void) strncpy(di->ds, tosnap, tdslen);
628*3227e6cfSchs 		di->ds[tdslen] = '\0';
629*3227e6cfSchs 
630*3227e6cfSchs 		zhp = zfs_open(hdl, di->ds, ZFS_TYPE_FILESYSTEM);
631*3227e6cfSchs 		while (zhp != NULL) {
632*3227e6cfSchs 			if (zfs_prop_get(zhp, ZFS_PROP_ORIGIN, origin,
633*3227e6cfSchs 			    sizeof (origin), &src, NULL, 0, B_FALSE) != 0) {
634*3227e6cfSchs 				(void) zfs_close(zhp);
635*3227e6cfSchs 				zhp = NULL;
636*3227e6cfSchs 				break;
637*3227e6cfSchs 			}
638*3227e6cfSchs 			if (strncmp(origin, fromsnap, fsnlen) == 0)
639*3227e6cfSchs 				break;
640*3227e6cfSchs 
641*3227e6cfSchs 			(void) zfs_close(zhp);
642*3227e6cfSchs 			zhp = zfs_open(hdl, origin, ZFS_TYPE_FILESYSTEM);
643*3227e6cfSchs 		}
644*3227e6cfSchs 
645*3227e6cfSchs 		if (zhp == NULL) {
646*3227e6cfSchs 			(void) snprintf(di->errbuf, sizeof (di->errbuf),
647*3227e6cfSchs 			    dgettext(TEXT_DOMAIN,
648*3227e6cfSchs 			    "Not an earlier snapshot from the same fs"));
649*3227e6cfSchs 			return (zfs_error(hdl, EZFS_INVALIDNAME, di->errbuf));
650*3227e6cfSchs 		} else {
651*3227e6cfSchs 			(void) zfs_close(zhp);
652*3227e6cfSchs 		}
653*3227e6cfSchs 
654*3227e6cfSchs 		di->isclone = B_TRUE;
655*3227e6cfSchs 		di->fromsnap = zfs_strdup(hdl, fromsnap);
656*3227e6cfSchs 		if (tsnlen) {
657*3227e6cfSchs 			di->tosnap = zfs_strdup(hdl, tosnap);
658*3227e6cfSchs 		} else {
659*3227e6cfSchs 			return (make_temp_snapshot(di));
660*3227e6cfSchs 		}
661*3227e6cfSchs 	} else {
662*3227e6cfSchs 		int dslen = fdslen ? fdslen : tdslen;
663*3227e6cfSchs 
664*3227e6cfSchs 		di->ds = zfs_alloc(hdl, dslen + 1);
665*3227e6cfSchs 		(void) strncpy(di->ds, fdslen ? fromsnap : tosnap, dslen);
666*3227e6cfSchs 		di->ds[dslen] = '\0';
667*3227e6cfSchs 
668*3227e6cfSchs 		di->fromsnap = zfs_asprintf(hdl, "%s%s", di->ds, atptrf);
669*3227e6cfSchs 		if (tsnlen) {
670*3227e6cfSchs 			di->tosnap = zfs_asprintf(hdl, "%s%s", di->ds, atptrt);
671*3227e6cfSchs 		} else {
672*3227e6cfSchs 			return (make_temp_snapshot(di));
673*3227e6cfSchs 		}
674*3227e6cfSchs 	}
675*3227e6cfSchs 	return (0);
676*3227e6cfSchs }
677*3227e6cfSchs 
678*3227e6cfSchs static int
get_mountpoint(differ_info_t * di,char * dsnm,char ** mntpt)679*3227e6cfSchs get_mountpoint(differ_info_t *di, char *dsnm, char **mntpt)
680*3227e6cfSchs {
681*3227e6cfSchs 	boolean_t mounted;
682*3227e6cfSchs 
683*3227e6cfSchs 	mounted = is_mounted(di->zhp->zfs_hdl, dsnm, mntpt);
684*3227e6cfSchs 	if (mounted == B_FALSE) {
685*3227e6cfSchs 		(void) snprintf(di->errbuf, sizeof (di->errbuf),
686*3227e6cfSchs 		    dgettext(TEXT_DOMAIN,
687*3227e6cfSchs 		    "Cannot diff an unmounted snapshot"));
688*3227e6cfSchs 		return (zfs_error(di->zhp->zfs_hdl, EZFS_BADTYPE, di->errbuf));
689*3227e6cfSchs 	}
690*3227e6cfSchs 
691*3227e6cfSchs 	/* Avoid a double slash at the beginning of root-mounted datasets */
692*3227e6cfSchs 	if (**mntpt == '/' && *(*mntpt + 1) == '\0')
693*3227e6cfSchs 		**mntpt = '\0';
694*3227e6cfSchs 	return (0);
695*3227e6cfSchs }
696*3227e6cfSchs 
697*3227e6cfSchs static int
get_mountpoints(differ_info_t * di)698*3227e6cfSchs get_mountpoints(differ_info_t *di)
699*3227e6cfSchs {
700*3227e6cfSchs 	char *strptr;
701*3227e6cfSchs 	char *frommntpt;
702*3227e6cfSchs 
703*3227e6cfSchs 	/*
704*3227e6cfSchs 	 * first get the mountpoint for the parent dataset
705*3227e6cfSchs 	 */
706*3227e6cfSchs 	if (get_mountpoint(di, di->ds, &di->dsmnt) != 0)
707*3227e6cfSchs 		return (-1);
708*3227e6cfSchs 
709*3227e6cfSchs 	strptr = strchr(di->tosnap, '@');
710*3227e6cfSchs 	ASSERT3P(strptr, !=, NULL);
711*3227e6cfSchs 	di->tomnt = zfs_asprintf(di->zhp->zfs_hdl, "%s%s%s", di->dsmnt,
712*3227e6cfSchs 	    ZDIFF_SNAPDIR, ++strptr);
713*3227e6cfSchs 
714*3227e6cfSchs 	strptr = strchr(di->fromsnap, '@');
715*3227e6cfSchs 	ASSERT3P(strptr, !=, NULL);
716*3227e6cfSchs 
717*3227e6cfSchs 	frommntpt = di->dsmnt;
718*3227e6cfSchs 	if (di->isclone) {
719*3227e6cfSchs 		char *mntpt;
720*3227e6cfSchs 		int err;
721*3227e6cfSchs 
722*3227e6cfSchs 		*strptr = '\0';
723*3227e6cfSchs 		err = get_mountpoint(di, di->fromsnap, &mntpt);
724*3227e6cfSchs 		*strptr = '@';
725*3227e6cfSchs 		if (err != 0)
726*3227e6cfSchs 			return (-1);
727*3227e6cfSchs 		frommntpt = mntpt;
728*3227e6cfSchs 	}
729*3227e6cfSchs 
730*3227e6cfSchs 	di->frommnt = zfs_asprintf(di->zhp->zfs_hdl, "%s%s%s", frommntpt,
731*3227e6cfSchs 	    ZDIFF_SNAPDIR, ++strptr);
732*3227e6cfSchs 
733*3227e6cfSchs 	if (di->isclone)
734*3227e6cfSchs 		free(frommntpt);
735*3227e6cfSchs 
736*3227e6cfSchs 	return (0);
737*3227e6cfSchs }
738*3227e6cfSchs 
739*3227e6cfSchs static int
setup_differ_info(zfs_handle_t * zhp,const char * fromsnap,const char * tosnap,differ_info_t * di)740*3227e6cfSchs setup_differ_info(zfs_handle_t *zhp, const char *fromsnap,
741*3227e6cfSchs     const char *tosnap, differ_info_t *di)
742*3227e6cfSchs {
743*3227e6cfSchs 	di->zhp = zhp;
744*3227e6cfSchs 
745*3227e6cfSchs 	di->cleanupfd = open(ZFS_DEV, O_RDWR|O_EXCL);
746*3227e6cfSchs 	VERIFY(di->cleanupfd >= 0);
747*3227e6cfSchs 
748*3227e6cfSchs 	if (get_snapshot_names(di, fromsnap, tosnap) != 0)
749*3227e6cfSchs 		return (-1);
750*3227e6cfSchs 
751*3227e6cfSchs 	if (get_mountpoints(di) != 0)
752*3227e6cfSchs 		return (-1);
753*3227e6cfSchs 
754*3227e6cfSchs 	if (find_shares_object(di) != 0)
755*3227e6cfSchs 		return (-1);
756*3227e6cfSchs 
757*3227e6cfSchs 	return (0);
758*3227e6cfSchs }
759*3227e6cfSchs 
760*3227e6cfSchs int
zfs_show_diffs(zfs_handle_t * zhp,int outfd,const char * fromsnap,const char * tosnap,int flags)761*3227e6cfSchs zfs_show_diffs(zfs_handle_t *zhp, int outfd, const char *fromsnap,
762*3227e6cfSchs     const char *tosnap, int flags)
763*3227e6cfSchs {
764*3227e6cfSchs 	zfs_cmd_t zc = { 0 };
765*3227e6cfSchs 	char errbuf[1024];
766*3227e6cfSchs 	differ_info_t di = { 0 };
767*3227e6cfSchs 	pthread_t tid;
768*3227e6cfSchs 	int pipefd[2];
769*3227e6cfSchs 	int iocerr;
770*3227e6cfSchs 
771*3227e6cfSchs 	(void) snprintf(errbuf, sizeof (errbuf),
772*3227e6cfSchs 	    dgettext(TEXT_DOMAIN, "zfs diff failed"));
773*3227e6cfSchs 
774*3227e6cfSchs 	if (setup_differ_info(zhp, fromsnap, tosnap, &di)) {
775*3227e6cfSchs 		teardown_differ_info(&di);
776*3227e6cfSchs 		return (-1);
777*3227e6cfSchs 	}
778*3227e6cfSchs 
779*3227e6cfSchs 	if (pipe(pipefd)) {
780*3227e6cfSchs 		zfs_error_aux(zhp->zfs_hdl, strerror(errno));
781*3227e6cfSchs 		teardown_differ_info(&di);
782*3227e6cfSchs 		return (zfs_error(zhp->zfs_hdl, EZFS_PIPEFAILED, errbuf));
783*3227e6cfSchs 	}
784*3227e6cfSchs 
785*3227e6cfSchs 	di.scripted = (flags & ZFS_DIFF_PARSEABLE);
786*3227e6cfSchs 	di.classify = (flags & ZFS_DIFF_CLASSIFY);
787*3227e6cfSchs 	di.timestamped = (flags & ZFS_DIFF_TIMESTAMP);
788*3227e6cfSchs 
789*3227e6cfSchs 	di.outputfd = outfd;
790*3227e6cfSchs 	di.datafd = pipefd[0];
791*3227e6cfSchs 
792*3227e6cfSchs 	if (pthread_create(&tid, NULL, differ, &di)) {
793*3227e6cfSchs 		zfs_error_aux(zhp->zfs_hdl, strerror(errno));
794*3227e6cfSchs 		(void) close(pipefd[0]);
795*3227e6cfSchs 		(void) close(pipefd[1]);
796*3227e6cfSchs 		teardown_differ_info(&di);
797*3227e6cfSchs 		return (zfs_error(zhp->zfs_hdl,
798*3227e6cfSchs 		    EZFS_THREADCREATEFAILED, errbuf));
799*3227e6cfSchs 	}
800*3227e6cfSchs 
801*3227e6cfSchs 	/* do the ioctl() */
802*3227e6cfSchs 	(void) strlcpy(zc.zc_value, di.fromsnap, strlen(di.fromsnap) + 1);
803*3227e6cfSchs 	(void) strlcpy(zc.zc_name, di.tosnap, strlen(di.tosnap) + 1);
804*3227e6cfSchs 	zc.zc_cookie = pipefd[1];
805*3227e6cfSchs 
806*3227e6cfSchs 	iocerr = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_DIFF, &zc);
807*3227e6cfSchs 	if (iocerr != 0) {
808*3227e6cfSchs 		(void) snprintf(errbuf, sizeof (errbuf),
809*3227e6cfSchs 		    dgettext(TEXT_DOMAIN, "Unable to obtain diffs"));
810*3227e6cfSchs 		if (errno == EPERM) {
811*3227e6cfSchs 			zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
812*3227e6cfSchs 			    "\n   The sys_mount privilege or diff delegated "
813*3227e6cfSchs 			    "permission is needed\n   to execute the "
814*3227e6cfSchs 			    "diff ioctl"));
815*3227e6cfSchs 		} else if (errno == EXDEV) {
816*3227e6cfSchs 			zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
817*3227e6cfSchs 			    "\n   Not an earlier snapshot from the same fs"));
818*3227e6cfSchs 		} else if (errno != EPIPE || di.zerr == 0) {
819*3227e6cfSchs 			zfs_error_aux(zhp->zfs_hdl, strerror(errno));
820*3227e6cfSchs 		}
821*3227e6cfSchs 		(void) close(pipefd[1]);
822*3227e6cfSchs 		(void) pthread_cancel(tid);
823*3227e6cfSchs 		(void) pthread_join(tid, NULL);
824*3227e6cfSchs 		teardown_differ_info(&di);
825*3227e6cfSchs 		if (di.zerr != 0 && di.zerr != EPIPE) {
826*3227e6cfSchs 			zfs_error_aux(zhp->zfs_hdl, strerror(di.zerr));
827*3227e6cfSchs 			return (zfs_error(zhp->zfs_hdl, EZFS_DIFF, di.errbuf));
828*3227e6cfSchs 		} else {
829*3227e6cfSchs 			return (zfs_error(zhp->zfs_hdl, EZFS_DIFFDATA, errbuf));
830*3227e6cfSchs 		}
831*3227e6cfSchs 	}
832*3227e6cfSchs 
833*3227e6cfSchs 	(void) close(pipefd[1]);
834*3227e6cfSchs 	(void) pthread_join(tid, NULL);
835*3227e6cfSchs 
836*3227e6cfSchs 	if (di.zerr != 0) {
837*3227e6cfSchs 		zfs_error_aux(zhp->zfs_hdl, strerror(di.zerr));
838*3227e6cfSchs 		return (zfs_error(zhp->zfs_hdl, EZFS_DIFF, di.errbuf));
839*3227e6cfSchs 	}
840*3227e6cfSchs 	teardown_differ_info(&di);
841*3227e6cfSchs 	return (0);
842*3227e6cfSchs }
843