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 /* Forward references */
47 static int match_volume(BSR *bsr, BSR_VOLUME *volume, VOLUME_LABEL *volrec, bool done);
48 static int match_sesstime(BSR *bsr, BSR_SESSTIME *sesstime, DEV_RECORD *rec, bool done);
49 static int match_sessid(BSR *bsr, BSR_SESSID *sessid, DEV_RECORD *rec);
50 static int match_client(BSR *bsr, BSR_CLIENT *client, SESSION_LABEL *sessrec, bool done);
51 static int match_job(BSR *bsr, BSR_JOB *job, SESSION_LABEL *sessrec, bool done);
52 static int match_job_type(BSR *bsr, BSR_JOBTYPE *job_type, SESSION_LABEL *sessrec, bool done);
53 static int match_job_level(BSR *bsr, BSR_JOBLEVEL *job_level, SESSION_LABEL *sessrec, bool done);
54 static int match_jobid(BSR *bsr, BSR_JOBID *jobid, SESSION_LABEL *sessrec, bool done);
55 static int match_findex(BSR *bsr, BSR_FINDEX *findex, DEV_RECORD *rec, bool done);
56 static int match_volfile(BSR *bsr, BSR_VOLFILE *volfile, DEV_RECORD *rec, bool done);
57 static int match_stream(BSR *bsr, BSR_STREAM *stream, DEV_RECORD *rec, bool done);
58 static int match_all(BSR *bsr, DEV_RECORD *rec, VOLUME_LABEL *volrec, SESSION_LABEL *sessrec, bool done);
59 static int match_block_sesstime(BSR *bsr, BSR_SESSTIME *sesstime, DEV_BLOCK *block);
60 static int match_block_sessid(BSR *bsr, BSR_SESSID *sessid, DEV_BLOCK *block);
61 static BSR *find_smallest_volfile(BSR *fbsr, BSR *bsr);
64 /*********************************************************************
66 * If possible, position the archive device (tape) to read the
69 void position_bsr_block(BSR *bsr, DEV_BLOCK *block)
71 /* To be implemented */
74 /*********************************************************************
76 * Do fast block rejection based on bootstrap records.
77 * use_fast_rejection will be set if we have VolSessionId and VolSessTime
78 * in each record. When BlockVer is >= 2, we have those in the block header
79 * so can do fast rejection.
81 * returns: 1 if block may contain valid records
82 * 0 if block may be skipped (i.e. it contains no records of
83 * that can match the bsr).
86 int match_bsr_block(BSR *bsr, DEV_BLOCK *block)
88 if (!bsr || !bsr->use_fast_rejection || (block->BlockVer < 2)) {
89 return 1; /* cannot fast reject */
92 for ( ; bsr; bsr=bsr->next) {
93 if (!match_block_sesstime(bsr, bsr->sesstime, block)) {
96 if (!match_block_sessid(bsr, bsr->sessid, block)) {
104 static int match_block_sesstime(BSR *bsr, BSR_SESSTIME *sesstime, DEV_BLOCK *block)
107 return 1; /* no specification matches all */
109 if (sesstime->sesstime == block->VolSessionTime) {
112 if (sesstime->next) {
113 return match_block_sesstime(bsr, sesstime->next, block);
118 static int match_block_sessid(BSR *bsr, BSR_SESSID *sessid, DEV_BLOCK *block)
121 return 1; /* no specification matches all */
123 if (sessid->sessid <= block->VolSessionId && sessid->sessid2 >= block->VolSessionId) {
127 return match_block_sessid(bsr, sessid->next, block);
133 /*********************************************************************
135 * Match Bootstrap records
137 * returns 0 no match and reposition is set if we should
138 * reposition the tape
139 * returns -1 no additional matches possible
141 int match_bsr(BSR *bsr, DEV_RECORD *rec, VOLUME_LABEL *volrec, SESSION_LABEL *sessrec)
146 * The bsr->reposition flag is set any time a bsr is done.
147 * In this case, we can probably reposition the
148 * tape to the next available bsr position.
151 bsr->reposition = false;
152 stat = match_all(bsr, rec, volrec, sessrec, true);
154 * Note, bsr->reposition is set by match_all when
155 * a bsr is done. We turn it off if a match was
156 * found or if we cannot use positioning
158 if (stat != 0 || !bsr->use_positioning) {
159 bsr->reposition = false;
162 stat = 1; /* no bsr => match all */
168 * Find the next bsr that applies to the current tape.
169 * It is the one with the smallest VolFile position.
171 BSR *find_next_bsr(BSR *root_bsr, DEVICE *dev)
174 BSR *found_bsr = NULL;
175 bool no_file_seek = !dev->is_tape();
177 no_file_seek = false;
180 if (!root_bsr || !root_bsr->use_positioning ||
181 !root_bsr->reposition || no_file_seek) {
182 Dmsg2(300, "No nxt_bsr use_pos=%d repos=%d\n", root_bsr->use_positioning, root_bsr->reposition);
185 Dmsg2(300, "use_pos=%d repos=%d\n", root_bsr->use_positioning, root_bsr->reposition);
186 root_bsr->mount_next_volume = false;
187 for (bsr=root_bsr; bsr; bsr=bsr->next) {
188 if (bsr->done || !match_volume(bsr, bsr->volume, &dev->VolHdr, 1)) {
191 if (found_bsr == NULL) {
194 found_bsr = find_smallest_volfile(found_bsr, bsr);
198 * If we get to this point and found no bsr, it means
199 * that any additional bsr's must apply to the next
200 * tape, so set a flag.
202 if (found_bsr == NULL) {
203 root_bsr->mount_next_volume = true;
208 static BSR *find_smallest_volfile(BSR *found_bsr, BSR *bsr)
210 BSR *return_bsr = found_bsr;
213 uint32_t found_bsr_sfile, bsr_sfile;
214 uint32_t found_bsr_sblock, bsr_sblock;
216 vf = found_bsr->volfile;
217 found_bsr_sfile = vf->sfile;
218 while ( (vf=vf->next) ) {
219 if (vf->sfile < found_bsr_sfile) {
220 found_bsr_sfile = vf->sfile;
224 bsr_sfile = vf->sfile;
225 while ( (vf=vf->next) ) {
226 if (vf->sfile < bsr_sfile) {
227 bsr_sfile = vf->sfile;
230 if (found_bsr_sfile > bsr_sfile) {
232 } else if (found_bsr_sfile == bsr_sfile) {
233 /* Must check block */
234 vb = found_bsr->volblock;
235 found_bsr_sblock = vb->sblock;
236 while ( (vb=vb->next) ) {
237 if (vb->sblock < found_bsr_sblock) {
238 found_bsr_sblock = vb->sblock;
242 bsr_sblock = vb->sblock;
243 while ( (vb=vb->next) ) {
244 if (vb->sblock < bsr_sblock) {
245 bsr_sblock = vb->sblock;
248 if (found_bsr_sblock > bsr_sblock) {
257 * Called after the signature record so that
258 * we can see if the current bsr has been
259 * fully processed (i.e. is done).
260 * The bsr argument is not used, but is included
261 * for consistency with the other match calls.
263 * Returns: true if we should reposition
266 bool is_this_bsr_done(BSR *bsr, DEV_RECORD *rec)
268 BSR *rbsr = rec->bsr;
269 Dmsg1(300, "match_set %d\n", rbsr != NULL);
275 if (rbsr->count && rbsr->found >= rbsr->count) {
277 rbsr->root->reposition = true;
278 Dmsg2(500, "is_end_this_bsr set reposition=1 count=%d found=%d\n",
279 rbsr->count, rbsr->found);
286 * Match all the components of current record
289 * returns -1 no additional matches possible
291 static int match_all(BSR *bsr, DEV_RECORD *rec, VOLUME_LABEL *volrec,
292 SESSION_LABEL *sessrec, bool done)
295 Dmsg0(300, "bsr->done set\n");
298 if (!match_volume(bsr, bsr->volume, volrec, 1)) {
299 Dmsg2(300, "bsr fail vol=%s != rec vol=%s\n", bsr->volume->VolumeName,
303 if (!match_volfile(bsr, bsr->volfile, rec, 1)) {
304 Dmsg2(300, "Fail on file. bsr=%d rec=%d\n", bsr->volfile->efile,
308 if (!match_sesstime(bsr, bsr->sesstime, rec, 1)) {
309 Dmsg2(300, "Fail on sesstime. bsr=%d rec=%d\n",
310 bsr->sesstime->sesstime, rec->VolSessionTime);
314 /* NOTE!! This test MUST come after the sesstime test */
315 if (!match_sessid(bsr, bsr->sessid, rec)) {
316 Dmsg2(300, "Fail on sessid. bsr=%d rec=%d\n",
317 bsr->sessid->sessid, rec->VolSessionId);
321 /* NOTE!! This test MUST come after sesstime and sessid tests */
322 if (!match_findex(bsr, bsr->FileIndex, rec, 1)) {
323 Dmsg2(300, "Fail on findex. bsr=%d rec=%d\n",
324 bsr->FileIndex->findex2, rec->FileIndex);
328 * If a count was specified and we have a FileIndex, assume
329 * it is a Bacula created bsr (or the equivalent). We
330 * then save the bsr where the match occurred so that
331 * after processing the record or records, we can update
332 * the found count. I.e. rec->bsr points to the bsr that
333 * satisfied the match.
335 if (bsr->count && bsr->FileIndex) {
337 return 1; /* this is a complete match */
341 * The selections below are not used by Bacula's
342 * restore command, and don't work because of
343 * the rec->bsr = bsr optimization above.
345 if (!match_jobid(bsr, bsr->JobId, sessrec, 1)) {
346 Dmsg0(300, "fail on JobId\n");
350 if (!match_job(bsr, bsr->job, sessrec, 1)) {
351 Dmsg0(300, "fail on Job\n");
354 if (!match_client(bsr, bsr->client, sessrec, 1)) {
355 Dmsg0(300, "fail on Client\n");
358 if (!match_job_type(bsr, bsr->JobType, sessrec, 1)) {
359 Dmsg0(300, "fail on Job type\n");
362 if (!match_job_level(bsr, bsr->JobLevel, sessrec, 1)) {
363 Dmsg0(300, "fail on Job level\n");
366 if (!match_stream(bsr, bsr->stream, rec, 1)) {
367 Dmsg0(300, "fail on stream\n");
374 return match_all(bsr->next, rec, volrec, sessrec, bsr->done && done);
376 if (bsr->done && done) {
382 static int match_volume(BSR *bsr, BSR_VOLUME *volume, VOLUME_LABEL *volrec, bool done)
385 return 0; /* Volume must match */
387 if (strcmp(volume->VolumeName, volrec->VolumeName) == 0) {
391 return match_volume(bsr, volume->next, volrec, 1);
396 static int match_client(BSR *bsr, BSR_CLIENT *client, SESSION_LABEL *sessrec, bool done)
399 return 1; /* no specification matches all */
401 if (fnmatch(client->ClientName, sessrec->ClientName, 0) == 0) {
405 return match_client(bsr, client->next, sessrec, 1);
410 static int match_job(BSR *bsr, BSR_JOB *job, SESSION_LABEL *sessrec, bool done)
413 return 1; /* no specification matches all */
415 if (fnmatch(job->Job, sessrec->Job, 0) == 0) {
419 return match_job(bsr, job->next, sessrec, 1);
424 static int match_job_type(BSR *bsr, BSR_JOBTYPE *job_type, SESSION_LABEL *sessrec, bool done)
427 return 1; /* no specification matches all */
429 if (job_type->JobType == sessrec->JobType) {
432 if (job_type->next) {
433 return match_job_type(bsr, job_type->next, sessrec, 1);
438 static int match_job_level(BSR *bsr, BSR_JOBLEVEL *job_level, SESSION_LABEL *sessrec, bool done)
441 return 1; /* no specification matches all */
443 if (job_level->JobLevel == sessrec->JobLevel) {
446 if (job_level->next) {
447 return match_job_level(bsr, job_level->next, sessrec, 1);
452 static int match_jobid(BSR *bsr, BSR_JOBID *jobid, SESSION_LABEL *sessrec, bool done)
455 return 1; /* no specification matches all */
457 if (jobid->JobId <= sessrec->JobId && jobid->JobId2 >= sessrec->JobId) {
461 return match_jobid(bsr, jobid->next, sessrec, 1);
466 static int match_volfile(BSR *bsr, BSR_VOLFILE *volfile, DEV_RECORD *rec, bool done)
469 return 1; /* no specification matches all */
471 /* For the moment, these tests work only with tapes. */
472 if (!(rec->state & REC_ISTAPE)) {
473 return 1; /* All File records OK for this match */
475 // Dmsg3(300, "match_volfile: sfile=%d efile=%d recfile=%d\n",
476 // volfile->sfile, volfile->efile, rec->File);
477 if (volfile->sfile <= rec->File && volfile->efile >= rec->File) {
480 /* Once we get past last efile, we are done */
481 if (rec->File > volfile->efile) {
482 volfile->done = true; /* set local done */
485 return match_volfile(bsr, volfile->next, rec, volfile->done && done);
488 /* If we are done and all prior matches are done, this bsr is finished */
489 if (volfile->done && done) {
491 bsr->root->reposition = true;
492 Dmsg2(300, "bsr done from volfile rec=%d volefile=%d\n",
493 rec->File, volfile->efile);
498 static int match_stream(BSR *bsr, BSR_STREAM *stream, DEV_RECORD *rec, bool done)
501 return 1; /* no specification matches all */
503 if (stream->stream == rec->Stream) {
507 return match_stream(bsr, stream->next, rec, 1);
512 static int match_sesstime(BSR *bsr, BSR_SESSTIME *sesstime, DEV_RECORD *rec, bool done)
515 return 1; /* no specification matches all */
517 if (sesstime->sesstime == rec->VolSessionTime) {
520 if (rec->VolSessionTime > sesstime->sesstime) {
521 sesstime->done = true;
523 if (sesstime->next) {
524 return match_sesstime(bsr, sesstime->next, rec, sesstime->done && done);
526 if (sesstime->done && done) {
528 bsr->root->reposition = true;
529 Dmsg0(300, "bsr done from sesstime\n");
534 static int match_sessid(BSR *bsr, BSR_SESSID *sessid, DEV_RECORD *rec)
537 return 1; /* no specification matches all */
539 if (sessid->sessid <= rec->VolSessionId && sessid->sessid2 >= rec->VolSessionId) {
543 return match_sessid(bsr, sessid->next, rec);
549 * When reading the Volume, the Volume Findex (rec->FileIndex) always
550 * are found in sequential order. Thus we can make optimizations.
552 * ***FIXME*** optimizations
553 * We could optimize a lot here by removing the recursion, and
554 * stopping the search earlier -- say when rec->FileIndex > findex->findex2
555 * and findex->next == NULL. Also, the current entry tests could be skipped
556 * if findex->done is set.
558 static int match_findex(BSR *bsr, BSR_FINDEX *findex, DEV_RECORD *rec, bool done)
561 return 1; /* no specification matches all */
563 if (findex->findex <= rec->FileIndex && findex->findex2 >= rec->FileIndex) {
566 if (rec->FileIndex > findex->findex2) {
570 return match_findex(bsr, findex->next, rec, findex->done && done);
572 if (findex->done && done) {
574 bsr->root->reposition = true;
575 Dmsg1(300, "bsr done from findex %d\n", rec->FileIndex);