]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/match_bsr.c
ebl remove buggy match_volblock
[bacula/bacula] / bacula / src / stored / match_bsr.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2002-2008 Free Software Foundation Europe e.V.
5
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
11    in the file LICENSE.
12
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.
17
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
21    02110-1301, USA.
22
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.
27 */
28 /*
29  *   Match Bootstrap Records (used for restores) against
30  *     Volume Records
31  *
32  *     Kern Sibbald, June MMII
33  *
34  *   Version $Id$
35  */
36
37 /*
38  * ***FIXME***
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).
47  *
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.
51  *
52  *   To be done ...
53  */
54
55 #include "bacula.h"
56 #include "stored.h"
57 #ifdef HAVE_FNMATCH
58 #include <fnmatch.h>
59 #else
60 #include "lib/fnmatch.h"
61 #endif
62
63 const int dbglevel = 500;
64
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_volblock(BSR *bsr, BSR_VOLBLOCK *volblock, DEV_RECORD *rec, bool done);
77 static int match_stream(BSR *bsr, BSR_STREAM *stream, DEV_RECORD *rec, bool done);
78 static int match_all(BSR *bsr, DEV_RECORD *rec, VOLUME_LABEL *volrec, SESSION_LABEL *sessrec, bool done, JCR *jcr);
79 static int match_block_sesstime(BSR *bsr, BSR_SESSTIME *sesstime, DEV_BLOCK *block);
80 static int match_block_sessid(BSR *bsr, BSR_SESSID *sessid, DEV_BLOCK *block);
81 static BSR *find_smallest_volfile(BSR *fbsr, BSR *bsr);
82
83
84 /*********************************************************************
85  *
86  *  If possible, position the archive device (tape) to read the
87  *  next block.
88  */
89 void position_bsr_block(BSR *bsr, DEV_BLOCK *block)
90 {
91    /* To be implemented */
92 }
93
94 /*********************************************************************
95  *
96  *  Do fast block rejection based on bootstrap records.
97  *    use_fast_rejection will be set if we have VolSessionId and VolSessTime
98  *    in each record. When BlockVer is >= 2, we have those in the block header
99  *    so can do fast rejection.
100  *
101  *   returns:  1 if block may contain valid records
102  *             0 if block may be skipped (i.e. it contains no records of
103  *                  that can match the bsr).
104  *
105  */
106 int match_bsr_block(BSR *bsr, DEV_BLOCK *block)
107 {
108    if (!bsr || !bsr->use_fast_rejection || (block->BlockVer < 2)) {
109       return 1;                       /* cannot fast reject */
110    }
111
112    for ( ; bsr; bsr=bsr->next) {
113       if (!match_block_sesstime(bsr, bsr->sesstime, block)) {
114          continue;
115       }
116       if (!match_block_sessid(bsr, bsr->sessid, block)) {
117          continue;
118       }
119       return 1;
120    }
121    return 0;
122 }
123
124 static int match_block_sesstime(BSR *bsr, BSR_SESSTIME *sesstime, DEV_BLOCK *block)
125 {
126    if (!sesstime) {
127       return 1;                       /* no specification matches all */
128    }
129    if (sesstime->sesstime == block->VolSessionTime) {
130       return 1;
131    }
132    if (sesstime->next) {
133       return match_block_sesstime(bsr, sesstime->next, block);
134    }
135    return 0;
136 }
137
138 static int match_block_sessid(BSR *bsr, BSR_SESSID *sessid, DEV_BLOCK *block)
139 {
140    if (!sessid) {
141       return 1;                       /* no specification matches all */
142    }
143    if (sessid->sessid <= block->VolSessionId && sessid->sessid2 >= block->VolSessionId) {
144       return 1;
145    }
146    if (sessid->next) {
147       return match_block_sessid(bsr, sessid->next, block);
148    }
149    return 0;
150 }
151
152 static int match_fileregex(BSR *bsr, DEV_RECORD *rec, JCR *jcr)
153 {
154    if (bsr->fileregex_re == NULL)
155       return 1;
156
157    if (bsr->attr == NULL)
158       bsr->attr = new_attr(jcr);
159
160    /* The code breaks if the first record associated with a file is
161     * not of this type
162     */
163    if (rec->Stream == STREAM_UNIX_ATTRIBUTES ||
164        rec->Stream == STREAM_UNIX_ATTRIBUTES_EX) {
165       bsr->skip_file = false;
166       if (unpack_attributes_record(jcr, rec->Stream, rec->data, bsr->attr)) {
167          if (regexec(bsr->fileregex_re, bsr->attr->fname, 0, NULL, 0) == 0) {
168             Dmsg2(dbglevel, "Matched pattern, fname=%s FI=%d\n",
169                   bsr->attr->fname, rec->FileIndex);
170          } else {
171             Dmsg2(dbglevel, "Didn't match, skipping fname=%s FI=%d\n",
172                   bsr->attr->fname, rec->FileIndex);
173             bsr->skip_file = true;
174          }
175       }
176    }
177    return 1;
178 }
179
180 /*********************************************************************
181  *
182  *      Match Bootstrap records
183  *        returns  1 on match
184  *        returns  0 no match and reposition is set if we should
185  *                      reposition the tape
186  *       returns -1 no additional matches possible
187  */
188 int match_bsr(BSR *bsr, DEV_RECORD *rec, VOLUME_LABEL *volrec, SESSION_LABEL *sessrec, JCR *jcr)
189 {
190    int stat;
191
192    /*
193     * The bsr->reposition flag is set any time a bsr is done.
194     *   In this case, we can probably reposition the
195     *   tape to the next available bsr position.
196     */
197    if (bsr) {
198       bsr->reposition = false;
199       stat = match_all(bsr, rec, volrec, sessrec, true, jcr);
200       /*
201        * Note, bsr->reposition is set by match_all when
202        *  a bsr is done. We turn it off if a match was
203        *  found or if we cannot use positioning
204        */
205       if (stat != 0 || !bsr->use_positioning) {
206          bsr->reposition = false;
207       }
208    } else {
209       stat = 1;                       /* no bsr => match all */
210    }
211    return stat;
212 }
213
214 /*
215  * Find the next bsr that applies to the current tape.
216  *   It is the one with the smallest VolFile position.
217  */
218 BSR *find_next_bsr(BSR *root_bsr, DEVICE *dev)
219 {
220    BSR *bsr;
221    BSR *found_bsr = NULL;
222
223    /* Do tape/disk seeking only if CAP_POSITIONBLOCKS is on */
224    if (!root_bsr) {
225       Dmsg0(dbglevel, "NULL root bsr pointer passed to find_next_bsr.\n");
226       return NULL;
227    }
228    if (!root_bsr->use_positioning ||
229        !root_bsr->reposition || !dev->has_cap(CAP_POSITIONBLOCKS)) {
230       Dmsg2(dbglevel, "No nxt_bsr use_pos=%d repos=%d\n", root_bsr->use_positioning, root_bsr->reposition);
231       return NULL;
232    }
233    Dmsg2(dbglevel, "use_pos=%d repos=%d\n", root_bsr->use_positioning, root_bsr->reposition);
234    root_bsr->mount_next_volume = false;
235    /* Walk through all bsrs to find the next one to use => smallest file,block */
236    for (bsr=root_bsr; bsr; bsr=bsr->next) {
237       if (bsr->done || !match_volume(bsr, bsr->volume, &dev->VolHdr, 1)) {
238          continue;
239       }
240       if (found_bsr == NULL) {
241          found_bsr = bsr;
242       } else {
243          found_bsr = find_smallest_volfile(found_bsr, bsr);
244       }
245    }
246    /*
247     * If we get to this point and found no bsr, it means
248     *  that any additional bsr's must apply to the next
249     *  tape, so set a flag.
250     */
251    if (found_bsr == NULL) {
252       root_bsr->mount_next_volume = true;
253    }
254    return found_bsr;
255 }
256
257 /*
258  * ***FIXME***
259  * This routine needs to be fixed to only look at items that
260  *   are not marked as done.  Otherwise, it can find a bsr
261  *   that has already been consumed, and this will cause the
262  *   bsr to be used, thus we may seek back and re-read the
263  *   same records, causing an error.  This deficiency must
264  *   be fixed.  For the moment, it has been kludged in 
265  *   read_record.c to avoid seeking back if find_next_bsr
266  *   returns a bsr pointing to a smaller address (file/block).
267  */
268 static BSR *find_smallest_volfile(BSR *found_bsr, BSR *bsr)
269 {
270    BSR *return_bsr = found_bsr;
271    BSR_VOLFILE *vf;
272    BSR_VOLBLOCK *vb;
273    uint32_t found_bsr_sfile, bsr_sfile;
274    uint32_t found_bsr_sblock, bsr_sblock;
275
276    /* Find the smallest file in the found_bsr */
277    vf = found_bsr->volfile;
278    found_bsr_sfile = vf->sfile;
279    while ( (vf=vf->next) ) {
280       if (vf->sfile < found_bsr_sfile) {
281          found_bsr_sfile = vf->sfile;
282       }
283    }
284
285    /* Find the smallest file in the bsr */
286    vf = bsr->volfile;
287    bsr_sfile = vf->sfile;
288    while ( (vf=vf->next) ) {
289       if (vf->sfile < bsr_sfile) {
290          bsr_sfile = vf->sfile;
291       }
292    }
293     
294    /* if the bsr file is less than the found_bsr file, return bsr */
295    if (found_bsr_sfile > bsr_sfile) {
296       return_bsr = bsr;
297    } else if (found_bsr_sfile == bsr_sfile) {
298       /* Files are equal */
299       /* find smallest block in found_bsr */
300       vb = found_bsr->volblock;
301       found_bsr_sblock = vb->sblock;
302       while ( (vb=vb->next) ) {
303          if (vb->sblock < found_bsr_sblock) {
304             found_bsr_sblock = vb->sblock;
305          }
306       }
307       /* Find smallest block in bsr */
308       vb = bsr->volblock;
309       bsr_sblock = vb->sblock;
310       while ( (vb=vb->next) ) {
311          if (vb->sblock < bsr_sblock) {
312             bsr_sblock = vb->sblock;
313          }
314       }
315       /* Compare and return the smallest */
316       if (found_bsr_sblock > bsr_sblock) {
317          return_bsr = bsr;
318       }
319    }
320    return return_bsr;
321 }
322
323 /*
324  * Called after the signature record so that
325  *   we can see if the current bsr has been
326  *   fully processed (i.e. is done).
327  *  The bsr argument is not used, but is included
328  *    for consistency with the other match calls.
329  *
330  * Returns: true if we should reposition
331  *        : false otherwise.
332  */
333 bool is_this_bsr_done(BSR *bsr, DEV_RECORD *rec)
334 {
335    BSR *rbsr = rec->bsr;
336    Dmsg1(dbglevel, "match_set %d\n", rbsr != NULL);
337    if (!rbsr) {
338       return false;
339    }
340    rec->bsr = NULL;
341    rbsr->found++;
342    if (rbsr->count && rbsr->found >= rbsr->count) {
343       rbsr->done = true;
344       rbsr->root->reposition = true;
345       Dmsg2(dbglevel, "is_end_this_bsr set reposition=1 count=%d found=%d\n",
346          rbsr->count, rbsr->found);
347       return true;
348    }
349    Dmsg2(dbglevel, "is_end_this_bsr not done count=%d found=%d\n",
350         rbsr->count, rbsr->found);
351    return false;
352 }
353
354 /*
355  * Match all the components of current record
356  *   returns  1 on match
357  *   returns  0 no match
358  *   returns -1 no additional matches possible
359  */
360 static int match_all(BSR *bsr, DEV_RECORD *rec, VOLUME_LABEL *volrec,
361                      SESSION_LABEL *sessrec, bool done, JCR *jcr)
362 {
363    Dmsg0(050, "Enter match_all\n");
364    if (bsr->done) {
365 //    Dmsg0(dbglevel, "bsr->done set\n");
366       goto no_match;
367    }
368    if (!match_volume(bsr, bsr->volume, volrec, 1)) {
369       Dmsg2(dbglevel, "bsr fail bsr_vol=%s != rec read_vol=%s\n", bsr->volume->VolumeName,
370             volrec->VolumeName);
371       goto no_match;
372    }
373    Dmsg2(dbglevel, "OK bsr match bsr_vol=%s read_vol=%s\n", bsr->volume->VolumeName,
374             volrec->VolumeName);
375
376    if (!match_volfile(bsr, bsr->volfile, rec, 1)) {
377       Dmsg3(dbglevel, "Fail on file=%u. bsr=%u,%u\n", 
378          rec->File, bsr->volfile->sfile, bsr->volfile->efile);
379       goto no_match;
380    }
381    Dmsg3(dbglevel, "OK bsr file=%u. bsr=%u,%u\n", 
382          rec->File, bsr->volfile->sfile, bsr->volfile->efile);
383
384    if (!match_volblock(bsr, bsr->volblock, rec, 1)) {
385       Dmsg3(dbglevel, "Fail on Block=%u. bsr=%u,%u\n", 
386          rec->Block, bsr->volblock->sblock, bsr->volblock->eblock);
387       goto no_match;
388    }
389    Dmsg3(dbglevel, "OK bsr Block=%u. bsr=%u,%u\n", 
390       rec->Block, bsr->volblock->sblock, bsr->volblock->eblock);
391
392    if (!match_sesstime(bsr, bsr->sesstime, rec, 1)) {
393       Dmsg2(dbglevel, "Fail on sesstime. bsr=%u rec=%u\n",
394          bsr->sesstime->sesstime, rec->VolSessionTime);
395       goto no_match;
396    }
397
398    /* NOTE!! This test MUST come after the sesstime test */
399    if (!match_sessid(bsr, bsr->sessid, rec)) {
400       Dmsg2(dbglevel, "Fail on sessid. bsr=%u rec=%u\n",
401          bsr->sessid->sessid, rec->VolSessionId);
402       goto no_match;
403    }
404
405    /* NOTE!! This test MUST come after sesstime and sessid tests */
406    if (!match_findex(bsr, bsr->FileIndex, rec, 1)) {
407       Dmsg3(dbglevel, "Fail on findex=%d. bsr=%d,%d\n",
408          rec->FileIndex, bsr->FileIndex->findex, bsr->FileIndex->findex2);
409       goto no_match;
410    }
411    Dmsg3(dbglevel, "match on findex=%d. bsr=%d,%d\n",
412          rec->FileIndex, bsr->FileIndex->findex, bsr->FileIndex->findex2);
413
414    if (!match_fileregex(bsr, rec, jcr)) {
415      Dmsg1(dbglevel, "Fail on fileregex='%s'\n", bsr->fileregex);
416      goto no_match;
417    }
418
419    /* This flag is set by match_fileregex (and perhaps other tests) */
420    if (bsr->skip_file) {
421       Dmsg1(dbglevel, "Skipping findex=%d\n", rec->FileIndex);
422       goto no_match;
423    }
424
425    /*
426     * If a count was specified and we have a FileIndex, assume
427     *   it is a Bacula created bsr (or the equivalent). We
428     *   then save the bsr where the match occurred so that
429     *   after processing the record or records, we can update
430     *   the found count. I.e. rec->bsr points to the bsr that
431     *   satisfied the match.
432     */
433    if (bsr->count && bsr->FileIndex) {
434       rec->bsr = bsr;
435       Dmsg0(050, "Leave match_all 1\n");
436       return 1;                       /* this is a complete match */
437    }
438
439    /*
440     * The selections below are not used by Bacula's
441     *   restore command, and don't work because of
442     *   the rec->bsr = bsr optimization above.
443     */
444    if (!match_jobid(bsr, bsr->JobId, sessrec, 1)) {
445       Dmsg0(dbglevel, "fail on JobId\n");
446       goto no_match;
447
448    }
449    if (!match_job(bsr, bsr->job, sessrec, 1)) {
450       Dmsg0(dbglevel, "fail on Job\n");
451       goto no_match;
452    }
453    if (!match_client(bsr, bsr->client, sessrec, 1)) {
454       Dmsg0(dbglevel, "fail on Client\n");
455       goto no_match;
456    }
457    if (!match_job_type(bsr, bsr->JobType, sessrec, 1)) {
458       Dmsg0(dbglevel, "fail on Job type\n");
459       goto no_match;
460    }
461    if (!match_job_level(bsr, bsr->JobLevel, sessrec, 1)) {
462       Dmsg0(dbglevel, "fail on Job level\n");
463       goto no_match;
464    }
465    if (!match_stream(bsr, bsr->stream, rec, 1)) {
466       Dmsg0(dbglevel, "fail on stream\n");
467       goto no_match;
468    }
469    Dmsg0(050, "Leave match_all 1\n");
470    return 1;
471
472 no_match:
473    if (bsr->next) {
474       return match_all(bsr->next, rec, volrec, sessrec, bsr->done && done, jcr);
475    }
476    if (bsr->done && done) {
477       Dmsg0(050, "Leave match all -1\n");
478       return -1;
479    }
480    Dmsg0(050, "Leave match all 0\n");
481    return 0;
482 }
483
484 static int match_volume(BSR *bsr, BSR_VOLUME *volume, VOLUME_LABEL *volrec, bool done)
485 {
486    if (!volume) {
487       return 0;                       /* Volume must match */
488    }
489    if (strcmp(volume->VolumeName, volrec->VolumeName) == 0) {
490       Dmsg1(050, "match_volume=%s\n", volrec->VolumeName);
491       return 1;
492    }
493    if (volume->next) {
494       return match_volume(bsr, volume->next, volrec, 1);
495    }
496    return 0;
497 }
498
499 static int match_client(BSR *bsr, BSR_CLIENT *client, SESSION_LABEL *sessrec, bool done)
500 {
501    if (!client) {
502       return 1;                       /* no specification matches all */
503    }
504    if (strcmp(client->ClientName, sessrec->ClientName) == 0) {
505       return 1;
506    }
507    if (client->next) {
508       return match_client(bsr, client->next, sessrec, 1);
509    }
510    return 0;
511 }
512
513 static int match_job(BSR *bsr, BSR_JOB *job, SESSION_LABEL *sessrec, bool done)
514 {
515    if (!job) {
516       return 1;                       /* no specification matches all */
517    }
518    if (strcmp(job->Job, sessrec->Job) == 0) {
519       return 1;
520    }
521    if (job->next) {
522       return match_job(bsr, job->next, sessrec, 1);
523    }
524    return 0;
525 }
526
527 static int match_job_type(BSR *bsr, BSR_JOBTYPE *job_type, SESSION_LABEL *sessrec, bool done)
528 {
529    if (!job_type) {
530       return 1;                       /* no specification matches all */
531    }
532    if (job_type->JobType == sessrec->JobType) {
533       return 1;
534    }
535    if (job_type->next) {
536       return match_job_type(bsr, job_type->next, sessrec, 1);
537    }
538    return 0;
539 }
540
541 static int match_job_level(BSR *bsr, BSR_JOBLEVEL *job_level, SESSION_LABEL *sessrec, bool done)
542 {
543    if (!job_level) {
544       return 1;                       /* no specification matches all */
545    }
546    if (job_level->JobLevel == sessrec->JobLevel) {
547       return 1;
548    }
549    if (job_level->next) {
550       return match_job_level(bsr, job_level->next, sessrec, 1);
551    }
552    return 0;
553 }
554
555 static int match_jobid(BSR *bsr, BSR_JOBID *jobid, SESSION_LABEL *sessrec, bool done)
556 {
557    if (!jobid) {
558       return 1;                       /* no specification matches all */
559    }
560    if (jobid->JobId <= sessrec->JobId && jobid->JobId2 >= sessrec->JobId) {
561       return 1;
562    }
563    if (jobid->next) {
564       return match_jobid(bsr, jobid->next, sessrec, 1);
565    }
566    return 0;
567 }
568
569 static int match_volfile(BSR *bsr, BSR_VOLFILE *volfile, DEV_RECORD *rec, bool done)
570 {
571    if (!volfile) {
572       return 1;                       /* no specification matches all */
573    }
574 /*
575  * The following code is turned off because this should now work
576  *   with disk files too, though since a "volfile" is 4GB, it does
577  *   not improve performance much.
578  */
579 #ifdef xxx
580    /* For the moment, these tests work only with tapes. */
581    if (!(rec->state & REC_ISTAPE)) {
582       return 1;                       /* All File records OK for this match */
583    }
584    Dmsg3(dbglevel, "match_volfile: sfile=%u efile=%u recfile=%u\n",
585              volfile->sfile, volfile->efile, rec->File);
586 #endif
587    if (volfile->sfile <= rec->File && volfile->efile >= rec->File) {
588       return 1;
589    }
590    /* Once we get past last efile, we are done */
591    if (rec->File > volfile->efile) {
592       volfile->done = true;              /* set local done */
593    }
594    if (volfile->next) {
595       return match_volfile(bsr, volfile->next, rec, volfile->done && done);
596    }
597
598    /* If we are done and all prior matches are done, this bsr is finished */
599    if (volfile->done && done) {
600       bsr->done = true;
601       bsr->root->reposition = true;
602       Dmsg2(dbglevel, "bsr done from volfile rec=%u volefile=%u\n",
603          rec->File, volfile->efile);
604    }
605    return 0;
606 }
607
608 static int match_volblock(BSR *bsr, BSR_VOLBLOCK *volblock, DEV_RECORD *rec, bool done)
609 {
610    /*
611     * Currently block matching does not work correctly for disk
612     * files in all cases, so it is "turned off" by the following 
613     * return statement.
614     */
615    return 1;
616
617
618    if (!volblock) {
619       return 1;                       /* no specification matches all */
620    }
621    /* For the moment, these tests work only with disk. */
622    if (rec->state & REC_ISTAPE) {
623       return 1;                       /* All File records OK for this match */
624    }
625 //  Dmsg3(dbglevel, "match_volblock: sblock=%u eblock=%u recblock=%u\n",
626 //             volblock->sblock, volblock->eblock, rec->Block);
627    if (volblock->sblock <= rec->Block && volblock->eblock >= rec->Block) {
628       return 1;
629    }
630    /* Once we get past last eblock, we are done */
631    if (rec->Block > volblock->eblock) {
632       volblock->done = true;              /* set local done */
633    }
634    if (volblock->next) {
635       return match_volblock(bsr, volblock->next, rec, volblock->done && done);
636    }
637
638    /* If we are done and all prior matches are done, this bsr is finished */
639    if (volblock->done && done) {
640       bsr->done = true;
641       bsr->root->reposition = true;
642       Dmsg2(dbglevel, "bsr done from volblock rec=%u voleblock=%u\n",
643          rec->Block, volblock->eblock);
644    }
645    return 0;
646 }
647
648
649 static int match_stream(BSR *bsr, BSR_STREAM *stream, DEV_RECORD *rec, bool done)
650 {
651    if (!stream) {
652       return 1;                       /* no specification matches all */
653    }
654    if (stream->stream == rec->Stream) {
655       return 1;
656    }
657    if (stream->next) {
658       return match_stream(bsr, stream->next, rec, 1);
659    }
660    return 0;
661 }
662
663 static int match_sesstime(BSR *bsr, BSR_SESSTIME *sesstime, DEV_RECORD *rec, bool done)
664 {
665    if (!sesstime) {
666       return 1;                       /* no specification matches all */
667    }
668    if (sesstime->sesstime == rec->VolSessionTime) {
669       return 1;
670    }
671    if (rec->VolSessionTime > sesstime->sesstime) {
672       sesstime->done = true;
673    }
674    if (sesstime->next) {
675       return match_sesstime(bsr, sesstime->next, rec, sesstime->done && done);
676    }
677    if (sesstime->done && done) {
678       bsr->done = true;
679       bsr->root->reposition = true;
680       Dmsg0(dbglevel, "bsr done from sesstime\n");
681    }
682    return 0;
683 }
684
685 /* 
686  * Note, we cannot mark bsr done based on session id because we may
687  *  have interleaved records, and there may be more of what we want
688  *  later.
689  */
690 static int match_sessid(BSR *bsr, BSR_SESSID *sessid, DEV_RECORD *rec)
691 {
692    if (!sessid) {
693       return 1;                       /* no specification matches all */
694    }
695    if (sessid->sessid <= rec->VolSessionId && sessid->sessid2 >= rec->VolSessionId) {
696       return 1;
697    }
698    if (sessid->next) {
699       return match_sessid(bsr, sessid->next, rec);
700    }
701    return 0;
702 }
703
704 /*
705  * When reading the Volume, the Volume Findex (rec->FileIndex) always
706  *   are found in sequential order. Thus we can make optimizations.
707  *
708  *  ***FIXME*** optimizations
709  * We could optimize by removing the recursion.
710  */
711 static int match_findex(BSR *bsr, BSR_FINDEX *findex, DEV_RECORD *rec, bool done)
712 {
713    if (!findex) {
714       return 1;                       /* no specification matches all */
715    }
716    if (!findex->done) {
717       if (findex->findex <= rec->FileIndex && findex->findex2 >= rec->FileIndex) {
718          Dmsg3(dbglevel, "Match on findex=%d. bsrFIs=%d,%d\n",
719                rec->FileIndex, findex->findex, findex->findex2);
720          return 1;
721       }
722       if (rec->FileIndex > findex->findex2) {
723          findex->done = true;
724       }
725    }
726    if (findex->next) {
727       return match_findex(bsr, findex->next, rec, findex->done && done);
728    }
729    if (findex->done && done) {
730       bsr->done = true;
731       bsr->root->reposition = true;
732       Dmsg1(dbglevel, "bsr done from findex %d\n", rec->FileIndex);
733    }
734    return 0;
735 }