xref: /illumos-gate/usr/src/cmd/ndmpd/ndmp/ndmpd_dtime.c (revision 9adfa60d484ce2435f5af77cc99dcd4e692b6660)
1 /*
2  * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
3  * Copyright (c) 2015 by Delphix. All rights reserved.
4  */
5 
6 /*
7  * BSD 3 Clause License
8  *
9  * Copyright (c) 2007, The Storage Networking Industry Association.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 	- Redistributions of source code must retain the above copyright
15  *	  notice, this list of conditions and the following disclaimer.
16  *
17  * 	- Redistributions in binary form must reproduce the above copyright
18  *	  notice, this list of conditions and the following disclaimer in
19  *	  the documentation and/or other materials provided with the
20  *	  distribution.
21  *
22  *	- Neither the name of The Storage Networking Industry Association (SNIA)
23  *	  nor the names of its contributors may be used to endorse or promote
24  *	  products derived from this software without specific prior written
25  *	  permission.
26  *
27  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
28  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
31  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
32  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
33  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
34  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
35  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
36  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
37  * POSSIBILITY OF SUCH DAMAGE.
38  */
39 
40 #include <sys/param.h>
41 #include <sys/types.h>
42 #include <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 /*
74  * The contents of the file dumpdates is maintained on a linked list.
75  */
76 typedef struct dumpdates {
77 	char dd_name[TLM_MAX_PATH_NAME];
78 	char dd_level;
79 	time_t dd_ddate;
80 	struct dumpdates *dd_next;
81 } dumpdates_t;
82 
83 
84 /*
85  * Month names used in ctime string.
86  */
87 static char months[] = "JanFebMarAprMayJunJulAugSepOctNovDec";
88 
89 
90 /*
91  * Binary lock for accessing the dumpdates file.
92  */
93 mutex_t ndmp_dd_lock = DEFAULTMUTEX;
94 
95 int ndmp_isdst = -1;
96 
97 char *zfs_dumpdate_props[] = {
98 	"dumpdates:level0",
99 	"dumpdates:level1",
100 	"dumpdates:level2",
101 	"dumpdates:level3",
102 	"dumpdates:level4",
103 	"dumpdates:level5",
104 	"dumpdates:level6",
105 	"dumpdates:level7",
106 	"dumpdates:level8",
107 	"dumpdates:level9",
108 };
109 
110 
111 /*
112  * lookup
113  *
114  * Look up the month (3-character) name and return its number.
115  *
116  * Returns -1 if the months name is not valid.
117  */
118 static int
lookup(char * str)119 lookup(char *str)
120 {
121 	register char *cp, *cp2;
122 
123 	if (!str)
124 		return (-1);
125 
126 	for (cp = months, cp2 = str; *cp != '\0'; cp += 3)
127 		if (strncmp(cp, cp2, 3) == 0)
128 			return ((cp-months) / 3);
129 	return (-1);
130 }
131 
132 
133 /*
134  * unctime
135  *
136  * Convert a ctime(3) format string into a system format date.
137  * Return the date thus calculated.
138  *
139  * Return -1 if the string is not in ctime format.
140  */
141 static int
unctime(char * str,time_t * t)142 unctime(char *str, time_t *t)
143 {
144 	struct tm then;
145 	char dbuf[26];
146 
147 	if (!str || !t)
148 		return (-1);
149 
150 	(void) memset(&then, 0, sizeof (then));
151 	(void) strlcpy(dbuf, str, sizeof (dbuf) - 1);
152 	dbuf[sizeof (dbuf) - 1] = '\0';
153 	dbuf[E_MONTH+3] = '\0';
154 	if ((then.tm_mon = lookup(&dbuf[E_MONTH])) < 0)
155 		return (-1);
156 
157 	then.tm_mday = atoi(&dbuf[E_DAY]);
158 	then.tm_hour = atoi(&dbuf[E_HOUR]);
159 	then.tm_min = atoi(&dbuf[E_MINUTE]);
160 	then.tm_sec = atoi(&dbuf[E_SECOND]);
161 	then.tm_year = atoi(&dbuf[E_YEAR]) - 1900;
162 	then.tm_isdst = ndmp_isdst;
163 
164 	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 *
ddates_pathname(char * buf)181 ddates_pathname(char *buf)
182 {
183 	return (ndmpd_make_bk_dir_path(buf, NDMP_DUMPDATES));
184 }
185 
186 
187 /*
188  * getaline
189  *
190  * Get a line from the file and handle the continued lines.
191  */
192 static char *
getaline(FILE * fp,char * line,int llen)193 getaline(FILE *fp, char *line, int llen)
194 {
195 	char *save;
196 	int len;
197 
198 	if (!fp || !line)
199 		return (NULL);
200 
201 	*(save = line) = '\0';
202 	do {
203 		if (fgets(line, llen, fp) != line)
204 			return (NULL);
205 
206 		/* comment line? */
207 		if (*line == '#')
208 			continue;
209 
210 		len = strlen(line);
211 		/* short line */
212 		if (len <= 0)
213 			continue;
214 
215 		line += len-1;
216 		if (*line != '\n')
217 			return (NULL);
218 
219 		/* trim the trailing new line */
220 		*line = '\0';
221 		if (--len <= 0)
222 			break;
223 
224 		if (*(line-1) != '\\')
225 			break;
226 
227 		*(line-1) = '\n';
228 		llen -= len;
229 	} while (llen > 0);
230 
231 	return (save);
232 }
233 
234 
235 /*
236  * get_ddname
237  *
238  * Get the path name from the buffer passed.
239  *
240  * Returns the beginning of the path name.  The buffer pointer is moved
241  * forward to point to where the next field (the dump level) begins.
242  */
243 static char *
get_ddname(char ** bpp)244 get_ddname(char **bpp)
245 {
246 	char *h, *t, *save;
247 
248 	if (!bpp || !*bpp)
249 		return (NULL);
250 
251 	*bpp += strspn(*bpp, "\t ");
252 	save = h = t = *bpp;
253 	while (*t) {
254 		if (*t == '\t' || *t == ' ') {
255 			/* consume the '\t' or space character */
256 			t++;
257 			break;
258 		}
259 
260 		if (*t == '\\')
261 			switch (*(t+1)) {
262 			case '\t':
263 			case ' ':
264 				t++; /* skip the '\\' */
265 			default:
266 				break;	/* nothing */
267 			}
268 
269 		*h++ = *t++;
270 	}
271 
272 	*bpp = t;
273 	*h++ = '\0';
274 	return (save);
275 }
276 
277 
278 /*
279  * get_ddlevel
280  *
281  * Get the dump level from the buffer passed.
282  *
283  * Returns the dump level found.  The buffer pointer is moved
284  * forward to point to where the next field (the dump date) begins.
285  */
286 static int
get_ddlevel(char ** bpp)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
322  *
323  * Get the dump date from the buffer passed.
324  *
325  * Returns the dump date string. The buffer pointer is moved
326  * forward.  It points to the end of the buffer now.
327  */
328 static char *
get_ddate(char ** bpp)329 get_ddate(char **bpp)
330 {
331 	char *save;
332 
333 	if (!bpp || !*bpp)
334 		return (NULL);
335 
336 	*bpp += strspn(*bpp, "\t ");
337 	save = *bpp;
338 	*bpp += strlen(*bpp);
339 	return (save);
340 }
341 
342 
343 /*
344  * put_ddname
345  *
346  * Print the dump path name to the dumpdates file.  It escapes the space,
347  * '\t' and new line characters in the path name.  The same characters are
348  * considered in the get_ddname().
349  */
350 static void
put_ddname(FILE * fp,char * nm)351 put_ddname(FILE *fp, char *nm)
352 {
353 	if (!nm)
354 		return;
355 
356 	while (*nm)
357 		switch (*nm) {
358 		case ' ':
359 		case '\n':
360 		case '\t':
361 			(void) fputc('\\', fp);
362 			/* FALLTHROUGH */
363 		default:
364 			(void) fputc(*nm++, fp);
365 		}
366 }
367 
368 
369 /*
370  * put_ddlevel
371  *
372  * Print the dump level into the dumpdates file.
373  */
374 static void
put_ddlevel(FILE * fp,int level)375 put_ddlevel(FILE *fp, int level)
376 {
377 	if (!fp)
378 		return;
379 
380 	(void) fprintf(fp, IS_LBR_BKTYPE(level) ? "%c" : "%d", level);
381 }
382 
383 
384 /*
385  * put_ddate
386  *
387  * Print the dump date into the dumpdates file.
388  */
put_ddate(FILE * fp,time_t t)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
dd_free(dumpdates_t * ddheadp)411 dd_free(dumpdates_t *ddheadp)
412 {
413 	dumpdates_t *save;
414 
415 	if (!ddheadp)
416 		return;
417 
418 	ddheadp = ddheadp->dd_next;
419 	while (ddheadp) {
420 		save = ddheadp->dd_next;
421 		free(ddheadp);
422 		ddheadp = save;
423 	}
424 }
425 
426 
427 /*
428  * makedumpdate
429  *
430  * Make the dumpdate node based on the string buffer passed to it.
431  */
432 static int
makedumpdate(dumpdates_t * ddp,char * tbuf)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
getrecord(FILE * fp,dumpdates_t * ddatep,int * recno)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
readdumptimes(FILE * fp,dumpdates_t * ddheadp)518 readdumptimes(FILE *fp, dumpdates_t *ddheadp)
519 {
520 	int recno;
521 	register struct	dumpdates *ddwalk;
522 
523 	if (!fp || !ddheadp)
524 		return (-1);
525 
526 	recno = 1;
527 	(void) memset((void *)ddheadp, 0, sizeof (*ddheadp));
528 	for (; ; ) {
529 		ddwalk = ndmp_malloc(sizeof (*ddwalk));
530 		if (!ddwalk)
531 			return (-1);
532 
533 		if (getrecord(fp, ddwalk, &recno) < 0) {
534 			free(ddwalk);
535 			break;
536 		}
537 
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
dumprecout(FILE * fp,dumpdates_t * ddp)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
initdumptimes(dumpdates_t * ddheadp)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
putdumptime(char * path,int level,time_t ddate)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);
732 
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
append_dumptime(char * fname,char * path,int level,time_t ddate)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 *
find_date(dumpdates_t * ddp,char * path,int level,time_t t)810 find_date(dumpdates_t *ddp, char *path, int level, time_t t)
811 {
812 	for (; ddp; ddp = ddp->dd_next)
813 		if (ddp->dd_level == level && ddp->dd_ddate > t &&
814 		    strcmp(path, ddp->dd_name) == 0)
815 			break;
816 
817 	return (ddp);
818 }
819 
820 
821 /*
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.
826  *
827  * Returns:
828  *   0 on success
829  *   < 0 on error
830  */
831 int
ndmpd_get_dumptime(char * path,int * level,time_t * ddate)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--) {
866 			if (nvlist_lookup_nvlist(userprops,
867 			    zfs_dumpdate_props[i], &propval) == 0) {
868 				*level = i;
869 				break;
870 			}
871 		}
872 		if (propval == NULL ||
873 		    nvlist_lookup_string(propval, ZPROP_VALUE,
874 		    &strval) != 0) {
875 			*level = 0;
876 			*ddate = (time_t)0;
877 			zfs_close(zhp);
878 			(void) mutex_unlock(&zlib_mtx);
879 			return (0);
880 		}
881 		if (unctime(strval, ddate) < 0) {
882 			zfs_close(zhp);
883 			(void) mutex_unlock(&zlib_mtx);
884 			return (-1);
885 		}
886 
887 		zfs_close(zhp);
888 		(void) mutex_unlock(&zlib_mtx);
889 		return (0);
890 	}
891 	(void) mutex_unlock(&zlib_mtx);
892 
893 	(void) memset((void *)&ddhead, 0, sizeof (ddhead));
894 	if (initdumptimes(&ddhead) < 0) {
895 		dd_free(&ddhead);
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;
937 			*ddate = (time_t)0;
938 		}
939 	}
940 
941 	dd_free(&ddhead);
942 
943 	return (0);
944 }
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
ndmpd_put_dumptime(char * path,int level,time_t ddate)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
ndmpd_append_dumptime(char * fname,char * path,int level,time_t ddate)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);
1023 
1024 	return (rv);
1025 }
1026