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