2 * Match Bootstrap Records (used for restores) against
5 * Kern Sibbald, June MMII
10 Copyright (C) 2002-2006 Kern Sibbald
12 This program is free software; you can redistribute it and/or
13 modify it under the terms of the GNU General Public License
14 version 2 as amended with additional clauses defined in the
15 file LICENSE in the main source directory.
17 This program is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 the file LICENSE for additional details.
30 #include "lib/fnmatch.h"
33 /* Forward references */
34 static int match_volume(BSR *bsr, BSR_VOLUME *volume, VOLUME_LABEL *volrec, bool done);
35 static int match_sesstime(BSR *bsr, BSR_SESSTIME *sesstime, DEV_RECORD *rec, bool done);
36 static int match_sessid(BSR *bsr, BSR_SESSID *sessid, DEV_RECORD *rec);
37 static int match_client(BSR *bsr, BSR_CLIENT *client, SESSION_LABEL *sessrec, bool done);
38 static int match_job(BSR *bsr, BSR_JOB *job, SESSION_LABEL *sessrec, bool done);
39 static int match_job_type(BSR *bsr, BSR_JOBTYPE *job_type, SESSION_LABEL *sessrec, bool done);
40 static int match_job_level(BSR *bsr, BSR_JOBLEVEL *job_level, SESSION_LABEL *sessrec, bool done);
41 static int match_jobid(BSR *bsr, BSR_JOBID *jobid, SESSION_LABEL *sessrec, bool done);
42 static int match_findex(BSR *bsr, BSR_FINDEX *findex, DEV_RECORD *rec, bool done);
43 static int match_volfile(BSR *bsr, BSR_VOLFILE *volfile, DEV_RECORD *rec, bool done);
44 static int match_stream(BSR *bsr, BSR_STREAM *stream, DEV_RECORD *rec, bool done);
45 static int match_all(BSR *bsr, DEV_RECORD *rec, VOLUME_LABEL *volrec, SESSION_LABEL *sessrec, bool done);
46 static int match_block_sesstime(BSR *bsr, BSR_SESSTIME *sesstime, DEV_BLOCK *block);
47 static int match_block_sessid(BSR *bsr, BSR_SESSID *sessid, DEV_BLOCK *block);
48 static BSR *find_smallest_volfile(BSR *fbsr, BSR *bsr);
51 /*********************************************************************
53 * If possible, position the archive device (tape) to read the
56 void position_bsr_block(BSR *bsr, DEV_BLOCK *block)
58 /* To be implemented */
61 /*********************************************************************
63 * Do fast block rejection based on bootstrap records.
64 * use_fast_rejection will be set if we have VolSessionId and VolSessTime
65 * in each record. When BlockVer is >= 2, we have those in the block header
66 * so can do fast rejection.
68 * returns: 1 if block may contain valid records
69 * 0 if block may be skipped (i.e. it contains no records of
70 * that can match the bsr).
73 int match_bsr_block(BSR *bsr, DEV_BLOCK *block)
75 if (!bsr || !bsr->use_fast_rejection || (block->BlockVer < 2)) {
76 return 1; /* cannot fast reject */
79 for ( ; bsr; bsr=bsr->next) {
80 if (!match_block_sesstime(bsr, bsr->sesstime, block)) {
83 if (!match_block_sessid(bsr, bsr->sessid, block)) {
91 static int match_block_sesstime(BSR *bsr, BSR_SESSTIME *sesstime, DEV_BLOCK *block)
94 return 1; /* no specification matches all */
96 if (sesstime->sesstime == block->VolSessionTime) {
100 return match_block_sesstime(bsr, sesstime->next, block);
105 static int match_block_sessid(BSR *bsr, BSR_SESSID *sessid, DEV_BLOCK *block)
108 return 1; /* no specification matches all */
110 if (sessid->sessid <= block->VolSessionId && sessid->sessid2 >= block->VolSessionId) {
114 return match_block_sessid(bsr, sessid->next, block);
120 /*********************************************************************
122 * Match Bootstrap records
124 * returns 0 no match and reposition is set if we should
125 * reposition the tape
126 * returns -1 no additional matches possible
128 int match_bsr(BSR *bsr, DEV_RECORD *rec, VOLUME_LABEL *volrec, SESSION_LABEL *sessrec)
133 * The bsr->reposition flag is set any time a bsr is done.
134 * In this case, we can probably reposition the
135 * tape to the next available bsr position.
138 bsr->reposition = false;
139 stat = match_all(bsr, rec, volrec, sessrec, true);
141 * Note, bsr->reposition is set by match_all when
142 * a bsr is done. We turn it off if a match was
143 * found or if we cannot use positioning
145 if (stat != 0 || !bsr->use_positioning) {
146 bsr->reposition = false;
149 stat = 1; /* no bsr => match all */
155 * Find the next bsr that applies to the current tape.
156 * It is the one with the smallest VolFile position.
158 BSR *find_next_bsr(BSR *root_bsr, DEVICE *dev)
161 BSR *found_bsr = NULL;
162 bool no_file_seek = !dev->is_tape();
164 no_file_seek = false;
167 if (!root_bsr || !root_bsr->use_positioning ||
168 !root_bsr->reposition || no_file_seek) {
169 Dmsg2(300, "No nxt_bsr use_pos=%d repos=%d\n", root_bsr->use_positioning, root_bsr->reposition);
172 Dmsg2(300, "use_pos=%d repos=%d\n", root_bsr->use_positioning, root_bsr->reposition);
173 root_bsr->mount_next_volume = false;
174 for (bsr=root_bsr; bsr; bsr=bsr->next) {
175 if (bsr->done || !match_volume(bsr, bsr->volume, &dev->VolHdr, 1)) {
178 if (found_bsr == NULL) {
181 found_bsr = find_smallest_volfile(found_bsr, bsr);
185 * If we get to this point and found no bsr, it means
186 * that any additional bsr's must apply to the next
187 * tape, so set a flag.
189 if (found_bsr == NULL) {
190 root_bsr->mount_next_volume = true;
195 static BSR *find_smallest_volfile(BSR *found_bsr, BSR *bsr)
197 BSR *return_bsr = found_bsr;
200 uint32_t found_bsr_sfile, bsr_sfile;
201 uint32_t found_bsr_sblock, bsr_sblock;
203 vf = found_bsr->volfile;
204 found_bsr_sfile = vf->sfile;
205 while ( (vf=vf->next) ) {
206 if (vf->sfile < found_bsr_sfile) {
207 found_bsr_sfile = vf->sfile;
211 bsr_sfile = vf->sfile;
212 while ( (vf=vf->next) ) {
213 if (vf->sfile < bsr_sfile) {
214 bsr_sfile = vf->sfile;
217 if (found_bsr_sfile > bsr_sfile) {
219 } else if (found_bsr_sfile == bsr_sfile) {
220 /* Must check block */
221 vb = found_bsr->volblock;
222 found_bsr_sblock = vb->sblock;
223 while ( (vb=vb->next) ) {
224 if (vb->sblock < found_bsr_sblock) {
225 found_bsr_sblock = vb->sblock;
229 bsr_sblock = vb->sblock;
230 while ( (vb=vb->next) ) {
231 if (vb->sblock < bsr_sblock) {
232 bsr_sblock = vb->sblock;
235 if (found_bsr_sblock > bsr_sblock) {
244 * Called after the signature record so that
245 * we can see if the current bsr has been
246 * fully processed (i.e. is done).
247 * The bsr argument is not used, but is included
248 * for consistency with the other match calls.
250 * Returns: true if we should reposition
253 bool is_this_bsr_done(BSR *bsr, DEV_RECORD *rec)
255 BSR *rbsr = rec->bsr;
256 Dmsg1(300, "match_set %d\n", rbsr != NULL);
262 if (rbsr->count && rbsr->found >= rbsr->count) {
264 rbsr->root->reposition = true;
265 Dmsg2(500, "is_end_this_bsr set reposition=1 count=%d found=%d\n",
266 rbsr->count, rbsr->found);
273 * Match all the components of current record
276 * returns -1 no additional matches possible
278 static int match_all(BSR *bsr, DEV_RECORD *rec, VOLUME_LABEL *volrec,
279 SESSION_LABEL *sessrec, bool done)
282 Dmsg0(300, "bsr->done set\n");
285 if (!match_volume(bsr, bsr->volume, volrec, 1)) {
286 Dmsg2(300, "bsr fail vol=%s != rec vol=%s\n", bsr->volume->VolumeName,
290 if (!match_volfile(bsr, bsr->volfile, rec, 1)) {
291 Dmsg2(300, "Fail on file. bsr=%d rec=%d\n", bsr->volfile->efile,
295 if (!match_sesstime(bsr, bsr->sesstime, rec, 1)) {
296 Dmsg2(300, "Fail on sesstime. bsr=%d rec=%d\n",
297 bsr->sesstime->sesstime, rec->VolSessionTime);
301 /* NOTE!! This test MUST come after the sesstime test */
302 if (!match_sessid(bsr, bsr->sessid, rec)) {
303 Dmsg2(300, "Fail on sessid. bsr=%d rec=%d\n",
304 bsr->sessid->sessid, rec->VolSessionId);
308 /* NOTE!! This test MUST come after sesstime and sessid tests */
309 if (!match_findex(bsr, bsr->FileIndex, rec, 1)) {
310 Dmsg2(300, "Fail on findex. bsr=%d rec=%d\n",
311 bsr->FileIndex->findex2, rec->FileIndex);
315 * If a count was specified and we have a FileIndex, assume
316 * it is a Bacula created bsr (or the equivalent). We
317 * then save the bsr where the match occurred so that
318 * after processing the record or records, we can update
319 * the found count. I.e. rec->bsr points to the bsr that
320 * satisfied the match.
322 if (bsr->count && bsr->FileIndex) {
324 return 1; /* this is a complete match */
328 * The selections below are not used by Bacula's
329 * restore command, and don't work because of
330 * the rec->bsr = bsr optimization above.
332 if (!match_jobid(bsr, bsr->JobId, sessrec, 1)) {
333 Dmsg0(300, "fail on JobId\n");
337 if (!match_job(bsr, bsr->job, sessrec, 1)) {
338 Dmsg0(300, "fail on Job\n");
341 if (!match_client(bsr, bsr->client, sessrec, 1)) {
342 Dmsg0(300, "fail on Client\n");
345 if (!match_job_type(bsr, bsr->JobType, sessrec, 1)) {
346 Dmsg0(300, "fail on Job type\n");
349 if (!match_job_level(bsr, bsr->JobLevel, sessrec, 1)) {
350 Dmsg0(300, "fail on Job level\n");
353 if (!match_stream(bsr, bsr->stream, rec, 1)) {
354 Dmsg0(300, "fail on stream\n");
361 return match_all(bsr->next, rec, volrec, sessrec, bsr->done && done);
363 if (bsr->done && done) {
369 static int match_volume(BSR *bsr, BSR_VOLUME *volume, VOLUME_LABEL *volrec, bool done)
372 return 0; /* Volume must match */
374 if (strcmp(volume->VolumeName, volrec->VolumeName) == 0) {
378 return match_volume(bsr, volume->next, volrec, 1);
383 static int match_client(BSR *bsr, BSR_CLIENT *client, SESSION_LABEL *sessrec, bool done)
386 return 1; /* no specification matches all */
388 if (fnmatch(client->ClientName, sessrec->ClientName, 0) == 0) {
392 return match_client(bsr, client->next, sessrec, 1);
397 static int match_job(BSR *bsr, BSR_JOB *job, SESSION_LABEL *sessrec, bool done)
400 return 1; /* no specification matches all */
402 if (fnmatch(job->Job, sessrec->Job, 0) == 0) {
406 return match_job(bsr, job->next, sessrec, 1);
411 static int match_job_type(BSR *bsr, BSR_JOBTYPE *job_type, SESSION_LABEL *sessrec, bool done)
414 return 1; /* no specification matches all */
416 if (job_type->JobType == sessrec->JobType) {
419 if (job_type->next) {
420 return match_job_type(bsr, job_type->next, sessrec, 1);
425 static int match_job_level(BSR *bsr, BSR_JOBLEVEL *job_level, SESSION_LABEL *sessrec, bool done)
428 return 1; /* no specification matches all */
430 if (job_level->JobLevel == sessrec->JobLevel) {
433 if (job_level->next) {
434 return match_job_level(bsr, job_level->next, sessrec, 1);
439 static int match_jobid(BSR *bsr, BSR_JOBID *jobid, SESSION_LABEL *sessrec, bool done)
442 return 1; /* no specification matches all */
444 if (jobid->JobId <= sessrec->JobId && jobid->JobId2 >= sessrec->JobId) {
448 return match_jobid(bsr, jobid->next, sessrec, 1);
453 static int match_volfile(BSR *bsr, BSR_VOLFILE *volfile, DEV_RECORD *rec, bool done)
456 return 1; /* no specification matches all */
458 /* For the moment, these tests work only with tapes. */
459 if (!(rec->state & REC_ISTAPE)) {
460 return 1; /* All File records OK for this match */
462 // Dmsg3(300, "match_volfile: sfile=%d efile=%d recfile=%d\n",
463 // volfile->sfile, volfile->efile, rec->File);
464 if (volfile->sfile <= rec->File && volfile->efile >= rec->File) {
467 /* Once we get past last efile, we are done */
468 if (rec->File > volfile->efile) {
469 volfile->done = true; /* set local done */
472 return match_volfile(bsr, volfile->next, rec, volfile->done && done);
475 /* If we are done and all prior matches are done, this bsr is finished */
476 if (volfile->done && done) {
478 bsr->root->reposition = true;
479 Dmsg2(300, "bsr done from volfile rec=%d volefile=%d\n",
480 rec->File, volfile->efile);
485 static int match_stream(BSR *bsr, BSR_STREAM *stream, DEV_RECORD *rec, bool done)
488 return 1; /* no specification matches all */
490 if (stream->stream == rec->Stream) {
494 return match_stream(bsr, stream->next, rec, 1);
499 static int match_sesstime(BSR *bsr, BSR_SESSTIME *sesstime, DEV_RECORD *rec, bool done)
502 return 1; /* no specification matches all */
504 if (sesstime->sesstime == rec->VolSessionTime) {
507 if (rec->VolSessionTime > sesstime->sesstime) {
508 sesstime->done = true;
510 if (sesstime->next) {
511 return match_sesstime(bsr, sesstime->next, rec, sesstime->done && done);
513 if (sesstime->done && done) {
515 bsr->root->reposition = true;
516 Dmsg0(300, "bsr done from sesstime\n");
521 static int match_sessid(BSR *bsr, BSR_SESSID *sessid, DEV_RECORD *rec)
524 return 1; /* no specification matches all */
526 if (sessid->sessid <= rec->VolSessionId && sessid->sessid2 >= rec->VolSessionId) {
530 return match_sessid(bsr, sessid->next, rec);
536 * When reading the Volume, the Volume Findex (rec->FileIndex) always
537 * are found in sequential order. Thus we can make optimizations.
539 * ***FIXME*** optimizations
540 * We could optimize a lot here by removing the recursion, and
541 * stopping the search earlier -- say when rec->FileIndex > findex->findex2
542 * and findex->next == NULL. Also, the current entry tests could be skipped
543 * if findex->done is set.
545 static int match_findex(BSR *bsr, BSR_FINDEX *findex, DEV_RECORD *rec, bool done)
548 return 1; /* no specification matches all */
550 if (findex->findex <= rec->FileIndex && findex->findex2 >= rec->FileIndex) {
553 if (rec->FileIndex > findex->findex2) {
557 return match_findex(bsr, findex->next, rec, findex->done && done);
559 if (findex->done && done) {
561 bsr->root->reposition = true;
562 Dmsg1(300, "bsr done from findex %d\n", rec->FileIndex);