1 /* 2 * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. 3 * Copyright (c) 2015 by Delphix. All rights reserved. 4 */ 5 6 /* 7 * BSD 3 Clause License 8 * 9 * Copyright (c) 2007, The Storage Networking Industry Association. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * - Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 17 * - Redistributions in binary form must reproduce the above copyright 18 * notice, this list of conditions and the following disclaimer in 19 * the documentation and/or other materials provided with the 20 * distribution. 21 * 22 * - Neither the name of The Storage Networking Industry Association (SNIA) 23 * nor the names of its contributors may be used to endorse or promote 24 * products derived from this software without specific prior written 25 * permission. 26 * 27 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 28 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 30 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 31 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 32 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 33 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 34 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 35 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 36 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 37 * POSSIBILITY OF SUCH DAMAGE. 38 */ 39 40 #include <sys/param.h> 41 #include <sys/types.h> 42 #include <ctype.h> 43 #include <errno.h> 44 #include <fcntl.h> 45 #include <limits.h> 46 #include <stdarg.h> 47 #include <stdio.h> 48 #include <stdlib.h> 49 #include <string.h> 50 #include <time.h> 51 #include <unistd.h> 52 #include <libnvpair.h> 53 #include "ndmpd_log.h" 54 #include "ndmpd.h" 55 56 /* 57 * The dumpdates file on file system. 58 */ 59 #define NDMP_DUMPDATES "dumpdates" 60 61 62 /* 63 * Offsets into the ctime string to various parts. 64 */ 65 #define E_MONTH 4 66 #define E_DAY 8 67 #define E_HOUR 11 68 #define E_MINUTE 14 69 #define E_SECOND 17 70 #define E_YEAR 20 71 72 73 /* 74 * The contents of the file dumpdates is maintained on a linked list. 75 */ 76 typedef struct dumpdates { 77 char dd_name[TLM_MAX_PATH_NAME]; 78 char dd_level; 79 time_t dd_ddate; 80 struct dumpdates *dd_next; 81 } dumpdates_t; 82 83 84 /* 85 * Month names used in ctime string. 86 */ 87 static char months[] = "JanFebMarAprMayJunJulAugSepOctNovDec"; 88 89 90 /* 91 * Binary lock for accessing the dumpdates file. 92 */ 93 mutex_t ndmp_dd_lock = DEFAULTMUTEX; 94 95 int ndmp_isdst = -1; 96 97 char *zfs_dumpdate_props[] = { 98 "dumpdates:level0", 99 "dumpdates:level1", 100 "dumpdates:level2", 101 "dumpdates:level3", 102 "dumpdates:level4", 103 "dumpdates:level5", 104 "dumpdates:level6", 105 "dumpdates:level7", 106 "dumpdates:level8", 107 "dumpdates:level9", 108 }; 109 110 111 /* 112 * lookup 113 * 114 * Look up the month (3-character) name and return its number. 115 * 116 * Returns -1 if the months name is not valid. 117 */ 118 static int 119 lookup(char *str) 120 { 121 register char *cp, *cp2; 122 123 if (!str) 124 return (-1); 125 126 for (cp = months, cp2 = str; *cp != '\0'; cp += 3) 127 if (strncmp(cp, cp2, 3) == 0) 128 return ((cp-months) / 3); 129 return (-1); 130 } 131 132 133 /* 134 * unctime 135 * 136 * Convert a ctime(3) format string into a system format date. 137 * Return the date thus calculated. 138 * 139 * Return -1 if the string is not in ctime format. 140 */ 141 static int 142 unctime(char *str, time_t *t) 143 { 144 struct tm then; 145 char dbuf[26]; 146 147 if (!str || !t) 148 return (-1); 149 150 (void) memset(&then, 0, sizeof (then)); 151 (void) strlcpy(dbuf, str, sizeof (dbuf) - 1); 152 dbuf[sizeof (dbuf) - 1] = '\0'; 153 dbuf[E_MONTH+3] = '\0'; 154 if ((then.tm_mon = lookup(&dbuf[E_MONTH])) < 0) 155 return (-1); 156 157 then.tm_mday = atoi(&dbuf[E_DAY]); 158 then.tm_hour = atoi(&dbuf[E_HOUR]); 159 then.tm_min = atoi(&dbuf[E_MINUTE]); 160 then.tm_sec = atoi(&dbuf[E_SECOND]); 161 then.tm_year = atoi(&dbuf[E_YEAR]) - 1900; 162 then.tm_isdst = ndmp_isdst; 163 164 NDMP_LOG(LOG_DEBUG, 165 "yday %d wday %d %d/%d/%d %02d:%02d:%02d", 166 then.tm_yday, then.tm_wday, then.tm_year, then.tm_mon, 167 then.tm_mday, then.tm_hour, then.tm_min, then.tm_sec); 168 169 *t = mktime(&then); 170 171 return (0); 172 } 173 174 175 /* 176 * ddates_pathname 177 * 178 * Create the dumpdates file full path name. 179 */ 180 static char * 181 ddates_pathname(char *buf) 182 { 183 return (ndmpd_make_bk_dir_path(buf, NDMP_DUMPDATES)); 184 } 185 186 187 /* 188 * getaline 189 * 190 * Get a line from the file and handle the continued lines. 191 */ 192 static char * 193 getaline(FILE *fp, char *line, int llen) 194 { 195 char *save; 196 int len; 197 198 if (!fp || !line) 199 return (NULL); 200 201 *(save = line) = '\0'; 202 do { 203 if (fgets(line, llen, fp) != line) 204 return (NULL); 205 206 /* comment line? */ 207 if (*line == '#') 208 continue; 209 210 len = strlen(line); 211 /* short line */ 212 if (len <= 0) 213 continue; 214 215 line += len-1; 216 if (*line != '\n') 217 return (NULL); 218 219 /* trim the trailing new line */ 220 *line = '\0'; 221 if (--len <= 0) 222 break; 223 224 if (*(line-1) != '\\') 225 break; 226 227 *(line-1) = '\n'; 228 llen -= len; 229 } while (llen > 0); 230 231 return (save); 232 } 233 234 235 /* 236 * get_ddname 237 * 238 * Get the path name from the buffer passed. 239 * 240 * Returns the beginning of the path name. The buffer pointer is moved 241 * forward to point to where the next field (the dump level) begins. 242 */ 243 static char * 244 get_ddname(char **bpp) 245 { 246 char *h, *t, *save; 247 248 if (!bpp || !*bpp) 249 return (NULL); 250 251 *bpp += strspn(*bpp, "\t "); 252 save = h = t = *bpp; 253 while (*t) { 254 if (*t == '\t' || *t == ' ') { 255 /* consume the '\t' or space character */ 256 t++; 257 break; 258 } 259 260 if (*t == '\\') 261 switch (*(t+1)) { 262 case '\t': 263 case ' ': 264 t++; /* skip the '\\' */ 265 default: 266 break; /* nothing */ 267 } 268 269 *h++ = *t++; 270 } 271 272 *bpp = t; 273 *h++ = '\0'; 274 return (save); 275 } 276 277 278 /* 279 * get_ddlevel 280 * 281 * Get the dump level from the buffer passed. 282 * 283 * Returns the dump level found. The buffer pointer is moved 284 * forward to point to where the next field (the dump date) begins. 285 */ 286 static int 287 get_ddlevel(char **bpp) 288 { 289 char *t, *save; 290 291 if (!bpp || !*bpp) 292 return (-1); 293 294 *bpp += strspn(*bpp, "\t "); 295 save = t = *bpp; 296 297 /* 298 * For 'F', 'A', 'I', and 'D' return the character itself. 299 */ 300 if (IS_LBR_BKTYPE(*t)) { 301 NDMP_LOG(LOG_DEBUG, "Lbr bk type %c", *t); 302 /* 303 * Skip the backup type character and null terminate the 304 * string. 305 */ 306 *++t = '\0'; 307 *bpp = ++t; 308 return (toupper(*save)); 309 } 310 311 while (isdigit(*t)) 312 t++; 313 314 *t++ = '\0'; 315 *bpp = t; 316 return (atoi(save)); 317 } 318 319 320 /* 321 * get_ddate 322 * 323 * Get the dump date from the buffer passed. 324 * 325 * Returns the dump date string. The buffer pointer is moved 326 * forward. It points to the end of the buffer now. 327 */ 328 static char * 329 get_ddate(char **bpp) 330 { 331 char *save; 332 333 if (!bpp || !*bpp) 334 return (NULL); 335 336 *bpp += strspn(*bpp, "\t "); 337 save = *bpp; 338 *bpp += strlen(*bpp); 339 return (save); 340 } 341 342 343 /* 344 * put_ddname 345 * 346 * Print the dump path name to the dumpdates file. It escapes the space, 347 * '\t' and new line characters in the path name. The same characters are 348 * considered in the get_ddname(). 349 */ 350 static void 351 put_ddname(FILE *fp, char *nm) 352 { 353 if (!nm) 354 return; 355 356 while (*nm) 357 switch (*nm) { 358 case ' ': 359 case '\n': 360 case '\t': 361 (void) fputc('\\', fp); 362 /* FALLTHROUGH */ 363 default: 364 (void) fputc(*nm++, fp); 365 } 366 } 367 368 369 /* 370 * put_ddlevel 371 * 372 * Print the dump level into the dumpdates file. 373 */ 374 static void 375 put_ddlevel(FILE *fp, int level) 376 { 377 if (!fp) 378 return; 379 380 (void) fprintf(fp, IS_LBR_BKTYPE(level) ? "%c" : "%d", level); 381 } 382 383 384 /* 385 * put_ddate 386 * 387 * Print the dump date into the dumpdates file. 388 */ 389 static void put_ddate(FILE *fp, 390 time_t t) 391 { 392 char tbuf[64]; 393 394 if (!fp) 395 return; 396 397 NDMP_LOG(LOG_DEBUG, "[%u]", t); 398 399 (void) ctime_r(&t, tbuf, sizeof (tbuf)); 400 /* LINTED variable format specifier */ 401 (void) fprintf(fp, tbuf); 402 } 403 404 405 /* 406 * dd_free 407 * 408 * Free the linked list of dumpdates entries. 409 */ 410 static void 411 dd_free(dumpdates_t *ddheadp) 412 { 413 dumpdates_t *save; 414 415 if (!ddheadp) 416 return; 417 418 ddheadp = ddheadp->dd_next; 419 while (ddheadp) { 420 save = ddheadp->dd_next; 421 free(ddheadp); 422 ddheadp = save; 423 } 424 } 425 426 427 /* 428 * makedumpdate 429 * 430 * Make the dumpdate node based on the string buffer passed to it. 431 */ 432 static int 433 makedumpdate(dumpdates_t *ddp, char *tbuf) 434 { 435 char *nmp, *un_buf; 436 int rv; 437 438 /* 439 * While parsing each line, if a line contains one of the 440 * LBR-type levels, then checking the return value of 441 * get_ddlevel() against negative values, it OK. Because 442 * neither of the 'F', 'A', 'I' nor 'D' have negative 443 * ASCII value. 444 */ 445 if (!ddp || !tbuf) 446 rv = -1; 447 else if (!(nmp = get_ddname(&tbuf))) { 448 rv = -1; 449 NDMP_LOG(LOG_DEBUG, "get_ddname failed 0x%p", nmp); 450 } else if ((ddp->dd_level = get_ddlevel(&tbuf)) < 0) { 451 rv = -1; 452 NDMP_LOG(LOG_DEBUG, "dd_level < 0 %d", ddp->dd_level); 453 } else if (!(un_buf = get_ddate(&tbuf))) { 454 rv = -1; 455 NDMP_LOG(LOG_DEBUG, "get_ddate failed 0x%p", un_buf); 456 } else if (unctime(un_buf, &ddp->dd_ddate) < 0) { 457 rv = -1; 458 NDMP_LOG(LOG_DEBUG, "unctime failed \"%s\"", un_buf); 459 } else { 460 (void) strlcpy(ddp->dd_name, nmp, TLM_MAX_PATH_NAME); 461 rv = 0; 462 } 463 464 return (rv); 465 } 466 467 468 /* 469 * getrecord 470 * 471 * Read a record of dumpdates file and parse it. 472 * The records that span multiple lines are covered. 473 * 474 * Returns: 475 * 0 on success 476 * < 0 on error 477 */ 478 static int 479 getrecord(FILE *fp, dumpdates_t *ddatep, int *recno) 480 { 481 char tbuf[BUFSIZ]; 482 483 if (!fp || !ddatep || !recno) 484 return (-1); 485 486 do { 487 if (getaline(fp, tbuf, sizeof (tbuf)) != tbuf) 488 return (-1); 489 } while (!*tbuf); 490 491 if (makedumpdate(ddatep, tbuf) < 0) 492 NDMP_LOG(LOG_DEBUG, 493 "Unknown intermediate format in %s, line %d", tbuf, *recno); 494 495 (*recno)++; 496 497 if (IS_LBR_BKTYPE(ddatep->dd_level & 0xff)) { 498 NDMP_LOG(LOG_DEBUG, "Lbr: [%s][%c][%u]", 499 ddatep->dd_name, ddatep->dd_level, ddatep->dd_ddate); 500 } else 501 NDMP_LOG(LOG_DEBUG, "[%s][%d][%u]", 502 ddatep->dd_name, ddatep->dd_level, ddatep->dd_ddate); 503 504 return (0); 505 } 506 507 508 /* 509 * readdumptimes 510 * 511 * Read the dumpdates file and make a linked list of its entries. 512 * 513 * Returns: 514 * 0 on success 515 * < 0 on error 516 */ 517 static int 518 readdumptimes(FILE *fp, dumpdates_t *ddheadp) 519 { 520 int recno; 521 register struct dumpdates *ddwalk; 522 523 if (!fp || !ddheadp) 524 return (-1); 525 526 recno = 1; 527 (void) memset((void *)ddheadp, 0, sizeof (*ddheadp)); 528 for (; ; ) { 529 ddwalk = ndmp_malloc(sizeof (*ddwalk)); 530 if (!ddwalk) 531 return (-1); 532 533 if (getrecord(fp, ddwalk, &recno) < 0) { 534 free(ddwalk); 535 break; 536 } 537 538 ddwalk->dd_next = ddheadp->dd_next; 539 ddheadp->dd_next = ddwalk; 540 ddheadp = ddwalk; 541 } 542 543 return (0); 544 } 545 546 547 /* 548 * dumprecout 549 * 550 * Print a record into the dumpdates file. 551 */ 552 static void 553 dumprecout(FILE *fp, dumpdates_t *ddp) 554 { 555 if (!ddp) 556 return; 557 558 if (IS_LBR_BKTYPE(ddp->dd_level)) { 559 NDMP_LOG(LOG_DEBUG, "Lbr: [%s][%c][%u]", 560 ddp->dd_name, ddp->dd_level, ddp->dd_ddate); 561 } else 562 NDMP_LOG(LOG_DEBUG, "[%s][%d][%u]", 563 ddp->dd_name, ddp->dd_level, ddp->dd_ddate); 564 565 put_ddname(fp, ddp->dd_name); 566 (void) fputc('\t', fp); 567 put_ddlevel(fp, ddp->dd_level); 568 (void) fputc('\t', fp); 569 put_ddate(fp, ddp->dd_ddate); 570 } 571 572 573 /* 574 * initdumptimes 575 * 576 * Open the dumpdates file and read it into memory. 577 * 578 * Returns: 579 * 0 on success 580 * < 0 on error 581 * 582 */ 583 static int 584 initdumptimes(dumpdates_t *ddheadp) 585 { 586 char fname[PATH_MAX]; 587 int rv; 588 FILE *fp; 589 590 if (!ddheadp) 591 return (-1); 592 593 if (!ddates_pathname(fname)) 594 return (-1); 595 596 fp = fopen(fname, "r"); 597 if (!fp) { 598 if (errno != ENOENT) { 599 NDMP_LOG(LOG_ERR, "Cannot read %s: %m.", fname); 600 return (-1); 601 } 602 /* 603 * Dumpdates does not exist, make an empty one. 604 */ 605 NDMP_LOG(LOG_DEBUG, 606 "No file `%s', making an empty one", fname); 607 608 fp = fopen(fname, "w"); 609 if (!fp) { 610 NDMP_LOG(LOG_ERR, "Cannot create %s: %m.", fname); 611 return (-1); 612 } 613 (void) fclose(fp); 614 615 fp = fopen(fname, "r"); 616 if (!fp) { 617 NDMP_LOG(LOG_ERR, 618 "Cannot read %s after creating it. %m.", fname); 619 return (-1); 620 } 621 } 622 623 rv = readdumptimes(fp, ddheadp); 624 (void) fclose(fp); 625 626 return (rv); 627 } 628 629 630 /* 631 * putdumptime 632 * 633 * Put the record specified by path, level and backup date to the file. 634 * Update the record if such entry already exists; append if not. 635 * 636 * Returns: 637 * 0 on success 638 * < 0 on error 639 */ 640 static int 641 putdumptime(char *path, int level, time_t ddate) 642 { 643 int found; 644 char fname[PATH_MAX], bakfname[PATH_MAX]; 645 FILE *rfp, *wfp; 646 dumpdates_t ddhead, tmpdd; 647 register dumpdates_t *ddp; 648 int rv; 649 650 if (!path) 651 return (-1); 652 653 if (IS_LBR_BKTYPE(level)) { 654 NDMP_LOG(LOG_DEBUG, "Lbr: [%s][%c][%u]", path, level, ddate); 655 } else { 656 NDMP_LOG(LOG_DEBUG, "[%s][%d][%u]", path, level, ddate); 657 } 658 659 if (!ddates_pathname(fname)) { 660 NDMP_LOG(LOG_ERR, "Cannot get dumpdate file path name."); 661 return (-1); 662 } 663 664 rfp = fopen(fname, "r"); 665 if (!rfp) { 666 NDMP_LOG(LOG_DEBUG, "Creating %s.", fname); 667 (void) memset((void *)&ddhead, 0, sizeof (ddhead)); 668 if (initdumptimes(&ddhead) < 0) { 669 NDMP_LOG(LOG_ERR, "Could not initialize %s.", 670 NDMP_DUMPDATES); 671 dd_free(&ddhead); 672 return (-1); 673 } 674 } else { 675 rv = readdumptimes(rfp, &ddhead); 676 677 if (rv < 0) { 678 NDMP_LOG(LOG_ERR, "Error reading dumpdates file."); 679 (void) fclose(rfp); 680 dd_free(&ddhead); 681 return (-1); 682 } 683 (void) fclose(rfp); 684 } 685 686 (void) snprintf(bakfname, PATH_MAX, "%s.bak", fname); 687 wfp = fopen(bakfname, "w"); 688 if (!wfp) { 689 NDMP_LOG(LOG_ERR, "Cannot open %s: %m.", bakfname); 690 dd_free(&ddhead); 691 return (-1); 692 } 693 694 NDMP_LOG(LOG_DEBUG, "[%s][%s]", fname, bakfname); 695 696 /* try to locate the entry in the file */ 697 found = 0; 698 for (ddp = ddhead.dd_next; ddp; ddp = ddp->dd_next) { 699 if (ddp->dd_level != level) 700 continue; 701 if (strcmp(path, ddp->dd_name)) 702 continue; 703 704 NDMP_LOG(LOG_DEBUG, "Found: [%s][%d][%u]", 705 ddp->dd_name, ddp->dd_level, ddp->dd_ddate); 706 707 /* update the record for the entry */ 708 found = 1; 709 ddp->dd_ddate = ddate; 710 711 NDMP_LOG(LOG_DEBUG, 712 "Updated to: [%s][%d][%u]", 713 ddp->dd_name, ddp->dd_level, ddp->dd_ddate); 714 } 715 716 /* dump all the read records */ 717 for (ddp = ddhead.dd_next; ddp; ddp = ddp->dd_next) 718 dumprecout(wfp, ddp); 719 720 dd_free(&ddhead); 721 722 /* append a new record */ 723 if (!found) { 724 (void) strlcpy(tmpdd.dd_name, path, TLM_MAX_PATH_NAME); 725 tmpdd.dd_level = level; 726 tmpdd.dd_ddate = ddate; 727 dumprecout(wfp, &tmpdd); 728 } 729 730 (void) fclose(wfp); 731 (void) rename(bakfname, fname); 732 733 return (0); 734 } 735 736 737 /* 738 * append_dumptime 739 * 740 * Append the record specified by path, level and backup date to the file. 741 */ 742 static int 743 append_dumptime(char *fname, char *path, int level, time_t ddate) 744 { 745 char fpath[PATH_MAX], bakfpath[PATH_MAX]; 746 FILE *fp; 747 dumpdates_t tmpdd; 748 749 if (!fname || !*fname || !path || !*path) 750 return (-1); 751 752 if (IS_LBR_BKTYPE(level & 0xff)) { 753 NDMP_LOG(LOG_DEBUG, 754 "Lbr: [%s][%s][%c][%u]", 755 fname, path, level, ddate); 756 } else 757 NDMP_LOG(LOG_DEBUG, "[%s][%s][%d][%u]", 758 fname, path, level, ddate); 759 760 if (!ndmpd_make_bk_dir_path(fpath, fname)) { 761 NDMP_LOG(LOG_ERR, "Cannot get dumpdate file path name %s.", 762 fname); 763 return (-1); 764 } 765 766 (void) snprintf(bakfpath, PATH_MAX, "%s.bak", fpath); 767 768 /* 769 * If the file is there and can be opened then make a 770 * backup copy it. 771 */ 772 fp = fopen(fpath, "r"); 773 if (fp) { 774 (void) fclose(fp); 775 if (filecopy(bakfpath, fpath) != 0) { 776 NDMP_LOG(LOG_ERR, "Cannot copy %s to %s: %m.", 777 fpath, bakfpath); 778 return (-1); 779 } 780 } 781 782 /* open the new copy to append the record to it */ 783 fp = fopen(bakfpath, "a"); 784 if (!fp) { 785 NDMP_LOG(LOG_ERR, "Cannot open %s: %m.", bakfpath); 786 return (-1); 787 } 788 789 NDMP_LOG(LOG_DEBUG, "[%s][%s]", fpath, bakfpath); 790 791 /* append a new record */ 792 (void) strlcpy(tmpdd.dd_name, path, TLM_MAX_PATH_NAME); 793 tmpdd.dd_level = level; 794 tmpdd.dd_ddate = ddate; 795 dumprecout(fp, &tmpdd); 796 797 (void) fclose(fp); 798 (void) rename(bakfpath, fpath); 799 800 return (0); 801 } 802 803 804 /* 805 * find_date 806 * 807 * Find the specified date 808 */ 809 static dumpdates_t * 810 find_date(dumpdates_t *ddp, char *path, int level, time_t t) 811 { 812 for (; ddp; ddp = ddp->dd_next) 813 if (ddp->dd_level == level && ddp->dd_ddate > t && 814 strcmp(path, ddp->dd_name) == 0) 815 break; 816 817 return (ddp); 818 } 819 820 821 /* 822 * Get the dumpdate of the last level backup done on the path. 823 * The last level normally is (level - 1) in case of NetBackup 824 * but some DMAs allow that previous level could be anything 825 * between 0 and the current level. 826 * 827 * Returns: 828 * 0 on success 829 * < 0 on error 830 */ 831 int 832 ndmpd_get_dumptime(char *path, int *level, time_t *ddate) 833 { 834 int i; 835 dumpdates_t ddhead, *ddp, *save; 836 char vol[ZFS_MAX_DATASET_NAME_LEN]; 837 nvlist_t *userprops; 838 zfs_handle_t *zhp; 839 nvlist_t *propval = NULL; 840 char *strval = NULL; 841 842 if (!path || !level || !ddate) 843 return (-1); 844 845 NDMP_LOG(LOG_DEBUG, "[%s] level %d", 846 path, *level); 847 848 if (*level == 0) { 849 *ddate = (time_t)0; 850 return (0); 851 } 852 853 (void) mutex_lock(&zlib_mtx); 854 /* Check if this is a ZFS dataset */ 855 if ((zlibh != NULL) && 856 (get_zfsvolname(vol, sizeof (vol), path) == 0) && 857 ((zhp = zfs_open(zlibh, vol, ZFS_TYPE_DATASET)) != NULL)) { 858 if ((userprops = zfs_get_user_props(zhp)) == NULL) { 859 *level = 0; 860 *ddate = (time_t)0; 861 zfs_close(zhp); 862 (void) mutex_unlock(&zlib_mtx); 863 return (0); 864 } 865 for (i = *level - 1; i >= 0; i--) { 866 if (nvlist_lookup_nvlist(userprops, 867 zfs_dumpdate_props[i], &propval) == 0) { 868 *level = i; 869 break; 870 } 871 } 872 if (propval == NULL || 873 nvlist_lookup_string(propval, ZPROP_VALUE, 874 &strval) != 0) { 875 *level = 0; 876 *ddate = (time_t)0; 877 zfs_close(zhp); 878 (void) mutex_unlock(&zlib_mtx); 879 return (0); 880 } 881 if (unctime(strval, ddate) < 0) { 882 zfs_close(zhp); 883 (void) mutex_unlock(&zlib_mtx); 884 return (-1); 885 } 886 887 zfs_close(zhp); 888 (void) mutex_unlock(&zlib_mtx); 889 return (0); 890 } 891 (void) mutex_unlock(&zlib_mtx); 892 893 (void) memset((void *)&ddhead, 0, sizeof (ddhead)); 894 if (initdumptimes(&ddhead) < 0) { 895 dd_free(&ddhead); 896 return (-1); 897 } 898 899 /* 900 * Empty dumpdates file means level 0 for all paths. 901 */ 902 if ((ddp = ddhead.dd_next) == 0) { 903 if (!IS_LBR_BKTYPE(*level & 0xff)) 904 *level = 0; 905 *ddate = 0; 906 return (0); 907 } 908 909 /* 910 * If it's not level backup, then find the exact record 911 * type. 912 */ 913 if (IS_LBR_BKTYPE(*level & 0xff)) { 914 save = find_date(ddp, path, *level, *ddate); 915 916 NDMP_LOG(LOG_DEBUG, 917 "LBR_BKTYPE save 0x%p", save); 918 919 *ddate = save ? save->dd_ddate : (time_t)0; 920 } else { 921 /* 922 * Go find the entry with the same name for a maximum of a 923 * lower increment and older date. 924 */ 925 save = NULL; 926 for (i = *level - 1; i >= 0; i--) { 927 save = find_date(ddp, path, i, *ddate); 928 if (save) { 929 *level = save->dd_level; 930 *ddate = save->dd_ddate; 931 break; 932 } 933 } 934 935 if (!save) { 936 *level = 0; 937 *ddate = (time_t)0; 938 } 939 } 940 941 dd_free(&ddhead); 942 943 return (0); 944 } 945 946 947 /* 948 * Put the date and the level of the back up for the 949 * specified path in the dumpdates file. If there is a line 950 * for the same path and the same level, the date is updated. 951 * Otherwise, a line is appended to the file. 952 * 953 * Returns: 954 * 0 on success 955 * < 0 on error 956 */ 957 int 958 ndmpd_put_dumptime(char *path, int level, time_t ddate) 959 { 960 char vol[ZFS_MAX_DATASET_NAME_LEN]; 961 zfs_handle_t *zhp; 962 char tbuf[64]; 963 int rv; 964 965 NDMP_LOG(LOG_DEBUG, "[%s][%d][%u]", path, level, 966 ddate); 967 968 /* Check if this is a ZFS dataset */ 969 (void) mutex_lock(&zlib_mtx); 970 if ((zlibh != NULL) && 971 (get_zfsvolname(vol, sizeof (vol), path) == 0) && 972 ((zhp = zfs_open(zlibh, vol, ZFS_TYPE_DATASET)) != NULL)) { 973 974 (void) ctime_r(&ddate, tbuf, sizeof (tbuf)); 975 rv = zfs_prop_set(zhp, zfs_dumpdate_props[level], tbuf); 976 zfs_close(zhp); 977 978 (void) mutex_unlock(&zlib_mtx); 979 return (rv); 980 } 981 (void) mutex_unlock(&zlib_mtx); 982 983 (void) mutex_lock(&ndmp_dd_lock); 984 rv = putdumptime(path, level, ddate); 985 (void) mutex_unlock(&ndmp_dd_lock); 986 987 return (rv); 988 } 989 990 991 /* 992 * Append a backup date record to the specified file. 993 */ 994 int 995 ndmpd_append_dumptime(char *fname, char *path, int level, time_t ddate) 996 { 997 char vol[ZFS_MAX_DATASET_NAME_LEN]; 998 zfs_handle_t *zhp; 999 char tbuf[64]; 1000 int rv; 1001 1002 NDMP_LOG(LOG_DEBUG, "[%s][%s][%d][%u]", fname, 1003 path, level, ddate); 1004 1005 /* Check if this is a ZFS dataset */ 1006 (void) mutex_lock(&zlib_mtx); 1007 if ((zlibh != NULL) && 1008 (get_zfsvolname(vol, sizeof (vol), path) == 0) && 1009 ((zhp = zfs_open(zlibh, vol, ZFS_TYPE_DATASET)) != NULL)) { 1010 1011 (void) ctime_r(&ddate, tbuf, sizeof (tbuf)); 1012 rv = zfs_prop_set(zhp, zfs_dumpdate_props[level], tbuf); 1013 zfs_close(zhp); 1014 1015 (void) mutex_unlock(&zlib_mtx); 1016 return (rv); 1017 } 1018 (void) mutex_unlock(&zlib_mtx); 1019 1020 (void) mutex_lock(&ndmp_dd_lock); 1021 rv = append_dumptime(fname, path, level, ddate); 1022 (void) mutex_unlock(&ndmp_dd_lock); 1023 1024 return (rv); 1025 } 1026