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