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);
349 Dmsg2(dbglevel, "is_end_this_bsr not done count=%d found=%d\n",
350 rbsr->count, rbsr->found);
355 * Match all the components of current record
358 * returns -1 no additional matches possible
360 static int match_all(BSR *bsr, DEV_RECORD *rec, VOLUME_LABEL *volrec,
361 SESSION_LABEL *sessrec, bool done, JCR *jcr)
363 Dmsg0(050, "Enter match_all\n");
365 // Dmsg0(dbglevel, "bsr->done set\n");
368 if (!match_volume(bsr, bsr->volume, volrec, 1)) {
369 Dmsg2(dbglevel, "bsr fail bsr_vol=%s != rec read_vol=%s\n", bsr->volume->VolumeName,
373 Dmsg2(dbglevel, "OK bsr match bsr_vol=%s read_vol=%s\n", bsr->volume->VolumeName,
376 if (!match_volfile(bsr, bsr->volfile, rec, 1)) {
377 Dmsg3(dbglevel, "Fail on file=%u. bsr=%u,%u\n",
378 rec->File, bsr->volfile->sfile, bsr->volfile->efile);
381 Dmsg3(dbglevel, "OK bsr file=%u. bsr=%u,%u\n",
382 rec->File, bsr->volfile->sfile, bsr->volfile->efile);
384 if (!match_volblock(bsr, bsr->volblock, rec, 1)) {
385 Dmsg3(dbglevel, "Fail on Block=%u. bsr=%u,%u\n",
386 rec->Block, bsr->volblock->sblock, bsr->volblock->eblock);
389 Dmsg3(dbglevel, "OK bsr Block=%u. bsr=%u,%u\n",
390 rec->Block, bsr->volblock->sblock, bsr->volblock->eblock);
392 if (!match_sesstime(bsr, bsr->sesstime, rec, 1)) {
393 Dmsg2(dbglevel, "Fail on sesstime. bsr=%u rec=%u\n",
394 bsr->sesstime->sesstime, rec->VolSessionTime);
398 /* NOTE!! This test MUST come after the sesstime test */
399 if (!match_sessid(bsr, bsr->sessid, rec)) {
400 Dmsg2(dbglevel, "Fail on sessid. bsr=%u rec=%u\n",
401 bsr->sessid->sessid, rec->VolSessionId);
405 /* NOTE!! This test MUST come after sesstime and sessid tests */
406 if (!match_findex(bsr, bsr->FileIndex, rec, 1)) {
407 Dmsg3(dbglevel, "Fail on findex=%d. bsr=%d,%d\n",
408 rec->FileIndex, bsr->FileIndex->findex, bsr->FileIndex->findex2);
411 Dmsg3(dbglevel, "match on findex=%d. bsr=%d,%d\n",
412 rec->FileIndex, bsr->FileIndex->findex, bsr->FileIndex->findex2);
414 if (!match_fileregex(bsr, rec, jcr)) {
415 Dmsg1(dbglevel, "Fail on fileregex='%s'\n", bsr->fileregex);
419 /* This flag is set by match_fileregex (and perhaps other tests) */
420 if (bsr->skip_file) {
421 Dmsg1(dbglevel, "Skipping findex=%d\n", rec->FileIndex);
426 * If a count was specified and we have a FileIndex, assume
427 * it is a Bacula created bsr (or the equivalent). We
428 * then save the bsr where the match occurred so that
429 * after processing the record or records, we can update
430 * the found count. I.e. rec->bsr points to the bsr that
431 * satisfied the match.
433 if (bsr->count && bsr->FileIndex) {
435 Dmsg0(050, "Leave match_all 1\n");
436 return 1; /* this is a complete match */
440 * The selections below are not used by Bacula's
441 * restore command, and don't work because of
442 * the rec->bsr = bsr optimization above.
444 if (!match_jobid(bsr, bsr->JobId, sessrec, 1)) {
445 Dmsg0(dbglevel, "fail on JobId\n");
449 if (!match_job(bsr, bsr->job, sessrec, 1)) {
450 Dmsg0(dbglevel, "fail on Job\n");
453 if (!match_client(bsr, bsr->client, sessrec, 1)) {
454 Dmsg0(dbglevel, "fail on Client\n");
457 if (!match_job_type(bsr, bsr->JobType, sessrec, 1)) {
458 Dmsg0(dbglevel, "fail on Job type\n");
461 if (!match_job_level(bsr, bsr->JobLevel, sessrec, 1)) {
462 Dmsg0(dbglevel, "fail on Job level\n");
465 if (!match_stream(bsr, bsr->stream, rec, 1)) {
466 Dmsg0(dbglevel, "fail on stream\n");
469 Dmsg0(050, "Leave match_all 1\n");
474 return match_all(bsr->next, rec, volrec, sessrec, bsr->done && done, jcr);
476 if (bsr->done && done) {
477 Dmsg0(050, "Leave match all -1\n");
480 Dmsg0(050, "Leave match all 0\n");
484 static int match_volume(BSR *bsr, BSR_VOLUME *volume, VOLUME_LABEL *volrec, bool done)
487 return 0; /* Volume must match */
489 if (strcmp(volume->VolumeName, volrec->VolumeName) == 0) {
490 Dmsg1(050, "match_volume=%s\n", volrec->VolumeName);
494 return match_volume(bsr, volume->next, volrec, 1);
499 static int match_client(BSR *bsr, BSR_CLIENT *client, SESSION_LABEL *sessrec, bool done)
502 return 1; /* no specification matches all */
504 if (strcmp(client->ClientName, sessrec->ClientName) == 0) {
508 return match_client(bsr, client->next, sessrec, 1);
513 static int match_job(BSR *bsr, BSR_JOB *job, SESSION_LABEL *sessrec, bool done)
516 return 1; /* no specification matches all */
518 if (strcmp(job->Job, sessrec->Job) == 0) {
522 return match_job(bsr, job->next, sessrec, 1);
527 static int match_job_type(BSR *bsr, BSR_JOBTYPE *job_type, SESSION_LABEL *sessrec, bool done)
530 return 1; /* no specification matches all */
532 if (job_type->JobType == sessrec->JobType) {
535 if (job_type->next) {
536 return match_job_type(bsr, job_type->next, sessrec, 1);
541 static int match_job_level(BSR *bsr, BSR_JOBLEVEL *job_level, SESSION_LABEL *sessrec, bool done)
544 return 1; /* no specification matches all */
546 if (job_level->JobLevel == sessrec->JobLevel) {
549 if (job_level->next) {
550 return match_job_level(bsr, job_level->next, sessrec, 1);
555 static int match_jobid(BSR *bsr, BSR_JOBID *jobid, SESSION_LABEL *sessrec, bool done)
558 return 1; /* no specification matches all */
560 if (jobid->JobId <= sessrec->JobId && jobid->JobId2 >= sessrec->JobId) {
564 return match_jobid(bsr, jobid->next, sessrec, 1);
569 static int match_volfile(BSR *bsr, BSR_VOLFILE *volfile, DEV_RECORD *rec, bool done)
572 return 1; /* no specification matches all */
575 * The following code is turned off because this should now work
576 * with disk files too, though since a "volfile" is 4GB, it does
577 * not improve performance much.
580 /* For the moment, these tests work only with tapes. */
581 if (!(rec->state & REC_ISTAPE)) {
582 return 1; /* All File records OK for this match */
584 Dmsg3(dbglevel, "match_volfile: sfile=%u efile=%u recfile=%u\n",
585 volfile->sfile, volfile->efile, rec->File);
587 if (volfile->sfile <= rec->File && volfile->efile >= rec->File) {
590 /* Once we get past last efile, we are done */
591 if (rec->File > volfile->efile) {
592 volfile->done = true; /* set local done */
595 return match_volfile(bsr, volfile->next, rec, volfile->done && done);
598 /* If we are done and all prior matches are done, this bsr is finished */
599 if (volfile->done && done) {
601 bsr->root->reposition = true;
602 Dmsg2(dbglevel, "bsr done from volfile rec=%u volefile=%u\n",
603 rec->File, volfile->efile);
608 static int match_volblock(BSR *bsr, BSR_VOLBLOCK *volblock, DEV_RECORD *rec, bool done)
611 * Currently block matching does not work correctly for disk
612 * files in all cases, so it is "turned off" by the following
619 return 1; /* no specification matches all */
621 /* For the moment, these tests work only with disk. */
622 if (rec->state & REC_ISTAPE) {
623 return 1; /* All File records OK for this match */
625 // Dmsg3(dbglevel, "match_volblock: sblock=%u eblock=%u recblock=%u\n",
626 // volblock->sblock, volblock->eblock, rec->Block);
627 if (volblock->sblock <= rec->Block && volblock->eblock >= rec->Block) {
630 /* Once we get past last eblock, we are done */
631 if (rec->Block > volblock->eblock) {
632 volblock->done = true; /* set local done */
634 if (volblock->next) {
635 return match_volblock(bsr, volblock->next, rec, volblock->done && done);
638 /* If we are done and all prior matches are done, this bsr is finished */
639 if (volblock->done && done) {
641 bsr->root->reposition = true;
642 Dmsg2(dbglevel, "bsr done from volblock rec=%u voleblock=%u\n",
643 rec->Block, volblock->eblock);
649 static int match_stream(BSR *bsr, BSR_STREAM *stream, DEV_RECORD *rec, bool done)
652 return 1; /* no specification matches all */
654 if (stream->stream == rec->Stream) {
658 return match_stream(bsr, stream->next, rec, 1);
663 static int match_sesstime(BSR *bsr, BSR_SESSTIME *sesstime, DEV_RECORD *rec, bool done)
666 return 1; /* no specification matches all */
668 if (sesstime->sesstime == rec->VolSessionTime) {
671 if (rec->VolSessionTime > sesstime->sesstime) {
672 sesstime->done = true;
674 if (sesstime->next) {
675 return match_sesstime(bsr, sesstime->next, rec, sesstime->done && done);
677 if (sesstime->done && done) {
679 bsr->root->reposition = true;
680 Dmsg0(dbglevel, "bsr done from sesstime\n");
686 * Note, we cannot mark bsr done based on session id because we may
687 * have interleaved records, and there may be more of what we want
690 static int match_sessid(BSR *bsr, BSR_SESSID *sessid, DEV_RECORD *rec)
693 return 1; /* no specification matches all */
695 if (sessid->sessid <= rec->VolSessionId && sessid->sessid2 >= rec->VolSessionId) {
699 return match_sessid(bsr, sessid->next, rec);
705 * When reading the Volume, the Volume Findex (rec->FileIndex) always
706 * are found in sequential order. Thus we can make optimizations.
708 * ***FIXME*** optimizations
709 * We could optimize by removing the recursion.
711 static int match_findex(BSR *bsr, BSR_FINDEX *findex, DEV_RECORD *rec, bool done)
714 return 1; /* no specification matches all */
717 if (findex->findex <= rec->FileIndex && findex->findex2 >= rec->FileIndex) {
718 Dmsg3(dbglevel, "Match on findex=%d. bsrFIs=%d,%d\n",
719 rec->FileIndex, findex->findex, findex->findex2);
722 if (rec->FileIndex > findex->findex2) {
727 return match_findex(bsr, findex->next, rec, findex->done && done);
729 if (findex->done && done) {
731 bsr->root->reposition = true;
732 Dmsg1(dbglevel, "bsr done from findex %d\n", rec->FileIndex);