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(050, "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=%lld. bsr=%lld,%lld\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(050, "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");
495 Dmsg0(050, "Leave match_all 1\n");
500 return match_all(bsr->next, rec, volrec, sessrec, bsr->done && done, jcr);
502 if (bsr->done && done) {
503 Dmsg0(050, "Leave match all -1\n");
506 Dmsg0(050, "Leave match all 0\n");
510 static int match_volume(BSR *bsr, BSR_VOLUME *volume, VOLUME_LABEL *volrec, bool done)
513 return 0; /* Volume must match */
515 if (strcmp(volume->VolumeName, volrec->VolumeName) == 0) {
516 Dmsg1(050, "match_volume=%s\n", volrec->VolumeName);
520 return match_volume(bsr, volume->next, volrec, 1);
525 static int match_client(BSR *bsr, BSR_CLIENT *client, SESSION_LABEL *sessrec, bool done)
528 return 1; /* no specification matches all */
530 if (strcmp(client->ClientName, sessrec->ClientName) == 0) {
534 return match_client(bsr, client->next, sessrec, 1);
539 static int match_job(BSR *bsr, BSR_JOB *job, SESSION_LABEL *sessrec, bool done)
542 return 1; /* no specification matches all */
544 if (strcmp(job->Job, sessrec->Job) == 0) {
548 return match_job(bsr, job->next, sessrec, 1);
553 static int match_job_type(BSR *bsr, BSR_JOBTYPE *job_type, SESSION_LABEL *sessrec, bool done)
556 return 1; /* no specification matches all */
558 if (job_type->JobType == sessrec->JobType) {
561 if (job_type->next) {
562 return match_job_type(bsr, job_type->next, sessrec, 1);
567 static int match_job_level(BSR *bsr, BSR_JOBLEVEL *job_level, SESSION_LABEL *sessrec, bool done)
570 return 1; /* no specification matches all */
572 if (job_level->JobLevel == sessrec->JobLevel) {
575 if (job_level->next) {
576 return match_job_level(bsr, job_level->next, sessrec, 1);
581 static int match_jobid(BSR *bsr, BSR_JOBID *jobid, SESSION_LABEL *sessrec, bool done)
584 return 1; /* no specification matches all */
586 if (jobid->JobId <= sessrec->JobId && jobid->JobId2 >= sessrec->JobId) {
590 return match_jobid(bsr, jobid->next, sessrec, 1);
595 static int match_volfile(BSR *bsr, BSR_VOLFILE *volfile, DEV_RECORD *rec, bool done)
598 return 1; /* no specification matches all */
601 * The following code is turned off because this should now work
602 * with disk files too, though since a "volfile" is 4GB, it does
603 * not improve performance much.
606 /* For the moment, these tests work only with tapes. */
607 if (!(rec->state & REC_ISTAPE)) {
608 return 1; /* All File records OK for this match */
610 Dmsg3(dbglevel, "match_volfile: sfile=%u efile=%u recfile=%u\n",
611 volfile->sfile, volfile->efile, rec->File);
613 if (volfile->sfile <= rec->File && volfile->efile >= rec->File) {
616 /* Once we get past last efile, we are done */
617 if (rec->File > volfile->efile) {
618 volfile->done = true; /* set local done */
621 return match_volfile(bsr, volfile->next, rec, volfile->done && done);
624 /* If we are done and all prior matches are done, this bsr is finished */
625 if (volfile->done && done) {
627 bsr->root->reposition = true;
628 Dmsg2(dbglevel, "bsr done from volfile rec=%u volefile=%u\n",
629 rec->File, volfile->efile);
634 static int match_voladdr(BSR *bsr, BSR_VOLADDR *voladdr, DEV_RECORD *rec, bool done)
637 return 1; /* no specification matches all */
639 /* For the moment, these tests work only with disk. */
640 if (rec->state & REC_ISTAPE) {
641 return 1; /* All File records OK for this match */
643 uint64_t addr = get_record_address(rec);
644 // Dmsg3(dbglevel, "match_voladdr: saddr=%lld eaddr=%lld recaddr=%lld\n",
645 // volblock->saddr, volblock->eaddr, addr);
646 if (voladdr->saddr <= addr && voladdr->eaddr >= addr) {
649 /* Once we get past last eblock, we are done */
650 if (addr > voladdr->eaddr) {
651 voladdr->done = true; /* set local done */
654 return match_voladdr(bsr, voladdr->next, rec, voladdr->done && done);
657 /* If we are done and all prior matches are done, this bsr is finished */
658 if (voladdr->done && done) {
660 bsr->root->reposition = true;
661 Dmsg2(dbglevel, "bsr done from voladdr rec=%lld voleaddr=%lld\n",
662 addr, voladdr->eaddr);
668 static int match_stream(BSR *bsr, BSR_STREAM *stream, DEV_RECORD *rec, bool done)
671 return 1; /* no specification matches all */
673 if (stream->stream == rec->Stream) {
677 return match_stream(bsr, stream->next, rec, 1);
682 static int match_sesstime(BSR *bsr, BSR_SESSTIME *sesstime, DEV_RECORD *rec, bool done)
685 return 1; /* no specification matches all */
687 if (sesstime->sesstime == rec->VolSessionTime) {
690 if (rec->VolSessionTime > sesstime->sesstime) {
691 sesstime->done = true;
693 if (sesstime->next) {
694 return match_sesstime(bsr, sesstime->next, rec, sesstime->done && done);
696 if (sesstime->done && done) {
698 bsr->root->reposition = true;
699 Dmsg0(dbglevel, "bsr done from sesstime\n");
705 * Note, we cannot mark bsr done based on session id because we may
706 * have interleaved records, and there may be more of what we want
709 static int match_sessid(BSR *bsr, BSR_SESSID *sessid, DEV_RECORD *rec)
712 return 1; /* no specification matches all */
714 if (sessid->sessid <= rec->VolSessionId && sessid->sessid2 >= rec->VolSessionId) {
718 return match_sessid(bsr, sessid->next, rec);
724 * When reading the Volume, the Volume Findex (rec->FileIndex) always
725 * are found in sequential order. Thus we can make optimizations.
727 * ***FIXME*** optimizations
728 * We could optimize by removing the recursion.
730 static int match_findex(BSR *bsr, BSR_FINDEX *findex, DEV_RECORD *rec, bool done)
733 return 1; /* no specification matches all */
736 if (findex->findex <= rec->FileIndex && findex->findex2 >= rec->FileIndex) {
737 Dmsg3(dbglevel, "Match on findex=%d. bsrFIs=%d,%d\n",
738 rec->FileIndex, findex->findex, findex->findex2);
741 if (rec->FileIndex > findex->findex2) {
746 return match_findex(bsr, findex->next, rec, findex->done && done);
748 if (findex->done && done) {
750 bsr->root->reposition = true;
751 Dmsg1(dbglevel, "bsr done from findex %d\n", rec->FileIndex);
756 uint64_t get_bsr_start_addr(BSR *bsr, uint32_t *file, uint32_t *block)
758 uint64_t bsr_addr = 0;
759 uint32_t sfile = 0, sblock = 0;
763 bsr_addr = bsr->voladdr->saddr;
764 sfile = bsr_addr>>32;
765 sblock = (uint32_t)bsr_addr;
767 } else if (bsr->volfile && bsr->volblock) {
768 bsr_addr = (((uint64_t)bsr->volfile->sfile)<<32)|bsr->volblock->sblock;
769 sfile = bsr->volfile->sfile;
770 sblock = bsr->volblock->sblock;