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