]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/match_bsr.c
4de4c74c233adc416ab6d56f342134c85490dd88
[bacula/bacula] / bacula / src / stored / match_bsr.c
1 /*
2  *   Match Bootstrap Records (used for restores) against
3  *     Volume Records
4  *
5  *     Kern Sibbald, June MMII
6  *
7  *   Version $Id$
8  */
9 /*
10    Copyright (C) 2002-2005 Kern Sibbald
11
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.
16
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.
21
22  */
23
24
25 #include "bacula.h"
26 #include "stored.h"
27 #ifdef HAVE_FNMATCH
28 #include <fnmatch.h>
29 #else
30 #include "lib/fnmatch.h"
31 #endif
32
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);
49
50
51 /*********************************************************************
52  *
53  *  If possible, position the archive device (tape) to read the
54  *  next block.
55  */
56 void position_bsr_block(BSR *bsr, DEV_BLOCK *block)
57 {
58    /* To be implemented */
59 }
60
61 /*********************************************************************
62  *
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.
67  *
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).
71  *
72  */
73 int match_bsr_block(BSR *bsr, DEV_BLOCK *block)
74 {
75    if (!bsr || !bsr->use_fast_rejection || (block->BlockVer < 2)) {
76       return 1;                       /* cannot fast reject */
77    }
78
79    for ( ; bsr; bsr=bsr->next) {
80       if (!match_block_sesstime(bsr, bsr->sesstime, block)) {
81          continue;
82       }
83       if (!match_block_sessid(bsr, bsr->sessid, block)) {
84          continue;
85       }
86       return 1;
87    }
88    return 0;
89 }
90
91 static int match_block_sesstime(BSR *bsr, BSR_SESSTIME *sesstime, DEV_BLOCK *block)
92 {
93    if (!sesstime) {
94       return 1;                       /* no specification matches all */
95    }
96    if (sesstime->sesstime == block->VolSessionTime) {
97       return 1;
98    }
99    if (sesstime->next) {
100       return match_block_sesstime(bsr, sesstime->next, block);
101    }
102    return 0;
103 }
104
105 static int match_block_sessid(BSR *bsr, BSR_SESSID *sessid, DEV_BLOCK *block)
106 {
107    if (!sessid) {
108       return 1;                       /* no specification matches all */
109    }
110    if (sessid->sessid <= block->VolSessionId && sessid->sessid2 >= block->VolSessionId) {
111       return 1;
112    }
113    if (sessid->next) {
114       return match_block_sessid(bsr, sessid->next, block);
115    }
116    return 0;
117 }
118
119
120 /*********************************************************************
121  *
122  *      Match Bootstrap records
123  *        returns  1 on match
124  *        returns  0 no match and reposition is set if we should
125  *                      reposition the tape
126  *       returns -1 no additional matches possible
127  */
128 int match_bsr(BSR *bsr, DEV_RECORD *rec, VOLUME_LABEL *volrec, SESSION_LABEL *sessrec)
129 {
130    int stat;
131
132    /*
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.
136     */
137    if (bsr) {
138       bsr->reposition = false;
139       stat = match_all(bsr, rec, volrec, sessrec, true);
140       /*
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
144        */
145       if (stat != 0 || !bsr->use_positioning) {
146          bsr->reposition = false;
147       }
148    } else {
149       stat = 1;                       /* no bsr => match all */
150    }
151    return stat;
152 }
153
154 /*
155  * Find the next bsr that applies to the current tape.
156  *   It is the one with the smallest VolFile position.
157  */
158 BSR *find_next_bsr(BSR *root_bsr, DEVICE *dev)
159 {
160    BSR *bsr;
161    BSR *found_bsr = NULL;
162
163    if (!root_bsr || !root_bsr->use_positioning ||
164        !root_bsr->reposition /* || !dev->is_tape()*/) {
165       Dmsg2(100, "No nxt_bsr use_pos=%d repos=%d\n", root_bsr->use_positioning, root_bsr->reposition);
166       return NULL;
167    }
168    Dmsg2(100, "use_pos=%d repos=%d\n", root_bsr->use_positioning, root_bsr->reposition);
169    root_bsr->mount_next_volume = false;
170    for (bsr=root_bsr; bsr; bsr=bsr->next) {
171       if (bsr->done || !match_volume(bsr, bsr->volume, &dev->VolHdr, 1)) {
172          continue;
173       }
174       if (found_bsr == NULL) {
175          found_bsr = bsr;
176       } else {
177          found_bsr = find_smallest_volfile(found_bsr, bsr);
178       }
179    }
180    /*
181     * If we get to this point and found no bsr, it means
182     *  that any additional bsr's must apply to the next
183     *  tape, so set a flag.
184     */
185    if (found_bsr == NULL) {
186       root_bsr->mount_next_volume = true;
187    }
188    return found_bsr;
189 }
190
191 static BSR *find_smallest_volfile(BSR *found_bsr, BSR *bsr)
192 {
193    BSR *return_bsr = found_bsr;
194    BSR_VOLFILE *vf;
195    BSR_VOLBLOCK *vb;
196    uint32_t found_bsr_sfile, bsr_sfile;
197    uint32_t found_bsr_sblock, bsr_sblock;
198
199    vf = found_bsr->volfile;
200    found_bsr_sfile = vf->sfile;
201    while ( (vf=vf->next) ) {
202       if (vf->sfile < found_bsr_sfile) {
203          found_bsr_sfile = vf->sfile;
204       }
205    }
206    vf = bsr->volfile;
207    bsr_sfile = vf->sfile;
208    while ( (vf=vf->next) ) {
209       if (vf->sfile < bsr_sfile) {
210          bsr_sfile = vf->sfile;
211       }
212    }
213    if (found_bsr_sfile > bsr_sfile) {
214       return_bsr = bsr;
215    } else if (found_bsr_sfile == bsr_sfile) {
216       /* Must check block */
217       vb = found_bsr->volblock;
218       found_bsr_sblock = vb->sblock;
219       while ( (vb=vb->next) ) {
220          if (vb->sblock < found_bsr_sblock) {
221             found_bsr_sblock = vb->sblock;
222          }
223       }
224       vb = bsr->volblock;
225       bsr_sblock = vb->sblock;
226       while ( (vb=vb->next) ) {
227          if (vb->sblock < bsr_sblock) {
228             bsr_sblock = vb->sblock;
229          }
230       }
231       if (found_bsr_sblock > bsr_sblock) {
232          return_bsr = bsr;
233       }
234    }
235
236    return return_bsr;
237 }
238
239 /*
240  * Called to tell the matcher that the end of
241  *   the current file has been reached.
242  *  The bsr argument is not used, but is included
243  *    for consistency with the other match calls.
244  *
245  * Returns: true if we should reposition
246  *        : false otherwise.
247  */
248 bool match_set_eof(BSR *bsr, DEV_RECORD *rec)
249 {
250    BSR *rbsr = rec->bsr;
251    Dmsg1(100, "match_set %d\n", rbsr != NULL);
252    if (!rbsr) {
253       return false;
254    }
255    rec->bsr = NULL;
256    rbsr->found++;
257    if (rbsr->count && rbsr->found >= rbsr->count) {
258       rbsr->done = true;
259       rbsr->root->reposition = true;
260       Dmsg2(100, "match_set_eof reposition count=%d found=%d\n",
261          rbsr->count, rbsr->found);
262       return true;
263    }
264    return false;
265 }
266
267 /*
268  * Match all the components of current record
269  *   returns  1 on match
270  *   returns  0 no match
271  *   returns -1 no additional matches possible
272  */
273 static int match_all(BSR *bsr, DEV_RECORD *rec, VOLUME_LABEL *volrec,
274                      SESSION_LABEL *sessrec, bool done)
275 {
276    if (bsr->done) {
277       goto no_match;
278    }
279    if (!match_volume(bsr, bsr->volume, volrec, 1)) {
280       goto no_match;
281    }
282    if (!match_volfile(bsr, bsr->volfile, rec, 1)) {
283       Dmsg2(100, "Fail on file. bsr=%d rec=%d\n", bsr->volfile->efile,
284          rec->File);
285       goto no_match;
286    }
287    if (!match_sesstime(bsr, bsr->sesstime, rec, 1)) {
288       Dmsg2(100, "Fail on sesstime. bsr=%d rec=%d\n",
289          bsr->sesstime->sesstime, rec->VolSessionTime);
290       goto no_match;
291    }
292
293    /* NOTE!! This test MUST come after the sesstime test */
294    if (!match_sessid(bsr, bsr->sessid, rec)) {
295       Dmsg2(100, "Fail on sessid. bsr=%d rec=%d\n",
296          bsr->sessid->sessid, rec->VolSessionId);
297       goto no_match;
298    }
299
300    /* NOTE!! This test MUST come after sesstime and sessid tests */
301    if (!match_findex(bsr, bsr->FileIndex, rec, 1)) {
302       Dmsg2(100, "Fail on findex. bsr=%d rec=%d\n",
303          bsr->FileIndex->findex2, rec->FileIndex);
304       goto no_match;
305    }
306    /*
307     * If a count was specified and we have a FileIndex, assume
308     *   it is a Bacula created bsr (or the equivalent). We
309     *   then save the bsr where the match occurred so that
310     *   after processing the record or records, we can update
311     *   the found count. I.e. rec->bsr points to the bsr that
312     *   satisfied the match.
313     */
314    if (bsr->count && bsr->FileIndex) {
315       rec->bsr = bsr;
316       return 1;                       /* this is a complete match */
317    }
318
319    /*
320     * The selections below are not used by Bacula's
321     *   restore command, and don't work because of
322     *   the rec->bsr = bsr optimization above.
323     */
324    if (!match_jobid(bsr, bsr->JobId, sessrec, 1)) {
325       goto no_match;
326
327    }
328    if (!match_job(bsr, bsr->job, sessrec, 1)) {
329       goto no_match;
330    }
331    if (!match_client(bsr, bsr->client, sessrec, 1)) {
332       goto no_match;
333    }
334    if (!match_job_type(bsr, bsr->JobType, sessrec, 1)) {
335       goto no_match;
336    }
337    if (!match_job_level(bsr, bsr->JobLevel, sessrec, 1)) {
338       goto no_match;
339    }
340    if (!match_stream(bsr, bsr->stream, rec, 1)) {
341       goto no_match;
342    }
343    return 1;
344
345 no_match:
346    if (bsr->next) {
347       return match_all(bsr->next, rec, volrec, sessrec, bsr->done && done);
348    }
349    if (bsr->done && done) {
350       return -1;
351    }
352    return 0;
353 }
354
355 static int match_volume(BSR *bsr, BSR_VOLUME *volume, VOLUME_LABEL *volrec, bool done)
356 {
357    if (!volume) {
358       return 0;                       /* Volume must match */
359    }
360    if (strcmp(volume->VolumeName, volrec->VolumeName) == 0) {
361       return 1;
362    }
363    if (volume->next) {
364       return match_volume(bsr, volume->next, volrec, 1);
365    }
366    return 0;
367 }
368
369 static int match_client(BSR *bsr, BSR_CLIENT *client, SESSION_LABEL *sessrec, bool done)
370 {
371    if (!client) {
372       return 1;                       /* no specification matches all */
373    }
374    if (fnmatch(client->ClientName, sessrec->ClientName, 0) == 0) {
375       return 1;
376    }
377    if (client->next) {
378       return match_client(bsr, client->next, sessrec, 1);
379    }
380    return 0;
381 }
382
383 static int match_job(BSR *bsr, BSR_JOB *job, SESSION_LABEL *sessrec, bool done)
384 {
385    if (!job) {
386       return 1;                       /* no specification matches all */
387    }
388    if (fnmatch(job->Job, sessrec->Job, 0) == 0) {
389       return 1;
390    }
391    if (job->next) {
392       return match_job(bsr, job->next, sessrec, 1);
393    }
394    return 0;
395 }
396
397 static int match_job_type(BSR *bsr, BSR_JOBTYPE *job_type, SESSION_LABEL *sessrec, bool done)
398 {
399    if (!job_type) {
400       return 1;                       /* no specification matches all */
401    }
402    if (job_type->JobType == sessrec->JobType) {
403       return 1;
404    }
405    if (job_type->next) {
406       return match_job_type(bsr, job_type->next, sessrec, 1);
407    }
408    return 0;
409 }
410
411 static int match_job_level(BSR *bsr, BSR_JOBLEVEL *job_level, SESSION_LABEL *sessrec, bool done)
412 {
413    if (!job_level) {
414       return 1;                       /* no specification matches all */
415    }
416    if (job_level->JobLevel == sessrec->JobLevel) {
417       return 1;
418    }
419    if (job_level->next) {
420       return match_job_level(bsr, job_level->next, sessrec, 1);
421    }
422    return 0;
423 }
424
425 static int match_jobid(BSR *bsr, BSR_JOBID *jobid, SESSION_LABEL *sessrec, bool done)
426 {
427    if (!jobid) {
428       return 1;                       /* no specification matches all */
429    }
430    if (jobid->JobId <= sessrec->JobId && jobid->JobId2 >= sessrec->JobId) {
431       return 1;
432    }
433    if (jobid->next) {
434       return match_jobid(bsr, jobid->next, sessrec, 1);
435    }
436    return 0;
437 }
438
439 static int match_volfile(BSR *bsr, BSR_VOLFILE *volfile, DEV_RECORD *rec, bool done)
440 {
441    if (!volfile) {
442       return 1;                       /* no specification matches all */
443    }
444    /* For the moment, these tests work only with tapes. */
445    if (!(rec->state & REC_ISTAPE)) {
446       return 1;                       /* All File records OK for this match */
447    }
448 // Dmsg3(100, "match_volfile: sfile=%d efile=%d recfile=%d\n",
449 //             volfile->sfile, volfile->efile, rec->File);
450    if (volfile->sfile <= rec->File && volfile->efile >= rec->File) {
451       return 1;
452    }
453    /* Once we get past last efile, we are done */
454    if (rec->File > volfile->efile) {
455       volfile->done = true;              /* set local done */
456    }
457    if (volfile->next) {
458       return match_volfile(bsr, volfile->next, rec, volfile->done && done);
459    }
460
461    /* If we are done and all prior matches are done, this bsr is finished */
462    if (volfile->done && done) {
463       bsr->done = true;
464       bsr->root->reposition = true;
465       Dmsg2(100, "bsr done from volfile rec=%d volefile=%d\n",
466          rec->File, volfile->efile);
467    }
468    return 0;
469 }
470
471 static int match_stream(BSR *bsr, BSR_STREAM *stream, DEV_RECORD *rec, bool done)
472 {
473    if (!stream) {
474       return 1;                       /* no specification matches all */
475    }
476    if (stream->stream == rec->Stream) {
477       return 1;
478    }
479    if (stream->next) {
480       return match_stream(bsr, stream->next, rec, 1);
481    }
482    return 0;
483 }
484
485 static int match_sesstime(BSR *bsr, BSR_SESSTIME *sesstime, DEV_RECORD *rec, bool done)
486 {
487    if (!sesstime) {
488       return 1;                       /* no specification matches all */
489    }
490    if (sesstime->sesstime == rec->VolSessionTime) {
491       return 1;
492    }
493    if (rec->VolSessionTime > sesstime->sesstime) {
494       sesstime->done = true;
495    }
496    if (sesstime->next) {
497       return match_sesstime(bsr, sesstime->next, rec, sesstime->done && done);
498    }
499    if (sesstime->done && done) {
500       bsr->done = true;
501       bsr->root->reposition = true;
502       Dmsg0(100, "bsr done from sesstime\n");
503    }
504    return 0;
505 }
506
507 static int match_sessid(BSR *bsr, BSR_SESSID *sessid, DEV_RECORD *rec)
508 {
509    if (!sessid) {
510       return 1;                       /* no specification matches all */
511    }
512    if (sessid->sessid <= rec->VolSessionId && sessid->sessid2 >= rec->VolSessionId) {
513       return 1;
514    }
515    if (sessid->next) {
516       return match_sessid(bsr, sessid->next, rec);
517    }
518    return 0;
519 }
520
521 /*
522  * When reading the Volume, the Volume Findex (rec->FileIndex) always
523  *   are found in sequential order. Thus we can make optimizations.
524  *
525  *  ***FIXME*** optimizations
526  * We could optimize a lot here by removing the recursion, and 
527  *   stopping the search earlier -- say when rec->FileIndex > findex->findex2
528  *   and findex->next == NULL.  Also, the current entry tests could be skipped
529  *   if findex->done is set.
530  */
531 static int match_findex(BSR *bsr, BSR_FINDEX *findex, DEV_RECORD *rec, bool done)
532 {
533    if (!findex) {
534       return 1;                       /* no specification matches all */
535    }
536    if (findex->findex <= rec->FileIndex && findex->findex2 >= rec->FileIndex) {
537       return 1;
538    }
539    if (rec->FileIndex > findex->findex2) {
540       findex->done = true;
541    }
542    if (findex->next) {
543       return match_findex(bsr, findex->next, rec, findex->done && done);
544    }
545    if (findex->done && done) {
546       bsr->done = true;
547       bsr->root->reposition = true;
548       Dmsg1(100, "bsr done from findex %d\n", rec->FileIndex);
549    }
550    return 0;
551 }