2 Bacula® - The Network Backup Solution
4 Copyright (C) 2002-2014 Free Software Foundation Europe e.V.
6 The main author of Bacula is Kern Sibbald, with contributions from many
7 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 Bacula® is a registered trademark of Kern Sibbald.
17 * Match Bootstrap Records (used for restores) against
20 * Kern Sibbald, June MMII
26 * Also for efficiency, once a bsr is done, it really should be
27 * delinked from the bsr chain. This will avoid the above
28 * problem and make traversal of the bsr chain more efficient.
38 #include "lib/fnmatch.h"
41 const int dbglevel = 500;
43 /* Forward references */
44 static int match_volume(BSR *bsr, BSR_VOLUME *volume, VOLUME_LABEL *volrec, bool done);
45 static int match_sesstime(BSR *bsr, BSR_SESSTIME *sesstime, DEV_RECORD *rec, bool done);
46 static int match_sessid(BSR *bsr, BSR_SESSID *sessid, DEV_RECORD *rec);
47 static int match_client(BSR *bsr, BSR_CLIENT *client, SESSION_LABEL *sessrec, bool done);
48 static int match_job(BSR *bsr, BSR_JOB *job, SESSION_LABEL *sessrec, bool done);
49 static int match_job_type(BSR *bsr, BSR_JOBTYPE *job_type, SESSION_LABEL *sessrec, bool done);
50 static int match_job_level(BSR *bsr, BSR_JOBLEVEL *job_level, SESSION_LABEL *sessrec, bool done);
51 static int match_jobid(BSR *bsr, BSR_JOBID *jobid, SESSION_LABEL *sessrec, bool done);
52 static int match_findex(BSR *bsr, BSR_FINDEX *findex, DEV_RECORD *rec, bool done);
53 static int match_volfile(BSR *bsr, BSR_VOLFILE *volfile, DEV_RECORD *rec, bool done);
54 static int match_voladdr(BSR *bsr, BSR_VOLADDR *voladdr, DEV_RECORD *rec, bool done);
55 static int match_stream(BSR *bsr, BSR_STREAM *stream, DEV_RECORD *rec, bool done);
56 static int match_all(BSR *bsr, DEV_RECORD *rec, VOLUME_LABEL *volrec, SESSION_LABEL *sessrec, bool done, JCR *jcr);
57 static int match_block_sesstime(BSR *bsr, BSR_SESSTIME *sesstime, DEV_BLOCK *block);
58 static int match_block_sessid(BSR *bsr, BSR_SESSID *sessid, DEV_BLOCK *block);
59 static BSR *find_smallest_volfile(BSR *fbsr, BSR *bsr);
62 /*********************************************************************
64 * If possible, position the archive device (tape) to read the
67 void position_bsr_block(BSR *bsr, DEV_BLOCK *block)
69 /* To be implemented */
72 /*********************************************************************
74 * Do fast block rejection based on bootstrap records.
75 * use_fast_rejection will be set if we have VolSessionId and VolSessTime
76 * in each record. When BlockVer is >= 2, we have those in the block header
77 * so can do fast rejection.
79 * returns: 1 if block may contain valid records
80 * 0 if block may be skipped (i.e. it contains no records of
81 * that can match the bsr).
84 int match_bsr_block(BSR *bsr, DEV_BLOCK *block)
86 if (!bsr || !bsr->use_fast_rejection || (block->BlockVer < 2)) {
87 return 1; /* cannot fast reject */
90 for ( ; bsr; bsr=bsr->next) {
91 if (!match_block_sesstime(bsr, bsr->sesstime, block)) {
94 if (!match_block_sessid(bsr, bsr->sessid, block)) {
102 static int match_block_sesstime(BSR *bsr, BSR_SESSTIME *sesstime, DEV_BLOCK *block)
105 return 1; /* no specification matches all */
107 if (sesstime->sesstime == block->VolSessionTime) {
110 if (sesstime->next) {
111 return match_block_sesstime(bsr, sesstime->next, block);
116 static int match_block_sessid(BSR *bsr, BSR_SESSID *sessid, DEV_BLOCK *block)
119 return 1; /* no specification matches all */
121 if (sessid->sessid <= block->VolSessionId && sessid->sessid2 >= block->VolSessionId) {
125 return match_block_sessid(bsr, sessid->next, block);
130 static int match_fileregex(BSR *bsr, DEV_RECORD *rec, JCR *jcr)
132 if (bsr->fileregex_re == NULL)
135 if (bsr->attr == NULL) {
136 bsr->attr = new_attr(jcr);
140 * The code breaks if the first record associated with a file is
143 if (rec->maskedStream == STREAM_UNIX_ATTRIBUTES ||
144 rec->maskedStream == STREAM_UNIX_ATTRIBUTES_EX) {
145 bsr->skip_file = false;
146 if (unpack_attributes_record(jcr, rec->Stream, rec->data, rec->data_len, bsr->attr)) {
147 if (regexec(bsr->fileregex_re, bsr->attr->fname, 0, NULL, 0) == 0) {
148 Dmsg2(dbglevel, "Matched pattern, fname=%s FI=%d\n",
149 bsr->attr->fname, rec->FileIndex);
151 Dmsg2(dbglevel, "Didn't match, skipping fname=%s FI=%d\n",
152 bsr->attr->fname, rec->FileIndex);
153 bsr->skip_file = true;
160 /*********************************************************************
162 * Match Bootstrap records
164 * returns 0 no match and reposition is set if we should
165 * reposition the tape
166 * returns -1 no additional matches possible
168 int match_bsr(BSR *bsr, DEV_RECORD *rec, VOLUME_LABEL *volrec, SESSION_LABEL *sessrec, JCR *jcr)
173 * The bsr->reposition flag is set any time a bsr is done.
174 * In this case, we can probably reposition the
175 * tape to the next available bsr position.
178 bsr->reposition = false;
179 stat = match_all(bsr, rec, volrec, sessrec, true, jcr);
181 * Note, bsr->reposition is set by match_all when
182 * a bsr is done. We turn it off if a match was
183 * found or if we cannot use positioning
185 if (stat != 0 || !bsr->use_positioning) {
186 bsr->reposition = false;
189 stat = 1; /* no bsr => match all */
195 * Find the next bsr that applies to the current tape.
196 * It is the one with the smallest VolFile position.
198 BSR *find_next_bsr(BSR *root_bsr, DEVICE *dev)
201 BSR *found_bsr = NULL;
203 /* Do tape/disk seeking only if CAP_POSITIONBLOCKS is on */
205 Dmsg0(dbglevel, "NULL root bsr pointer passed to find_next_bsr.\n");
208 if (!root_bsr->use_positioning ||
209 !root_bsr->reposition || !dev->has_cap(CAP_POSITIONBLOCKS)) {
210 Dmsg2(dbglevel, "No nxt_bsr use_pos=%d repos=%d\n", root_bsr->use_positioning, root_bsr->reposition);
213 Dmsg2(dbglevel, "use_pos=%d repos=%d\n", root_bsr->use_positioning, root_bsr->reposition);
214 root_bsr->mount_next_volume = false;
215 /* Walk through all bsrs to find the next one to use => smallest file,block */
216 for (bsr=root_bsr; bsr; bsr=bsr->next) {
217 if (bsr->done || !match_volume(bsr, bsr->volume, &dev->VolHdr, 1)) {
220 if (found_bsr == NULL) {
223 found_bsr = find_smallest_volfile(found_bsr, bsr);
227 * If we get to this point and found no bsr, it means
228 * that any additional bsr's must apply to the next
229 * tape, so set a flag.
231 if (found_bsr == NULL) {
232 root_bsr->mount_next_volume = true;
238 * Get the smallest address from this voladdr part
239 * Don't use "done" elements
241 static bool get_smallest_voladdr(BSR_VOLADDR *va, uint64_t *ret)
246 for (; va ; va = va->next) {
249 min_val = MIN(min_val, va->saddr);
261 * This routine needs to be fixed to only look at items that
262 * are not marked as done. Otherwise, it can find a bsr
263 * that has already been consumed, and this will cause the
264 * bsr to be used, thus we may seek back and re-read the
265 * same records, causing an error. This deficiency must
266 * be fixed. For the moment, it has been kludged in
267 * read_record.c to avoid seeking back if find_next_bsr
268 * returns a bsr pointing to a smaller address (file/block).
271 static BSR *find_smallest_volfile(BSR *found_bsr, BSR *bsr)
273 BSR *return_bsr = found_bsr;
276 uint32_t found_bsr_sfile, bsr_sfile;
277 uint32_t found_bsr_sblock, bsr_sblock;
278 uint64_t found_bsr_saddr, bsr_saddr;
280 /* if we have VolAddr, use it, else try with File and Block */
281 if (get_smallest_voladdr(found_bsr->voladdr, &found_bsr_saddr)) {
282 if (get_smallest_voladdr(bsr->voladdr, &bsr_saddr)) {
283 if (found_bsr_saddr > bsr_saddr) {
291 /* Find the smallest file in the found_bsr */
292 vf = found_bsr->volfile;
293 found_bsr_sfile = vf->sfile;
294 while ( (vf=vf->next) ) {
295 if (vf->sfile < found_bsr_sfile) {
296 found_bsr_sfile = vf->sfile;
300 /* Find the smallest file in the bsr */
302 bsr_sfile = vf->sfile;
303 while ( (vf=vf->next) ) {
304 if (vf->sfile < bsr_sfile) {
305 bsr_sfile = vf->sfile;
309 /* if the bsr file is less than the found_bsr file, return bsr */
310 if (found_bsr_sfile > bsr_sfile) {
312 } else if (found_bsr_sfile == bsr_sfile) {
313 /* Files are equal */
314 /* find smallest block in found_bsr */
315 vb = found_bsr->volblock;
316 found_bsr_sblock = vb->sblock;
317 while ( (vb=vb->next) ) {
318 if (vb->sblock < found_bsr_sblock) {
319 found_bsr_sblock = vb->sblock;
322 /* Find smallest block in bsr */
324 bsr_sblock = vb->sblock;
325 while ( (vb=vb->next) ) {
326 if (vb->sblock < bsr_sblock) {
327 bsr_sblock = vb->sblock;
330 /* Compare and return the smallest */
331 if (found_bsr_sblock > bsr_sblock) {
339 * Called after the signature record so that
340 * we can see if the current bsr has been
341 * fully processed (i.e. is done).
342 * The bsr argument is not used, but is included
343 * for consistency with the other match calls.
345 * Returns: true if we should reposition
348 bool is_this_bsr_done(BSR *bsr, DEV_RECORD *rec)
350 BSR *rbsr = rec->bsr;
351 Dmsg1(dbglevel, "match_set %d\n", rbsr != NULL);
357 if (rbsr->count && rbsr->found >= rbsr->count) {
359 rbsr->root->reposition = true;
360 Dmsg2(dbglevel, "is_end_this_bsr set reposition=1 count=%d found=%d\n",
361 rbsr->count, rbsr->found);
364 Dmsg2(dbglevel, "is_end_this_bsr not done count=%d found=%d\n",
365 rbsr->count, rbsr->found);
370 * Match all the components of current record
373 * returns -1 no additional matches possible
375 static int match_all(BSR *bsr, DEV_RECORD *rec, VOLUME_LABEL *volrec,
376 SESSION_LABEL *sessrec, bool done, JCR *jcr)
378 Dmsg0(dbglevel, "Enter match_all\n");
380 // Dmsg0(dbglevel, "bsr->done set\n");
383 if (!match_volume(bsr, bsr->volume, volrec, 1)) {
384 Dmsg2(dbglevel, "bsr fail bsr_vol=%s != rec read_vol=%s\n", bsr->volume->VolumeName,
388 Dmsg2(dbglevel, "OK bsr match bsr_vol=%s read_vol=%s\n", bsr->volume->VolumeName,
391 if (!match_volfile(bsr, bsr->volfile, rec, 1)) {
393 Dmsg3(dbglevel, "Fail on file=%u. bsr=%u,%u\n",
394 rec->File, bsr->volfile->sfile, bsr->volfile->efile);
399 if (!match_voladdr(bsr, bsr->voladdr, rec, 1)) {
401 Dmsg3(dbglevel, "Fail on Addr=%llu. bsr=%llu,%llu\n",
402 get_record_address(rec), bsr->voladdr->saddr, bsr->voladdr->eaddr);
407 if (!match_sesstime(bsr, bsr->sesstime, rec, 1)) {
408 Dmsg2(dbglevel, "Fail on sesstime. bsr=%u rec=%u\n",
409 bsr->sesstime->sesstime, rec->VolSessionTime);
413 /* NOTE!! This test MUST come after the sesstime test */
414 if (!match_sessid(bsr, bsr->sessid, rec)) {
415 Dmsg2(dbglevel, "Fail on sessid. bsr=%u rec=%u\n",
416 bsr->sessid->sessid, rec->VolSessionId);
420 /* NOTE!! This test MUST come after sesstime and sessid tests */
421 if (!match_findex(bsr, bsr->FileIndex, rec, 1)) {
422 Dmsg3(dbglevel, "Fail on findex=%d. bsr=%d,%d\n",
423 rec->FileIndex, bsr->FileIndex->findex, bsr->FileIndex->findex2);
426 if (bsr->FileIndex) {
427 Dmsg3(dbglevel, "match on findex=%d. bsr=%d,%d\n",
428 rec->FileIndex, bsr->FileIndex->findex, bsr->FileIndex->findex2);
431 if (!match_fileregex(bsr, rec, jcr)) {
432 Dmsg1(dbglevel, "Fail on fileregex='%s'\n", NPRT(bsr->fileregex));
436 /* This flag is set by match_fileregex (and perhaps other tests) */
437 if (bsr->skip_file) {
438 Dmsg1(dbglevel, "Skipping findex=%d\n", rec->FileIndex);
443 * If a count was specified and we have a FileIndex, assume
444 * it is a Bacula created bsr (or the equivalent). We
445 * then save the bsr where the match occurred so that
446 * after processing the record or records, we can update
447 * the found count. I.e. rec->bsr points to the bsr that
448 * satisfied the match.
450 if (bsr->count && bsr->FileIndex) {
452 Dmsg0(dbglevel, "Leave match_all 1\n");
453 return 1; /* this is a complete match */
457 * The selections below are not used by Bacula's
458 * restore command, and don't work because of
459 * the rec->bsr = bsr optimization above.
461 if (!match_jobid(bsr, bsr->JobId, sessrec, 1)) {
462 Dmsg0(dbglevel, "fail on JobId\n");
466 if (!match_job(bsr, bsr->job, sessrec, 1)) {
467 Dmsg0(dbglevel, "fail on Job\n");
470 if (!match_client(bsr, bsr->client, sessrec, 1)) {
471 Dmsg0(dbglevel, "fail on Client\n");
474 if (!match_job_type(bsr, bsr->JobType, sessrec, 1)) {
475 Dmsg0(dbglevel, "fail on Job type\n");
478 if (!match_job_level(bsr, bsr->JobLevel, sessrec, 1)) {
479 Dmsg0(dbglevel, "fail on Job level\n");
482 if (!match_stream(bsr, bsr->stream, rec, 1)) {
483 Dmsg0(dbglevel, "fail on stream\n");
490 return match_all(bsr->next, rec, volrec, sessrec, bsr->done && done, jcr);
492 if (bsr->done && done) {
493 Dmsg0(dbglevel, "Leave match all -1\n");
496 Dmsg0(dbglevel, "Leave match all 0\n");
500 static int match_volume(BSR *bsr, BSR_VOLUME *volume, VOLUME_LABEL *volrec, bool done)
503 return 0; /* Volume must match */
505 if (strcmp(volume->VolumeName, volrec->VolumeName) == 0) {
506 Dmsg1(dbglevel, "match_volume=%s\n", volrec->VolumeName);
510 return match_volume(bsr, volume->next, volrec, 1);
515 static int match_client(BSR *bsr, BSR_CLIENT *client, SESSION_LABEL *sessrec, bool done)
518 return 1; /* no specification matches all */
520 if (strcmp(client->ClientName, sessrec->ClientName) == 0) {
524 return match_client(bsr, client->next, sessrec, 1);
529 static int match_job(BSR *bsr, BSR_JOB *job, SESSION_LABEL *sessrec, bool done)
532 return 1; /* no specification matches all */
534 if (strcmp(job->Job, sessrec->Job) == 0) {
538 return match_job(bsr, job->next, sessrec, 1);
543 static int match_job_type(BSR *bsr, BSR_JOBTYPE *job_type, SESSION_LABEL *sessrec, bool done)
546 return 1; /* no specification matches all */
548 if (job_type->JobType == sessrec->JobType) {
551 if (job_type->next) {
552 return match_job_type(bsr, job_type->next, sessrec, 1);
557 static int match_job_level(BSR *bsr, BSR_JOBLEVEL *job_level, SESSION_LABEL *sessrec, bool done)
560 return 1; /* no specification matches all */
562 if (job_level->JobLevel == sessrec->JobLevel) {
565 if (job_level->next) {
566 return match_job_level(bsr, job_level->next, sessrec, 1);
571 static int match_jobid(BSR *bsr, BSR_JOBID *jobid, SESSION_LABEL *sessrec, bool done)
574 return 1; /* no specification matches all */
576 if (jobid->JobId <= sessrec->JobId && jobid->JobId2 >= sessrec->JobId) {
580 return match_jobid(bsr, jobid->next, sessrec, 1);
585 static int match_volfile(BSR *bsr, BSR_VOLFILE *volfile, DEV_RECORD *rec, bool done)
588 return 1; /* no specification matches all */
591 * The following code is turned off because this should now work
592 * with disk files too, though since a "volfile" is 4GB, it does
593 * not improve performance much.
596 /* For the moment, these tests work only with tapes. */
597 if (!(rec->state & REC_ISTAPE)) {
598 return 1; /* All File records OK for this match */
600 Dmsg3(dbglevel, "match_volfile: sfile=%u efile=%u recfile=%u\n",
601 volfile->sfile, volfile->efile, rec->File);
603 if (volfile->sfile <= rec->File && volfile->efile >= rec->File) {
606 /* Once we get past last efile, we are done */
607 if (rec->File > volfile->efile) {
608 volfile->done = true; /* set local done */
611 return match_volfile(bsr, volfile->next, rec, volfile->done && done);
614 /* If we are done and all prior matches are done, this bsr is finished */
615 if (volfile->done && done) {
617 bsr->root->reposition = true;
618 Dmsg2(dbglevel, "bsr done from volfile rec=%u volefile=%u\n",
619 rec->File, volfile->efile);
624 static int match_voladdr(BSR *bsr, BSR_VOLADDR *voladdr, DEV_RECORD *rec, bool done)
627 return 1; /* no specification matches all */
632 /* For the moment, these tests work only with disk. */
633 if (rec->state & REC_ISTAPE) {
634 uint32_t sFile = (voladdr->saddr)>>32;
635 uint32_t eFile = (voladdr->eaddr)>>32;
636 if (sFile <= rec->File && eFile >= rec->File) {
643 uint64_t addr = get_record_address(rec);
644 Dmsg6(dbglevel, "match_voladdr: saddr=%llu eaddr=%llu recaddr=%llu sfile=%u efile=%u recfile=%u\n",
645 voladdr->saddr, voladdr->eaddr, addr, voladdr->saddr>>32, voladdr->eaddr>>32, addr>>32);
647 if (voladdr->saddr <= addr && voladdr->eaddr >= addr) {
650 /* Once we get past last eblock, we are done */
651 if (addr > voladdr->eaddr) {
652 voladdr->done = true; /* set local done */
655 return match_voladdr(bsr, voladdr->next, rec, voladdr->done && done);
658 /* If we are done and all prior matches are done, this bsr is finished */
659 if (voladdr->done && done) {
661 bsr->root->reposition = true;
662 Dmsg2(dbglevel, "bsr done from voladdr rec=%llu voleaddr=%llu\n",
663 addr, voladdr->eaddr);
669 static int match_stream(BSR *bsr, BSR_STREAM *stream, DEV_RECORD *rec, bool done)
672 return 1; /* no specification matches all */
674 if (stream->stream == rec->Stream) {
678 return match_stream(bsr, stream->next, rec, 1);
683 static int match_sesstime(BSR *bsr, BSR_SESSTIME *sesstime, DEV_RECORD *rec, bool done)
686 return 1; /* no specification matches all */
688 if (sesstime->sesstime == rec->VolSessionTime) {
691 if (rec->VolSessionTime > sesstime->sesstime) {
692 sesstime->done = true;
694 if (sesstime->next) {
695 return match_sesstime(bsr, sesstime->next, rec, sesstime->done && done);
697 if (sesstime->done && done) {
699 bsr->root->reposition = true;
700 Dmsg0(dbglevel, "bsr done from sesstime\n");
706 * Note, we cannot mark bsr done based on session id because we may
707 * have interleaved records, and there may be more of what we want
710 static int match_sessid(BSR *bsr, BSR_SESSID *sessid, DEV_RECORD *rec)
713 return 1; /* no specification matches all */
715 if (sessid->sessid <= rec->VolSessionId && sessid->sessid2 >= rec->VolSessionId) {
719 return match_sessid(bsr, sessid->next, rec);
725 * When reading the Volume, the Volume Findex (rec->FileIndex) always
726 * are found in sequential order. Thus we can make optimizations.
728 * ***FIXME*** optimizations
729 * We could optimize by removing the recursion.
731 static int match_findex(BSR *bsr, BSR_FINDEX *findex, DEV_RECORD *rec, bool done)
734 return 1; /* no specification matches all */
737 if (findex->findex <= rec->FileIndex && findex->findex2 >= rec->FileIndex) {
738 Dmsg3(dbglevel, "Match on findex=%d. bsrFIs=%d,%d\n",
739 rec->FileIndex, findex->findex, findex->findex2);
742 if (rec->FileIndex > findex->findex2) {
747 return match_findex(bsr, findex->next, rec, findex->done && done);
749 if (findex->done && done) {
751 bsr->root->reposition = true;
752 Dmsg1(dbglevel, "bsr done from findex %d\n", rec->FileIndex);
757 uint64_t get_bsr_start_addr(BSR *bsr, uint32_t *file, uint32_t *block)
759 uint64_t bsr_addr = 0;
760 uint32_t sfile = 0, sblock = 0;
764 bsr_addr = bsr->voladdr->saddr;
765 sfile = bsr_addr>>32;
766 sblock = (uint32_t)bsr_addr;
768 } else if (bsr->volfile && bsr->volblock) {
769 bsr_addr = (((uint64_t)bsr->volfile->sfile)<<32)|bsr->volblock->sblock;
770 sfile = bsr->volfile->sfile;
771 sblock = bsr->volblock->sblock;