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.
43 #include "lib/fnmatch.h"
46 const int dbglevel = 10;
48 /* Forward references */
49 static int match_volume(BSR *bsr, BSR_VOLUME *volume, VOLUME_LABEL *volrec, bool done);
50 static int match_sesstime(BSR *bsr, BSR_SESSTIME *sesstime, DEV_RECORD *rec, bool done);
51 static int match_sessid(BSR *bsr, BSR_SESSID *sessid, DEV_RECORD *rec);
52 static int match_client(BSR *bsr, BSR_CLIENT *client, SESSION_LABEL *sessrec, bool done);
53 static int match_job(BSR *bsr, BSR_JOB *job, SESSION_LABEL *sessrec, bool done);
54 static int match_job_type(BSR *bsr, BSR_JOBTYPE *job_type, SESSION_LABEL *sessrec, bool done);
55 static int match_job_level(BSR *bsr, BSR_JOBLEVEL *job_level, SESSION_LABEL *sessrec, bool done);
56 static int match_jobid(BSR *bsr, BSR_JOBID *jobid, SESSION_LABEL *sessrec, bool done);
57 static int match_findex(BSR *bsr, BSR_FINDEX *findex, DEV_RECORD *rec, bool done);
58 static int match_volfile(BSR *bsr, BSR_VOLFILE *volfile, DEV_RECORD *rec, bool done);
59 static int match_stream(BSR *bsr, BSR_STREAM *stream, DEV_RECORD *rec, bool done);
60 static int match_all(BSR *bsr, DEV_RECORD *rec, VOLUME_LABEL *volrec, SESSION_LABEL *sessrec, bool done);
61 static int match_block_sesstime(BSR *bsr, BSR_SESSTIME *sesstime, DEV_BLOCK *block);
62 static int match_block_sessid(BSR *bsr, BSR_SESSID *sessid, DEV_BLOCK *block);
63 static BSR *find_smallest_volfile(BSR *fbsr, BSR *bsr);
66 /*********************************************************************
68 * If possible, position the archive device (tape) to read the
71 void position_bsr_block(BSR *bsr, DEV_BLOCK *block)
73 /* To be implemented */
76 /*********************************************************************
78 * Do fast block rejection based on bootstrap records.
79 * use_fast_rejection will be set if we have VolSessionId and VolSessTime
80 * in each record. When BlockVer is >= 2, we have those in the block header
81 * so can do fast rejection.
83 * returns: 1 if block may contain valid records
84 * 0 if block may be skipped (i.e. it contains no records of
85 * that can match the bsr).
88 int match_bsr_block(BSR *bsr, DEV_BLOCK *block)
90 if (!bsr || !bsr->use_fast_rejection || (block->BlockVer < 2)) {
91 return 1; /* cannot fast reject */
94 for ( ; bsr; bsr=bsr->next) {
95 if (!match_block_sesstime(bsr, bsr->sesstime, block)) {
98 if (!match_block_sessid(bsr, bsr->sessid, block)) {
106 static int match_block_sesstime(BSR *bsr, BSR_SESSTIME *sesstime, DEV_BLOCK *block)
109 return 1; /* no specification matches all */
111 if (sesstime->sesstime == block->VolSessionTime) {
114 if (sesstime->next) {
115 return match_block_sesstime(bsr, sesstime->next, block);
120 static int match_block_sessid(BSR *bsr, BSR_SESSID *sessid, DEV_BLOCK *block)
123 return 1; /* no specification matches all */
125 if (sessid->sessid <= block->VolSessionId && sessid->sessid2 >= block->VolSessionId) {
129 return match_block_sessid(bsr, sessid->next, block);
135 /*********************************************************************
137 * Match Bootstrap records
139 * returns 0 no match and reposition is set if we should
140 * reposition the tape
141 * returns -1 no additional matches possible
143 int match_bsr(BSR *bsr, DEV_RECORD *rec, VOLUME_LABEL *volrec, SESSION_LABEL *sessrec)
148 * The bsr->reposition flag is set any time a bsr is done.
149 * In this case, we can probably reposition the
150 * tape to the next available bsr position.
153 bsr->reposition = false;
154 stat = match_all(bsr, rec, volrec, sessrec, true);
156 * Note, bsr->reposition is set by match_all when
157 * a bsr is done. We turn it off if a match was
158 * found or if we cannot use positioning
160 if (stat != 0 || !bsr->use_positioning) {
161 bsr->reposition = false;
164 stat = 1; /* no bsr => match all */
170 * Find the next bsr that applies to the current tape.
171 * It is the one with the smallest VolFile position.
173 BSR *find_next_bsr(BSR *root_bsr, DEVICE *dev)
176 BSR *found_bsr = NULL;
177 bool no_file_seek = !dev->is_tape();
179 no_file_seek = false;
182 if (!root_bsr || !root_bsr->use_positioning ||
183 !root_bsr->reposition || no_file_seek) {
184 Dmsg2(dbglevel, "No nxt_bsr use_pos=%d repos=%d\n", root_bsr->use_positioning, root_bsr->reposition);
187 Dmsg2(dbglevel, "use_pos=%d repos=%d\n", root_bsr->use_positioning, root_bsr->reposition);
188 root_bsr->mount_next_volume = false;
189 for (bsr=root_bsr; bsr; bsr=bsr->next) {
190 if (bsr->done || !match_volume(bsr, bsr->volume, &dev->VolHdr, 1)) {
193 if (found_bsr == NULL) {
196 found_bsr = find_smallest_volfile(found_bsr, bsr);
200 * If we get to this point and found no bsr, it means
201 * that any additional bsr's must apply to the next
202 * tape, so set a flag.
204 if (found_bsr == NULL) {
205 root_bsr->mount_next_volume = true;
210 static BSR *find_smallest_volfile(BSR *found_bsr, BSR *bsr)
212 BSR *return_bsr = found_bsr;
215 uint32_t found_bsr_sfile, bsr_sfile;
216 uint32_t found_bsr_sblock, bsr_sblock;
218 vf = found_bsr->volfile;
219 found_bsr_sfile = vf->sfile;
220 while ( (vf=vf->next) ) {
221 if (vf->sfile < found_bsr_sfile) {
222 found_bsr_sfile = vf->sfile;
226 bsr_sfile = vf->sfile;
227 while ( (vf=vf->next) ) {
228 if (vf->sfile < bsr_sfile) {
229 bsr_sfile = vf->sfile;
232 if (found_bsr_sfile > bsr_sfile) {
234 } else if (found_bsr_sfile == bsr_sfile) {
235 /* Must check block */
236 vb = found_bsr->volblock;
237 found_bsr_sblock = vb->sblock;
238 while ( (vb=vb->next) ) {
239 if (vb->sblock < found_bsr_sblock) {
240 found_bsr_sblock = vb->sblock;
244 bsr_sblock = vb->sblock;
245 while ( (vb=vb->next) ) {
246 if (vb->sblock < bsr_sblock) {
247 bsr_sblock = vb->sblock;
250 if (found_bsr_sblock > bsr_sblock) {
259 * Called after the signature record so that
260 * we can see if the current bsr has been
261 * fully processed (i.e. is done).
262 * The bsr argument is not used, but is included
263 * for consistency with the other match calls.
265 * Returns: true if we should reposition
268 bool is_this_bsr_done(BSR *bsr, DEV_RECORD *rec)
270 BSR *rbsr = rec->bsr;
271 Dmsg1(dbglevel, "match_set %d\n", rbsr != NULL);
277 if (rbsr->count && rbsr->found >= rbsr->count) {
279 rbsr->root->reposition = true;
280 Dmsg2(dbglevel, "is_end_this_bsr set reposition=1 count=%d found=%d\n",
281 rbsr->count, rbsr->found);
288 * Match all the components of current record
291 * returns -1 no additional matches possible
293 static int match_all(BSR *bsr, DEV_RECORD *rec, VOLUME_LABEL *volrec,
294 SESSION_LABEL *sessrec, bool done)
297 Dmsg0(dbglevel, "bsr->done set\n");
300 if (!match_volume(bsr, bsr->volume, volrec, 1)) {
301 Dmsg2(dbglevel, "bsr fail vol=%s != rec vol=%s\n", bsr->volume->VolumeName,
305 if (!match_volfile(bsr, bsr->volfile, rec, 1)) {
306 Dmsg2(dbglevel, "Fail on file. bsr=%d rec=%d\n", bsr->volfile->efile,
310 if (!match_sesstime(bsr, bsr->sesstime, rec, 1)) {
311 Dmsg2(dbglevel, "Fail on sesstime. bsr=%d rec=%d\n",
312 bsr->sesstime->sesstime, rec->VolSessionTime);
316 /* NOTE!! This test MUST come after the sesstime test */
317 if (!match_sessid(bsr, bsr->sessid, rec)) {
318 Dmsg2(dbglevel, "Fail on sessid. bsr=%d rec=%d\n",
319 bsr->sessid->sessid, rec->VolSessionId);
323 /* NOTE!! This test MUST come after sesstime and sessid tests */
324 if (!match_findex(bsr, bsr->FileIndex, rec, 1)) {
325 Dmsg3(dbglevel, "Fail on findex=%d. bsr=%d,%d\n",
326 rec->FileIndex, bsr->FileIndex->findex, bsr->FileIndex->findex2);
330 * If a count was specified and we have a FileIndex, assume
331 * it is a Bacula created bsr (or the equivalent). We
332 * then save the bsr where the match occurred so that
333 * after processing the record or records, we can update
334 * the found count. I.e. rec->bsr points to the bsr that
335 * satisfied the match.
337 if (bsr->count && bsr->FileIndex) {
339 return 1; /* this is a complete match */
343 * The selections below are not used by Bacula's
344 * restore command, and don't work because of
345 * the rec->bsr = bsr optimization above.
347 if (!match_jobid(bsr, bsr->JobId, sessrec, 1)) {
348 Dmsg0(dbglevel, "fail on JobId\n");
352 if (!match_job(bsr, bsr->job, sessrec, 1)) {
353 Dmsg0(dbglevel, "fail on Job\n");
356 if (!match_client(bsr, bsr->client, sessrec, 1)) {
357 Dmsg0(dbglevel, "fail on Client\n");
360 if (!match_job_type(bsr, bsr->JobType, sessrec, 1)) {
361 Dmsg0(dbglevel, "fail on Job type\n");
364 if (!match_job_level(bsr, bsr->JobLevel, sessrec, 1)) {
365 Dmsg0(dbglevel, "fail on Job level\n");
368 if (!match_stream(bsr, bsr->stream, rec, 1)) {
369 Dmsg0(dbglevel, "fail on stream\n");
376 return match_all(bsr->next, rec, volrec, sessrec, bsr->done && done);
378 if (bsr->done && done) {
384 static int match_volume(BSR *bsr, BSR_VOLUME *volume, VOLUME_LABEL *volrec, bool done)
387 return 0; /* Volume must match */
389 if (strcmp(volume->VolumeName, volrec->VolumeName) == 0) {
393 return match_volume(bsr, volume->next, volrec, 1);
398 static int match_client(BSR *bsr, BSR_CLIENT *client, SESSION_LABEL *sessrec, bool done)
401 return 1; /* no specification matches all */
403 if (fnmatch(client->ClientName, sessrec->ClientName, 0) == 0) {
407 return match_client(bsr, client->next, sessrec, 1);
412 static int match_job(BSR *bsr, BSR_JOB *job, SESSION_LABEL *sessrec, bool done)
415 return 1; /* no specification matches all */
417 if (fnmatch(job->Job, sessrec->Job, 0) == 0) {
421 return match_job(bsr, job->next, sessrec, 1);
426 static int match_job_type(BSR *bsr, BSR_JOBTYPE *job_type, SESSION_LABEL *sessrec, bool done)
429 return 1; /* no specification matches all */
431 if (job_type->JobType == sessrec->JobType) {
434 if (job_type->next) {
435 return match_job_type(bsr, job_type->next, sessrec, 1);
440 static int match_job_level(BSR *bsr, BSR_JOBLEVEL *job_level, SESSION_LABEL *sessrec, bool done)
443 return 1; /* no specification matches all */
445 if (job_level->JobLevel == sessrec->JobLevel) {
448 if (job_level->next) {
449 return match_job_level(bsr, job_level->next, sessrec, 1);
454 static int match_jobid(BSR *bsr, BSR_JOBID *jobid, SESSION_LABEL *sessrec, bool done)
457 return 1; /* no specification matches all */
459 if (jobid->JobId <= sessrec->JobId && jobid->JobId2 >= sessrec->JobId) {
463 return match_jobid(bsr, jobid->next, sessrec, 1);
468 static int match_volfile(BSR *bsr, BSR_VOLFILE *volfile, DEV_RECORD *rec, bool done)
471 return 1; /* no specification matches all */
473 /* For the moment, these tests work only with tapes. */
474 if (!(rec->state & REC_ISTAPE)) {
475 return 1; /* All File records OK for this match */
477 // Dmsg3(dbglevel, "match_volfile: sfile=%d efile=%d recfile=%d\n",
478 // volfile->sfile, volfile->efile, rec->File);
479 if (volfile->sfile <= rec->File && volfile->efile >= rec->File) {
482 /* Once we get past last efile, we are done */
483 if (rec->File > volfile->efile) {
484 volfile->done = true; /* set local done */
487 return match_volfile(bsr, volfile->next, rec, volfile->done && done);
490 /* If we are done and all prior matches are done, this bsr is finished */
491 if (volfile->done && done) {
493 bsr->root->reposition = true;
494 Dmsg2(dbglevel, "bsr done from volfile rec=%d volefile=%d\n",
495 rec->File, volfile->efile);
500 static int match_stream(BSR *bsr, BSR_STREAM *stream, DEV_RECORD *rec, bool done)
503 return 1; /* no specification matches all */
505 if (stream->stream == rec->Stream) {
509 return match_stream(bsr, stream->next, rec, 1);
514 static int match_sesstime(BSR *bsr, BSR_SESSTIME *sesstime, DEV_RECORD *rec, bool done)
517 return 1; /* no specification matches all */
519 if (sesstime->sesstime == rec->VolSessionTime) {
522 if (rec->VolSessionTime > sesstime->sesstime) {
523 sesstime->done = true;
525 if (sesstime->next) {
526 return match_sesstime(bsr, sesstime->next, rec, sesstime->done && done);
528 if (sesstime->done && done) {
530 bsr->root->reposition = true;
531 Dmsg0(dbglevel, "bsr done from sesstime\n");
536 static int match_sessid(BSR *bsr, BSR_SESSID *sessid, DEV_RECORD *rec)
539 return 1; /* no specification matches all */
541 if (sessid->sessid <= rec->VolSessionId && sessid->sessid2 >= rec->VolSessionId) {
545 return match_sessid(bsr, sessid->next, rec);
551 * When reading the Volume, the Volume Findex (rec->FileIndex) always
552 * are found in sequential order. Thus we can make optimizations.
554 * ***FIXME*** optimizations
555 * We could optimize a lot here by removing the recursion, and
556 * stopping the search earlier -- say when rec->FileIndex > findex->findex2
557 * and findex->next == NULL. Also, the current entry tests could be skipped
558 * if findex->done is set.
560 static int match_findex(BSR *bsr, BSR_FINDEX *findex, DEV_RECORD *rec, bool done)
563 return 1; /* no specification matches all */
565 if (findex->findex <= rec->FileIndex && findex->findex2 >= rec->FileIndex) {
566 Dmsg3(dbglevel, "Match on findex=%d. bsr=%d,%d\n",
567 rec->FileIndex, findex->findex, findex->findex2);
570 if (rec->FileIndex > findex->findex2) {
574 Dmsg0(dbglevel, "Next findex link\n");
575 return match_findex(bsr, findex->next, rec, findex->done && done);
577 if (findex->done && done) {
579 bsr->root->reposition = true;
580 Dmsg1(dbglevel, "bsr done from findex %d\n", rec->FileIndex);