]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/match_bsr.c
Convert to pure GPL v2 license.
[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 John Walker.
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);
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
195    /* Do tape/disk seeking only if CAP_POSITIONBLOCKS is on */
196    if (!root_bsr || !root_bsr->use_positioning ||
197        !root_bsr->reposition || !dev->has_cap(CAP_POSITIONBLOCKS)) {
198       Dmsg2(dbglevel, "No nxt_bsr use_pos=%d repos=%d\n", root_bsr->use_positioning, root_bsr->reposition);
199       return NULL;
200    }
201    Dmsg2(dbglevel, "use_pos=%d repos=%d\n", root_bsr->use_positioning, root_bsr->reposition);
202    root_bsr->mount_next_volume = false;
203    /* Walk through all bsrs to find the next one to use => smallest file,block */
204    for (bsr=root_bsr; bsr; bsr=bsr->next) {
205       if (bsr->done || !match_volume(bsr, bsr->volume, &dev->VolHdr, 1)) {
206          continue;
207       }
208       if (found_bsr == NULL) {
209          found_bsr = bsr;
210       } else {
211          found_bsr = find_smallest_volfile(found_bsr, bsr);
212       }
213    }
214    /*
215     * If we get to this point and found no bsr, it means
216     *  that any additional bsr's must apply to the next
217     *  tape, so set a flag.
218     */
219    if (found_bsr == NULL) {
220       root_bsr->mount_next_volume = true;
221    }
222    return found_bsr;
223 }
224
225 /*
226  * ***FIXME***
227  * This routine needs to be fixed to only look at items that
228  *   are not marked as done.  Otherwise, it can find a bsr
229  *   that has already been consumed, and this will cause the
230  *   bsr to be used, thus we may seek back and re-read the
231  *   same records, causing an error.  This deficiency must
232  *   be fixed.  For the moment, it has been kludged in 
233  *   read_record.c to avoid seeking back if find_next_bsr
234  *   returns a bsr pointing to a smaller address (file/block).
235  */
236 static BSR *find_smallest_volfile(BSR *found_bsr, BSR *bsr)
237 {
238    BSR *return_bsr = found_bsr;
239    BSR_VOLFILE *vf;
240    BSR_VOLBLOCK *vb;
241    uint32_t found_bsr_sfile, bsr_sfile;
242    uint32_t found_bsr_sblock, bsr_sblock;
243
244    /* Find the smallest file in the found_bsr */
245    vf = found_bsr->volfile;
246    found_bsr_sfile = vf->sfile;
247    while ( (vf=vf->next) ) {
248       if (vf->sfile < found_bsr_sfile) {
249          found_bsr_sfile = vf->sfile;
250       }
251    }
252
253    /* Find the smallest file in the bsr */
254    vf = bsr->volfile;
255    bsr_sfile = vf->sfile;
256    while ( (vf=vf->next) ) {
257       if (vf->sfile < bsr_sfile) {
258          bsr_sfile = vf->sfile;
259       }
260    }
261     
262    /* if the bsr file is less than the found_bsr file, return bsr */
263    if (found_bsr_sfile > bsr_sfile) {
264       return_bsr = bsr;
265    } else if (found_bsr_sfile == bsr_sfile) {
266       /* Files are equal */
267       /* find smallest block in found_bsr */
268       vb = found_bsr->volblock;
269       found_bsr_sblock = vb->sblock;
270       while ( (vb=vb->next) ) {
271          if (vb->sblock < found_bsr_sblock) {
272             found_bsr_sblock = vb->sblock;
273          }
274       }
275       /* Find smallest block in bsr */
276       vb = bsr->volblock;
277       bsr_sblock = vb->sblock;
278       while ( (vb=vb->next) ) {
279          if (vb->sblock < bsr_sblock) {
280             bsr_sblock = vb->sblock;
281          }
282       }
283       /* Compare and return the smallest */
284       if (found_bsr_sblock > bsr_sblock) {
285          return_bsr = bsr;
286       }
287    }
288    return return_bsr;
289 }
290
291 /*
292  * Called after the signature record so that
293  *   we can see if the current bsr has been
294  *   fully processed (i.e. is done).
295  *  The bsr argument is not used, but is included
296  *    for consistency with the other match calls.
297  *
298  * Returns: true if we should reposition
299  *        : false otherwise.
300  */
301 bool is_this_bsr_done(BSR *bsr, DEV_RECORD *rec)
302 {
303    BSR *rbsr = rec->bsr;
304    Dmsg1(dbglevel, "match_set %d\n", rbsr != NULL);
305    if (!rbsr) {
306       return false;
307    }
308    rec->bsr = NULL;
309    rbsr->found++;
310    if (rbsr->count && rbsr->found >= rbsr->count) {
311       rbsr->done = true;
312       rbsr->root->reposition = true;
313       Dmsg2(dbglevel, "is_end_this_bsr set reposition=1 count=%d found=%d\n",
314          rbsr->count, rbsr->found);
315       return true;
316    }
317    return false;
318 }
319
320 /*
321  * Match all the components of current record
322  *   returns  1 on match
323  *   returns  0 no match
324  *   returns -1 no additional matches possible
325  */
326 static int match_all(BSR *bsr, DEV_RECORD *rec, VOLUME_LABEL *volrec,
327                      SESSION_LABEL *sessrec, bool done)
328 {
329    if (bsr->done) {
330 //    Dmsg0(dbglevel, "bsr->done set\n");
331       goto no_match;
332    }
333    if (!match_volume(bsr, bsr->volume, volrec, 1)) {
334       Dmsg2(dbglevel, "bsr fail vol=%s != rec vol=%s\n", bsr->volume->VolumeName,
335             volrec->VolumeName);
336       goto no_match;
337    }
338    if (!match_volfile(bsr, bsr->volfile, rec, 1)) {
339       Dmsg3(dbglevel, "Fail on file=%d. bsr=%d,%d\n", 
340          rec->File, bsr->volfile->sfile, bsr->volfile->efile);
341       goto no_match;
342    }
343    if (!match_sesstime(bsr, bsr->sesstime, rec, 1)) {
344       Dmsg2(dbglevel, "Fail on sesstime. bsr=%d rec=%d\n",
345          bsr->sesstime->sesstime, rec->VolSessionTime);
346       goto no_match;
347    }
348
349    /* NOTE!! This test MUST come after the sesstime test */
350    if (!match_sessid(bsr, bsr->sessid, rec)) {
351       Dmsg2(dbglevel, "Fail on sessid. bsr=%d rec=%d\n",
352          bsr->sessid->sessid, rec->VolSessionId);
353       goto no_match;
354    }
355
356    /* NOTE!! This test MUST come after sesstime and sessid tests */
357    if (!match_findex(bsr, bsr->FileIndex, rec, 1)) {
358       Dmsg3(dbglevel, "Fail on findex=%d. bsr=%d,%d\n",
359          rec->FileIndex, bsr->FileIndex->findex, bsr->FileIndex->findex2);
360       goto no_match;
361    }
362    /*
363     * If a count was specified and we have a FileIndex, assume
364     *   it is a Bacula created bsr (or the equivalent). We
365     *   then save the bsr where the match occurred so that
366     *   after processing the record or records, we can update
367     *   the found count. I.e. rec->bsr points to the bsr that
368     *   satisfied the match.
369     */
370    if (bsr->count && bsr->FileIndex) {
371       rec->bsr = bsr;
372       return 1;                       /* this is a complete match */
373    }
374
375    /*
376     * The selections below are not used by Bacula's
377     *   restore command, and don't work because of
378     *   the rec->bsr = bsr optimization above.
379     */
380    if (!match_jobid(bsr, bsr->JobId, sessrec, 1)) {
381       Dmsg0(dbglevel, "fail on JobId\n");
382       goto no_match;
383
384    }
385    if (!match_job(bsr, bsr->job, sessrec, 1)) {
386       Dmsg0(dbglevel, "fail on Job\n");
387       goto no_match;
388    }
389    if (!match_client(bsr, bsr->client, sessrec, 1)) {
390       Dmsg0(dbglevel, "fail on Client\n");
391       goto no_match;
392    }
393    if (!match_job_type(bsr, bsr->JobType, sessrec, 1)) {
394       Dmsg0(dbglevel, "fail on Job type\n");
395       goto no_match;
396    }
397    if (!match_job_level(bsr, bsr->JobLevel, sessrec, 1)) {
398       Dmsg0(dbglevel, "fail on Job level\n");
399       goto no_match;
400    }
401    if (!match_stream(bsr, bsr->stream, rec, 1)) {
402       Dmsg0(dbglevel, "fail on stream\n");
403       goto no_match;
404    }
405    return 1;
406
407 no_match:
408    if (bsr->next) {
409       return match_all(bsr->next, rec, volrec, sessrec, bsr->done && done);
410    }
411    if (bsr->done && done) {
412       return -1;
413    }
414    return 0;
415 }
416
417 static int match_volume(BSR *bsr, BSR_VOLUME *volume, VOLUME_LABEL *volrec, bool done)
418 {
419    if (!volume) {
420       return 0;                       /* Volume must match */
421    }
422    if (strcmp(volume->VolumeName, volrec->VolumeName) == 0) {
423       return 1;
424    }
425    if (volume->next) {
426       return match_volume(bsr, volume->next, volrec, 1);
427    }
428    return 0;
429 }
430
431 static int match_client(BSR *bsr, BSR_CLIENT *client, SESSION_LABEL *sessrec, bool done)
432 {
433    if (!client) {
434       return 1;                       /* no specification matches all */
435    }
436    if (fnmatch(client->ClientName, sessrec->ClientName, 0) == 0) {
437       return 1;
438    }
439    if (client->next) {
440       return match_client(bsr, client->next, sessrec, 1);
441    }
442    return 0;
443 }
444
445 static int match_job(BSR *bsr, BSR_JOB *job, SESSION_LABEL *sessrec, bool done)
446 {
447    if (!job) {
448       return 1;                       /* no specification matches all */
449    }
450    if (fnmatch(job->Job, sessrec->Job, 0) == 0) {
451       return 1;
452    }
453    if (job->next) {
454       return match_job(bsr, job->next, sessrec, 1);
455    }
456    return 0;
457 }
458
459 static int match_job_type(BSR *bsr, BSR_JOBTYPE *job_type, SESSION_LABEL *sessrec, bool done)
460 {
461    if (!job_type) {
462       return 1;                       /* no specification matches all */
463    }
464    if (job_type->JobType == sessrec->JobType) {
465       return 1;
466    }
467    if (job_type->next) {
468       return match_job_type(bsr, job_type->next, sessrec, 1);
469    }
470    return 0;
471 }
472
473 static int match_job_level(BSR *bsr, BSR_JOBLEVEL *job_level, SESSION_LABEL *sessrec, bool done)
474 {
475    if (!job_level) {
476       return 1;                       /* no specification matches all */
477    }
478    if (job_level->JobLevel == sessrec->JobLevel) {
479       return 1;
480    }
481    if (job_level->next) {
482       return match_job_level(bsr, job_level->next, sessrec, 1);
483    }
484    return 0;
485 }
486
487 static int match_jobid(BSR *bsr, BSR_JOBID *jobid, SESSION_LABEL *sessrec, bool done)
488 {
489    if (!jobid) {
490       return 1;                       /* no specification matches all */
491    }
492    if (jobid->JobId <= sessrec->JobId && jobid->JobId2 >= sessrec->JobId) {
493       return 1;
494    }
495    if (jobid->next) {
496       return match_jobid(bsr, jobid->next, sessrec, 1);
497    }
498    return 0;
499 }
500
501 static int match_volfile(BSR *bsr, BSR_VOLFILE *volfile, DEV_RECORD *rec, bool done)
502 {
503    if (!volfile) {
504       return 1;                       /* no specification matches all */
505    }
506    /* For the moment, these tests work only with tapes. */
507    if (!(rec->state & REC_ISTAPE)) {
508       return 1;                       /* All File records OK for this match */
509    }
510 // Dmsg3(dbglevel, "match_volfile: sfile=%d efile=%d recfile=%d\n",
511 //             volfile->sfile, volfile->efile, rec->File);
512    if (volfile->sfile <= rec->File && volfile->efile >= rec->File) {
513       return 1;
514    }
515    /* Once we get past last efile, we are done */
516    if (rec->File > volfile->efile) {
517       volfile->done = true;              /* set local done */
518    }
519    if (volfile->next) {
520       return match_volfile(bsr, volfile->next, rec, volfile->done && done);
521    }
522
523    /* If we are done and all prior matches are done, this bsr is finished */
524    if (volfile->done && done) {
525       bsr->done = true;
526       bsr->root->reposition = true;
527       Dmsg2(dbglevel, "bsr done from volfile rec=%d volefile=%d\n",
528          rec->File, volfile->efile);
529    }
530    return 0;
531 }
532
533 static int match_stream(BSR *bsr, BSR_STREAM *stream, DEV_RECORD *rec, bool done)
534 {
535    if (!stream) {
536       return 1;                       /* no specification matches all */
537    }
538    if (stream->stream == rec->Stream) {
539       return 1;
540    }
541    if (stream->next) {
542       return match_stream(bsr, stream->next, rec, 1);
543    }
544    return 0;
545 }
546
547 static int match_sesstime(BSR *bsr, BSR_SESSTIME *sesstime, DEV_RECORD *rec, bool done)
548 {
549    if (!sesstime) {
550       return 1;                       /* no specification matches all */
551    }
552    if (sesstime->sesstime == rec->VolSessionTime) {
553       return 1;
554    }
555    if (rec->VolSessionTime > sesstime->sesstime) {
556       sesstime->done = true;
557    }
558    if (sesstime->next) {
559       return match_sesstime(bsr, sesstime->next, rec, sesstime->done && done);
560    }
561    if (sesstime->done && done) {
562       bsr->done = true;
563       bsr->root->reposition = true;
564       Dmsg0(dbglevel, "bsr done from sesstime\n");
565    }
566    return 0;
567 }
568
569 static int match_sessid(BSR *bsr, BSR_SESSID *sessid, DEV_RECORD *rec)
570 {
571    if (!sessid) {
572       return 1;                       /* no specification matches all */
573    }
574    if (sessid->sessid <= rec->VolSessionId && sessid->sessid2 >= rec->VolSessionId) {
575       return 1;
576    }
577    if (sessid->next) {
578       return match_sessid(bsr, sessid->next, rec);
579    }
580    return 0;
581 }
582
583 /*
584  * When reading the Volume, the Volume Findex (rec->FileIndex) always
585  *   are found in sequential order. Thus we can make optimizations.
586  *
587  *  ***FIXME*** optimizations
588  * We could optimize a lot here by removing the recursion, and 
589  *   stopping the search earlier -- say when rec->FileIndex > findex->findex2
590  *   and findex->next == NULL.  
591  */
592 static int match_findex(BSR *bsr, BSR_FINDEX *findex, DEV_RECORD *rec, bool done)
593 {
594    if (!findex) {
595       return 1;                       /* no specification matches all */
596    }
597    if (!findex->done) {
598       if (findex->findex <= rec->FileIndex && findex->findex2 >= rec->FileIndex) {
599          Dmsg3(dbglevel, "Match on findex=%d. bsr=%d,%d\n",
600                rec->FileIndex, findex->findex, findex->findex2);
601          return 1;
602       }
603       if (rec->FileIndex > findex->findex2) {
604          findex->done = true;
605       }
606    }
607    if (findex->next) {
608       return match_findex(bsr, findex->next, rec, findex->done && done);
609    }
610    if (findex->done && done) {
611       bsr->done = true;
612       bsr->root->reposition = true;
613       Dmsg1(dbglevel, "bsr done from findex %d\n", rec->FileIndex);
614    }
615    return 0;
616 }