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 }