xref: /illumos-gate/usr/src/cmd/ndmpd/ndmp/ndmpd_dtime.c (revision f3012b5938cc7fcdee754ac9bb2817f86ebd9e92)
1 /*
2  * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
3  */
4 
5 /*
6  * BSD 3 Clause License
7  *
8  * Copyright (c) 2007, The Storage Networking Industry Association.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 	- Redistributions of source code must retain the above copyright
14  *	  notice, this list of conditions and the following disclaimer.
15  *
16  * 	- Redistributions in binary form must reproduce the above copyright
17  *	  notice, this list of conditions and the following disclaimer in
18  *	  the documentation and/or other materials provided with the
19  *	  distribution.
20  *
21  *	- Neither the name of The Storage Networking Industry Association (SNIA)
22  *	  nor the names of its contributors may be used to endorse or promote
23  *	  products derived from this software without specific prior written
24  *	  permission.
25  *
26  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
27  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
30  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36  * POSSIBILITY OF SUCH DAMAGE.
37  */
38 
39 #include <sys/param.h>
40 #include <sys/types.h>
41 #include <ctype.h>
42 #include <errno.h>
43 #include <fcntl.h>
44 #include <limits.h>
45 #include <stdarg.h>
46 #include <stdio.h>
47 #include <stdlib.h>
48 #include <string.h>
49 #include <time.h>
50 #include <unistd.h>
51 #include <libnvpair.h>
52 #include "ndmpd_log.h"
53 #include "ndmpd.h"
54 
55 /*
56  * The dumpdates file on file system.
57  */
58 #define	NDMP_DUMPDATES	"dumpdates"
59 
60 
61 /*
62  * Offsets into the ctime string to various parts.
63  */
64 #define	E_MONTH		4
65 #define	E_DAY		8
66 #define	E_HOUR		11
67 #define	E_MINUTE	14
68 #define	E_SECOND	17
69 #define	E_YEAR		20
70 
71 
72 /*
73  * The contents of the file dumpdates is maintained on a linked list.
74  */
75 typedef struct dumpdates {
76 	char dd_name[TLM_MAX_PATH_NAME];
77 	char dd_level;
78 	time_t dd_ddate;
79 	struct dumpdates *dd_next;
80 } dumpdates_t;
81 
82 
83 /*
84  * Month names used in ctime string.
85  */
86 static char months[] = "JanFebMarAprMayJunJulAugSepOctNovDec";
87 
88 
89 /*
90  * Binary lock for accessing the dumpdates file.
91  */
92 mutex_t ndmp_dd_lock = DEFAULTMUTEX;
93 
94 int ndmp_isdst = -1;
95 
96 char *zfs_dumpdate_props[] = {
97 	"dumpdates:level0",
98 	"dumpdates:level1",
99 	"dumpdates:level2",
100 	"dumpdates:level3",
101 	"dumpdates:level4",
102 	"dumpdates:level5",
103 	"dumpdates:level6",
104 	"dumpdates:level7",
105 	"dumpdates:level8",
106 	"dumpdates:level9",
107 };
108 
109 
110 /*
111  * lookup
112  *
113  * Look up the month (3-character) name and return its number.
114  *
115  * Returns -1 if the months name is not valid.
116  */
117 static int
118 lookup(char *str)
119 {
120 	register char *cp, *cp2;
121 
122 	if (!str)
123 		return (-1);
124 
125 	for (cp = months, cp2 = str; *cp != '\0'; cp += 3)
126 		if (strncmp(cp, cp2, 3) == 0)
127 			return ((cp-months) / 3);
128 	return (-1);
129 }
130 
131 
132 /*
133  * unctime
134  *
135  * Convert a ctime(3) format string into a system format date.
136  * Return the date thus calculated.
137  *
138  * Return -1 if the string is not in ctime format.
139  */
140 static int
141 unctime(char *str, time_t *t)
142 {
143 	struct tm then;
144 	char dbuf[26];
145 
146 	if (!str || !t)
147 		return (-1);
148 
149 	(void) memset(&then, 0, sizeof (then));
150 	(void) strlcpy(dbuf, str, sizeof (dbuf) - 1);
151 	dbuf[sizeof (dbuf) - 1] = '\0';
152 	dbuf[E_MONTH+3] = '\0';
153 	if ((then.tm_mon = lookup(&dbuf[E_MONTH])) < 0)
154 		return (-1);
155 
156 	then.tm_mday = atoi(&dbuf[E_DAY]);
157 	then.tm_hour = atoi(&dbuf[E_HOUR]);
158 	then.tm_min = atoi(&dbuf[E_MINUTE]);
159 	then.tm_sec = atoi(&dbuf[E_SECOND]);
160 	then.tm_year = atoi(&dbuf[E_YEAR]) - 1900;
161 	then.tm_isdst = ndmp_isdst;
162 
163 	NDMP_LOG(LOG_DEBUG,
164 	    "yday %d wday %d %d/%d/%d %02d:%02d:%02d",
165 	    then.tm_yday, then.tm_wday, then.tm_year, then.tm_mon,
166 	    then.tm_mday, then.tm_hour, then.tm_min, then.tm_sec);
167 
168 	*t = mktime(&then);
169 
170 	return (0);
171 }
172 
173 
174 /*
175  * ddates_pathname
176  *
177  * Create the dumpdates file full path name.
178  */
179 static char *
180 ddates_pathname(char *buf)
181 {
182 	return (ndmpd_make_bk_dir_path(buf, NDMP_DUMPDATES));
183 }
184 
185 
186 /*
187  * getline
188  *
189  * Get a line from the file and handle the continued lines.
190  */
191 static char *
192 getline(FILE *fp, char *line, int llen)
193 {
194 	char *save;
195 	int len;
196 
197 	if (!fp || !line)
198 		return (NULL);
199 
200 	*(save = line) = '\0';
201 	do {
202 		if (fgets(line, llen, fp) != line)
203 			return (NULL);
204 
205 		/* comment line? */
206 		if (*line == '#')
207 			continue;
208 
209 		len = strlen(line);
210 		/* short line */
211 		if (len <= 0)
212 			continue;
213 
214 		line += len-1;
215 		if (*line != '\n')
216 			return (NULL);
217 
218 		/* trim the trailing new line */
219 		*line = '\0';
220 		if (--len <= 0)
221 			break;
222 
223 		if (*(line-1) != '\\')
224 			break;
225 
226 		*(line-1) = '\n';
227 		llen -= len;
228 	} while (llen > 0);
229 
230 	return (save);
231 }
232 
233 
234 /*
235  * get_ddname
236  *
237  * Get the path name from the buffer passed.
238  *
239  * Returns the beginning of the path name.  The buffer pointer is moved
240  * forward to point to where the next field (the dump level) begins.
241  */
242 static char *
243 get_ddname(char **bpp)
244 {
245 	char *h, *t, *save;
246 
247 	if (!bpp || !*bpp)
248 		return (NULL);
249 
250 	*bpp += strspn(*bpp, "\t ");
251 	save = h = t = *bpp;
252 	while (*t) {
253 		if (*t == '\t' || *t == ' ') {
254 			/* consume the '\t' or space character */
255 			t++;
256 			break;
257 		}
258 
259 		if (*t == '\\')
260 			switch (*(t+1)) {
261 			case '\t':
262 			case ' ':
263 				t++; /* skip the '\\' */
264 			default:
265 				break;	/* nothing */
266 			}
267 
268 		*h++ = *t++;
269 	}
270 
271 	*bpp = t;
272 	*h++ = '\0';
273 	return (save);
274 }
275 
276 
277 /*
278  * get_ddlevel
279  *
280  * Get the dump level from the buffer passed.
281  *
282  * Returns the dump level found.  The buffer pointer is moved
283  * forward to point to where the next field (the dump date) begins.
284  */
285 static int
286 get_ddlevel(char **bpp)
287 {
288 	char *t, *save;
289 
290 	if (!bpp || !*bpp)
291 		return (-1);
292 
293 	*bpp += strspn(*bpp, "\t ");
294 	save = t = *bpp;
295 
296 	/*
297 	 * For 'F', 'A', 'I', and 'D' return the character itself.
298 	 */
299 	if (IS_LBR_BKTYPE(*t)) {
300 		NDMP_LOG(LOG_DEBUG, "Lbr bk type %c", *t);
301 		/*
302 		 * Skip the backup type character and null terminate the
303 		 * string.
304 		 */
305 		*++t = '\0';
306 		*bpp = ++t;
307 		return (toupper(*save));
308 	}
309 
310 	while (isdigit(*t))
311 		t++;
312 
313 	*t++ = '\0';
314 	*bpp = t;
315 	return (atoi(save));
316 }
317 
318 
319 /*
320  * get_ddate
321  *
322  * Get the dump date from the buffer passed.
323  *
324  * Returns the dump date string. The buffer pointer is moved
325  * forward.  It points to the end of the buffer now.
326  */
327 static char *
328 get_ddate(char **bpp)
329 {
330 	char *save;
331 
332 	if (!bpp || !*bpp)
333 		return (NULL);
334 
335 	*bpp += strspn(*bpp, "\t ");
336 	save = *bpp;
337 	*bpp += strlen(*bpp);
338 	return (save);
339 }
340 
341 
342 /*
343  * put_ddname
344  *
345  * Print the dump path name to the dumpdates file.  It escapes the space,
346  * '\t' and new line characters in the path name.  The same characters are
347  * considered in the get_ddname().
348  */
349 static void
350 put_ddname(FILE *fp, char *nm)
351 {
352 	if (!nm)
353 		return;
354 
355 	while (*nm)
356 		switch (*nm) {
357 		case ' ':
358 		case '\n':
359 		case '\t':
360 			(void) fputc('\\', fp);
361 			/* FALLTHROUGH */
362 		default:
363 			(void) fputc(*nm++, fp);
364 		}
365 }
366 
367 
368 /*
369  * put_ddlevel
370  *
371  * Print the dump level into the dumpdates file.
372  */
373 static void
374 put_ddlevel(FILE *fp, int level)
375 {
376 	if (!fp)
377 		return;
378 
379 	(void) fprintf(fp, IS_LBR_BKTYPE(level) ? "%c" : "%d", level);
380 }
381 
382 
383 /*
384  * put_ddate
385  *
386  * Print the dump date into the dumpdates file.
387  */
388 static void put_ddate(FILE *fp,
389 	time_t t)
390 {
391 	char tbuf[64];
392 
393 	if (!fp)
394 		return;
395 
396 	NDMP_LOG(LOG_DEBUG, "[%u]", t);
397 
398 	(void) ctime_r(&t, tbuf, sizeof (tbuf));
399 	/* LINTED variable format specifier */
400 	(void) fprintf(fp, tbuf);
401 }
402 
403 
404 /*
405  * dd_free
406  *
407  * Free the linked list of dumpdates entries.
408  */
409 static void
410 dd_free(dumpdates_t *ddheadp)
411 {
412 	dumpdates_t *save;
413 
414 	if (!ddheadp)
415 		return;
416 
417 	ddheadp = ddheadp->dd_next;
418 	while (ddheadp) {
419 		save = ddheadp->dd_next;
420 		free(ddheadp);
421 		ddheadp = save;
422 	}
423 }
424 
425 
426 /*
427  * makedumpdate
428  *
429  * Make the dumpdate node based on the string buffer passed to it.
430  */
431 static int
432 makedumpdate(dumpdates_t *ddp, char *tbuf)
433 {
434 	char *nmp, *un_buf;
435 	int rv;
436 
437 	/*
438 	 * While parsing each line, if a line contains one of the
439 	 * LBR-type levels, then checking the return value of
440 	 * get_ddlevel() against negative values, it OK.  Because
441 	 * neither of the 'F', 'A', 'I' nor 'D' have negative
442 	 * ASCII value.
443 	 */
444 	if (!ddp || !tbuf)
445 		rv = -1;
446 	else if (!(nmp = get_ddname(&tbuf))) {
447 		rv = -1;
448 		NDMP_LOG(LOG_DEBUG, "get_ddname failed 0x%p", nmp);
449 	} else if ((ddp->dd_level = get_ddlevel(&tbuf)) < 0) {
450 		rv = -1;
451 		NDMP_LOG(LOG_DEBUG, "dd_level < 0 %d", ddp->dd_level);
452 	} else if (!(un_buf = get_ddate(&tbuf))) {
453 		rv = -1;
454 		NDMP_LOG(LOG_DEBUG, "get_ddate failed 0x%p", un_buf);
455 	} else if (unctime(un_buf, &ddp->dd_ddate) < 0) {
456 		rv = -1;
457 		NDMP_LOG(LOG_DEBUG, "unctime failed \"%s\"", un_buf);
458 	} else {
459 		(void) strlcpy(ddp->dd_name, nmp, TLM_MAX_PATH_NAME);
460 		rv = 0;
461 	}
462 
463 	return (rv);
464 }
465 
466 
467 /*
468  * getrecord
469  *
470  * Read a record of dumpdates file and parse it.
471  * The records that span multiple lines are covered.
472  *
473  * Returns:
474  *   0 on success
475  *   < 0 on error
476  */
477 static int
478 getrecord(FILE *fp, dumpdates_t *ddatep, int *recno)
479 {
480 	char tbuf[BUFSIZ];
481 
482 	if (!fp || !ddatep || !recno)
483 		return (-1);
484 
485 	do {
486 		if (getline(fp, tbuf, sizeof (tbuf)) != tbuf)
487 			return (-1);
488 	} while (!*tbuf);
489 
490 	if (makedumpdate(ddatep, tbuf) < 0)
491 		NDMP_LOG(LOG_DEBUG,
492 		    "Unknown intermediate format in %s, line %d", tbuf, *recno);
493 
494 	(*recno)++;
495 
496 	if (IS_LBR_BKTYPE(ddatep->dd_level & 0xff)) {
497 		NDMP_LOG(LOG_DEBUG, "Lbr: [%s][%c][%u]",
498 		    ddatep->dd_name, ddatep->dd_level, ddatep->dd_ddate);
499 	} else
500 		NDMP_LOG(LOG_DEBUG, "[%s][%d][%u]",
501 		    ddatep->dd_name, ddatep->dd_level, ddatep->dd_ddate);
502 
503 	return (0);
504 }
505 
506 
507 /*
508  * readdumptimes
509  *
510  * Read the dumpdates file and make a linked list of its entries.
511  *
512  * Returns:
513  *   0 on success
514  *   < 0 on error
515  */
516 static int
517 readdumptimes(FILE *fp, dumpdates_t *ddheadp)
518 {
519 	int recno;
520 	register struct	dumpdates *ddwalk;
521 
522 	if (!fp || !ddheadp)
523 		return (-1);
524 
525 	recno = 1;
526 	(void) memset((void *)ddheadp, 0, sizeof (*ddheadp));
527 	for (; ; ) {
528 		ddwalk = ndmp_malloc(sizeof (*ddwalk));
529 		if (!ddwalk)
530 			return (-1);
531 
532 		if (getrecord(fp, ddwalk, &recno) < 0) {
533 			free(ddwalk);
534 			break;
535 		}
536 
537 		ddwalk->dd_next = ddheadp->dd_next;
538 		ddheadp->dd_next = ddwalk;
539 		ddheadp = ddwalk;
540 	}
541 
542 	return (0);
543 }
544 
545 
546 /*
547  * dumprecout
548  *
549  * Print a record into the dumpdates file.
550  */
551 static void
552 dumprecout(FILE *fp, dumpdates_t *ddp)
553 {
554 	if (!ddp)
555 		return;
556 
557 	if (IS_LBR_BKTYPE(ddp->dd_level)) {
558 		NDMP_LOG(LOG_DEBUG, "Lbr: [%s][%c][%u]",
559 		    ddp->dd_name, ddp->dd_level, ddp->dd_ddate);
560 	} else
561 		NDMP_LOG(LOG_DEBUG, "[%s][%d][%u]",
562 		    ddp->dd_name, ddp->dd_level, ddp->dd_ddate);
563 
564 	put_ddname(fp, ddp->dd_name);
565 	(void) fputc('\t', fp);
566 	put_ddlevel(fp, ddp->dd_level);
567 	(void) fputc('\t', fp);
568 	put_ddate(fp, ddp->dd_ddate);
569 }
570 
571 
572 /*
573  * initdumptimes
574  *
575  * Open the dumpdates file and read it into memory.
576  *
577  * Returns:
578  *   0 on success
579  *   < 0 on error
580  *
581  */
582 static int
583 initdumptimes(dumpdates_t *ddheadp)
584 {
585 	char fname[PATH_MAX];
586 	int rv;
587 	FILE *fp;
588 
589 	if (!ddheadp)
590 		return (-1);
591 
592 	if (!ddates_pathname(fname))
593 		return (-1);
594 
595 	fp = fopen(fname, "r");
596 	if (!fp) {
597 		if (errno != ENOENT) {
598 			NDMP_LOG(LOG_ERR, "Cannot read %s: %m.", fname);
599 			return (-1);
600 		}
601 		/*
602 		 * Dumpdates does not exist, make an empty one.
603 		 */
604 		NDMP_LOG(LOG_DEBUG,
605 		    "No file `%s', making an empty one", fname);
606 
607 		fp = fopen(fname, "w");
608 		if (!fp) {
609 			NDMP_LOG(LOG_ERR, "Cannot create %s: %m.", fname);
610 			return (-1);
611 		}
612 		(void) fclose(fp);
613 
614 		fp = fopen(fname, "r");
615 		if (!fp) {
616 			NDMP_LOG(LOG_ERR,
617 			    "Cannot read %s after creating it. %m.", fname);
618 			return (-1);
619 		}
620 	}
621 
622 	rv = readdumptimes(fp, ddheadp);
623 	(void) fclose(fp);
624 
625 	return (rv);
626 }
627 
628 
629 /*
630  * putdumptime
631  *
632  * Put the record specified by path, level and backup date to the file.
633  * Update the record if such entry already exists; append if not.
634  *
635  * Returns:
636  *   0 on success
637  *   < 0 on error
638  */
639 static int
640 putdumptime(char *path, int level, time_t ddate)
641 {
642 	int found;
643 	char fname[PATH_MAX], bakfname[PATH_MAX];
644 	FILE *rfp, *wfp;
645 	dumpdates_t ddhead, tmpdd;
646 	register dumpdates_t *ddp;
647 	int rv;
648 
649 	if (!path)
650 		return (-1);
651 
652 	if (IS_LBR_BKTYPE(level)) {
653 		NDMP_LOG(LOG_DEBUG, "Lbr: [%s][%c][%u]", path, level, ddate);
654 	} else {
655 		NDMP_LOG(LOG_DEBUG, "[%s][%d][%u]", path, level, ddate);
656 	}
657 
658 	if (!ddates_pathname(fname)) {
659 		NDMP_LOG(LOG_ERR, "Cannot get dumpdate file path name.");
660 		return (-1);
661 	}
662 
663 	rfp = fopen(fname, "r");
664 	if (!rfp) {
665 		NDMP_LOG(LOG_DEBUG, "Creating %s.", fname);
666 		(void) memset((void *)&ddhead, 0, sizeof (ddhead));
667 		if (initdumptimes(&ddhead) < 0) {
668 			NDMP_LOG(LOG_ERR, "Could not initialize %s.",
669 			    NDMP_DUMPDATES);
670 			dd_free(&ddhead);
671 			return (-1);
672 		}
673 	} else {
674 		rv = readdumptimes(rfp, &ddhead);
675 
676 		if (rv < 0) {
677 			NDMP_LOG(LOG_ERR, "Error reading dumpdates file.");
678 			(void) fclose(rfp);
679 			dd_free(&ddhead);
680 			return (-1);
681 		}
682 		(void) fclose(rfp);
683 	}
684 
685 	(void) snprintf(bakfname, PATH_MAX, "%s.bak", fname);
686 	wfp = fopen(bakfname, "w");
687 	if (!wfp) {
688 		NDMP_LOG(LOG_ERR, "Cannot open %s: %m.", bakfname);
689 		dd_free(&ddhead);
690 		return (-1);
691 	}
692 
693 	NDMP_LOG(LOG_DEBUG, "[%s][%s]", fname, bakfname);
694 
695 	/* try to locate the entry in the file */
696 	found = 0;
697 	for (ddp = ddhead.dd_next; ddp; ddp = ddp->dd_next) {
698 		if (ddp->dd_level != level)
699 			continue;
700 		if (strcmp(path, ddp->dd_name))
701 			continue;
702 
703 		NDMP_LOG(LOG_DEBUG, "Found: [%s][%d][%u]",
704 		    ddp->dd_name, ddp->dd_level, ddp->dd_ddate);
705 
706 		/* update the record for the entry */
707 		found = 1;
708 		ddp->dd_ddate = ddate;
709 
710 		NDMP_LOG(LOG_DEBUG,
711 		    "Updated to: [%s][%d][%u]",
712 		    ddp->dd_name, ddp->dd_level, ddp->dd_ddate);
713 	}
714 
715 	/* dump all the read records */
716 	for (ddp = ddhead.dd_next; ddp; ddp = ddp->dd_next)
717 		dumprecout(wfp, ddp);
718 
719 	dd_free(&ddhead);
720 
721 	/* append a new record */
722 	if (!found) {
723 		(void) strlcpy(tmpdd.dd_name, path, TLM_MAX_PATH_NAME);
724 		tmpdd.dd_level = level;
725 		tmpdd.dd_ddate = ddate;
726 		dumprecout(wfp, &tmpdd);
727 	}
728 
729 	(void) fclose(wfp);
730 	(void) rename(bakfname, fname);
731 
732 	return (0);
733 }
734 
735 
736 /*
737  * append_dumptime
738  *
739  * Append the record specified by path, level and backup date to the file.
740  */
741 static int
742 append_dumptime(char *fname, char *path, int level, time_t ddate)
743 {
744 	char fpath[PATH_MAX], bakfpath[PATH_MAX];
745 	FILE *fp;
746 	dumpdates_t tmpdd;
747 
748 	if (!fname || !*fname || !path || !*path)
749 		return (-1);
750 
751 	if (IS_LBR_BKTYPE(level & 0xff)) {
752 		NDMP_LOG(LOG_DEBUG,
753 		    "Lbr: [%s][%s][%c][%u]",
754 		    fname, path, level, ddate);
755 	} else
756 		NDMP_LOG(LOG_DEBUG, "[%s][%s][%d][%u]",
757 		    fname, path, level, ddate);
758 
759 	if (!ndmpd_make_bk_dir_path(fpath, fname)) {
760 		NDMP_LOG(LOG_ERR, "Cannot get dumpdate file path name %s.",
761 		    fname);
762 		return (-1);
763 	}
764 
765 	(void) snprintf(bakfpath, PATH_MAX, "%s.bak", fpath);
766 
767 	/*
768 	 * If the file is there and can be opened then make a
769 	 * backup copy it.
770 	 */
771 	fp = fopen(fpath, "r");
772 	if (fp) {
773 		(void) fclose(fp);
774 		if (filecopy(bakfpath, fpath) != 0) {
775 			NDMP_LOG(LOG_ERR, "Cannot copy %s to %s: %m.",
776 			    fpath, bakfpath);
777 			return (-1);
778 		}
779 	}
780 
781 	/* open the new copy to append the record to it */
782 	fp = fopen(bakfpath, "a");
783 	if (!fp) {
784 		NDMP_LOG(LOG_ERR, "Cannot open %s: %m.", bakfpath);
785 		return (-1);
786 	}
787 
788 	NDMP_LOG(LOG_DEBUG, "[%s][%s]", fpath, bakfpath);
789 
790 	/* append a new record */
791 	(void) strlcpy(tmpdd.dd_name, path, TLM_MAX_PATH_NAME);
792 	tmpdd.dd_level = level;
793 	tmpdd.dd_ddate = ddate;
794 	dumprecout(fp, &tmpdd);
795 
796 	(void) fclose(fp);
797 	(void) rename(bakfpath, fpath);
798 
799 	return (0);
800 }
801 
802 
803 /*
804  * find_date
805  *
806  * Find the specified date
807  */
808 static dumpdates_t *
809 find_date(dumpdates_t *ddp, char *path, int level, time_t t)
810 {
811 	for (; ddp; ddp = ddp->dd_next)
812 		if (ddp->dd_level == level && ddp->dd_ddate > t &&
813 		    strcmp(path, ddp->dd_name) == 0)
814 			break;
815 
816 	return (ddp);
817 }
818 
819 
820 /*
821  * Get the dumpdate of the last level backup done on the path.
822  * The last level normally is (level - 1) in case of NetBackup
823  * but some DMAs allow that previous level could be anything
824  * between 0 and the current level.
825  *
826  * Returns:
827  *   0 on success
828  *   < 0 on error
829  */
830 int
831 ndmpd_get_dumptime(char *path, int *level, time_t *ddate)
832 {
833 	int i;
834 	dumpdates_t ddhead, *ddp, *save;
835 	char vol[ZFS_MAXNAMELEN];
836 	nvlist_t *userprops;
837 	zfs_handle_t *zhp;
838 	nvlist_t *propval = NULL;
839 	char *strval = NULL;
840 
841 	if (!path || !level || !ddate)
842 		return (-1);
843 
844 	NDMP_LOG(LOG_DEBUG, "[%s] level %d",
845 	    path, *level);
846 
847 	if (*level == 0) {
848 		*ddate = (time_t)0;
849 		return (0);
850 	}
851 
852 	(void) mutex_lock(&zlib_mtx);
853 	/* Check if this is a ZFS dataset */
854 	if ((zlibh != NULL) &&
855 	    (get_zfsvolname(vol, sizeof (vol), path) == 0) &&
856 	    ((zhp = zfs_open(zlibh, vol, ZFS_TYPE_DATASET)) != NULL)) {
857 		if ((userprops = zfs_get_user_props(zhp)) == NULL) {
858 			*level = 0;
859 			*ddate = (time_t)0;
860 			zfs_close(zhp);
861 			(void) mutex_unlock(&zlib_mtx);
862 			return (0);
863 		}
864 		for (i = *level - 1; i >= 0; i--) {
865 			if (nvlist_lookup_nvlist(userprops,
866 			    zfs_dumpdate_props[i], &propval) == 0) {
867 				*level = i;
868 				break;
869 			}
870 		}
871 		if (propval == NULL ||
872 		    nvlist_lookup_string(propval, ZPROP_VALUE,
873 		    &strval) != 0) {
874 			*level = 0;
875 			*ddate = (time_t)0;
876 			zfs_close(zhp);
877 			(void) mutex_unlock(&zlib_mtx);
878 			return (0);
879 		}
880 		if (unctime(strval, ddate) < 0) {
881 			zfs_close(zhp);
882 			(void) mutex_unlock(&zlib_mtx);
883 			return (-1);
884 		}
885 
886 		zfs_close(zhp);
887 		(void) mutex_unlock(&zlib_mtx);
888 		return (0);
889 	}
890 	(void) mutex_unlock(&zlib_mtx);
891 
892 	(void) memset((void *)&ddhead, 0, sizeof (ddhead));
893 	if (initdumptimes(&ddhead) < 0) {
894 		dd_free(&ddhead);
895 		return (-1);
896 	}
897 
898 	/*
899 	 * Empty dumpdates file means level 0 for all paths.
900 	 */
901 	if ((ddp = ddhead.dd_next) == 0) {
902 		if (!IS_LBR_BKTYPE(*level & 0xff))
903 			*level = 0;
904 		*ddate = 0;
905 		return (0);
906 	}
907 
908 	/*
909 	 * If it's not level backup, then find the exact record
910 	 * type.
911 	 */
912 	if (IS_LBR_BKTYPE(*level & 0xff)) {
913 		save = find_date(ddp, path, *level, *ddate);
914 
915 		NDMP_LOG(LOG_DEBUG,
916 		    "LBR_BKTYPE save 0x%p", save);
917 
918 		*ddate = save ? save->dd_ddate : (time_t)0;
919 	} else {
920 		/*
921 		 * Go find the entry with the same name for a maximum of a
922 		 * lower increment and older date.
923 		 */
924 		save = NULL;
925 		for (i = *level - 1; i >= 0; i--) {
926 			save = find_date(ddp, path, i, *ddate);
927 			if (save) {
928 				*level = save->dd_level;
929 				*ddate = save->dd_ddate;
930 				break;
931 			}
932 		}
933 
934 		if (!save) {
935 			*level = 0;
936 			*ddate = (time_t)0;
937 		}
938 	}
939 
940 	dd_free(&ddhead);
941 
942 	return (0);
943 }
944 
945 
946 /*
947  * Put the date and the level of the back up for the
948  * specified path in the dumpdates file.  If there is a line
949  * for the same path and the same level, the date is updated.
950  * Otherwise, a line is appended to the file.
951  *
952  * Returns:
953  *   0 on success
954  *   < 0 on error
955  */
956 int
957 ndmpd_put_dumptime(char *path, int level, time_t ddate)
958 {
959 	char vol[ZFS_MAXNAMELEN];
960 	zfs_handle_t *zhp;
961 	char tbuf[64];
962 	int rv;
963 
964 	NDMP_LOG(LOG_DEBUG, "[%s][%d][%u]", path, level,
965 	    ddate);
966 
967 	/* Check if this is a ZFS dataset */
968 	(void) mutex_lock(&zlib_mtx);
969 	if ((zlibh != NULL) &&
970 	    (get_zfsvolname(vol, sizeof (vol), path) == 0) &&
971 	    ((zhp = zfs_open(zlibh, vol, ZFS_TYPE_DATASET)) != NULL)) {
972 
973 		(void) ctime_r(&ddate, tbuf, sizeof (tbuf));
974 		rv = zfs_prop_set(zhp, zfs_dumpdate_props[level], tbuf);
975 		zfs_close(zhp);
976 
977 		(void) mutex_unlock(&zlib_mtx);
978 		return (rv);
979 	}
980 	(void) mutex_unlock(&zlib_mtx);
981 
982 	(void) mutex_lock(&ndmp_dd_lock);
983 	rv = putdumptime(path, level, ddate);
984 	(void) mutex_unlock(&ndmp_dd_lock);
985 
986 	return (rv);
987 }
988 
989 
990 /*
991  * Append a backup date record to the specified file.
992  */
993 int
994 ndmpd_append_dumptime(char *fname, char *path, int level, time_t ddate)
995 {
996 	char vol[ZFS_MAXNAMELEN];
997 	zfs_handle_t *zhp;
998 	char tbuf[64];
999 	int rv;
1000 
1001 	NDMP_LOG(LOG_DEBUG, "[%s][%s][%d][%u]", fname,
1002 	    path, level, ddate);
1003 
1004 	/* Check if this is a ZFS dataset */
1005 	(void) mutex_lock(&zlib_mtx);
1006 	if ((zlibh != NULL) &&
1007 	    (get_zfsvolname(vol, sizeof (vol), path) == 0) &&
1008 	    ((zhp = zfs_open(zlibh, vol, ZFS_TYPE_DATASET)) != NULL)) {
1009 
1010 		(void) ctime_r(&ddate, tbuf, sizeof (tbuf));
1011 		rv = zfs_prop_set(zhp, zfs_dumpdate_props[level], tbuf);
1012 		zfs_close(zhp);
1013 
1014 		(void) mutex_unlock(&zlib_mtx);
1015 		return (rv);
1016 	}
1017 	(void) mutex_unlock(&zlib_mtx);
1018 
1019 	(void) mutex_lock(&ndmp_dd_lock);
1020 	rv = append_dumptime(fname, path, level, ddate);
1021 	(void) mutex_unlock(&ndmp_dd_lock);
1022 
1023 	return (rv);
1024 }
1025