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