2 Bacula(R) - The Network Backup Solution
4 Copyright (C) 2000-2015 Kern Sibbald
5 Copyright (C) 2000-2014 Free Software Foundation Europe e.V.
7 The original author of Bacula is Kern Sibbald, with contributions
8 from many others, a complete list can be found in the file AUTHORS.
10 You may use this file and others of this release according to the
11 license defined in the LICENSE file, which includes the Affero General
12 Public License, v3.0 ("AGPLv3") and some additional permissions and
13 terms pursuant to its AGPLv3 Section 7.
15 This notice must be preserved when any source code is
16 conveyed and/or propagated.
18 Bacula(R) is a registered trademark of Kern Sibbald.
21 * Match Bootstrap Records (used for restores) against
24 * Kern Sibbald, June MMII
30 * Also for efficiency, once a bsr is done, it really should be
31 * delinked from the bsr chain. This will avoid the above
32 * problem and make traversal of the bsr chain more efficient.
42 #include "lib/fnmatch.h"
45 const int dbglevel = 500;
47 /* Forward references */
48 static int match_volume(BSR *bsr, BSR_VOLUME *volume, VOLUME_LABEL *volrec, bool done);
49 static int match_sesstime(BSR *bsr, BSR_SESSTIME *sesstime, DEV_RECORD *rec, bool done);
50 static int match_sessid(BSR *bsr, BSR_SESSID *sessid, DEV_RECORD *rec);
51 static int match_client(BSR *bsr, BSR_CLIENT *client, SESSION_LABEL *sessrec, bool done);
52 static int match_job(BSR *bsr, BSR_JOB *job, SESSION_LABEL *sessrec, bool done);
53 static int match_job_type(BSR *bsr, BSR_JOBTYPE *job_type, SESSION_LABEL *sessrec, bool done);
54 static int match_job_level(BSR *bsr, BSR_JOBLEVEL *job_level, SESSION_LABEL *sessrec, bool done);
55 static int match_jobid(BSR *bsr, BSR_JOBID *jobid, SESSION_LABEL *sessrec, bool done);
56 static int match_findex(BSR *bsr, BSR_FINDEX *findex, DEV_RECORD *rec, bool done);
57 static int match_volfile(BSR *bsr, BSR_VOLFILE *volfile, DEV_RECORD *rec, bool done);
58 static int match_voladdr(BSR *bsr, BSR_VOLADDR *voladdr, DEV_RECORD *rec, bool done);
59 static int match_stream(BSR *bsr, BSR_STREAM *stream, DEV_RECORD *rec, bool done);
60 static int match_all(BSR *bsr, DEV_RECORD *rec, VOLUME_LABEL *volrec, SESSION_LABEL *sessrec, bool done, JCR *jcr);
61 static int match_block_sesstime(BSR *bsr, BSR_SESSTIME *sesstime, DEV_BLOCK *block);
62 static int match_block_sessid(BSR *bsr, BSR_SESSID *sessid, DEV_BLOCK *block);
63 static BSR *find_smallest_volfile(BSR *fbsr, BSR *bsr);
66 /*********************************************************************
68 * If possible, position the archive device (tape) to read the
71 void position_bsr_block(BSR *bsr, DEV_BLOCK *block)
73 /* To be implemented */
76 /*********************************************************************
78 * Do fast block rejection based on bootstrap records.
79 * use_fast_rejection will be set if we have VolSessionId and VolSessTime
80 * in each record. When BlockVer is >= 2, we have those in the block header
81 * so can do fast rejection.
83 * returns: 1 if block may contain valid records
84 * 0 if block may be skipped (i.e. it contains no records of
85 * that can match the bsr).
88 int match_bsr_block(BSR *bsr, DEV_BLOCK *block)
90 if (!bsr || !bsr->use_fast_rejection || (block->BlockVer < 2)) {
91 return 1; /* cannot fast reject */
94 for ( ; bsr; bsr=bsr->next) {
95 if (!match_block_sesstime(bsr, bsr->sesstime, block)) {
98 if (!match_block_sessid(bsr, bsr->sessid, block)) {
106 static int match_block_sesstime(BSR *bsr, BSR_SESSTIME *sesstime, DEV_BLOCK *block)
109 return 1; /* no specification matches all */
111 if (sesstime->sesstime == block->VolSessionTime) {
114 if (sesstime->next) {
115 return match_block_sesstime(bsr, sesstime->next, block);
120 static int match_block_sessid(BSR *bsr, BSR_SESSID *sessid, DEV_BLOCK *block)
123 return 1; /* no specification matches all */
125 if (sessid->sessid <= block->VolSessionId && sessid->sessid2 >= block->VolSessionId) {
129 return match_block_sessid(bsr, sessid->next, block);
134 static int match_fileregex(BSR *bsr, DEV_RECORD *rec, JCR *jcr)
136 if (bsr->fileregex_re == NULL)
139 if (bsr->attr == NULL) {
140 bsr->attr = new_attr(jcr);
144 * The code breaks if the first record associated with a file is
147 if (rec->maskedStream == STREAM_UNIX_ATTRIBUTES ||
148 rec->maskedStream == STREAM_UNIX_ATTRIBUTES_EX) {
149 bsr->skip_file = false;
150 if (unpack_attributes_record(jcr, rec->Stream, rec->data, rec->data_len, bsr->attr)) {
151 if (regexec(bsr->fileregex_re, bsr->attr->fname, 0, NULL, 0) == 0) {
152 Dmsg2(dbglevel, "Matched pattern, fname=%s FI=%d\n",
153 bsr->attr->fname, rec->FileIndex);
155 Dmsg2(dbglevel, "Didn't match, skipping fname=%s FI=%d\n",
156 bsr->attr->fname, rec->FileIndex);
157 bsr->skip_file = true;
164 /*********************************************************************
166 * Match Bootstrap records
168 * returns 0 no match and reposition is set if we should
169 * reposition the tape
170 * returns -1 no additional matches possible
172 int match_bsr(BSR *bsr, DEV_RECORD *rec, VOLUME_LABEL *volrec, SESSION_LABEL *sessrec, JCR *jcr)
177 * The bsr->reposition flag is set any time a bsr is done.
178 * In this case, we can probably reposition the
179 * tape to the next available bsr position.
182 bsr->reposition = false;
183 stat = match_all(bsr, rec, volrec, sessrec, true, jcr);
185 * Note, bsr->reposition is set by match_all when
186 * a bsr is done. We turn it off if a match was
187 * found or if we cannot use positioning
189 if (stat != 0 || !bsr->use_positioning) {
190 bsr->reposition = false;
193 stat = 1; /* no bsr => match all */
199 * Find the next bsr that applies to the current tape.
200 * It is the one with the smallest VolFile position.
202 BSR *find_next_bsr(BSR *root_bsr, DEVICE *dev)
205 BSR *found_bsr = NULL;
207 /* Do tape/disk seeking only if CAP_POSITIONBLOCKS is on */
209 Dmsg0(dbglevel, "NULL root bsr pointer passed to find_next_bsr.\n");
212 if (!root_bsr->use_positioning ||
213 !root_bsr->reposition || !dev->has_cap(CAP_POSITIONBLOCKS)) {
214 Dmsg2(dbglevel, "No nxt_bsr use_pos=%d repos=%d\n", root_bsr->use_positioning, root_bsr->reposition);
217 Dmsg2(dbglevel, "use_pos=%d repos=%d\n", root_bsr->use_positioning, root_bsr->reposition);
218 root_bsr->mount_next_volume = false;
219 /* Walk through all bsrs to find the next one to use => smallest file,block */
220 for (bsr=root_bsr; bsr; bsr=bsr->next) {
221 if (bsr->done || !match_volume(bsr, bsr->volume, &dev->VolHdr, 1)) {
224 if (found_bsr == NULL) {
227 found_bsr = find_smallest_volfile(found_bsr, bsr);
231 * If we get to this point and found no bsr, it means
232 * that any additional bsr's must apply to the next
233 * tape, so set a flag.
235 if (found_bsr == NULL) {
236 root_bsr->mount_next_volume = true;
242 * Get the smallest address from this voladdr part
243 * Don't use "done" elements
245 static bool get_smallest_voladdr(BSR_VOLADDR *va, uint64_t *ret)
250 for (; va ; va = va->next) {
253 min_val = MIN(min_val, va->saddr);
265 * This routine needs to be fixed to only look at items that
266 * are not marked as done. Otherwise, it can find a bsr
267 * that has already been consumed, and this will cause the
268 * bsr to be used, thus we may seek back and re-read the
269 * same records, causing an error. This deficiency must
270 * be fixed. For the moment, it has been kludged in
271 * read_record.c to avoid seeking back if find_next_bsr
272 * returns a bsr pointing to a smaller address (file/block).
275 static BSR *find_smallest_volfile(BSR *found_bsr, BSR *bsr)
277 BSR *return_bsr = found_bsr;
280 uint32_t found_bsr_sfile, bsr_sfile;
281 uint32_t found_bsr_sblock, bsr_sblock;
282 uint64_t found_bsr_saddr, bsr_saddr;
284 /* if we have VolAddr, use it, else try with File and Block */
285 if (get_smallest_voladdr(found_bsr->voladdr, &found_bsr_saddr)) {
286 if (get_smallest_voladdr(bsr->voladdr, &bsr_saddr)) {
287 if (found_bsr_saddr > bsr_saddr) {
295 /* Find the smallest file in the found_bsr */
296 vf = found_bsr->volfile;
297 found_bsr_sfile = vf->sfile;
298 while ( (vf=vf->next) ) {
299 if (vf->sfile < found_bsr_sfile) {
300 found_bsr_sfile = vf->sfile;
304 /* Find the smallest file in the bsr */
306 bsr_sfile = vf->sfile;
307 while ( (vf=vf->next) ) {
308 if (vf->sfile < bsr_sfile) {
309 bsr_sfile = vf->sfile;
313 /* if the bsr file is less than the found_bsr file, return bsr */
314 if (found_bsr_sfile > bsr_sfile) {
316 } else if (found_bsr_sfile == bsr_sfile) {
317 /* Files are equal */
318 /* find smallest block in found_bsr */
319 vb = found_bsr->volblock;
320 found_bsr_sblock = vb->sblock;
321 while ( (vb=vb->next) ) {
322 if (vb->sblock < found_bsr_sblock) {
323 found_bsr_sblock = vb->sblock;
326 /* Find smallest block in bsr */
328 bsr_sblock = vb->sblock;
329 while ( (vb=vb->next) ) {
330 if (vb->sblock < bsr_sblock) {
331 bsr_sblock = vb->sblock;
334 /* Compare and return the smallest */
335 if (found_bsr_sblock > bsr_sblock) {
343 * Called after the signature record so that
344 * we can see if the current bsr has been
345 * fully processed (i.e. is done).
346 * The bsr argument is not used, but is included
347 * for consistency with the other match calls.
349 * Returns: true if we should reposition
352 bool is_this_bsr_done(BSR *bsr, DEV_RECORD *rec)
354 BSR *rbsr = rec->bsr;
355 Dmsg1(dbglevel, "match_set %d\n", rbsr != NULL);
361 if (rbsr->count && rbsr->found >= rbsr->count) {
363 rbsr->root->reposition = true;
364 Dmsg2(dbglevel, "is_end_this_bsr set reposition=1 count=%d found=%d\n",
365 rbsr->count, rbsr->found);
368 Dmsg2(dbglevel, "is_end_this_bsr not done count=%d found=%d\n",
369 rbsr->count, rbsr->found);
374 * Match all the components of current record
377 * returns -1 no additional matches possible
379 static int match_all(BSR *bsr, DEV_RECORD *rec, VOLUME_LABEL *volrec,
380 SESSION_LABEL *sessrec, bool done, JCR *jcr)
382 Dmsg0(dbglevel, "Enter match_all\n");
384 // Dmsg0(dbglevel, "bsr->done set\n");
387 if (!match_volume(bsr, bsr->volume, volrec, 1)) {
388 Dmsg2(dbglevel, "bsr fail bsr_vol=%s != rec read_vol=%s\n", bsr->volume->VolumeName,
392 Dmsg2(dbglevel, "OK bsr match bsr_vol=%s read_vol=%s\n", bsr->volume->VolumeName,
395 if (!match_volfile(bsr, bsr->volfile, rec, 1)) {
397 Dmsg3(dbglevel, "Fail on file=%u. bsr=%u,%u\n",
398 rec->File, bsr->volfile->sfile, bsr->volfile->efile);
403 if (!match_voladdr(bsr, bsr->voladdr, rec, 1)) {
405 Dmsg3(dbglevel, "Fail on Addr=%llu. bsr=%llu,%llu\n",
406 get_record_address(rec), bsr->voladdr->saddr, bsr->voladdr->eaddr);
411 if (!match_sesstime(bsr, bsr->sesstime, rec, 1)) {
412 Dmsg2(dbglevel, "Fail on sesstime. bsr=%u rec=%u\n",
413 bsr->sesstime->sesstime, rec->VolSessionTime);
417 /* NOTE!! This test MUST come after the sesstime test */
418 if (!match_sessid(bsr, bsr->sessid, rec)) {
419 Dmsg2(dbglevel, "Fail on sessid. bsr=%u rec=%u\n",
420 bsr->sessid->sessid, rec->VolSessionId);
424 /* NOTE!! This test MUST come after sesstime and sessid tests */
425 if (!match_findex(bsr, bsr->FileIndex, rec, 1)) {
426 Dmsg3(dbglevel, "Fail on findex=%d. bsr=%d,%d\n",
427 rec->FileIndex, bsr->FileIndex->findex, bsr->FileIndex->findex2);
430 if (bsr->FileIndex) {
431 Dmsg3(dbglevel, "match on findex=%d. bsr=%d,%d\n",
432 rec->FileIndex, bsr->FileIndex->findex, bsr->FileIndex->findex2);
435 if (!match_fileregex(bsr, rec, jcr)) {
436 Dmsg1(dbglevel, "Fail on fileregex='%s'\n", NPRT(bsr->fileregex));
440 /* This flag is set by match_fileregex (and perhaps other tests) */
441 if (bsr->skip_file) {
442 Dmsg1(dbglevel, "Skipping findex=%d\n", rec->FileIndex);
447 * If a count was specified and we have a FileIndex, assume
448 * it is a Bacula created bsr (or the equivalent). We
449 * then save the bsr where the match occurred so that
450 * after processing the record or records, we can update
451 * the found count. I.e. rec->bsr points to the bsr that
452 * satisfied the match.
454 if (bsr->count && bsr->FileIndex) {
456 Dmsg0(dbglevel, "Leave match_all 1\n");
457 return 1; /* this is a complete match */
461 * The selections below are not used by Bacula's
462 * restore command, and don't work because of
463 * the rec->bsr = bsr optimization above.
465 if (!match_jobid(bsr, bsr->JobId, sessrec, 1)) {
466 Dmsg0(dbglevel, "fail on JobId\n");
470 if (!match_job(bsr, bsr->job, sessrec, 1)) {
471 Dmsg0(dbglevel, "fail on Job\n");
474 if (!match_client(bsr, bsr->client, sessrec, 1)) {
475 Dmsg0(dbglevel, "fail on Client\n");
478 if (!match_job_type(bsr, bsr->JobType, sessrec, 1)) {
479 Dmsg0(dbglevel, "fail on Job type\n");
482 if (!match_job_level(bsr, bsr->JobLevel, sessrec, 1)) {
483 Dmsg0(dbglevel, "fail on Job level\n");
486 if (!match_stream(bsr, bsr->stream, rec, 1)) {
487 Dmsg0(dbglevel, "fail on stream\n");
494 return match_all(bsr->next, rec, volrec, sessrec, bsr->done && done, jcr);
496 if (bsr->done && done) {
497 Dmsg0(dbglevel, "Leave match all -1\n");
500 Dmsg0(dbglevel, "Leave match all 0\n");
504 static int match_volume(BSR *bsr, BSR_VOLUME *volume, VOLUME_LABEL *volrec, bool done)
507 return 0; /* Volume must match */
509 if (strcmp(volume->VolumeName, volrec->VolumeName) == 0) {
510 Dmsg1(dbglevel, "match_volume=%s\n", volrec->VolumeName);
514 return match_volume(bsr, volume->next, volrec, 1);
519 static int match_client(BSR *bsr, BSR_CLIENT *client, SESSION_LABEL *sessrec, bool done)
522 return 1; /* no specification matches all */
524 if (strcmp(client->ClientName, sessrec->ClientName) == 0) {
528 return match_client(bsr, client->next, sessrec, 1);
533 static int match_job(BSR *bsr, BSR_JOB *job, SESSION_LABEL *sessrec, bool done)
536 return 1; /* no specification matches all */
538 if (strcmp(job->Job, sessrec->Job) == 0) {
542 return match_job(bsr, job->next, sessrec, 1);
547 static int match_job_type(BSR *bsr, BSR_JOBTYPE *job_type, SESSION_LABEL *sessrec, bool done)
550 return 1; /* no specification matches all */
552 if (job_type->JobType == sessrec->JobType) {
555 if (job_type->next) {
556 return match_job_type(bsr, job_type->next, sessrec, 1);
561 static int match_job_level(BSR *bsr, BSR_JOBLEVEL *job_level, SESSION_LABEL *sessrec, bool done)
564 return 1; /* no specification matches all */
566 if (job_level->JobLevel == sessrec->JobLevel) {
569 if (job_level->next) {
570 return match_job_level(bsr, job_level->next, sessrec, 1);
575 static int match_jobid(BSR *bsr, BSR_JOBID *jobid, SESSION_LABEL *sessrec, bool done)
578 return 1; /* no specification matches all */
580 if (jobid->JobId <= sessrec->JobId && jobid->JobId2 >= sessrec->JobId) {
584 return match_jobid(bsr, jobid->next, sessrec, 1);
589 static int match_volfile(BSR *bsr, BSR_VOLFILE *volfile, DEV_RECORD *rec, bool done)
592 return 1; /* no specification matches all */
595 * The following code is turned off because this should now work
596 * with disk files too, though since a "volfile" is 4GB, it does
597 * not improve performance much.
600 /* For the moment, these tests work only with tapes. */
601 if (!(rec->state & REC_ISTAPE)) {
602 return 1; /* All File records OK for this match */
604 Dmsg3(dbglevel, "match_volfile: sfile=%u efile=%u recfile=%u\n",
605 volfile->sfile, volfile->efile, rec->File);
607 if (volfile->sfile <= rec->File && volfile->efile >= rec->File) {
610 /* Once we get past last efile, we are done */
611 if (rec->File > volfile->efile) {
612 volfile->done = true; /* set local done */
615 return match_volfile(bsr, volfile->next, rec, volfile->done && done);
618 /* If we are done and all prior matches are done, this bsr is finished */
619 if (volfile->done && done) {
621 bsr->root->reposition = true;
622 Dmsg2(dbglevel, "bsr done from volfile rec=%u volefile=%u\n",
623 rec->File, volfile->efile);
628 static int match_voladdr(BSR *bsr, BSR_VOLADDR *voladdr, DEV_RECORD *rec, bool done)
631 return 1; /* no specification matches all */
636 /* For the moment, these tests work only with disk. */
637 if (rec->state & REC_ISTAPE) {
638 uint32_t sFile = (voladdr->saddr)>>32;
639 uint32_t eFile = (voladdr->eaddr)>>32;
640 if (sFile <= rec->File && eFile >= rec->File) {
647 uint64_t addr = get_record_address(rec);
648 Dmsg6(dbglevel, "match_voladdr: saddr=%llu eaddr=%llu recaddr=%llu sfile=%u efile=%u recfile=%u\n",
649 voladdr->saddr, voladdr->eaddr, addr, voladdr->saddr>>32, voladdr->eaddr>>32, addr>>32);
651 if (voladdr->saddr <= addr && voladdr->eaddr >= addr) {
654 /* Once we get past last eblock, we are done */
655 if (addr > voladdr->eaddr) {
656 voladdr->done = true; /* set local done */
659 return match_voladdr(bsr, voladdr->next, rec, voladdr->done && done);
662 /* If we are done and all prior matches are done, this bsr is finished */
663 if (voladdr->done && done) {
665 bsr->root->reposition = true;
666 Dmsg2(dbglevel, "bsr done from voladdr rec=%llu voleaddr=%llu\n",
667 addr, voladdr->eaddr);
673 static int match_stream(BSR *bsr, BSR_STREAM *stream, DEV_RECORD *rec, bool done)
676 return 1; /* no specification matches all */
678 if (stream->stream == rec->Stream) {
682 return match_stream(bsr, stream->next, rec, 1);
687 static int match_sesstime(BSR *bsr, BSR_SESSTIME *sesstime, DEV_RECORD *rec, bool done)
690 return 1; /* no specification matches all */
692 if (sesstime->sesstime == rec->VolSessionTime) {
695 if (rec->VolSessionTime > sesstime->sesstime) {
696 sesstime->done = true;
698 if (sesstime->next) {
699 return match_sesstime(bsr, sesstime->next, rec, sesstime->done && done);
701 if (sesstime->done && done) {
703 bsr->root->reposition = true;
704 Dmsg0(dbglevel, "bsr done from sesstime\n");
710 * Note, we cannot mark bsr done based on session id because we may
711 * have interleaved records, and there may be more of what we want
714 static int match_sessid(BSR *bsr, BSR_SESSID *sessid, DEV_RECORD *rec)
717 return 1; /* no specification matches all */
719 if (sessid->sessid <= rec->VolSessionId && sessid->sessid2 >= rec->VolSessionId) {
723 return match_sessid(bsr, sessid->next, rec);
729 * When reading the Volume, the Volume Findex (rec->FileIndex) always
730 * are found in sequential order. Thus we can make optimizations.
732 * ***FIXME*** optimizations
733 * We could optimize by removing the recursion.
735 static int match_findex(BSR *bsr, BSR_FINDEX *findex, DEV_RECORD *rec, bool done)
738 return 1; /* no specification matches all */
741 if (findex->findex <= rec->FileIndex && findex->findex2 >= rec->FileIndex) {
742 Dmsg3(dbglevel, "Match on findex=%d. bsrFIs=%d,%d\n",
743 rec->FileIndex, findex->findex, findex->findex2);
746 if (rec->FileIndex > findex->findex2) {
751 return match_findex(bsr, findex->next, rec, findex->done && done);
753 if (findex->done && done) {
755 bsr->root->reposition = true;
756 Dmsg1(dbglevel, "bsr done from findex %d\n", rec->FileIndex);
761 uint64_t get_bsr_start_addr(BSR *bsr, uint32_t *file, uint32_t *block)
763 uint64_t bsr_addr = 0;
764 uint32_t sfile = 0, sblock = 0;
768 bsr_addr = bsr->voladdr->saddr;
769 sfile = bsr_addr>>32;
770 sblock = (uint32_t)bsr_addr;
772 } else if (bsr->volfile && bsr->volblock) {
773 bsr_addr = (((uint64_t)bsr->volfile->sfile)<<32)|bsr->volblock->sblock;
774 sfile = bsr->volfile->sfile;
775 sblock = bsr->volblock->sblock;