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
lookup(char * str)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
unctime(char * str,time_t * t)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 *
ddates_pathname(char * buf)180 ddates_pathname(char *buf)
181 {
182 return (ndmpd_make_bk_dir_path(buf, NDMP_DUMPDATES));
183 }
184
185
186 /*
187 * getaline
188 *
189 * Get a line from the file and handle the continued lines.
190 */
191 static char *
getaline(FILE * fp,char * line,int llen)192 getaline(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 *
get_ddname(char ** bpp)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
get_ddlevel(char ** bpp)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 *
get_ddate(char ** bpp)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
put_ddname(FILE * fp,char * nm)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
put_ddlevel(FILE * fp,int level)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 */
put_ddate(FILE * fp,time_t t)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
dd_free(dumpdates_t * ddheadp)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
makedumpdate(dumpdates_t * ddp,char * tbuf)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
getrecord(FILE * fp,dumpdates_t * ddatep,int * recno)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 (getaline(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
readdumptimes(FILE * fp,dumpdates_t * ddheadp)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
dumprecout(FILE * fp,dumpdates_t * ddp)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
initdumptimes(dumpdates_t * ddheadp)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
putdumptime(char * path,int level,time_t ddate)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
append_dumptime(char * fname,char * path,int level,time_t ddate)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 *
find_date(dumpdates_t * ddp,char * path,int level,time_t 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
ndmpd_get_dumptime(char * path,int * level,time_t * ddate)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
ndmpd_put_dumptime(char * path,int level,time_t ddate)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
ndmpd_append_dumptime(char * fname,char * path,int level,time_t ddate)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