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> 47eda14cbcSMatt Macy #include "libzfs_impl.h" 48eda14cbcSMatt Macy 49eda14cbcSMatt Macy #define ZDIFF_SNAPDIR "/.zfs/snapshot/" 50eda14cbcSMatt Macy #define ZDIFF_PREFIX "zfs-diff-%d" 51eda14cbcSMatt Macy 52eda14cbcSMatt Macy #define ZDIFF_ADDED '+' 53681ce946SMartin Matuska #define ZDIFF_MODIFIED "M" 54eda14cbcSMatt Macy #define ZDIFF_REMOVED '-' 55681ce946SMartin Matuska #define ZDIFF_RENAMED "R" 56eda14cbcSMatt Macy 57eda14cbcSMatt Macy 58eda14cbcSMatt Macy /* 59eda14cbcSMatt Macy * Given a {dsname, object id}, get the object path 60eda14cbcSMatt Macy */ 61eda14cbcSMatt Macy static int 62eda14cbcSMatt Macy get_stats_for_obj(differ_info_t *di, const char *dsname, uint64_t obj, 63eda14cbcSMatt Macy char *pn, int maxlen, zfs_stat_t *sb) 64eda14cbcSMatt Macy { 65eda14cbcSMatt Macy zfs_cmd_t zc = {"\0"}; 66eda14cbcSMatt Macy int error; 67eda14cbcSMatt Macy 68eda14cbcSMatt Macy (void) strlcpy(zc.zc_name, dsname, sizeof (zc.zc_name)); 69eda14cbcSMatt Macy zc.zc_obj = obj; 70eda14cbcSMatt Macy 71eda14cbcSMatt Macy errno = 0; 72eda14cbcSMatt Macy error = zfs_ioctl(di->zhp->zfs_hdl, ZFS_IOC_OBJ_TO_STATS, &zc); 73eda14cbcSMatt Macy di->zerr = errno; 74eda14cbcSMatt Macy 75eda14cbcSMatt Macy /* we can get stats even if we failed to get a path */ 76eda14cbcSMatt Macy (void) memcpy(sb, &zc.zc_stat, sizeof (zfs_stat_t)); 77eda14cbcSMatt Macy if (error == 0) { 78eda14cbcSMatt Macy ASSERT(di->zerr == 0); 79eda14cbcSMatt Macy (void) strlcpy(pn, zc.zc_value, maxlen); 80eda14cbcSMatt Macy return (0); 81eda14cbcSMatt Macy } 82eda14cbcSMatt Macy 83eda14cbcSMatt Macy if (di->zerr == ESTALE) { 84eda14cbcSMatt Macy (void) snprintf(pn, maxlen, "(on_delete_queue)"); 85eda14cbcSMatt Macy return (0); 86eda14cbcSMatt Macy } else if (di->zerr == EPERM) { 87eda14cbcSMatt Macy (void) snprintf(di->errbuf, sizeof (di->errbuf), 88eda14cbcSMatt Macy dgettext(TEXT_DOMAIN, 89eda14cbcSMatt Macy "The sys_config privilege or diff delegated permission " 90eda14cbcSMatt Macy "is needed\nto discover path names")); 91eda14cbcSMatt Macy return (-1); 92eda14cbcSMatt Macy } else if (di->zerr == EACCES) { 93eda14cbcSMatt Macy (void) snprintf(di->errbuf, sizeof (di->errbuf), 94eda14cbcSMatt Macy dgettext(TEXT_DOMAIN, 95eda14cbcSMatt Macy "Key must be loaded to discover path names")); 96eda14cbcSMatt Macy return (-1); 97eda14cbcSMatt Macy } else { 98eda14cbcSMatt Macy (void) snprintf(di->errbuf, sizeof (di->errbuf), 99eda14cbcSMatt Macy dgettext(TEXT_DOMAIN, 100eda14cbcSMatt Macy "Unable to determine path or stats for " 101eda14cbcSMatt Macy "object %lld in %s"), (longlong_t)obj, dsname); 102eda14cbcSMatt Macy return (-1); 103eda14cbcSMatt Macy } 104eda14cbcSMatt Macy } 105eda14cbcSMatt Macy 106eda14cbcSMatt Macy /* 107eda14cbcSMatt Macy * stream_bytes 108eda14cbcSMatt Macy * 109eda14cbcSMatt Macy * Prints a file name out a character at a time. If the character is 110eda14cbcSMatt Macy * not in the range of what we consider "printable" ASCII, display it 111eda14cbcSMatt Macy * as an escaped 4-digit octal value. ASCII values less than a space 112eda14cbcSMatt Macy * are all control characters and we declare the upper end as the 113eda14cbcSMatt Macy * DELete character. This also is the last 7-bit ASCII character. 114eda14cbcSMatt Macy * We choose to treat all 8-bit ASCII as not printable for this 115eda14cbcSMatt Macy * application. 116eda14cbcSMatt Macy */ 117eda14cbcSMatt Macy static void 118eda14cbcSMatt Macy stream_bytes(FILE *fp, const char *string) 119eda14cbcSMatt Macy { 120eda14cbcSMatt Macy char c; 121eda14cbcSMatt Macy 122eda14cbcSMatt Macy while ((c = *string++) != '\0') { 123eda14cbcSMatt Macy if (c > ' ' && c != '\\' && c < '\177') { 124681ce946SMartin Matuska (void) fputc(c, fp); 125eda14cbcSMatt Macy } else { 126681ce946SMartin Matuska (void) fprintf(fp, "\\%04hho", (uint8_t)c); 127eda14cbcSMatt Macy } 128eda14cbcSMatt Macy } 129eda14cbcSMatt Macy } 130eda14cbcSMatt Macy 131681ce946SMartin Matuska static char 132681ce946SMartin Matuska get_what(mode_t what) 133eda14cbcSMatt Macy { 134eda14cbcSMatt Macy switch (what & S_IFMT) { 135eda14cbcSMatt Macy case S_IFBLK: 136681ce946SMartin Matuska return ('B'); 137eda14cbcSMatt Macy case S_IFCHR: 138681ce946SMartin Matuska return ('C'); 139eda14cbcSMatt Macy case S_IFDIR: 140681ce946SMartin Matuska return ('/'); 141eda14cbcSMatt Macy #ifdef S_IFDOOR 142eda14cbcSMatt Macy case S_IFDOOR: 143681ce946SMartin Matuska return ('>'); 144eda14cbcSMatt Macy #endif 145eda14cbcSMatt Macy case S_IFIFO: 146681ce946SMartin Matuska return ('|'); 147eda14cbcSMatt Macy case S_IFLNK: 148681ce946SMartin Matuska return ('@'); 149eda14cbcSMatt Macy #ifdef S_IFPORT 150eda14cbcSMatt Macy case S_IFPORT: 151681ce946SMartin Matuska return ('P'); 152eda14cbcSMatt Macy #endif 153eda14cbcSMatt Macy case S_IFSOCK: 154681ce946SMartin Matuska return ('='); 155eda14cbcSMatt Macy case S_IFREG: 156681ce946SMartin Matuska return ('F'); 157eda14cbcSMatt Macy default: 158681ce946SMartin Matuska return ('?'); 159eda14cbcSMatt Macy } 160eda14cbcSMatt Macy } 161eda14cbcSMatt Macy 162eda14cbcSMatt Macy static void 163eda14cbcSMatt Macy print_cmn(FILE *fp, differ_info_t *di, const char *file) 164eda14cbcSMatt Macy { 165681ce946SMartin Matuska if (!di->no_mangle) { 166eda14cbcSMatt Macy stream_bytes(fp, di->dsmnt); 167eda14cbcSMatt Macy stream_bytes(fp, file); 168681ce946SMartin Matuska } else { 169681ce946SMartin Matuska (void) fputs(di->dsmnt, fp); 170681ce946SMartin Matuska (void) fputs(file, fp); 171681ce946SMartin Matuska } 172eda14cbcSMatt Macy } 173eda14cbcSMatt Macy 174eda14cbcSMatt Macy static void 175eda14cbcSMatt Macy print_rename(FILE *fp, differ_info_t *di, const char *old, const char *new, 176eda14cbcSMatt Macy zfs_stat_t *isb) 177eda14cbcSMatt Macy { 178eda14cbcSMatt Macy if (di->timestamped) 179eda14cbcSMatt Macy (void) fprintf(fp, "%10lld.%09lld\t", 180eda14cbcSMatt Macy (longlong_t)isb->zs_ctime[0], 181eda14cbcSMatt Macy (longlong_t)isb->zs_ctime[1]); 182681ce946SMartin Matuska (void) fputs(ZDIFF_RENAMED "\t", fp); 183681ce946SMartin Matuska if (di->classify) 184681ce946SMartin Matuska (void) fprintf(fp, "%c\t", get_what(isb->zs_mode)); 185eda14cbcSMatt Macy print_cmn(fp, di, old); 186681ce946SMartin Matuska (void) fputs(di->scripted ? "\t" : " -> ", fp); 187eda14cbcSMatt Macy print_cmn(fp, di, new); 188681ce946SMartin Matuska (void) fputc('\n', fp); 189eda14cbcSMatt Macy } 190eda14cbcSMatt Macy 191eda14cbcSMatt Macy static void 192eda14cbcSMatt Macy print_link_change(FILE *fp, differ_info_t *di, int delta, const char *file, 193eda14cbcSMatt Macy zfs_stat_t *isb) 194eda14cbcSMatt Macy { 195eda14cbcSMatt Macy if (di->timestamped) 196eda14cbcSMatt Macy (void) fprintf(fp, "%10lld.%09lld\t", 197eda14cbcSMatt Macy (longlong_t)isb->zs_ctime[0], 198eda14cbcSMatt Macy (longlong_t)isb->zs_ctime[1]); 199681ce946SMartin Matuska (void) fputs(ZDIFF_MODIFIED "\t", fp); 200681ce946SMartin Matuska if (di->classify) 201681ce946SMartin Matuska (void) fprintf(fp, "%c\t", get_what(isb->zs_mode)); 202eda14cbcSMatt Macy print_cmn(fp, di, file); 203681ce946SMartin Matuska (void) fprintf(fp, "\t(%+d)\n", delta); 204eda14cbcSMatt Macy } 205eda14cbcSMatt Macy 206eda14cbcSMatt Macy static void 207eda14cbcSMatt Macy print_file(FILE *fp, differ_info_t *di, char type, const char *file, 208eda14cbcSMatt Macy zfs_stat_t *isb) 209eda14cbcSMatt Macy { 210eda14cbcSMatt Macy if (di->timestamped) 211eda14cbcSMatt Macy (void) fprintf(fp, "%10lld.%09lld\t", 212eda14cbcSMatt Macy (longlong_t)isb->zs_ctime[0], 213eda14cbcSMatt Macy (longlong_t)isb->zs_ctime[1]); 214eda14cbcSMatt Macy (void) fprintf(fp, "%c\t", type); 215681ce946SMartin Matuska if (di->classify) 216681ce946SMartin Matuska (void) fprintf(fp, "%c\t", get_what(isb->zs_mode)); 217eda14cbcSMatt Macy print_cmn(fp, di, file); 218681ce946SMartin Matuska (void) fputc('\n', fp); 219eda14cbcSMatt Macy } 220eda14cbcSMatt Macy 221eda14cbcSMatt Macy static int 222eda14cbcSMatt Macy write_inuse_diffs_one(FILE *fp, differ_info_t *di, uint64_t dobj) 223eda14cbcSMatt Macy { 224eda14cbcSMatt Macy struct zfs_stat fsb, tsb; 225eda14cbcSMatt Macy mode_t fmode, tmode; 226eda14cbcSMatt Macy char fobjname[MAXPATHLEN], tobjname[MAXPATHLEN]; 22716038816SMartin Matuska boolean_t already_logged = B_FALSE; 228eda14cbcSMatt Macy int fobjerr, tobjerr; 229eda14cbcSMatt Macy int change; 230eda14cbcSMatt Macy 231eda14cbcSMatt Macy if (dobj == di->shares) 232eda14cbcSMatt Macy return (0); 233eda14cbcSMatt Macy 234eda14cbcSMatt Macy /* 235eda14cbcSMatt Macy * Check the from and to snapshots for info on the object. If 236eda14cbcSMatt Macy * we get ENOENT, then the object just didn't exist in that 237eda14cbcSMatt Macy * snapshot. If we get ENOTSUP, then we tried to get 238eda14cbcSMatt Macy * info on a non-ZPL object, which we don't care about anyway. 23916038816SMartin Matuska * For any other error we print a warning which includes the 24016038816SMartin Matuska * errno and continue. 241eda14cbcSMatt Macy */ 24216038816SMartin Matuska 243eda14cbcSMatt Macy fobjerr = get_stats_for_obj(di, di->fromsnap, dobj, fobjname, 244eda14cbcSMatt Macy MAXPATHLEN, &fsb); 24516038816SMartin Matuska if (fobjerr && di->zerr != ENOTSUP && di->zerr != ENOENT) { 24616038816SMartin Matuska zfs_error_aux(di->zhp->zfs_hdl, "%s", strerror(di->zerr)); 24716038816SMartin Matuska zfs_error(di->zhp->zfs_hdl, di->zerr, di->errbuf); 24816038816SMartin Matuska /* 24916038816SMartin Matuska * Let's not print an error for the same object more than 25016038816SMartin Matuska * once if it happens in both snapshots 25116038816SMartin Matuska */ 25216038816SMartin Matuska already_logged = B_TRUE; 25316038816SMartin Matuska } 254eda14cbcSMatt Macy 255eda14cbcSMatt Macy tobjerr = get_stats_for_obj(di, di->tosnap, dobj, tobjname, 256eda14cbcSMatt Macy MAXPATHLEN, &tsb); 257eda14cbcSMatt Macy 25816038816SMartin Matuska if (tobjerr && di->zerr != ENOTSUP && di->zerr != ENOENT) { 25916038816SMartin Matuska if (!already_logged) { 26016038816SMartin Matuska zfs_error_aux(di->zhp->zfs_hdl, 26116038816SMartin Matuska "%s", strerror(di->zerr)); 26216038816SMartin Matuska zfs_error(di->zhp->zfs_hdl, di->zerr, di->errbuf); 26316038816SMartin Matuska } 26416038816SMartin Matuska } 265eda14cbcSMatt Macy /* 266eda14cbcSMatt Macy * Unallocated object sharing the same meta dnode block 267eda14cbcSMatt Macy */ 268eda14cbcSMatt Macy if (fobjerr && tobjerr) { 269eda14cbcSMatt Macy di->zerr = 0; 270eda14cbcSMatt Macy return (0); 271eda14cbcSMatt Macy } 272eda14cbcSMatt Macy 273eda14cbcSMatt Macy di->zerr = 0; /* negate get_stats_for_obj() from side that failed */ 274eda14cbcSMatt Macy fmode = fsb.zs_mode & S_IFMT; 275eda14cbcSMatt Macy tmode = tsb.zs_mode & S_IFMT; 276eda14cbcSMatt Macy if (fmode == S_IFDIR || tmode == S_IFDIR || fsb.zs_links == 0 || 277eda14cbcSMatt Macy tsb.zs_links == 0) 278eda14cbcSMatt Macy change = 0; 279eda14cbcSMatt Macy else 280eda14cbcSMatt Macy change = tsb.zs_links - fsb.zs_links; 281eda14cbcSMatt Macy 282eda14cbcSMatt Macy if (fobjerr) { 283eda14cbcSMatt Macy if (change) { 284eda14cbcSMatt Macy print_link_change(fp, di, change, tobjname, &tsb); 285eda14cbcSMatt Macy return (0); 286eda14cbcSMatt Macy } 287eda14cbcSMatt Macy print_file(fp, di, ZDIFF_ADDED, tobjname, &tsb); 288eda14cbcSMatt Macy return (0); 289eda14cbcSMatt Macy } else if (tobjerr) { 290eda14cbcSMatt Macy if (change) { 291eda14cbcSMatt Macy print_link_change(fp, di, change, fobjname, &fsb); 292eda14cbcSMatt Macy return (0); 293eda14cbcSMatt Macy } 294eda14cbcSMatt Macy print_file(fp, di, ZDIFF_REMOVED, fobjname, &fsb); 295eda14cbcSMatt Macy return (0); 296eda14cbcSMatt Macy } 297eda14cbcSMatt Macy 298eda14cbcSMatt Macy if (fmode != tmode && fsb.zs_gen == tsb.zs_gen) 299eda14cbcSMatt Macy tsb.zs_gen++; /* Force a generational difference */ 300eda14cbcSMatt Macy 301eda14cbcSMatt Macy /* Simple modification or no change */ 302eda14cbcSMatt Macy if (fsb.zs_gen == tsb.zs_gen) { 303eda14cbcSMatt Macy /* No apparent changes. Could we assert !this? */ 304eda14cbcSMatt Macy if (fsb.zs_ctime[0] == tsb.zs_ctime[0] && 305eda14cbcSMatt Macy fsb.zs_ctime[1] == tsb.zs_ctime[1]) 306eda14cbcSMatt Macy return (0); 307eda14cbcSMatt Macy if (change) { 308eda14cbcSMatt Macy print_link_change(fp, di, change, 309eda14cbcSMatt Macy change > 0 ? fobjname : tobjname, &tsb); 310eda14cbcSMatt Macy } else if (strcmp(fobjname, tobjname) == 0) { 311681ce946SMartin Matuska print_file(fp, di, *ZDIFF_MODIFIED, fobjname, &tsb); 312eda14cbcSMatt Macy } else { 313eda14cbcSMatt Macy print_rename(fp, di, fobjname, tobjname, &tsb); 314eda14cbcSMatt Macy } 315eda14cbcSMatt Macy return (0); 316eda14cbcSMatt Macy } else { 317eda14cbcSMatt Macy /* file re-created or object re-used */ 318eda14cbcSMatt Macy print_file(fp, di, ZDIFF_REMOVED, fobjname, &fsb); 319eda14cbcSMatt Macy print_file(fp, di, ZDIFF_ADDED, tobjname, &tsb); 320eda14cbcSMatt Macy return (0); 321eda14cbcSMatt Macy } 322eda14cbcSMatt Macy } 323eda14cbcSMatt Macy 324eda14cbcSMatt Macy static int 325eda14cbcSMatt Macy write_inuse_diffs(FILE *fp, differ_info_t *di, dmu_diff_record_t *dr) 326eda14cbcSMatt Macy { 327eda14cbcSMatt Macy uint64_t o; 328eda14cbcSMatt Macy int err; 329eda14cbcSMatt Macy 330eda14cbcSMatt Macy for (o = dr->ddr_first; o <= dr->ddr_last; o++) { 331eda14cbcSMatt Macy if ((err = write_inuse_diffs_one(fp, di, o)) != 0) 332eda14cbcSMatt Macy return (err); 333eda14cbcSMatt Macy } 334eda14cbcSMatt Macy return (0); 335eda14cbcSMatt Macy } 336eda14cbcSMatt Macy 337eda14cbcSMatt Macy static int 338eda14cbcSMatt Macy describe_free(FILE *fp, differ_info_t *di, uint64_t object, char *namebuf, 339eda14cbcSMatt Macy int maxlen) 340eda14cbcSMatt Macy { 341eda14cbcSMatt Macy struct zfs_stat sb; 342eda14cbcSMatt Macy 34316038816SMartin Matuska (void) get_stats_for_obj(di, di->fromsnap, object, namebuf, 34416038816SMartin Matuska maxlen, &sb); 34516038816SMartin Matuska 346eda14cbcSMatt Macy /* Don't print if in the delete queue on from side */ 34716038816SMartin Matuska if (di->zerr == ESTALE || di->zerr == ENOENT) { 348eda14cbcSMatt Macy di->zerr = 0; 349eda14cbcSMatt Macy return (0); 350eda14cbcSMatt Macy } 351eda14cbcSMatt Macy 352eda14cbcSMatt Macy print_file(fp, di, ZDIFF_REMOVED, namebuf, &sb); 353eda14cbcSMatt Macy return (0); 354eda14cbcSMatt Macy } 355eda14cbcSMatt Macy 356eda14cbcSMatt Macy static int 357eda14cbcSMatt Macy write_free_diffs(FILE *fp, differ_info_t *di, dmu_diff_record_t *dr) 358eda14cbcSMatt Macy { 359eda14cbcSMatt Macy zfs_cmd_t zc = {"\0"}; 360eda14cbcSMatt Macy libzfs_handle_t *lhdl = di->zhp->zfs_hdl; 361eda14cbcSMatt Macy char fobjname[MAXPATHLEN]; 362eda14cbcSMatt Macy 363eda14cbcSMatt Macy (void) strlcpy(zc.zc_name, di->fromsnap, sizeof (zc.zc_name)); 364eda14cbcSMatt Macy zc.zc_obj = dr->ddr_first - 1; 365eda14cbcSMatt Macy 366eda14cbcSMatt Macy ASSERT(di->zerr == 0); 367eda14cbcSMatt Macy 368eda14cbcSMatt Macy while (zc.zc_obj < dr->ddr_last) { 369eda14cbcSMatt Macy int err; 370eda14cbcSMatt Macy 371eda14cbcSMatt Macy err = zfs_ioctl(lhdl, ZFS_IOC_NEXT_OBJ, &zc); 372eda14cbcSMatt Macy if (err == 0) { 373eda14cbcSMatt Macy if (zc.zc_obj == di->shares) { 374eda14cbcSMatt Macy zc.zc_obj++; 375eda14cbcSMatt Macy continue; 376eda14cbcSMatt Macy } 377eda14cbcSMatt Macy if (zc.zc_obj > dr->ddr_last) { 378eda14cbcSMatt Macy break; 379eda14cbcSMatt Macy } 380*dbd5678dSMartin Matuska (void) describe_free(fp, di, zc.zc_obj, fobjname, 381eda14cbcSMatt Macy MAXPATHLEN); 382eda14cbcSMatt Macy } else if (errno == ESRCH) { 383eda14cbcSMatt Macy break; 384eda14cbcSMatt Macy } else { 385eda14cbcSMatt Macy (void) snprintf(di->errbuf, sizeof (di->errbuf), 386eda14cbcSMatt Macy dgettext(TEXT_DOMAIN, 387eda14cbcSMatt Macy "next allocated object (> %lld) find failure"), 388eda14cbcSMatt Macy (longlong_t)zc.zc_obj); 389eda14cbcSMatt Macy di->zerr = errno; 390eda14cbcSMatt Macy break; 391eda14cbcSMatt Macy } 392eda14cbcSMatt Macy } 393eda14cbcSMatt Macy if (di->zerr) 394eda14cbcSMatt Macy return (-1); 395eda14cbcSMatt Macy return (0); 396eda14cbcSMatt Macy } 397eda14cbcSMatt Macy 398eda14cbcSMatt Macy static void * 399eda14cbcSMatt Macy differ(void *arg) 400eda14cbcSMatt Macy { 401eda14cbcSMatt Macy differ_info_t *di = arg; 402eda14cbcSMatt Macy dmu_diff_record_t dr; 403eda14cbcSMatt Macy FILE *ofp; 404eda14cbcSMatt Macy int err = 0; 405eda14cbcSMatt Macy 406eda14cbcSMatt Macy if ((ofp = fdopen(di->outputfd, "w")) == NULL) { 407eda14cbcSMatt Macy di->zerr = errno; 408eda14cbcSMatt Macy strlcpy(di->errbuf, strerror(errno), sizeof (di->errbuf)); 409eda14cbcSMatt Macy (void) close(di->datafd); 410eda14cbcSMatt Macy return ((void *)-1); 411eda14cbcSMatt Macy } 412eda14cbcSMatt Macy 413eda14cbcSMatt Macy for (;;) { 414eda14cbcSMatt Macy char *cp = (char *)&dr; 415eda14cbcSMatt Macy int len = sizeof (dr); 416eda14cbcSMatt Macy int rv; 417eda14cbcSMatt Macy 418eda14cbcSMatt Macy do { 419eda14cbcSMatt Macy rv = read(di->datafd, cp, len); 420eda14cbcSMatt Macy cp += rv; 421eda14cbcSMatt Macy len -= rv; 422eda14cbcSMatt Macy } while (len > 0 && rv > 0); 423eda14cbcSMatt Macy 424eda14cbcSMatt Macy if (rv < 0 || (rv == 0 && len != sizeof (dr))) { 425eda14cbcSMatt Macy di->zerr = EPIPE; 426eda14cbcSMatt Macy break; 427eda14cbcSMatt Macy } else if (rv == 0) { 428eda14cbcSMatt Macy /* end of file at a natural breaking point */ 429eda14cbcSMatt Macy break; 430eda14cbcSMatt Macy } 431eda14cbcSMatt Macy 432eda14cbcSMatt Macy switch (dr.ddr_type) { 433eda14cbcSMatt Macy case DDR_FREE: 434eda14cbcSMatt Macy err = write_free_diffs(ofp, di, &dr); 435eda14cbcSMatt Macy break; 436eda14cbcSMatt Macy case DDR_INUSE: 437eda14cbcSMatt Macy err = write_inuse_diffs(ofp, di, &dr); 438eda14cbcSMatt Macy break; 439eda14cbcSMatt Macy default: 440eda14cbcSMatt Macy di->zerr = EPIPE; 441eda14cbcSMatt Macy break; 442eda14cbcSMatt Macy } 443eda14cbcSMatt Macy 444eda14cbcSMatt Macy if (err || di->zerr) 445eda14cbcSMatt Macy break; 446eda14cbcSMatt Macy } 447eda14cbcSMatt Macy 448eda14cbcSMatt Macy (void) fclose(ofp); 449eda14cbcSMatt Macy (void) close(di->datafd); 450eda14cbcSMatt Macy if (err) 451eda14cbcSMatt Macy return ((void *)-1); 452eda14cbcSMatt Macy if (di->zerr) { 453eda14cbcSMatt Macy ASSERT(di->zerr == EPIPE); 454eda14cbcSMatt Macy (void) snprintf(di->errbuf, sizeof (di->errbuf), 455eda14cbcSMatt Macy dgettext(TEXT_DOMAIN, 456eda14cbcSMatt Macy "Internal error: bad data from diff IOCTL")); 457eda14cbcSMatt Macy return ((void *)-1); 458eda14cbcSMatt Macy } 459eda14cbcSMatt Macy return ((void *)0); 460eda14cbcSMatt Macy } 461eda14cbcSMatt Macy 462eda14cbcSMatt Macy static int 463eda14cbcSMatt Macy make_temp_snapshot(differ_info_t *di) 464eda14cbcSMatt Macy { 465eda14cbcSMatt Macy libzfs_handle_t *hdl = di->zhp->zfs_hdl; 466eda14cbcSMatt Macy zfs_cmd_t zc = {"\0"}; 467eda14cbcSMatt Macy 468eda14cbcSMatt Macy (void) snprintf(zc.zc_value, sizeof (zc.zc_value), 469eda14cbcSMatt Macy ZDIFF_PREFIX, getpid()); 470eda14cbcSMatt Macy (void) strlcpy(zc.zc_name, di->ds, sizeof (zc.zc_name)); 471eda14cbcSMatt Macy zc.zc_cleanup_fd = di->cleanupfd; 472eda14cbcSMatt Macy 473eda14cbcSMatt Macy if (zfs_ioctl(hdl, ZFS_IOC_TMP_SNAPSHOT, &zc) != 0) { 474eda14cbcSMatt Macy int err = errno; 475eda14cbcSMatt Macy if (err == EPERM) { 476eda14cbcSMatt Macy (void) snprintf(di->errbuf, sizeof (di->errbuf), 477eda14cbcSMatt Macy dgettext(TEXT_DOMAIN, "The diff delegated " 478eda14cbcSMatt Macy "permission is needed in order\nto create a " 479eda14cbcSMatt Macy "just-in-time snapshot for diffing\n")); 480eda14cbcSMatt Macy return (zfs_error(hdl, EZFS_DIFF, di->errbuf)); 481eda14cbcSMatt Macy } else { 482eda14cbcSMatt Macy (void) snprintf(di->errbuf, sizeof (di->errbuf), 483eda14cbcSMatt Macy dgettext(TEXT_DOMAIN, "Cannot create just-in-time " 484eda14cbcSMatt Macy "snapshot of '%s'"), zc.zc_name); 485eda14cbcSMatt Macy return (zfs_standard_error(hdl, err, di->errbuf)); 486eda14cbcSMatt Macy } 487eda14cbcSMatt Macy } 488eda14cbcSMatt Macy 489eda14cbcSMatt Macy di->tmpsnap = zfs_strdup(hdl, zc.zc_value); 490eda14cbcSMatt Macy di->tosnap = zfs_asprintf(hdl, "%s@%s", di->ds, di->tmpsnap); 491eda14cbcSMatt Macy return (0); 492eda14cbcSMatt Macy } 493eda14cbcSMatt Macy 494eda14cbcSMatt Macy static void 495eda14cbcSMatt Macy teardown_differ_info(differ_info_t *di) 496eda14cbcSMatt Macy { 497eda14cbcSMatt Macy free(di->ds); 498eda14cbcSMatt Macy free(di->dsmnt); 499eda14cbcSMatt Macy free(di->fromsnap); 500eda14cbcSMatt Macy free(di->frommnt); 501eda14cbcSMatt Macy free(di->tosnap); 502eda14cbcSMatt Macy free(di->tmpsnap); 503eda14cbcSMatt Macy free(di->tomnt); 504eda14cbcSMatt Macy (void) close(di->cleanupfd); 505eda14cbcSMatt Macy } 506eda14cbcSMatt Macy 507eda14cbcSMatt Macy static int 508eda14cbcSMatt Macy get_snapshot_names(differ_info_t *di, const char *fromsnap, 509eda14cbcSMatt Macy const char *tosnap) 510eda14cbcSMatt Macy { 511eda14cbcSMatt Macy libzfs_handle_t *hdl = di->zhp->zfs_hdl; 512eda14cbcSMatt Macy char *atptrf = NULL; 513eda14cbcSMatt Macy char *atptrt = NULL; 514eda14cbcSMatt Macy int fdslen, fsnlen; 515eda14cbcSMatt Macy int tdslen, tsnlen; 516eda14cbcSMatt Macy 517eda14cbcSMatt Macy /* 518eda14cbcSMatt Macy * Can accept 519eda14cbcSMatt Macy * fdslen fsnlen tdslen tsnlen 520eda14cbcSMatt Macy * dataset@snap1 521eda14cbcSMatt Macy * 0. dataset@snap1 dataset@snap2 >0 >1 >0 >1 522eda14cbcSMatt Macy * 1. dataset@snap1 @snap2 >0 >1 ==0 >1 523eda14cbcSMatt Macy * 2. dataset@snap1 dataset >0 >1 >0 ==0 524eda14cbcSMatt Macy * 3. @snap1 dataset@snap2 ==0 >1 >0 >1 525eda14cbcSMatt Macy * 4. @snap1 dataset ==0 >1 >0 ==0 526eda14cbcSMatt Macy */ 527eda14cbcSMatt Macy if (tosnap == NULL) { 528eda14cbcSMatt Macy /* only a from snapshot given, must be valid */ 529eda14cbcSMatt Macy (void) snprintf(di->errbuf, sizeof (di->errbuf), 530eda14cbcSMatt Macy dgettext(TEXT_DOMAIN, 531eda14cbcSMatt Macy "Badly formed snapshot name %s"), fromsnap); 532eda14cbcSMatt Macy 533eda14cbcSMatt Macy if (!zfs_validate_name(hdl, fromsnap, ZFS_TYPE_SNAPSHOT, 534eda14cbcSMatt Macy B_FALSE)) { 535eda14cbcSMatt Macy return (zfs_error(hdl, EZFS_INVALIDNAME, 536eda14cbcSMatt Macy di->errbuf)); 537eda14cbcSMatt Macy } 538eda14cbcSMatt Macy 539eda14cbcSMatt Macy atptrf = strchr(fromsnap, '@'); 540eda14cbcSMatt Macy ASSERT(atptrf != NULL); 541eda14cbcSMatt Macy fdslen = atptrf - fromsnap; 542eda14cbcSMatt Macy 543eda14cbcSMatt Macy di->fromsnap = zfs_strdup(hdl, fromsnap); 544eda14cbcSMatt Macy di->ds = zfs_strdup(hdl, fromsnap); 545eda14cbcSMatt Macy di->ds[fdslen] = '\0'; 546eda14cbcSMatt Macy 547eda14cbcSMatt Macy /* the to snap will be a just-in-time snap of the head */ 548eda14cbcSMatt Macy return (make_temp_snapshot(di)); 549eda14cbcSMatt Macy } 550eda14cbcSMatt Macy 551eda14cbcSMatt Macy (void) snprintf(di->errbuf, sizeof (di->errbuf), 552eda14cbcSMatt Macy dgettext(TEXT_DOMAIN, 553eda14cbcSMatt Macy "Unable to determine which snapshots to compare")); 554eda14cbcSMatt Macy 555eda14cbcSMatt Macy atptrf = strchr(fromsnap, '@'); 556eda14cbcSMatt Macy atptrt = strchr(tosnap, '@'); 557eda14cbcSMatt Macy fdslen = atptrf ? atptrf - fromsnap : strlen(fromsnap); 558eda14cbcSMatt Macy tdslen = atptrt ? atptrt - tosnap : strlen(tosnap); 559eda14cbcSMatt Macy fsnlen = strlen(fromsnap) - fdslen; /* includes @ sign */ 560eda14cbcSMatt Macy tsnlen = strlen(tosnap) - tdslen; /* includes @ sign */ 561eda14cbcSMatt Macy 562eda14cbcSMatt Macy if (fsnlen <= 1 || tsnlen == 1 || (fdslen == 0 && tdslen == 0)) { 563eda14cbcSMatt Macy return (zfs_error(hdl, EZFS_INVALIDNAME, di->errbuf)); 564eda14cbcSMatt Macy } else if ((fdslen > 0 && tdslen > 0) && 565eda14cbcSMatt Macy ((tdslen != fdslen || strncmp(fromsnap, tosnap, fdslen) != 0))) { 566eda14cbcSMatt Macy /* 567eda14cbcSMatt Macy * not the same dataset name, might be okay if 568eda14cbcSMatt Macy * tosnap is a clone of a fromsnap descendant. 569eda14cbcSMatt Macy */ 570eda14cbcSMatt Macy char origin[ZFS_MAX_DATASET_NAME_LEN]; 571eda14cbcSMatt Macy zprop_source_t src; 572eda14cbcSMatt Macy zfs_handle_t *zhp; 573eda14cbcSMatt Macy 574eda14cbcSMatt Macy di->ds = zfs_alloc(di->zhp->zfs_hdl, tdslen + 1); 575be181ee2SMartin Matuska (void) strlcpy(di->ds, tosnap, tdslen + 1); 576eda14cbcSMatt Macy 577eda14cbcSMatt Macy zhp = zfs_open(hdl, di->ds, ZFS_TYPE_FILESYSTEM); 578eda14cbcSMatt Macy while (zhp != NULL) { 579eda14cbcSMatt Macy if (zfs_prop_get(zhp, ZFS_PROP_ORIGIN, origin, 580eda14cbcSMatt Macy sizeof (origin), &src, NULL, 0, B_FALSE) != 0) { 581eda14cbcSMatt Macy (void) zfs_close(zhp); 582eda14cbcSMatt Macy zhp = NULL; 583eda14cbcSMatt Macy break; 584eda14cbcSMatt Macy } 585eda14cbcSMatt Macy if (strncmp(origin, fromsnap, fsnlen) == 0) 586eda14cbcSMatt Macy break; 587eda14cbcSMatt Macy 588eda14cbcSMatt Macy (void) zfs_close(zhp); 589eda14cbcSMatt Macy zhp = zfs_open(hdl, origin, ZFS_TYPE_FILESYSTEM); 590eda14cbcSMatt Macy } 591eda14cbcSMatt Macy 592eda14cbcSMatt Macy if (zhp == NULL) { 593eda14cbcSMatt Macy (void) snprintf(di->errbuf, sizeof (di->errbuf), 594eda14cbcSMatt Macy dgettext(TEXT_DOMAIN, 595eda14cbcSMatt Macy "Not an earlier snapshot from the same fs")); 596eda14cbcSMatt Macy return (zfs_error(hdl, EZFS_INVALIDNAME, di->errbuf)); 597eda14cbcSMatt Macy } else { 598eda14cbcSMatt Macy (void) zfs_close(zhp); 599eda14cbcSMatt Macy } 600eda14cbcSMatt Macy 601eda14cbcSMatt Macy di->isclone = B_TRUE; 602eda14cbcSMatt Macy di->fromsnap = zfs_strdup(hdl, fromsnap); 603716fd348SMartin Matuska if (tsnlen) 604eda14cbcSMatt Macy di->tosnap = zfs_strdup(hdl, tosnap); 605716fd348SMartin Matuska else 606eda14cbcSMatt Macy return (make_temp_snapshot(di)); 607eda14cbcSMatt Macy } else { 608eda14cbcSMatt Macy int dslen = fdslen ? fdslen : tdslen; 609eda14cbcSMatt Macy 610eda14cbcSMatt Macy di->ds = zfs_alloc(hdl, dslen + 1); 611be181ee2SMartin Matuska (void) strlcpy(di->ds, fdslen ? fromsnap : tosnap, dslen + 1); 612eda14cbcSMatt Macy 613eda14cbcSMatt Macy di->fromsnap = zfs_asprintf(hdl, "%s%s", di->ds, atptrf); 614eda14cbcSMatt Macy if (tsnlen) { 615eda14cbcSMatt Macy di->tosnap = zfs_asprintf(hdl, "%s%s", di->ds, atptrt); 616eda14cbcSMatt Macy } else { 617eda14cbcSMatt Macy return (make_temp_snapshot(di)); 618eda14cbcSMatt Macy } 619eda14cbcSMatt Macy } 620eda14cbcSMatt Macy return (0); 621eda14cbcSMatt Macy } 622eda14cbcSMatt Macy 623eda14cbcSMatt Macy static int 624eda14cbcSMatt Macy get_mountpoint(differ_info_t *di, char *dsnm, char **mntpt) 625eda14cbcSMatt Macy { 626eda14cbcSMatt Macy boolean_t mounted; 627eda14cbcSMatt Macy 628eda14cbcSMatt Macy mounted = is_mounted(di->zhp->zfs_hdl, dsnm, mntpt); 629eda14cbcSMatt Macy if (mounted == B_FALSE) { 630eda14cbcSMatt Macy (void) snprintf(di->errbuf, sizeof (di->errbuf), 631eda14cbcSMatt Macy dgettext(TEXT_DOMAIN, 632eda14cbcSMatt Macy "Cannot diff an unmounted snapshot")); 633eda14cbcSMatt Macy return (zfs_error(di->zhp->zfs_hdl, EZFS_BADTYPE, di->errbuf)); 634eda14cbcSMatt Macy } 635eda14cbcSMatt Macy 636eda14cbcSMatt Macy /* Avoid a double slash at the beginning of root-mounted datasets */ 637eda14cbcSMatt Macy if (**mntpt == '/' && *(*mntpt + 1) == '\0') 638eda14cbcSMatt Macy **mntpt = '\0'; 639eda14cbcSMatt Macy return (0); 640eda14cbcSMatt Macy } 641eda14cbcSMatt Macy 642eda14cbcSMatt Macy static int 643eda14cbcSMatt Macy get_mountpoints(differ_info_t *di) 644eda14cbcSMatt Macy { 645eda14cbcSMatt Macy char *strptr; 646eda14cbcSMatt Macy char *frommntpt; 647eda14cbcSMatt Macy 648eda14cbcSMatt Macy /* 649eda14cbcSMatt Macy * first get the mountpoint for the parent dataset 650eda14cbcSMatt Macy */ 651eda14cbcSMatt Macy if (get_mountpoint(di, di->ds, &di->dsmnt) != 0) 652eda14cbcSMatt Macy return (-1); 653eda14cbcSMatt Macy 654eda14cbcSMatt Macy strptr = strchr(di->tosnap, '@'); 655eda14cbcSMatt Macy ASSERT3P(strptr, !=, NULL); 656eda14cbcSMatt Macy di->tomnt = zfs_asprintf(di->zhp->zfs_hdl, "%s%s%s", di->dsmnt, 657eda14cbcSMatt Macy ZDIFF_SNAPDIR, ++strptr); 658eda14cbcSMatt Macy 659eda14cbcSMatt Macy strptr = strchr(di->fromsnap, '@'); 660eda14cbcSMatt Macy ASSERT3P(strptr, !=, NULL); 661eda14cbcSMatt Macy 662eda14cbcSMatt Macy frommntpt = di->dsmnt; 663eda14cbcSMatt Macy if (di->isclone) { 664eda14cbcSMatt Macy char *mntpt; 665eda14cbcSMatt Macy int err; 666eda14cbcSMatt Macy 667eda14cbcSMatt Macy *strptr = '\0'; 668eda14cbcSMatt Macy err = get_mountpoint(di, di->fromsnap, &mntpt); 669eda14cbcSMatt Macy *strptr = '@'; 670eda14cbcSMatt Macy if (err != 0) 671eda14cbcSMatt Macy return (-1); 672eda14cbcSMatt Macy frommntpt = mntpt; 673eda14cbcSMatt Macy } 674eda14cbcSMatt Macy 675eda14cbcSMatt Macy di->frommnt = zfs_asprintf(di->zhp->zfs_hdl, "%s%s%s", frommntpt, 676eda14cbcSMatt Macy ZDIFF_SNAPDIR, ++strptr); 677eda14cbcSMatt Macy 678eda14cbcSMatt Macy if (di->isclone) 679eda14cbcSMatt Macy free(frommntpt); 680eda14cbcSMatt Macy 681eda14cbcSMatt Macy return (0); 682eda14cbcSMatt Macy } 683eda14cbcSMatt Macy 684eda14cbcSMatt Macy static int 685eda14cbcSMatt Macy setup_differ_info(zfs_handle_t *zhp, const char *fromsnap, 686eda14cbcSMatt Macy const char *tosnap, differ_info_t *di) 687eda14cbcSMatt Macy { 688eda14cbcSMatt Macy di->zhp = zhp; 689eda14cbcSMatt Macy 69016038816SMartin Matuska di->cleanupfd = open(ZFS_DEV, O_RDWR | O_CLOEXEC); 691eda14cbcSMatt Macy VERIFY(di->cleanupfd >= 0); 692eda14cbcSMatt Macy 693eda14cbcSMatt Macy if (get_snapshot_names(di, fromsnap, tosnap) != 0) 694eda14cbcSMatt Macy return (-1); 695eda14cbcSMatt Macy 696eda14cbcSMatt Macy if (get_mountpoints(di) != 0) 697eda14cbcSMatt Macy return (-1); 698eda14cbcSMatt Macy 699eda14cbcSMatt Macy if (find_shares_object(di) != 0) 700eda14cbcSMatt Macy return (-1); 701eda14cbcSMatt Macy 702eda14cbcSMatt Macy return (0); 703eda14cbcSMatt Macy } 704eda14cbcSMatt Macy 705eda14cbcSMatt Macy int 706eda14cbcSMatt Macy zfs_show_diffs(zfs_handle_t *zhp, int outfd, const char *fromsnap, 707eda14cbcSMatt Macy const char *tosnap, int flags) 708eda14cbcSMatt Macy { 709eda14cbcSMatt Macy zfs_cmd_t zc = {"\0"}; 7101f1e2261SMartin Matuska char errbuf[ERRBUFLEN]; 711eda14cbcSMatt Macy differ_info_t di = { 0 }; 712eda14cbcSMatt Macy pthread_t tid; 713eda14cbcSMatt Macy int pipefd[2]; 714eda14cbcSMatt Macy int iocerr; 715eda14cbcSMatt Macy 716eda14cbcSMatt Macy (void) snprintf(errbuf, sizeof (errbuf), 717eda14cbcSMatt Macy dgettext(TEXT_DOMAIN, "zfs diff failed")); 718eda14cbcSMatt Macy 719eda14cbcSMatt Macy if (setup_differ_info(zhp, fromsnap, tosnap, &di)) { 720eda14cbcSMatt Macy teardown_differ_info(&di); 721eda14cbcSMatt Macy return (-1); 722eda14cbcSMatt Macy } 723eda14cbcSMatt Macy 72416038816SMartin Matuska if (pipe2(pipefd, O_CLOEXEC)) { 72516038816SMartin Matuska zfs_error_aux(zhp->zfs_hdl, "%s", strerror(errno)); 726eda14cbcSMatt Macy teardown_differ_info(&di); 727eda14cbcSMatt Macy return (zfs_error(zhp->zfs_hdl, EZFS_PIPEFAILED, errbuf)); 728eda14cbcSMatt Macy } 729eda14cbcSMatt Macy 730eda14cbcSMatt Macy di.scripted = (flags & ZFS_DIFF_PARSEABLE); 731eda14cbcSMatt Macy di.classify = (flags & ZFS_DIFF_CLASSIFY); 732eda14cbcSMatt Macy di.timestamped = (flags & ZFS_DIFF_TIMESTAMP); 733681ce946SMartin Matuska di.no_mangle = (flags & ZFS_DIFF_NO_MANGLE); 734eda14cbcSMatt Macy 735eda14cbcSMatt Macy di.outputfd = outfd; 736eda14cbcSMatt Macy di.datafd = pipefd[0]; 737eda14cbcSMatt Macy 738eda14cbcSMatt Macy if (pthread_create(&tid, NULL, differ, &di)) { 73916038816SMartin Matuska zfs_error_aux(zhp->zfs_hdl, "%s", strerror(errno)); 740eda14cbcSMatt Macy (void) close(pipefd[0]); 741eda14cbcSMatt Macy (void) close(pipefd[1]); 742eda14cbcSMatt Macy teardown_differ_info(&di); 743eda14cbcSMatt Macy return (zfs_error(zhp->zfs_hdl, 744eda14cbcSMatt Macy EZFS_THREADCREATEFAILED, errbuf)); 745eda14cbcSMatt Macy } 746eda14cbcSMatt Macy 747eda14cbcSMatt Macy /* do the ioctl() */ 748eda14cbcSMatt Macy (void) strlcpy(zc.zc_value, di.fromsnap, strlen(di.fromsnap) + 1); 749eda14cbcSMatt Macy (void) strlcpy(zc.zc_name, di.tosnap, strlen(di.tosnap) + 1); 750eda14cbcSMatt Macy zc.zc_cookie = pipefd[1]; 751eda14cbcSMatt Macy 752eda14cbcSMatt Macy iocerr = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_DIFF, &zc); 753eda14cbcSMatt Macy if (iocerr != 0) { 754eda14cbcSMatt Macy (void) snprintf(errbuf, sizeof (errbuf), 755eda14cbcSMatt Macy dgettext(TEXT_DOMAIN, "Unable to obtain diffs")); 756eda14cbcSMatt Macy if (errno == EPERM) { 757eda14cbcSMatt Macy zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, 758eda14cbcSMatt Macy "\n The sys_mount privilege or diff delegated " 759eda14cbcSMatt Macy "permission is needed\n to execute the " 760eda14cbcSMatt Macy "diff ioctl")); 761eda14cbcSMatt Macy } else if (errno == EXDEV) { 762eda14cbcSMatt Macy zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, 763eda14cbcSMatt Macy "\n Not an earlier snapshot from the same fs")); 764eda14cbcSMatt Macy } else if (errno != EPIPE || di.zerr == 0) { 76516038816SMartin Matuska zfs_error_aux(zhp->zfs_hdl, "%s", strerror(errno)); 766eda14cbcSMatt Macy } 767eda14cbcSMatt Macy (void) close(pipefd[1]); 768eda14cbcSMatt Macy (void) pthread_cancel(tid); 769eda14cbcSMatt Macy (void) pthread_join(tid, NULL); 770eda14cbcSMatt Macy teardown_differ_info(&di); 771eda14cbcSMatt Macy if (di.zerr != 0 && di.zerr != EPIPE) { 77216038816SMartin Matuska zfs_error_aux(zhp->zfs_hdl, "%s", strerror(di.zerr)); 773eda14cbcSMatt Macy return (zfs_error(zhp->zfs_hdl, EZFS_DIFF, di.errbuf)); 774eda14cbcSMatt Macy } else { 775eda14cbcSMatt Macy return (zfs_error(zhp->zfs_hdl, EZFS_DIFFDATA, errbuf)); 776eda14cbcSMatt Macy } 777eda14cbcSMatt Macy } 778eda14cbcSMatt Macy 779eda14cbcSMatt Macy (void) close(pipefd[1]); 780eda14cbcSMatt Macy (void) pthread_join(tid, NULL); 781eda14cbcSMatt Macy 782eda14cbcSMatt Macy if (di.zerr != 0) { 78316038816SMartin Matuska zfs_error_aux(zhp->zfs_hdl, "%s", strerror(di.zerr)); 784eda14cbcSMatt Macy return (zfs_error(zhp->zfs_hdl, EZFS_DIFF, di.errbuf)); 785eda14cbcSMatt Macy } 786eda14cbcSMatt Macy teardown_differ_info(&di); 787eda14cbcSMatt Macy return (0); 788eda14cbcSMatt Macy } 789