1eda14cbcSMatt Macy /* 2eda14cbcSMatt Macy * CDDL HEADER START 3eda14cbcSMatt Macy * 4eda14cbcSMatt Macy * The contents of this file are subject to the terms of the 5eda14cbcSMatt Macy * Common Development and Distribution License (the "License"). 6eda14cbcSMatt Macy * You may not use this file except in compliance with the License. 7eda14cbcSMatt Macy * 8eda14cbcSMatt Macy * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9271171e0SMartin Matuska * or https://opensource.org/licenses/CDDL-1.0. 10eda14cbcSMatt Macy * See the License for the specific language governing permissions 11eda14cbcSMatt Macy * and limitations under the License. 12eda14cbcSMatt Macy * 13eda14cbcSMatt Macy * When distributing Covered Code, include this CDDL HEADER in each 14eda14cbcSMatt Macy * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15eda14cbcSMatt Macy * If applicable, add the following below this CDDL HEADER, with the 16eda14cbcSMatt Macy * fields enclosed by brackets "[]" replaced with your own identifying 17eda14cbcSMatt Macy * information: Portions Copyright [yyyy] [name of copyright owner] 18eda14cbcSMatt Macy * 19eda14cbcSMatt Macy * CDDL HEADER END 20eda14cbcSMatt Macy */ 21eda14cbcSMatt Macy 22eda14cbcSMatt Macy /* 23eda14cbcSMatt Macy * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. 24eda14cbcSMatt Macy * Copyright 2015 Nexenta Systems, Inc. All rights reserved. 25eda14cbcSMatt Macy * Copyright (c) 2015, 2018 by Delphix. All rights reserved. 26eda14cbcSMatt Macy * Copyright 2016 Joyent, Inc. 27eda14cbcSMatt Macy * Copyright 2016 Igor Kozhukhov <ikozhukhov@gmail.com> 28eda14cbcSMatt Macy */ 29eda14cbcSMatt Macy 30eda14cbcSMatt Macy /* 31eda14cbcSMatt Macy * zfs diff support 32eda14cbcSMatt Macy */ 33eda14cbcSMatt Macy #include <ctype.h> 34eda14cbcSMatt Macy #include <errno.h> 35eda14cbcSMatt Macy #include <libintl.h> 36eda14cbcSMatt Macy #include <string.h> 37eda14cbcSMatt Macy #include <sys/types.h> 38eda14cbcSMatt Macy #include <sys/stat.h> 39eda14cbcSMatt Macy #include <fcntl.h> 40eda14cbcSMatt Macy #include <stddef.h> 41eda14cbcSMatt Macy #include <unistd.h> 42eda14cbcSMatt Macy #include <stdio.h> 43eda14cbcSMatt Macy #include <stdlib.h> 44eda14cbcSMatt Macy #include <pthread.h> 45eda14cbcSMatt Macy #include <sys/zfs_ioctl.h> 46eda14cbcSMatt Macy #include <libzfs.h> 4715f0b8c3SMartin Matuska #include <libzutil.h> 48eda14cbcSMatt Macy #include "libzfs_impl.h" 49eda14cbcSMatt Macy 50eda14cbcSMatt Macy #define ZDIFF_SNAPDIR "/.zfs/snapshot/" 51eda14cbcSMatt Macy #define ZDIFF_PREFIX "zfs-diff-%d" 52eda14cbcSMatt Macy 53eda14cbcSMatt Macy #define ZDIFF_ADDED '+' 54681ce946SMartin Matuska #define ZDIFF_MODIFIED "M" 55eda14cbcSMatt Macy #define ZDIFF_REMOVED '-' 56681ce946SMartin Matuska #define ZDIFF_RENAMED "R" 57eda14cbcSMatt Macy 5815f0b8c3SMartin Matuska #define ZDIFF_ADDED_COLOR ANSI_GREEN 5915f0b8c3SMartin Matuska #define ZDIFF_MODIFIED_COLOR ANSI_YELLOW 6015f0b8c3SMartin Matuska #define ZDIFF_REMOVED_COLOR ANSI_RED 612a58b312SMartin Matuska #define ZDIFF_RENAMED_COLOR ANSI_BOLD_BLUE 62eda14cbcSMatt Macy 63eda14cbcSMatt Macy /* 64eda14cbcSMatt Macy * Given a {dsname, object id}, get the object path 65eda14cbcSMatt Macy */ 66eda14cbcSMatt Macy static int 67eda14cbcSMatt Macy get_stats_for_obj(differ_info_t *di, const char *dsname, uint64_t obj, 68eda14cbcSMatt Macy char *pn, int maxlen, zfs_stat_t *sb) 69eda14cbcSMatt Macy { 70eda14cbcSMatt Macy zfs_cmd_t zc = {"\0"}; 71eda14cbcSMatt Macy int error; 72eda14cbcSMatt Macy 73eda14cbcSMatt Macy (void) strlcpy(zc.zc_name, dsname, sizeof (zc.zc_name)); 74eda14cbcSMatt Macy zc.zc_obj = obj; 75eda14cbcSMatt Macy 76eda14cbcSMatt Macy errno = 0; 77eda14cbcSMatt Macy error = zfs_ioctl(di->zhp->zfs_hdl, ZFS_IOC_OBJ_TO_STATS, &zc); 78eda14cbcSMatt Macy di->zerr = errno; 79eda14cbcSMatt Macy 80eda14cbcSMatt Macy /* we can get stats even if we failed to get a path */ 81eda14cbcSMatt Macy (void) memcpy(sb, &zc.zc_stat, sizeof (zfs_stat_t)); 82eda14cbcSMatt Macy if (error == 0) { 83eda14cbcSMatt Macy ASSERT(di->zerr == 0); 84eda14cbcSMatt Macy (void) strlcpy(pn, zc.zc_value, maxlen); 85eda14cbcSMatt Macy return (0); 86eda14cbcSMatt Macy } 87eda14cbcSMatt Macy 88eda14cbcSMatt Macy if (di->zerr == ESTALE) { 89eda14cbcSMatt Macy (void) snprintf(pn, maxlen, "(on_delete_queue)"); 90eda14cbcSMatt Macy return (0); 91eda14cbcSMatt Macy } else if (di->zerr == EPERM) { 92eda14cbcSMatt Macy (void) snprintf(di->errbuf, sizeof (di->errbuf), 93eda14cbcSMatt Macy dgettext(TEXT_DOMAIN, 94eda14cbcSMatt Macy "The sys_config privilege or diff delegated permission " 95eda14cbcSMatt Macy "is needed\nto discover path names")); 96eda14cbcSMatt Macy return (-1); 97eda14cbcSMatt Macy } else if (di->zerr == EACCES) { 98eda14cbcSMatt Macy (void) snprintf(di->errbuf, sizeof (di->errbuf), 99eda14cbcSMatt Macy dgettext(TEXT_DOMAIN, 100eda14cbcSMatt Macy "Key must be loaded to discover path names")); 101eda14cbcSMatt Macy return (-1); 102eda14cbcSMatt Macy } else { 103eda14cbcSMatt Macy (void) snprintf(di->errbuf, sizeof (di->errbuf), 104eda14cbcSMatt Macy dgettext(TEXT_DOMAIN, 105eda14cbcSMatt Macy "Unable to determine path or stats for " 106eda14cbcSMatt Macy "object %lld in %s"), (longlong_t)obj, dsname); 107eda14cbcSMatt Macy return (-1); 108eda14cbcSMatt Macy } 109eda14cbcSMatt Macy } 110eda14cbcSMatt Macy 111eda14cbcSMatt Macy /* 112eda14cbcSMatt Macy * stream_bytes 113eda14cbcSMatt Macy * 114eda14cbcSMatt Macy * Prints a file name out a character at a time. If the character is 115eda14cbcSMatt Macy * not in the range of what we consider "printable" ASCII, display it 116eda14cbcSMatt Macy * as an escaped 4-digit octal value. ASCII values less than a space 117eda14cbcSMatt Macy * are all control characters and we declare the upper end as the 118eda14cbcSMatt Macy * DELete character. This also is the last 7-bit ASCII character. 119eda14cbcSMatt Macy * We choose to treat all 8-bit ASCII as not printable for this 120eda14cbcSMatt Macy * application. 121eda14cbcSMatt Macy */ 122eda14cbcSMatt Macy static void 123eda14cbcSMatt Macy stream_bytes(FILE *fp, const char *string) 124eda14cbcSMatt Macy { 125eda14cbcSMatt Macy char c; 126eda14cbcSMatt Macy 127eda14cbcSMatt Macy while ((c = *string++) != '\0') { 128eda14cbcSMatt Macy if (c > ' ' && c != '\\' && c < '\177') { 129681ce946SMartin Matuska (void) fputc(c, fp); 130eda14cbcSMatt Macy } else { 131681ce946SMartin Matuska (void) fprintf(fp, "\\%04hho", (uint8_t)c); 132eda14cbcSMatt Macy } 133eda14cbcSMatt Macy } 134eda14cbcSMatt Macy } 135eda14cbcSMatt Macy 13615f0b8c3SMartin Matuska /* 13715f0b8c3SMartin Matuska * Takes the type of change (like `print_file`), outputs the appropriate color 13815f0b8c3SMartin Matuska */ 13915f0b8c3SMartin Matuska static const char * 14015f0b8c3SMartin Matuska type_to_color(char type) 14115f0b8c3SMartin Matuska { 14215f0b8c3SMartin Matuska if (type == '+') 14315f0b8c3SMartin Matuska return (ZDIFF_ADDED_COLOR); 14415f0b8c3SMartin Matuska else if (type == '-') 14515f0b8c3SMartin Matuska return (ZDIFF_REMOVED_COLOR); 14615f0b8c3SMartin Matuska else if (type == 'M') 14715f0b8c3SMartin Matuska return (ZDIFF_MODIFIED_COLOR); 14815f0b8c3SMartin Matuska else if (type == 'R') 14915f0b8c3SMartin Matuska return (ZDIFF_RENAMED_COLOR); 15015f0b8c3SMartin Matuska else 15115f0b8c3SMartin Matuska return (NULL); 15215f0b8c3SMartin Matuska } 15315f0b8c3SMartin Matuska 15415f0b8c3SMartin Matuska 155681ce946SMartin Matuska static char 156681ce946SMartin Matuska get_what(mode_t what) 157eda14cbcSMatt Macy { 158eda14cbcSMatt Macy switch (what & S_IFMT) { 159eda14cbcSMatt Macy case S_IFBLK: 160681ce946SMartin Matuska return ('B'); 161eda14cbcSMatt Macy case S_IFCHR: 162681ce946SMartin Matuska return ('C'); 163eda14cbcSMatt Macy case S_IFDIR: 164681ce946SMartin Matuska return ('/'); 165eda14cbcSMatt Macy #ifdef S_IFDOOR 166eda14cbcSMatt Macy case S_IFDOOR: 167681ce946SMartin Matuska return ('>'); 168eda14cbcSMatt Macy #endif 169eda14cbcSMatt Macy case S_IFIFO: 170681ce946SMartin Matuska return ('|'); 171eda14cbcSMatt Macy case S_IFLNK: 172681ce946SMartin Matuska return ('@'); 173eda14cbcSMatt Macy #ifdef S_IFPORT 174eda14cbcSMatt Macy case S_IFPORT: 175681ce946SMartin Matuska return ('P'); 176eda14cbcSMatt Macy #endif 177eda14cbcSMatt Macy case S_IFSOCK: 178681ce946SMartin Matuska return ('='); 179eda14cbcSMatt Macy case S_IFREG: 180681ce946SMartin Matuska return ('F'); 181eda14cbcSMatt Macy default: 182681ce946SMartin Matuska return ('?'); 183eda14cbcSMatt Macy } 184eda14cbcSMatt Macy } 185eda14cbcSMatt Macy 186eda14cbcSMatt Macy static void 187eda14cbcSMatt Macy print_cmn(FILE *fp, differ_info_t *di, const char *file) 188eda14cbcSMatt Macy { 189681ce946SMartin Matuska if (!di->no_mangle) { 190eda14cbcSMatt Macy stream_bytes(fp, di->dsmnt); 191eda14cbcSMatt Macy stream_bytes(fp, file); 192681ce946SMartin Matuska } else { 193681ce946SMartin Matuska (void) fputs(di->dsmnt, fp); 194681ce946SMartin Matuska (void) fputs(file, fp); 195681ce946SMartin Matuska } 196eda14cbcSMatt Macy } 197eda14cbcSMatt Macy 198eda14cbcSMatt Macy static void 199eda14cbcSMatt Macy print_rename(FILE *fp, differ_info_t *di, const char *old, const char *new, 200eda14cbcSMatt Macy zfs_stat_t *isb) 201eda14cbcSMatt Macy { 20215f0b8c3SMartin Matuska if (isatty(fileno(fp))) 20315f0b8c3SMartin Matuska color_start(ZDIFF_RENAMED_COLOR); 204eda14cbcSMatt Macy if (di->timestamped) 205eda14cbcSMatt Macy (void) fprintf(fp, "%10lld.%09lld\t", 206eda14cbcSMatt Macy (longlong_t)isb->zs_ctime[0], 207eda14cbcSMatt Macy (longlong_t)isb->zs_ctime[1]); 208681ce946SMartin Matuska (void) fputs(ZDIFF_RENAMED "\t", fp); 209681ce946SMartin Matuska if (di->classify) 210681ce946SMartin Matuska (void) fprintf(fp, "%c\t", get_what(isb->zs_mode)); 211eda14cbcSMatt Macy print_cmn(fp, di, old); 212681ce946SMartin Matuska (void) fputs(di->scripted ? "\t" : " -> ", fp); 213eda14cbcSMatt Macy print_cmn(fp, di, new); 214681ce946SMartin Matuska (void) fputc('\n', fp); 21515f0b8c3SMartin Matuska 21615f0b8c3SMartin Matuska if (isatty(fileno(fp))) 21715f0b8c3SMartin Matuska color_end(); 218eda14cbcSMatt Macy } 219eda14cbcSMatt Macy 220eda14cbcSMatt Macy static void 221eda14cbcSMatt Macy print_link_change(FILE *fp, differ_info_t *di, int delta, const char *file, 222eda14cbcSMatt Macy zfs_stat_t *isb) 223eda14cbcSMatt Macy { 22415f0b8c3SMartin Matuska if (isatty(fileno(fp))) 22515f0b8c3SMartin Matuska color_start(ZDIFF_MODIFIED_COLOR); 22615f0b8c3SMartin Matuska 227eda14cbcSMatt Macy if (di->timestamped) 228eda14cbcSMatt Macy (void) fprintf(fp, "%10lld.%09lld\t", 229eda14cbcSMatt Macy (longlong_t)isb->zs_ctime[0], 230eda14cbcSMatt Macy (longlong_t)isb->zs_ctime[1]); 231681ce946SMartin Matuska (void) fputs(ZDIFF_MODIFIED "\t", fp); 232681ce946SMartin Matuska if (di->classify) 233681ce946SMartin Matuska (void) fprintf(fp, "%c\t", get_what(isb->zs_mode)); 234eda14cbcSMatt Macy print_cmn(fp, di, file); 235681ce946SMartin Matuska (void) fprintf(fp, "\t(%+d)\n", delta); 23615f0b8c3SMartin Matuska if (isatty(fileno(fp))) 23715f0b8c3SMartin Matuska color_end(); 238eda14cbcSMatt Macy } 239eda14cbcSMatt Macy 240eda14cbcSMatt Macy static void 241eda14cbcSMatt Macy print_file(FILE *fp, differ_info_t *di, char type, const char *file, 242eda14cbcSMatt Macy zfs_stat_t *isb) 243eda14cbcSMatt Macy { 24415f0b8c3SMartin Matuska if (isatty(fileno(fp))) 24515f0b8c3SMartin Matuska color_start(type_to_color(type)); 24615f0b8c3SMartin Matuska 247eda14cbcSMatt Macy if (di->timestamped) 248eda14cbcSMatt Macy (void) fprintf(fp, "%10lld.%09lld\t", 249eda14cbcSMatt Macy (longlong_t)isb->zs_ctime[0], 250eda14cbcSMatt Macy (longlong_t)isb->zs_ctime[1]); 251eda14cbcSMatt Macy (void) fprintf(fp, "%c\t", type); 252681ce946SMartin Matuska if (di->classify) 253681ce946SMartin Matuska (void) fprintf(fp, "%c\t", get_what(isb->zs_mode)); 254eda14cbcSMatt Macy print_cmn(fp, di, file); 255681ce946SMartin Matuska (void) fputc('\n', fp); 25615f0b8c3SMartin Matuska 25715f0b8c3SMartin Matuska if (isatty(fileno(fp))) 25815f0b8c3SMartin Matuska color_end(); 259eda14cbcSMatt Macy } 260eda14cbcSMatt Macy 261eda14cbcSMatt Macy static int 262eda14cbcSMatt Macy write_inuse_diffs_one(FILE *fp, differ_info_t *di, uint64_t dobj) 263eda14cbcSMatt Macy { 264eda14cbcSMatt Macy struct zfs_stat fsb, tsb; 265eda14cbcSMatt Macy mode_t fmode, tmode; 266eda14cbcSMatt Macy char fobjname[MAXPATHLEN], tobjname[MAXPATHLEN]; 26716038816SMartin Matuska boolean_t already_logged = B_FALSE; 268eda14cbcSMatt Macy int fobjerr, tobjerr; 269eda14cbcSMatt Macy int change; 270eda14cbcSMatt Macy 271eda14cbcSMatt Macy if (dobj == di->shares) 272eda14cbcSMatt Macy return (0); 273eda14cbcSMatt Macy 274eda14cbcSMatt Macy /* 275eda14cbcSMatt Macy * Check the from and to snapshots for info on the object. If 276eda14cbcSMatt Macy * we get ENOENT, then the object just didn't exist in that 277eda14cbcSMatt Macy * snapshot. If we get ENOTSUP, then we tried to get 278eda14cbcSMatt Macy * info on a non-ZPL object, which we don't care about anyway. 27916038816SMartin Matuska * For any other error we print a warning which includes the 28016038816SMartin Matuska * errno and continue. 281eda14cbcSMatt Macy */ 28216038816SMartin Matuska 283eda14cbcSMatt Macy fobjerr = get_stats_for_obj(di, di->fromsnap, dobj, fobjname, 284eda14cbcSMatt Macy MAXPATHLEN, &fsb); 28516038816SMartin Matuska if (fobjerr && di->zerr != ENOTSUP && di->zerr != ENOENT) { 286*fd45b686SMartin Matuska zfs_error_aux(di->zhp->zfs_hdl, "%s", zfs_strerror(di->zerr)); 28716038816SMartin Matuska zfs_error(di->zhp->zfs_hdl, di->zerr, di->errbuf); 28816038816SMartin Matuska /* 28916038816SMartin Matuska * Let's not print an error for the same object more than 29016038816SMartin Matuska * once if it happens in both snapshots 29116038816SMartin Matuska */ 29216038816SMartin Matuska already_logged = B_TRUE; 29316038816SMartin Matuska } 294eda14cbcSMatt Macy 295eda14cbcSMatt Macy tobjerr = get_stats_for_obj(di, di->tosnap, dobj, tobjname, 296eda14cbcSMatt Macy MAXPATHLEN, &tsb); 297eda14cbcSMatt Macy 29816038816SMartin Matuska if (tobjerr && di->zerr != ENOTSUP && di->zerr != ENOENT) { 29916038816SMartin Matuska if (!already_logged) { 30016038816SMartin Matuska zfs_error_aux(di->zhp->zfs_hdl, 301*fd45b686SMartin Matuska "%s", zfs_strerror(di->zerr)); 30216038816SMartin Matuska zfs_error(di->zhp->zfs_hdl, di->zerr, di->errbuf); 30316038816SMartin Matuska } 30416038816SMartin Matuska } 305eda14cbcSMatt Macy /* 306eda14cbcSMatt Macy * Unallocated object sharing the same meta dnode block 307eda14cbcSMatt Macy */ 308eda14cbcSMatt Macy if (fobjerr && tobjerr) { 309eda14cbcSMatt Macy di->zerr = 0; 310eda14cbcSMatt Macy return (0); 311eda14cbcSMatt Macy } 312eda14cbcSMatt Macy 313eda14cbcSMatt Macy di->zerr = 0; /* negate get_stats_for_obj() from side that failed */ 314eda14cbcSMatt Macy fmode = fsb.zs_mode & S_IFMT; 315eda14cbcSMatt Macy tmode = tsb.zs_mode & S_IFMT; 316eda14cbcSMatt Macy if (fmode == S_IFDIR || tmode == S_IFDIR || fsb.zs_links == 0 || 317eda14cbcSMatt Macy tsb.zs_links == 0) 318eda14cbcSMatt Macy change = 0; 319eda14cbcSMatt Macy else 320eda14cbcSMatt Macy change = tsb.zs_links - fsb.zs_links; 321eda14cbcSMatt Macy 322eda14cbcSMatt Macy if (fobjerr) { 323eda14cbcSMatt Macy if (change) { 324eda14cbcSMatt Macy print_link_change(fp, di, change, tobjname, &tsb); 325eda14cbcSMatt Macy return (0); 326eda14cbcSMatt Macy } 327eda14cbcSMatt Macy print_file(fp, di, ZDIFF_ADDED, tobjname, &tsb); 328eda14cbcSMatt Macy return (0); 329eda14cbcSMatt Macy } else if (tobjerr) { 330eda14cbcSMatt Macy if (change) { 331eda14cbcSMatt Macy print_link_change(fp, di, change, fobjname, &fsb); 332eda14cbcSMatt Macy return (0); 333eda14cbcSMatt Macy } 334eda14cbcSMatt Macy print_file(fp, di, ZDIFF_REMOVED, fobjname, &fsb); 335eda14cbcSMatt Macy return (0); 336eda14cbcSMatt Macy } 337eda14cbcSMatt Macy 338eda14cbcSMatt Macy if (fmode != tmode && fsb.zs_gen == tsb.zs_gen) 339eda14cbcSMatt Macy tsb.zs_gen++; /* Force a generational difference */ 340eda14cbcSMatt Macy 341eda14cbcSMatt Macy /* Simple modification or no change */ 342eda14cbcSMatt Macy if (fsb.zs_gen == tsb.zs_gen) { 343eda14cbcSMatt Macy /* No apparent changes. Could we assert !this? */ 344eda14cbcSMatt Macy if (fsb.zs_ctime[0] == tsb.zs_ctime[0] && 345eda14cbcSMatt Macy fsb.zs_ctime[1] == tsb.zs_ctime[1]) 346eda14cbcSMatt Macy return (0); 347eda14cbcSMatt Macy if (change) { 348eda14cbcSMatt Macy print_link_change(fp, di, change, 349eda14cbcSMatt Macy change > 0 ? fobjname : tobjname, &tsb); 350eda14cbcSMatt Macy } else if (strcmp(fobjname, tobjname) == 0) { 351681ce946SMartin Matuska print_file(fp, di, *ZDIFF_MODIFIED, fobjname, &tsb); 352eda14cbcSMatt Macy } else { 353eda14cbcSMatt Macy print_rename(fp, di, fobjname, tobjname, &tsb); 354eda14cbcSMatt Macy } 355eda14cbcSMatt Macy return (0); 356eda14cbcSMatt Macy } else { 357eda14cbcSMatt Macy /* file re-created or object re-used */ 358eda14cbcSMatt Macy print_file(fp, di, ZDIFF_REMOVED, fobjname, &fsb); 359eda14cbcSMatt Macy print_file(fp, di, ZDIFF_ADDED, tobjname, &tsb); 360eda14cbcSMatt Macy return (0); 361eda14cbcSMatt Macy } 362eda14cbcSMatt Macy } 363eda14cbcSMatt Macy 364eda14cbcSMatt Macy static int 365eda14cbcSMatt Macy write_inuse_diffs(FILE *fp, differ_info_t *di, dmu_diff_record_t *dr) 366eda14cbcSMatt Macy { 367eda14cbcSMatt Macy uint64_t o; 368eda14cbcSMatt Macy int err; 369eda14cbcSMatt Macy 370eda14cbcSMatt Macy for (o = dr->ddr_first; o <= dr->ddr_last; o++) { 371eda14cbcSMatt Macy if ((err = write_inuse_diffs_one(fp, di, o)) != 0) 372eda14cbcSMatt Macy return (err); 373eda14cbcSMatt Macy } 374eda14cbcSMatt Macy return (0); 375eda14cbcSMatt Macy } 376eda14cbcSMatt Macy 377eda14cbcSMatt Macy static int 378eda14cbcSMatt Macy describe_free(FILE *fp, differ_info_t *di, uint64_t object, char *namebuf, 379eda14cbcSMatt Macy int maxlen) 380eda14cbcSMatt Macy { 381eda14cbcSMatt Macy struct zfs_stat sb; 382eda14cbcSMatt Macy 38316038816SMartin Matuska (void) get_stats_for_obj(di, di->fromsnap, object, namebuf, 38416038816SMartin Matuska maxlen, &sb); 38516038816SMartin Matuska 386eda14cbcSMatt Macy /* Don't print if in the delete queue on from side */ 38716038816SMartin Matuska if (di->zerr == ESTALE || di->zerr == ENOENT) { 388eda14cbcSMatt Macy di->zerr = 0; 389eda14cbcSMatt Macy return (0); 390eda14cbcSMatt Macy } 391eda14cbcSMatt Macy 392eda14cbcSMatt Macy print_file(fp, di, ZDIFF_REMOVED, namebuf, &sb); 393eda14cbcSMatt Macy return (0); 394eda14cbcSMatt Macy } 395eda14cbcSMatt Macy 396eda14cbcSMatt Macy static int 397eda14cbcSMatt Macy write_free_diffs(FILE *fp, differ_info_t *di, dmu_diff_record_t *dr) 398eda14cbcSMatt Macy { 399eda14cbcSMatt Macy zfs_cmd_t zc = {"\0"}; 400eda14cbcSMatt Macy libzfs_handle_t *lhdl = di->zhp->zfs_hdl; 401eda14cbcSMatt Macy char fobjname[MAXPATHLEN]; 402eda14cbcSMatt Macy 403eda14cbcSMatt Macy (void) strlcpy(zc.zc_name, di->fromsnap, sizeof (zc.zc_name)); 404eda14cbcSMatt Macy zc.zc_obj = dr->ddr_first - 1; 405eda14cbcSMatt Macy 406eda14cbcSMatt Macy ASSERT(di->zerr == 0); 407eda14cbcSMatt Macy 408eda14cbcSMatt Macy while (zc.zc_obj < dr->ddr_last) { 409eda14cbcSMatt Macy int err; 410eda14cbcSMatt Macy 411eda14cbcSMatt Macy err = zfs_ioctl(lhdl, ZFS_IOC_NEXT_OBJ, &zc); 412eda14cbcSMatt Macy if (err == 0) { 413eda14cbcSMatt Macy if (zc.zc_obj == di->shares) { 414eda14cbcSMatt Macy zc.zc_obj++; 415eda14cbcSMatt Macy continue; 416eda14cbcSMatt Macy } 417eda14cbcSMatt Macy if (zc.zc_obj > dr->ddr_last) { 418eda14cbcSMatt Macy break; 419eda14cbcSMatt Macy } 420dbd5678dSMartin Matuska (void) describe_free(fp, di, zc.zc_obj, fobjname, 421eda14cbcSMatt Macy MAXPATHLEN); 422eda14cbcSMatt Macy } else if (errno == ESRCH) { 423eda14cbcSMatt Macy break; 424eda14cbcSMatt Macy } else { 425eda14cbcSMatt Macy (void) snprintf(di->errbuf, sizeof (di->errbuf), 426eda14cbcSMatt Macy dgettext(TEXT_DOMAIN, 427eda14cbcSMatt Macy "next allocated object (> %lld) find failure"), 428eda14cbcSMatt Macy (longlong_t)zc.zc_obj); 429eda14cbcSMatt Macy di->zerr = errno; 430eda14cbcSMatt Macy break; 431eda14cbcSMatt Macy } 432eda14cbcSMatt Macy } 433eda14cbcSMatt Macy if (di->zerr) 434eda14cbcSMatt Macy return (-1); 435eda14cbcSMatt Macy return (0); 436eda14cbcSMatt Macy } 437eda14cbcSMatt Macy 438eda14cbcSMatt Macy static void * 439eda14cbcSMatt Macy differ(void *arg) 440eda14cbcSMatt Macy { 441eda14cbcSMatt Macy differ_info_t *di = arg; 442eda14cbcSMatt Macy dmu_diff_record_t dr; 443eda14cbcSMatt Macy FILE *ofp; 444eda14cbcSMatt Macy int err = 0; 445eda14cbcSMatt Macy 446eda14cbcSMatt Macy if ((ofp = fdopen(di->outputfd, "w")) == NULL) { 447eda14cbcSMatt Macy di->zerr = errno; 448*fd45b686SMartin Matuska strlcpy(di->errbuf, zfs_strerror(errno), sizeof (di->errbuf)); 449eda14cbcSMatt Macy (void) close(di->datafd); 450eda14cbcSMatt Macy return ((void *)-1); 451eda14cbcSMatt Macy } 452eda14cbcSMatt Macy 453eda14cbcSMatt Macy for (;;) { 454eda14cbcSMatt Macy char *cp = (char *)&dr; 455eda14cbcSMatt Macy int len = sizeof (dr); 456eda14cbcSMatt Macy int rv; 457eda14cbcSMatt Macy 458eda14cbcSMatt Macy do { 459eda14cbcSMatt Macy rv = read(di->datafd, cp, len); 460eda14cbcSMatt Macy cp += rv; 461eda14cbcSMatt Macy len -= rv; 462eda14cbcSMatt Macy } while (len > 0 && rv > 0); 463eda14cbcSMatt Macy 464eda14cbcSMatt Macy if (rv < 0 || (rv == 0 && len != sizeof (dr))) { 465eda14cbcSMatt Macy di->zerr = EPIPE; 466eda14cbcSMatt Macy break; 467eda14cbcSMatt Macy } else if (rv == 0) { 468eda14cbcSMatt Macy /* end of file at a natural breaking point */ 469eda14cbcSMatt Macy break; 470eda14cbcSMatt Macy } 471eda14cbcSMatt Macy 472eda14cbcSMatt Macy switch (dr.ddr_type) { 473eda14cbcSMatt Macy case DDR_FREE: 474eda14cbcSMatt Macy err = write_free_diffs(ofp, di, &dr); 475eda14cbcSMatt Macy break; 476eda14cbcSMatt Macy case DDR_INUSE: 477eda14cbcSMatt Macy err = write_inuse_diffs(ofp, di, &dr); 478eda14cbcSMatt Macy break; 479eda14cbcSMatt Macy default: 480eda14cbcSMatt Macy di->zerr = EPIPE; 481eda14cbcSMatt Macy break; 482eda14cbcSMatt Macy } 483eda14cbcSMatt Macy 484eda14cbcSMatt Macy if (err || di->zerr) 485eda14cbcSMatt Macy break; 486eda14cbcSMatt Macy } 487eda14cbcSMatt Macy 488eda14cbcSMatt Macy (void) fclose(ofp); 489eda14cbcSMatt Macy (void) close(di->datafd); 490eda14cbcSMatt Macy if (err) 491eda14cbcSMatt Macy return ((void *)-1); 492eda14cbcSMatt Macy if (di->zerr) { 493eda14cbcSMatt Macy ASSERT(di->zerr == EPIPE); 494eda14cbcSMatt Macy (void) snprintf(di->errbuf, sizeof (di->errbuf), 495eda14cbcSMatt Macy dgettext(TEXT_DOMAIN, 496eda14cbcSMatt Macy "Internal error: bad data from diff IOCTL")); 497eda14cbcSMatt Macy return ((void *)-1); 498eda14cbcSMatt Macy } 499eda14cbcSMatt Macy return ((void *)0); 500eda14cbcSMatt Macy } 501eda14cbcSMatt Macy 502eda14cbcSMatt Macy static int 503eda14cbcSMatt Macy make_temp_snapshot(differ_info_t *di) 504eda14cbcSMatt Macy { 505eda14cbcSMatt Macy libzfs_handle_t *hdl = di->zhp->zfs_hdl; 506eda14cbcSMatt Macy zfs_cmd_t zc = {"\0"}; 507eda14cbcSMatt Macy 508eda14cbcSMatt Macy (void) snprintf(zc.zc_value, sizeof (zc.zc_value), 509eda14cbcSMatt Macy ZDIFF_PREFIX, getpid()); 510eda14cbcSMatt Macy (void) strlcpy(zc.zc_name, di->ds, sizeof (zc.zc_name)); 511eda14cbcSMatt Macy zc.zc_cleanup_fd = di->cleanupfd; 512eda14cbcSMatt Macy 513eda14cbcSMatt Macy if (zfs_ioctl(hdl, ZFS_IOC_TMP_SNAPSHOT, &zc) != 0) { 514eda14cbcSMatt Macy int err = errno; 515eda14cbcSMatt Macy if (err == EPERM) { 516eda14cbcSMatt Macy (void) snprintf(di->errbuf, sizeof (di->errbuf), 517eda14cbcSMatt Macy dgettext(TEXT_DOMAIN, "The diff delegated " 518eda14cbcSMatt Macy "permission is needed in order\nto create a " 519eda14cbcSMatt Macy "just-in-time snapshot for diffing\n")); 520eda14cbcSMatt Macy return (zfs_error(hdl, EZFS_DIFF, di->errbuf)); 521eda14cbcSMatt Macy } else { 522eda14cbcSMatt Macy (void) snprintf(di->errbuf, sizeof (di->errbuf), 523eda14cbcSMatt Macy dgettext(TEXT_DOMAIN, "Cannot create just-in-time " 524eda14cbcSMatt Macy "snapshot of '%s'"), zc.zc_name); 525eda14cbcSMatt Macy return (zfs_standard_error(hdl, err, di->errbuf)); 526eda14cbcSMatt Macy } 527eda14cbcSMatt Macy } 528eda14cbcSMatt Macy 529eda14cbcSMatt Macy di->tmpsnap = zfs_strdup(hdl, zc.zc_value); 530eda14cbcSMatt Macy di->tosnap = zfs_asprintf(hdl, "%s@%s", di->ds, di->tmpsnap); 531eda14cbcSMatt Macy return (0); 532eda14cbcSMatt Macy } 533eda14cbcSMatt Macy 534eda14cbcSMatt Macy static void 535eda14cbcSMatt Macy teardown_differ_info(differ_info_t *di) 536eda14cbcSMatt Macy { 537eda14cbcSMatt Macy free(di->ds); 538eda14cbcSMatt Macy free(di->dsmnt); 539eda14cbcSMatt Macy free(di->fromsnap); 540eda14cbcSMatt Macy free(di->frommnt); 541eda14cbcSMatt Macy free(di->tosnap); 542eda14cbcSMatt Macy free(di->tmpsnap); 543eda14cbcSMatt Macy free(di->tomnt); 544eda14cbcSMatt Macy (void) close(di->cleanupfd); 545eda14cbcSMatt Macy } 546eda14cbcSMatt Macy 547eda14cbcSMatt Macy static int 548eda14cbcSMatt Macy get_snapshot_names(differ_info_t *di, const char *fromsnap, 549eda14cbcSMatt Macy const char *tosnap) 550eda14cbcSMatt Macy { 551eda14cbcSMatt Macy libzfs_handle_t *hdl = di->zhp->zfs_hdl; 552eda14cbcSMatt Macy char *atptrf = NULL; 553eda14cbcSMatt Macy char *atptrt = NULL; 554eda14cbcSMatt Macy int fdslen, fsnlen; 555eda14cbcSMatt Macy int tdslen, tsnlen; 556eda14cbcSMatt Macy 557eda14cbcSMatt Macy /* 558eda14cbcSMatt Macy * Can accept 559eda14cbcSMatt Macy * fdslen fsnlen tdslen tsnlen 560eda14cbcSMatt Macy * dataset@snap1 561eda14cbcSMatt Macy * 0. dataset@snap1 dataset@snap2 >0 >1 >0 >1 562eda14cbcSMatt Macy * 1. dataset@snap1 @snap2 >0 >1 ==0 >1 563eda14cbcSMatt Macy * 2. dataset@snap1 dataset >0 >1 >0 ==0 564eda14cbcSMatt Macy * 3. @snap1 dataset@snap2 ==0 >1 >0 >1 565eda14cbcSMatt Macy * 4. @snap1 dataset ==0 >1 >0 ==0 566eda14cbcSMatt Macy */ 567eda14cbcSMatt Macy if (tosnap == NULL) { 568eda14cbcSMatt Macy /* only a from snapshot given, must be valid */ 569eda14cbcSMatt Macy (void) snprintf(di->errbuf, sizeof (di->errbuf), 570eda14cbcSMatt Macy dgettext(TEXT_DOMAIN, 571eda14cbcSMatt Macy "Badly formed snapshot name %s"), fromsnap); 572eda14cbcSMatt Macy 573eda14cbcSMatt Macy if (!zfs_validate_name(hdl, fromsnap, ZFS_TYPE_SNAPSHOT, 574eda14cbcSMatt Macy B_FALSE)) { 575eda14cbcSMatt Macy return (zfs_error(hdl, EZFS_INVALIDNAME, 576eda14cbcSMatt Macy di->errbuf)); 577eda14cbcSMatt Macy } 578eda14cbcSMatt Macy 579eda14cbcSMatt Macy atptrf = strchr(fromsnap, '@'); 580eda14cbcSMatt Macy ASSERT(atptrf != NULL); 581eda14cbcSMatt Macy fdslen = atptrf - fromsnap; 582eda14cbcSMatt Macy 583eda14cbcSMatt Macy di->fromsnap = zfs_strdup(hdl, fromsnap); 584eda14cbcSMatt Macy di->ds = zfs_strdup(hdl, fromsnap); 585eda14cbcSMatt Macy di->ds[fdslen] = '\0'; 586eda14cbcSMatt Macy 587eda14cbcSMatt Macy /* the to snap will be a just-in-time snap of the head */ 588eda14cbcSMatt Macy return (make_temp_snapshot(di)); 589eda14cbcSMatt Macy } 590eda14cbcSMatt Macy 591eda14cbcSMatt Macy (void) snprintf(di->errbuf, sizeof (di->errbuf), 592eda14cbcSMatt Macy dgettext(TEXT_DOMAIN, 593eda14cbcSMatt Macy "Unable to determine which snapshots to compare")); 594eda14cbcSMatt Macy 595eda14cbcSMatt Macy atptrf = strchr(fromsnap, '@'); 596eda14cbcSMatt Macy atptrt = strchr(tosnap, '@'); 597eda14cbcSMatt Macy fdslen = atptrf ? atptrf - fromsnap : strlen(fromsnap); 598eda14cbcSMatt Macy tdslen = atptrt ? atptrt - tosnap : strlen(tosnap); 599eda14cbcSMatt Macy fsnlen = strlen(fromsnap) - fdslen; /* includes @ sign */ 600eda14cbcSMatt Macy tsnlen = strlen(tosnap) - tdslen; /* includes @ sign */ 601eda14cbcSMatt Macy 602eda14cbcSMatt Macy if (fsnlen <= 1 || tsnlen == 1 || (fdslen == 0 && tdslen == 0)) { 603eda14cbcSMatt Macy return (zfs_error(hdl, EZFS_INVALIDNAME, di->errbuf)); 604eda14cbcSMatt Macy } else if ((fdslen > 0 && tdslen > 0) && 605eda14cbcSMatt Macy ((tdslen != fdslen || strncmp(fromsnap, tosnap, fdslen) != 0))) { 606eda14cbcSMatt Macy /* 607eda14cbcSMatt Macy * not the same dataset name, might be okay if 608eda14cbcSMatt Macy * tosnap is a clone of a fromsnap descendant. 609eda14cbcSMatt Macy */ 610eda14cbcSMatt Macy char origin[ZFS_MAX_DATASET_NAME_LEN]; 611eda14cbcSMatt Macy zprop_source_t src; 612eda14cbcSMatt Macy zfs_handle_t *zhp; 613eda14cbcSMatt Macy 614eda14cbcSMatt Macy di->ds = zfs_alloc(di->zhp->zfs_hdl, tdslen + 1); 615be181ee2SMartin Matuska (void) strlcpy(di->ds, tosnap, tdslen + 1); 616eda14cbcSMatt Macy 617eda14cbcSMatt Macy zhp = zfs_open(hdl, di->ds, ZFS_TYPE_FILESYSTEM); 618eda14cbcSMatt Macy while (zhp != NULL) { 619eda14cbcSMatt Macy if (zfs_prop_get(zhp, ZFS_PROP_ORIGIN, origin, 620eda14cbcSMatt Macy sizeof (origin), &src, NULL, 0, B_FALSE) != 0) { 621eda14cbcSMatt Macy (void) zfs_close(zhp); 622eda14cbcSMatt Macy zhp = NULL; 623eda14cbcSMatt Macy break; 624eda14cbcSMatt Macy } 625eda14cbcSMatt Macy if (strncmp(origin, fromsnap, fsnlen) == 0) 626eda14cbcSMatt Macy break; 627eda14cbcSMatt Macy 628eda14cbcSMatt Macy (void) zfs_close(zhp); 629eda14cbcSMatt Macy zhp = zfs_open(hdl, origin, ZFS_TYPE_FILESYSTEM); 630eda14cbcSMatt Macy } 631eda14cbcSMatt Macy 632eda14cbcSMatt Macy if (zhp == NULL) { 633eda14cbcSMatt Macy (void) snprintf(di->errbuf, sizeof (di->errbuf), 634eda14cbcSMatt Macy dgettext(TEXT_DOMAIN, 635eda14cbcSMatt Macy "Not an earlier snapshot from the same fs")); 636eda14cbcSMatt Macy return (zfs_error(hdl, EZFS_INVALIDNAME, di->errbuf)); 637eda14cbcSMatt Macy } else { 638eda14cbcSMatt Macy (void) zfs_close(zhp); 639eda14cbcSMatt Macy } 640eda14cbcSMatt Macy 641eda14cbcSMatt Macy di->isclone = B_TRUE; 642eda14cbcSMatt Macy di->fromsnap = zfs_strdup(hdl, fromsnap); 643716fd348SMartin Matuska if (tsnlen) 644eda14cbcSMatt Macy di->tosnap = zfs_strdup(hdl, tosnap); 645716fd348SMartin Matuska else 646eda14cbcSMatt Macy return (make_temp_snapshot(di)); 647eda14cbcSMatt Macy } else { 648eda14cbcSMatt Macy int dslen = fdslen ? fdslen : tdslen; 649eda14cbcSMatt Macy 650eda14cbcSMatt Macy di->ds = zfs_alloc(hdl, dslen + 1); 651be181ee2SMartin Matuska (void) strlcpy(di->ds, fdslen ? fromsnap : tosnap, dslen + 1); 652eda14cbcSMatt Macy 653eda14cbcSMatt Macy di->fromsnap = zfs_asprintf(hdl, "%s%s", di->ds, atptrf); 654eda14cbcSMatt Macy if (tsnlen) { 655eda14cbcSMatt Macy di->tosnap = zfs_asprintf(hdl, "%s%s", di->ds, atptrt); 656eda14cbcSMatt Macy } else { 657eda14cbcSMatt Macy return (make_temp_snapshot(di)); 658eda14cbcSMatt Macy } 659eda14cbcSMatt Macy } 660eda14cbcSMatt Macy return (0); 661eda14cbcSMatt Macy } 662eda14cbcSMatt Macy 663eda14cbcSMatt Macy static int 664eda14cbcSMatt Macy get_mountpoint(differ_info_t *di, char *dsnm, char **mntpt) 665eda14cbcSMatt Macy { 666eda14cbcSMatt Macy boolean_t mounted; 667eda14cbcSMatt Macy 668eda14cbcSMatt Macy mounted = is_mounted(di->zhp->zfs_hdl, dsnm, mntpt); 669eda14cbcSMatt Macy if (mounted == B_FALSE) { 670eda14cbcSMatt Macy (void) snprintf(di->errbuf, sizeof (di->errbuf), 671eda14cbcSMatt Macy dgettext(TEXT_DOMAIN, 672eda14cbcSMatt Macy "Cannot diff an unmounted snapshot")); 673eda14cbcSMatt Macy return (zfs_error(di->zhp->zfs_hdl, EZFS_BADTYPE, di->errbuf)); 674eda14cbcSMatt Macy } 675eda14cbcSMatt Macy 676eda14cbcSMatt Macy /* Avoid a double slash at the beginning of root-mounted datasets */ 677eda14cbcSMatt Macy if (**mntpt == '/' && *(*mntpt + 1) == '\0') 678eda14cbcSMatt Macy **mntpt = '\0'; 679eda14cbcSMatt Macy return (0); 680eda14cbcSMatt Macy } 681eda14cbcSMatt Macy 682eda14cbcSMatt Macy static int 683eda14cbcSMatt Macy get_mountpoints(differ_info_t *di) 684eda14cbcSMatt Macy { 685eda14cbcSMatt Macy char *strptr; 686eda14cbcSMatt Macy char *frommntpt; 687eda14cbcSMatt Macy 688eda14cbcSMatt Macy /* 689eda14cbcSMatt Macy * first get the mountpoint for the parent dataset 690eda14cbcSMatt Macy */ 691eda14cbcSMatt Macy if (get_mountpoint(di, di->ds, &di->dsmnt) != 0) 692eda14cbcSMatt Macy return (-1); 693eda14cbcSMatt Macy 694eda14cbcSMatt Macy strptr = strchr(di->tosnap, '@'); 695eda14cbcSMatt Macy ASSERT3P(strptr, !=, NULL); 696eda14cbcSMatt Macy di->tomnt = zfs_asprintf(di->zhp->zfs_hdl, "%s%s%s", di->dsmnt, 697eda14cbcSMatt Macy ZDIFF_SNAPDIR, ++strptr); 698eda14cbcSMatt Macy 699eda14cbcSMatt Macy strptr = strchr(di->fromsnap, '@'); 700eda14cbcSMatt Macy ASSERT3P(strptr, !=, NULL); 701eda14cbcSMatt Macy 702eda14cbcSMatt Macy frommntpt = di->dsmnt; 703eda14cbcSMatt Macy if (di->isclone) { 704eda14cbcSMatt Macy char *mntpt; 705eda14cbcSMatt Macy int err; 706eda14cbcSMatt Macy 707eda14cbcSMatt Macy *strptr = '\0'; 708eda14cbcSMatt Macy err = get_mountpoint(di, di->fromsnap, &mntpt); 709eda14cbcSMatt Macy *strptr = '@'; 710eda14cbcSMatt Macy if (err != 0) 711eda14cbcSMatt Macy return (-1); 712eda14cbcSMatt Macy frommntpt = mntpt; 713eda14cbcSMatt Macy } 714eda14cbcSMatt Macy 715eda14cbcSMatt Macy di->frommnt = zfs_asprintf(di->zhp->zfs_hdl, "%s%s%s", frommntpt, 716eda14cbcSMatt Macy ZDIFF_SNAPDIR, ++strptr); 717eda14cbcSMatt Macy 718eda14cbcSMatt Macy if (di->isclone) 719eda14cbcSMatt Macy free(frommntpt); 720eda14cbcSMatt Macy 721eda14cbcSMatt Macy return (0); 722eda14cbcSMatt Macy } 723eda14cbcSMatt Macy 724eda14cbcSMatt Macy static int 725eda14cbcSMatt Macy setup_differ_info(zfs_handle_t *zhp, const char *fromsnap, 726eda14cbcSMatt Macy const char *tosnap, differ_info_t *di) 727eda14cbcSMatt Macy { 728eda14cbcSMatt Macy di->zhp = zhp; 729eda14cbcSMatt Macy 73016038816SMartin Matuska di->cleanupfd = open(ZFS_DEV, O_RDWR | O_CLOEXEC); 731eda14cbcSMatt Macy VERIFY(di->cleanupfd >= 0); 732eda14cbcSMatt Macy 733eda14cbcSMatt Macy if (get_snapshot_names(di, fromsnap, tosnap) != 0) 734eda14cbcSMatt Macy return (-1); 735eda14cbcSMatt Macy 736eda14cbcSMatt Macy if (get_mountpoints(di) != 0) 737eda14cbcSMatt Macy return (-1); 738eda14cbcSMatt Macy 739eda14cbcSMatt Macy if (find_shares_object(di) != 0) 740eda14cbcSMatt Macy return (-1); 741eda14cbcSMatt Macy 742eda14cbcSMatt Macy return (0); 743eda14cbcSMatt Macy } 744eda14cbcSMatt Macy 745eda14cbcSMatt Macy int 746eda14cbcSMatt Macy zfs_show_diffs(zfs_handle_t *zhp, int outfd, const char *fromsnap, 747eda14cbcSMatt Macy const char *tosnap, int flags) 748eda14cbcSMatt Macy { 749eda14cbcSMatt Macy zfs_cmd_t zc = {"\0"}; 7501f1e2261SMartin Matuska char errbuf[ERRBUFLEN]; 751eda14cbcSMatt Macy differ_info_t di = { 0 }; 752eda14cbcSMatt Macy pthread_t tid; 753eda14cbcSMatt Macy int pipefd[2]; 754eda14cbcSMatt Macy int iocerr; 755eda14cbcSMatt Macy 756eda14cbcSMatt Macy (void) snprintf(errbuf, sizeof (errbuf), 757eda14cbcSMatt Macy dgettext(TEXT_DOMAIN, "zfs diff failed")); 758eda14cbcSMatt Macy 759eda14cbcSMatt Macy if (setup_differ_info(zhp, fromsnap, tosnap, &di)) { 760eda14cbcSMatt Macy teardown_differ_info(&di); 761eda14cbcSMatt Macy return (-1); 762eda14cbcSMatt Macy } 763eda14cbcSMatt Macy 76416038816SMartin Matuska if (pipe2(pipefd, O_CLOEXEC)) { 765*fd45b686SMartin Matuska zfs_error_aux(zhp->zfs_hdl, "%s", zfs_strerror(errno)); 766eda14cbcSMatt Macy teardown_differ_info(&di); 767eda14cbcSMatt Macy return (zfs_error(zhp->zfs_hdl, EZFS_PIPEFAILED, errbuf)); 768eda14cbcSMatt Macy } 769eda14cbcSMatt Macy 770eda14cbcSMatt Macy di.scripted = (flags & ZFS_DIFF_PARSEABLE); 771eda14cbcSMatt Macy di.classify = (flags & ZFS_DIFF_CLASSIFY); 772eda14cbcSMatt Macy di.timestamped = (flags & ZFS_DIFF_TIMESTAMP); 773681ce946SMartin Matuska di.no_mangle = (flags & ZFS_DIFF_NO_MANGLE); 774eda14cbcSMatt Macy 775eda14cbcSMatt Macy di.outputfd = outfd; 776eda14cbcSMatt Macy di.datafd = pipefd[0]; 777eda14cbcSMatt Macy 778eda14cbcSMatt Macy if (pthread_create(&tid, NULL, differ, &di)) { 779*fd45b686SMartin Matuska zfs_error_aux(zhp->zfs_hdl, "%s", zfs_strerror(errno)); 780eda14cbcSMatt Macy (void) close(pipefd[0]); 781eda14cbcSMatt Macy (void) close(pipefd[1]); 782eda14cbcSMatt Macy teardown_differ_info(&di); 783eda14cbcSMatt Macy return (zfs_error(zhp->zfs_hdl, 784eda14cbcSMatt Macy EZFS_THREADCREATEFAILED, errbuf)); 785eda14cbcSMatt Macy } 786eda14cbcSMatt Macy 787eda14cbcSMatt Macy /* do the ioctl() */ 788eda14cbcSMatt Macy (void) strlcpy(zc.zc_value, di.fromsnap, strlen(di.fromsnap) + 1); 789eda14cbcSMatt Macy (void) strlcpy(zc.zc_name, di.tosnap, strlen(di.tosnap) + 1); 790eda14cbcSMatt Macy zc.zc_cookie = pipefd[1]; 791eda14cbcSMatt Macy 792eda14cbcSMatt Macy iocerr = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_DIFF, &zc); 793eda14cbcSMatt Macy if (iocerr != 0) { 794eda14cbcSMatt Macy (void) snprintf(errbuf, sizeof (errbuf), 795eda14cbcSMatt Macy dgettext(TEXT_DOMAIN, "Unable to obtain diffs")); 796eda14cbcSMatt Macy if (errno == EPERM) { 797eda14cbcSMatt Macy zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, 798eda14cbcSMatt Macy "\n The sys_mount privilege or diff delegated " 799eda14cbcSMatt Macy "permission is needed\n to execute the " 800eda14cbcSMatt Macy "diff ioctl")); 801eda14cbcSMatt Macy } else if (errno == EXDEV) { 802eda14cbcSMatt Macy zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, 803eda14cbcSMatt Macy "\n Not an earlier snapshot from the same fs")); 804eda14cbcSMatt Macy } else if (errno != EPIPE || di.zerr == 0) { 805*fd45b686SMartin Matuska zfs_error_aux(zhp->zfs_hdl, "%s", zfs_strerror(errno)); 806eda14cbcSMatt Macy } 807eda14cbcSMatt Macy (void) close(pipefd[1]); 808eda14cbcSMatt Macy (void) pthread_cancel(tid); 809eda14cbcSMatt Macy (void) pthread_join(tid, NULL); 810eda14cbcSMatt Macy teardown_differ_info(&di); 811eda14cbcSMatt Macy if (di.zerr != 0 && di.zerr != EPIPE) { 812*fd45b686SMartin Matuska zfs_error_aux(zhp->zfs_hdl, "%s", 813*fd45b686SMartin Matuska zfs_strerror(di.zerr)); 814eda14cbcSMatt Macy return (zfs_error(zhp->zfs_hdl, EZFS_DIFF, di.errbuf)); 815eda14cbcSMatt Macy } else { 816eda14cbcSMatt Macy return (zfs_error(zhp->zfs_hdl, EZFS_DIFFDATA, errbuf)); 817eda14cbcSMatt Macy } 818eda14cbcSMatt Macy } 819eda14cbcSMatt Macy 820eda14cbcSMatt Macy (void) close(pipefd[1]); 821eda14cbcSMatt Macy (void) pthread_join(tid, NULL); 822eda14cbcSMatt Macy 823eda14cbcSMatt Macy if (di.zerr != 0) { 824*fd45b686SMartin Matuska zfs_error_aux(zhp->zfs_hdl, "%s", zfs_strerror(di.zerr)); 825eda14cbcSMatt Macy return (zfs_error(zhp->zfs_hdl, EZFS_DIFF, di.errbuf)); 826eda14cbcSMatt Macy } 827eda14cbcSMatt Macy teardown_differ_info(&di); 828eda14cbcSMatt Macy return (0); 829eda14cbcSMatt Macy } 830