]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/match_bsr.c
Implement fixes to make disk seeking work. Currently turned
[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    Bacula® - The Network Backup Solution
11
12    Copyright (C) 2002-2006 Free Software Foundation Europe e.V.
13
14    The main author of Bacula is Kern Sibbald, with contributions from
15    many others, a complete list can be found in the file AUTHORS.
16    This program is Free Software; you can redistribute it and/or
17    modify it under the terms of version two of the GNU General Public
18    License as published by the Free Software Foundation plus additions
19    that are listed in the file LICENSE.
20
21    This program is distributed in the hope that it will be useful, but
22    WITHOUT ANY WARRANTY; without even the implied warranty of
23    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
24    General Public License for more details.
25
26    You should have received a copy of the GNU General Public License
27    along with this program; if not, write to the Free Software
28    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
29    02110-1301, USA.
30
31    Bacula® is a registered trademark of John Walker.
32    The licensor of Bacula is the Free Software Foundation Europe
33    (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
34    Switzerland, email:ftf@fsfeurope.org.
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 = 10;
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);
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
152 /*********************************************************************
153  *
154  *      Match Bootstrap records
155  *        returns  1 on match
156  *        returns  0 no match and reposition is set if we should
157  *                      reposition the tape
158  *       returns -1 no additional matches possible
159  */
160 int match_bsr(BSR *bsr, DEV_RECORD *rec, VOLUME_LABEL *volrec, SESSION_LABEL *sessrec)
161 {
162    int stat;
163
164    /*
165     * The bsr->reposition flag is set any time a bsr is done.
166     *   In this case, we can probably reposition the
167     *   tape to the next available bsr position.
168     */
169    if (bsr) {
170       bsr->reposition = false;
171       stat = match_all(bsr, rec, volrec, sessrec, true);
172       /*
173        * Note, bsr->reposition is set by match_all when
174        *  a bsr is done. We turn it off if a match was
175        *  found or if we cannot use positioning
176        */
177       if (stat != 0 || !bsr->use_positioning) {
178          bsr->reposition = false;
179       }
180    } else {
181       stat = 1;                       /* no bsr => match all */
182    }
183    return stat;
184 }
185
186 /*
187  * Find the next bsr that applies to the current tape.
188  *   It is the one with the smallest VolFile position.
189  */
190 BSR *find_next_bsr(BSR *root_bsr, DEVICE *dev)
191 {
192    BSR *bsr;
193    BSR *found_bsr = NULL;
194    bool no_file_seek = !dev->is_tape();
195 #ifdef FILE_SEEK
196    no_file_seek = false;
197 #endif
198
199    if (!root_bsr || !root_bsr->use_positioning ||
200        !root_bsr->reposition || no_file_seek) {
201       Dmsg2(dbglevel, "No nxt_bsr use_pos=%d repos=%d\n", root_bsr->use_positioning, root_bsr->reposition);
202       return NULL;
203    }
204    Dmsg2(dbglevel, "use_pos=%d repos=%d\n", root_bsr->use_positioning, root_bsr->reposition);
205    root_bsr->mount_next_volume = false;
206    /* Walk through all bsrs to find the next one to use => smallest file,block */
207    for (bsr=root_bsr; bsr; bsr=bsr->next) {
208       if (bsr->done || !match_volume(bsr, bsr->volume, &dev->VolHdr, 1)) {
209          continue;
210       }
211       if (found_bsr == NULL) {
212          found_bsr = bsr;
213       } else {
214          found_bsr = find_smallest_volfile(found_bsr, bsr);
215       }
216    }
217    /*
218     * If we get to this point and found no bsr, it means
219     *  that any additional bsr's must apply to the next
220     *  tape, so set a flag.
221     */
222    if (found_bsr == NULL) {
223       root_bsr->mount_next_volume = true;
224    }
225    return found_bsr;
226 }
227
228 /*
229  * ***FIXME***
230  * This routine needs to be fixed to only look at items that
231  *   are not marked as done.  Otherwise, it can find a bsr
232  *   that has already been consumed, and this will cause the
233  *   bsr to be used, thus we may seek back and re-read the
234  *   same records, causing an error.  This deficiency must
235  *   be fixed.  For the moment, it has been kludged in 
236  *   read_record.c to avoid seeking back if find_next_bsr
237  *   returns a bsr pointing to a smaller address (file/block).
238  */
239 static BSR *find_smallest_volfile(BSR *found_bsr, BSR *bsr)
240 {
241    BSR *return_bsr = found_bsr;
242    BSR_VOLFILE *vf;
243    BSR_VOLBLOCK *vb;
244    uint32_t found_bsr_sfile, bsr_sfile;
245    uint32_t found_bsr_sblock, bsr_sblock;
246
247    /* Find the smallest file in the found_bsr */
248    vf = found_bsr->volfile;
249    found_bsr_sfile = vf->sfile;
250    while ( (vf=vf->next) ) {
251       if (vf->sfile < found_bsr_sfile) {
252          found_bsr_sfile = vf->sfile;
253       }
254    }
255
256    /* Find the smallest file in the bsr */
257    vf = bsr->volfile;
258    bsr_sfile = vf->sfile;
259    while ( (vf=vf->next) ) {
260       if (vf->sfile < bsr_sfile) {
261          bsr_sfile = vf->sfile;
262       }
263    }
264     
265    /* if the bsr file is less than the found_bsr file, return bsr */
266    if (found_bsr_sfile > bsr_sfile) {
267       return_bsr = bsr;
268    } else if (found_bsr_sfile == bsr_sfile) {
269       /* Files are equal */
270       /* find smallest block in found_bsr */
271       vb = found_bsr->volblock;
272       found_bsr_sblock = vb->sblock;
273       while ( (vb=vb->next) ) {
274          if (vb->sblock < found_bsr_sblock) {
275             found_bsr_sblock = vb->sblock;
276          }
277       }
278       /* Find smallest block in bsr */
279       vb = bsr->volblock;
280       bsr_sblock = vb->sblock;
281       while ( (vb=vb->next) ) {
282          if (vb->sblock < bsr_sblock) {
283             bsr_sblock = vb->sblock;
284          }
285       }
286       /* Compare and return the smallest */
287       if (found_bsr_sblock > bsr_sblock) {
288          return_bsr = bsr;
289       }
290    }
291    return return_bsr;
292 }
293
294 /*
295  * Called after the signature record so that
296  *   we can see if the current bsr has been
297  *   fully processed (i.e. is done).
298  *  The bsr argument is not used, but is included
299  *    for consistency with the other match calls.
300  *
301  * Returns: true if we should reposition
302  *        : false otherwise.
303  */
304 bool is_this_bsr_done(BSR *bsr, DEV_RECORD *rec)
305 {
306    BSR *rbsr = rec->bsr;
307    Dmsg1(dbglevel, "match_set %d\n", rbsr != NULL);
308    if (!rbsr) {
309       return false;
310    }
311    rec->bsr = NULL;
312    rbsr->found++;
313    if (rbsr->count && rbsr->found >= rbsr->count) {
314       rbsr->done = true;
315       rbsr->root->reposition = true;
316       Dmsg2(dbglevel, "is_end_this_bsr set reposition=1 count=%d found=%d\n",
317          rbsr->count, rbsr->found);
318       return true;
319    }
320    return false;
321 }
322
323 /*
324  * Match all the components of current record
325  *   returns  1 on match
326  *   returns  0 no match
327  *   returns -1 no additional matches possible
328  */
329 static int match_all(BSR *bsr, DEV_RECORD *rec, VOLUME_LABEL *volrec,
330                      SESSION_LABEL *sessrec, bool done)
331 {
332    if (bsr->done) {
333       Dmsg0(dbglevel, "bsr->done set\n");
334       goto no_match;
335    }
336    if (!match_volume(bsr, bsr->volume, volrec, 1)) {
337       Dmsg2(dbglevel, "bsr fail vol=%s != rec vol=%s\n", bsr->volume->VolumeName,
338             volrec->VolumeName);
339       goto no_match;
340    }
341    if (!match_volfile(bsr, bsr->volfile, rec, 1)) {
342       Dmsg3(dbglevel, "Fail on file=%d. bsr=%d,%d\n", 
343          rec->File, bsr->volfile->sfile, bsr->volfile->efile);
344       goto no_match;
345    }
346    if (!match_sesstime(bsr, bsr->sesstime, rec, 1)) {
347       Dmsg2(dbglevel, "Fail on sesstime. bsr=%d rec=%d\n",
348          bsr->sesstime->sesstime, rec->VolSessionTime);
349       goto no_match;
350    }
351
352    /* NOTE!! This test MUST come after the sesstime test */
353    if (!match_sessid(bsr, bsr->sessid, rec)) {
354       Dmsg2(dbglevel, "Fail on sessid. bsr=%d rec=%d\n",
355          bsr->sessid->sessid, rec->VolSessionId);
356       goto no_match;
357    }
358
359    /* NOTE!! This test MUST come after sesstime and sessid tests */
360    if (!match_findex(bsr, bsr->FileIndex, rec, 1)) {
361       Dmsg3(dbglevel, "Fail on findex=%d. bsr=%d,%d\n",
362          rec->FileIndex, bsr->FileIndex->findex, bsr->FileIndex->findex2);
363       goto no_match;
364    }
365    /*
366     * If a count was specified and we have a FileIndex, assume
367     *   it is a Bacula created bsr (or the equivalent). We
368     *   then save the bsr where the match occurred so that
369     *   after processing the record or records, we can update
370     *   the found count. I.e. rec->bsr points to the bsr that
371     *   satisfied the match.
372     */
373    if (bsr->count && bsr->FileIndex) {
374       rec->bsr = bsr;
375       return 1;                       /* this is a complete match */
376    }
377
378    /*
379     * The selections below are not used by Bacula's
380     *   restore command, and don't work because of
381     *   the rec->bsr = bsr optimization above.
382     */
383    if (!match_jobid(bsr, bsr->JobId, sessrec, 1)) {
384       Dmsg0(dbglevel, "fail on JobId\n");
385       goto no_match;
386
387    }
388    if (!match_job(bsr, bsr->job, sessrec, 1)) {
389       Dmsg0(dbglevel, "fail on Job\n");
390       goto no_match;
391    }
392    if (!match_client(bsr, bsr->client, sessrec, 1)) {
393       Dmsg0(dbglevel, "fail on Client\n");
394       goto no_match;
395    }
396    if (!match_job_type(bsr, bsr->JobType, sessrec, 1)) {
397       Dmsg0(dbglevel, "fail on Job type\n");
398       goto no_match;
399    }
400    if (!match_job_level(bsr, bsr->JobLevel, sessrec, 1)) {
401       Dmsg0(dbglevel, "fail on Job level\n");
402       goto no_match;
403    }
404    if (!match_stream(bsr, bsr->stream, rec, 1)) {
405       Dmsg0(dbglevel, "fail on stream\n");
406       goto no_match;
407    }
408    return 1;
409
410 no_match:
411    if (bsr->next) {
412       return match_all(bsr->next, rec, volrec, sessrec, bsr->done && done);
413    }
414    if (bsr->done && done) {
415       return -1;
416    }
417    return 0;
418 }
419
420 static int match_volume(BSR *bsr, BSR_VOLUME *volume, VOLUME_LABEL *volrec, bool done)
421 {
422    if (!volume) {
423       return 0;                       /* Volume must match */
424    }
425    if (strcmp(volume->VolumeName, volrec->VolumeName) == 0) {
426       return 1;
427    }
428    if (volume->next) {
429       return match_volume(bsr, volume->next, volrec, 1);
430    }
431    return 0;
432 }
433
434 static int match_client(BSR *bsr, BSR_CLIENT *client, SESSION_LABEL *sessrec, bool done)
435 {
436    if (!client) {
437       return 1;                       /* no specification matches all */
438    }
439    if (fnmatch(client->ClientName, sessrec->ClientName, 0) == 0) {
440       return 1;
441    }
442    if (client->next) {
443       return match_client(bsr, client->next, sessrec, 1);
444    }
445    return 0;
446 }
447
448 static int match_job(BSR *bsr, BSR_JOB *job, SESSION_LABEL *sessrec, bool done)
449 {
450    if (!job) {
451       return 1;                       /* no specification matches all */
452    }
453    if (fnmatch(job->Job, sessrec->Job, 0) == 0) {
454       return 1;
455    }
456    if (job->next) {
457       return match_job(bsr, job->next, sessrec, 1);
458    }
459    return 0;
460 }
461
462 static int match_job_type(BSR *bsr, BSR_JOBTYPE *job_type, SESSION_LABEL *sessrec, bool done)
463 {
464    if (!job_type) {
465       return 1;                       /* no specification matches all */
466    }
467    if (job_type->JobType == sessrec->JobType) {
468       return 1;
469    }
470    if (job_type->next) {
471       return match_job_type(bsr, job_type->next, sessrec, 1);
472    }
473    return 0;
474 }
475
476 static int match_job_level(BSR *bsr, BSR_JOBLEVEL *job_level, SESSION_LABEL *sessrec, bool done)
477 {
478    if (!job_level) {
479       return 1;                       /* no specification matches all */
480    }
481    if (job_level->JobLevel == sessrec->JobLevel) {
482       return 1;
483    }
484    if (job_level->next) {
485       return match_job_level(bsr, job_level->next, sessrec, 1);
486    }
487    return 0;
488 }
489
490 static int match_jobid(BSR *bsr, BSR_JOBID *jobid, SESSION_LABEL *sessrec, bool done)
491 {
492    if (!jobid) {
493       return 1;                       /* no specification matches all */
494    }
495    if (jobid->JobId <= sessrec->JobId && jobid->JobId2 >= sessrec->JobId) {
496       return 1;
497    }
498    if (jobid->next) {
499       return match_jobid(bsr, jobid->next, sessrec, 1);
500    }
501    return 0;
502 }
503
504 static int match_volfile(BSR *bsr, BSR_VOLFILE *volfile, DEV_RECORD *rec, bool done)
505 {
506    if (!volfile) {
507       return 1;                       /* no specification matches all */
508    }
509    /* For the moment, these tests work only with tapes. */
510    if (!(rec->state & REC_ISTAPE)) {
511       return 1;                       /* All File records OK for this match */
512    }
513 // Dmsg3(dbglevel, "match_volfile: sfile=%d efile=%d recfile=%d\n",
514 //             volfile->sfile, volfile->efile, rec->File);
515    if (volfile->sfile <= rec->File && volfile->efile >= rec->File) {
516       return 1;
517    }
518    /* Once we get past last efile, we are done */
519    if (rec->File > volfile->efile) {
520       volfile->done = true;              /* set local done */
521    }
522    if (volfile->next) {
523       return match_volfile(bsr, volfile->next, rec, volfile->done && done);
524    }
525
526    /* If we are done and all prior matches are done, this bsr is finished */
527    if (volfile->done && done) {
528       bsr->done = true;
529       bsr->root->reposition = true;
530       Dmsg2(dbglevel, "bsr done from volfile rec=%d volefile=%d\n",
531          rec->File, volfile->efile);
532    }
533    return 0;
534 }
535
536 static int match_stream(BSR *bsr, BSR_STREAM *stream, DEV_RECORD *rec, bool done)
537 {
538    if (!stream) {
539       return 1;                       /* no specification matches all */
540    }
541    if (stream->stream == rec->Stream) {
542       return 1;
543    }
544    if (stream->next) {
545       return match_stream(bsr, stream->next, rec, 1);
546    }
547    return 0;
548 }
549
550 static int match_sesstime(BSR *bsr, BSR_SESSTIME *sesstime, DEV_RECORD *rec, bool done)
551 {
552    if (!sesstime) {
553       return 1;                       /* no specification matches all */
554    }
555    if (sesstime->sesstime == rec->VolSessionTime) {
556       return 1;
557    }
558    if (rec->VolSessionTime > sesstime->sesstime) {
559       sesstime->done = true;
560    }
561    if (sesstime->next) {
562       return match_sesstime(bsr, sesstime->next, rec, sesstime->done && done);
563    }
564    if (sesstime->done && done) {
565       bsr->done = true;
566       bsr->root->reposition = true;
567       Dmsg0(dbglevel, "bsr done from sesstime\n");
568    }
569    return 0;
570 }
571
572 static int match_sessid(BSR *bsr, BSR_SESSID *sessid, DEV_RECORD *rec)
573 {
574    if (!sessid) {
575       return 1;                       /* no specification matches all */
576    }
577    if (sessid->sessid <= rec->VolSessionId && sessid->sessid2 >= rec->VolSessionId) {
578       return 1;
579    }
580    if (sessid->next) {
581       return match_sessid(bsr, sessid->next, rec);
582    }
583    return 0;
584 }
585
586 /*
587  * When reading the Volume, the Volume Findex (rec->FileIndex) always
588  *   are found in sequential order. Thus we can make optimizations.
589  *
590  *  ***FIXME*** optimizations
591  * We could optimize a lot here by removing the recursion, and 
592  *   stopping the search earlier -- say when rec->FileIndex > findex->findex2
593  *   and findex->next == NULL.  
594  */
595 static int match_findex(BSR *bsr, BSR_FINDEX *findex, DEV_RECORD *rec, bool done)
596 {
597    if (!findex) {
598       return 1;                       /* no specification matches all */
599    }
600    if (!findex->done) {
601       if (findex->findex <= rec->FileIndex && findex->findex2 >= rec->FileIndex) {
602          Dmsg3(dbglevel, "Match on findex=%d. bsr=%d,%d\n",
603                rec->FileIndex, findex->findex, findex->findex2);
604          return 1;
605       }
606       if (rec->FileIndex > findex->findex2) {
607          findex->done = true;
608       }
609    }
610    if (findex->next) {
611       return match_findex(bsr, findex->next, rec, findex->done && done);
612    }
613    if (findex->done && done) {
614       bsr->done = true;
615       bsr->root->reposition = true;
616       Dmsg1(dbglevel, "bsr done from findex %d\n", rec->FileIndex);
617    }
618    return 0;
619 }