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 /*
 
 
 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 }
 
 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
 
 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;
 
 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)
 
 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);
 
 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 *
 
 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--) {
 
 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;
 
 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);
 | 
 
 
  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 /*
 
 
 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 }
 
 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
 
 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;
 
 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)
 
 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);
 
 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 *
 
 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--) {
 
 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;
 
 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);
 |