2 * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
3 * Copyright (c) 2015 by Delphix. All rights reserved.
9 * Copyright (c) 2007, The Storage Networking Industry Association.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
14 * - Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
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
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
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.
40 #include <sys/param.h>
41 #include <sys/types.h>
52 #include <libnvpair.h>
53 #include "ndmpd_log.h"
57 * The dumpdates file on file system.
59 #define NDMP_DUMPDATES "dumpdates"
63 * Offsets into the ctime string to various parts.
74 * The contents of the file dumpdates is maintained on a linked list.
76 typedef struct dumpdates
{
77 char dd_name
[TLM_MAX_PATH_NAME
];
80 struct dumpdates
*dd_next
;
85 * Month names used in ctime string.
87 static char months
[] = "JanFebMarAprMayJunJulAugSepOctNovDec";
91 * Binary lock for accessing the dumpdates file.
93 mutex_t ndmp_dd_lock
= DEFAULTMUTEX
;
97 char *zfs_dumpdate_props
[] = {
114 * Look up the month (3-character) name and return its number.
116 * Returns -1 if the months name is not valid.
121 register char *cp
, *cp2
;
126 for (cp
= months
, cp2
= str
; *cp
!= '\0'; cp
+= 3)
127 if (strncmp(cp
, cp2
, 3) == 0)
128 return ((cp
-months
) / 3);
136 * Convert a ctime(3) format string into a system format date.
137 * Return the date thus calculated.
139 * Return -1 if the string is not in ctime format.
142 unctime(char *str
, time_t *t
)
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)
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
;
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
);
178 * Create the dumpdates file full path name.
181 ddates_pathname(char *buf
)
183 return (ndmpd_make_bk_dir_path(buf
, NDMP_DUMPDATES
));
190 * Get a line from the file and handle the continued lines.
193 getaline(FILE *fp
, char *line
, int llen
)
201 *(save
= line
) = '\0';
203 if (fgets(line
, llen
, fp
) != line
)
219 /* trim the trailing new line */
224 if (*(line
-1) != '\\')
238 * Get the path name from the buffer passed.
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.
244 get_ddname(char **bpp
)
251 *bpp
+= strspn(*bpp
, "\t ");
254 if (*t
== '\t' || *t
== ' ') {
255 /* consume the '\t' or space character */
264 t
++; /* skip the '\\' */
281 * Get the dump level from the buffer passed.
283 * Returns the dump level found. The buffer pointer is moved
284 * forward to point to where the next field (the dump date) begins.
287 get_ddlevel(char **bpp
)
294 *bpp
+= strspn(*bpp
, "\t ");
298 * For 'F', 'A', 'I', and 'D' return the character itself.
300 if (IS_LBR_BKTYPE(*t
)) {
301 NDMP_LOG(LOG_DEBUG
, "Lbr bk type %c", *t
);
303 * Skip the backup type character and null terminate the
308 return (toupper(*save
));
323 * Get the dump date from the buffer passed.
325 * Returns the dump date string. The buffer pointer is moved
326 * forward. It points to the end of the buffer now.
329 get_ddate(char **bpp
)
336 *bpp
+= strspn(*bpp
, "\t ");
338 *bpp
+= strlen(*bpp
);
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().
351 put_ddname(FILE *fp
, char *nm
)
361 (void) fputc('\\', fp
);
364 (void) fputc(*nm
++, fp
);
372 * Print the dump level into the dumpdates file.
375 put_ddlevel(FILE *fp
, int level
)
380 (void) fprintf(fp
, IS_LBR_BKTYPE(level
) ? "%c" : "%d", level
);
387 * Print the dump date into the dumpdates file.
389 static void put_ddate(FILE *fp
,
397 NDMP_LOG(LOG_DEBUG
, "[%u]", t
);
399 (void) ctime_r(&t
, tbuf
, sizeof (tbuf
));
400 /* LINTED variable format specifier */
401 (void) fprintf(fp
, tbuf
);
408 * Free the linked list of dumpdates entries.
411 dd_free(dumpdates_t
*ddheadp
)
418 ddheadp
= ddheadp
->dd_next
;
420 save
= ddheadp
->dd_next
;
430 * Make the dumpdate node based on the string buffer passed to it.
433 makedumpdate(dumpdates_t
*ddp
, char *tbuf
)
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
447 else if (!(nmp
= get_ddname(&tbuf
))) {
449 NDMP_LOG(LOG_DEBUG
, "get_ddname failed 0x%p", nmp
);
450 } else if ((ddp
->dd_level
= get_ddlevel(&tbuf
)) < 0) {
452 NDMP_LOG(LOG_DEBUG
, "dd_level < 0 %d", ddp
->dd_level
);
453 } else if (!(un_buf
= get_ddate(&tbuf
))) {
455 NDMP_LOG(LOG_DEBUG
, "get_ddate failed 0x%p", un_buf
);
456 } else if (unctime(un_buf
, &ddp
->dd_ddate
) < 0) {
458 NDMP_LOG(LOG_DEBUG
, "unctime failed \"%s\"", un_buf
);
460 (void) strlcpy(ddp
->dd_name
, nmp
, TLM_MAX_PATH_NAME
);
471 * Read a record of dumpdates file and parse it.
472 * The records that span multiple lines are covered.
479 getrecord(FILE *fp
, dumpdates_t
*ddatep
, int *recno
)
483 if (!fp
|| !ddatep
|| !recno
)
487 if (getaline(fp
, tbuf
, sizeof (tbuf
)) != tbuf
)
491 if (makedumpdate(ddatep
, tbuf
) < 0)
493 "Unknown intermediate format in %s, line %d", tbuf
, *recno
);
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
);
501 NDMP_LOG(LOG_DEBUG
, "[%s][%d][%u]",
502 ddatep
->dd_name
, ddatep
->dd_level
, ddatep
->dd_ddate
);
511 * Read the dumpdates file and make a linked list of its entries.
518 readdumptimes(FILE *fp
, dumpdates_t
*ddheadp
)
521 register struct dumpdates
*ddwalk
;
527 (void) memset((void *)ddheadp
, 0, sizeof (*ddheadp
));
529 ddwalk
= ndmp_malloc(sizeof (*ddwalk
));
533 if (getrecord(fp
, ddwalk
, &recno
) < 0) {
538 ddwalk
->dd_next
= ddheadp
->dd_next
;
539 ddheadp
->dd_next
= ddwalk
;
550 * Print a record into the dumpdates file.
553 dumprecout(FILE *fp
, dumpdates_t
*ddp
)
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
);
562 NDMP_LOG(LOG_DEBUG
, "[%s][%d][%u]",
563 ddp
->dd_name
, ddp
->dd_level
, ddp
->dd_ddate
);
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
);
576 * Open the dumpdates file and read it into memory.
584 initdumptimes(dumpdates_t
*ddheadp
)
586 char fname
[PATH_MAX
];
593 if (!ddates_pathname(fname
))
596 fp
= fopen(fname
, "r");
598 if (errno
!= ENOENT
) {
599 NDMP_LOG(LOG_ERR
, "Cannot read %s: %m.", fname
);
603 * Dumpdates does not exist, make an empty one.
606 "No file `%s', making an empty one", fname
);
608 fp
= fopen(fname
, "w");
610 NDMP_LOG(LOG_ERR
, "Cannot create %s: %m.", fname
);
615 fp
= fopen(fname
, "r");
618 "Cannot read %s after creating it. %m.", fname
);
623 rv
= readdumptimes(fp
, ddheadp
);
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.
641 putdumptime(char *path
, int level
, time_t ddate
)
644 char fname
[PATH_MAX
], bakfname
[PATH_MAX
];
646 dumpdates_t ddhead
, tmpdd
;
647 register dumpdates_t
*ddp
;
653 if (IS_LBR_BKTYPE(level
)) {
654 NDMP_LOG(LOG_DEBUG
, "Lbr: [%s][%c][%u]", path
, level
, ddate
);
656 NDMP_LOG(LOG_DEBUG
, "[%s][%d][%u]", path
, level
, ddate
);
659 if (!ddates_pathname(fname
)) {
660 NDMP_LOG(LOG_ERR
, "Cannot get dumpdate file path name.");
664 rfp
= fopen(fname
, "r");
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.",
675 rv
= readdumptimes(rfp
, &ddhead
);
678 NDMP_LOG(LOG_ERR
, "Error reading dumpdates file.");
686 (void) snprintf(bakfname
, PATH_MAX
, "%s.bak", fname
);
687 wfp
= fopen(bakfname
, "w");
689 NDMP_LOG(LOG_ERR
, "Cannot open %s: %m.", bakfname
);
694 NDMP_LOG(LOG_DEBUG
, "[%s][%s]", fname
, bakfname
);
696 /* try to locate the entry in the file */
698 for (ddp
= ddhead
.dd_next
; ddp
; ddp
= ddp
->dd_next
) {
699 if (ddp
->dd_level
!= level
)
701 if (strcmp(path
, ddp
->dd_name
))
704 NDMP_LOG(LOG_DEBUG
, "Found: [%s][%d][%u]",
705 ddp
->dd_name
, ddp
->dd_level
, ddp
->dd_ddate
);
707 /* update the record for the entry */
709 ddp
->dd_ddate
= ddate
;
712 "Updated to: [%s][%d][%u]",
713 ddp
->dd_name
, ddp
->dd_level
, ddp
->dd_ddate
);
716 /* dump all the read records */
717 for (ddp
= ddhead
.dd_next
; ddp
; ddp
= ddp
->dd_next
)
718 dumprecout(wfp
, ddp
);
722 /* append a new record */
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
);
731 (void) rename(bakfname
, fname
);
740 * Append the record specified by path, level and backup date to the file.
743 append_dumptime(char *fname
, char *path
, int level
, time_t ddate
)
745 char fpath
[PATH_MAX
], bakfpath
[PATH_MAX
];
749 if (!fname
|| !*fname
|| !path
|| !*path
)
752 if (IS_LBR_BKTYPE(level
& 0xff)) {
754 "Lbr: [%s][%s][%c][%u]",
755 fname
, path
, level
, ddate
);
757 NDMP_LOG(LOG_DEBUG
, "[%s][%s][%d][%u]",
758 fname
, path
, level
, ddate
);
760 if (!ndmpd_make_bk_dir_path(fpath
, fname
)) {
761 NDMP_LOG(LOG_ERR
, "Cannot get dumpdate file path name %s.",
766 (void) snprintf(bakfpath
, PATH_MAX
, "%s.bak", fpath
);
769 * If the file is there and can be opened then make a
772 fp
= fopen(fpath
, "r");
775 if (filecopy(bakfpath
, fpath
) != 0) {
776 NDMP_LOG(LOG_ERR
, "Cannot copy %s to %s: %m.",
782 /* open the new copy to append the record to it */
783 fp
= fopen(bakfpath
, "a");
785 NDMP_LOG(LOG_ERR
, "Cannot open %s: %m.", bakfpath
);
789 NDMP_LOG(LOG_DEBUG
, "[%s][%s]", fpath
, bakfpath
);
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
);
798 (void) rename(bakfpath
, fpath
);
807 * Find the specified date
810 find_date(dumpdates_t
*ddp
, char *path
, int level
, time_t t
)
812 for (; ddp
; ddp
= ddp
->dd_next
)
813 if (ddp
->dd_level
== level
&& ddp
->dd_ddate
> t
&&
814 strcmp(path
, ddp
->dd_name
) == 0)
822 * Get the dumpdate of the last level backup done on the path.
823 * The last level normally is (level - 1) in case of NetBackup
824 * but some DMAs allow that previous level could be anything
825 * between 0 and the current level.
832 ndmpd_get_dumptime(char *path
, int *level
, time_t *ddate
)
835 dumpdates_t ddhead
, *ddp
, *save
;
836 char vol
[ZFS_MAX_DATASET_NAME_LEN
];
839 nvlist_t
*propval
= NULL
;
842 if (!path
|| !level
|| !ddate
)
845 NDMP_LOG(LOG_DEBUG
, "[%s] level %d",
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
) {
862 (void) mutex_unlock(&zlib_mtx
);
865 for (i
= *level
- 1; i
>= 0; i
--) {
866 if (nvlist_lookup_nvlist(userprops
,
867 zfs_dumpdate_props
[i
], &propval
) == 0) {
872 if (propval
== NULL
||
873 nvlist_lookup_string(propval
, ZPROP_VALUE
,
878 (void) mutex_unlock(&zlib_mtx
);
881 if (unctime(strval
, ddate
) < 0) {
883 (void) mutex_unlock(&zlib_mtx
);
888 (void) mutex_unlock(&zlib_mtx
);
891 (void) mutex_unlock(&zlib_mtx
);
893 (void) memset((void *)&ddhead
, 0, sizeof (ddhead
));
894 if (initdumptimes(&ddhead
) < 0) {
900 * Empty dumpdates file means level 0 for all paths.
902 if ((ddp
= ddhead
.dd_next
) == 0) {
903 if (!IS_LBR_BKTYPE(*level
& 0xff))
910 * If it's not level backup, then find the exact record
913 if (IS_LBR_BKTYPE(*level
& 0xff)) {
914 save
= find_date(ddp
, path
, *level
, *ddate
);
917 "LBR_BKTYPE save 0x%p", save
);
919 *ddate
= save
? save
->dd_ddate
: (time_t)0;
922 * Go find the entry with the same name for a maximum of a
923 * lower increment and older date.
926 for (i
= *level
- 1; i
>= 0; i
--) {
927 save
= find_date(ddp
, path
, i
, *ddate
);
929 *level
= save
->dd_level
;
930 *ddate
= save
->dd_ddate
;
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.
958 ndmpd_put_dumptime(char *path
, int level
, time_t ddate
)
960 char vol
[ZFS_MAX_DATASET_NAME_LEN
];
965 NDMP_LOG(LOG_DEBUG
, "[%s][%d][%u]", path
, level
,
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
)) {
974 (void) ctime_r(&ddate
, tbuf
, sizeof (tbuf
));
975 rv
= zfs_prop_set(zhp
, zfs_dumpdate_props
[level
], tbuf
);
978 (void) mutex_unlock(&zlib_mtx
);
981 (void) mutex_unlock(&zlib_mtx
);
983 (void) mutex_lock(&ndmp_dd_lock
);
984 rv
= putdumptime(path
, level
, ddate
);
985 (void) mutex_unlock(&ndmp_dd_lock
);
992 * Append a backup date record to the specified file.
995 ndmpd_append_dumptime(char *fname
, char *path
, int level
, time_t ddate
)
997 char vol
[ZFS_MAX_DATASET_NAME_LEN
];
1002 NDMP_LOG(LOG_DEBUG
, "[%s][%s][%d][%u]", fname
,
1003 path
, level
, ddate
);
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
)) {
1011 (void) ctime_r(&ddate
, tbuf
, sizeof (tbuf
));
1012 rv
= zfs_prop_set(zhp
, zfs_dumpdate_props
[level
], tbuf
);
1015 (void) mutex_unlock(&zlib_mtx
);
1018 (void) mutex_unlock(&zlib_mtx
);
1020 (void) mutex_lock(&ndmp_dd_lock
);
1021 rv
= append_dumptime(fname
, path
, level
, ddate
);
1022 (void) mutex_unlock(&ndmp_dd_lock
);