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