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 * find_smallest_volfile needs to be fixed to only look at items that
40 * are not marked as done. Otherwise, it can find a bsr
41 * that has already been consumed, and this will cause the
42 * bsr to be used, thus we may seek back and re-read the
43 * same records, causing an error. This deficiency must
44 * be fixed. For the moment, it has been kludged in
45 * read_record.c to avoid seeking back if find_next_bsr
46 * returns a bsr pointing to a smaller address (file/block).
48 * Also for efficiency, once a bsr is done, it really should be
49 * delinked from the bsr chain. This will avoid the above
50 * problem and make traversal of the bsr chain more efficient.
60 #include "lib/fnmatch.h"
63 const int dbglevel = 500;
65 /* Forward references */
66 static int match_volume(BSR *bsr, BSR_VOLUME *volume, VOLUME_LABEL *volrec, bool done);
67 static int match_sesstime(BSR *bsr, BSR_SESSTIME *sesstime, DEV_RECORD *rec, bool done);
68 static int match_sessid(BSR *bsr, BSR_SESSID *sessid, DEV_RECORD *rec);
69 static int match_client(BSR *bsr, BSR_CLIENT *client, SESSION_LABEL *sessrec, bool done);
70 static int match_job(BSR *bsr, BSR_JOB *job, SESSION_LABEL *sessrec, bool done);
71 static int match_job_type(BSR *bsr, BSR_JOBTYPE *job_type, SESSION_LABEL *sessrec, bool done);
72 static int match_job_level(BSR *bsr, BSR_JOBLEVEL *job_level, SESSION_LABEL *sessrec, bool done);
73 static int match_jobid(BSR *bsr, BSR_JOBID *jobid, SESSION_LABEL *sessrec, bool done);
74 static int match_findex(BSR *bsr, BSR_FINDEX *findex, DEV_RECORD *rec, bool done);
75 static int match_volfile(BSR *bsr, BSR_VOLFILE *volfile, DEV_RECORD *rec, bool done);
76 static int match_volblock(BSR *bsr, BSR_VOLBLOCK *volblock, DEV_RECORD *rec, bool done);
77 static int match_stream(BSR *bsr, BSR_STREAM *stream, DEV_RECORD *rec, bool done);
78 static int match_all(BSR *bsr, DEV_RECORD *rec, VOLUME_LABEL *volrec, SESSION_LABEL *sessrec, bool done, JCR *jcr);
79 static int match_block_sesstime(BSR *bsr, BSR_SESSTIME *sesstime, DEV_BLOCK *block);
80 static int match_block_sessid(BSR *bsr, BSR_SESSID *sessid, DEV_BLOCK *block);
81 static BSR *find_smallest_volfile(BSR *fbsr, BSR *bsr);
84 /*********************************************************************
86 * If possible, position the archive device (tape) to read the
89 void position_bsr_block(BSR *bsr, DEV_BLOCK *block)
91 /* To be implemented */
94 /*********************************************************************
96 * Do fast block rejection based on bootstrap records.
97 * use_fast_rejection will be set if we have VolSessionId and VolSessTime
98 * in each record. When BlockVer is >= 2, we have those in the block header
99 * so can do fast rejection.
101 * returns: 1 if block may contain valid records
102 * 0 if block may be skipped (i.e. it contains no records of
103 * that can match the bsr).
106 int match_bsr_block(BSR *bsr, DEV_BLOCK *block)
108 if (!bsr || !bsr->use_fast_rejection || (block->BlockVer < 2)) {
109 return 1; /* cannot fast reject */
112 for ( ; bsr; bsr=bsr->next) {
113 if (!match_block_sesstime(bsr, bsr->sesstime, block)) {
116 if (!match_block_sessid(bsr, bsr->sessid, block)) {
124 static int match_block_sesstime(BSR *bsr, BSR_SESSTIME *sesstime, DEV_BLOCK *block)
127 return 1; /* no specification matches all */
129 if (sesstime->sesstime == block->VolSessionTime) {
132 if (sesstime->next) {
133 return match_block_sesstime(bsr, sesstime->next, block);
138 static int match_block_sessid(BSR *bsr, BSR_SESSID *sessid, DEV_BLOCK *block)
141 return 1; /* no specification matches all */
143 if (sessid->sessid <= block->VolSessionId && sessid->sessid2 >= block->VolSessionId) {
147 return match_block_sessid(bsr, sessid->next, block);
152 static int match_fileregex(BSR *bsr, DEV_RECORD *rec, JCR *jcr)
154 if (bsr->fileregex_re == NULL)
157 if (bsr->attr == NULL)
158 bsr->attr = new_attr(jcr);
160 /* The code breaks if the first record associated with a file is
163 if (rec->Stream == STREAM_UNIX_ATTRIBUTES ||
164 rec->Stream == STREAM_UNIX_ATTRIBUTES_EX) {
165 bsr->skip_file = false;
166 if (unpack_attributes_record(jcr, rec->Stream, rec->data, bsr->attr)) {
167 if (regexec(bsr->fileregex_re, bsr->attr->fname, 0, NULL, 0) == 0) {
168 Dmsg2(dbglevel, "Matched pattern, fname=%s FI=%d\n",
169 bsr->attr->fname, rec->FileIndex);
171 Dmsg2(dbglevel, "Didn't match, skipping fname=%s FI=%d\n",
172 bsr->attr->fname, rec->FileIndex);
173 bsr->skip_file = true;
180 /*********************************************************************
182 * Match Bootstrap records
184 * returns 0 no match and reposition is set if we should
185 * reposition the tape
186 * returns -1 no additional matches possible
188 int match_bsr(BSR *bsr, DEV_RECORD *rec, VOLUME_LABEL *volrec, SESSION_LABEL *sessrec, JCR *jcr)
193 * The bsr->reposition flag is set any time a bsr is done.
194 * In this case, we can probably reposition the
195 * tape to the next available bsr position.
198 bsr->reposition = false;
199 stat = match_all(bsr, rec, volrec, sessrec, true, jcr);
201 * Note, bsr->reposition is set by match_all when
202 * a bsr is done. We turn it off if a match was
203 * found or if we cannot use positioning
205 if (stat != 0 || !bsr->use_positioning) {
206 bsr->reposition = false;
209 stat = 1; /* no bsr => match all */
215 * Find the next bsr that applies to the current tape.
216 * It is the one with the smallest VolFile position.
218 BSR *find_next_bsr(BSR *root_bsr, DEVICE *dev)
221 BSR *found_bsr = NULL;
223 /* Do tape/disk seeking only if CAP_POSITIONBLOCKS is on */
225 Dmsg0(dbglevel, "NULL root bsr pointer passed to find_next_bsr.\n");
228 if (!root_bsr->use_positioning ||
229 !root_bsr->reposition || !dev->has_cap(CAP_POSITIONBLOCKS)) {
230 Dmsg2(dbglevel, "No nxt_bsr use_pos=%d repos=%d\n", root_bsr->use_positioning, root_bsr->reposition);
233 Dmsg2(dbglevel, "use_pos=%d repos=%d\n", root_bsr->use_positioning, root_bsr->reposition);
234 root_bsr->mount_next_volume = false;
235 /* Walk through all bsrs to find the next one to use => smallest file,block */
236 for (bsr=root_bsr; bsr; bsr=bsr->next) {
237 if (bsr->done || !match_volume(bsr, bsr->volume, &dev->VolHdr, 1)) {
240 if (found_bsr == NULL) {
243 found_bsr = find_smallest_volfile(found_bsr, bsr);
247 * If we get to this point and found no bsr, it means
248 * that any additional bsr's must apply to the next
249 * tape, so set a flag.
251 if (found_bsr == NULL) {
252 root_bsr->mount_next_volume = true;
259 * This routine needs to be fixed to only look at items that
260 * are not marked as done. Otherwise, it can find a bsr
261 * that has already been consumed, and this will cause the
262 * bsr to be used, thus we may seek back and re-read the
263 * same records, causing an error. This deficiency must
264 * be fixed. For the moment, it has been kludged in
265 * read_record.c to avoid seeking back if find_next_bsr
266 * returns a bsr pointing to a smaller address (file/block).
268 static BSR *find_smallest_volfile(BSR *found_bsr, BSR *bsr)
270 BSR *return_bsr = found_bsr;
273 uint32_t found_bsr_sfile, bsr_sfile;
274 uint32_t found_bsr_sblock, bsr_sblock;
276 /* Find the smallest file in the found_bsr */
277 vf = found_bsr->volfile;
278 found_bsr_sfile = vf->sfile;
279 while ( (vf=vf->next) ) {
280 if (vf->sfile < found_bsr_sfile) {
281 found_bsr_sfile = vf->sfile;
285 /* Find the smallest file in the bsr */
287 bsr_sfile = vf->sfile;
288 while ( (vf=vf->next) ) {
289 if (vf->sfile < bsr_sfile) {
290 bsr_sfile = vf->sfile;
294 /* if the bsr file is less than the found_bsr file, return bsr */
295 if (found_bsr_sfile > bsr_sfile) {
297 } else if (found_bsr_sfile == bsr_sfile) {
298 /* Files are equal */
299 /* find smallest block in found_bsr */
300 vb = found_bsr->volblock;
301 found_bsr_sblock = vb->sblock;
302 while ( (vb=vb->next) ) {
303 if (vb->sblock < found_bsr_sblock) {
304 found_bsr_sblock = vb->sblock;
307 /* Find smallest block in bsr */
309 bsr_sblock = vb->sblock;
310 while ( (vb=vb->next) ) {
311 if (vb->sblock < bsr_sblock) {
312 bsr_sblock = vb->sblock;
315 /* Compare and return the smallest */
316 if (found_bsr_sblock > bsr_sblock) {
324 * Called after the signature record so that
325 * we can see if the current bsr has been
326 * fully processed (i.e. is done).
327 * The bsr argument is not used, but is included
328 * for consistency with the other match calls.
330 * Returns: true if we should reposition
333 bool is_this_bsr_done(BSR *bsr, DEV_RECORD *rec)
335 BSR *rbsr = rec->bsr;
336 Dmsg1(dbglevel, "match_set %d\n", rbsr != NULL);
342 if (rbsr->count && rbsr->found >= rbsr->count) {
344 rbsr->root->reposition = true;
345 Dmsg2(dbglevel, "is_end_this_bsr set reposition=1 count=%d found=%d\n",
346 rbsr->count, rbsr->found);
353 * Match all the components of current record
356 * returns -1 no additional matches possible
358 static int match_all(BSR *bsr, DEV_RECORD *rec, VOLUME_LABEL *volrec,
359 SESSION_LABEL *sessrec, bool done, JCR *jcr)
361 Dmsg0(050, "Enter match_all\n");
363 // Dmsg0(dbglevel, "bsr->done set\n");
366 if (!match_volume(bsr, bsr->volume, volrec, 1)) {
367 Dmsg2(dbglevel, "bsr fail bsr_vol=%s != rec read_vol=%s\n", bsr->volume->VolumeName,
371 Dmsg2(dbglevel, "OK bsr match bsr_vol=%s read_vol=%s\n", bsr->volume->VolumeName,
374 if (!match_volfile(bsr, bsr->volfile, rec, 1)) {
375 Dmsg3(dbglevel, "Fail on file=%d. bsr=%d,%d\n",
376 rec->File, bsr->volfile->sfile, bsr->volfile->efile);
379 Dmsg3(dbglevel, "OK bsr file=%d. bsr=%d,%d\n",
380 rec->File, bsr->volfile->sfile, bsr->volfile->efile);
382 if (!match_volblock(bsr, bsr->volblock, rec, 1)) {
383 Dmsg3(dbglevel, "Fail on Block=%d. bsr=%d,%d\n",
384 rec->Block, bsr->volblock->sblock, bsr->volblock->eblock);
387 Dmsg3(dbglevel, "OK bsr Block=%d. bsr=%d,%d\n",
388 rec->Block, bsr->volblock->sblock, bsr->volblock->eblock);
390 if (!match_sesstime(bsr, bsr->sesstime, rec, 1)) {
391 Dmsg2(dbglevel, "Fail on sesstime. bsr=%d rec=%d\n",
392 bsr->sesstime->sesstime, rec->VolSessionTime);
396 /* NOTE!! This test MUST come after the sesstime test */
397 if (!match_sessid(bsr, bsr->sessid, rec)) {
398 Dmsg2(dbglevel, "Fail on sessid. bsr=%d rec=%d\n",
399 bsr->sessid->sessid, rec->VolSessionId);
403 /* NOTE!! This test MUST come after sesstime and sessid tests */
404 if (!match_findex(bsr, bsr->FileIndex, rec, 1)) {
405 Dmsg3(dbglevel, "Fail on findex=%d. bsr=%d,%d\n",
406 rec->FileIndex, bsr->FileIndex->findex, bsr->FileIndex->findex2);
409 Dmsg3(dbglevel, "match on findex=%d. bsr=%d,%d\n",
410 rec->FileIndex, bsr->FileIndex->findex, bsr->FileIndex->findex2);
412 if (!match_fileregex(bsr, rec, jcr)) {
413 Dmsg1(dbglevel, "Fail on fileregex='%s'\n", bsr->fileregex);
417 /* This flag is set by match_fileregex (and perhaps other tests) */
418 if (bsr->skip_file) {
419 Dmsg1(dbglevel, "Skipping findex=%d\n", rec->FileIndex);
424 * If a count was specified and we have a FileIndex, assume
425 * it is a Bacula created bsr (or the equivalent). We
426 * then save the bsr where the match occurred so that
427 * after processing the record or records, we can update
428 * the found count. I.e. rec->bsr points to the bsr that
429 * satisfied the match.
431 if (bsr->count && bsr->FileIndex) {
433 Dmsg0(050, "Leave match_all 1\n");
434 return 1; /* this is a complete match */
438 * The selections below are not used by Bacula's
439 * restore command, and don't work because of
440 * the rec->bsr = bsr optimization above.
442 if (!match_jobid(bsr, bsr->JobId, sessrec, 1)) {
443 Dmsg0(dbglevel, "fail on JobId\n");
447 if (!match_job(bsr, bsr->job, sessrec, 1)) {
448 Dmsg0(dbglevel, "fail on Job\n");
451 if (!match_client(bsr, bsr->client, sessrec, 1)) {
452 Dmsg0(dbglevel, "fail on Client\n");
455 if (!match_job_type(bsr, bsr->JobType, sessrec, 1)) {
456 Dmsg0(dbglevel, "fail on Job type\n");
459 if (!match_job_level(bsr, bsr->JobLevel, sessrec, 1)) {
460 Dmsg0(dbglevel, "fail on Job level\n");
463 if (!match_stream(bsr, bsr->stream, rec, 1)) {
464 Dmsg0(dbglevel, "fail on stream\n");
467 Dmsg0(050, "Leave match_all 1\n");
472 return match_all(bsr->next, rec, volrec, sessrec, bsr->done && done, jcr);
474 if (bsr->done && done) {
475 Dmsg0(050, "Leave match all -1\n");
478 Dmsg0(050, "Leave match all 0\n");
482 static int match_volume(BSR *bsr, BSR_VOLUME *volume, VOLUME_LABEL *volrec, bool done)
485 return 0; /* Volume must match */
487 if (strcmp(volume->VolumeName, volrec->VolumeName) == 0) {
488 Dmsg1(050, "match_volume=%s\n", volrec->VolumeName);
492 return match_volume(bsr, volume->next, volrec, 1);
497 static int match_client(BSR *bsr, BSR_CLIENT *client, SESSION_LABEL *sessrec, bool done)
500 return 1; /* no specification matches all */
502 if (strcmp(client->ClientName, sessrec->ClientName) == 0) {
506 return match_client(bsr, client->next, sessrec, 1);
511 static int match_job(BSR *bsr, BSR_JOB *job, SESSION_LABEL *sessrec, bool done)
514 return 1; /* no specification matches all */
516 if (strcmp(job->Job, sessrec->Job) == 0) {
520 return match_job(bsr, job->next, sessrec, 1);
525 static int match_job_type(BSR *bsr, BSR_JOBTYPE *job_type, SESSION_LABEL *sessrec, bool done)
528 return 1; /* no specification matches all */
530 if (job_type->JobType == sessrec->JobType) {
533 if (job_type->next) {
534 return match_job_type(bsr, job_type->next, sessrec, 1);
539 static int match_job_level(BSR *bsr, BSR_JOBLEVEL *job_level, SESSION_LABEL *sessrec, bool done)
542 return 1; /* no specification matches all */
544 if (job_level->JobLevel == sessrec->JobLevel) {
547 if (job_level->next) {
548 return match_job_level(bsr, job_level->next, sessrec, 1);
553 static int match_jobid(BSR *bsr, BSR_JOBID *jobid, SESSION_LABEL *sessrec, bool done)
556 return 1; /* no specification matches all */
558 if (jobid->JobId <= sessrec->JobId && jobid->JobId2 >= sessrec->JobId) {
562 return match_jobid(bsr, jobid->next, sessrec, 1);
567 static int match_volfile(BSR *bsr, BSR_VOLFILE *volfile, DEV_RECORD *rec, bool done)
570 return 1; /* no specification matches all */
573 * The following code is turned off because this should now work
574 * with disk files too, though since a "volfile" is 4GB, it does
575 * not improve performance much.
578 /* For the moment, these tests work only with tapes. */
579 if (!(rec->state & REC_ISTAPE)) {
580 return 1; /* All File records OK for this match */
582 Dmsg3(dbglevel, "match_volfile: sfile=%d efile=%d recfile=%d\n",
583 volfile->sfile, volfile->efile, rec->File);
585 if (volfile->sfile <= rec->File && volfile->efile >= rec->File) {
588 /* Once we get past last efile, we are done */
589 if (rec->File > volfile->efile) {
590 volfile->done = true; /* set local done */
593 return match_volfile(bsr, volfile->next, rec, volfile->done && done);
596 /* If we are done and all prior matches are done, this bsr is finished */
597 if (volfile->done && done) {
599 bsr->root->reposition = true;
600 Dmsg2(dbglevel, "bsr done from volfile rec=%d volefile=%d\n",
601 rec->File, volfile->efile);
606 static int match_volblock(BSR *bsr, BSR_VOLBLOCK *volblock, DEV_RECORD *rec, bool done)
609 * Currently block matching does not work correctly for disk
610 * files in all cases, so it is "turned off" by the following
617 return 1; /* no specification matches all */
619 /* For the moment, these tests work only with disk. */
620 if (rec->state & REC_ISTAPE) {
621 return 1; /* All File records OK for this match */
623 // Dmsg3(dbglevel, "match_volblock: sblock=%d eblock=%d recblock=%d\n",
624 // volblock->sblock, volblock->eblock, rec->Block);
625 if (volblock->sblock <= rec->Block && volblock->eblock >= rec->Block) {
628 /* Once we get past last eblock, we are done */
629 if (rec->Block > volblock->eblock) {
630 volblock->done = true; /* set local done */
632 if (volblock->next) {
633 return match_volblock(bsr, volblock->next, rec, volblock->done && done);
637 * This is turned off because I do not believe that we can mark
638 * the bsr as done at this level.
641 /* If we are done and all prior matches are done, this bsr is finished */
642 if (volblock->done && done) {
644 bsr->root->reposition = true;
645 Dmsg2(dbglevel, "bsr done from volblock rec=%d voleblock=%d\n",
646 rec->Block, volblock->eblock);
653 static int match_stream(BSR *bsr, BSR_STREAM *stream, DEV_RECORD *rec, bool done)
656 return 1; /* no specification matches all */
658 if (stream->stream == rec->Stream) {
662 return match_stream(bsr, stream->next, rec, 1);
667 static int match_sesstime(BSR *bsr, BSR_SESSTIME *sesstime, DEV_RECORD *rec, bool done)
670 return 1; /* no specification matches all */
672 if (sesstime->sesstime == rec->VolSessionTime) {
675 if (rec->VolSessionTime > sesstime->sesstime) {
676 sesstime->done = true;
678 if (sesstime->next) {
679 return match_sesstime(bsr, sesstime->next, rec, sesstime->done && done);
681 if (sesstime->done && done) {
683 bsr->root->reposition = true;
684 Dmsg0(dbglevel, "bsr done from sesstime\n");
689 static int match_sessid(BSR *bsr, BSR_SESSID *sessid, DEV_RECORD *rec)
692 return 1; /* no specification matches all */
694 if (sessid->sessid <= rec->VolSessionId && sessid->sessid2 >= rec->VolSessionId) {
698 return match_sessid(bsr, sessid->next, rec);
704 * When reading the Volume, the Volume Findex (rec->FileIndex) always
705 * are found in sequential order. Thus we can make optimizations.
707 * ***FIXME*** optimizations
708 * We could optimize by removing the recursion.
710 static int match_findex(BSR *bsr, BSR_FINDEX *findex, DEV_RECORD *rec, bool done)
713 return 1; /* no specification matches all */
716 if (findex->findex <= rec->FileIndex && findex->findex2 >= rec->FileIndex) {
717 Dmsg3(dbglevel, "Match on findex=%d. bsrFIs=%d,%d\n",
718 rec->FileIndex, findex->findex, findex->findex2);
721 if (rec->FileIndex > findex->findex2) {
726 return match_findex(bsr, findex->next, rec, findex->done && done);
728 if (findex->done && done) {
730 bsr->root->reposition = true;
731 Dmsg1(dbglevel, "bsr done from findex %d\n", rec->FileIndex);