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 */
76typedef 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 */
87static char months[] = "JanFebMarAprMayJunJulAugSepOctNovDec";
88
89
90/*
91 * Binary lock for accessing the dumpdates file.
92 */
93mutex_t ndmp_dd_lock = DEFAULTMUTEX;
94
95int ndmp_isdst = -1;
96
97char *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 */
118static int
119lookup(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 */
141static int
142unctime(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 */
180static char *
181ddates_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 */
192static char *
193getaline(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 */
243static char *
244get_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 */
286static int
287get_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 */
328static char *
329get_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 */
350static void
351put_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 */
374static void
375put_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 */
389static 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 */
410static void
411dd_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 */
432static int
433makedumpdate(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 */
478static int
479getrecord(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 */
517static int
518readdumptimes(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 */
552static void
553dumprecout(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 */
583static int
584initdumptimes(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 */
640static int
641putdumptime(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 */
742static int
743append_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 */
809static dumpdates_t *
810find_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 */
831int
832ndmpd_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 */
957int
958ndmpd_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 */
994int
995ndmpd_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