2 Bacula(R) - The Network Backup Solution
4 Copyright (C) 2000-2017 Kern Sibbald
6 The original author of Bacula is Kern Sibbald, with contributions
7 from many others, a complete list can be found in the file AUTHORS.
9 You may use this file and others of this release according to the
10 license defined in the LICENSE file, which includes the Affero General
11 Public License, v3.0 ("AGPLv3") and some additional permissions and
12 terms pursuant to its AGPLv3 Section 7.
14 This notice must be preserved when any source code is
15 conveyed and/or propagated.
17 Bacula(R) is a registered trademark of Kern Sibbald.
20 * Match Bootstrap Records (used for restores) against
23 * Kern Sibbald, June MMII
29 * Also for efficiency, once a bsr is done, it really should be
30 * delinked from the bsr chain. This will avoid the above
31 * problem and make traversal of the bsr chain more efficient.
41 #include "lib/fnmatch.h"
44 /* Temp code to test the new match_all code */
45 int use_new_match_all = 0;
47 const int dbglevel = 200;
49 /* Forward references */
50 static int match_volume(BSR *bsr, BSR_VOLUME *volume, VOLUME_LABEL *volrec, bool done);
51 static int match_sesstime(BSR *bsr, BSR_SESSTIME *sesstime, DEV_RECORD *rec, bool done);
52 static int match_sessid(BSR *bsr, BSR_SESSID *sessid, DEV_RECORD *rec);
53 static int match_client(BSR *bsr, BSR_CLIENT *client, SESSION_LABEL *sessrec, bool done);
54 static int match_job(BSR *bsr, BSR_JOB *job, SESSION_LABEL *sessrec, bool done);
55 static int match_job_type(BSR *bsr, BSR_JOBTYPE *job_type, SESSION_LABEL *sessrec, bool done);
56 static int match_job_level(BSR *bsr, BSR_JOBLEVEL *job_level, SESSION_LABEL *sessrec, bool done);
57 static int match_jobid(BSR *bsr, BSR_JOBID *jobid, SESSION_LABEL *sessrec, bool done);
58 static int match_findex(BSR *bsr, DEV_RECORD *rec, bool done);
59 static int match_voladdr(BSR *bsr, BSR_VOLADDR *voladdr, DEV_RECORD *rec, bool done);
60 static int match_stream(BSR *bsr, BSR_STREAM *stream, DEV_RECORD *rec, bool done);
61 static int match_all(BSR *bsr, DEV_RECORD *rec, VOLUME_LABEL *volrec, SESSION_LABEL *sessrec, bool done, JCR *jcr);
62 static int match_block_sesstime(BSR *bsr, BSR_SESSTIME *sesstime, DEV_BLOCK *block);
63 static int match_block_sessid(BSR *bsr, BSR_SESSID *sessid, DEV_BLOCK *block);
64 static BSR *find_smallest_volfile(BSR *fbsr, BSR *bsr);
66 /* Temp function to test the new code */
67 static int new_match_all(BSR *bsr, DEV_RECORD *rec, VOLUME_LABEL *volrec,
68 SESSION_LABEL *sessrec, bool done, JCR *jcr);
70 /*********************************************************************
72 * If possible, position the archive device (tape) to read the
75 void position_bsr_block(BSR *bsr, DEV_BLOCK *block)
77 /* To be implemented */
80 /*********************************************************************
82 * Do fast block rejection based on bootstrap records.
83 * use_fast_rejection will be set if we have VolSessionId and VolSessTime
84 * in each record. When BlockVer is >= 2, we have those in the block header
85 * so can do fast rejection.
87 * returns: 1 if block may contain valid records
88 * 0 if block may be skipped (i.e. it contains no records of
89 * that can match the bsr).
92 int match_bsr_block(BSR *bsr, DEV_BLOCK *block)
94 if (!bsr || !bsr->use_fast_rejection || (block->BlockVer < 2)) {
95 return 1; /* cannot fast reject */
98 for ( ; bsr; bsr=bsr->next) {
99 if (!match_block_sesstime(bsr, bsr->sesstime, block)) {
102 if (!match_block_sessid(bsr, bsr->sessid, block)) {
110 static int match_block_sesstime(BSR *bsr, BSR_SESSTIME *sesstime, DEV_BLOCK *block)
113 return 1; /* no specification matches all */
115 if (sesstime->sesstime == block->VolSessionTime) {
118 if (sesstime->next) {
119 return match_block_sesstime(bsr, sesstime->next, block);
124 static int match_block_sessid(BSR *bsr, BSR_SESSID *sessid, DEV_BLOCK *block)
127 return 1; /* no specification matches all */
129 if (sessid->sessid <= block->VolSessionId && sessid->sessid2 >= block->VolSessionId) {
133 return match_block_sessid(bsr, sessid->next, block);
138 static int match_fileregex(BSR *bsr, DEV_RECORD *rec, JCR *jcr)
140 if (bsr->fileregex_re == NULL)
143 if (bsr->attr == NULL) {
144 bsr->attr = new_attr(jcr);
148 * The code breaks if the first record associated with a file is
151 if (rec->maskedStream == STREAM_UNIX_ATTRIBUTES ||
152 rec->maskedStream == STREAM_UNIX_ATTRIBUTES_EX) {
153 bsr->skip_file = false;
154 if (unpack_attributes_record(jcr, rec->Stream, rec->data, rec->data_len, bsr->attr)) {
155 if (regexec(bsr->fileregex_re, bsr->attr->fname, 0, NULL, 0) == 0) {
156 Dmsg2(dbglevel, "Matched pattern, fname=%s FI=%d\n",
157 bsr->attr->fname, rec->FileIndex);
159 Dmsg2(dbglevel, "Didn't match, skipping fname=%s FI=%d\n",
160 bsr->attr->fname, rec->FileIndex);
161 bsr->skip_file = true;
168 /*********************************************************************
170 * Match Bootstrap records
172 * returns 0 no match and reposition is set if we should
173 * reposition the tape
174 * returns -1 no additional matches possible
176 int match_bsr(BSR *bsr, DEV_RECORD *rec, VOLUME_LABEL *volrec, SESSION_LABEL *sessrec, JCR *jcr)
181 * The bsr->reposition flag is set any time a bsr is done.
182 * In this case, we can probably reposition the
183 * tape to the next available bsr position.
185 if (jcr->use_new_match_all) { /* TODO: Remove the if when the new code is tested */
191 bsr->reposition = false;
192 /* Temp code to test the new match_all */
193 if (jcr->use_new_match_all) {
194 stat = new_match_all(bsr, rec, volrec, sessrec, true, jcr);
196 stat = match_all(bsr, rec, volrec, sessrec, true, jcr);
199 * Note, bsr->reposition is set by match_all when
200 * a bsr is done. We turn it off if a match was
201 * found or if we cannot use positioning
203 if (stat != 0 || !bsr->use_positioning) {
204 bsr->reposition = false;
207 stat = 1; /* no bsr => match all */
213 * Find the next bsr that applies to the current tape.
214 * It is the one with the smallest VolFile position.
216 BSR *find_next_bsr(BSR *root_bsr, DEVICE *dev)
219 BSR *found_bsr = NULL;
221 /* Do tape/disk seeking only if CAP_POSITIONBLOCKS is on */
223 Dmsg0(dbglevel, "NULL root bsr pointer passed to find_next_bsr.\n");
226 if (!root_bsr->use_positioning ||
227 !root_bsr->reposition || !dev->has_cap(CAP_POSITIONBLOCKS)) {
228 Dmsg2(dbglevel, "No nxt_bsr use_pos=%d repos=%d\n", root_bsr->use_positioning, root_bsr->reposition);
231 Dmsg2(dbglevel, "use_pos=%d repos=%d\n", root_bsr->use_positioning, root_bsr->reposition);
232 root_bsr->mount_next_volume = false;
233 /* Walk through all bsrs to find the next one to use => smallest file,block */
234 for (bsr=root_bsr; bsr; bsr=bsr->next) {
235 if (bsr->done || !match_volume(bsr, bsr->volume, &dev->VolHdr, 1)) {
238 if (found_bsr == NULL) {
241 found_bsr = find_smallest_volfile(found_bsr, bsr);
245 * If we get to this point and found no bsr, it means
246 * that any additional bsr's must apply to the next
247 * tape, so set a flag.
249 if (found_bsr == NULL) {
250 root_bsr->mount_next_volume = true;
256 * Get the smallest address from this voladdr part
257 * Don't use "done" elements
259 static bool get_smallest_voladdr(BSR_VOLADDR *va, uint64_t *ret)
264 for (; va ; va = va->next) {
267 min_val = MIN(min_val, va->saddr);
279 * This routine needs to be fixed to only look at items that
280 * are not marked as done. Otherwise, it can find a bsr
281 * that has already been consumed, and this will cause the
282 * bsr to be used, thus we may seek back and re-read the
283 * same records, causing an error. This deficiency must
284 * be fixed. For the moment, it has been kludged in
285 * read_record.c to avoid seeking back if find_next_bsr
286 * returns a bsr pointing to a smaller address (file/block).
289 static BSR *find_smallest_volfile(BSR *found_bsr, BSR *bsr)
291 BSR *return_bsr = found_bsr;
292 uint64_t found_bsr_saddr, bsr_saddr;
294 /* if we have VolAddr, use it, else try with File and Block */
295 if (get_smallest_voladdr(found_bsr->voladdr, &found_bsr_saddr)) {
296 if (get_smallest_voladdr(bsr->voladdr, &bsr_saddr)) {
297 if (found_bsr_saddr > bsr_saddr) {
309 * Called after the signature record so that
310 * we can see if the current bsr has been
311 * fully processed (i.e. is done).
312 * The bsr argument is not used, but is included
313 * for consistency with the other match calls.
315 * Returns: true if we should reposition
318 bool is_this_bsr_done(JCR *jcr, BSR *bsr, DEV_RECORD *rec)
320 BSR *rbsr = rec->bsr;
321 Dmsg1(dbglevel, "match_set %d\n", rbsr != NULL);
327 /* TODO: When the new code is stable, drop the else part */
328 if (jcr->use_new_match_all) {
332 /* Normally the loop must stop only *after* the last record has been read,
333 * and we are about to read the next record.
335 if (rbsr->count && rbsr->found > rbsr->count) {
337 rbsr->root->reposition = true;
338 Dmsg2(dbglevel, "is_end_this_bsr set reposition=1 count=%d found=%d\n",
339 rbsr->count, rbsr->found);
344 /* Old code that is stable */
347 if (rbsr->count && rbsr->found >= rbsr->count) {
349 rbsr->root->reposition = true;
350 Dmsg2(dbglevel, "is_end_this_bsr set reposition=1 count=%d found=%d\n",
351 rbsr->count, rbsr->found);
355 Dmsg2(dbglevel, "is_end_this_bsr not done count=%d found=%d\n",
356 rbsr->count, rbsr->found);
361 static int new_match_all(BSR *bsr, DEV_RECORD *rec, VOLUME_LABEL *volrec,
362 SESSION_LABEL *sessrec, bool done, JCR *jcr)
364 Dmsg0(dbglevel, "Enter match_all\n");
369 if (!match_volume(bsr, bsr->volume, volrec, 1)) {
370 Dmsg2(dbglevel, "bsr fail bsr_vol=%s != rec read_vol=%s\n", bsr->volume->VolumeName,
375 if (!match_voladdr(bsr, bsr->voladdr, rec, 1)) {
377 Dmsg3(dbglevel, "Fail on Addr=%llu. bsr=%llu,%llu\n",
378 get_record_address(rec), bsr->voladdr->saddr, bsr->voladdr->eaddr);
383 if (!match_sesstime(bsr, bsr->sesstime, rec, 1)) {
384 Dmsg2(dbglevel, "Fail on sesstime. bsr=%u rec=%u\n",
385 bsr->sesstime->sesstime, rec->VolSessionTime);
389 /* NOTE!! This test MUST come after the sesstime test */
390 if (!match_sessid(bsr, bsr->sessid, rec)) {
391 Dmsg2(dbglevel, "Fail on sessid. bsr=%u rec=%u\n",
392 bsr->sessid->sessid, rec->VolSessionId);
396 /* NOTE!! This test MUST come after sesstime and sessid tests */
397 if (!match_findex(bsr, rec, 1)) {
398 Dmsg3(dbglevel, "Fail on recFI=%d. bsrFI=%d,%d\n",
399 rec->FileIndex, bsr->FileIndex->findex, bsr->FileIndex->findex2);
402 if (bsr->FileIndex) {
403 Dmsg3(dbglevel, "match on findex=%d. bsrFI=%d,%d\n",
404 rec->FileIndex, bsr->FileIndex->findex, bsr->FileIndex->findex2);
407 if (!match_fileregex(bsr, rec, jcr)) {
408 Dmsg1(dbglevel, "Fail on fileregex='%s'\n", NPRT(bsr->fileregex));
412 /* This flag is set by match_fileregex (and perhaps other tests) */
413 if (bsr->skip_file) {
414 Dmsg1(dbglevel, "Skipping findex=%d\n", rec->FileIndex);
419 * If a count was specified and we have a FileIndex, assume
420 * it is a Bacula created bsr (or the equivalent). We
421 * then save the bsr where the match occurred so that
422 * after processing the record or records, we can update
423 * the found count. I.e. rec->bsr points to the bsr that
424 * satisfied the match.
426 if (bsr->count && bsr->FileIndex) {
428 if (bsr->next && rec->FileIndex != bsr->LastFI) {
429 bsr->LastFI = rec->FileIndex;
431 Dmsg1(dbglevel, "Leave match_all 1 found=%d\n", bsr->found);
432 return 1; /* this is a complete match */
436 * The selections below are not used by Bacula's
437 * restore command, and don't work because of
438 * the rec->bsr = bsr optimization above.
441 if (!match_jobid(bsr, bsr->JobId, sessrec, 1)) {
442 Dmsg0(dbglevel, "fail on JobId\n");
445 if (!match_job(bsr, bsr->job, sessrec, 1)) {
446 Dmsg0(dbglevel, "fail on Job\n");
449 if (!match_client(bsr, bsr->client, sessrec, 1)) {
450 Dmsg0(dbglevel, "fail on Client\n");
453 if (!match_job_type(bsr, bsr->JobType, sessrec, 1)) {
454 Dmsg0(dbglevel, "fail on Job type\n");
457 if (!match_job_level(bsr, bsr->JobLevel, sessrec, 1)) {
458 Dmsg0(dbglevel, "fail on Job level\n");
461 if (!match_stream(bsr, bsr->stream, rec, 1)) {
462 Dmsg0(dbglevel, "fail on stream\n");
469 if (bsr->count && bsr->found >= bsr->count) {
471 if (bsr->next) bsr->root->cur_bsr = bsr->next;
472 Dmsg1(dbglevel, "bsr done: Volume=%s\n", bsr->volume->VolumeName);
475 done = bsr->done && done;
479 if (bsr->done && done) {
480 Dmsg0(dbglevel, "Leave match all -1\n");
483 Dmsg1(dbglevel, "Leave match all 0, repos=%d\n", bsr->reposition);
490 * Match all the components of current record
493 * returns -1 no additional matches possible
495 static int match_all(BSR *bsr, DEV_RECORD *rec, VOLUME_LABEL *volrec,
496 SESSION_LABEL *sessrec, bool done, JCR *jcr)
498 Dmsg0(dbglevel, "Enter match_all\n");
502 if (!match_volume(bsr, bsr->volume, volrec, 1)) {
503 Dmsg2(dbglevel, "bsr fail bsr_vol=%s != rec read_vol=%s\n", bsr->volume->VolumeName,
507 Dmsg2(dbglevel, "OK bsr match bsr_vol=%s read_vol=%s\n", bsr->volume->VolumeName,
510 if (!match_voladdr(bsr, bsr->voladdr, rec, 1)) {
512 Dmsg3(dbglevel, "Fail on Addr=%llu. bsr=%llu,%llu\n",
513 get_record_address(rec), bsr->voladdr->saddr, bsr->voladdr->eaddr);
519 if (!match_sesstime(bsr, bsr->sesstime, rec, 1)) {
520 Dmsg2(dbglevel, "Fail on sesstime. bsr=%u rec=%u\n",
521 bsr->sesstime->sesstime, rec->VolSessionTime);
525 /* NOTE!! This test MUST come after the sesstime test */
526 if (!match_sessid(bsr, bsr->sessid, rec)) {
527 Dmsg2(dbglevel, "Fail on sessid. bsr=%u rec=%u\n",
528 bsr->sessid->sessid, rec->VolSessionId);
532 /* NOTE!! This test MUST come after sesstime and sessid tests */
533 if (!match_findex(bsr, rec, 1)) {
534 Dmsg3(dbglevel, "Fail on findex=%d. bsr=%d,%d\n",
535 rec->FileIndex, bsr->FileIndex->findex, bsr->FileIndex->findex2);
538 if (bsr->FileIndex) {
539 Dmsg3(dbglevel, "match on findex=%d. bsr=%d,%d\n",
540 rec->FileIndex, bsr->FileIndex->findex, bsr->FileIndex->findex2);
543 if (!match_fileregex(bsr, rec, jcr)) {
544 Dmsg1(dbglevel, "Fail on fileregex='%s'\n", NPRT(bsr->fileregex));
548 /* This flag is set by match_fileregex (and perhaps other tests) */
549 if (bsr->skip_file) {
550 Dmsg1(dbglevel, "Skipping findex=%d\n", rec->FileIndex);
555 * If a count was specified and we have a FileIndex, assume
556 * it is a Bacula created bsr (or the equivalent). We
557 * then save the bsr where the match occurred so that
558 * after processing the record or records, we can update
559 * the found count. I.e. rec->bsr points to the bsr that
560 * satisfied the match.
562 if (bsr->count && bsr->FileIndex) {
564 Dmsg0(dbglevel, "Leave match_all 1\n");
565 return 1; /* this is a complete match */
569 * The selections below are not used by Bacula's
570 * restore command, and don't work because of
571 * the rec->bsr = bsr optimization above.
573 if (!match_jobid(bsr, bsr->JobId, sessrec, 1)) {
574 Dmsg0(dbglevel, "fail on JobId\n");
578 if (!match_job(bsr, bsr->job, sessrec, 1)) {
579 Dmsg0(dbglevel, "fail on Job\n");
582 if (!match_client(bsr, bsr->client, sessrec, 1)) {
583 Dmsg0(dbglevel, "fail on Client\n");
586 if (!match_job_type(bsr, bsr->JobType, sessrec, 1)) {
587 Dmsg0(dbglevel, "fail on Job type\n");
590 if (!match_job_level(bsr, bsr->JobLevel, sessrec, 1)) {
591 Dmsg0(dbglevel, "fail on Job level\n");
594 if (!match_stream(bsr, bsr->stream, rec, 1)) {
595 Dmsg0(dbglevel, "fail on stream\n");
602 return match_all(bsr->next, rec, volrec, sessrec, bsr->done && done, jcr);
604 if (bsr->done && done) {
605 Dmsg0(dbglevel, "Leave match all -1\n");
608 Dmsg0(dbglevel, "Leave match all 0\n");
612 static int match_volume(BSR *bsr, BSR_VOLUME *volume, VOLUME_LABEL *volrec, bool done)
615 return 0; /* Volume must match */
617 if (strcmp(volume->VolumeName, volrec->VolumeName) == 0) {
618 Dmsg1(dbglevel, "OK match_volume=%s\n", volrec->VolumeName);
622 return match_volume(bsr, volume->next, volrec, 1);
627 static int match_client(BSR *bsr, BSR_CLIENT *client, SESSION_LABEL *sessrec, bool done)
630 return 1; /* no specification matches all */
632 if (strcmp(client->ClientName, sessrec->ClientName) == 0) {
636 return match_client(bsr, client->next, sessrec, 1);
641 static int match_job(BSR *bsr, BSR_JOB *job, SESSION_LABEL *sessrec, bool done)
644 return 1; /* no specification matches all */
646 if (strcmp(job->Job, sessrec->Job) == 0) {
650 return match_job(bsr, job->next, sessrec, 1);
655 static int match_job_type(BSR *bsr, BSR_JOBTYPE *job_type, SESSION_LABEL *sessrec, bool done)
658 return 1; /* no specification matches all */
660 if (job_type->JobType == sessrec->JobType) {
663 if (job_type->next) {
664 return match_job_type(bsr, job_type->next, sessrec, 1);
669 static int match_job_level(BSR *bsr, BSR_JOBLEVEL *job_level, SESSION_LABEL *sessrec, bool done)
672 return 1; /* no specification matches all */
674 if (job_level->JobLevel == sessrec->JobLevel) {
677 if (job_level->next) {
678 return match_job_level(bsr, job_level->next, sessrec, 1);
683 static int match_jobid(BSR *bsr, BSR_JOBID *jobid, SESSION_LABEL *sessrec, bool done)
686 return 1; /* no specification matches all */
688 if (jobid->JobId <= sessrec->JobId && jobid->JobId2 >= sessrec->JobId) {
692 return match_jobid(bsr, jobid->next, sessrec, 1);
697 static int match_voladdr(BSR *bsr, BSR_VOLADDR *voladdr, DEV_RECORD *rec, bool done)
700 return 1; /* no specification matches all */
703 uint64_t addr = get_record_address(rec);
704 Dmsg6(dbglevel, "match_voladdr: saddr=%llu eaddr=%llu recaddr=%llu sfile=%u efile=%u recfile=%u\n",
705 voladdr->saddr, voladdr->eaddr, addr, (uint32_t)(voladdr->saddr>>32),
706 (uint32_t)(voladdr->eaddr>>32), (uint32_t)(addr>>32));
708 if (voladdr->saddr <= addr && voladdr->eaddr >= addr) {
709 Dmsg1(dbglevel, "OK match voladdr=%lld\n", addr);
712 /* Once we get past last eblock, we are done */
713 if (addr > voladdr->eaddr) {
714 voladdr->done = true; /* set local done */
715 if (!voladdr->next) { /* done with everything? */
716 bsr->done = true; /* yes */
720 return match_voladdr(bsr, voladdr->next, rec, voladdr->done && done);
723 /* If we are done and all prior matches are done, this bsr is finished */
724 if (voladdr->done && done) {
726 bsr->root->reposition = true;
727 Dmsg2(dbglevel, "bsr done from voladdr rec=%llu voleaddr=%llu\n",
728 addr, voladdr->eaddr);
734 static int match_stream(BSR *bsr, BSR_STREAM *stream, DEV_RECORD *rec, bool done)
737 return 1; /* no specification matches all */
739 if (stream->stream == rec->Stream) {
743 return match_stream(bsr, stream->next, rec, 1);
748 static int match_sesstime(BSR *bsr, BSR_SESSTIME *sesstime, DEV_RECORD *rec, bool done)
751 return 1; /* no specification matches all */
753 if (sesstime->sesstime == rec->VolSessionTime) {
756 if (rec->VolSessionTime > sesstime->sesstime) {
757 sesstime->done = true;
759 if (sesstime->next) {
760 return match_sesstime(bsr, sesstime->next, rec, sesstime->done && done);
762 if (sesstime->done && done) {
764 bsr->root->reposition = true;
765 Dmsg0(dbglevel, "bsr done from sesstime\n");
771 * Note, we cannot mark bsr done based on session id because we may
772 * have interleaved records, and there may be more of what we want
775 static int match_sessid(BSR *bsr, BSR_SESSID *sessid, DEV_RECORD *rec)
778 return 1; /* no specification matches all */
780 if (sessid->sessid <= rec->VolSessionId && sessid->sessid2 >= rec->VolSessionId) {
784 return match_sessid(bsr, sessid->next, rec);
790 * When reading the Volume, the Volume Findex (rec->FileIndex) always
791 * are found in sequential order. Thus we can make optimizations.
794 static int match_findex(BSR *bsr, DEV_RECORD *rec, bool done)
796 BSR_FINDEX *findex = bsr->FileIndex;
800 return 1; /* no specification matches all */
804 if (findex->findex <= rec->FileIndex && findex->findex2 >= rec->FileIndex) {
805 Dmsg3(dbglevel, "Match on recFindex=%d. bsrFIs=%d,%d\n",
806 rec->FileIndex, findex->findex, findex->findex2);
809 if (rec->FileIndex > findex->findex2) {
810 /* TODO: See if we really want to modify the findex when we will try
811 * to seek backward */
814 Dmsg3(dbglevel, "No match recFindex=%d. bsrFIs=%d,%d\n",
815 rec->FileIndex, findex->findex, findex->findex2);
818 bsr->FileIndex = findex;
822 bsr->root->reposition = true;
829 uint64_t get_bsr_start_addr(BSR *bsr)
831 uint64_t bsr_addr = 0;
835 bsr_addr = bsr->voladdr->saddr;