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 Kern Sibbald.
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, JCR *jcr);
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);
151 static int match_fileregex(BSR *bsr, DEV_RECORD *rec, JCR *jcr)
153 if (bsr->fileregex_re == NULL)
156 if (bsr->attr == NULL)
157 bsr->attr = new_attr(jcr);
159 /* The code breaks if the first record associated with a file is
162 if (rec->Stream == STREAM_UNIX_ATTRIBUTES ||
163 rec->Stream == STREAM_UNIX_ATTRIBUTES_EX) {
164 bsr->skip_file = false;
165 if (unpack_attributes_record(jcr, rec->Stream, rec->data, bsr->attr)) {
166 if (regexec(bsr->fileregex_re, bsr->attr->fname, 0, NULL, 0) == 0) {
167 Dmsg2(dbglevel, "Matched pattern, fname=%s FI=%d\n",
168 bsr->attr->fname, rec->FileIndex);
170 Dmsg2(dbglevel, "Didn't match, skipping fname=%s FI=%d\n",
171 bsr->attr->fname, rec->FileIndex);
172 bsr->skip_file = true;
179 /*********************************************************************
181 * Match Bootstrap records
183 * returns 0 no match and reposition is set if we should
184 * reposition the tape
185 * returns -1 no additional matches possible
187 int match_bsr(BSR *bsr, DEV_RECORD *rec, VOLUME_LABEL *volrec, SESSION_LABEL *sessrec, JCR *jcr)
192 * The bsr->reposition flag is set any time a bsr is done.
193 * In this case, we can probably reposition the
194 * tape to the next available bsr position.
197 bsr->reposition = false;
198 stat = match_all(bsr, rec, volrec, sessrec, true, jcr);
200 * Note, bsr->reposition is set by match_all when
201 * a bsr is done. We turn it off if a match was
202 * found or if we cannot use positioning
204 if (stat != 0 || !bsr->use_positioning) {
205 bsr->reposition = false;
208 stat = 1; /* no bsr => match all */
214 * Find the next bsr that applies to the current tape.
215 * It is the one with the smallest VolFile position.
217 BSR *find_next_bsr(BSR *root_bsr, DEVICE *dev)
220 BSR *found_bsr = NULL;
222 /* Do tape/disk seeking only if CAP_POSITIONBLOCKS is on */
224 Dmsg0(dbglevel, "NULL root bsr pointer passed to find_next_bsr.\n");
227 if (!root_bsr->use_positioning ||
228 !root_bsr->reposition || !dev->has_cap(CAP_POSITIONBLOCKS)) {
229 Dmsg2(dbglevel, "No nxt_bsr use_pos=%d repos=%d\n", root_bsr->use_positioning, root_bsr->reposition);
232 Dmsg2(dbglevel, "use_pos=%d repos=%d\n", root_bsr->use_positioning, root_bsr->reposition);
233 root_bsr->mount_next_volume = false;
234 /* Walk through all bsrs to find the next one to use => smallest file,block */
235 for (bsr=root_bsr; bsr; bsr=bsr->next) {
236 if (bsr->done || !match_volume(bsr, bsr->volume, &dev->VolHdr, 1)) {
239 if (found_bsr == NULL) {
242 found_bsr = find_smallest_volfile(found_bsr, bsr);
246 * If we get to this point and found no bsr, it means
247 * that any additional bsr's must apply to the next
248 * tape, so set a flag.
250 if (found_bsr == NULL) {
251 root_bsr->mount_next_volume = true;
258 * This routine needs to be fixed to only look at items that
259 * are not marked as done. Otherwise, it can find a bsr
260 * that has already been consumed, and this will cause the
261 * bsr to be used, thus we may seek back and re-read the
262 * same records, causing an error. This deficiency must
263 * be fixed. For the moment, it has been kludged in
264 * read_record.c to avoid seeking back if find_next_bsr
265 * returns a bsr pointing to a smaller address (file/block).
267 static BSR *find_smallest_volfile(BSR *found_bsr, BSR *bsr)
269 BSR *return_bsr = found_bsr;
272 uint32_t found_bsr_sfile, bsr_sfile;
273 uint32_t found_bsr_sblock, bsr_sblock;
275 /* Find the smallest file in the found_bsr */
276 vf = found_bsr->volfile;
277 found_bsr_sfile = vf->sfile;
278 while ( (vf=vf->next) ) {
279 if (vf->sfile < found_bsr_sfile) {
280 found_bsr_sfile = vf->sfile;
284 /* Find the smallest file in the bsr */
286 bsr_sfile = vf->sfile;
287 while ( (vf=vf->next) ) {
288 if (vf->sfile < bsr_sfile) {
289 bsr_sfile = vf->sfile;
293 /* if the bsr file is less than the found_bsr file, return bsr */
294 if (found_bsr_sfile > bsr_sfile) {
296 } else if (found_bsr_sfile == bsr_sfile) {
297 /* Files are equal */
298 /* find smallest block in found_bsr */
299 vb = found_bsr->volblock;
300 found_bsr_sblock = vb->sblock;
301 while ( (vb=vb->next) ) {
302 if (vb->sblock < found_bsr_sblock) {
303 found_bsr_sblock = vb->sblock;
306 /* Find smallest block in bsr */
308 bsr_sblock = vb->sblock;
309 while ( (vb=vb->next) ) {
310 if (vb->sblock < bsr_sblock) {
311 bsr_sblock = vb->sblock;
314 /* Compare and return the smallest */
315 if (found_bsr_sblock > bsr_sblock) {
323 * Called after the signature record so that
324 * we can see if the current bsr has been
325 * fully processed (i.e. is done).
326 * The bsr argument is not used, but is included
327 * for consistency with the other match calls.
329 * Returns: true if we should reposition
332 bool is_this_bsr_done(BSR *bsr, DEV_RECORD *rec)
334 BSR *rbsr = rec->bsr;
335 Dmsg1(dbglevel, "match_set %d\n", rbsr != NULL);
341 if (rbsr->count && rbsr->found >= rbsr->count) {
343 rbsr->root->reposition = true;
344 Dmsg2(dbglevel, "is_end_this_bsr set reposition=1 count=%d found=%d\n",
345 rbsr->count, rbsr->found);
352 * Match all the components of current record
355 * returns -1 no additional matches possible
357 static int match_all(BSR *bsr, DEV_RECORD *rec, VOLUME_LABEL *volrec,
358 SESSION_LABEL *sessrec, bool done, JCR *jcr)
361 // Dmsg0(dbglevel, "bsr->done set\n");
364 if (!match_volume(bsr, bsr->volume, volrec, 1)) {
365 Dmsg2(dbglevel, "bsr fail vol=%s != rec vol=%s\n", bsr->volume->VolumeName,
369 if (!match_volfile(bsr, bsr->volfile, rec, 1)) {
370 Dmsg3(dbglevel, "Fail on file=%d. bsr=%d,%d\n",
371 rec->File, bsr->volfile->sfile, bsr->volfile->efile);
374 if (!match_sesstime(bsr, bsr->sesstime, rec, 1)) {
375 Dmsg2(dbglevel, "Fail on sesstime. bsr=%d rec=%d\n",
376 bsr->sesstime->sesstime, rec->VolSessionTime);
380 /* NOTE!! This test MUST come after the sesstime test */
381 if (!match_sessid(bsr, bsr->sessid, rec)) {
382 Dmsg2(dbglevel, "Fail on sessid. bsr=%d rec=%d\n",
383 bsr->sessid->sessid, rec->VolSessionId);
387 /* NOTE!! This test MUST come after sesstime and sessid tests */
388 if (!match_findex(bsr, bsr->FileIndex, rec, 1)) {
389 Dmsg3(dbglevel, "Fail on findex=%d. bsr=%d,%d\n",
390 rec->FileIndex, bsr->FileIndex->findex, bsr->FileIndex->findex2);
394 if (!match_fileregex(bsr, rec, jcr)) {
395 Dmsg1(dbglevel, "Fail on fileregex='%s'\n", bsr->fileregex);
399 /* This flag is set by match_fileregex (and perhaps other tests) */
400 if (bsr->skip_file) {
401 Dmsg1(dbglevel, "Skipping findex=%d\n", rec->FileIndex);
406 * If a count was specified and we have a FileIndex, assume
407 * it is a Bacula created bsr (or the equivalent). We
408 * then save the bsr where the match occurred so that
409 * after processing the record or records, we can update
410 * the found count. I.e. rec->bsr points to the bsr that
411 * satisfied the match.
413 if (bsr->count && bsr->FileIndex) {
415 return 1; /* this is a complete match */
419 * The selections below are not used by Bacula's
420 * restore command, and don't work because of
421 * the rec->bsr = bsr optimization above.
423 if (!match_jobid(bsr, bsr->JobId, sessrec, 1)) {
424 Dmsg0(dbglevel, "fail on JobId\n");
428 if (!match_job(bsr, bsr->job, sessrec, 1)) {
429 Dmsg0(dbglevel, "fail on Job\n");
432 if (!match_client(bsr, bsr->client, sessrec, 1)) {
433 Dmsg0(dbglevel, "fail on Client\n");
436 if (!match_job_type(bsr, bsr->JobType, sessrec, 1)) {
437 Dmsg0(dbglevel, "fail on Job type\n");
440 if (!match_job_level(bsr, bsr->JobLevel, sessrec, 1)) {
441 Dmsg0(dbglevel, "fail on Job level\n");
444 if (!match_stream(bsr, bsr->stream, rec, 1)) {
445 Dmsg0(dbglevel, "fail on stream\n");
452 return match_all(bsr->next, rec, volrec, sessrec, bsr->done && done, jcr);
454 if (bsr->done && done) {
460 static int match_volume(BSR *bsr, BSR_VOLUME *volume, VOLUME_LABEL *volrec, bool done)
463 return 0; /* Volume must match */
465 if (strcmp(volume->VolumeName, volrec->VolumeName) == 0) {
469 return match_volume(bsr, volume->next, volrec, 1);
474 static int match_client(BSR *bsr, BSR_CLIENT *client, SESSION_LABEL *sessrec, bool done)
477 return 1; /* no specification matches all */
479 if (strcmp(client->ClientName, sessrec->ClientName) == 0) {
483 return match_client(bsr, client->next, sessrec, 1);
488 static int match_job(BSR *bsr, BSR_JOB *job, SESSION_LABEL *sessrec, bool done)
491 return 1; /* no specification matches all */
493 if (strcmp(job->Job, sessrec->Job) == 0) {
497 return match_job(bsr, job->next, sessrec, 1);
502 static int match_job_type(BSR *bsr, BSR_JOBTYPE *job_type, SESSION_LABEL *sessrec, bool done)
505 return 1; /* no specification matches all */
507 if (job_type->JobType == sessrec->JobType) {
510 if (job_type->next) {
511 return match_job_type(bsr, job_type->next, sessrec, 1);
516 static int match_job_level(BSR *bsr, BSR_JOBLEVEL *job_level, SESSION_LABEL *sessrec, bool done)
519 return 1; /* no specification matches all */
521 if (job_level->JobLevel == sessrec->JobLevel) {
524 if (job_level->next) {
525 return match_job_level(bsr, job_level->next, sessrec, 1);
530 static int match_jobid(BSR *bsr, BSR_JOBID *jobid, SESSION_LABEL *sessrec, bool done)
533 return 1; /* no specification matches all */
535 if (jobid->JobId <= sessrec->JobId && jobid->JobId2 >= sessrec->JobId) {
539 return match_jobid(bsr, jobid->next, sessrec, 1);
544 static int match_volfile(BSR *bsr, BSR_VOLFILE *volfile, DEV_RECORD *rec, bool done)
547 return 1; /* no specification matches all */
549 /* For the moment, these tests work only with tapes. */
550 if (!(rec->state & REC_ISTAPE)) {
551 return 1; /* All File records OK for this match */
553 // Dmsg3(dbglevel, "match_volfile: sfile=%d efile=%d recfile=%d\n",
554 // volfile->sfile, volfile->efile, rec->File);
555 if (volfile->sfile <= rec->File && volfile->efile >= rec->File) {
558 /* Once we get past last efile, we are done */
559 if (rec->File > volfile->efile) {
560 volfile->done = true; /* set local done */
563 return match_volfile(bsr, volfile->next, rec, volfile->done && done);
566 /* If we are done and all prior matches are done, this bsr is finished */
567 if (volfile->done && done) {
569 bsr->root->reposition = true;
570 Dmsg2(dbglevel, "bsr done from volfile rec=%d volefile=%d\n",
571 rec->File, volfile->efile);
576 static int match_stream(BSR *bsr, BSR_STREAM *stream, DEV_RECORD *rec, bool done)
579 return 1; /* no specification matches all */
581 if (stream->stream == rec->Stream) {
585 return match_stream(bsr, stream->next, rec, 1);
590 static int match_sesstime(BSR *bsr, BSR_SESSTIME *sesstime, DEV_RECORD *rec, bool done)
593 return 1; /* no specification matches all */
595 if (sesstime->sesstime == rec->VolSessionTime) {
598 if (rec->VolSessionTime > sesstime->sesstime) {
599 sesstime->done = true;
601 if (sesstime->next) {
602 return match_sesstime(bsr, sesstime->next, rec, sesstime->done && done);
604 if (sesstime->done && done) {
606 bsr->root->reposition = true;
607 Dmsg0(dbglevel, "bsr done from sesstime\n");
612 static int match_sessid(BSR *bsr, BSR_SESSID *sessid, DEV_RECORD *rec)
615 return 1; /* no specification matches all */
617 if (sessid->sessid <= rec->VolSessionId && sessid->sessid2 >= rec->VolSessionId) {
621 return match_sessid(bsr, sessid->next, rec);
627 * When reading the Volume, the Volume Findex (rec->FileIndex) always
628 * are found in sequential order. Thus we can make optimizations.
630 * ***FIXME*** optimizations
631 * We could optimize a lot here by removing the recursion, and
632 * stopping the search earlier -- say when rec->FileIndex > findex->findex2
633 * and findex->next == NULL.
635 static int match_findex(BSR *bsr, BSR_FINDEX *findex, DEV_RECORD *rec, bool done)
638 return 1; /* no specification matches all */
641 if (findex->findex <= rec->FileIndex && findex->findex2 >= rec->FileIndex) {
642 Dmsg3(dbglevel, "Match on findex=%d. bsr=%d,%d\n",
643 rec->FileIndex, findex->findex, findex->findex2);
646 if (rec->FileIndex > findex->findex2) {
651 return match_findex(bsr, findex->next, rec, findex->done && done);
653 if (findex->done && done) {
655 bsr->root->reposition = true;
656 Dmsg1(dbglevel, "bsr done from findex %d\n", rec->FileIndex);