2 Bacula® - The Network Backup Solution
4 Copyright (C) 2002-2007 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 John Walker.
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_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;
195 /* Do tape/disk seeking only if CAP_POSITIONBLOCKS is on */
196 if (!root_bsr || !root_bsr->use_positioning ||
197 !root_bsr->reposition || !dev->has_cap(CAP_POSITIONBLOCKS)) {
198 Dmsg2(dbglevel, "No nxt_bsr use_pos=%d repos=%d\n", root_bsr->use_positioning, root_bsr->reposition);
201 Dmsg2(dbglevel, "use_pos=%d repos=%d\n", root_bsr->use_positioning, root_bsr->reposition);
202 root_bsr->mount_next_volume = false;
203 /* Walk through all bsrs to find the next one to use => smallest file,block */
204 for (bsr=root_bsr; bsr; bsr=bsr->next) {
205 if (bsr->done || !match_volume(bsr, bsr->volume, &dev->VolHdr, 1)) {
208 if (found_bsr == NULL) {
211 found_bsr = find_smallest_volfile(found_bsr, bsr);
215 * If we get to this point and found no bsr, it means
216 * that any additional bsr's must apply to the next
217 * tape, so set a flag.
219 if (found_bsr == NULL) {
220 root_bsr->mount_next_volume = true;
227 * This routine needs to be fixed to only look at items that
228 * are not marked as done. Otherwise, it can find a bsr
229 * that has already been consumed, and this will cause the
230 * bsr to be used, thus we may seek back and re-read the
231 * same records, causing an error. This deficiency must
232 * be fixed. For the moment, it has been kludged in
233 * read_record.c to avoid seeking back if find_next_bsr
234 * returns a bsr pointing to a smaller address (file/block).
236 static BSR *find_smallest_volfile(BSR *found_bsr, BSR *bsr)
238 BSR *return_bsr = found_bsr;
241 uint32_t found_bsr_sfile, bsr_sfile;
242 uint32_t found_bsr_sblock, bsr_sblock;
244 /* Find the smallest file in the found_bsr */
245 vf = found_bsr->volfile;
246 found_bsr_sfile = vf->sfile;
247 while ( (vf=vf->next) ) {
248 if (vf->sfile < found_bsr_sfile) {
249 found_bsr_sfile = vf->sfile;
253 /* Find the smallest file in the bsr */
255 bsr_sfile = vf->sfile;
256 while ( (vf=vf->next) ) {
257 if (vf->sfile < bsr_sfile) {
258 bsr_sfile = vf->sfile;
262 /* if the bsr file is less than the found_bsr file, return bsr */
263 if (found_bsr_sfile > bsr_sfile) {
265 } else if (found_bsr_sfile == bsr_sfile) {
266 /* Files are equal */
267 /* find smallest block in found_bsr */
268 vb = found_bsr->volblock;
269 found_bsr_sblock = vb->sblock;
270 while ( (vb=vb->next) ) {
271 if (vb->sblock < found_bsr_sblock) {
272 found_bsr_sblock = vb->sblock;
275 /* Find smallest block in bsr */
277 bsr_sblock = vb->sblock;
278 while ( (vb=vb->next) ) {
279 if (vb->sblock < bsr_sblock) {
280 bsr_sblock = vb->sblock;
283 /* Compare and return the smallest */
284 if (found_bsr_sblock > bsr_sblock) {
292 * Called after the signature record so that
293 * we can see if the current bsr has been
294 * fully processed (i.e. is done).
295 * The bsr argument is not used, but is included
296 * for consistency with the other match calls.
298 * Returns: true if we should reposition
301 bool is_this_bsr_done(BSR *bsr, DEV_RECORD *rec)
303 BSR *rbsr = rec->bsr;
304 Dmsg1(dbglevel, "match_set %d\n", rbsr != NULL);
310 if (rbsr->count && rbsr->found >= rbsr->count) {
312 rbsr->root->reposition = true;
313 Dmsg2(dbglevel, "is_end_this_bsr set reposition=1 count=%d found=%d\n",
314 rbsr->count, rbsr->found);
321 * Match all the components of current record
324 * returns -1 no additional matches possible
326 static int match_all(BSR *bsr, DEV_RECORD *rec, VOLUME_LABEL *volrec,
327 SESSION_LABEL *sessrec, bool done)
330 // Dmsg0(dbglevel, "bsr->done set\n");
333 if (!match_volume(bsr, bsr->volume, volrec, 1)) {
334 Dmsg2(dbglevel, "bsr fail vol=%s != rec vol=%s\n", bsr->volume->VolumeName,
338 if (!match_volfile(bsr, bsr->volfile, rec, 1)) {
339 Dmsg3(dbglevel, "Fail on file=%d. bsr=%d,%d\n",
340 rec->File, bsr->volfile->sfile, bsr->volfile->efile);
343 if (!match_sesstime(bsr, bsr->sesstime, rec, 1)) {
344 Dmsg2(dbglevel, "Fail on sesstime. bsr=%d rec=%d\n",
345 bsr->sesstime->sesstime, rec->VolSessionTime);
349 /* NOTE!! This test MUST come after the sesstime test */
350 if (!match_sessid(bsr, bsr->sessid, rec)) {
351 Dmsg2(dbglevel, "Fail on sessid. bsr=%d rec=%d\n",
352 bsr->sessid->sessid, rec->VolSessionId);
356 /* NOTE!! This test MUST come after sesstime and sessid tests */
357 if (!match_findex(bsr, bsr->FileIndex, rec, 1)) {
358 Dmsg3(dbglevel, "Fail on findex=%d. bsr=%d,%d\n",
359 rec->FileIndex, bsr->FileIndex->findex, bsr->FileIndex->findex2);
363 * If a count was specified and we have a FileIndex, assume
364 * it is a Bacula created bsr (or the equivalent). We
365 * then save the bsr where the match occurred so that
366 * after processing the record or records, we can update
367 * the found count. I.e. rec->bsr points to the bsr that
368 * satisfied the match.
370 if (bsr->count && bsr->FileIndex) {
372 return 1; /* this is a complete match */
376 * The selections below are not used by Bacula's
377 * restore command, and don't work because of
378 * the rec->bsr = bsr optimization above.
380 if (!match_jobid(bsr, bsr->JobId, sessrec, 1)) {
381 Dmsg0(dbglevel, "fail on JobId\n");
385 if (!match_job(bsr, bsr->job, sessrec, 1)) {
386 Dmsg0(dbglevel, "fail on Job\n");
389 if (!match_client(bsr, bsr->client, sessrec, 1)) {
390 Dmsg0(dbglevel, "fail on Client\n");
393 if (!match_job_type(bsr, bsr->JobType, sessrec, 1)) {
394 Dmsg0(dbglevel, "fail on Job type\n");
397 if (!match_job_level(bsr, bsr->JobLevel, sessrec, 1)) {
398 Dmsg0(dbglevel, "fail on Job level\n");
401 if (!match_stream(bsr, bsr->stream, rec, 1)) {
402 Dmsg0(dbglevel, "fail on stream\n");
409 return match_all(bsr->next, rec, volrec, sessrec, bsr->done && done);
411 if (bsr->done && done) {
417 static int match_volume(BSR *bsr, BSR_VOLUME *volume, VOLUME_LABEL *volrec, bool done)
420 return 0; /* Volume must match */
422 if (strcmp(volume->VolumeName, volrec->VolumeName) == 0) {
426 return match_volume(bsr, volume->next, volrec, 1);
431 static int match_client(BSR *bsr, BSR_CLIENT *client, SESSION_LABEL *sessrec, bool done)
434 return 1; /* no specification matches all */
436 if (strcmp(client->ClientName, sessrec->ClientName) == 0) {
440 return match_client(bsr, client->next, sessrec, 1);
445 static int match_job(BSR *bsr, BSR_JOB *job, SESSION_LABEL *sessrec, bool done)
448 return 1; /* no specification matches all */
450 if (strcmp(job->Job, sessrec->Job) == 0) {
454 return match_job(bsr, job->next, sessrec, 1);
459 static int match_job_type(BSR *bsr, BSR_JOBTYPE *job_type, SESSION_LABEL *sessrec, bool done)
462 return 1; /* no specification matches all */
464 if (job_type->JobType == sessrec->JobType) {
467 if (job_type->next) {
468 return match_job_type(bsr, job_type->next, sessrec, 1);
473 static int match_job_level(BSR *bsr, BSR_JOBLEVEL *job_level, SESSION_LABEL *sessrec, bool done)
476 return 1; /* no specification matches all */
478 if (job_level->JobLevel == sessrec->JobLevel) {
481 if (job_level->next) {
482 return match_job_level(bsr, job_level->next, sessrec, 1);
487 static int match_jobid(BSR *bsr, BSR_JOBID *jobid, SESSION_LABEL *sessrec, bool done)
490 return 1; /* no specification matches all */
492 if (jobid->JobId <= sessrec->JobId && jobid->JobId2 >= sessrec->JobId) {
496 return match_jobid(bsr, jobid->next, sessrec, 1);
501 static int match_volfile(BSR *bsr, BSR_VOLFILE *volfile, DEV_RECORD *rec, bool done)
504 return 1; /* no specification matches all */
506 /* For the moment, these tests work only with tapes. */
507 if (!(rec->state & REC_ISTAPE)) {
508 return 1; /* All File records OK for this match */
510 // Dmsg3(dbglevel, "match_volfile: sfile=%d efile=%d recfile=%d\n",
511 // volfile->sfile, volfile->efile, rec->File);
512 if (volfile->sfile <= rec->File && volfile->efile >= rec->File) {
515 /* Once we get past last efile, we are done */
516 if (rec->File > volfile->efile) {
517 volfile->done = true; /* set local done */
520 return match_volfile(bsr, volfile->next, rec, volfile->done && done);
523 /* If we are done and all prior matches are done, this bsr is finished */
524 if (volfile->done && done) {
526 bsr->root->reposition = true;
527 Dmsg2(dbglevel, "bsr done from volfile rec=%d volefile=%d\n",
528 rec->File, volfile->efile);
533 static int match_stream(BSR *bsr, BSR_STREAM *stream, DEV_RECORD *rec, bool done)
536 return 1; /* no specification matches all */
538 if (stream->stream == rec->Stream) {
542 return match_stream(bsr, stream->next, rec, 1);
547 static int match_sesstime(BSR *bsr, BSR_SESSTIME *sesstime, DEV_RECORD *rec, bool done)
550 return 1; /* no specification matches all */
552 if (sesstime->sesstime == rec->VolSessionTime) {
555 if (rec->VolSessionTime > sesstime->sesstime) {
556 sesstime->done = true;
558 if (sesstime->next) {
559 return match_sesstime(bsr, sesstime->next, rec, sesstime->done && done);
561 if (sesstime->done && done) {
563 bsr->root->reposition = true;
564 Dmsg0(dbglevel, "bsr done from sesstime\n");
569 static int match_sessid(BSR *bsr, BSR_SESSID *sessid, DEV_RECORD *rec)
572 return 1; /* no specification matches all */
574 if (sessid->sessid <= rec->VolSessionId && sessid->sessid2 >= rec->VolSessionId) {
578 return match_sessid(bsr, sessid->next, rec);
584 * When reading the Volume, the Volume Findex (rec->FileIndex) always
585 * are found in sequential order. Thus we can make optimizations.
587 * ***FIXME*** optimizations
588 * We could optimize a lot here by removing the recursion, and
589 * stopping the search earlier -- say when rec->FileIndex > findex->findex2
590 * and findex->next == NULL.
592 static int match_findex(BSR *bsr, BSR_FINDEX *findex, DEV_RECORD *rec, bool done)
595 return 1; /* no specification matches all */
598 if (findex->findex <= rec->FileIndex && findex->findex2 >= rec->FileIndex) {
599 Dmsg3(dbglevel, "Match on findex=%d. bsr=%d,%d\n",
600 rec->FileIndex, findex->findex, findex->findex2);
603 if (rec->FileIndex > findex->findex2) {
608 return match_findex(bsr, findex->next, rec, findex->done && done);
610 if (findex->done && done) {
612 bsr->root->reposition = true;
613 Dmsg1(dbglevel, "bsr done from findex %d\n", rec->FileIndex);