]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/match_bsr.c
Apply Joao's patch to SQLite tables to make chars work.
[bacula/bacula] / bacula / src / stored / match_bsr.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2002-2007 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_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);
81
82
83 /*********************************************************************
84  *
85  *  If possible, position the archive device (tape) to read the
86  *  next block.
87  */
88 void position_bsr_block(BSR *bsr, DEV_BLOCK *block)
89 {
90    /* To be implemented */
91 }
92
93 /*********************************************************************
94  *
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.
99  *
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).
103  *
104  */
105 int match_bsr_block(BSR *bsr, DEV_BLOCK *block)
106 {
107    if (!bsr || !bsr->use_fast_rejection || (block->BlockVer < 2)) {
108       return 1;                       /* cannot fast reject */
109    }
110
111    for ( ; bsr; bsr=bsr->next) {
112       if (!match_block_sesstime(bsr, bsr->sesstime, block)) {
113          continue;
114       }
115       if (!match_block_sessid(bsr, bsr->sessid, block)) {
116          continue;
117       }
118       return 1;
119    }
120    return 0;
121 }
122
123 static int match_block_sesstime(BSR *bsr, BSR_SESSTIME *sesstime, DEV_BLOCK *block)
124 {
125    if (!sesstime) {
126       return 1;                       /* no specification matches all */
127    }
128    if (sesstime->sesstime == block->VolSessionTime) {
129       return 1;
130    }
131    if (sesstime->next) {
132       return match_block_sesstime(bsr, sesstime->next, block);
133    }
134    return 0;
135 }
136
137 static int match_block_sessid(BSR *bsr, BSR_SESSID *sessid, DEV_BLOCK *block)
138 {
139    if (!sessid) {
140       return 1;                       /* no specification matches all */
141    }
142    if (sessid->sessid <= block->VolSessionId && sessid->sessid2 >= block->VolSessionId) {
143       return 1;
144    }
145    if (sessid->next) {
146       return match_block_sessid(bsr, sessid->next, block);
147    }
148    return 0;
149 }
150
151 static int match_fileregex(BSR *bsr, DEV_RECORD *rec, JCR *jcr)
152 {
153    if (bsr->fileregex_re == NULL)
154       return 1;
155
156    if (bsr->attr == NULL)
157       bsr->attr = new_attr(jcr);
158
159    /* The code breaks if the first record associated with a file is
160     * not of this type
161     */
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);
169          } else {
170             Dmsg2(dbglevel, "Didn't match, skipping fname=%s FI=%d\n",
171                   bsr->attr->fname, rec->FileIndex);
172             bsr->skip_file = true;
173          }
174       }
175    }
176    return 1;
177 }
178
179 /*********************************************************************
180  *
181  *      Match Bootstrap records
182  *        returns  1 on match
183  *        returns  0 no match and reposition is set if we should
184  *                      reposition the tape
185  *       returns -1 no additional matches possible
186  */
187 int match_bsr(BSR *bsr, DEV_RECORD *rec, VOLUME_LABEL *volrec, SESSION_LABEL *sessrec, JCR *jcr)
188 {
189    int stat;
190
191    /*
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.
195     */
196    if (bsr) {
197       bsr->reposition = false;
198       stat = match_all(bsr, rec, volrec, sessrec, true, jcr);
199       /*
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
203        */
204       if (stat != 0 || !bsr->use_positioning) {
205          bsr->reposition = false;
206       }
207    } else {
208       stat = 1;                       /* no bsr => match all */
209    }
210    return stat;
211 }
212
213 /*
214  * Find the next bsr that applies to the current tape.
215  *   It is the one with the smallest VolFile position.
216  */
217 BSR *find_next_bsr(BSR *root_bsr, DEVICE *dev)
218 {
219    BSR *bsr;
220    BSR *found_bsr = NULL;
221
222    /* Do tape/disk seeking only if CAP_POSITIONBLOCKS is on */
223    if (!root_bsr) {
224       Dmsg0(dbglevel, "NULL root bsr pointer passed to find_next_bsr.\n");
225       return NULL;
226    }
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);
230       return NULL;
231    }
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)) {
237          continue;
238       }
239       if (found_bsr == NULL) {
240          found_bsr = bsr;
241       } else {
242          found_bsr = find_smallest_volfile(found_bsr, bsr);
243       }
244    }
245    /*
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.
249     */
250    if (found_bsr == NULL) {
251       root_bsr->mount_next_volume = true;
252    }
253    return found_bsr;
254 }
255
256 /*
257  * ***FIXME***
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).
266  */
267 static BSR *find_smallest_volfile(BSR *found_bsr, BSR *bsr)
268 {
269    BSR *return_bsr = found_bsr;
270    BSR_VOLFILE *vf;
271    BSR_VOLBLOCK *vb;
272    uint32_t found_bsr_sfile, bsr_sfile;
273    uint32_t found_bsr_sblock, bsr_sblock;
274
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;
281       }
282    }
283
284    /* Find the smallest file in the bsr */
285    vf = bsr->volfile;
286    bsr_sfile = vf->sfile;
287    while ( (vf=vf->next) ) {
288       if (vf->sfile < bsr_sfile) {
289          bsr_sfile = vf->sfile;
290       }
291    }
292     
293    /* if the bsr file is less than the found_bsr file, return bsr */
294    if (found_bsr_sfile > bsr_sfile) {
295       return_bsr = bsr;
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;
304          }
305       }
306       /* Find smallest block in bsr */
307       vb = bsr->volblock;
308       bsr_sblock = vb->sblock;
309       while ( (vb=vb->next) ) {
310          if (vb->sblock < bsr_sblock) {
311             bsr_sblock = vb->sblock;
312          }
313       }
314       /* Compare and return the smallest */
315       if (found_bsr_sblock > bsr_sblock) {
316          return_bsr = bsr;
317       }
318    }
319    return return_bsr;
320 }
321
322 /*
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.
328  *
329  * Returns: true if we should reposition
330  *        : false otherwise.
331  */
332 bool is_this_bsr_done(BSR *bsr, DEV_RECORD *rec)
333 {
334    BSR *rbsr = rec->bsr;
335    Dmsg1(dbglevel, "match_set %d\n", rbsr != NULL);
336    if (!rbsr) {
337       return false;
338    }
339    rec->bsr = NULL;
340    rbsr->found++;
341    if (rbsr->count && rbsr->found >= rbsr->count) {
342       rbsr->done = true;
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);
346       return true;
347    }
348    return false;
349 }
350
351 /*
352  * Match all the components of current record
353  *   returns  1 on match
354  *   returns  0 no match
355  *   returns -1 no additional matches possible
356  */
357 static int match_all(BSR *bsr, DEV_RECORD *rec, VOLUME_LABEL *volrec,
358                      SESSION_LABEL *sessrec, bool done, JCR *jcr)
359 {
360    if (bsr->done) {
361 //    Dmsg0(dbglevel, "bsr->done set\n");
362       goto no_match;
363    }
364    if (!match_volume(bsr, bsr->volume, volrec, 1)) {
365       Dmsg2(dbglevel, "bsr fail vol=%s != rec vol=%s\n", bsr->volume->VolumeName,
366             volrec->VolumeName);
367       goto no_match;
368    }
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);
372       goto no_match;
373    }
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);
377       goto no_match;
378    }
379
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);
384       goto no_match;
385    }
386
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);
391       goto no_match;
392    }
393
394    if (!match_fileregex(bsr, rec, jcr)) {
395      Dmsg1(dbglevel, "Fail on fileregex='%s'\n", bsr->fileregex);
396      goto no_match;
397    }
398
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);
402       goto no_match;
403    }
404
405    /*
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.
412     */
413    if (bsr->count && bsr->FileIndex) {
414       rec->bsr = bsr;
415       return 1;                       /* this is a complete match */
416    }
417
418    /*
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.
422     */
423    if (!match_jobid(bsr, bsr->JobId, sessrec, 1)) {
424       Dmsg0(dbglevel, "fail on JobId\n");
425       goto no_match;
426
427    }
428    if (!match_job(bsr, bsr->job, sessrec, 1)) {
429       Dmsg0(dbglevel, "fail on Job\n");
430       goto no_match;
431    }
432    if (!match_client(bsr, bsr->client, sessrec, 1)) {
433       Dmsg0(dbglevel, "fail on Client\n");
434       goto no_match;
435    }
436    if (!match_job_type(bsr, bsr->JobType, sessrec, 1)) {
437       Dmsg0(dbglevel, "fail on Job type\n");
438       goto no_match;
439    }
440    if (!match_job_level(bsr, bsr->JobLevel, sessrec, 1)) {
441       Dmsg0(dbglevel, "fail on Job level\n");
442       goto no_match;
443    }
444    if (!match_stream(bsr, bsr->stream, rec, 1)) {
445       Dmsg0(dbglevel, "fail on stream\n");
446       goto no_match;
447    }
448    return 1;
449
450 no_match:
451    if (bsr->next) {
452       return match_all(bsr->next, rec, volrec, sessrec, bsr->done && done, jcr);
453    }
454    if (bsr->done && done) {
455       return -1;
456    }
457    return 0;
458 }
459
460 static int match_volume(BSR *bsr, BSR_VOLUME *volume, VOLUME_LABEL *volrec, bool done)
461 {
462    if (!volume) {
463       return 0;                       /* Volume must match */
464    }
465    if (strcmp(volume->VolumeName, volrec->VolumeName) == 0) {
466       return 1;
467    }
468    if (volume->next) {
469       return match_volume(bsr, volume->next, volrec, 1);
470    }
471    return 0;
472 }
473
474 static int match_client(BSR *bsr, BSR_CLIENT *client, SESSION_LABEL *sessrec, bool done)
475 {
476    if (!client) {
477       return 1;                       /* no specification matches all */
478    }
479    if (strcmp(client->ClientName, sessrec->ClientName) == 0) {
480       return 1;
481    }
482    if (client->next) {
483       return match_client(bsr, client->next, sessrec, 1);
484    }
485    return 0;
486 }
487
488 static int match_job(BSR *bsr, BSR_JOB *job, SESSION_LABEL *sessrec, bool done)
489 {
490    if (!job) {
491       return 1;                       /* no specification matches all */
492    }
493    if (strcmp(job->Job, sessrec->Job) == 0) {
494       return 1;
495    }
496    if (job->next) {
497       return match_job(bsr, job->next, sessrec, 1);
498    }
499    return 0;
500 }
501
502 static int match_job_type(BSR *bsr, BSR_JOBTYPE *job_type, SESSION_LABEL *sessrec, bool done)
503 {
504    if (!job_type) {
505       return 1;                       /* no specification matches all */
506    }
507    if (job_type->JobType == sessrec->JobType) {
508       return 1;
509    }
510    if (job_type->next) {
511       return match_job_type(bsr, job_type->next, sessrec, 1);
512    }
513    return 0;
514 }
515
516 static int match_job_level(BSR *bsr, BSR_JOBLEVEL *job_level, SESSION_LABEL *sessrec, bool done)
517 {
518    if (!job_level) {
519       return 1;                       /* no specification matches all */
520    }
521    if (job_level->JobLevel == sessrec->JobLevel) {
522       return 1;
523    }
524    if (job_level->next) {
525       return match_job_level(bsr, job_level->next, sessrec, 1);
526    }
527    return 0;
528 }
529
530 static int match_jobid(BSR *bsr, BSR_JOBID *jobid, SESSION_LABEL *sessrec, bool done)
531 {
532    if (!jobid) {
533       return 1;                       /* no specification matches all */
534    }
535    if (jobid->JobId <= sessrec->JobId && jobid->JobId2 >= sessrec->JobId) {
536       return 1;
537    }
538    if (jobid->next) {
539       return match_jobid(bsr, jobid->next, sessrec, 1);
540    }
541    return 0;
542 }
543
544 static int match_volfile(BSR *bsr, BSR_VOLFILE *volfile, DEV_RECORD *rec, bool done)
545 {
546    if (!volfile) {
547       return 1;                       /* no specification matches all */
548    }
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 */
552    }
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) {
556       return 1;
557    }
558    /* Once we get past last efile, we are done */
559    if (rec->File > volfile->efile) {
560       volfile->done = true;              /* set local done */
561    }
562    if (volfile->next) {
563       return match_volfile(bsr, volfile->next, rec, volfile->done && done);
564    }
565
566    /* If we are done and all prior matches are done, this bsr is finished */
567    if (volfile->done && done) {
568       bsr->done = true;
569       bsr->root->reposition = true;
570       Dmsg2(dbglevel, "bsr done from volfile rec=%d volefile=%d\n",
571          rec->File, volfile->efile);
572    }
573    return 0;
574 }
575
576 static int match_stream(BSR *bsr, BSR_STREAM *stream, DEV_RECORD *rec, bool done)
577 {
578    if (!stream) {
579       return 1;                       /* no specification matches all */
580    }
581    if (stream->stream == rec->Stream) {
582       return 1;
583    }
584    if (stream->next) {
585       return match_stream(bsr, stream->next, rec, 1);
586    }
587    return 0;
588 }
589
590 static int match_sesstime(BSR *bsr, BSR_SESSTIME *sesstime, DEV_RECORD *rec, bool done)
591 {
592    if (!sesstime) {
593       return 1;                       /* no specification matches all */
594    }
595    if (sesstime->sesstime == rec->VolSessionTime) {
596       return 1;
597    }
598    if (rec->VolSessionTime > sesstime->sesstime) {
599       sesstime->done = true;
600    }
601    if (sesstime->next) {
602       return match_sesstime(bsr, sesstime->next, rec, sesstime->done && done);
603    }
604    if (sesstime->done && done) {
605       bsr->done = true;
606       bsr->root->reposition = true;
607       Dmsg0(dbglevel, "bsr done from sesstime\n");
608    }
609    return 0;
610 }
611
612 static int match_sessid(BSR *bsr, BSR_SESSID *sessid, DEV_RECORD *rec)
613 {
614    if (!sessid) {
615       return 1;                       /* no specification matches all */
616    }
617    if (sessid->sessid <= rec->VolSessionId && sessid->sessid2 >= rec->VolSessionId) {
618       return 1;
619    }
620    if (sessid->next) {
621       return match_sessid(bsr, sessid->next, rec);
622    }
623    return 0;
624 }
625
626 /*
627  * When reading the Volume, the Volume Findex (rec->FileIndex) always
628  *   are found in sequential order. Thus we can make optimizations.
629  *
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.  
634  */
635 static int match_findex(BSR *bsr, BSR_FINDEX *findex, DEV_RECORD *rec, bool done)
636 {
637    if (!findex) {
638       return 1;                       /* no specification matches all */
639    }
640    if (!findex->done) {
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);
644          return 1;
645       }
646       if (rec->FileIndex > findex->findex2) {
647          findex->done = true;
648       }
649    }
650    if (findex->next) {
651       return match_findex(bsr, findex->next, rec, findex->done && done);
652    }
653    if (findex->done && done) {
654       bsr->done = true;
655       bsr->root->reposition = true;
656       Dmsg1(dbglevel, "bsr done from findex %d\n", rec->FileIndex);
657    }
658    return 0;
659 }