2 Bacula® - The Network Backup Solution
4 Copyright (C) 2002-2008 Free Software Foundation Europe e.V.
6 The main author of Bacula is Kern Sibbald, with contributions from
7 many others, a complete list can be found in the file AUTHORS.
8 This program is Free Software; you can redistribute it and/or
9 modify it under the terms of version two of the GNU General Public
10 License as published by the Free Software Foundation and included
13 This program is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
23 Bacula® is a registered trademark of Kern Sibbald.
24 The licensor of Bacula is the Free Software Foundation Europe
25 (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26 Switzerland, email:ftf@fsfeurope.org.
29 * Match Bootstrap Records (used for restores) against
32 * Kern Sibbald, June MMII
39 * Also for efficiency, once a bsr is done, it really should be
40 * delinked from the bsr chain. This will avoid the above
41 * problem and make traversal of the bsr chain more efficient.
51 #include "lib/fnmatch.h"
54 const int dbglevel = 500;
56 /* Forward references */
57 static int match_volume(BSR *bsr, BSR_VOLUME *volume, VOLUME_LABEL *volrec, bool done);
58 static int match_sesstime(BSR *bsr, BSR_SESSTIME *sesstime, DEV_RECORD *rec, bool done);
59 static int match_sessid(BSR *bsr, BSR_SESSID *sessid, DEV_RECORD *rec);
60 static int match_client(BSR *bsr, BSR_CLIENT *client, SESSION_LABEL *sessrec, bool done);
61 static int match_job(BSR *bsr, BSR_JOB *job, SESSION_LABEL *sessrec, bool done);
62 static int match_job_type(BSR *bsr, BSR_JOBTYPE *job_type, SESSION_LABEL *sessrec, bool done);
63 static int match_job_level(BSR *bsr, BSR_JOBLEVEL *job_level, SESSION_LABEL *sessrec, bool done);
64 static int match_jobid(BSR *bsr, BSR_JOBID *jobid, SESSION_LABEL *sessrec, bool done);
65 static int match_findex(BSR *bsr, BSR_FINDEX *findex, DEV_RECORD *rec, bool done);
66 static int match_volfile(BSR *bsr, BSR_VOLFILE *volfile, DEV_RECORD *rec, bool done);
67 static int match_voladdr(BSR *bsr, BSR_VOLADDR *voladdr, DEV_RECORD *rec, bool done);
68 static int match_stream(BSR *bsr, BSR_STREAM *stream, DEV_RECORD *rec, bool done);
69 static int match_all(BSR *bsr, DEV_RECORD *rec, VOLUME_LABEL *volrec, SESSION_LABEL *sessrec, bool done, JCR *jcr);
70 static int match_block_sesstime(BSR *bsr, BSR_SESSTIME *sesstime, DEV_BLOCK *block);
71 static int match_block_sessid(BSR *bsr, BSR_SESSID *sessid, DEV_BLOCK *block);
72 static BSR *find_smallest_volfile(BSR *fbsr, BSR *bsr);
75 /*********************************************************************
77 * If possible, position the archive device (tape) to read the
80 void position_bsr_block(BSR *bsr, DEV_BLOCK *block)
82 /* To be implemented */
85 /*********************************************************************
87 * Do fast block rejection based on bootstrap records.
88 * use_fast_rejection will be set if we have VolSessionId and VolSessTime
89 * in each record. When BlockVer is >= 2, we have those in the block header
90 * so can do fast rejection.
92 * returns: 1 if block may contain valid records
93 * 0 if block may be skipped (i.e. it contains no records of
94 * that can match the bsr).
97 int match_bsr_block(BSR *bsr, DEV_BLOCK *block)
99 if (!bsr || !bsr->use_fast_rejection || (block->BlockVer < 2)) {
100 return 1; /* cannot fast reject */
103 for ( ; bsr; bsr=bsr->next) {
104 if (!match_block_sesstime(bsr, bsr->sesstime, block)) {
107 if (!match_block_sessid(bsr, bsr->sessid, block)) {
115 static int match_block_sesstime(BSR *bsr, BSR_SESSTIME *sesstime, DEV_BLOCK *block)
118 return 1; /* no specification matches all */
120 if (sesstime->sesstime == block->VolSessionTime) {
123 if (sesstime->next) {
124 return match_block_sesstime(bsr, sesstime->next, block);
129 static int match_block_sessid(BSR *bsr, BSR_SESSID *sessid, DEV_BLOCK *block)
132 return 1; /* no specification matches all */
134 if (sessid->sessid <= block->VolSessionId && sessid->sessid2 >= block->VolSessionId) {
138 return match_block_sessid(bsr, sessid->next, block);
143 static int match_fileregex(BSR *bsr, DEV_RECORD *rec, JCR *jcr)
145 if (bsr->fileregex_re == NULL)
148 if (bsr->attr == NULL)
149 bsr->attr = new_attr(jcr);
151 /* The code breaks if the first record associated with a file is
154 if (rec->Stream == STREAM_UNIX_ATTRIBUTES ||
155 rec->Stream == STREAM_UNIX_ATTRIBUTES_EX) {
156 bsr->skip_file = false;
157 if (unpack_attributes_record(jcr, rec->Stream, rec->data, bsr->attr)) {
158 if (regexec(bsr->fileregex_re, bsr->attr->fname, 0, NULL, 0) == 0) {
159 Dmsg2(dbglevel, "Matched pattern, fname=%s FI=%d\n",
160 bsr->attr->fname, rec->FileIndex);
162 Dmsg2(dbglevel, "Didn't match, skipping fname=%s FI=%d\n",
163 bsr->attr->fname, rec->FileIndex);
164 bsr->skip_file = true;
171 /*********************************************************************
173 * Match Bootstrap records
175 * returns 0 no match and reposition is set if we should
176 * reposition the tape
177 * returns -1 no additional matches possible
179 int match_bsr(BSR *bsr, DEV_RECORD *rec, VOLUME_LABEL *volrec, SESSION_LABEL *sessrec, JCR *jcr)
184 * The bsr->reposition flag is set any time a bsr is done.
185 * In this case, we can probably reposition the
186 * tape to the next available bsr position.
189 bsr->reposition = false;
190 stat = match_all(bsr, rec, volrec, sessrec, true, jcr);
192 * Note, bsr->reposition is set by match_all when
193 * a bsr is done. We turn it off if a match was
194 * found or if we cannot use positioning
196 if (stat != 0 || !bsr->use_positioning) {
197 bsr->reposition = false;
200 stat = 1; /* no bsr => match all */
206 * Find the next bsr that applies to the current tape.
207 * It is the one with the smallest VolFile position.
209 BSR *find_next_bsr(BSR *root_bsr, DEVICE *dev)
212 BSR *found_bsr = NULL;
214 /* Do tape/disk seeking only if CAP_POSITIONBLOCKS is on */
216 Dmsg0(dbglevel, "NULL root bsr pointer passed to find_next_bsr.\n");
219 if (!root_bsr->use_positioning ||
220 !root_bsr->reposition || !dev->has_cap(CAP_POSITIONBLOCKS)) {
221 Dmsg2(dbglevel, "No nxt_bsr use_pos=%d repos=%d\n", root_bsr->use_positioning, root_bsr->reposition);
224 Dmsg2(dbglevel, "use_pos=%d repos=%d\n", root_bsr->use_positioning, root_bsr->reposition);
225 root_bsr->mount_next_volume = false;
226 /* Walk through all bsrs to find the next one to use => smallest file,block */
227 for (bsr=root_bsr; bsr; bsr=bsr->next) {
228 if (bsr->done || !match_volume(bsr, bsr->volume, &dev->VolHdr, 1)) {
231 if (found_bsr == NULL) {
234 found_bsr = find_smallest_volfile(found_bsr, bsr);
238 * If we get to this point and found no bsr, it means
239 * that any additional bsr's must apply to the next
240 * tape, so set a flag.
242 if (found_bsr == NULL) {
243 root_bsr->mount_next_volume = true;
249 * Get the smallest address from this voladdr part
250 * Don't use "done" elements
252 static bool get_smallest_voladdr(BSR_VOLADDR *va, uint64_t *ret)
257 for (; va ; va = va->next) {
260 min_val = MIN(min_val, va->saddr);
272 * This routine needs to be fixed to only look at items that
273 * are not marked as done. Otherwise, it can find a bsr
274 * that has already been consumed, and this will cause the
275 * bsr to be used, thus we may seek back and re-read the
276 * same records, causing an error. This deficiency must
277 * be fixed. For the moment, it has been kludged in
278 * read_record.c to avoid seeking back if find_next_bsr
279 * returns a bsr pointing to a smaller address (file/block).
282 static BSR *find_smallest_volfile(BSR *found_bsr, BSR *bsr)
284 BSR *return_bsr = found_bsr;
287 uint32_t found_bsr_sfile, bsr_sfile;
288 uint32_t found_bsr_sblock, bsr_sblock;
289 uint64_t found_bsr_saddr, bsr_saddr;
291 /* if we have VolAddr, use it, else try with File and Block */
292 if (get_smallest_voladdr(found_bsr->voladdr, &found_bsr_saddr)) {
293 if (get_smallest_voladdr(bsr->voladdr, &bsr_saddr)) {
294 if (found_bsr_saddr > bsr_saddr) {
302 /* Find the smallest file in the found_bsr */
303 vf = found_bsr->volfile;
304 found_bsr_sfile = vf->sfile;
305 while ( (vf=vf->next) ) {
306 if (vf->sfile < found_bsr_sfile) {
307 found_bsr_sfile = vf->sfile;
311 /* Find the smallest file in the bsr */
313 bsr_sfile = vf->sfile;
314 while ( (vf=vf->next) ) {
315 if (vf->sfile < bsr_sfile) {
316 bsr_sfile = vf->sfile;
320 /* if the bsr file is less than the found_bsr file, return bsr */
321 if (found_bsr_sfile > bsr_sfile) {
323 } else if (found_bsr_sfile == bsr_sfile) {
324 /* Files are equal */
325 /* find smallest block in found_bsr */
326 vb = found_bsr->volblock;
327 found_bsr_sblock = vb->sblock;
328 while ( (vb=vb->next) ) {
329 if (vb->sblock < found_bsr_sblock) {
330 found_bsr_sblock = vb->sblock;
333 /* Find smallest block in bsr */
335 bsr_sblock = vb->sblock;
336 while ( (vb=vb->next) ) {
337 if (vb->sblock < bsr_sblock) {
338 bsr_sblock = vb->sblock;
341 /* Compare and return the smallest */
342 if (found_bsr_sblock > bsr_sblock) {
350 * Called after the signature record so that
351 * we can see if the current bsr has been
352 * fully processed (i.e. is done).
353 * The bsr argument is not used, but is included
354 * for consistency with the other match calls.
356 * Returns: true if we should reposition
359 bool is_this_bsr_done(BSR *bsr, DEV_RECORD *rec)
361 BSR *rbsr = rec->bsr;
362 Dmsg1(dbglevel, "match_set %d\n", rbsr != NULL);
368 if (rbsr->count && rbsr->found >= rbsr->count) {
370 rbsr->root->reposition = true;
371 Dmsg2(dbglevel, "is_end_this_bsr set reposition=1 count=%d found=%d\n",
372 rbsr->count, rbsr->found);
375 Dmsg2(dbglevel, "is_end_this_bsr not done count=%d found=%d\n",
376 rbsr->count, rbsr->found);
381 * Match all the components of current record
384 * returns -1 no additional matches possible
386 static int match_all(BSR *bsr, DEV_RECORD *rec, VOLUME_LABEL *volrec,
387 SESSION_LABEL *sessrec, bool done, JCR *jcr)
389 Dmsg0(dbglevel, "Enter match_all\n");
391 // Dmsg0(dbglevel, "bsr->done set\n");
394 if (!match_volume(bsr, bsr->volume, volrec, 1)) {
395 Dmsg2(dbglevel, "bsr fail bsr_vol=%s != rec read_vol=%s\n", bsr->volume->VolumeName,
399 Dmsg2(dbglevel, "OK bsr match bsr_vol=%s read_vol=%s\n", bsr->volume->VolumeName,
402 if (!match_volfile(bsr, bsr->volfile, rec, 1)) {
404 Dmsg3(dbglevel, "Fail on file=%u. bsr=%u,%u\n",
405 rec->File, bsr->volfile->sfile, bsr->volfile->efile);
410 if (!match_voladdr(bsr, bsr->voladdr, rec, 1)) {
412 Dmsg3(dbglevel, "Fail on Addr=%llu. bsr=%llu,%llu\n",
413 get_record_address(rec), bsr->voladdr->saddr, bsr->voladdr->eaddr);
418 if (!match_sesstime(bsr, bsr->sesstime, rec, 1)) {
419 Dmsg2(dbglevel, "Fail on sesstime. bsr=%u rec=%u\n",
420 bsr->sesstime->sesstime, rec->VolSessionTime);
424 /* NOTE!! This test MUST come after the sesstime test */
425 if (!match_sessid(bsr, bsr->sessid, rec)) {
426 Dmsg2(dbglevel, "Fail on sessid. bsr=%u rec=%u\n",
427 bsr->sessid->sessid, rec->VolSessionId);
431 /* NOTE!! This test MUST come after sesstime and sessid tests */
432 if (!match_findex(bsr, bsr->FileIndex, rec, 1)) {
433 Dmsg3(dbglevel, "Fail on findex=%d. bsr=%d,%d\n",
434 rec->FileIndex, bsr->FileIndex->findex, bsr->FileIndex->findex2);
437 Dmsg3(dbglevel, "match on findex=%d. bsr=%d,%d\n",
438 rec->FileIndex, bsr->FileIndex->findex, bsr->FileIndex->findex2);
440 if (!match_fileregex(bsr, rec, jcr)) {
441 Dmsg1(dbglevel, "Fail on fileregex='%s'\n", bsr->fileregex);
445 /* This flag is set by match_fileregex (and perhaps other tests) */
446 if (bsr->skip_file) {
447 Dmsg1(dbglevel, "Skipping findex=%d\n", rec->FileIndex);
452 * If a count was specified and we have a FileIndex, assume
453 * it is a Bacula created bsr (or the equivalent). We
454 * then save the bsr where the match occurred so that
455 * after processing the record or records, we can update
456 * the found count. I.e. rec->bsr points to the bsr that
457 * satisfied the match.
459 if (bsr->count && bsr->FileIndex) {
461 Dmsg0(dbglevel, "Leave match_all 1\n");
462 return 1; /* this is a complete match */
466 * The selections below are not used by Bacula's
467 * restore command, and don't work because of
468 * the rec->bsr = bsr optimization above.
470 if (!match_jobid(bsr, bsr->JobId, sessrec, 1)) {
471 Dmsg0(dbglevel, "fail on JobId\n");
475 if (!match_job(bsr, bsr->job, sessrec, 1)) {
476 Dmsg0(dbglevel, "fail on Job\n");
479 if (!match_client(bsr, bsr->client, sessrec, 1)) {
480 Dmsg0(dbglevel, "fail on Client\n");
483 if (!match_job_type(bsr, bsr->JobType, sessrec, 1)) {
484 Dmsg0(dbglevel, "fail on Job type\n");
487 if (!match_job_level(bsr, bsr->JobLevel, sessrec, 1)) {
488 Dmsg0(dbglevel, "fail on Job level\n");
491 if (!match_stream(bsr, bsr->stream, rec, 1)) {
492 Dmsg0(dbglevel, "fail on stream\n");
499 return match_all(bsr->next, rec, volrec, sessrec, bsr->done && done, jcr);
501 if (bsr->done && done) {
502 Dmsg0(dbglevel, "Leave match all -1\n");
505 Dmsg0(dbglevel, "Leave match all 0\n");
509 static int match_volume(BSR *bsr, BSR_VOLUME *volume, VOLUME_LABEL *volrec, bool done)
512 return 0; /* Volume must match */
514 if (strcmp(volume->VolumeName, volrec->VolumeName) == 0) {
515 Dmsg1(dbglevel, "match_volume=%s\n", volrec->VolumeName);
519 return match_volume(bsr, volume->next, volrec, 1);
524 static int match_client(BSR *bsr, BSR_CLIENT *client, SESSION_LABEL *sessrec, bool done)
527 return 1; /* no specification matches all */
529 if (strcmp(client->ClientName, sessrec->ClientName) == 0) {
533 return match_client(bsr, client->next, sessrec, 1);
538 static int match_job(BSR *bsr, BSR_JOB *job, SESSION_LABEL *sessrec, bool done)
541 return 1; /* no specification matches all */
543 if (strcmp(job->Job, sessrec->Job) == 0) {
547 return match_job(bsr, job->next, sessrec, 1);
552 static int match_job_type(BSR *bsr, BSR_JOBTYPE *job_type, SESSION_LABEL *sessrec, bool done)
555 return 1; /* no specification matches all */
557 if (job_type->JobType == sessrec->JobType) {
560 if (job_type->next) {
561 return match_job_type(bsr, job_type->next, sessrec, 1);
566 static int match_job_level(BSR *bsr, BSR_JOBLEVEL *job_level, SESSION_LABEL *sessrec, bool done)
569 return 1; /* no specification matches all */
571 if (job_level->JobLevel == sessrec->JobLevel) {
574 if (job_level->next) {
575 return match_job_level(bsr, job_level->next, sessrec, 1);
580 static int match_jobid(BSR *bsr, BSR_JOBID *jobid, SESSION_LABEL *sessrec, bool done)
583 return 1; /* no specification matches all */
585 if (jobid->JobId <= sessrec->JobId && jobid->JobId2 >= sessrec->JobId) {
589 return match_jobid(bsr, jobid->next, sessrec, 1);
594 static int match_volfile(BSR *bsr, BSR_VOLFILE *volfile, DEV_RECORD *rec, bool done)
597 return 1; /* no specification matches all */
600 * The following code is turned off because this should now work
601 * with disk files too, though since a "volfile" is 4GB, it does
602 * not improve performance much.
605 /* For the moment, these tests work only with tapes. */
606 if (!(rec->state & REC_ISTAPE)) {
607 return 1; /* All File records OK for this match */
609 Dmsg3(dbglevel, "match_volfile: sfile=%u efile=%u recfile=%u\n",
610 volfile->sfile, volfile->efile, rec->File);
612 if (volfile->sfile <= rec->File && volfile->efile >= rec->File) {
615 /* Once we get past last efile, we are done */
616 if (rec->File > volfile->efile) {
617 volfile->done = true; /* set local done */
620 return match_volfile(bsr, volfile->next, rec, volfile->done && done);
623 /* If we are done and all prior matches are done, this bsr is finished */
624 if (volfile->done && done) {
626 bsr->root->reposition = true;
627 Dmsg2(dbglevel, "bsr done from volfile rec=%u volefile=%u\n",
628 rec->File, volfile->efile);
633 static int match_voladdr(BSR *bsr, BSR_VOLADDR *voladdr, DEV_RECORD *rec, bool done)
636 return 1; /* no specification matches all */
641 /* For the moment, these tests work only with disk. */
642 if (rec->state & REC_ISTAPE) {
643 uint32_t sFile = (voladdr->saddr)>>32;
644 uint32_t eFile = (voladdr->eaddr)>>32;
645 if (sFile <= rec->File && eFile >= rec->File) {
652 uint64_t addr = get_record_address(rec);
653 Dmsg6(dbglevel, "match_voladdr: saddr=%llu eaddr=%llu recaddr=%llu sfile=%u efile=%u recfile=%u\n",
654 voladdr->saddr, voladdr->eaddr, addr, voladdr->saddr>>32, voladdr->eaddr>>32, addr>>32);
656 if (voladdr->saddr <= addr && voladdr->eaddr >= addr) {
659 /* Once we get past last eblock, we are done */
660 if (addr > voladdr->eaddr) {
661 voladdr->done = true; /* set local done */
664 return match_voladdr(bsr, voladdr->next, rec, voladdr->done && done);
667 /* If we are done and all prior matches are done, this bsr is finished */
668 if (voladdr->done && done) {
670 bsr->root->reposition = true;
671 Dmsg2(dbglevel, "bsr done from voladdr rec=%llu voleaddr=%llu\n",
672 addr, voladdr->eaddr);
678 static int match_stream(BSR *bsr, BSR_STREAM *stream, DEV_RECORD *rec, bool done)
681 return 1; /* no specification matches all */
683 if (stream->stream == rec->Stream) {
687 return match_stream(bsr, stream->next, rec, 1);
692 static int match_sesstime(BSR *bsr, BSR_SESSTIME *sesstime, DEV_RECORD *rec, bool done)
695 return 1; /* no specification matches all */
697 if (sesstime->sesstime == rec->VolSessionTime) {
700 if (rec->VolSessionTime > sesstime->sesstime) {
701 sesstime->done = true;
703 if (sesstime->next) {
704 return match_sesstime(bsr, sesstime->next, rec, sesstime->done && done);
706 if (sesstime->done && done) {
708 bsr->root->reposition = true;
709 Dmsg0(dbglevel, "bsr done from sesstime\n");
715 * Note, we cannot mark bsr done based on session id because we may
716 * have interleaved records, and there may be more of what we want
719 static int match_sessid(BSR *bsr, BSR_SESSID *sessid, DEV_RECORD *rec)
722 return 1; /* no specification matches all */
724 if (sessid->sessid <= rec->VolSessionId && sessid->sessid2 >= rec->VolSessionId) {
728 return match_sessid(bsr, sessid->next, rec);
734 * When reading the Volume, the Volume Findex (rec->FileIndex) always
735 * are found in sequential order. Thus we can make optimizations.
737 * ***FIXME*** optimizations
738 * We could optimize by removing the recursion.
740 static int match_findex(BSR *bsr, BSR_FINDEX *findex, DEV_RECORD *rec, bool done)
743 return 1; /* no specification matches all */
746 if (findex->findex <= rec->FileIndex && findex->findex2 >= rec->FileIndex) {
747 Dmsg3(dbglevel, "Match on findex=%d. bsrFIs=%d,%d\n",
748 rec->FileIndex, findex->findex, findex->findex2);
751 if (rec->FileIndex > findex->findex2) {
756 return match_findex(bsr, findex->next, rec, findex->done && done);
758 if (findex->done && done) {
760 bsr->root->reposition = true;
761 Dmsg1(dbglevel, "bsr done from findex %d\n", rec->FileIndex);
766 uint64_t get_bsr_start_addr(BSR *bsr, uint32_t *file, uint32_t *block)
768 uint64_t bsr_addr = 0;
769 uint32_t sfile = 0, sblock = 0;
773 bsr_addr = bsr->voladdr->saddr;
774 sfile = bsr_addr>>32;
775 sblock = (uint32_t)bsr_addr;
777 } else if (bsr->volfile && bsr->volblock) {
778 bsr_addr = (((uint64_t)bsr->volfile->sfile)<<32)|bsr->volblock->sblock;
779 sfile = bsr->volfile->sfile;
780 sblock = bsr->volblock->sblock;