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);
|