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 <syslog.h>
  43 #include <ctype.h>
  44 #include <errno.h>
  45 #include <fcntl.h>
  46 #include <limits.h>
  47 #include <stdarg.h>
  48 #include <stdio.h>
  49 #include <stdlib.h>
  50 #include <string.h>
  51 #include <time.h>
  52 #include <unistd.h>
  53 #include <libnvpair.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         syslog(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                 syslog(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         (void) ctime_r(&t, tbuf, sizeof (tbuf));
 398         /* LINTED variable format specifier */
 399         (void) fprintf(fp, tbuf);
 400 }
 401 
 402 
 403 /*
 404  * dd_free
 405  *
 406  * Free the linked list of dumpdates entries.
 407  */
 408 static void
 409 dd_free(dumpdates_t *ddheadp)
 410 {
 411         dumpdates_t *save;
 412 
 413         if (!ddheadp)
 414                 return;
 415 
 416         ddheadp = ddheadp->dd_next;
 417         while (ddheadp) {
 418                 save = ddheadp->dd_next;
 419                 free(ddheadp);
 420                 ddheadp = save;
 421         }
 422 }
 423 
 424 
 425 /*
 426  * makedumpdate
 427  *
 428  * Make the dumpdate node based on the string buffer passed to it.
 429  */
 430 static int
 431 makedumpdate(dumpdates_t *ddp, char *tbuf)
 432 {
 433         char *nmp, *un_buf;
 434         int rv;
 435 
 436         /*
 437          * While parsing each line, if a line contains one of the
 438          * LBR-type levels, then checking the return value of
 439          * get_ddlevel() against negative values, it OK.  Because
 440          * neither of the 'F', 'A', 'I' nor 'D' have negative
 441          * ASCII value.
 442          */
 443         if (!ddp || !tbuf)
 444                 rv = -1;
 445         else if (!(nmp = get_ddname(&tbuf))) {
 446                 rv = -1;
 447                 syslog(LOG_ERR, "get_ddname failed 0x%p", nmp);
 448         } else if ((ddp->dd_level = get_ddlevel(&tbuf)) < 0) {
 449                 rv = -1;
 450                 syslog(LOG_ERR, "dd_level < 0 %d", ddp->dd_level);
 451         } else if (!(un_buf = get_ddate(&tbuf))) {
 452                 rv = -1;
 453                 syslog(LOG_ERR, "get_ddate failed 0x%p", un_buf);
 454         } else if (unctime(un_buf, &ddp->dd_ddate) < 0) {
 455                 rv = -1;
 456                 syslog(LOG_ERR, "unctime failed \"%s\"", un_buf);
 457         } else {
 458                 (void) strlcpy(ddp->dd_name, nmp, TLM_MAX_PATH_NAME);
 459                 rv = 0;
 460         }
 461 
 462         return (rv);
 463 }
 464 
 465 
 466 /*
 467  * getrecord
 468  *
 469  * Read a record of dumpdates file and parse it.
 470  * The records that span multiple lines are covered.
 471  *
 472  * Returns:
 473  *   0 on success
 474  *   < 0 on error
 475  */
 476 static int
 477 getrecord(FILE *fp, dumpdates_t *ddatep, int *recno)
 478 {
 479         char tbuf[BUFSIZ];
 480 
 481         if (!fp || !ddatep || !recno)
 482                 return (-1);
 483 
 484         do {
 485                 if (getaline(fp, tbuf, sizeof (tbuf)) != tbuf)
 486                         return (-1);
 487         } while (!*tbuf);
 488 
 489         if (makedumpdate(ddatep, tbuf) < 0)
 490                 syslog(LOG_ERR,
 491                     "Unknown intermediate format in %s, line %d", tbuf, *recno);
 492 
 493         (*recno)++;
 494 
 495         return (0);
 496 }
 497 
 498 
 499 /*
 500  * readdumptimes
 501  *
 502  * Read the dumpdates file and make a linked list of its entries.
 503  *
 504  * Returns:
 505  *   0 on success
 506  *   < 0 on error
 507  */
 508 static int
 509 readdumptimes(FILE *fp, dumpdates_t *ddheadp)
 510 {
 511         int recno;
 512         register struct dumpdates *ddwalk;
 513 
 514         if (!fp || !ddheadp)
 515                 return (-1);
 516 
 517         recno = 1;
 518         (void) memset((void *)ddheadp, 0, sizeof (*ddheadp));
 519         for (; ; ) {
 520                 ddwalk = ndmp_malloc(sizeof (*ddwalk));
 521                 if (!ddwalk)
 522                         return (-1);
 523 
 524                 if (getrecord(fp, ddwalk, &recno) < 0) {
 525                         free(ddwalk);
 526                         break;
 527                 }
 528 
 529                 ddwalk->dd_next = ddheadp->dd_next;
 530                 ddheadp->dd_next = ddwalk;
 531                 ddheadp = ddwalk;
 532         }
 533 
 534         return (0);
 535 }
 536 
 537 
 538 /*
 539  * dumprecout
 540  *
 541  * Print a record into the dumpdates file.
 542  */
 543 static void
 544 dumprecout(FILE *fp, dumpdates_t *ddp)
 545 {
 546         if (!ddp)
 547                 return;
 548 
 549         put_ddname(fp, ddp->dd_name);
 550         (void) fputc('\t', fp);
 551         put_ddlevel(fp, ddp->dd_level);
 552         (void) fputc('\t', fp);
 553         put_ddate(fp, ddp->dd_ddate);
 554 }
 555 
 556 
 557 /*
 558  * initdumptimes
 559  *
 560  * Open the dumpdates file and read it into memory.
 561  *
 562  * Returns:
 563  *   0 on success
 564  *   < 0 on error
 565  *
 566  */
 567 static int
 568 initdumptimes(dumpdates_t *ddheadp)
 569 {
 570         char fname[PATH_MAX];
 571         int rv;
 572         FILE *fp;
 573 
 574         if (!ddheadp)
 575                 return (-1);
 576 
 577         if (!ddates_pathname(fname))
 578                 return (-1);
 579 
 580         fp = fopen(fname, "r");
 581         if (!fp) {
 582                 if (errno != ENOENT) {
 583                         syslog(LOG_ERR, "Cannot read %s: %m.", fname);
 584                         return (-1);
 585                 }
 586                 /*
 587                  * Dumpdates does not exist, make an empty one.
 588                  */
 589                 syslog(LOG_DEBUG,
 590                     "No file `%s', making an empty one", fname);
 591 
 592                 fp = fopen(fname, "w");
 593                 if (!fp) {
 594                         syslog(LOG_ERR, "Cannot create %s: %m.", fname);
 595                         return (-1);
 596                 }
 597                 (void) fclose(fp);
 598 
 599                 fp = fopen(fname, "r");
 600                 if (!fp) {
 601                         syslog(LOG_ERR,
 602                             "Cannot read %s after creating it. %m.", fname);
 603                         return (-1);
 604                 }
 605         }
 606 
 607         rv = readdumptimes(fp, ddheadp);
 608         (void) fclose(fp);
 609 
 610         return (rv);
 611 }
 612 
 613 
 614 /*
 615  * putdumptime
 616  *
 617  * Put the record specified by path, level and backup date to the file.
 618  * Update the record if such entry already exists; append if not.
 619  *
 620  * Returns:
 621  *   0 on success
 622  *   < 0 on error
 623  */
 624 static int
 625 putdumptime(char *path, int level, time_t ddate)
 626 {
 627         int found;
 628         char fname[PATH_MAX], bakfname[PATH_MAX];
 629         FILE *rfp, *wfp;
 630         dumpdates_t ddhead, tmpdd;
 631         register dumpdates_t *ddp;
 632         int rv;
 633 
 634         if (!path)
 635                 return (-1);
 636 
 637         if (IS_LBR_BKTYPE(level)) {
 638                 syslog(LOG_DEBUG, "Lbr: [%s][%c][%u]", path, level, ddate);
 639         } else {
 640                 syslog(LOG_DEBUG, "[%s][%d][%u]", path, level, ddate);
 641         }
 642 
 643         if (!ddates_pathname(fname)) {
 644                 syslog(LOG_ERR, "Cannot get dumpdate file path name.");
 645                 return (-1);
 646         }
 647 
 648         rfp = fopen(fname, "r");
 649         if (!rfp) {
 650                 syslog(LOG_DEBUG, "Creating %s.", fname);
 651                 (void) memset((void *)&ddhead, 0, sizeof (ddhead));
 652                 if (initdumptimes(&ddhead) < 0) {
 653                         syslog(LOG_ERR, "Could not initialize %s.",
 654                             NDMP_DUMPDATES);
 655                         dd_free(&ddhead);
 656                         return (-1);
 657                 }
 658         } else {
 659                 rv = readdumptimes(rfp, &ddhead);
 660 
 661                 if (rv < 0) {
 662                         syslog(LOG_ERR, "Error reading dumpdates file.");
 663                         (void) fclose(rfp);
 664                         dd_free(&ddhead);
 665                         return (-1);
 666                 }
 667                 (void) fclose(rfp);
 668         }
 669 
 670         (void) snprintf(bakfname, PATH_MAX, "%s.bak", fname);
 671         wfp = fopen(bakfname, "w");
 672         if (!wfp) {
 673                 syslog(LOG_ERR, "Cannot open %s: %m.", bakfname);
 674                 dd_free(&ddhead);
 675                 return (-1);
 676         }
 677 
 678         syslog(LOG_DEBUG, "[%s][%s]", fname, bakfname);
 679 
 680         /* try to locate the entry in the file */
 681         found = 0;
 682         for (ddp = ddhead.dd_next; ddp; ddp = ddp->dd_next) {
 683                 if (ddp->dd_level != level)
 684                         continue;
 685                 if (strcmp(path, ddp->dd_name))
 686                         continue;
 687 
 688                 syslog(LOG_DEBUG, "Found: [%s][%d][%u]",
 689                     ddp->dd_name, ddp->dd_level, ddp->dd_ddate);
 690 
 691                 /* update the record for the entry */
 692                 found = 1;
 693                 ddp->dd_ddate = ddate;
 694 
 695                 syslog(LOG_DEBUG,
 696                     "Updated to: [%s][%d][%u]",
 697                     ddp->dd_name, ddp->dd_level, ddp->dd_ddate);
 698         }
 699 
 700         /* dump all the read records */
 701         for (ddp = ddhead.dd_next; ddp; ddp = ddp->dd_next)
 702                 dumprecout(wfp, ddp);
 703 
 704         dd_free(&ddhead);
 705 
 706         /* append a new record */
 707         if (!found) {
 708                 (void) strlcpy(tmpdd.dd_name, path, TLM_MAX_PATH_NAME);
 709                 tmpdd.dd_level = level;
 710                 tmpdd.dd_ddate = ddate;
 711                 dumprecout(wfp, &tmpdd);
 712         }
 713 
 714         (void) fclose(wfp);
 715         (void) rename(bakfname, fname);
 716 
 717         return (0);
 718 }
 719 
 720 
 721 /*
 722  * append_dumptime
 723  *
 724  * Append the record specified by path, level and backup date to the file.
 725  */
 726 static int
 727 append_dumptime(char *fname, char *path, int level, time_t ddate)
 728 {
 729         char fpath[PATH_MAX], bakfpath[PATH_MAX];
 730         FILE *fp;
 731         dumpdates_t tmpdd;
 732 
 733         if (!fname || !*fname || !path || !*path)
 734                 return (-1);
 735 
 736         if (IS_LBR_BKTYPE(level & 0xff)) {
 737                 syslog(LOG_DEBUG,
 738                     "Lbr: [%s][%s][%c][%u]",
 739                     fname, path, level, ddate);
 740         } else
 741                 syslog(LOG_DEBUG, "[%s][%s][%d][%u]",
 742                     fname, path, level, ddate);
 743 
 744         if (!ndmpd_make_bk_dir_path(fpath, fname)) {
 745                 syslog(LOG_ERR, "Cannot get dumpdate file path name %s.",
 746                     fname);
 747                 return (-1);
 748         }
 749 
 750         (void) snprintf(bakfpath, PATH_MAX, "%s.bak", fpath);
 751 
 752         /*
 753          * If the file is there and can be opened then make a
 754          * backup copy it.
 755          */
 756         fp = fopen(fpath, "r");
 757         if (fp) {
 758                 (void) fclose(fp);
 759                 if (filecopy(bakfpath, fpath) != 0) {
 760                         syslog(LOG_ERR, "Cannot copy %s to %s: %m.",
 761                             fpath, bakfpath);
 762                         return (-1);
 763                 }
 764         }
 765 
 766         /* open the new copy to append the record to it */
 767         fp = fopen(bakfpath, "a");
 768         if (!fp) {
 769                 syslog(LOG_ERR, "Cannot open %s: %m.", bakfpath);
 770                 return (-1);
 771         }
 772 
 773         syslog(LOG_DEBUG, "[%s][%s]", fpath, bakfpath);
 774 
 775         /* append a new record */
 776         (void) strlcpy(tmpdd.dd_name, path, TLM_MAX_PATH_NAME);
 777         tmpdd.dd_level = level;
 778         tmpdd.dd_ddate = ddate;
 779         dumprecout(fp, &tmpdd);
 780 
 781         (void) fclose(fp);
 782         (void) rename(bakfpath, fpath);
 783 
 784         return (0);
 785 }
 786 
 787 
 788 /*
 789  * find_date
 790  *
 791  * Find the specified date
 792  */
 793 static dumpdates_t *
 794 find_date(dumpdates_t *ddp, char *path, int level, time_t t)
 795 {
 796         for (; ddp; ddp = ddp->dd_next)
 797                 if (ddp->dd_level == level && ddp->dd_ddate > t &&
 798                     strcmp(path, ddp->dd_name) == 0)
 799                         break;
 800 
 801         return (ddp);
 802 }
 803 
 804 
 805 /*
 806  * Get the dumpdate of the last level backup done on the path.
 807  * The last level normally is (level - 1) in case of NetBackup
 808  * but some DMAs allow that previous level could be anything
 809  * between 0 and the current level.
 810  *
 811  * Returns:
 812  *   0 on success
 813  *   < 0 on error
 814  */
 815 int
 816 ndmpd_get_dumptime(char *path, int *level, time_t *ddate)
 817 {
 818         int i;
 819         dumpdates_t ddhead, *ddp, *save;
 820         char vol[ZFS_MAX_DATASET_NAME_LEN];
 821         nvlist_t *userprops;
 822         zfs_handle_t *zhp;
 823         nvlist_t *propval = NULL;
 824         char *strval = NULL;
 825 
 826         if (!path || !level || !ddate)
 827                 return (-1);
 828 
 829         syslog(LOG_DEBUG, "[%s] level %d",
 830             path, *level);
 831 
 832         if (*level == 0) {
 833                 *ddate = (time_t)0;
 834                 return (0);
 835         }
 836 
 837         (void) mutex_lock(&zlib_mtx);
 838         /* Check if this is a ZFS dataset */
 839         if ((zlibh != NULL) &&
 840             (get_zfsvolname(vol, sizeof (vol), path) == 0) &&
 841             ((zhp = zfs_open(zlibh, vol, ZFS_TYPE_DATASET)) != NULL)) {
 842                 if ((userprops = zfs_get_user_props(zhp)) == NULL) {
 843                         *level = 0;
 844                         *ddate = (time_t)0;
 845                         zfs_close(zhp);
 846                         (void) mutex_unlock(&zlib_mtx);
 847                         return (0);
 848                 }
 849                 for (i = *level - 1; i >= 0; i--) {
 850                         if (nvlist_lookup_nvlist(userprops,
 851                             zfs_dumpdate_props[i], &propval) == 0) {
 852                                 *level = i;
 853                                 break;
 854                         }
 855                 }
 856                 if (propval == NULL ||
 857                     nvlist_lookup_string(propval, ZPROP_VALUE,
 858                     &strval) != 0) {
 859                         *level = 0;
 860                         *ddate = (time_t)0;
 861                         zfs_close(zhp);
 862                         (void) mutex_unlock(&zlib_mtx);
 863                         return (0);
 864                 }
 865                 if (unctime(strval, ddate) < 0) {
 866                         zfs_close(zhp);
 867                         (void) mutex_unlock(&zlib_mtx);
 868                         return (-1);
 869                 }
 870 
 871                 zfs_close(zhp);
 872                 (void) mutex_unlock(&zlib_mtx);
 873                 return (0);
 874         }
 875         (void) mutex_unlock(&zlib_mtx);
 876 
 877         (void) memset((void *)&ddhead, 0, sizeof (ddhead));
 878         if (initdumptimes(&ddhead) < 0) {
 879                 dd_free(&ddhead);
 880                 return (-1);
 881         }
 882 
 883         /*
 884          * Empty dumpdates file means level 0 for all paths.
 885          */
 886         if ((ddp = ddhead.dd_next) == 0) {
 887                 if (!IS_LBR_BKTYPE(*level & 0xff))
 888                         *level = 0;
 889                 *ddate = 0;
 890                 return (0);
 891         }
 892 
 893         /*
 894          * If it's not level backup, then find the exact record
 895          * type.
 896          */
 897         if (IS_LBR_BKTYPE(*level & 0xff)) {
 898                 save = find_date(ddp, path, *level, *ddate);
 899 
 900                 syslog(LOG_DEBUG,
 901                     "LBR_BKTYPE save 0x%p", save);
 902 
 903                 *ddate = save ? save->dd_ddate : (time_t)0;
 904         } else {
 905                 /*
 906                  * Go find the entry with the same name for a maximum of a
 907                  * lower increment and older date.
 908                  */
 909                 save = NULL;
 910                 for (i = *level - 1; i >= 0; i--) {
 911                         save = find_date(ddp, path, i, *ddate);
 912                         if (save) {
 913                                 *level = save->dd_level;
 914                                 *ddate = save->dd_ddate;
 915                                 break;
 916                         }
 917                 }
 918 
 919                 if (!save) {
 920                         *level = 0;
 921                         *ddate = (time_t)0;
 922                 }
 923         }
 924 
 925         dd_free(&ddhead);
 926 
 927         return (0);
 928 }
 929 
 930 
 931 /*
 932  * Put the date and the level of the back up for the
 933  * specified path in the dumpdates file.  If there is a line
 934  * for the same path and the same level, the date is updated.
 935  * Otherwise, a line is appended to the file.
 936  *
 937  * Returns:
 938  *   0 on success
 939  *   < 0 on error
 940  */
 941 int
 942 ndmpd_put_dumptime(char *path, int level, time_t ddate)
 943 {
 944         char vol[ZFS_MAX_DATASET_NAME_LEN];
 945         zfs_handle_t *zhp;
 946         char tbuf[64];
 947         int rv;
 948 
 949         syslog(LOG_DEBUG, "[%s][%d][%u]", path, level,
 950             ddate);
 951 
 952         /* Check if this is a ZFS dataset */
 953         (void) mutex_lock(&zlib_mtx);
 954         if ((zlibh != NULL) &&
 955             (get_zfsvolname(vol, sizeof (vol), path) == 0) &&
 956             ((zhp = zfs_open(zlibh, vol, ZFS_TYPE_DATASET)) != NULL)) {
 957 
 958                 (void) ctime_r(&ddate, tbuf, sizeof (tbuf));
 959                 rv = zfs_prop_set(zhp, zfs_dumpdate_props[level], tbuf);
 960                 zfs_close(zhp);
 961 
 962                 (void) mutex_unlock(&zlib_mtx);
 963                 return (rv);
 964         }
 965         (void) mutex_unlock(&zlib_mtx);
 966 
 967         (void) mutex_lock(&ndmp_dd_lock);
 968         rv = putdumptime(path, level, ddate);
 969         (void) mutex_unlock(&ndmp_dd_lock);
 970 
 971         return (rv);
 972 }
 973 
 974 
 975 /*
 976  * Append a backup date record to the specified file.
 977  */
 978 int
 979 ndmpd_append_dumptime(char *fname, char *path, int level, time_t ddate)
 980 {
 981         char vol[ZFS_MAX_DATASET_NAME_LEN];
 982         zfs_handle_t *zhp;
 983         char tbuf[64];
 984         int rv;
 985 
 986         syslog(LOG_DEBUG, "[%s][%s][%d][%u]", fname,
 987             path, level, ddate);
 988 
 989         /* Check if this is a ZFS dataset */
 990         (void) mutex_lock(&zlib_mtx);
 991         if ((zlibh != NULL) &&
 992             (get_zfsvolname(vol, sizeof (vol), path) == 0) &&
 993             ((zhp = zfs_open(zlibh, vol, ZFS_TYPE_DATASET)) != NULL)) {
 994 
 995                 (void) ctime_r(&ddate, tbuf, sizeof (tbuf));
 996                 rv = zfs_prop_set(zhp, zfs_dumpdate_props[level], tbuf);
 997                 zfs_close(zhp);
 998 
 999                 (void) mutex_unlock(&zlib_mtx);
1000                 return (rv);
1001         }
1002         (void) mutex_unlock(&zlib_mtx);
1003 
1004         (void) mutex_lock(&ndmp_dd_lock);
1005         rv = append_dumptime(fname, path, level, ddate);
1006         (void) mutex_unlock(&ndmp_dd_lock);
1007 
1008         return (rv);
1009 }