2 * Match Bootstrap Records (used for restores) against
5 * Kern Sibbald, June MMII
10 Bacula® - The Network Backup Solution
12 Copyright (C) 2002-2006 Free Software Foundation Europe e.V.
14 The main author of Bacula is Kern Sibbald, with contributions from
15 many others, a complete list can be found in the file AUTHORS.
16 This program is Free Software; you can redistribute it and/or
17 modify it under the terms of version two of the GNU General Public
18 License as published by the Free Software Foundation plus additions
19 that are listed in the file LICENSE.
21 This program is distributed in the hope that it will be useful, but
22 WITHOUT ANY WARRANTY; without even the implied warranty of
23 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
24 General Public License for more details.
26 You should have received a copy of the GNU General Public License
27 along with this program; if not, write to the Free Software
28 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
31 Bacula® is a registered trademark of John Walker.
32 The licensor of Bacula is the Free Software Foundation Europe
33 (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
34 Switzerland, email:ftf@fsfeurope.org.
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 = 10;
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_stream(BSR *bsr, BSR_STREAM *stream, DEV_RECORD *rec, bool done);
77 static int match_all(BSR *bsr, DEV_RECORD *rec, VOLUME_LABEL *volrec, SESSION_LABEL *sessrec, bool done);
78 static int match_block_sesstime(BSR *bsr, BSR_SESSTIME *sesstime, DEV_BLOCK *block);
79 static int match_block_sessid(BSR *bsr, BSR_SESSID *sessid, DEV_BLOCK *block);
80 static BSR *find_smallest_volfile(BSR *fbsr, BSR *bsr);
83 /*********************************************************************
85 * If possible, position the archive device (tape) to read the
88 void position_bsr_block(BSR *bsr, DEV_BLOCK *block)
90 /* To be implemented */
93 /*********************************************************************
95 * Do fast block rejection based on bootstrap records.
96 * use_fast_rejection will be set if we have VolSessionId and VolSessTime
97 * in each record. When BlockVer is >= 2, we have those in the block header
98 * so can do fast rejection.
100 * returns: 1 if block may contain valid records
101 * 0 if block may be skipped (i.e. it contains no records of
102 * that can match the bsr).
105 int match_bsr_block(BSR *bsr, DEV_BLOCK *block)
107 if (!bsr || !bsr->use_fast_rejection || (block->BlockVer < 2)) {
108 return 1; /* cannot fast reject */
111 for ( ; bsr; bsr=bsr->next) {
112 if (!match_block_sesstime(bsr, bsr->sesstime, block)) {
115 if (!match_block_sessid(bsr, bsr->sessid, block)) {
123 static int match_block_sesstime(BSR *bsr, BSR_SESSTIME *sesstime, DEV_BLOCK *block)
126 return 1; /* no specification matches all */
128 if (sesstime->sesstime == block->VolSessionTime) {
131 if (sesstime->next) {
132 return match_block_sesstime(bsr, sesstime->next, block);
137 static int match_block_sessid(BSR *bsr, BSR_SESSID *sessid, DEV_BLOCK *block)
140 return 1; /* no specification matches all */
142 if (sessid->sessid <= block->VolSessionId && sessid->sessid2 >= block->VolSessionId) {
146 return match_block_sessid(bsr, sessid->next, block);
152 /*********************************************************************
154 * Match Bootstrap records
156 * returns 0 no match and reposition is set if we should
157 * reposition the tape
158 * returns -1 no additional matches possible
160 int match_bsr(BSR *bsr, DEV_RECORD *rec, VOLUME_LABEL *volrec, SESSION_LABEL *sessrec)
165 * The bsr->reposition flag is set any time a bsr is done.
166 * In this case, we can probably reposition the
167 * tape to the next available bsr position.
170 bsr->reposition = false;
171 stat = match_all(bsr, rec, volrec, sessrec, true);
173 * Note, bsr->reposition is set by match_all when
174 * a bsr is done. We turn it off if a match was
175 * found or if we cannot use positioning
177 if (stat != 0 || !bsr->use_positioning) {
178 bsr->reposition = false;
181 stat = 1; /* no bsr => match all */
187 * Find the next bsr that applies to the current tape.
188 * It is the one with the smallest VolFile position.
190 BSR *find_next_bsr(BSR *root_bsr, DEVICE *dev)
193 BSR *found_bsr = NULL;
194 bool no_file_seek = !dev->is_tape();
196 no_file_seek = false;
199 if (!root_bsr || !root_bsr->use_positioning ||
200 !root_bsr->reposition || no_file_seek) {
201 Dmsg2(dbglevel, "No nxt_bsr use_pos=%d repos=%d\n", root_bsr->use_positioning, root_bsr->reposition);
204 Dmsg2(dbglevel, "use_pos=%d repos=%d\n", root_bsr->use_positioning, root_bsr->reposition);
205 root_bsr->mount_next_volume = false;
206 /* Walk through all bsrs to find the next one to use => smallest file,block */
207 for (bsr=root_bsr; bsr; bsr=bsr->next) {
208 if (bsr->done || !match_volume(bsr, bsr->volume, &dev->VolHdr, 1)) {
211 if (found_bsr == NULL) {
214 found_bsr = find_smallest_volfile(found_bsr, bsr);
218 * If we get to this point and found no bsr, it means
219 * that any additional bsr's must apply to the next
220 * tape, so set a flag.
222 if (found_bsr == NULL) {
223 root_bsr->mount_next_volume = true;
230 * This routine needs to be fixed to only look at items that
231 * are not marked as done. Otherwise, it can find a bsr
232 * that has already been consumed, and this will cause the
233 * bsr to be used, thus we may seek back and re-read the
234 * same records, causing an error. This deficiency must
235 * be fixed. For the moment, it has been kludged in
236 * read_record.c to avoid seeking back if find_next_bsr
237 * returns a bsr pointing to a smaller address (file/block).
239 static BSR *find_smallest_volfile(BSR *found_bsr, BSR *bsr)
241 BSR *return_bsr = found_bsr;
244 uint32_t found_bsr_sfile, bsr_sfile;
245 uint32_t found_bsr_sblock, bsr_sblock;
247 /* Find the smallest file in the found_bsr */
248 vf = found_bsr->volfile;
249 found_bsr_sfile = vf->sfile;
250 while ( (vf=vf->next) ) {
251 if (vf->sfile < found_bsr_sfile) {
252 found_bsr_sfile = vf->sfile;
256 /* Find the smallest file in the bsr */
258 bsr_sfile = vf->sfile;
259 while ( (vf=vf->next) ) {
260 if (vf->sfile < bsr_sfile) {
261 bsr_sfile = vf->sfile;
265 /* if the bsr file is less than the found_bsr file, return bsr */
266 if (found_bsr_sfile > bsr_sfile) {
268 } else if (found_bsr_sfile == bsr_sfile) {
269 /* Files are equal */
270 /* find smallest block in found_bsr */
271 vb = found_bsr->volblock;
272 found_bsr_sblock = vb->sblock;
273 while ( (vb=vb->next) ) {
274 if (vb->sblock < found_bsr_sblock) {
275 found_bsr_sblock = vb->sblock;
278 /* Find smallest block in bsr */
280 bsr_sblock = vb->sblock;
281 while ( (vb=vb->next) ) {
282 if (vb->sblock < bsr_sblock) {
283 bsr_sblock = vb->sblock;
286 /* Compare and return the smallest */
287 if (found_bsr_sblock > bsr_sblock) {
295 * Called after the signature record so that
296 * we can see if the current bsr has been
297 * fully processed (i.e. is done).
298 * The bsr argument is not used, but is included
299 * for consistency with the other match calls.
301 * Returns: true if we should reposition
304 bool is_this_bsr_done(BSR *bsr, DEV_RECORD *rec)
306 BSR *rbsr = rec->bsr;
307 Dmsg1(dbglevel, "match_set %d\n", rbsr != NULL);
313 if (rbsr->count && rbsr->found >= rbsr->count) {
315 rbsr->root->reposition = true;
316 Dmsg2(dbglevel, "is_end_this_bsr set reposition=1 count=%d found=%d\n",
317 rbsr->count, rbsr->found);
324 * Match all the components of current record
327 * returns -1 no additional matches possible
329 static int match_all(BSR *bsr, DEV_RECORD *rec, VOLUME_LABEL *volrec,
330 SESSION_LABEL *sessrec, bool done)
333 Dmsg0(dbglevel, "bsr->done set\n");
336 if (!match_volume(bsr, bsr->volume, volrec, 1)) {
337 Dmsg2(dbglevel, "bsr fail vol=%s != rec vol=%s\n", bsr->volume->VolumeName,
341 if (!match_volfile(bsr, bsr->volfile, rec, 1)) {
342 Dmsg3(dbglevel, "Fail on file=%d. bsr=%d,%d\n",
343 rec->File, bsr->volfile->sfile, bsr->volfile->efile);
346 if (!match_sesstime(bsr, bsr->sesstime, rec, 1)) {
347 Dmsg2(dbglevel, "Fail on sesstime. bsr=%d rec=%d\n",
348 bsr->sesstime->sesstime, rec->VolSessionTime);
352 /* NOTE!! This test MUST come after the sesstime test */
353 if (!match_sessid(bsr, bsr->sessid, rec)) {
354 Dmsg2(dbglevel, "Fail on sessid. bsr=%d rec=%d\n",
355 bsr->sessid->sessid, rec->VolSessionId);
359 /* NOTE!! This test MUST come after sesstime and sessid tests */
360 if (!match_findex(bsr, bsr->FileIndex, rec, 1)) {
361 Dmsg3(dbglevel, "Fail on findex=%d. bsr=%d,%d\n",
362 rec->FileIndex, bsr->FileIndex->findex, bsr->FileIndex->findex2);
366 * If a count was specified and we have a FileIndex, assume
367 * it is a Bacula created bsr (or the equivalent). We
368 * then save the bsr where the match occurred so that
369 * after processing the record or records, we can update
370 * the found count. I.e. rec->bsr points to the bsr that
371 * satisfied the match.
373 if (bsr->count && bsr->FileIndex) {
375 return 1; /* this is a complete match */
379 * The selections below are not used by Bacula's
380 * restore command, and don't work because of
381 * the rec->bsr = bsr optimization above.
383 if (!match_jobid(bsr, bsr->JobId, sessrec, 1)) {
384 Dmsg0(dbglevel, "fail on JobId\n");
388 if (!match_job(bsr, bsr->job, sessrec, 1)) {
389 Dmsg0(dbglevel, "fail on Job\n");
392 if (!match_client(bsr, bsr->client, sessrec, 1)) {
393 Dmsg0(dbglevel, "fail on Client\n");
396 if (!match_job_type(bsr, bsr->JobType, sessrec, 1)) {
397 Dmsg0(dbglevel, "fail on Job type\n");
400 if (!match_job_level(bsr, bsr->JobLevel, sessrec, 1)) {
401 Dmsg0(dbglevel, "fail on Job level\n");
404 if (!match_stream(bsr, bsr->stream, rec, 1)) {
405 Dmsg0(dbglevel, "fail on stream\n");
412 return match_all(bsr->next, rec, volrec, sessrec, bsr->done && done);
414 if (bsr->done && done) {
420 static int match_volume(BSR *bsr, BSR_VOLUME *volume, VOLUME_LABEL *volrec, bool done)
423 return 0; /* Volume must match */
425 if (strcmp(volume->VolumeName, volrec->VolumeName) == 0) {
429 return match_volume(bsr, volume->next, volrec, 1);
434 static int match_client(BSR *bsr, BSR_CLIENT *client, SESSION_LABEL *sessrec, bool done)
437 return 1; /* no specification matches all */
439 if (fnmatch(client->ClientName, sessrec->ClientName, 0) == 0) {
443 return match_client(bsr, client->next, sessrec, 1);
448 static int match_job(BSR *bsr, BSR_JOB *job, SESSION_LABEL *sessrec, bool done)
451 return 1; /* no specification matches all */
453 if (fnmatch(job->Job, sessrec->Job, 0) == 0) {
457 return match_job(bsr, job->next, sessrec, 1);
462 static int match_job_type(BSR *bsr, BSR_JOBTYPE *job_type, SESSION_LABEL *sessrec, bool done)
465 return 1; /* no specification matches all */
467 if (job_type->JobType == sessrec->JobType) {
470 if (job_type->next) {
471 return match_job_type(bsr, job_type->next, sessrec, 1);
476 static int match_job_level(BSR *bsr, BSR_JOBLEVEL *job_level, SESSION_LABEL *sessrec, bool done)
479 return 1; /* no specification matches all */
481 if (job_level->JobLevel == sessrec->JobLevel) {
484 if (job_level->next) {
485 return match_job_level(bsr, job_level->next, sessrec, 1);
490 static int match_jobid(BSR *bsr, BSR_JOBID *jobid, SESSION_LABEL *sessrec, bool done)
493 return 1; /* no specification matches all */
495 if (jobid->JobId <= sessrec->JobId && jobid->JobId2 >= sessrec->JobId) {
499 return match_jobid(bsr, jobid->next, sessrec, 1);
504 static int match_volfile(BSR *bsr, BSR_VOLFILE *volfile, DEV_RECORD *rec, bool done)
507 return 1; /* no specification matches all */
509 /* For the moment, these tests work only with tapes. */
510 if (!(rec->state & REC_ISTAPE)) {
511 return 1; /* All File records OK for this match */
513 // Dmsg3(dbglevel, "match_volfile: sfile=%d efile=%d recfile=%d\n",
514 // volfile->sfile, volfile->efile, rec->File);
515 if (volfile->sfile <= rec->File && volfile->efile >= rec->File) {
518 /* Once we get past last efile, we are done */
519 if (rec->File > volfile->efile) {
520 volfile->done = true; /* set local done */
523 return match_volfile(bsr, volfile->next, rec, volfile->done && done);
526 /* If we are done and all prior matches are done, this bsr is finished */
527 if (volfile->done && done) {
529 bsr->root->reposition = true;
530 Dmsg2(dbglevel, "bsr done from volfile rec=%d volefile=%d\n",
531 rec->File, volfile->efile);
536 static int match_stream(BSR *bsr, BSR_STREAM *stream, DEV_RECORD *rec, bool done)
539 return 1; /* no specification matches all */
541 if (stream->stream == rec->Stream) {
545 return match_stream(bsr, stream->next, rec, 1);
550 static int match_sesstime(BSR *bsr, BSR_SESSTIME *sesstime, DEV_RECORD *rec, bool done)
553 return 1; /* no specification matches all */
555 if (sesstime->sesstime == rec->VolSessionTime) {
558 if (rec->VolSessionTime > sesstime->sesstime) {
559 sesstime->done = true;
561 if (sesstime->next) {
562 return match_sesstime(bsr, sesstime->next, rec, sesstime->done && done);
564 if (sesstime->done && done) {
566 bsr->root->reposition = true;
567 Dmsg0(dbglevel, "bsr done from sesstime\n");
572 static int match_sessid(BSR *bsr, BSR_SESSID *sessid, DEV_RECORD *rec)
575 return 1; /* no specification matches all */
577 if (sessid->sessid <= rec->VolSessionId && sessid->sessid2 >= rec->VolSessionId) {
581 return match_sessid(bsr, sessid->next, rec);
587 * When reading the Volume, the Volume Findex (rec->FileIndex) always
588 * are found in sequential order. Thus we can make optimizations.
590 * ***FIXME*** optimizations
591 * We could optimize a lot here by removing the recursion, and
592 * stopping the search earlier -- say when rec->FileIndex > findex->findex2
593 * and findex->next == NULL.
595 static int match_findex(BSR *bsr, BSR_FINDEX *findex, DEV_RECORD *rec, bool done)
598 return 1; /* no specification matches all */
601 if (findex->findex <= rec->FileIndex && findex->findex2 >= rec->FileIndex) {
602 Dmsg3(dbglevel, "Match on findex=%d. bsr=%d,%d\n",
603 rec->FileIndex, findex->findex, findex->findex2);
606 if (rec->FileIndex > findex->findex2) {
611 return match_findex(bsr, findex->next, rec, findex->done && done);
613 if (findex->done && done) {
615 bsr->root->reposition = true;
616 Dmsg1(dbglevel, "bsr done from findex %d\n", rec->FileIndex);