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