]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/match_bsr.c
Back out changes apparently crashing system
[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 after the signature record so that
245  *   we can see if the current bsr has been
246  *   fully processed (i.e. is done).
247  *  The bsr argument is not used, but is included
248  *    for consistency with the other match calls.
249  *
250  * Returns: true if we should reposition
251  *        : false otherwise.
252  */
253 bool is_this_bsr_done(BSR *bsr, DEV_RECORD *rec)
254 {
255    BSR *rbsr = rec->bsr;
256    Dmsg1(300, "match_set %d\n", rbsr != NULL);
257    if (!rbsr) {
258       return false;
259    }
260    rec->bsr = NULL;
261    rbsr->found++;
262    if (rbsr->count && rbsr->found >= rbsr->count) {
263       rbsr->done = true;
264       rbsr->root->reposition = true;
265       Dmsg2(500, "is_end_this_bsr set reposition=1 count=%d found=%d\n",
266          rbsr->count, rbsr->found);
267       return true;
268    }
269    return false;
270 }
271
272 /*
273  * Match all the components of current record
274  *   returns  1 on match
275  *   returns  0 no match
276  *   returns -1 no additional matches possible
277  */
278 static int match_all(BSR *bsr, DEV_RECORD *rec, VOLUME_LABEL *volrec,
279                      SESSION_LABEL *sessrec, bool done)
280 {
281    if (bsr->done) {
282       Dmsg0(300, "bsr->done set\n");
283       goto no_match;
284    }
285    if (!match_volume(bsr, bsr->volume, volrec, 1)) {
286       Dmsg2(300, "bsr fail vol=%s != rec vol=%s\n", bsr->volume->VolumeName,
287             volrec->VolumeName);
288       goto no_match;
289    }
290    if (!match_volfile(bsr, bsr->volfile, rec, 1)) {
291       Dmsg2(300, "Fail on file. bsr=%d rec=%d\n", bsr->volfile->efile,
292          rec->File);
293       goto no_match;
294    }
295    if (!match_sesstime(bsr, bsr->sesstime, rec, 1)) {
296       Dmsg2(300, "Fail on sesstime. bsr=%d rec=%d\n",
297          bsr->sesstime->sesstime, rec->VolSessionTime);
298       goto no_match;
299    }
300
301    /* NOTE!! This test MUST come after the sesstime test */
302    if (!match_sessid(bsr, bsr->sessid, rec)) {
303       Dmsg2(300, "Fail on sessid. bsr=%d rec=%d\n",
304          bsr->sessid->sessid, rec->VolSessionId);
305       goto no_match;
306    }
307
308    /* NOTE!! This test MUST come after sesstime and sessid tests */
309    if (!match_findex(bsr, bsr->FileIndex, rec, 1)) {
310       Dmsg2(300, "Fail on findex. bsr=%d rec=%d\n",
311          bsr->FileIndex->findex2, rec->FileIndex);
312       goto no_match;
313    }
314    /*
315     * If a count was specified and we have a FileIndex, assume
316     *   it is a Bacula created bsr (or the equivalent). We
317     *   then save the bsr where the match occurred so that
318     *   after processing the record or records, we can update
319     *   the found count. I.e. rec->bsr points to the bsr that
320     *   satisfied the match.
321     */
322    if (bsr->count && bsr->FileIndex) {
323       rec->bsr = bsr;
324       return 1;                       /* this is a complete match */
325    }
326
327    /*
328     * The selections below are not used by Bacula's
329     *   restore command, and don't work because of
330     *   the rec->bsr = bsr optimization above.
331     */
332    if (!match_jobid(bsr, bsr->JobId, sessrec, 1)) {
333       Dmsg0(300, "fail on JobId\n");
334       goto no_match;
335
336    }
337    if (!match_job(bsr, bsr->job, sessrec, 1)) {
338       Dmsg0(300, "fail on Job\n");
339       goto no_match;
340    }
341    if (!match_client(bsr, bsr->client, sessrec, 1)) {
342       Dmsg0(300, "fail on Client\n");
343       goto no_match;
344    }
345    if (!match_job_type(bsr, bsr->JobType, sessrec, 1)) {
346       Dmsg0(300, "fail on Job type\n");
347       goto no_match;
348    }
349    if (!match_job_level(bsr, bsr->JobLevel, sessrec, 1)) {
350       Dmsg0(300, "fail on Job level\n");
351       goto no_match;
352    }
353    if (!match_stream(bsr, bsr->stream, rec, 1)) {
354       Dmsg0(300, "fail on stream\n");
355       goto no_match;
356    }
357    return 1;
358
359 no_match:
360    if (bsr->next) {
361       return match_all(bsr->next, rec, volrec, sessrec, bsr->done && done);
362    }
363    if (bsr->done && done) {
364       return -1;
365    }
366    return 0;
367 }
368
369 static int match_volume(BSR *bsr, BSR_VOLUME *volume, VOLUME_LABEL *volrec, bool done)
370 {
371    if (!volume) {
372       return 0;                       /* Volume must match */
373    }
374    if (strcmp(volume->VolumeName, volrec->VolumeName) == 0) {
375       return 1;
376    }
377    if (volume->next) {
378       return match_volume(bsr, volume->next, volrec, 1);
379    }
380    return 0;
381 }
382
383 static int match_client(BSR *bsr, BSR_CLIENT *client, SESSION_LABEL *sessrec, bool done)
384 {
385    if (!client) {
386       return 1;                       /* no specification matches all */
387    }
388    if (fnmatch(client->ClientName, sessrec->ClientName, 0) == 0) {
389       return 1;
390    }
391    if (client->next) {
392       return match_client(bsr, client->next, sessrec, 1);
393    }
394    return 0;
395 }
396
397 static int match_job(BSR *bsr, BSR_JOB *job, SESSION_LABEL *sessrec, bool done)
398 {
399    if (!job) {
400       return 1;                       /* no specification matches all */
401    }
402    if (fnmatch(job->Job, sessrec->Job, 0) == 0) {
403       return 1;
404    }
405    if (job->next) {
406       return match_job(bsr, job->next, sessrec, 1);
407    }
408    return 0;
409 }
410
411 static int match_job_type(BSR *bsr, BSR_JOBTYPE *job_type, SESSION_LABEL *sessrec, bool done)
412 {
413    if (!job_type) {
414       return 1;                       /* no specification matches all */
415    }
416    if (job_type->JobType == sessrec->JobType) {
417       return 1;
418    }
419    if (job_type->next) {
420       return match_job_type(bsr, job_type->next, sessrec, 1);
421    }
422    return 0;
423 }
424
425 static int match_job_level(BSR *bsr, BSR_JOBLEVEL *job_level, SESSION_LABEL *sessrec, bool done)
426 {
427    if (!job_level) {
428       return 1;                       /* no specification matches all */
429    }
430    if (job_level->JobLevel == sessrec->JobLevel) {
431       return 1;
432    }
433    if (job_level->next) {
434       return match_job_level(bsr, job_level->next, sessrec, 1);
435    }
436    return 0;
437 }
438
439 static int match_jobid(BSR *bsr, BSR_JOBID *jobid, SESSION_LABEL *sessrec, bool done)
440 {
441    if (!jobid) {
442       return 1;                       /* no specification matches all */
443    }
444    if (jobid->JobId <= sessrec->JobId && jobid->JobId2 >= sessrec->JobId) {
445       return 1;
446    }
447    if (jobid->next) {
448       return match_jobid(bsr, jobid->next, sessrec, 1);
449    }
450    return 0;
451 }
452
453 static int match_volfile(BSR *bsr, BSR_VOLFILE *volfile, DEV_RECORD *rec, bool done)
454 {
455    if (!volfile) {
456       return 1;                       /* no specification matches all */
457    }
458    /* For the moment, these tests work only with tapes. */
459    if (!(rec->state & REC_ISTAPE)) {
460       return 1;                       /* All File records OK for this match */
461    }
462 // Dmsg3(300, "match_volfile: sfile=%d efile=%d recfile=%d\n",
463 //             volfile->sfile, volfile->efile, rec->File);
464    if (volfile->sfile <= rec->File && volfile->efile >= rec->File) {
465       return 1;
466    }
467    /* Once we get past last efile, we are done */
468    if (rec->File > volfile->efile) {
469       volfile->done = true;              /* set local done */
470    }
471    if (volfile->next) {
472       return match_volfile(bsr, volfile->next, rec, volfile->done && done);
473    }
474
475    /* If we are done and all prior matches are done, this bsr is finished */
476    if (volfile->done && done) {
477       bsr->done = true;
478       bsr->root->reposition = true;
479       Dmsg2(300, "bsr done from volfile rec=%d volefile=%d\n",
480          rec->File, volfile->efile);
481    }
482    return 0;
483 }
484
485 static int match_stream(BSR *bsr, BSR_STREAM *stream, DEV_RECORD *rec, bool done)
486 {
487    if (!stream) {
488       return 1;                       /* no specification matches all */
489    }
490    if (stream->stream == rec->Stream) {
491       return 1;
492    }
493    if (stream->next) {
494       return match_stream(bsr, stream->next, rec, 1);
495    }
496    return 0;
497 }
498
499 static int match_sesstime(BSR *bsr, BSR_SESSTIME *sesstime, DEV_RECORD *rec, bool done)
500 {
501    if (!sesstime) {
502       return 1;                       /* no specification matches all */
503    }
504    if (sesstime->sesstime == rec->VolSessionTime) {
505       return 1;
506    }
507    if (rec->VolSessionTime > sesstime->sesstime) {
508       sesstime->done = true;
509    }
510    if (sesstime->next) {
511       return match_sesstime(bsr, sesstime->next, rec, sesstime->done && done);
512    }
513    if (sesstime->done && done) {
514       bsr->done = true;
515       bsr->root->reposition = true;
516       Dmsg0(300, "bsr done from sesstime\n");
517    }
518    return 0;
519 }
520
521 static int match_sessid(BSR *bsr, BSR_SESSID *sessid, DEV_RECORD *rec)
522 {
523    if (!sessid) {
524       return 1;                       /* no specification matches all */
525    }
526    if (sessid->sessid <= rec->VolSessionId && sessid->sessid2 >= rec->VolSessionId) {
527       return 1;
528    }
529    if (sessid->next) {
530       return match_sessid(bsr, sessid->next, rec);
531    }
532    return 0;
533 }
534
535 /*
536  * When reading the Volume, the Volume Findex (rec->FileIndex) always
537  *   are found in sequential order. Thus we can make optimizations.
538  *
539  *  ***FIXME*** optimizations
540  * We could optimize a lot here by removing the recursion, and 
541  *   stopping the search earlier -- say when rec->FileIndex > findex->findex2
542  *   and findex->next == NULL.  Also, the current entry tests could be skipped
543  *   if findex->done is set.
544  */
545 static int match_findex(BSR *bsr, BSR_FINDEX *findex, DEV_RECORD *rec, bool done)
546 {
547    if (!findex) {
548       return 1;                       /* no specification matches all */
549    }
550    if (findex->findex <= rec->FileIndex && findex->findex2 >= rec->FileIndex) {
551       return 1;
552    }
553    if (rec->FileIndex > findex->findex2) {
554       findex->done = true;
555    }
556    if (findex->next) {
557       return match_findex(bsr, findex->next, rec, findex->done && done);
558    }
559    if (findex->done && done) {
560       bsr->done = true;
561       bsr->root->reposition = true;
562       Dmsg1(300, "bsr done from findex %d\n", rec->FileIndex);
563    }
564    return 0;
565 }