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