2 Bacula(R) - The Network Backup Solution
4 Copyright (C) 2000-2016 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 const int dbglevel = 500;
46 /* Forward references */
47 static int match_volume(BSR *bsr, BSR_VOLUME *volume, VOLUME_LABEL *volrec, bool done);
48 static int match_sesstime(BSR *bsr, BSR_SESSTIME *sesstime, DEV_RECORD *rec, bool done);
49 static int match_sessid(BSR *bsr, BSR_SESSID *sessid, DEV_RECORD *rec);
50 static int match_client(BSR *bsr, BSR_CLIENT *client, SESSION_LABEL *sessrec, bool done);
51 static int match_job(BSR *bsr, BSR_JOB *job, SESSION_LABEL *sessrec, bool done);
52 static int match_job_type(BSR *bsr, BSR_JOBTYPE *job_type, SESSION_LABEL *sessrec, bool done);
53 static int match_job_level(BSR *bsr, BSR_JOBLEVEL *job_level, SESSION_LABEL *sessrec, bool done);
54 static int match_jobid(BSR *bsr, BSR_JOBID *jobid, SESSION_LABEL *sessrec, bool done);
55 static int match_findex(BSR *bsr, BSR_FINDEX *findex, DEV_RECORD *rec, bool done);
56 static int match_volfile(BSR *bsr, BSR_VOLFILE *volfile, DEV_RECORD *rec, bool done);
57 static int match_voladdr(BSR *bsr, BSR_VOLADDR *voladdr, DEV_RECORD *rec, bool done);
58 static int match_stream(BSR *bsr, BSR_STREAM *stream, DEV_RECORD *rec, bool done);
59 static int match_all(BSR *bsr, DEV_RECORD *rec, VOLUME_LABEL *volrec, SESSION_LABEL *sessrec, bool done, JCR *jcr);
60 static int match_block_sesstime(BSR *bsr, BSR_SESSTIME *sesstime, DEV_BLOCK *block);
61 static int match_block_sessid(BSR *bsr, BSR_SESSID *sessid, DEV_BLOCK *block);
62 static BSR *find_smallest_volfile(BSR *fbsr, BSR *bsr);
65 /*********************************************************************
67 * If possible, position the archive device (tape) to read the
70 void position_bsr_block(BSR *bsr, DEV_BLOCK *block)
72 /* To be implemented */
75 /*********************************************************************
77 * Do fast block rejection based on bootstrap records.
78 * use_fast_rejection will be set if we have VolSessionId and VolSessTime
79 * in each record. When BlockVer is >= 2, we have those in the block header
80 * so can do fast rejection.
82 * returns: 1 if block may contain valid records
83 * 0 if block may be skipped (i.e. it contains no records of
84 * that can match the bsr).
87 int match_bsr_block(BSR *bsr, DEV_BLOCK *block)
89 if (!bsr || !bsr->use_fast_rejection || (block->BlockVer < 2)) {
90 return 1; /* cannot fast reject */
93 for ( ; bsr; bsr=bsr->next) {
94 if (!match_block_sesstime(bsr, bsr->sesstime, block)) {
97 if (!match_block_sessid(bsr, bsr->sessid, block)) {
105 static int match_block_sesstime(BSR *bsr, BSR_SESSTIME *sesstime, DEV_BLOCK *block)
108 return 1; /* no specification matches all */
110 if (sesstime->sesstime == block->VolSessionTime) {
113 if (sesstime->next) {
114 return match_block_sesstime(bsr, sesstime->next, block);
119 static int match_block_sessid(BSR *bsr, BSR_SESSID *sessid, DEV_BLOCK *block)
122 return 1; /* no specification matches all */
124 if (sessid->sessid <= block->VolSessionId && sessid->sessid2 >= block->VolSessionId) {
128 return match_block_sessid(bsr, sessid->next, block);
133 static int match_fileregex(BSR *bsr, DEV_RECORD *rec, JCR *jcr)
135 if (bsr->fileregex_re == NULL)
138 if (bsr->attr == NULL) {
139 bsr->attr = new_attr(jcr);
143 * The code breaks if the first record associated with a file is
146 if (rec->maskedStream == STREAM_UNIX_ATTRIBUTES ||
147 rec->maskedStream == STREAM_UNIX_ATTRIBUTES_EX) {
148 bsr->skip_file = false;
149 if (unpack_attributes_record(jcr, rec->Stream, rec->data, rec->data_len, bsr->attr)) {
150 if (regexec(bsr->fileregex_re, bsr->attr->fname, 0, NULL, 0) == 0) {
151 Dmsg2(dbglevel, "Matched pattern, fname=%s FI=%d\n",
152 bsr->attr->fname, rec->FileIndex);
154 Dmsg2(dbglevel, "Didn't match, skipping fname=%s FI=%d\n",
155 bsr->attr->fname, rec->FileIndex);
156 bsr->skip_file = true;
163 /*********************************************************************
165 * Match Bootstrap records
167 * returns 0 no match and reposition is set if we should
168 * reposition the tape
169 * returns -1 no additional matches possible
171 int match_bsr(BSR *bsr, DEV_RECORD *rec, VOLUME_LABEL *volrec, SESSION_LABEL *sessrec, JCR *jcr)
176 * The bsr->reposition flag is set any time a bsr is done.
177 * In this case, we can probably reposition the
178 * tape to the next available bsr position.
181 bsr->reposition = false;
182 stat = match_all(bsr, rec, volrec, sessrec, true, jcr);
184 * Note, bsr->reposition is set by match_all when
185 * a bsr is done. We turn it off if a match was
186 * found or if we cannot use positioning
188 if (stat != 0 || !bsr->use_positioning) {
189 bsr->reposition = false;
192 stat = 1; /* no bsr => match all */
198 * Find the next bsr that applies to the current tape.
199 * It is the one with the smallest VolFile position.
201 BSR *find_next_bsr(BSR *root_bsr, DEVICE *dev)
204 BSR *found_bsr = NULL;
206 /* Do tape/disk seeking only if CAP_POSITIONBLOCKS is on */
208 Dmsg0(dbglevel, "NULL root bsr pointer passed to find_next_bsr.\n");
211 if (!root_bsr->use_positioning ||
212 !root_bsr->reposition || !dev->has_cap(CAP_POSITIONBLOCKS)) {
213 Dmsg2(dbglevel, "No nxt_bsr use_pos=%d repos=%d\n", root_bsr->use_positioning, root_bsr->reposition);
216 Dmsg2(dbglevel, "use_pos=%d repos=%d\n", root_bsr->use_positioning, root_bsr->reposition);
217 root_bsr->mount_next_volume = false;
218 /* Walk through all bsrs to find the next one to use => smallest file,block */
219 for (bsr=root_bsr; bsr; bsr=bsr->next) {
220 if (bsr->done || !match_volume(bsr, bsr->volume, &dev->VolHdr, 1)) {
223 if (found_bsr == NULL) {
226 found_bsr = find_smallest_volfile(found_bsr, bsr);
230 * If we get to this point and found no bsr, it means
231 * that any additional bsr's must apply to the next
232 * tape, so set a flag.
234 if (found_bsr == NULL) {
235 root_bsr->mount_next_volume = true;
241 * Get the smallest address from this voladdr part
242 * Don't use "done" elements
244 static bool get_smallest_voladdr(BSR_VOLADDR *va, uint64_t *ret)
249 for (; va ; va = va->next) {
252 min_val = MIN(min_val, va->saddr);
264 * This routine needs to be fixed to only look at items that
265 * are not marked as done. Otherwise, it can find a bsr
266 * that has already been consumed, and this will cause the
267 * bsr to be used, thus we may seek back and re-read the
268 * same records, causing an error. This deficiency must
269 * be fixed. For the moment, it has been kludged in
270 * read_record.c to avoid seeking back if find_next_bsr
271 * returns a bsr pointing to a smaller address (file/block).
274 static BSR *find_smallest_volfile(BSR *found_bsr, BSR *bsr)
276 BSR *return_bsr = found_bsr;
279 uint32_t found_bsr_sfile, bsr_sfile;
280 uint32_t found_bsr_sblock, bsr_sblock;
281 uint64_t found_bsr_saddr, bsr_saddr;
283 /* if we have VolAddr, use it, else try with File and Block */
284 if (get_smallest_voladdr(found_bsr->voladdr, &found_bsr_saddr)) {
285 if (get_smallest_voladdr(bsr->voladdr, &bsr_saddr)) {
286 if (found_bsr_saddr > bsr_saddr) {
294 /* Find the smallest file in the found_bsr */
295 vf = found_bsr->volfile;
296 found_bsr_sfile = vf->sfile;
297 while ( (vf=vf->next) ) {
298 if (vf->sfile < found_bsr_sfile) {
299 found_bsr_sfile = vf->sfile;
303 /* Find the smallest file in the bsr */
305 bsr_sfile = vf->sfile;
306 while ( (vf=vf->next) ) {
307 if (vf->sfile < bsr_sfile) {
308 bsr_sfile = vf->sfile;
312 /* if the bsr file is less than the found_bsr file, return bsr */
313 if (found_bsr_sfile > bsr_sfile) {
315 } else if (found_bsr_sfile == bsr_sfile) {
316 /* Files are equal */
317 /* find smallest block in found_bsr */
318 vb = found_bsr->volblock;
319 found_bsr_sblock = vb->sblock;
320 while ( (vb=vb->next) ) {
321 if (vb->sblock < found_bsr_sblock) {
322 found_bsr_sblock = vb->sblock;
325 /* Find smallest block in bsr */
327 bsr_sblock = vb->sblock;
328 while ( (vb=vb->next) ) {
329 if (vb->sblock < bsr_sblock) {
330 bsr_sblock = vb->sblock;
333 /* Compare and return the smallest */
334 if (found_bsr_sblock > bsr_sblock) {
342 * Called after the signature record so that
343 * we can see if the current bsr has been
344 * fully processed (i.e. is done).
345 * The bsr argument is not used, but is included
346 * for consistency with the other match calls.
348 * Returns: true if we should reposition
351 bool is_this_bsr_done(BSR *bsr, DEV_RECORD *rec)
353 BSR *rbsr = rec->bsr;
354 Dmsg1(dbglevel, "match_set %d\n", rbsr != NULL);
360 if (rbsr->count && rbsr->found >= rbsr->count) {
362 rbsr->root->reposition = true;
363 Dmsg2(dbglevel, "is_end_this_bsr set reposition=1 count=%d found=%d\n",
364 rbsr->count, rbsr->found);
367 Dmsg2(dbglevel, "is_end_this_bsr not done count=%d found=%d\n",
368 rbsr->count, rbsr->found);
373 * Match all the components of current record
376 * returns -1 no additional matches possible
378 static int match_all(BSR *bsr, DEV_RECORD *rec, VOLUME_LABEL *volrec,
379 SESSION_LABEL *sessrec, bool done, JCR *jcr)
381 Dmsg0(dbglevel, "Enter match_all\n");
383 // Dmsg0(dbglevel, "bsr->done set\n");
386 if (!match_volume(bsr, bsr->volume, volrec, 1)) {
387 Dmsg2(dbglevel, "bsr fail bsr_vol=%s != rec read_vol=%s\n", bsr->volume->VolumeName,
391 Dmsg2(dbglevel, "OK bsr match bsr_vol=%s read_vol=%s\n", bsr->volume->VolumeName,
394 if (!match_volfile(bsr, bsr->volfile, rec, 1)) {
396 Dmsg3(dbglevel, "Fail on file=%u. bsr=%u,%u\n",
397 rec->File, bsr->volfile->sfile, bsr->volfile->efile);
402 if (!match_voladdr(bsr, bsr->voladdr, rec, 1)) {
404 Dmsg3(dbglevel, "Fail on Addr=%llu. bsr=%llu,%llu\n",
405 get_record_address(rec), bsr->voladdr->saddr, bsr->voladdr->eaddr);
410 if (!match_sesstime(bsr, bsr->sesstime, rec, 1)) {
411 Dmsg2(dbglevel, "Fail on sesstime. bsr=%u rec=%u\n",
412 bsr->sesstime->sesstime, rec->VolSessionTime);
416 /* NOTE!! This test MUST come after the sesstime test */
417 if (!match_sessid(bsr, bsr->sessid, rec)) {
418 Dmsg2(dbglevel, "Fail on sessid. bsr=%u rec=%u\n",
419 bsr->sessid->sessid, rec->VolSessionId);
423 /* NOTE!! This test MUST come after sesstime and sessid tests */
424 if (!match_findex(bsr, bsr->FileIndex, rec, 1)) {
425 Dmsg3(dbglevel, "Fail on findex=%d. bsr=%d,%d\n",
426 rec->FileIndex, bsr->FileIndex->findex, bsr->FileIndex->findex2);
429 if (bsr->FileIndex) {
430 Dmsg3(dbglevel, "match on findex=%d. bsr=%d,%d\n",
431 rec->FileIndex, bsr->FileIndex->findex, bsr->FileIndex->findex2);
434 if (!match_fileregex(bsr, rec, jcr)) {
435 Dmsg1(dbglevel, "Fail on fileregex='%s'\n", NPRT(bsr->fileregex));
439 /* This flag is set by match_fileregex (and perhaps other tests) */
440 if (bsr->skip_file) {
441 Dmsg1(dbglevel, "Skipping findex=%d\n", rec->FileIndex);
446 * If a count was specified and we have a FileIndex, assume
447 * it is a Bacula created bsr (or the equivalent). We
448 * then save the bsr where the match occurred so that
449 * after processing the record or records, we can update
450 * the found count. I.e. rec->bsr points to the bsr that
451 * satisfied the match.
453 if (bsr->count && bsr->FileIndex) {
455 Dmsg0(dbglevel, "Leave match_all 1\n");
456 return 1; /* this is a complete match */
460 * The selections below are not used by Bacula's
461 * restore command, and don't work because of
462 * the rec->bsr = bsr optimization above.
464 if (!match_jobid(bsr, bsr->JobId, sessrec, 1)) {
465 Dmsg0(dbglevel, "fail on JobId\n");
469 if (!match_job(bsr, bsr->job, sessrec, 1)) {
470 Dmsg0(dbglevel, "fail on Job\n");
473 if (!match_client(bsr, bsr->client, sessrec, 1)) {
474 Dmsg0(dbglevel, "fail on Client\n");
477 if (!match_job_type(bsr, bsr->JobType, sessrec, 1)) {
478 Dmsg0(dbglevel, "fail on Job type\n");
481 if (!match_job_level(bsr, bsr->JobLevel, sessrec, 1)) {
482 Dmsg0(dbglevel, "fail on Job level\n");
485 if (!match_stream(bsr, bsr->stream, rec, 1)) {
486 Dmsg0(dbglevel, "fail on stream\n");
493 return match_all(bsr->next, rec, volrec, sessrec, bsr->done && done, jcr);
495 if (bsr->done && done) {
496 Dmsg0(dbglevel, "Leave match all -1\n");
499 Dmsg0(dbglevel, "Leave match all 0\n");
503 static int match_volume(BSR *bsr, BSR_VOLUME *volume, VOLUME_LABEL *volrec, bool done)
506 return 0; /* Volume must match */
508 if (strcmp(volume->VolumeName, volrec->VolumeName) == 0) {
509 Dmsg1(dbglevel, "match_volume=%s\n", volrec->VolumeName);
513 return match_volume(bsr, volume->next, volrec, 1);
518 static int match_client(BSR *bsr, BSR_CLIENT *client, SESSION_LABEL *sessrec, bool done)
521 return 1; /* no specification matches all */
523 if (strcmp(client->ClientName, sessrec->ClientName) == 0) {
527 return match_client(bsr, client->next, sessrec, 1);
532 static int match_job(BSR *bsr, BSR_JOB *job, SESSION_LABEL *sessrec, bool done)
535 return 1; /* no specification matches all */
537 if (strcmp(job->Job, sessrec->Job) == 0) {
541 return match_job(bsr, job->next, sessrec, 1);
546 static int match_job_type(BSR *bsr, BSR_JOBTYPE *job_type, SESSION_LABEL *sessrec, bool done)
549 return 1; /* no specification matches all */
551 if (job_type->JobType == sessrec->JobType) {
554 if (job_type->next) {
555 return match_job_type(bsr, job_type->next, sessrec, 1);
560 static int match_job_level(BSR *bsr, BSR_JOBLEVEL *job_level, SESSION_LABEL *sessrec, bool done)
563 return 1; /* no specification matches all */
565 if (job_level->JobLevel == sessrec->JobLevel) {
568 if (job_level->next) {
569 return match_job_level(bsr, job_level->next, sessrec, 1);
574 static int match_jobid(BSR *bsr, BSR_JOBID *jobid, SESSION_LABEL *sessrec, bool done)
577 return 1; /* no specification matches all */
579 if (jobid->JobId <= sessrec->JobId && jobid->JobId2 >= sessrec->JobId) {
583 return match_jobid(bsr, jobid->next, sessrec, 1);
588 static int match_volfile(BSR *bsr, BSR_VOLFILE *volfile, DEV_RECORD *rec, bool done)
591 return 1; /* no specification matches all */
594 * The following code is turned off because this should now work
595 * with disk files too, though since a "volfile" is 4GB, it does
596 * not improve performance much.
599 /* For the moment, these tests work only with tapes. */
600 if (!(rec->state & REC_ISTAPE)) {
601 return 1; /* All File records OK for this match */
603 Dmsg3(dbglevel, "match_volfile: sfile=%u efile=%u recfile=%u\n",
604 volfile->sfile, volfile->efile, rec->File);
606 if (volfile->sfile <= rec->File && volfile->efile >= rec->File) {
609 /* Once we get past last efile, we are done */
610 if (rec->File > volfile->efile) {
611 volfile->done = true; /* set local done */
614 return match_volfile(bsr, volfile->next, rec, volfile->done && done);
617 /* If we are done and all prior matches are done, this bsr is finished */
618 if (volfile->done && done) {
620 bsr->root->reposition = true;
621 Dmsg2(dbglevel, "bsr done from volfile rec=%u volefile=%u\n",
622 rec->File, volfile->efile);
627 static int match_voladdr(BSR *bsr, BSR_VOLADDR *voladdr, DEV_RECORD *rec, bool done)
630 return 1; /* no specification matches all */
635 /* For the moment, these tests work only with disk. */
636 if (rec->state & REC_ISTAPE) {
637 uint32_t sFile = (voladdr->saddr)>>32;
638 uint32_t eFile = (voladdr->eaddr)>>32;
639 if (sFile <= rec->File && eFile >= rec->File) {
646 uint64_t addr = get_record_address(rec);
647 Dmsg6(dbglevel, "match_voladdr: saddr=%llu eaddr=%llu recaddr=%llu sfile=%u efile=%u recfile=%u\n",
648 voladdr->saddr, voladdr->eaddr, addr, voladdr->saddr>>32, voladdr->eaddr>>32, addr>>32);
650 if (voladdr->saddr <= addr && voladdr->eaddr >= addr) {
653 /* Once we get past last eblock, we are done */
654 if (addr > voladdr->eaddr) {
655 voladdr->done = true; /* set local done */
658 return match_voladdr(bsr, voladdr->next, rec, voladdr->done && done);
661 /* If we are done and all prior matches are done, this bsr is finished */
662 if (voladdr->done && done) {
664 bsr->root->reposition = true;
665 Dmsg2(dbglevel, "bsr done from voladdr rec=%llu voleaddr=%llu\n",
666 addr, voladdr->eaddr);
672 static int match_stream(BSR *bsr, BSR_STREAM *stream, DEV_RECORD *rec, bool done)
675 return 1; /* no specification matches all */
677 if (stream->stream == rec->Stream) {
681 return match_stream(bsr, stream->next, rec, 1);
686 static int match_sesstime(BSR *bsr, BSR_SESSTIME *sesstime, DEV_RECORD *rec, bool done)
689 return 1; /* no specification matches all */
691 if (sesstime->sesstime == rec->VolSessionTime) {
694 if (rec->VolSessionTime > sesstime->sesstime) {
695 sesstime->done = true;
697 if (sesstime->next) {
698 return match_sesstime(bsr, sesstime->next, rec, sesstime->done && done);
700 if (sesstime->done && done) {
702 bsr->root->reposition = true;
703 Dmsg0(dbglevel, "bsr done from sesstime\n");
709 * Note, we cannot mark bsr done based on session id because we may
710 * have interleaved records, and there may be more of what we want
713 static int match_sessid(BSR *bsr, BSR_SESSID *sessid, DEV_RECORD *rec)
716 return 1; /* no specification matches all */
718 if (sessid->sessid <= rec->VolSessionId && sessid->sessid2 >= rec->VolSessionId) {
722 return match_sessid(bsr, sessid->next, rec);
728 * When reading the Volume, the Volume Findex (rec->FileIndex) always
729 * are found in sequential order. Thus we can make optimizations.
731 * ***FIXME*** optimizations
732 * We could optimize by removing the recursion.
734 static int match_findex(BSR *bsr, BSR_FINDEX *findex, DEV_RECORD *rec, bool done)
737 return 1; /* no specification matches all */
740 if (findex->findex <= rec->FileIndex && findex->findex2 >= rec->FileIndex) {
741 Dmsg3(dbglevel, "Match on findex=%d. bsrFIs=%d,%d\n",
742 rec->FileIndex, findex->findex, findex->findex2);
745 if (rec->FileIndex > findex->findex2) {
750 return match_findex(bsr, findex->next, rec, findex->done && done);
752 if (findex->done && done) {
754 bsr->root->reposition = true;
755 Dmsg1(dbglevel, "bsr done from findex %d\n", rec->FileIndex);
760 uint64_t get_bsr_start_addr(BSR *bsr, uint32_t *file, uint32_t *block)
762 uint64_t bsr_addr = 0;
763 uint32_t sfile = 0, sblock = 0;
767 bsr_addr = bsr->voladdr->saddr;
768 sfile = bsr_addr>>32;
769 sblock = (uint32_t)bsr_addr;
771 } else if (bsr->volfile && bsr->volblock) {
772 bsr_addr = (((uint64_t)bsr->volfile->sfile)<<32)|bsr->volblock->sblock;
773 sfile = bsr->volfile->sfile;
774 sblock = bsr->volblock->sblock;