]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/match_bsr.c
Make weof() and clrerror() methods of class DEVICE.
[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-2006 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    bool no_file_seek = !dev->is_tape();
163 #ifdef FILE_SEEK
164    no_file_seek = false;
165 #endif
166
167    if (!root_bsr || !root_bsr->use_positioning ||
168        !root_bsr->reposition || no_file_seek) {
169       Dmsg2(300, "No nxt_bsr use_pos=%d repos=%d\n", root_bsr->use_positioning, root_bsr->reposition);
170       return NULL;
171    }
172    Dmsg2(300, "use_pos=%d repos=%d\n", root_bsr->use_positioning, root_bsr->reposition);
173    root_bsr->mount_next_volume = false;
174    for (bsr=root_bsr; bsr; bsr=bsr->next) {
175       if (bsr->done || !match_volume(bsr, bsr->volume, &dev->VolHdr, 1)) {
176          continue;
177       }
178       if (found_bsr == NULL) {
179          found_bsr = bsr;
180       } else {
181          found_bsr = find_smallest_volfile(found_bsr, bsr);
182       }
183    }
184    /*
185     * If we get to this point and found no bsr, it means
186     *  that any additional bsr's must apply to the next
187     *  tape, so set a flag.
188     */
189    if (found_bsr == NULL) {
190       root_bsr->mount_next_volume = true;
191    }
192    return found_bsr;
193 }
194
195 static BSR *find_smallest_volfile(BSR *found_bsr, BSR *bsr)
196 {
197    BSR *return_bsr = found_bsr;
198    BSR_VOLFILE *vf;
199    BSR_VOLBLOCK *vb;
200    uint32_t found_bsr_sfile, bsr_sfile;
201    uint32_t found_bsr_sblock, bsr_sblock;
202
203    vf = found_bsr->volfile;
204    found_bsr_sfile = vf->sfile;
205    while ( (vf=vf->next) ) {
206       if (vf->sfile < found_bsr_sfile) {
207          found_bsr_sfile = vf->sfile;
208       }
209    }
210    vf = bsr->volfile;
211    bsr_sfile = vf->sfile;
212    while ( (vf=vf->next) ) {
213       if (vf->sfile < bsr_sfile) {
214          bsr_sfile = vf->sfile;
215       }
216    }
217    if (found_bsr_sfile > bsr_sfile) {
218       return_bsr = bsr;
219    } else if (found_bsr_sfile == bsr_sfile) {
220       /* Must check block */
221       vb = found_bsr->volblock;
222       found_bsr_sblock = vb->sblock;
223       while ( (vb=vb->next) ) {
224          if (vb->sblock < found_bsr_sblock) {
225             found_bsr_sblock = vb->sblock;
226          }
227       }
228       vb = bsr->volblock;
229       bsr_sblock = vb->sblock;
230       while ( (vb=vb->next) ) {
231          if (vb->sblock < bsr_sblock) {
232             bsr_sblock = vb->sblock;
233          }
234       }
235       if (found_bsr_sblock > bsr_sblock) {
236          return_bsr = bsr;
237       }
238    }
239
240    return return_bsr;
241 }
242
243 /*
244  * Called to tell the matcher that the end of
245  *   the current file has been reached.
246  *  The bsr argument is not used, but is included
247  *    for consistency with the other match calls.
248  *
249  * Returns: true if we should reposition
250  *        : false otherwise.
251  */
252 bool match_set_eof(BSR *bsr, DEV_RECORD *rec)
253 {
254    BSR *rbsr = rec->bsr;
255    Dmsg1(300, "match_set %d\n", rbsr != NULL);
256    if (!rbsr) {
257       return false;
258    }
259    rec->bsr = NULL;
260    rbsr->found++;
261    if (rbsr->count && rbsr->found >= rbsr->count) {
262       rbsr->done = true;
263       rbsr->root->reposition = true;
264       Dmsg2(500, "match_set_eof reposition count=%d found=%d\n",
265          rbsr->count, rbsr->found);
266       return true;
267    }
268    return false;
269 }
270
271 /*
272  * Match all the components of current record
273  *   returns  1 on match
274  *   returns  0 no match
275  *   returns -1 no additional matches possible
276  */
277 static int match_all(BSR *bsr, DEV_RECORD *rec, VOLUME_LABEL *volrec,
278                      SESSION_LABEL *sessrec, bool done)
279 {
280    if (bsr->done) {
281       Dmsg0(300, "bsr->done set\n");
282       goto no_match;
283    }
284    if (!match_volume(bsr, bsr->volume, volrec, 1)) {
285       Dmsg2(300, "bsr vol=%s != rec vol=%s\n", bsr->volume, volrec);
286       goto no_match;
287    }
288    if (!match_volfile(bsr, bsr->volfile, rec, 1)) {
289       Dmsg2(300, "Fail on file. bsr=%d rec=%d\n", bsr->volfile->efile,
290          rec->File);
291       goto no_match;
292    }
293    if (!match_sesstime(bsr, bsr->sesstime, rec, 1)) {
294       Dmsg2(300, "Fail on sesstime. bsr=%d rec=%d\n",
295          bsr->sesstime->sesstime, rec->VolSessionTime);
296       goto no_match;
297    }
298
299    /* NOTE!! This test MUST come after the sesstime test */
300    if (!match_sessid(bsr, bsr->sessid, rec)) {
301       Dmsg2(300, "Fail on sessid. bsr=%d rec=%d\n",
302          bsr->sessid->sessid, rec->VolSessionId);
303       goto no_match;
304    }
305
306    /* NOTE!! This test MUST come after sesstime and sessid tests */
307    if (!match_findex(bsr, bsr->FileIndex, rec, 1)) {
308       Dmsg2(300, "Fail on findex. bsr=%d rec=%d\n",
309          bsr->FileIndex->findex2, rec->FileIndex);
310       goto no_match;
311    }
312    /*
313     * If a count was specified and we have a FileIndex, assume
314     *   it is a Bacula created bsr (or the equivalent). We
315     *   then save the bsr where the match occurred so that
316     *   after processing the record or records, we can update
317     *   the found count. I.e. rec->bsr points to the bsr that
318     *   satisfied the match.
319     */
320    if (bsr->count && bsr->FileIndex) {
321       rec->bsr = bsr;
322       return 1;                       /* this is a complete match */
323    }
324
325    /*
326     * The selections below are not used by Bacula's
327     *   restore command, and don't work because of
328     *   the rec->bsr = bsr optimization above.
329     */
330    if (!match_jobid(bsr, bsr->JobId, sessrec, 1)) {
331       Dmsg0(300, "fail on JobId\n");
332       goto no_match;
333
334    }
335    if (!match_job(bsr, bsr->job, sessrec, 1)) {
336       Dmsg0(300, "fail on Job\n");
337       goto no_match;
338    }
339    if (!match_client(bsr, bsr->client, sessrec, 1)) {
340       Dmsg0(300, "fail on Client\n");
341       goto no_match;
342    }
343    if (!match_job_type(bsr, bsr->JobType, sessrec, 1)) {
344       Dmsg0(300, "fail on Job type\n");
345       goto no_match;
346    }
347    if (!match_job_level(bsr, bsr->JobLevel, sessrec, 1)) {
348       Dmsg0(300, "fail on Job level\n");
349       goto no_match;
350    }
351    if (!match_stream(bsr, bsr->stream, rec, 1)) {
352       Dmsg0(300, "fail on stream\n");
353       goto no_match;
354    }
355    return 1;
356
357 no_match:
358    if (bsr->next) {
359       return match_all(bsr->next, rec, volrec, sessrec, bsr->done && done);
360    }
361    if (bsr->done && done) {
362       return -1;
363    }
364    return 0;
365 }
366
367 static int match_volume(BSR *bsr, BSR_VOLUME *volume, VOLUME_LABEL *volrec, bool done)
368 {
369    if (!volume) {
370       return 0;                       /* Volume must match */
371    }
372    if (strcmp(volume->VolumeName, volrec->VolumeName) == 0) {
373       return 1;
374    }
375    if (volume->next) {
376       return match_volume(bsr, volume->next, volrec, 1);
377    }
378    return 0;
379 }
380
381 static int match_client(BSR *bsr, BSR_CLIENT *client, SESSION_LABEL *sessrec, bool done)
382 {
383    if (!client) {
384       return 1;                       /* no specification matches all */
385    }
386    if (fnmatch(client->ClientName, sessrec->ClientName, 0) == 0) {
387       return 1;
388    }
389    if (client->next) {
390       return match_client(bsr, client->next, sessrec, 1);
391    }
392    return 0;
393 }
394
395 static int match_job(BSR *bsr, BSR_JOB *job, SESSION_LABEL *sessrec, bool done)
396 {
397    if (!job) {
398       return 1;                       /* no specification matches all */
399    }
400    if (fnmatch(job->Job, sessrec->Job, 0) == 0) {
401       return 1;
402    }
403    if (job->next) {
404       return match_job(bsr, job->next, sessrec, 1);
405    }
406    return 0;
407 }
408
409 static int match_job_type(BSR *bsr, BSR_JOBTYPE *job_type, SESSION_LABEL *sessrec, bool done)
410 {
411    if (!job_type) {
412       return 1;                       /* no specification matches all */
413    }
414    if (job_type->JobType == sessrec->JobType) {
415       return 1;
416    }
417    if (job_type->next) {
418       return match_job_type(bsr, job_type->next, sessrec, 1);
419    }
420    return 0;
421 }
422
423 static int match_job_level(BSR *bsr, BSR_JOBLEVEL *job_level, SESSION_LABEL *sessrec, bool done)
424 {
425    if (!job_level) {
426       return 1;                       /* no specification matches all */
427    }
428    if (job_level->JobLevel == sessrec->JobLevel) {
429       return 1;
430    }
431    if (job_level->next) {
432       return match_job_level(bsr, job_level->next, sessrec, 1);
433    }
434    return 0;
435 }
436
437 static int match_jobid(BSR *bsr, BSR_JOBID *jobid, SESSION_LABEL *sessrec, bool done)
438 {
439    if (!jobid) {
440       return 1;                       /* no specification matches all */
441    }
442    if (jobid->JobId <= sessrec->JobId && jobid->JobId2 >= sessrec->JobId) {
443       return 1;
444    }
445    if (jobid->next) {
446       return match_jobid(bsr, jobid->next, sessrec, 1);
447    }
448    return 0;
449 }
450
451 static int match_volfile(BSR *bsr, BSR_VOLFILE *volfile, DEV_RECORD *rec, bool done)
452 {
453    if (!volfile) {
454       return 1;                       /* no specification matches all */
455    }
456    /* For the moment, these tests work only with tapes. */
457    if (!(rec->state & REC_ISTAPE)) {
458       return 1;                       /* All File records OK for this match */
459    }
460 // Dmsg3(300, "match_volfile: sfile=%d efile=%d recfile=%d\n",
461 //             volfile->sfile, volfile->efile, rec->File);
462    if (volfile->sfile <= rec->File && volfile->efile >= rec->File) {
463       return 1;
464    }
465    /* Once we get past last efile, we are done */
466    if (rec->File > volfile->efile) {
467       volfile->done = true;              /* set local done */
468    }
469    if (volfile->next) {
470       return match_volfile(bsr, volfile->next, rec, volfile->done && done);
471    }
472
473    /* If we are done and all prior matches are done, this bsr is finished */
474    if (volfile->done && done) {
475       bsr->done = true;
476       bsr->root->reposition = true;
477       Dmsg2(300, "bsr done from volfile rec=%d volefile=%d\n",
478          rec->File, volfile->efile);
479    }
480    return 0;
481 }
482
483 static int match_stream(BSR *bsr, BSR_STREAM *stream, DEV_RECORD *rec, bool done)
484 {
485    if (!stream) {
486       return 1;                       /* no specification matches all */
487    }
488    if (stream->stream == rec->Stream) {
489       return 1;
490    }
491    if (stream->next) {
492       return match_stream(bsr, stream->next, rec, 1);
493    }
494    return 0;
495 }
496
497 static int match_sesstime(BSR *bsr, BSR_SESSTIME *sesstime, DEV_RECORD *rec, bool done)
498 {
499    if (!sesstime) {
500       return 1;                       /* no specification matches all */
501    }
502    if (sesstime->sesstime == rec->VolSessionTime) {
503       return 1;
504    }
505    if (rec->VolSessionTime > sesstime->sesstime) {
506       sesstime->done = true;
507    }
508    if (sesstime->next) {
509       return match_sesstime(bsr, sesstime->next, rec, sesstime->done && done);
510    }
511    if (sesstime->done && done) {
512       bsr->done = true;
513       bsr->root->reposition = true;
514       Dmsg0(300, "bsr done from sesstime\n");
515    }
516    return 0;
517 }
518
519 static int match_sessid(BSR *bsr, BSR_SESSID *sessid, DEV_RECORD *rec)
520 {
521    if (!sessid) {
522       return 1;                       /* no specification matches all */
523    }
524    if (sessid->sessid <= rec->VolSessionId && sessid->sessid2 >= rec->VolSessionId) {
525       return 1;
526    }
527    if (sessid->next) {
528       return match_sessid(bsr, sessid->next, rec);
529    }
530    return 0;
531 }
532
533 /*
534  * When reading the Volume, the Volume Findex (rec->FileIndex) always
535  *   are found in sequential order. Thus we can make optimizations.
536  *
537  *  ***FIXME*** optimizations
538  * We could optimize a lot here by removing the recursion, and 
539  *   stopping the search earlier -- say when rec->FileIndex > findex->findex2
540  *   and findex->next == NULL.  Also, the current entry tests could be skipped
541  *   if findex->done is set.
542  */
543 static int match_findex(BSR *bsr, BSR_FINDEX *findex, DEV_RECORD *rec, bool done)
544 {
545    if (!findex) {
546       return 1;                       /* no specification matches all */
547    }
548    if (findex->findex <= rec->FileIndex && findex->findex2 >= rec->FileIndex) {
549       return 1;
550    }
551    if (rec->FileIndex > findex->findex2) {
552       findex->done = true;
553    }
554    if (findex->next) {
555       return match_findex(bsr, findex->next, rec, findex->done && done);
556    }
557    if (findex->done && done) {
558       bsr->done = true;
559       bsr->root->reposition = true;
560       Dmsg1(300, "bsr done from findex %d\n", rec->FileIndex);
561    }
562    return 0;
563 }